@drawcall/charta 0.1.19 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +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,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"}
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;CAyP1B"}
@@ -15,7 +15,7 @@ export class GrassMesh extends InstancedMesh {
15
15
  for (let row = 0; row < rows; row++) {
16
16
  for (let col = 0; col < cols; col++) {
17
17
  // getCalls validates the schema and reports errors
18
- interpreter.getCalls([row, col], { grass: grassSchema });
18
+ interpreter.getCalls({ row, col }, { grass: grassSchema });
19
19
  }
20
20
  }
21
21
  return;
@@ -31,7 +31,7 @@ export class GrassMesh extends InstancedMesh {
31
31
  for (let row = 0; row < rows; row++) {
32
32
  for (let col = 0; col < cols; col++) {
33
33
  const grassParsed = interpreter
34
- .getCalls([row, col], { grass: grassSchema })
34
+ .getCalls({ row, col }, { grass: grassSchema })
35
35
  .at(-1)?.[1];
36
36
  if (!grassParsed)
37
37
  continue;
@@ -42,7 +42,7 @@ export class GrassMesh extends InstancedMesh {
42
42
  const step = cellSize / subN;
43
43
  const jitterAmp = step * 0.35;
44
44
  // top surface stack index = total number of ground/ceiling - 1
45
- const topStackIndex = Math.max(0, interpreter.countCalls([row, col], (c) => c.name === "ground" || c.name === "ceiling") - 1);
45
+ const topStackIndex = Math.max(0, interpreter.countCalls({ row, col }, (c) => c.name === "ground" || c.name === "ceiling") - 1);
46
46
  perCell[row][col] = {
47
47
  subN,
48
48
  jitterAmp,
@@ -101,8 +101,7 @@ export class GrassMesh extends InstancedMesh {
101
101
  const nb = neighbors;
102
102
  const step = cellSize / subN;
103
103
  // Cell center
104
- const cx = (col - cols / 2 + 0.5) * cellSize;
105
- const cz = (row - rows / 2 + 0.5) * cellSize;
104
+ const { x: cx, z: cz } = interpreter.gridToWorld({ row, col });
106
105
  for (let gz = 0; gz < subN; gz++) {
107
106
  for (let gx = 0; gx < subN; gx++) {
108
107
  // Optimization: Check stochastic rejection early using simplified fade check
@@ -144,16 +143,16 @@ export class GrassMesh extends InstancedMesh {
144
143
  if (Math.random() >= f)
145
144
  continue;
146
145
  // --- Instance is accepted ---
147
- // Position
148
- const ox = -cellSize * 0.5 + (gx + 0.5) * step;
149
- const oz = -cellSize * 0.5 + (gz + 0.5) * step;
146
+ // Position (offset from cell center)
147
+ const offsetX = -cellSize * 0.5 + (gx + 0.5) * step;
148
+ const offsetZ = -cellSize * 0.5 + (gz + 0.5) * step;
150
149
  const jx = (Math.random() * 2 - 1) * jitterAmp;
151
150
  const jz = (Math.random() * 2 - 1) * jitterAmp;
152
- const px = cx + ox + jx;
153
- const pz = cz + oz + jz;
151
+ const px = cx + offsetX + jx;
152
+ const pz = cz + offsetZ + jz;
154
153
  let baseY = 0;
155
154
  if (tilesGeometry) {
156
- baseY = tilesGeometry.getHeight(row, col, topStackIndex, px - cx, pz - cz);
155
+ baseY = tilesGeometry.getHeightAt({ row, col, offsetX: offsetX + jx, offsetZ: offsetZ + jz }, { stackIndex: topStackIndex });
157
156
  }
158
157
  tmpPos.set(px, baseY, pz);
159
158
  // Scale
@@ -182,5 +181,7 @@ export class GrassMesh extends InstancedMesh {
182
181
  this.instanceMatrix.needsUpdate = true;
183
182
  if (this.instanceColor)
184
183
  this.instanceColor.needsUpdate = true;
184
+ // Disable raycasting entirely for grass (no-op)
185
+ this.raycast = () => { };
185
186
  }
186
187
  }
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
@@ -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
@@ -10,4 +10,5 @@ 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";
@@ -1,7 +1,8 @@
1
1
  import { output, ZodObject } from 'zod';
2
2
  import { Call } from './parser.js';
3
3
  import { ChartaError, ErrorLocation } from './errors.js';
4
- import { cellSizeSchema } from './schemas.js';
4
+ import { cellSizeSchema, GridCoord, WorldCoord } from './schemas.js';
5
+ export type { GridCoord, WorldCoord };
5
6
  export { cellSizeSchema as cellSizeFunctionParams };
6
7
  export type InterpreterOptions = {
7
8
  throwOnError?: boolean;
@@ -19,13 +20,14 @@ export declare class Interpreter {
19
20
  private readonly sourceLines;
20
21
  getSource(): string;
21
22
  getLineTextForRow(row: number): string;
22
- getWorldCellCenter(row: number, col: number): [number, number];
23
+ gridToWorld(coord: GridCoord): WorldCoord;
24
+ worldToGrid(coord: WorldCoord): GridCoord;
23
25
  getRows(): number;
24
26
  getCols(): number;
25
- countCalls(index: [number, number] | undefined, filter: string | ((call: Call) => boolean), startAt?: number, endAtExcl?: number): number;
27
+ countCalls(coord: GridCoord | undefined, filter: string | ((call: Call) => boolean), startAt?: number, endAtExcl?: number): number;
26
28
  getCalls<T extends {
27
29
  [Key in string]: ZodObject<any>;
28
- }>(index: [number, number] | undefined, callSchemas: T): Array<{
30
+ }>(coord: GridCoord | undefined, callSchemas: T): Array<{
29
31
  [Key in keyof T]: [Key, output<T[Key]>, number, {
30
32
  line: number;
31
33
  column: number;
@@ -1 +1 @@
1
- {"version":3,"file":"interpreter.d.ts","sourceRoot":"","sources":["../src/interpreter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAa,MAAM,KAAK,CAAA;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAClC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,EAAE,cAAc,IAAI,sBAAsB,EAAE,CAAA;AAEnD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,qBAAa,WAAW;IAKpB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IANzB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,YAAY,CAAS;gBAGV,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,EACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,YAAK,EAChC,MAAM,EAAE,MAAM,EAC/B,OAAO,GAAE,kBAAuB;IAelC,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAOrC,SAAS,IAAI,WAAW,EAAE;IAI1B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IAEtC,SAAS,IAAI,MAAM;IAInB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAMtC,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAO9D,OAAO,IAAI,MAAM;IAKjB,OAAO,IAAI,MAAM;IAKjB,UAAU,CACR,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EACnC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,EAC1C,OAAO,GAAE,MAAU,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM;IAgBT,QAAQ,CAAC,CAAC,SAAS;SAAG,GAAG,IAAI,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC;KAAE,EACpD,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EACnC,WAAW,EAAE,CAAC,GACb,KAAK,CACN;SACG,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAC;KACpG,CAAC,MAAM,CAAC,CAAC,CACX;IA0GD,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,uBAAuB;IAa/B,gBAAgB,CAAC,CAAC,EAAE,WAAW,EAAE;QAAE,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;KAAE,EAAE,IAAI,GAAE,MAAyB,GAAG,CAAC,GAAG,SAAS;IAWlH,QAAQ,CAAC,CAAC,EACR,WAAW,EAAE;QAAE,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;KAAE,EAC7C,IAAI,GAAE,MAAyB,EAC/B,GAAG,CAAC,EAAE,aAAa,GAClB,CAAC,GAAG,SAAS;IAmBhB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAIxC,WAAW,IAAI,MAAM;CAStB"}
1
+ {"version":3,"file":"interpreter.d.ts","sourceRoot":"","sources":["../src/interpreter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAa,MAAM,KAAK,CAAA;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAClC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEpE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,CAAA;AAErC,OAAO,EAAE,cAAc,IAAI,sBAAsB,EAAE,CAAA;AAEnD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,qBAAa,WAAW;IAKpB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IANzB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,YAAY,CAAS;gBAGV,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,EACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,YAAK,EAChC,MAAM,EAAE,MAAM,EAC/B,OAAO,GAAE,kBAAuB;IAelC,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAOrC,SAAS,IAAI,WAAW,EAAE;IAI1B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IAEtC,SAAS,IAAI,MAAM;IAInB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAMtC,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU;IAUzC,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS;IAsBzC,OAAO,IAAI,MAAM;IAKjB,OAAO,IAAI,MAAM;IAKjB,UAAU,CACR,KAAK,EAAE,SAAS,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,EAC1C,OAAO,GAAE,MAAU,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM;IAgBT,QAAQ,CAAC,CAAC,SAAS;SAAG,GAAG,IAAI,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC;KAAE,EACpD,KAAK,EAAE,SAAS,GAAG,SAAS,EAC5B,WAAW,EAAE,CAAC,GACb,KAAK,CACN;SACG,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAC;KACpG,CAAC,MAAM,CAAC,CAAC,CACX;IA0GD,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,uBAAuB;IAa/B,gBAAgB,CAAC,CAAC,EAAE,WAAW,EAAE;QAAE,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;KAAE,EAAE,IAAI,GAAE,MAAyB,GAAG,CAAC,GAAG,SAAS;IAWlH,QAAQ,CAAC,CAAC,EACR,WAAW,EAAE;QAAE,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;KAAE,EAC7C,IAAI,GAAE,MAAyB,EAC/B,GAAG,CAAC,EAAE,aAAa,GAClB,CAAC,GAAG,SAAS;IAmBhB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAIxC,WAAW,IAAI,MAAM;CAStB"}
@@ -40,11 +40,31 @@ export class Interpreter {
40
40
  const lineNumber = row + 2;
41
41
  return this.sourceLines[lineNumber - 1] ?? '';
42
42
  }
43
- getWorldCellCenter(row, col) {
43
+ gridToWorld(coord) {
44
44
  const cellSize = this.getCellSize();
45
- const worldCellCenterX = (col - this.getCols() / 2 + 0.5) * cellSize;
46
- const worldCellCenterZ = (row - this.getRows() / 2 + 0.5) * cellSize;
47
- return [worldCellCenterX, worldCellCenterZ];
45
+ const cx = (coord.col - this.getCols() / 2 + 0.5) * cellSize;
46
+ const cz = (coord.row - this.getRows() / 2 + 0.5) * cellSize;
47
+ return {
48
+ x: cx + (coord.offsetX ?? 0),
49
+ z: cz + (coord.offsetZ ?? 0),
50
+ };
51
+ }
52
+ worldToGrid(coord) {
53
+ const cellSize = this.getCellSize();
54
+ const cols = this.getCols();
55
+ const rows = this.getRows();
56
+ const rawCol = coord.x / cellSize + cols / 2 - 0.5;
57
+ const rawRow = coord.z / cellSize + rows / 2 - 0.5;
58
+ const col = Math.floor(rawCol);
59
+ const row = Math.floor(rawRow);
60
+ const cellCenterX = (col - cols / 2 + 0.5) * cellSize;
61
+ const cellCenterZ = (row - rows / 2 + 0.5) * cellSize;
62
+ return {
63
+ row,
64
+ col,
65
+ offsetX: coord.x - cellCenterX,
66
+ offsetZ: coord.z - cellCenterZ,
67
+ };
48
68
  }
49
69
  getRows() {
50
70
  // Exclude meta row
@@ -54,9 +74,9 @@ export class Interpreter {
54
74
  // Use first grid row, if present
55
75
  return this.ast.length > 1 ? this.ast[1].length : 0;
56
76
  }
57
- countCalls(index, filter, startAt = 0, endAtExcl) {
77
+ countCalls(coord, filter, startAt = 0, endAtExcl) {
58
78
  let sum = 0;
59
- const calls = index == null ? this.ast[0][0] : this.ast[index[0] + 1][index[1]];
79
+ const calls = coord == null ? this.ast[0][0] : this.ast[coord.row + 1][coord.col];
60
80
  const length = Math.min(endAtExcl ?? Infinity, calls.length);
61
81
  for (let i = startAt; i < length; i++) {
62
82
  if (typeof filter === 'string' && calls[i].name != filter) {
@@ -69,28 +89,28 @@ export class Interpreter {
69
89
  }
70
90
  return sum;
71
91
  }
72
- getCalls(index, callSchemas) {
92
+ getCalls(coord, callSchemas) {
73
93
  let calls;
74
94
  const normalizedSource = this.normalizedSource;
75
95
  const sourceLines = this.sourceLines;
76
96
  let lineNumber = 1;
77
97
  let lineText = sourceLines[0] ?? '';
78
98
  let callStartColumns = [];
79
- if (index == null) {
99
+ if (coord == null) {
80
100
  calls = this.ast[0][0];
81
101
  callStartColumns = this.computeCallStartColumns(lineText);
82
102
  }
83
103
  else {
84
- const row = index[0] + 1;
85
- const col = index[1];
104
+ const row = coord.row + 1;
105
+ const col = coord.col;
86
106
  if (row >= this.ast.length || col >= this.ast[row].length) {
87
- const lineNumber = index[0] + 2;
107
+ const lineNumber = coord.row + 2;
88
108
  const lineText = sourceLines[lineNumber - 1] ?? '';
89
109
  this.reportError(new ChartaError(`index ${col}/${row} cannot be used to index the ast (${this.ast.length}/${this.ast[Math.min(1, this.ast.length - 1)].length})`, normalizedSource, { line: lineNumber, column: 1, lineText }));
90
110
  return []; // Return empty if index invalid
91
111
  }
92
112
  calls = this.ast[row][col];
93
- lineNumber = index[0] + 2;
113
+ lineNumber = coord.row + 2;
94
114
  lineText = sourceLines[lineNumber - 1] ?? '';
95
115
  const [cellStart, cellEnd] = this.getCellBounds(lineText, col);
96
116
  const cellText = lineText.slice(cellStart, cellEnd);
@@ -1 +1 @@
1
- {"version":3,"file":"locations.d.ts","sourceRoot":"","sources":["../src/locations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAG9C,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,EAAE,cAAc,EAAE,CAAA;AAEzB,qBAAa,SAAS;IACpB,SAAgB,cAAc,uBAA6B;gBAE/C,WAAW,EAAE,WAAW;IAgCpC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO;CAOzD"}
1
+ {"version":3,"file":"locations.d.ts","sourceRoot":"","sources":["../src/locations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAG9C,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,EAAE,cAAc,EAAE,CAAA;AAEzB,qBAAa,SAAS;IACpB,SAAgB,cAAc,uBAA6B;gBAE/C,WAAW,EAAE,WAAW;IA8BpC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO;CAOzD"}
package/dist/locations.js CHANGED
@@ -12,7 +12,7 @@ export class Locations {
12
12
  const isLayer = (c) => c.name === 'ground' || c.name === 'ceiling';
13
13
  for (let row = 0; row < rows; row++) {
14
14
  for (let col = 0; col < cols; col++) {
15
- const entries = interpreter.getCalls([row, col], { location: locationSchema });
15
+ const entries = interpreter.getCalls({ row, col }, { location: locationSchema });
16
16
  for (const [, parsed, callIdx, loc] of entries) {
17
17
  const name = parsed.name;
18
18
  if (this.worldPositions.has(name)) {
@@ -21,12 +21,10 @@ export class Locations {
21
21
  }
22
22
  const offsetX = parsed.offsetX ?? 0;
23
23
  const offsetZ = parsed.offsetZ ?? 0;
24
- const [cx, cz] = interpreter.getWorldCellCenter(row, col);
25
- const x = cx + offsetX;
26
- const z = cz + offsetZ;
27
- const layersBefore = interpreter.countCalls([row, col], isLayer, 0, callIdx);
24
+ const { x, z } = interpreter.gridToWorld({ row, col, offsetX, offsetZ });
25
+ const layersBefore = interpreter.countCalls({ row, col }, isLayer, 0, callIdx);
28
26
  const stackIndex = layersBefore > 0 ? layersBefore - 1 : 0;
29
- const y = tilesGeometry?.getHeight(row, col, stackIndex, offsetX, offsetZ) ?? 0;
27
+ const y = tilesGeometry?.getHeightAt({ row, col, offsetX, offsetZ }, { stackIndex }) ?? 0;
30
28
  this.worldPositions.set(name, new Vector3(x, y, z));
31
29
  }
32
30
  }
@@ -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,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"}
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;CA+LpH"}
@@ -4,6 +4,7 @@ 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();
@@ -27,31 +28,30 @@ export class PillarMesh extends InstancedMesh {
27
28
  return idx;
28
29
  };
29
30
  function resolveCornerPosition(row, col, corner) {
30
- const [cx, cz] = interpreter.getWorldCellCenter(row, col);
31
31
  const half = cellSize * 0.5;
32
- switch (corner) {
33
- case 'topleft':
34
- return [cx - half, cz - half];
35
- case 'topright':
36
- return [cx + half, cz - half];
37
- case 'bottomright':
38
- return [cx + half, cz + half];
39
- case 'bottomleft':
40
- return [cx - half, cz + half];
41
- default: {
42
- const lineNumber = row + 2;
43
- const lineText = interpreter.getLineTextForRow(row);
44
- const pos = lineText.indexOf('pillar(');
45
- const columnNumber = pos >= 0 ? pos + 1 : 1;
46
- interpreter.reportError(new ChartaError(`pillar at ${row}/${col}: invalid corner "${corner}", expected one of topleft, topright, bottomright, bottomleft`, interpreter.getSource(), { line: lineNumber, column: columnNumber, lineText }));
47
- return undefined;
48
- }
32
+ const offsets = {
33
+ topleft: [-half, -half],
34
+ topright: [half, -half],
35
+ bottomright: [half, half],
36
+ bottomleft: [-half, half],
37
+ };
38
+ const offset = offsets[corner];
39
+ if (offset) {
40
+ const { x, z } = interpreter.gridToWorld({ row, col, offsetX: offset[0], offsetZ: offset[1] });
41
+ return [x, z];
49
42
  }
43
+ // Invalid corner
44
+ const lineNumber = row + 2;
45
+ const lineText = interpreter.getLineTextForRow(row);
46
+ const pos = lineText.indexOf('pillar(');
47
+ const columnNumber = pos >= 0 ? pos + 1 : 1;
48
+ interpreter.reportError(new ChartaError(`pillar at ${row}/${col}: invalid corner "${corner}", expected one of topleft, topright, bottomright, bottomleft`, interpreter.getSource(), { line: lineNumber, column: columnNumber, lineText }));
49
+ return undefined;
50
50
  }
51
51
  // Validation loop runs in both modes
52
52
  for (let row = 0; row < rows; row++) {
53
53
  for (let col = 0; col < cols; col++) {
54
- const entries = interpreter.getCalls([row, col], {
54
+ const entries = interpreter.getCalls({ row, col }, {
55
55
  pillar: pillarSchema,
56
56
  });
57
57
  for (const [, parsed, idx, loc] of entries) {
@@ -61,9 +61,7 @@ export class PillarMesh extends InstancedMesh {
61
61
  if (!cornerPos)
62
62
  continue;
63
63
  const [px, pz] = cornerPos;
64
- const [cx, cz] = interpreter.getWorldCellCenter(row, col);
65
- const offsetX = px - cx;
66
- const offsetZ = pz - cz;
64
+ const { offsetX, offsetZ } = interpreter.worldToGrid({ x: px, z: pz });
67
65
  // Validate texture reference (runs in both modes)
68
66
  const textureId = getTextureId(parsed.texture, loc);
69
67
  // Derive default layers relative to call position (like walls)
@@ -73,7 +71,7 @@ export class PillarMesh extends InstancedMesh {
73
71
  // Validate layer existence (runs in both modes)
74
72
  let bottomStackIndex;
75
73
  if (bottomY == null) {
76
- const layersBefore = interpreter.countCalls([row, col], isLayer, 0, idx);
74
+ const layersBefore = interpreter.countCalls({ row, col }, isLayer, 0, idx);
77
75
  bottomStackIndex = layersBefore - 1;
78
76
  if (bottomStackIndex < 0) {
79
77
  interpreter.reportError(new ChartaError(`pillar at ${row}/${col}: missing bottomY and no preceding layer`, interpreter.getSource(), loc));
@@ -84,24 +82,24 @@ export class PillarMesh extends InstancedMesh {
84
82
  const tilesGeometry = interpreter.getAsset(TilesGeometry, 'tilesGeometry', loc);
85
83
  if (!tilesGeometry)
86
84
  continue;
87
- bottomY = tilesGeometry.getHeight(row, col, bottomStackIndex, offsetX, offsetZ);
85
+ bottomY = tilesGeometry.getHeightAt({ row, col, offsetX, offsetZ }, { stackIndex: bottomStackIndex });
88
86
  }
89
87
  }
90
88
  let topStackIndex;
91
89
  if (topY == null) {
92
- const layersAfter = interpreter.countCalls([row, col], isLayer, idx + 1);
90
+ const layersAfter = interpreter.countCalls({ row, col }, isLayer, idx + 1);
93
91
  if (layersAfter <= 0) {
94
92
  interpreter.reportError(new ChartaError(`pillar at ${row}/${col}: missing topY and no subsequent layer`, interpreter.getSource(), loc));
95
93
  continue;
96
94
  }
97
95
  // Height sampling only in non-validateOnly mode
98
96
  if (!options.validateOnly) {
99
- const layersBefore = interpreter.countCalls([row, col], isLayer, 0, idx);
97
+ const layersBefore = interpreter.countCalls({ row, col }, isLayer, 0, idx);
100
98
  topStackIndex = layersBefore;
101
99
  const tilesGeometry = interpreter.getAsset(TilesGeometry, 'tilesGeometry', loc);
102
100
  if (!tilesGeometry)
103
101
  continue;
104
- topY = tilesGeometry.getHeight(row, col, topStackIndex, offsetX, offsetZ);
102
+ topY = tilesGeometry.getHeightAt({ row, col, offsetX, offsetZ }, { stackIndex: topStackIndex });
105
103
  }
106
104
  }
107
105
  // Only collect pillar data in non-validateOnly mode
@@ -160,5 +158,8 @@ export class PillarMesh extends InstancedMesh {
160
158
  const textureArray = buildTextureArrayFromAssets(usedTextures);
161
159
  buildPillarMeshMaterial(this.material, textureArray);
162
160
  }
161
+ // Setup BVH for accelerated raycasting
162
+ setupBVH(this.geometry);
163
+ this.raycast = acceleratedRaycast;
163
164
  }
164
165
  }
@@ -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,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"}
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;IAkPhG,OAAO;CAOR"}
@@ -61,18 +61,17 @@ export class PlaceGroup extends Group {
61
61
  const scale = Math.min(sFactorX ?? sFactorZ ?? 1, sFactorZ ?? sFactorX ?? 1);
62
62
  return new Vector3(scale, scale, scale);
63
63
  };
64
- const computeTransform = (px, pz, cell, transformOpts) => {
64
+ const computeTransform = (worldX, worldZ, cell, transformOpts) => {
65
65
  const normal = new Vector3();
66
- const offsetX = px - cell.center[0];
67
- const offsetZ = pz - cell.center[1];
66
+ const { offsetX, offsetZ } = interpreter.worldToGrid({ x: worldX, z: worldZ });
68
67
  let py = 0;
69
68
  if (transformOpts.useFixedY) {
70
69
  py = transformOpts.fixedY;
71
70
  }
72
71
  else {
73
- py = tilesGeometry.getHeight(cell.row, cell.col, transformOpts.layerIndex, offsetX, offsetZ, normal);
72
+ py = tilesGeometry.getHeightAt({ row: cell.row, col: cell.col, offsetX, offsetZ }, { stackIndex: transformOpts.layerIndex, normalTarget: normal });
74
73
  }
75
- const pos = new Vector3(px, py, pz);
74
+ const pos = new Vector3(worldX, py, worldZ);
76
75
  const quat = new Quaternion();
77
76
  if (transformOpts.alignMode === 'normal' && !transformOpts.useFixedY) {
78
77
  const qAlign = new Quaternion().setFromUnitVectors(new Vector3(0, 1, 0), normal);
@@ -90,19 +89,18 @@ export class PlaceGroup extends Group {
90
89
  for (let row = 0; row < rows; row++) {
91
90
  for (let col = 0; col < cols; col++) {
92
91
  // getCalls validates the schema and reports errors
93
- const scatterEntries = interpreter.getCalls([row, col], { scatter: scatterSchema });
94
- const placeEntries = interpreter.getCalls([row, col], { place: placeSchema });
92
+ const scatterEntries = interpreter.getCalls({ row, col }, { scatter: scatterSchema });
93
+ const placeEntries = interpreter.getCalls({ row, col }, { place: placeSchema });
95
94
  if (scatterEntries.length === 0 && placeEntries.length === 0)
96
95
  continue;
97
- const cx = (col - cols / 2 + 0.5) * cellSize;
98
- const cz = (row - rows / 2 + 0.5) * cellSize;
96
+ const { x: cx, z: cz } = interpreter.gridToWorld({ row, col });
99
97
  const half = cellSize * 0.5;
100
98
  // Collect location points in this cell for collision/footprint validation
101
- const locationEntries = interpreter.getCalls([row, col], { location: locationSchema });
102
- const locationPoints = locationEntries.map(([, parsed]) => ({
103
- x: cx + (parsed.offsetX ?? 0),
104
- z: cz + (parsed.offsetZ ?? 0),
105
- }));
99
+ const locationEntries = interpreter.getCalls({ row, col }, { location: locationSchema });
100
+ const locationPoints = locationEntries.map(([, parsed]) => {
101
+ const { x, z } = interpreter.gridToWorld({ row, col, offsetX: parsed.offsetX, offsetZ: parsed.offsetZ });
102
+ return { x, z };
103
+ });
106
104
  // scatter: randomized distribution
107
105
  for (const [, parsed, callIdx, loc] of scatterEntries) {
108
106
  const prefabName = `${parsed.model}Prefab`;
@@ -115,7 +113,7 @@ export class PlaceGroup extends Group {
115
113
  this.batchBuilders.set(parsed.model, batchBuilder);
116
114
  const usualSize = new Vector3();
117
115
  usualSize.copy(batchBuilder.prefabSize);
118
- const layerIndex = interpreter.countCalls([row, col], (c) => c.name === 'ground' || c.name === 'ceiling', 0, callIdx) - 1;
116
+ const layerIndex = interpreter.countCalls({ row, col }, (c) => c.name === 'ground' || c.name === 'ceiling', 0, callIdx) - 1;
119
117
  const base = hashStringToUint32(parsed.model);
120
118
  const seed = hashNumbersToUint32(base, row, col);
121
119
  const rng = createRng(seed);
@@ -143,7 +141,7 @@ export class PlaceGroup extends Group {
143
141
  const px = cx + ox;
144
142
  const pz = cz + oz;
145
143
  const yaw = yawMin === yawMax ? yawMin : yawMin + (yawMax - yawMin) * rng();
146
- const { pos, quat, scl } = computeTransform(px, pz, { row, col, center: [cx, cz] }, {
144
+ const { pos, quat, scl } = computeTransform(px, pz, { row, col }, {
147
145
  alignMode,
148
146
  yaw,
149
147
  useFixedY: useFixedY,
@@ -174,8 +172,12 @@ export class PlaceGroup extends Group {
174
172
  if (!batchBuilder)
175
173
  continue;
176
174
  const usualSize = batchBuilder.prefabSize;
177
- const px = cx + (parsed.offsetX ?? 0);
178
- const pz = cz + (parsed.offsetZ ?? 0);
175
+ const { x: px, z: pz } = interpreter.gridToWorld({
176
+ row,
177
+ col,
178
+ offsetX: parsed.offsetX,
179
+ offsetZ: parsed.offsetZ,
180
+ });
179
181
  // Compute scale for footprint validation (needed for both validateOnly and normal mode)
180
182
  const scale = computeScale(usualSize, parsed.sizeX, parsed.sizeZ);
181
183
  const halfX = (usualSize.x * scale.x) / 2;
@@ -190,10 +192,10 @@ export class PlaceGroup extends Group {
190
192
  if (validateOnly)
191
193
  continue;
192
194
  this.batchBuilders.set(parsed.model, batchBuilder);
193
- const layerIndex = interpreter.countCalls([row, col], (c) => c.name === 'ground' || c.name === 'ceiling', 0, callIdx) - 1;
195
+ const layerIndex = interpreter.countCalls({ row, col }, (c) => c.name === 'ground' || c.name === 'ceiling', 0, callIdx) - 1;
194
196
  const yaw = parsed.yaw ?? 0;
195
197
  const alignMode = (parsed.align ?? 'up').toLowerCase();
196
- const { pos, quat, scl } = computeTransform(px, pz, { row, col, center: [cx, cz] }, {
198
+ const { pos, quat, scl } = computeTransform(px, pz, { row, col }, {
197
199
  alignMode,
198
200
  yaw,
199
201
  useFixedY: parsed.bottomY != null,
package/dist/schemas.d.ts CHANGED
@@ -1,4 +1,22 @@
1
1
  import { coerce } from 'zod';
2
+ /**
3
+ * Grid coordinate with optional offset from cell center.
4
+ * row/col are integer indices into the grid.
5
+ * offsetX/offsetZ are in meters from the cell center.
6
+ */
7
+ export type GridCoord = {
8
+ row: number;
9
+ col: number;
10
+ offsetX?: number;
11
+ offsetZ?: number;
12
+ };
13
+ /**
14
+ * World coordinate in meters (top-down X/Z plane).
15
+ */
16
+ export type WorldCoord = {
17
+ x: number;
18
+ z: number;
19
+ };
2
20
  export declare const alignEnum: import("zod").ZodEnum<{
3
21
  up: "up";
4
22
  normal: "normal";
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAmC,MAAM,KAAK,CAAA;AAM7D,eAAO,MAAM,SAAS;;;EAA4B,CAAA;AAClD,eAAO,MAAM,UAAU;;;;;EAAgE,CAAA;AACvF,eAAO,MAAM,WAAW;;;;;EAA8C,CAAA;AAMtE,eAAO,MAAM,cAAc;;gCAAoC,CAAA;AAC/D,eAAO,MAAM,cAAc;;gCAA6B,CAAA;AACxD,eAAO,MAAM,iBAAiB;;;gCAA6C,CAAA;AAC3E,eAAO,MAAM,eAAe;;;gCAA6C,CAAA;AAMzE,eAAO,MAAM,YAAY;;;gCAAoD,CAAA;AAC7E,eAAO,MAAM,aAAa;;;gCAAoD,CAAA;AAC9E,eAAO,MAAM,WAAW;;gCAAiC,CAAA;AAMzD,eAAO,MAAM,UAAU;;;;;;;;;;gCAKrB,CAAA;AAEF,eAAO,MAAM,YAAY;;;;;gCAKvB,CAAA;AAEF,eAAO,MAAM,UAAU;;;;;;gCAMrB,CAAA;AAEF,eAAO,MAAM,YAAY;;;;;;;;;;;;gCAOvB,CAAA;AAMF,eAAO,MAAM,cAAc;;;;gCAIzB,CAAA;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;gCAStB,CAAA;AAEF,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;gCAcxB,CAAA;AAMF,eAAO,MAAM,WAAW;;;;gCAItB,CAAA;AAMF;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAET"}
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAmC,MAAM,KAAK,CAAA;AAM7D;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV,CAAA;AAMD,eAAO,MAAM,SAAS;;;EAA4B,CAAA;AAClD,eAAO,MAAM,UAAU;;;;;EAAgE,CAAA;AACvF,eAAO,MAAM,WAAW;;;;;EAA8C,CAAA;AAMtE,eAAO,MAAM,cAAc;;gCAAoC,CAAA;AAC/D,eAAO,MAAM,cAAc;;gCAA6B,CAAA;AACxD,eAAO,MAAM,iBAAiB;;;gCAA6C,CAAA;AAC3E,eAAO,MAAM,eAAe;;;gCAA6C,CAAA;AAMzE,eAAO,MAAM,YAAY;;;gCAAoD,CAAA;AAC7E,eAAO,MAAM,aAAa;;;gCAAoD,CAAA;AAC9E,eAAO,MAAM,WAAW;;gCAAiC,CAAA;AAMzD,eAAO,MAAM,UAAU;;;;;;;;;;gCAKrB,CAAA;AAEF,eAAO,MAAM,YAAY;;;;;gCAKvB,CAAA;AAEF,eAAO,MAAM,UAAU;;;;;;gCAMrB,CAAA;AAEF,eAAO,MAAM,YAAY;;;;;;;;;;;;gCAOvB,CAAA;AAMF,eAAO,MAAM,cAAc;;;;gCAIzB,CAAA;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;gCAStB,CAAA;AAEF,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;gCAcxB,CAAA;AAMF,eAAO,MAAM,WAAW;;;;gCAItB,CAAA;AAMF;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAET"}
@@ -1,4 +1,17 @@
1
1
  import { BufferGeometry, Vector3 } from 'three';
2
+ import type { GridCoord } from '../schemas.js';
3
+ export type GetHeightAtOptions = {
4
+ /**
5
+ * index in the tile stack
6
+ * use 0 for the lowest and Infinity for the highest
7
+ * @default infinity
8
+ */
9
+ stackIndex?: number;
10
+ /**
11
+ * optional target vector that will receive the interpolated face normal at the queried point
12
+ */
13
+ normalTarget?: Vector3;
14
+ };
2
15
  export type Tile = {
3
16
  type: string;
4
17
  y: number;
@@ -30,15 +43,7 @@ export declare class TilesGeometry extends BufferGeometry {
30
43
  bottom: Array<WallData>;
31
44
  left: Array<WallData>;
32
45
  }>>, mapSizeX: number, mapSizeZ: number);
33
- /**
34
- * @param row tile grid row index (Z direction)
35
- * @param col tile grid column index (X direction)
36
- * @param stackIndex index in the tile stack
37
- * @param offsetX world unit offset from tile center X
38
- * @param offsetZ world unit offset from tile center Z
39
- * @param normalTarget optional target vector that will receive the interpolated face normal at the queried point
40
- */
41
- getHeight(row: number, col: number, stackIndex?: number, offsetX?: number, offsetZ?: number, normalTarget?: Vector3): number;
46
+ getHeightAt({ row, col, offsetX, offsetZ }: GridCoord, { normalTarget, stackIndex }?: GetHeightAtOptions): number;
42
47
  private addVertex;
43
48
  private getVertices;
44
49
  private getConnectionVertexId;
@@ -1 +1 @@
1
- {"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../src/tiles/geometry.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EAMd,OAAO,EACR,MAAM,OAAO,CAAA;AAGd,MAAM,MAAM,IAAI,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAoB3F,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAkQxD,qBAAa,aAAc,SAAQ,cAAc;IAe7C,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;aAQN,QAAQ,EAAE,MAAM;aAChB,QAAQ,EAAE,MAAM;IAxBlC,SAAgB,SAAS,EAAE,MAAM,CAAA;IACjC,SAAgB,SAAS,EAAE,MAAM,CAAA;IAEjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAoB;IAErD,OAAO,CAAC,SAAS,CAAI;IAErB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;IAChE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;gBAG7C,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAChC,KAAK,EAAE,KAAK,CAC3B,KAAK,CAAC;QACJ,GAAG,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;QACpB,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;QACtB,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;QACvB,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;KACtB,CAAC,CACH,EACe,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM;IA0ElC;;;;;;;OAOG;IACI,SAAS,CACd,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,GAAE,MAAU,EACnB,OAAO,GAAE,MAAU,EACnB,YAAY,CAAC,EAAE,OAAO;IAqFxB,OAAO,CAAC,SAAS;IAoGjB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,qBAAqB;CAiB9B"}
1
+ {"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../src/tiles/geometry.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EAMd,OAAO,EACR,MAAM,OAAO,CAAA;AAGd,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE9C,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,IAAI,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAoB3F,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAkQxD,qBAAa,aAAc,SAAQ,cAAc;IAe7C,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;aAQN,QAAQ,EAAE,MAAM;aAChB,QAAQ,EAAE,MAAM;IAxBlC,SAAgB,SAAS,EAAE,MAAM,CAAA;IACjC,SAAgB,SAAS,EAAE,MAAM,CAAA;IAEjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAoB;IAErD,OAAO,CAAC,SAAS,CAAI;IAErB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;IAChE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;gBAG7C,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAChC,KAAK,EAAE,KAAK,CAC3B,KAAK,CAAC;QACJ,GAAG,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;QACpB,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;QACtB,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;QACvB,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;KACtB,CAAC,CACH,EACe,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM;IA0E3B,WAAW,CAChB,EAAE,GAAG,EAAE,GAAG,EAAE,OAAW,EAAE,OAAW,EAAE,EAAE,SAAS,EACjD,EAAE,YAAY,EAAE,UAAqB,EAAE,GAAE,kBAAuB;IAiFlE,OAAO,CAAC,SAAS;IAoGjB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,qBAAqB;CAiB9B"}
@@ -1,5 +1,6 @@
1
1
  import { BufferGeometry, Float32BufferAttribute, Uint16BufferAttribute, } from 'three';
2
2
  import { isLayerConnectedToWall } from '../walls/index.js';
3
+ import { clamp } from 'three/src/math/MathUtils.js';
3
4
  const MAX_AUTO_CONNECT_HEIGHT_DIFF_RATIO = 1 / 4;
4
5
  const MAX_CONNECTION_HEIGHT_DIFF_RATIO = 8;
5
6
  function isBetterConnection(newConnection, oldConnection, tileSize) {
@@ -293,20 +294,12 @@ export class TilesGeometry extends BufferGeometry {
293
294
  this.computeVertexNormals();
294
295
  this.computeTangents();
295
296
  }
296
- /**
297
- * @param row tile grid row index (Z direction)
298
- * @param col tile grid column index (X direction)
299
- * @param stackIndex index in the tile stack
300
- * @param offsetX world unit offset from tile center X
301
- * @param offsetZ world unit offset from tile center Z
302
- * @param normalTarget optional target vector that will receive the interpolated face normal at the queried point
303
- */
304
- getHeight(row, col, stackIndex, offsetX = 0, offsetZ = 0, normalTarget) {
297
+ getHeightAt({ row, col, offsetX = 0, offsetZ = 0 }, { normalTarget, stackIndex = Infinity } = {}) {
305
298
  if (col < 0 || col >= this.tiles[0].length || row < 0 || row >= this.tiles.length) {
306
299
  throw new Error(`Tile index out of bounds: (row=${row}, col=${col}). Grid size: ${this.tiles.length}x${this.tiles[0].length}`);
307
300
  }
308
301
  const vertices = this.getVertices(col, row);
309
- const vertex1 = vertices[stackIndex ?? vertices.length - 1];
302
+ const vertex1 = vertices[clamp(stackIndex, 0, vertices.length - 1)];
310
303
  if (vertex1 == null) {
311
304
  throw new Error(`No vertex found at stack index "${stackIndex ?? 'any'}" for tile (row=${row}, col=${col}).`);
312
305
  }
@@ -1,7 +1,7 @@
1
1
  import { Material, Mesh } from 'three';
2
2
  import { Interpreter } from '../interpreter.js';
3
- import { TilesGeometry } from './geometry.js';
4
- import { groundSchema, ceilingSchema } from '../schemas.js';
3
+ import { GetHeightAtOptions, TilesGeometry } from './geometry.js';
4
+ import { groundSchema, ceilingSchema, GridCoord } from '../schemas.js';
5
5
  export { groundSchema, ceilingSchema };
6
6
  export type TilesMeshOptions = {
7
7
  /**
@@ -13,5 +13,7 @@ export type TilesMeshOptions = {
13
13
  };
14
14
  export declare class TilesMesh extends Mesh<TilesGeometry> {
15
15
  constructor(interpreter: Interpreter, material?: Material, options?: TilesMeshOptions);
16
+ getHeightAt(coord: GridCoord, options?: GetHeightAtOptions): number;
17
+ dispose(): void;
16
18
  }
17
19
  //# sourceMappingURL=index.d.ts.map
@@ -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,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"}
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,kBAAkB,EAAE,aAAa,EAAuB,MAAM,eAAe,CAAA;AAItF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAc,SAAS,EAAE,MAAM,eAAe,CAAA;AAGlF,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;IAyIlH,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,kBAAkB;IAI1D,OAAO,IAAI,IAAI;CAMhB"}
@@ -1,9 +1,10 @@
1
- import { Mesh, Texture, MeshBasicMaterial, } from 'three';
1
+ import { Material, Mesh, Texture, MeshBasicMaterial, } from 'three';
2
2
  import { TilesGeometry } from './geometry.js';
3
3
  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];
@@ -65,7 +66,7 @@ export class TilesMesh extends Mesh {
65
66
  for (let row = 0; row < rows; row++) {
66
67
  for (let col = 0; col < cols; col++) {
67
68
  // Collect and validate all entries for this cell
68
- const calls = Array.from(interpreter.getCalls([row, col], {
69
+ const calls = Array.from(interpreter.getCalls({ row, col }, {
69
70
  ground: groundSchema,
70
71
  ceiling: ceilingSchema,
71
72
  wall: wallSchema,
@@ -133,5 +134,17 @@ 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;
140
+ }
141
+ getHeightAt(coord, options) {
142
+ return this.geometry.getHeightAt(coord, options);
143
+ }
144
+ dispose() {
145
+ this.geometry.dispose();
146
+ if (this.material instanceof Material) {
147
+ this.material.dispose();
148
+ }
136
149
  }
137
150
  }
@@ -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;AAE7E,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;IA6CtE,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;IAiB1C;;;OAGG;IACH,OAAO;CAOR"}
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();
@@ -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,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"}
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;YAwFO,YAAY;UACd,YAAY;;;cA8DpC;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"}
@@ -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);
@@ -42,14 +43,17 @@ export function computeWallVerticalBounds(interpreter, tilesGeometry, row, col,
42
43
  neighborCol--;
43
44
  else if (parsed.dir === 'right')
44
45
  neighborCol++;
45
- const [worldCellCenterX, worldCellCenterZ] = interpreter.getWorldCellCenter(row, col);
46
46
  const localWallX = ('xOffset' in config ? config.xOffset : 0) * cellSize;
47
47
  const localWallZ = ('zOffset' in config ? config.zOffset : 0) * cellSize;
48
- const worldWallX = worldCellCenterX + localWallX;
49
- const worldWallZ = worldCellCenterZ + localWallZ;
48
+ const { x: worldWallX, z: worldWallZ } = interpreter.gridToWorld({
49
+ row,
50
+ col,
51
+ offsetX: localWallX,
52
+ offsetZ: localWallZ,
53
+ });
50
54
  // Determine layer context
51
- const layersBefore = interpreter.countCalls([row, col], isLayer, 0, wallIdx);
52
- const layersAfter = interpreter.countCalls([row, col], isLayer, wallIdx + 1);
55
+ const layersBefore = interpreter.countCalls({ row, col }, isLayer, 0, wallIdx);
56
+ const layersAfter = interpreter.countCalls({ row, col }, isLayer, wallIdx + 1);
53
57
  const resolveLayer = (explicitY, isTop) => {
54
58
  // Case 1: No explicit height -> connect to adjacent layer in current cell
55
59
  if (explicitY === undefined) {
@@ -72,7 +76,7 @@ export function computeWallVerticalBounds(interpreter, tilesGeometry, row, col,
72
76
  const checkCell = (r, c) => {
73
77
  if (r < 0 || r >= rows || c < 0 || c >= cols)
74
78
  return;
75
- const calls = interpreter.getCalls([r, c], { ground: groundSchema, ceiling: ceilingSchema });
79
+ const calls = interpreter.getCalls({ row: r, col: c }, { ground: groundSchema, ceiling: ceilingSchema });
76
80
  for (let i = 0; i < calls.length; i++) {
77
81
  const [, { y: layerY }] = calls[i];
78
82
  const diff = Math.abs(layerY - explicitY);
@@ -119,7 +123,7 @@ export function computeWallVerticalBounds(interpreter, tilesGeometry, row, col,
119
123
  if (bottomLayer) {
120
124
  const offX = sX + (col - bottomLayer.col) * cellSize;
121
125
  const offZ = sZ + (row - bottomLayer.row) * cellSize;
122
- yStart[k] = tilesGeometry.getHeight(bottomLayer.row, bottomLayer.col, bottomLayer.layerIdx, offX, offZ);
126
+ yStart[k] = tilesGeometry.getHeightAt({ row: bottomLayer.row, col: bottomLayer.col, offsetX: offX, offsetZ: offZ }, { stackIndex: bottomLayer.layerIdx });
123
127
  }
124
128
  else {
125
129
  yStart[k] = parsed.bottomY;
@@ -128,7 +132,7 @@ export function computeWallVerticalBounds(interpreter, tilesGeometry, row, col,
128
132
  if (topLayer) {
129
133
  const offX = sX + (col - topLayer.col) * cellSize;
130
134
  const offZ = sZ + (row - topLayer.row) * cellSize;
131
- yEnd[k] = tilesGeometry.getHeight(topLayer.row, topLayer.col, topLayer.layerIdx, offX, offZ);
135
+ yEnd[k] = tilesGeometry.getHeightAt({ row: topLayer.row, col: topLayer.col, offsetX: offX, offsetZ: offZ }, { stackIndex: topLayer.layerIdx });
132
136
  }
133
137
  else {
134
138
  yEnd[k] = parsed.topY;
@@ -175,7 +179,7 @@ export class WallMesh extends Mesh {
175
179
  // Validation loop runs in both modes
176
180
  for (let row = 0; row < rows; row++) {
177
181
  for (let col = 0; col < cols; col++) {
178
- const entries = interpreter.getCalls([row, col], {
182
+ const entries = interpreter.getCalls({ row, col }, {
179
183
  wall: wallSchema,
180
184
  window: windowSchema,
181
185
  door: doorSchema,
@@ -385,6 +389,9 @@ export class WallMesh extends Mesh {
385
389
  const textureArray = buildTextureArrayFromAssets(usedTextures);
386
390
  buildWallMeshMaterial(this.material, textureArray);
387
391
  }
392
+ // Setup BVH for accelerated raycasting
393
+ setupBVH(this.geometry);
394
+ this.raycast = acceleratedRaycast;
388
395
  }
389
396
  }
390
397
  export function isLayerConnectedToWall(wallTopY, wallBotY, y, tileSize) {
@@ -1,5 +1,7 @@
1
- import { Mesh } from "three";
2
- import { Interpreter } from "../interpreter.js";
1
+ import { Mesh } from 'three';
2
+ import { Interpreter } from '../interpreter.js';
3
+ import { GetHeightAtOptions, TilesGeometry } from '../tiles/geometry.js';
4
+ import { GridCoord } from '../schemas.js';
3
5
  export type WaterMeshOptions = {
4
6
  /**
5
7
  * Validate-only mode: validates water calls but skips geometry creation.
@@ -7,10 +9,11 @@ export type WaterMeshOptions = {
7
9
  */
8
10
  validateOnly?: boolean;
9
11
  };
10
- export declare class WaterMesh extends Mesh {
12
+ export declare class WaterMesh extends Mesh<TilesGeometry> {
11
13
  constructor(interpreter: Interpreter, options?: WaterMeshOptions);
14
+ getHeightAt(coord: GridCoord, options?: GetHeightAtOptions): number;
12
15
  dispose(): void;
13
16
  }
14
- export * from "./texture.js";
15
- export * from "./material.js";
17
+ export * from './texture.js';
18
+ export * from './material.js';
16
19
  //# sourceMappingURL=index.d.ts.map
@@ -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,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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/water/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAY,MAAM,OAAO,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAa,MAAM,sBAAsB,CAAA;AACnF,OAAO,EAAE,SAAS,EAAe,MAAM,eAAe,CAAA;AAGtD,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,qBAAa,SAAU,SAAQ,IAAI,CAAC,aAAa,CAAC;gBACpC,WAAW,EAAE,WAAW,EAAE,OAAO,GAAE,gBAAqB;IAsDpE,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,kBAAkB;IAI1D,OAAO,IAAI,IAAI;CAMhB;AAED,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA"}
@@ -1,6 +1,7 @@
1
- import { Mesh, Material } from "three";
2
- import { TilesGeometry } from "../tiles/geometry.js";
3
- import { waterSchema } from "../schemas.js";
1
+ import { Mesh, Material } from 'three';
2
+ import { TilesGeometry } from '../tiles/geometry.js';
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();
@@ -8,20 +9,18 @@ export class WaterMesh extends Mesh {
8
9
  // Build tiles only in non-validateOnly mode (expensive allocation)
9
10
  let tiles;
10
11
  if (!options.validateOnly) {
11
- tiles = new Array(rows)
12
- .fill(undefined)
13
- .map(() => new Array(cols).fill(undefined).map(() => []));
12
+ tiles = new Array(rows).fill(undefined).map(() => new Array(cols).fill(undefined).map(() => []));
14
13
  }
15
14
  // Validation loop runs in both modes
16
15
  for (let row = 0; row < rows; row++) {
17
16
  for (let col = 0; col < cols; col++) {
18
- const entries = interpreter.getCalls([row, col], { water: waterSchema });
17
+ const entries = interpreter.getCalls({ row, col }, { water: waterSchema });
19
18
  // Only build tile stack in non-validateOnly mode
20
19
  if (!options.validateOnly) {
21
20
  const stack = [];
22
21
  for (const [, parsed] of entries) {
23
22
  stack.push({
24
- type: "water",
23
+ type: 'water',
25
24
  y: parsed.y,
26
25
  textureId: 0,
27
26
  row,
@@ -41,10 +40,16 @@ export class WaterMesh extends Mesh {
41
40
  const mapSizeX = cols * cellSize;
42
41
  const mapSizeZ = rows * cellSize;
43
42
  const geometry = new TilesGeometry(tiles, [], mapSizeX, mapSizeZ);
44
- interpreter.setAsset("waterGeometry", geometry);
43
+ interpreter.setAsset('waterGeometry', geometry);
45
44
  super(geometry);
46
45
  this.renderOrder = 1;
47
46
  this.frustumCulled = true;
47
+ // Setup BVH for accelerated raycasting
48
+ setupBVH(this.geometry);
49
+ this.raycast = acceleratedRaycast;
50
+ }
51
+ getHeightAt(coord, options) {
52
+ return this.geometry.getHeightAt(coord, options);
48
53
  }
49
54
  dispose() {
50
55
  this.geometry.dispose();
@@ -53,5 +58,5 @@ export class WaterMesh extends Mesh {
53
58
  }
54
59
  }
55
60
  }
56
- export * from "./texture.js";
57
- export * from "./material.js";
61
+ export * from './texture.js';
62
+ export * from './material.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawcall/charta",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
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": {