@gmvjs/torque3d 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -1,18 +1,20 @@
1
1
  # @gmvjs/torque3d — Torque3D (BeamNG)
2
2
 
3
- Torque3D — движок BeamNG.drive: конфигурации `.pc` → слоты jbeam → flexbody из `.dae` с материалами и покраской.
3
+ [![Patreon](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/cw/shibisty)
4
4
 
5
- Плагин движка для [`@gmvjs/core`](../core/README.md): форматы, архивы и пресеты игр. Тяжёлые разборщики загружаются по требованию (`import()` внутри `load`), так что подключение пакета почти ничего не стоит, пока файл этого движка не открыт.
5
+ Torque3D, the BeamNG.drive engine: `.pc` configurations → jbeam slots → flexbodies from `.dae` with materials and paint.
6
6
 
7
- ## Установка
7
+ An engine plugin for [`@gmvjs/core`](../core/README.md): formats, archives and game presets. Heavy parsers are loaded on demand (`import()` inside `load`), so adding the package costs almost nothing until a file of this engine is opened.
8
+
9
+ ## Installation
8
10
 
9
11
  ```sh
10
12
  npm i @gmvjs/core @gmvjs/torque3d @gmvjs/collada
11
13
  ```
12
14
 
13
- ## Подключение
15
+ ## Usage
14
16
 
15
- Весь движок (все игры):
17
+ The whole engine (all games):
16
18
 
17
19
  ```ts
18
20
  import { createRegistry, ViewerSession } from '@gmvjs/core';
@@ -21,7 +23,7 @@ import { torque3dEngine } from '@gmvjs/torque3d';
21
23
  const session = new ViewerSession(createRegistry([torque3dEngine]));
22
24
  ```
23
25
 
24
- Одна игра — подпуть пакета (пресет игры и движок, ограниченный этой игрой):
26
+ One game: a package subpath (the game preset and an engine limited to that game):
25
27
 
26
28
  ```ts
27
29
  import { createRegistry, ViewerSession } from '@gmvjs/core';
@@ -30,27 +32,31 @@ import beamng from '@gmvjs/torque3d/beamng';
30
32
  const session = new ViewerSession(createRegistry([beamng.engine]), beamng.preset.id);
31
33
  ```
32
34
 
33
- ## Игры (подпути)
35
+ ## Games (subpaths)
34
36
 
35
- | Импорт | Пресет | Игра | Статус | Особенности |
37
+ | Import | Preset | Game | Status | Notes |
36
38
  |---|---|---|---|---|
37
- | `@gmvjs/torque3d/beamng` | `beamng` | BeamNG.drive | 🟡 бета | — |
39
+ | `@gmvjs/torque3d/beamng` | `beamng` | BeamNG.drive | 🟡 beta | — |
38
40
 
39
- ## Форматы
41
+ ## Formats
40
42
 
41
- | id | Что | Файлы | Статус |
43
+ | id | What | Files | Status |
42
44
  |---|---|---|---|
43
- | `torque3d-pc` | BeamNG: конфигурация машины (.pc) | `.pc` | 🟡 бета |
45
+ | `torque3d-pc` | BeamNG: vehicle configuration (.pc) | `.pc` | 🟡 beta |
44
46
 
45
- ## Зависимости
47
+ ## Dependencies
46
48
 
47
- - `@gmvjs/core` — API плагинов, типы, архивы ZIP/папка, zlib, DDS/BC, геометрия
48
- - `@gmvjs/collada` — разбор `.dae` (flexbody)
49
+ - `@gmvjs/core` — plugin API, types, ZIP/folder archives, zlib, DDS/BC, geometry
50
+ - `@gmvjs/collada` — `.dae` parsing (flexbody)
49
51
 
50
- ## Не сделано
52
+ ## Not done yet
51
53
 
52
- - Другие игры на Torque3D (DTS/DIF)
54
+ - Other Torque3D games (DTS/DIF)
53
55
 
54
- ## Лицензия
56
+ ## License
55
57
 
56
58
  MIT
59
+
60
+ [![Patreon](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/cw/shibisty)
61
+
62
+ If this project helps you, consider supporting its development on Patreon ❤️
@@ -1 +1 @@
1
- {"version":3,"file":"beamng.js","sources":["../../src/games/beamng.ts"],"sourcesContent":["// BeamNG.drive — подпуть `@gmvjs/torque3d/beamng`: пресет игры и движок только с ней.\nimport { pickGames } from '@gmvjs/core/plugin';\nimport { torque3dEngine } from '../index';\n\nexport const engine = pickGames(torque3dEngine, 'beamng');\nexport const preset = engine.games![0]!;\nexport default { engine, preset };\n"],"names":[],"mappings":";;AAIO,MAAM,SAAS,UAAU,gBAAgB,QAAQ;AACjD,MAAM,SAAS,OAAO,MAAO,CAAC;AACrC,MAAA,SAAe,EAAE,QAAQ,OAAA;"}
1
+ {"version":3,"file":"beamng.js","sources":["../../src/games/beamng.ts"],"sourcesContent":["// BeamNG.drive — subpath `@gmvjs/torque3d/beamng`: the game preset and an engine limited to it.\nimport { pickGames } from '@gmvjs/core/plugin';\nimport { torque3dEngine } from '../index';\n\nexport const engine = pickGames(torque3dEngine, 'beamng');\nexport const preset = engine.games![0]!;\nexport default { engine, preset };\n"],"names":[],"mappings":";;AAIO,MAAM,SAAS,UAAU,gBAAgB,QAAQ;AACjD,MAAM,SAAS,OAAO,MAAO,CAAC;AACrC,MAAA,SAAe,EAAE,QAAQ,OAAA;"}
package/dist/i18n.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.js","sources":["../src/i18n.ts"],"sourcesContent":["// Английские тексты движка torque3d (русские — в самих плагинах).\nimport type { EngineTexts } from '@gmvjs/core/plugin';\n\nexport const EN: EngineTexts = {\n \"presets\": {\n \"beamng\": {\n \"hint\": \"Open a mod (.zip) or the vehicles/<car> folder with \\\"Game folder\\\" and pick a .pc configuration. Parts missing from the mod (shared game parts) are found if the game's content/vehicles is open too.\",\n \"archiveLabel\": \"Open mod (.zip)…\"\n }\n }\n};\n"],"names":[],"mappings":"AAGO,MAAM,KAAkB;AAAA,EAC7B,WAAW;AAAA,IACT,UAAU;AAAA,MACR,QAAQ;AAAA,MACR,gBAAgB;AAAA,IAAA;AAAA,EAClB;AAEJ;"}
1
+ {"version":3,"file":"i18n.js","sources":["../src/i18n.ts"],"sourcesContent":["// English texts of the torque3d engine (Russian ones live in the plugins themselves).\nimport type { EngineTexts } from '@gmvjs/core/plugin';\n\nexport const EN: EngineTexts = {\n \"presets\": {\n \"beamng\": {\n \"hint\": \"Open a mod (.zip) or the vehicles/<car> folder with \\\"Game folder\\\" and pick a .pc configuration. Parts missing from the mod (shared game parts) are found if the game's content/vehicles is open too.\",\n \"archiveLabel\": \"Open mod (.zip)…\"\n }\n }\n};\n"],"names":[],"mappings":"AAGO,MAAM,KAAkB;AAAA,EAC7B,WAAW;AAAA,IACT,UAAU;AAAA,MACR,QAAQ;AAAA,MACR,gBAAgB;AAAA,IAAA;AAAA,EAClB;AAEJ;"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["// BeamNG.drive: машина собирается из .pc (конфигурация) + .jbeam (детали и слоты) + .dae (меши flexbody) + *.materials.json.\nimport { defineEngine } from '@gmvjs/core/plugin';\nimport { EN } from './i18n';\n\nexport const torque3dEngine = defineEngine({\n id: 'torque3d',\n title: 'Torque3D (BeamNG)',\n i18n: { en: EN },\n formats: [\n {\n id: 'torque3d-pc', title: 'BeamNG: конфигурация машины (.pc)', engine: 'torque3d', extensions: ['pc'], status: 'beta',\n sniff: (head) => {\n const t = new TextDecoder().decode(head);\n return /^\\s*\\{/.test(t) && /\"(parts|mainPartName|format|model)\"\\s*:/.test(t) ? 1 : 0;\n },\n load: async (main, ctx) => (await import('./vehicle')).loadVehicle(main, ctx),\n },\n ],\n materials: [\n {\n id: 'torque3d-materials',\n async resolve(mesh, lib) {\n if (!mesh.material) return null;\n const { collectMaterials } = await import('./vehicle');\n const mats = await lib.memo('torque3d-materials', () => collectMaterials(lib.archives));\n const t = mats.get(mesh.material.toLowerCase());\n return t?.color ? lib.find(t.color) : null;\n },\n },\n ],\n games: [\n {\n id: 'beamng', group: 'Игры', title: 'BeamNG.drive', engine: 'torque3d', formats: ['torque3d-pc'], status: 'beta', ui: { zUp: true, archives: true, archiveExts: ['zip'], archiveLabel: 'Открыть мод (.zip)…' }, markers: ['beamng.drive.exe'],\n hint: 'Откройте мод (.zip) или папку vehicles/<машина> через «Папку игры» и выберите конфигурацию .pc. Детали, которых нет в моде (общие из игры), подтянутся, если открыт и content/vehicles игры.',\n },\n ],\n});\n"],"names":[],"mappings":";;AAIO,MAAM,iBAAiB,aAAa;AAAA,EACzC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM,EAAE,IAAI,GAAA;AAAA,EACZ,SAAS;AAAA,IACP;AAAA,MACE,IAAI;AAAA,MAAe,OAAO;AAAA,MAAqC,QAAQ;AAAA,MAAY,YAAY,CAAC,IAAI;AAAA,MAAG,QAAQ;AAAA,MAC/G,OAAO,CAAC,SAAS;AACf,cAAM,IAAI,IAAI,cAAc,OAAO,IAAI;AACvC,eAAO,SAAS,KAAK,CAAC,KAAK,0CAA0C,KAAK,CAAC,IAAI,IAAI;AAAA,MACrF;AAAA,MACA,MAAM,OAAO,MAAM,SAAS,MAAM,OAAO,cAAW,GAAG,YAAY,MAAM,GAAG;AAAA,IAAA;AAAA,EAC9E;AAAA,EAEF,WAAW;AAAA,IACT;AAAA,MACE,IAAI;AAAA,MACJ,MAAM,QAAQ,MAAM,KAAK;AACvB,YAAI,CAAC,KAAK,SAAU,QAAO;AAC3B,cAAM,EAAE,iBAAA,IAAqB,MAAM,OAAO,cAAW;AACrD,cAAM,OAAO,MAAM,IAAI,KAAK,sBAAsB,MAAM,iBAAiB,IAAI,QAAQ,CAAC;AACtF,cAAM,IAAI,KAAK,IAAI,KAAK,SAAS,aAAa;AAC9C,eAAO,GAAG,QAAQ,IAAI,KAAK,EAAE,KAAK,IAAI;AAAA,MACxC;AAAA,IAAA;AAAA,EACF;AAAA,EAEF,OAAO;AAAA,IACL;AAAA,MACE,IAAI;AAAA,MAAU,OAAO;AAAA,MAAQ,OAAO;AAAA,MAAgB,QAAQ;AAAA,MAAY,SAAS,CAAC,aAAa;AAAA,MAAG,QAAQ;AAAA,MAAQ,IAAI,EAAE,KAAK,MAAM,UAAU,MAAM,aAAa,CAAC,KAAK,GAAG,cAAc,sBAAA;AAAA,MAAyB,SAAS,CAAC,kBAAkB;AAAA,MAC5O,MAAM;AAAA,IAAA;AAAA,EACR;AAEJ,CAAC;"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["// BeamNG.drive: a vehicle is assembled from .pc (configuration) + .jbeam (parts and slots) + .dae (flexbody meshes) + *.materials.json.\nimport { defineEngine } from '@gmvjs/core/plugin';\nimport { EN } from './i18n';\n\nexport const torque3dEngine = defineEngine({\n id: 'torque3d',\n title: 'Torque3D (BeamNG)',\n i18n: { en: EN },\n formats: [\n {\n id: 'torque3d-pc', title: 'BeamNG: конфигурация машины (.pc)', engine: 'torque3d', extensions: ['pc'], status: 'beta',\n sniff: (head) => {\n const t = new TextDecoder().decode(head);\n return /^\\s*\\{/.test(t) && /\"(parts|mainPartName|format|model)\"\\s*:/.test(t) ? 1 : 0;\n },\n load: async (main, ctx) => (await import('./vehicle')).loadVehicle(main, ctx),\n },\n ],\n materials: [\n {\n id: 'torque3d-materials',\n async resolve(mesh, lib) {\n if (!mesh.material) return null;\n const { collectMaterials } = await import('./vehicle');\n const mats = await lib.memo('torque3d-materials', () => collectMaterials(lib.archives));\n const t = mats.get(mesh.material.toLowerCase());\n return t?.color ? lib.find(t.color) : null;\n },\n },\n ],\n games: [\n {\n id: 'beamng', group: 'Игры', title: 'BeamNG.drive', engine: 'torque3d', formats: ['torque3d-pc'], status: 'beta', ui: { zUp: true, archives: true, archiveExts: ['zip'], archiveLabel: 'Открыть мод (.zip)…' }, markers: ['beamng.drive.exe'],\n hint: 'Откройте мод (.zip) или папку vehicles/<машина> через «Папку игры» и выберите конфигурацию .pc. Детали, которых нет в моде (общие из игры), подтянутся, если открыт и content/vehicles игры.',\n },\n ],\n});\n"],"names":[],"mappings":";;AAIO,MAAM,iBAAiB,aAAa;AAAA,EACzC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM,EAAE,IAAI,GAAA;AAAA,EACZ,SAAS;AAAA,IACP;AAAA,MACE,IAAI;AAAA,MAAe,OAAO;AAAA,MAAqC,QAAQ;AAAA,MAAY,YAAY,CAAC,IAAI;AAAA,MAAG,QAAQ;AAAA,MAC/G,OAAO,CAAC,SAAS;AACf,cAAM,IAAI,IAAI,cAAc,OAAO,IAAI;AACvC,eAAO,SAAS,KAAK,CAAC,KAAK,0CAA0C,KAAK,CAAC,IAAI,IAAI;AAAA,MACrF;AAAA,MACA,MAAM,OAAO,MAAM,SAAS,MAAM,OAAO,cAAW,GAAG,YAAY,MAAM,GAAG;AAAA,IAAA;AAAA,EAC9E;AAAA,EAEF,WAAW;AAAA,IACT;AAAA,MACE,IAAI;AAAA,MACJ,MAAM,QAAQ,MAAM,KAAK;AACvB,YAAI,CAAC,KAAK,SAAU,QAAO;AAC3B,cAAM,EAAE,iBAAA,IAAqB,MAAM,OAAO,cAAW;AACrD,cAAM,OAAO,MAAM,IAAI,KAAK,sBAAsB,MAAM,iBAAiB,IAAI,QAAQ,CAAC;AACtF,cAAM,IAAI,KAAK,IAAI,KAAK,SAAS,aAAa;AAC9C,eAAO,GAAG,QAAQ,IAAI,KAAK,EAAE,KAAK,IAAI;AAAA,MACxC;AAAA,IAAA;AAAA,EACF;AAAA,EAEF,OAAO;AAAA,IACL;AAAA,MACE,IAAI;AAAA,MAAU,OAAO;AAAA,MAAQ,OAAO;AAAA,MAAgB,QAAQ;AAAA,MAAY,SAAS,CAAC,aAAa;AAAA,MAAG,QAAQ;AAAA,MAAQ,IAAI,EAAE,KAAK,MAAM,UAAU,MAAM,aAAa,CAAC,KAAK,GAAG,cAAc,sBAAA;AAAA,MAAyB,SAAS,CAAC,kBAAkB;AAAA,MAC5O,MAAM;AAAA,IAAA;AAAA,EACR;AAEJ,CAAC;"}
package/dist/jbeam.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  export type JValue = string | number | boolean | null | JValue[] | {
2
2
  [k: string]: JValue;
3
3
  };
4
- /** Разбор jbeam/pc/materials.json: комментарии // и /* *\/, запятые необязательны. */
4
+ /** Parsing jbeam/pc/materials.json: // and /* *\/ comments, commas are optional. */
5
5
  export declare function parseJbeam(text: string): JValue;
6
6
  export type Vars = Map<string, number | string | boolean | null>;
7
7
  type V = number | string | boolean | null;
8
- /** Значение поля jbeam: число, "$var" или "$=выражение". */
8
+ /** A jbeam field value: a number, "$var" or "$=expression". */
9
9
  export declare function evalValue(v: JValue | undefined, vars: Vars): V;
10
10
  export declare const evalNumber: (v: JValue | undefined, vars: Vars, def?: number) => number;
11
11
  export declare function evalExpr(src: string, vars: Vars): V;
package/dist/jbeam.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"jbeam.js","sources":["../src/jbeam.ts"],"sourcesContent":["// JBeam (BeamNG.drive): «JSON с вольностями» — комментарии, висячие и пропущенные запятые. Плюс вычисление выражений \"$=…\".\n\nexport type JValue = string | number | boolean | null | JValue[] | { [k: string]: JValue };\n\n/** Разбор jbeam/pc/materials.json: комментарии // и /* *\\/, запятые необязательны. */\nexport function parseJbeam(text: string): JValue {\n let i = 0;\n const n = text.length;\n const skip = () => {\n for (;;) {\n while (i < n && /[\\s,]/.test(text[i]!)) i++;\n if (text[i] === '/' && text[i + 1] === '/') { while (i < n && text[i] !== '\\n') i++; continue; }\n if (text[i] === '/' && text[i + 1] === '*') { const e = text.indexOf('*/', i + 2); i = e < 0 ? n : e + 2; continue; }\n break;\n }\n };\n const err = (msg: string) => { const line = text.slice(0, i).split('\\n').length; return new Error(`jbeam: ${msg} (строка ${line})`); };\n const value = (): JValue => {\n skip();\n const c = text[i];\n if (c === '{') {\n i++;\n const o: { [k: string]: JValue } = {};\n for (;;) {\n skip();\n if (text[i] === '}') { i++; return o; }\n if (i >= n) throw err('незакрытый объект');\n const k = value();\n skip();\n if (text[i] !== ':') throw err('ожидалось «:»');\n i++;\n o[String(k)] = value();\n }\n }\n if (c === '[') {\n i++;\n const a: JValue[] = [];\n for (;;) {\n skip();\n if (text[i] === ']') { i++; return a; }\n if (i >= n) throw err('незакрытый массив');\n a.push(value());\n }\n }\n if (c === '\"') {\n let s = '';\n i++;\n while (i < n && text[i] !== '\"') {\n if (text[i] === '\\\\') { const e = text[i + 1]!; s += e === 'n' ? '\\n' : e === 't' ? '\\t' : e; i += 2; }\n else s += text[i++];\n }\n i++;\n return s;\n }\n const m = /^(-?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][-+]?\\d+)?|true|false|null|nil|-?inf|nan)/i.exec(text.slice(i, i + 40));\n if (!m) throw err(`неожиданный символ «${c ?? 'конец'}»`);\n i += m[0].length;\n const t = m[0].toLowerCase();\n if (t === 'true') return true;\n if (t === 'false') return false;\n if (t === 'null' || t === 'nil') return null;\n if (t === 'inf') return Infinity;\n if (t === '-inf') return -Infinity;\n if (t === 'nan') return NaN;\n return +m[0];\n };\n const v = value();\n return v;\n}\n\n// ---------- выражения \"$=…\" (подмножество Lua, как в BeamNG) ----------\nexport type Vars = Map<string, number | string | boolean | null>;\ntype V = number | string | boolean | null;\n\n/** Значение поля jbeam: число, \"$var\" или \"$=выражение\". */\nexport function evalValue(v: JValue | undefined, vars: Vars): V {\n if (typeof v !== 'string') return (v ?? null) as V;\n if (v.startsWith('$=')) return evalExpr(v.slice(2), vars);\n if (/^\\$[A-Za-z_]\\w*$/.test(v)) return vars.has(v) ? vars.get(v)! : null;\n return v;\n}\nexport const evalNumber = (v: JValue | undefined, vars: Vars, def = 0): number => {\n const r = evalValue(v, vars);\n return typeof r === 'number' && Number.isFinite(r) ? r : typeof r === 'boolean' ? +r : def;\n};\n\nconst FUNCS: Record<string, (...a: V[]) => V> = {\n case: (...a) => { for (let k = 0; k + 1 < a.length; k += 2) if (a[k] !== null && a[k] !== false) return a[k + 1]!; return a.length % 2 ? a[a.length - 1]! : null; },\n min: (...a) => Math.min(...a.map(Number)),\n max: (...a) => Math.max(...a.map(Number)),\n abs: (a) => Math.abs(Number(a)),\n clamp: (x, lo, hi) => Math.min(Number(hi), Math.max(Number(lo), Number(x))),\n round: (x) => Math.round(Number(x)),\n floor: (x) => Math.floor(Number(x)),\n ceil: (x) => Math.ceil(Number(x)),\n sqrt: (x) => Math.sqrt(Number(x)),\n sin: (x) => Math.sin(Number(x)),\n cos: (x) => Math.cos(Number(x)),\n tan: (x) => Math.tan(Number(x)),\n rad: (x) => (Number(x) * Math.PI) / 180,\n deg: (x) => (Number(x) * 180) / Math.PI,\n};\n\nexport function evalExpr(src: string, vars: Vars): V {\n const toks = src.match(/\\$?[A-Za-z_][\\w.]*|\\d+\\.?\\d*(?:[eE][-+]?\\d+)?|\\.\\d+|==|~=|!=|<=|>=|&&|\\|\\||[-+*/%^()<>,!?:]|\"[^\"]*\"/g) ?? [];\n let p = 0;\n const peek = () => toks[p], next = () => toks[p++];\n const truthy = (v: V) => v !== null && v !== false;\n const num = (v: V) => (typeof v === 'number' ? v : typeof v === 'boolean' ? +v : Number(v) || 0);\n const primary = (): V => {\n const t = next();\n if (t === undefined) return null;\n if (t === '(') { const v = ternary(); next(); return v; }\n if (t === '-') return -num(unary());\n if (t === 'not' || t === '!') return !truthy(unary());\n if (t.startsWith('\"')) return t.slice(1, -1);\n if (/^[\\d.]/.test(t)) return +t;\n if (t.startsWith('$')) return vars.has(t) ? vars.get(t)! : null;\n if (t === 'nil') return null;\n if (t === 'true') return true;\n if (t === 'false') return false;\n if (peek() === '(') {\n next();\n const args: V[] = [];\n while (peek() !== ')' && p < toks.length) { args.push(ternary()); if (peek() === ',') next(); }\n next();\n const f = FUNCS[t.replace(/^math\\./, '')];\n return f ? f(...args) : null;\n }\n return null;\n };\n const unary = (): V => primary();\n const power = (): V => { let a = unary(); while (peek() === '^') { next(); a = num(a) ** num(unary()); } return a; };\n const mul = (): V => {\n let a = power();\n for (let t = peek(); t === '*' || t === '/' || t === '%'; t = peek()) { next(); const b = num(power()); a = t === '*' ? num(a) * b : t === '/' ? num(a) / b : num(a) % b; }\n return a;\n };\n const add = (): V => { let a = mul(); for (let t = peek(); t === '+' || t === '-'; t = peek()) { next(); const b = num(mul()); a = t === '+' ? num(a) + b : num(a) - b; } return a; };\n const cmp = (): V => {\n let a = add();\n for (let t = peek(); t && ['==', '~=', '!=', '<', '>', '<=', '>='].includes(t); t = peek()) {\n next();\n const b = add();\n a = t === '==' ? a === b : t === '~=' || t === '!=' ? a !== b : t === '<' ? num(a) < num(b) : t === '>' ? num(a) > num(b) : t === '<=' ? num(a) <= num(b) : num(a) >= num(b);\n }\n return a;\n };\n const and = (): V => { let a = cmp(); while (peek() === 'and' || peek() === '&&') { next(); const b = cmp(); a = truthy(a) ? b : a; } return a; };\n const or = (): V => { let a = and(); while (peek() === 'or' || peek() === '||') { next(); const b = and(); a = truthy(a) ? a : b; } return a; };\n const ternary = (): V => { const c = or(); if (peek() === '?') { next(); const a = ternary(); next(); const b = ternary(); return truthy(c) ? a : b; } return c; };\n return ternary();\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jbeam.js","sources":["../src/jbeam.ts"],"sourcesContent":["// JBeam (BeamNG.drive): \"JSON with liberties\" (comments, trailing and missing commas). Plus evaluation of \"$=…\" expressions.\n\nexport type JValue = string | number | boolean | null | JValue[] | { [k: string]: JValue };\n\n/** Parsing jbeam/pc/materials.json: // and /* *\\/ comments, commas are optional. */\nexport function parseJbeam(text: string): JValue {\n let i = 0;\n const n = text.length;\n const skip = () => {\n for (;;) {\n while (i < n && /[\\s,]/.test(text[i]!)) i++;\n if (text[i] === '/' && text[i + 1] === '/') { while (i < n && text[i] !== '\\n') i++; continue; }\n if (text[i] === '/' && text[i + 1] === '*') { const e = text.indexOf('*/', i + 2); i = e < 0 ? n : e + 2; continue; }\n break;\n }\n };\n const err = (msg: string) => { const line = text.slice(0, i).split('\\n').length; return new Error(`jbeam: ${msg} (строка ${line})`); };\n const value = (): JValue => {\n skip();\n const c = text[i];\n if (c === '{') {\n i++;\n const o: { [k: string]: JValue } = {};\n for (;;) {\n skip();\n if (text[i] === '}') { i++; return o; }\n if (i >= n) throw err('незакрытый объект');\n const k = value();\n skip();\n if (text[i] !== ':') throw err('ожидалось «:»');\n i++;\n o[String(k)] = value();\n }\n }\n if (c === '[') {\n i++;\n const a: JValue[] = [];\n for (;;) {\n skip();\n if (text[i] === ']') { i++; return a; }\n if (i >= n) throw err('незакрытый массив');\n a.push(value());\n }\n }\n if (c === '\"') {\n let s = '';\n i++;\n while (i < n && text[i] !== '\"') {\n if (text[i] === '\\\\') { const e = text[i + 1]!; s += e === 'n' ? '\\n' : e === 't' ? '\\t' : e; i += 2; }\n else s += text[i++];\n }\n i++;\n return s;\n }\n const m = /^(-?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][-+]?\\d+)?|true|false|null|nil|-?inf|nan)/i.exec(text.slice(i, i + 40));\n if (!m) throw err(`неожиданный символ «${c ?? 'конец'}»`);\n i += m[0].length;\n const t = m[0].toLowerCase();\n if (t === 'true') return true;\n if (t === 'false') return false;\n if (t === 'null' || t === 'nil') return null;\n if (t === 'inf') return Infinity;\n if (t === '-inf') return -Infinity;\n if (t === 'nan') return NaN;\n return +m[0];\n };\n const v = value();\n return v;\n}\n\n// ---------- \"$=…\" expressions (a Lua subset, as in BeamNG) ----------\nexport type Vars = Map<string, number | string | boolean | null>;\ntype V = number | string | boolean | null;\n\n/** A jbeam field value: a number, \"$var\" or \"$=expression\". */\nexport function evalValue(v: JValue | undefined, vars: Vars): V {\n if (typeof v !== 'string') return (v ?? null) as V;\n if (v.startsWith('$=')) return evalExpr(v.slice(2), vars);\n if (/^\\$[A-Za-z_]\\w*$/.test(v)) return vars.has(v) ? vars.get(v)! : null;\n return v;\n}\nexport const evalNumber = (v: JValue | undefined, vars: Vars, def = 0): number => {\n const r = evalValue(v, vars);\n return typeof r === 'number' && Number.isFinite(r) ? r : typeof r === 'boolean' ? +r : def;\n};\n\nconst FUNCS: Record<string, (...a: V[]) => V> = {\n case: (...a) => { for (let k = 0; k + 1 < a.length; k += 2) if (a[k] !== null && a[k] !== false) return a[k + 1]!; return a.length % 2 ? a[a.length - 1]! : null; },\n min: (...a) => Math.min(...a.map(Number)),\n max: (...a) => Math.max(...a.map(Number)),\n abs: (a) => Math.abs(Number(a)),\n clamp: (x, lo, hi) => Math.min(Number(hi), Math.max(Number(lo), Number(x))),\n round: (x) => Math.round(Number(x)),\n floor: (x) => Math.floor(Number(x)),\n ceil: (x) => Math.ceil(Number(x)),\n sqrt: (x) => Math.sqrt(Number(x)),\n sin: (x) => Math.sin(Number(x)),\n cos: (x) => Math.cos(Number(x)),\n tan: (x) => Math.tan(Number(x)),\n rad: (x) => (Number(x) * Math.PI) / 180,\n deg: (x) => (Number(x) * 180) / Math.PI,\n};\n\nexport function evalExpr(src: string, vars: Vars): V {\n const toks = src.match(/\\$?[A-Za-z_][\\w.]*|\\d+\\.?\\d*(?:[eE][-+]?\\d+)?|\\.\\d+|==|~=|!=|<=|>=|&&|\\|\\||[-+*/%^()<>,!?:]|\"[^\"]*\"/g) ?? [];\n let p = 0;\n const peek = () => toks[p], next = () => toks[p++];\n const truthy = (v: V) => v !== null && v !== false;\n const num = (v: V) => (typeof v === 'number' ? v : typeof v === 'boolean' ? +v : Number(v) || 0);\n const primary = (): V => {\n const t = next();\n if (t === undefined) return null;\n if (t === '(') { const v = ternary(); next(); return v; }\n if (t === '-') return -num(unary());\n if (t === 'not' || t === '!') return !truthy(unary());\n if (t.startsWith('\"')) return t.slice(1, -1);\n if (/^[\\d.]/.test(t)) return +t;\n if (t.startsWith('$')) return vars.has(t) ? vars.get(t)! : null;\n if (t === 'nil') return null;\n if (t === 'true') return true;\n if (t === 'false') return false;\n if (peek() === '(') {\n next();\n const args: V[] = [];\n while (peek() !== ')' && p < toks.length) { args.push(ternary()); if (peek() === ',') next(); }\n next();\n const f = FUNCS[t.replace(/^math\\./, '')];\n return f ? f(...args) : null;\n }\n return null;\n };\n const unary = (): V => primary();\n const power = (): V => { let a = unary(); while (peek() === '^') { next(); a = num(a) ** num(unary()); } return a; };\n const mul = (): V => {\n let a = power();\n for (let t = peek(); t === '*' || t === '/' || t === '%'; t = peek()) { next(); const b = num(power()); a = t === '*' ? num(a) * b : t === '/' ? num(a) / b : num(a) % b; }\n return a;\n };\n const add = (): V => { let a = mul(); for (let t = peek(); t === '+' || t === '-'; t = peek()) { next(); const b = num(mul()); a = t === '+' ? num(a) + b : num(a) - b; } return a; };\n const cmp = (): V => {\n let a = add();\n for (let t = peek(); t && ['==', '~=', '!=', '<', '>', '<=', '>='].includes(t); t = peek()) {\n next();\n const b = add();\n a = t === '==' ? a === b : t === '~=' || t === '!=' ? a !== b : t === '<' ? num(a) < num(b) : t === '>' ? num(a) > num(b) : t === '<=' ? num(a) <= num(b) : num(a) >= num(b);\n }\n return a;\n };\n const and = (): V => { let a = cmp(); while (peek() === 'and' || peek() === '&&') { next(); const b = cmp(); a = truthy(a) ? b : a; } return a; };\n const or = (): V => { let a = and(); while (peek() === 'or' || peek() === '||') { next(); const b = and(); a = truthy(a) ? a : b; } return a; };\n const ternary = (): V => { const c = or(); if (peek() === '?') { next(); const a = ternary(); next(); const b = ternary(); return truthy(c) ? a : b; } return c; };\n return ternary();\n}\n"],"names":[],"mappings":"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"}
package/dist/vehicle.d.ts CHANGED
@@ -27,7 +27,7 @@ export interface Flexbody {
27
27
  offsets: Obj[];
28
28
  }
29
29
  export declare function slotsOf(part: JPart): Slot[];
30
- /** Все детали из jbeam-файлов архивов (папка vehicles/ мода и, если открыта, игры). */
30
+ /** All parts from jbeam files in the archives (the mod's vehicles/ folder and, if open, the game's). */
31
31
  export declare function collectParts(archives: readonly Archive[]): Promise<Map<string, JPart>>;
32
32
  export interface Assembly {
33
33
  chosen: Chosen[];
@@ -36,9 +36,9 @@ export interface Assembly {
36
36
  substituted: string[];
37
37
  vars: Vars;
38
38
  }
39
- /** Обход дерева слотов от главной детали. pcParts: slotType -> имя детали ("" — пусто). */
39
+ /** Walk the slot tree from the main part. pcParts: slotType -> part name ("" = empty). */
40
40
  export declare function assemble(parts: Map<string, JPart>, mainPart: string, pcParts: Record<string, string>, pcVars?: Record<string, JValue>): Assembly;
41
- /** Копия меша с преобразованием flexbody и смещением узлов. */
41
+ /** Copy of a mesh with the flexbody transform and node offset applied. */
42
42
  export declare function placeMesh(src: ColladaMesh, fb: Flexbody, vars: Vars): Mesh;
43
43
  export interface PcFile {
44
44
  parts: Record<string, string>;
@@ -50,7 +50,7 @@ export interface PcFile {
50
50
  }>;
51
51
  }
52
52
  export declare function loadVehicle(main: VFile, ctx: LoadContext): Promise<Model>;
53
- /** color — карта цвета (пусто, если только цвет-множитель или заглушка generic_*); factor — baseColorFactor слоя 0. */
53
+ /** color = the color map (empty if only a color multiplier or a generic_* placeholder); factor = baseColorFactor of layer 0. */
54
54
  export interface MaterialTex {
55
55
  color: string;
56
56
  factor?: number[];
@@ -58,8 +58,8 @@ export interface MaterialTex {
58
58
  surface?: SurfaceInfo;
59
59
  }
60
60
  export declare function readMaterialsJson(text: string, out: Map<string, MaterialTex>): void;
61
- /** Старый формат TorqueScript: singleton Material(name) { mapTo = "x"; diffuseMap[0] = "…"; } */
61
+ /** The old TorqueScript format: singleton Material(name) { mapTo = "x"; diffuseMap[0] = "…"; } */
62
62
  export declare function readMaterialsCs(text: string, out: Map<string, MaterialTex>): void;
63
- /** Все материалы из открытых архивов (json важнее cs: читаем json первым). */
63
+ /** All materials from the open archives (json takes precedence over cs: json is read first). */
64
64
  export declare function collectMaterials(archives: readonly Archive[]): Promise<Map<string, MaterialTex>>;
65
65
  export { evalValue };
@@ -1 +1 @@
1
- {"version":3,"file":"vehicle.js","sources":["../src/vehicle.ts"],"sourcesContent":["// Сборка машины BeamNG.drive по конфигурации .pc: дерево слотов jbeam -> выбранные детали -> flexbody-меши из .dae.\n// Положение мешей: pos/rot/scale flexbody + nodeOffset слота (смещение узлов детали; X зеркалится по стороне меша).\nimport { decodeText } from '@gmvjs/core/binary';\nimport { boundsOf, sumTris, sumVerts } from '@gmvjs/core/geometry';\nimport type { LoadContext } from '@gmvjs/core/plugin';\nimport type { Archive, ArchiveEntry, Mesh, Model, SurfaceInfo, VFile } from '@gmvjs/core/types';\nimport { parseCollada, type ColladaMesh } from '@gmvjs/collada';\nimport { evalNumber, evalValue, parseJbeam, type JValue, type Vars } from './jbeam';\n\ntype Obj = { [k: string]: JValue };\nconst isObj = (v: JValue | undefined): v is Obj => !!v && typeof v === 'object' && !Array.isArray(v);\n\nexport interface JPart { name: string; slotType: string; data: Obj; file: string }\ninterface Slot { type: string; def: string; opts: Obj | null }\ninterface Chosen { part: JPart; offsets: Obj[] }\nexport interface Flexbody { mesh: string; part: string; opts: Obj | null; offsets: Obj[] }\n\n/** Строки таблицы jbeam: первая — заголовок, словари между строками — модификаторы (пропускаем). */\nfunction rows(v: JValue | undefined): JValue[][] {\n if (!Array.isArray(v)) return [];\n return v.slice(1).filter((r): r is JValue[] => Array.isArray(r));\n}\nconst lastObj = (r: JValue[]): Obj | null => { for (let k = r.length - 1; k >= 0; k--) if (isObj(r[k])) return r[k] as Obj; return null; };\n\nexport function slotsOf(part: JPart): Slot[] {\n const out: Slot[] = [];\n for (const r of rows(part.data.slots)) out.push({ type: String(r[0] ?? ''), def: String(r[1] ?? ''), opts: lastObj(r) });\n // slots2: [\"name\", \"allowTypes\", \"denyTypes\", \"default\", \"description\"]\n for (const r of rows(part.data.slots2)) {\n const allow = Array.isArray(r[1]) ? (r[1] as JValue[]).map(String) : [String(r[0] ?? '')];\n for (const t of allow.length ? allow : [String(r[0] ?? '')]) out.push({ type: String(r[0] ?? t), def: String(r[3] ?? ''), opts: lastObj(r) });\n }\n return out;\n}\n\n/** Все детали из jbeam-файлов архивов (папка vehicles/ мода и, если открыта, игры). */\nexport async function collectParts(archives: readonly Archive[]): Promise<Map<string, JPart>> {\n const parts = new Map<string, JPart>();\n for (const a of archives)\n for (const e of a.entries) {\n if (!e.path.endsWith('.jbeam')) continue;\n let data: JValue;\n try { data = parseJbeam(decodeText(await a.read(e))); } catch { continue; }\n if (!isObj(data)) continue;\n for (const [name, v] of Object.entries(data)) if (isObj(v) && !parts.has(name)) parts.set(name, { name, slotType: String(v.slotType ?? ''), data: v, file: e.path });\n }\n return parts;\n}\n\nexport interface Assembly { chosen: Chosen[]; flexbodies: Flexbody[]; missing: string[]; substituted: string[]; vars: Vars }\n\n/** Обход дерева слотов от главной детали. pcParts: slotType -> имя детали (\"\" — пусто). */\nexport function assemble(parts: Map<string, JPart>, mainPart: string, pcParts: Record<string, string>, pcVars: Record<string, JValue> = {}): Assembly {\n const chosen: Chosen[] = [], missing: string[] = [], substituted: string[] = [], seen = new Set<string>();\n const bySlot = new Map<string, string[]>();\n for (const p of parts.values()) { if (!bySlot.has(p.slotType)) bySlot.set(p.slotType, []); bySlot.get(p.slotType)!.push(p.name); }\n const visit = (name: string, offsets: Obj[]) => {\n if (!name || seen.has(name)) return;\n const part = parts.get(name);\n if (!part) { missing.push(name); return; }\n seen.add(name);\n chosen.push({ part, offsets });\n for (const s of slotsOf(part)) {\n const pick = Object.prototype.hasOwnProperty.call(pcParts, s.type) ? pcParts[s.type]! : s.def;\n const off = s.opts && isObj(s.opts.nodeOffset) ? [...offsets, s.opts.nodeOffset as Obj] : offsets;\n // деталь из .pc не найдена (общая деталь игры) — берём деталь слота по умолчанию из мода\n // а если и её нет — первую деталь мода с тем же slotType (колёса, шины из общих файлов игры)\n if (pick && !parts.has(pick)) {\n const alt = parts.has(s.def) ? s.def : bySlot.get(s.type)?.[0];\n if (alt) { substituted.push(`${pick} → ${alt}`); visit(alt, off); continue; }\n }\n visit(pick, off);\n }\n };\n visit(mainPart, []);\n // переменные: значения по умолчанию из выбранных деталей, затем из .pc\n const vars: Vars = new Map();\n for (const c of chosen) {\n const tbl = c.part.data.variables;\n if (!Array.isArray(tbl) || !Array.isArray(tbl[0])) continue;\n const head = (tbl[0] as JValue[]).map(String), iName = head.indexOf('name'), iDef = head.indexOf('default');\n for (const r of rows(tbl)) if (iName >= 0 && iDef >= 0) vars.set(String(r[iName]), (r[iDef] ?? null) as never);\n }\n for (const [k, v] of Object.entries(pcVars)) vars.set(k, v as never);\n const flexbodies: Flexbody[] = [];\n for (const c of chosen)\n for (const r of rows(c.part.data.flexbodies)) {\n if (typeof r[0] !== 'string' || !r[0]) continue;\n flexbodies.push({ mesh: r[0], part: c.part.name, opts: lastObj(r), offsets: c.offsets });\n }\n return { chosen, flexbodies, missing, substituted, vars };\n}\n\nconst vec = (o: JValue | undefined, vars: Vars, def: number): [number, number, number] =>\n isObj(o) ? [evalNumber(o.x, vars, def), evalNumber(o.y, vars, def), evalNumber(o.z, vars, def)] : [def, def, def];\n\n/** Поворот в градусах: сначала X, затем Y, затем Z. */\nfunction rotMat([rx, ry, rz]: number[]): number[] {\n const [a, b, c] = [rx!, ry!, rz!].map((d) => (d * Math.PI) / 180) as [number, number, number];\n const ca = Math.cos(a), sa = Math.sin(a), cb = Math.cos(b), sb = Math.sin(b), cc = Math.cos(c), sc = Math.sin(c);\n // Rz·Ry·Rx, строки\n return [cc * cb, cc * sb * sa - sc * ca, cc * sb * ca + sc * sa, sc * cb, sc * sb * sa + cc * ca, sc * sb * ca - cc * sa, -sb, cb * sa, cb * ca];\n}\n\n/** Копия меша с преобразованием flexbody и смещением узлов. */\nexport function placeMesh(src: ColladaMesh, fb: Flexbody, vars: Vars): Mesh {\n const o = fb.opts, pos = vec(o?.pos, vars, 0), rot = vec(o?.rot, vars, 0), scl = vec(o?.scale, vars, 1), R = rotMat(rot);\n const n = src.vertexCount, P = new Float32Array(n * 3), N = new Float32Array(n * 3);\n for (let v = 0; v < n; v++) {\n const x = src.pos[v * 3]! * scl[0], y = src.pos[v * 3 + 1]! * scl[1], z = src.pos[v * 3 + 2]! * scl[2];\n P[v * 3] = R[0]! * x + R[1]! * y + R[2]! * z + pos[0];\n P[v * 3 + 1] = R[3]! * x + R[4]! * y + R[5]! * z + pos[1];\n P[v * 3 + 2] = R[6]! * x + R[7]! * y + R[8]! * z + pos[2];\n const a = src.nor[v * 3]!, b = src.nor[v * 3 + 1]!, c = src.nor[v * 3 + 2]!;\n N[v * 3] = R[0]! * a + R[1]! * b + R[2]! * c; N[v * 3 + 1] = R[3]! * a + R[4]! * b + R[5]! * c; N[v * 3 + 2] = R[6]! * a + R[7]! * b + R[8]! * c;\n }\n // nodeOffset слотов: X — наружу по стороне вершины (узлы смещаются на x·sign(x узла)), Y/Z — как есть.\n // Меш ступиц часто один на обе стороны, поэтому знак берётся по каждой вершине.\n let ox = 0, oy = 0, oz = 0;\n for (const off of fb.offsets) { const [x, y, z] = vec(off, vars, 0); ox += x; oy += y; oz += z; }\n if (ox || oy || oz) for (let v = 0; v < n; v++) { const x = P[v * 3]!; P[v * 3] = x + Math.sign(x) * ox; P[v * 3 + 1] += oy; P[v * 3 + 2] += oz; }\n return { ...src, name: `${fb.mesh} · ${fb.part}`, pos: P, nor: N };\n}\n\nexport interface PcFile { parts: Record<string, string>; vars: Record<string, JValue>; mainPartName?: string; model?: string; paints?: Array<{ baseColor?: number[] }> }\n\nexport async function loadVehicle(main: VFile, ctx: LoadContext): Promise<Model> {\n const pc = parseJbeam(decodeText(await main.arrayBuffer())) as unknown as PcFile;\n if (!pc || typeof pc !== 'object' || !pc.parts) throw new Error('Не конфигурация BeamNG (.pc): нет \"parts\"');\n const archives = [...ctx.archives];\n if (main.archive && !archives.includes(main.archive)) archives.push(main.archive);\n const parts = await collectParts(archives);\n if (!parts.size) throw new Error('Не найдено ни одного .jbeam — откройте мод целиком («Папка игры…» или zip)');\n const model = pc.model ?? (main.path ? /vehicles\\/([^/]+)\\//.exec(main.path)?.[1] : undefined) ?? '';\n let mainPart = pc.mainPartName ?? '';\n if (!parts.has(mainPart)) mainPart = [...parts.values()].find((p) => p.slotType === 'main' && p.file.includes(`/vehicles/${model.toLowerCase()}/`))?.name ?? [...parts.values()].find((p) => p.slotType === 'main')?.name ?? '';\n if (!mainPart) throw new Error('Не найдена главная деталь (slotType \"main\")');\n const asm = assemble(parts, mainPart, pc.parts, pc.vars ?? {});\n const wanted = new Set(asm.flexbodies.map((f) => f.mesh.toLowerCase()));\n\n // меши: .dae из тех же архивов, где встречаются нужные имена узлов\n const byName = new Map<string, ColladaMesh[]>();\n const daes: Array<[Archive, ArchiveEntry]> = [];\n for (const a of archives) for (const e of a.entries) if (e.path.endsWith('.dae')) daes.push([a, e]);\n const vehDir = `/vehicles/${model.toLowerCase()}/`;\n daes.sort((x, y) => +y[1].path.includes(vehDir) - +x[1].path.includes(vehDir)); // сначала папка машины\n for (const [a, e] of daes) {\n if ([...wanted].every((w) => byName.has(w))) break;\n const xml = decodeText(await a.read(e)), low = xml.toLowerCase();\n const need = new Set([...wanted].filter((w) => !byName.has(w) && (low.includes(`name=\"${w}\"`) || low.includes(`id=\"${w}\"`))));\n if (!need.size) continue;\n for (const m of parseCollada(xml, { nodes: need }).meshes) {\n const k = m.nodeName.toLowerCase();\n if (!byName.has(k)) byName.set(k, []);\n byName.get(k)!.push(m);\n }\n }\n const paint = pc.paints?.[0]?.baseColor;\n const mats = await collectMaterials(archives);\n const meshes: Mesh[] = [], noMesh = new Set<string>();\n for (const fb of asm.flexbodies) {\n const src = byName.get(fb.mesh.toLowerCase());\n if (!src) { noMesh.add(fb.mesh); continue; }\n for (const m of src) {\n const pm = placeMesh(m, fb, asm.vars), mt = mats.get((m.material ?? '').toLowerCase());\n // без карты цвета: множитель материала, иначе цвет покраски из .pc\n if (mt?.normal) pm.normalMap = mt.normal;\n if (mt?.surface) pm.surface = mt.surface;\n if (mt?.color) pm.texture = mt.color;\n else pm.color = (mt?.factor ?? paint ?? [0.7, 0.7, 0.7]).slice(0, 3).map((x) => Math.min(1, x));\n meshes.push(pm);\n }\n }\n if (!meshes.length) throw new Error(`Ни один меш не найден в .dae (нужно ${wanted.size}: ${[...wanted].slice(0, 5).join(', ')}…)`);\n const info = [\n `BeamNG: ${model} (${main.name}), деталей ${asm.chosen.length}, мешей ${meshes.length}, вершин ${sumVerts(meshes)}, треугольников ${sumTris(meshes)}`,\n 'Пропсы (руль, приборы, фары-источники) и деформация пока не показываются.',\n ];\n if (asm.missing.length) info.push(`Нет деталей (в моде не хватает файлов игры?): ${asm.missing.slice(0, 8).join(', ')}${asm.missing.length > 8 ? '…' : ''}`);\n if (asm.substituted.length) info.push(`Заменены деталями слота по умолчанию: ${asm.substituted.slice(0, 6).join(', ')}${asm.substituted.length > 6 ? '…' : ''}`);\n if (noMesh.size) info.push(`Нет мешей в .dae: ${[...noMesh].slice(0, 8).join(', ')}${noMesh.size > 8 ? '…' : ''}`);\n return { meshes, skeleton: null, motions: [], bounds: boundsOf(meshes), flipZ: false, defaultZUp: true, cameraYaw: 0, info,\n meta: { beamng: { model, parts: asm.chosen.map((c) => c.part.name) } } };\n}\n\n// ---------- материалы (*.materials.json, materials.cs) ----------\n/** color — карта цвета (пусто, если только цвет-множитель или заглушка generic_*); factor — baseColorFactor слоя 0. */\nexport interface MaterialTex { color: string; factor?: number[]; normal?: string; surface?: SurfaceInfo }\nconst PLACEHOLDER = /(^|\\/)(null|generic_[a-z]*|white)(\\.[a-z]+)*\\.(dds|png|jpe?g|tga)$/i;\nconst putMat = (out: Map<string, MaterialTex>, names: Array<string | undefined>, t: MaterialTex) => {\n for (const name of names) if (name && !out.has(name.toLowerCase())) out.set(name.toLowerCase(), t);\n};\nexport function readMaterialsJson(text: string, out: Map<string, MaterialTex>): void {\n let j: JValue;\n try { j = parseJbeam(text); } catch { return; }\n if (!isObj(j)) return;\n for (const [key, v] of Object.entries(j)) {\n if (!isObj(v)) continue;\n let color = '', factor: number[] | undefined, normal = '', surface: SurfaceInfo | undefined;\n const stages = Array.isArray(v.Stages) ? (v.Stages as JValue[]) : [];\n for (const s of stages) {\n if (!isObj(s)) continue;\n if (!surface) surface = pbrOf(s);\n if (!normal && typeof s.normalMap === 'string' && s.normalMap && !/generic_n|null\\./i.test(s.normalMap)) normal = s.normalMap;\n if (!factor && Array.isArray(s.baseColorFactor)) factor = (s.baseColorFactor as JValue[]).slice(0, 3).map(Number);\n for (const k of ['baseColorMap', 'colorMap', 'diffuseMap']) {\n const p = s[k];\n if (typeof p === 'string' && p && !PLACEHOLDER.test(p)) { color = p; break; }\n }\n if (color) break;\n }\n if (!color && typeof v.diffuseMap === 'string' && !PLACEHOLDER.test(v.diffuseMap)) color = v.diffuseMap;\n if (!color && !factor) continue;\n putMat(out, [key, typeof v.mapTo === 'string' ? v.mapTo : '', typeof v.name === 'string' ? v.name : ''], { color, factor, ...(normal ? { normal } : {}), ...(surface ? { surface } : {}) });\n }\n}\n/** PBR стадии материала BeamNG (v1.5): отдельные одноканальные карты и множители. */\nfunction pbrOf(s: Obj): SurfaceInfo | undefined {\n const map = (k: string) => (typeof s[k] === 'string' && s[k] && !/null\\./i.test(s[k] as string) ? { map: s[k] as string, channel: 0 as const } : undefined);\n const num = (k: string) => (typeof s[k] === 'number' ? (s[k] as number) : undefined);\n const out: SurfaceInfo = { roughness: map('roughnessMap'), metallic: map('metallicMap'), ao: map('ambientOcclusionMap'), roughnessFactor: num('roughnessFactor'), metallicFactor: num('metallicFactor'), source: 'BeamNG' };\n return out.roughness || out.metallic || out.ao || out.roughnessFactor !== undefined || out.metallicFactor !== undefined ? out : undefined;\n}\n/** Старый формат TorqueScript: singleton Material(name) { mapTo = \"x\"; diffuseMap[0] = \"…\"; } */\nexport function readMaterialsCs(text: string, out: Map<string, MaterialTex>): void {\n const re = /(?:singleton|new)\\s+Material\\s*\\(\\s*([\\w.]+)\\s*\\)\\s*\\{([\\s\\S]*?)\\n\\s*\\};?/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(text))) {\n const body = m[2]!, mapTo = /mapTo\\s*=\\s*\"([^\"]*)\"/.exec(body)?.[1];\n const maps = [...body.matchAll(/(?:diffuseMap|colorMap|baseColorMap)\\s*(?:\\[\\s*(\\d+)\\s*\\])?\\s*=\\s*\"([^\"]*)\"/g)]\n .map((x) => ({ layer: +(x[1] ?? 0), path: x[2]! })).filter((x) => x.path && !PLACEHOLDER.test(x.path)).sort((a, b) => a.layer - b.layer);\n const f = /(?:baseColorFactor|diffuseColor)\\s*(?:\\[\\s*0\\s*\\])?\\s*=\\s*\"([^\"]*)\"/.exec(body)?.[1]?.trim().split(/\\s+/).map(Number);\n const factor = f && f.length >= 3 && f.every(Number.isFinite) ? f.slice(0, 3) : undefined;\n if (!maps.length && !factor) continue;\n const normal = [...body.matchAll(/normalMap\\s*(?:\\[\\s*(\\d+)\\s*\\])?\\s*=\\s*\"([^\"]*)\"/g)].map((x) => x[2]!).find((n) => n && !/generic_n|null\\./i.test(n));\n const csMap = (k: string) => { const v = new RegExp(`${k}\\\\s*\\\\[\\\\s*0\\\\s*\\\\]\\\\s*=\\\\s*\"([^\"]*)\"`).exec(body)?.[1]; return v && !/null\\./i.test(v) ? { map: v, channel: 0 as const } : undefined; };\n const csNum = (k: string) => { const v = new RegExp(`${k}\\\\s*\\\\[\\\\s*0\\\\s*\\\\]\\\\s*=\\\\s*\"([\\\\d.]+)\"`).exec(body)?.[1]; return v !== undefined ? +v : undefined; };\n const sf: SurfaceInfo = { roughness: csMap('roughnessMap'), metallic: csMap('metallicMap'), ao: csMap('ambientOcclusionMap'), roughnessFactor: csNum('roughnessFactor'), metallicFactor: csNum('metallicFactor'), source: 'BeamNG' };\n const surface = sf.roughness || sf.metallic || sf.ao || sf.roughnessFactor !== undefined || sf.metallicFactor !== undefined ? sf : undefined;\n putMat(out, [m[1]!, mapTo], { color: maps[0]?.path ?? '', factor, ...(normal ? { normal } : {}), ...(surface ? { surface } : {}) });\n }\n}\n\n/** Все материалы из открытых архивов (json важнее cs: читаем json первым). */\nexport async function collectMaterials(archives: readonly Archive[]): Promise<Map<string, MaterialTex>> {\n const out = new Map<string, MaterialTex>();\n for (const pass of ['json', 'cs'] as const)\n for (const a of archives)\n for (const e of a.entries) {\n const ok = pass === 'json' ? /materials\\.json$/.test(e.path) : /(^|\\/)materials\\.cs$/.test(e.path);\n if (!ok) continue;\n const text = decodeText(await a.read(e));\n if (pass === 'json') readMaterialsJson(text, out); else readMaterialsCs(text, out);\n }\n return out;\n}\nexport { evalValue };\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vehicle.js","sources":["../src/vehicle.ts"],"sourcesContent":["// Assembling a BeamNG.drive vehicle from a .pc configuration: the jbeam slot tree -> chosen parts -> flexbody meshes from .dae.\n// Mesh placement: flexbody pos/rot/scale + the slot's nodeOffset (part node offset; X is mirrored by the mesh side).\nimport { decodeText } from '@gmvjs/core/binary';\nimport { boundsOf, sumTris, sumVerts } from '@gmvjs/core/geometry';\nimport type { LoadContext } from '@gmvjs/core/plugin';\nimport type { Archive, ArchiveEntry, Mesh, Model, SurfaceInfo, VFile } from '@gmvjs/core/types';\nimport { parseCollada, type ColladaMesh } from '@gmvjs/collada';\nimport { evalNumber, evalValue, parseJbeam, type JValue, type Vars } from './jbeam';\n\ntype Obj = { [k: string]: JValue };\nconst isObj = (v: JValue | undefined): v is Obj => !!v && typeof v === 'object' && !Array.isArray(v);\n\nexport interface JPart { name: string; slotType: string; data: Obj; file: string }\ninterface Slot { type: string; def: string; opts: Obj | null }\ninterface Chosen { part: JPart; offsets: Obj[] }\nexport interface Flexbody { mesh: string; part: string; opts: Obj | null; offsets: Obj[] }\n\n/** jbeam table rows: the first is the header, dictionaries between rows are modifiers (skipped). */\nfunction rows(v: JValue | undefined): JValue[][] {\n if (!Array.isArray(v)) return [];\n return v.slice(1).filter((r): r is JValue[] => Array.isArray(r));\n}\nconst lastObj = (r: JValue[]): Obj | null => { for (let k = r.length - 1; k >= 0; k--) if (isObj(r[k])) return r[k] as Obj; return null; };\n\nexport function slotsOf(part: JPart): Slot[] {\n const out: Slot[] = [];\n for (const r of rows(part.data.slots)) out.push({ type: String(r[0] ?? ''), def: String(r[1] ?? ''), opts: lastObj(r) });\n // slots2: [\"name\", \"allowTypes\", \"denyTypes\", \"default\", \"description\"]\n for (const r of rows(part.data.slots2)) {\n const allow = Array.isArray(r[1]) ? (r[1] as JValue[]).map(String) : [String(r[0] ?? '')];\n for (const t of allow.length ? allow : [String(r[0] ?? '')]) out.push({ type: String(r[0] ?? t), def: String(r[3] ?? ''), opts: lastObj(r) });\n }\n return out;\n}\n\n/** All parts from jbeam files in the archives (the mod's vehicles/ folder and, if open, the game's). */\nexport async function collectParts(archives: readonly Archive[]): Promise<Map<string, JPart>> {\n const parts = new Map<string, JPart>();\n for (const a of archives)\n for (const e of a.entries) {\n if (!e.path.endsWith('.jbeam')) continue;\n let data: JValue;\n try { data = parseJbeam(decodeText(await a.read(e))); } catch { continue; }\n if (!isObj(data)) continue;\n for (const [name, v] of Object.entries(data)) if (isObj(v) && !parts.has(name)) parts.set(name, { name, slotType: String(v.slotType ?? ''), data: v, file: e.path });\n }\n return parts;\n}\n\nexport interface Assembly { chosen: Chosen[]; flexbodies: Flexbody[]; missing: string[]; substituted: string[]; vars: Vars }\n\n/** Walk the slot tree from the main part. pcParts: slotType -> part name (\"\" = empty). */\nexport function assemble(parts: Map<string, JPart>, mainPart: string, pcParts: Record<string, string>, pcVars: Record<string, JValue> = {}): Assembly {\n const chosen: Chosen[] = [], missing: string[] = [], substituted: string[] = [], seen = new Set<string>();\n const bySlot = new Map<string, string[]>();\n for (const p of parts.values()) { if (!bySlot.has(p.slotType)) bySlot.set(p.slotType, []); bySlot.get(p.slotType)!.push(p.name); }\n const visit = (name: string, offsets: Obj[]) => {\n if (!name || seen.has(name)) return;\n const part = parts.get(name);\n if (!part) { missing.push(name); return; }\n seen.add(name);\n chosen.push({ part, offsets });\n for (const s of slotsOf(part)) {\n const pick = Object.prototype.hasOwnProperty.call(pcParts, s.type) ? pcParts[s.type]! : s.def;\n const off = s.opts && isObj(s.opts.nodeOffset) ? [...offsets, s.opts.nodeOffset as Obj] : offsets;\n // the part from .pc is not found (a shared game part): take the slot's default part from the mod\n // and if that is missing too, the mod's first part with the same slotType (wheels, tires from shared game files)\n if (pick && !parts.has(pick)) {\n const alt = parts.has(s.def) ? s.def : bySlot.get(s.type)?.[0];\n if (alt) { substituted.push(`${pick} → ${alt}`); visit(alt, off); continue; }\n }\n visit(pick, off);\n }\n };\n visit(mainPart, []);\n // variables: defaults from the chosen parts, then from .pc\n const vars: Vars = new Map();\n for (const c of chosen) {\n const tbl = c.part.data.variables;\n if (!Array.isArray(tbl) || !Array.isArray(tbl[0])) continue;\n const head = (tbl[0] as JValue[]).map(String), iName = head.indexOf('name'), iDef = head.indexOf('default');\n for (const r of rows(tbl)) if (iName >= 0 && iDef >= 0) vars.set(String(r[iName]), (r[iDef] ?? null) as never);\n }\n for (const [k, v] of Object.entries(pcVars)) vars.set(k, v as never);\n const flexbodies: Flexbody[] = [];\n for (const c of chosen)\n for (const r of rows(c.part.data.flexbodies)) {\n if (typeof r[0] !== 'string' || !r[0]) continue;\n flexbodies.push({ mesh: r[0], part: c.part.name, opts: lastObj(r), offsets: c.offsets });\n }\n return { chosen, flexbodies, missing, substituted, vars };\n}\n\nconst vec = (o: JValue | undefined, vars: Vars, def: number): [number, number, number] =>\n isObj(o) ? [evalNumber(o.x, vars, def), evalNumber(o.y, vars, def), evalNumber(o.z, vars, def)] : [def, def, def];\n\n/** Rotation in degrees: X first, then Y, then Z. */\nfunction rotMat([rx, ry, rz]: number[]): number[] {\n const [a, b, c] = [rx!, ry!, rz!].map((d) => (d * Math.PI) / 180) as [number, number, number];\n const ca = Math.cos(a), sa = Math.sin(a), cb = Math.cos(b), sb = Math.sin(b), cc = Math.cos(c), sc = Math.sin(c);\n // Rz·Ry·Rx, rows\n return [cc * cb, cc * sb * sa - sc * ca, cc * sb * ca + sc * sa, sc * cb, sc * sb * sa + cc * ca, sc * sb * ca - cc * sa, -sb, cb * sa, cb * ca];\n}\n\n/** Copy of a mesh with the flexbody transform and node offset applied. */\nexport function placeMesh(src: ColladaMesh, fb: Flexbody, vars: Vars): Mesh {\n const o = fb.opts, pos = vec(o?.pos, vars, 0), rot = vec(o?.rot, vars, 0), scl = vec(o?.scale, vars, 1), R = rotMat(rot);\n const n = src.vertexCount, P = new Float32Array(n * 3), N = new Float32Array(n * 3);\n for (let v = 0; v < n; v++) {\n const x = src.pos[v * 3]! * scl[0], y = src.pos[v * 3 + 1]! * scl[1], z = src.pos[v * 3 + 2]! * scl[2];\n P[v * 3] = R[0]! * x + R[1]! * y + R[2]! * z + pos[0];\n P[v * 3 + 1] = R[3]! * x + R[4]! * y + R[5]! * z + pos[1];\n P[v * 3 + 2] = R[6]! * x + R[7]! * y + R[8]! * z + pos[2];\n const a = src.nor[v * 3]!, b = src.nor[v * 3 + 1]!, c = src.nor[v * 3 + 2]!;\n N[v * 3] = R[0]! * a + R[1]! * b + R[2]! * c; N[v * 3 + 1] = R[3]! * a + R[4]! * b + R[5]! * c; N[v * 3 + 2] = R[6]! * a + R[7]! * b + R[8]! * c;\n }\n // slot nodeOffset: X points outward by the vertex side (nodes move by x·sign(node x)), Y/Z as is.\n // A hub mesh often serves both sides, so the sign is taken per vertex.\n let ox = 0, oy = 0, oz = 0;\n for (const off of fb.offsets) { const [x, y, z] = vec(off, vars, 0); ox += x; oy += y; oz += z; }\n if (ox || oy || oz) for (let v = 0; v < n; v++) { const x = P[v * 3]!; P[v * 3] = x + Math.sign(x) * ox; P[v * 3 + 1] += oy; P[v * 3 + 2] += oz; }\n return { ...src, name: `${fb.mesh} · ${fb.part}`, pos: P, nor: N };\n}\n\nexport interface PcFile { parts: Record<string, string>; vars: Record<string, JValue>; mainPartName?: string; model?: string; paints?: Array<{ baseColor?: number[] }> }\n\nexport async function loadVehicle(main: VFile, ctx: LoadContext): Promise<Model> {\n const pc = parseJbeam(decodeText(await main.arrayBuffer())) as unknown as PcFile;\n if (!pc || typeof pc !== 'object' || !pc.parts) throw new Error('Не конфигурация BeamNG (.pc): нет \"parts\"');\n const archives = [...ctx.archives];\n if (main.archive && !archives.includes(main.archive)) archives.push(main.archive);\n const parts = await collectParts(archives);\n if (!parts.size) throw new Error('Не найдено ни одного .jbeam — откройте мод целиком («Папка игры…» или zip)');\n const model = pc.model ?? (main.path ? /vehicles\\/([^/]+)\\//.exec(main.path)?.[1] : undefined) ?? '';\n let mainPart = pc.mainPartName ?? '';\n if (!parts.has(mainPart)) mainPart = [...parts.values()].find((p) => p.slotType === 'main' && p.file.includes(`/vehicles/${model.toLowerCase()}/`))?.name ?? [...parts.values()].find((p) => p.slotType === 'main')?.name ?? '';\n if (!mainPart) throw new Error('Не найдена главная деталь (slotType \"main\")');\n const asm = assemble(parts, mainPart, pc.parts, pc.vars ?? {});\n const wanted = new Set(asm.flexbodies.map((f) => f.mesh.toLowerCase()));\n\n // meshes: .dae files from the same archives where the needed node names occur\n const byName = new Map<string, ColladaMesh[]>();\n const daes: Array<[Archive, ArchiveEntry]> = [];\n for (const a of archives) for (const e of a.entries) if (e.path.endsWith('.dae')) daes.push([a, e]);\n const vehDir = `/vehicles/${model.toLowerCase()}/`;\n daes.sort((x, y) => +y[1].path.includes(vehDir) - +x[1].path.includes(vehDir)); // the vehicle folder first\n for (const [a, e] of daes) {\n if ([...wanted].every((w) => byName.has(w))) break;\n const xml = decodeText(await a.read(e)), low = xml.toLowerCase();\n const need = new Set([...wanted].filter((w) => !byName.has(w) && (low.includes(`name=\"${w}\"`) || low.includes(`id=\"${w}\"`))));\n if (!need.size) continue;\n for (const m of parseCollada(xml, { nodes: need }).meshes) {\n const k = m.nodeName.toLowerCase();\n if (!byName.has(k)) byName.set(k, []);\n byName.get(k)!.push(m);\n }\n }\n const paint = pc.paints?.[0]?.baseColor;\n const mats = await collectMaterials(archives);\n const meshes: Mesh[] = [], noMesh = new Set<string>();\n for (const fb of asm.flexbodies) {\n const src = byName.get(fb.mesh.toLowerCase());\n if (!src) { noMesh.add(fb.mesh); continue; }\n for (const m of src) {\n const pm = placeMesh(m, fb, asm.vars), mt = mats.get((m.material ?? '').toLowerCase());\n // without a color map: the material multiplier, otherwise the paint color from .pc\n if (mt?.normal) pm.normalMap = mt.normal;\n if (mt?.surface) pm.surface = mt.surface;\n if (mt?.color) pm.texture = mt.color;\n else pm.color = (mt?.factor ?? paint ?? [0.7, 0.7, 0.7]).slice(0, 3).map((x) => Math.min(1, x));\n meshes.push(pm);\n }\n }\n if (!meshes.length) throw new Error(`Ни один меш не найден в .dae (нужно ${wanted.size}: ${[...wanted].slice(0, 5).join(', ')}…)`);\n const info = [\n `BeamNG: ${model} (${main.name}), деталей ${asm.chosen.length}, мешей ${meshes.length}, вершин ${sumVerts(meshes)}, треугольников ${sumTris(meshes)}`,\n 'Пропсы (руль, приборы, фары-источники) и деформация пока не показываются.',\n ];\n if (asm.missing.length) info.push(`Нет деталей (в моде не хватает файлов игры?): ${asm.missing.slice(0, 8).join(', ')}${asm.missing.length > 8 ? '…' : ''}`);\n if (asm.substituted.length) info.push(`Заменены деталями слота по умолчанию: ${asm.substituted.slice(0, 6).join(', ')}${asm.substituted.length > 6 ? '…' : ''}`);\n if (noMesh.size) info.push(`Нет мешей в .dae: ${[...noMesh].slice(0, 8).join(', ')}${noMesh.size > 8 ? '…' : ''}`);\n return { meshes, skeleton: null, motions: [], bounds: boundsOf(meshes), flipZ: false, defaultZUp: true, cameraYaw: 0, info,\n meta: { beamng: { model, parts: asm.chosen.map((c) => c.part.name) } } };\n}\n\n// ---------- materials (*.materials.json, materials.cs) ----------\n/** color = the color map (empty if only a color multiplier or a generic_* placeholder); factor = baseColorFactor of layer 0. */\nexport interface MaterialTex { color: string; factor?: number[]; normal?: string; surface?: SurfaceInfo }\nconst PLACEHOLDER = /(^|\\/)(null|generic_[a-z]*|white)(\\.[a-z]+)*\\.(dds|png|jpe?g|tga)$/i;\nconst putMat = (out: Map<string, MaterialTex>, names: Array<string | undefined>, t: MaterialTex) => {\n for (const name of names) if (name && !out.has(name.toLowerCase())) out.set(name.toLowerCase(), t);\n};\nexport function readMaterialsJson(text: string, out: Map<string, MaterialTex>): void {\n let j: JValue;\n try { j = parseJbeam(text); } catch { return; }\n if (!isObj(j)) return;\n for (const [key, v] of Object.entries(j)) {\n if (!isObj(v)) continue;\n let color = '', factor: number[] | undefined, normal = '', surface: SurfaceInfo | undefined;\n const stages = Array.isArray(v.Stages) ? (v.Stages as JValue[]) : [];\n for (const s of stages) {\n if (!isObj(s)) continue;\n if (!surface) surface = pbrOf(s);\n if (!normal && typeof s.normalMap === 'string' && s.normalMap && !/generic_n|null\\./i.test(s.normalMap)) normal = s.normalMap;\n if (!factor && Array.isArray(s.baseColorFactor)) factor = (s.baseColorFactor as JValue[]).slice(0, 3).map(Number);\n for (const k of ['baseColorMap', 'colorMap', 'diffuseMap']) {\n const p = s[k];\n if (typeof p === 'string' && p && !PLACEHOLDER.test(p)) { color = p; break; }\n }\n if (color) break;\n }\n if (!color && typeof v.diffuseMap === 'string' && !PLACEHOLDER.test(v.diffuseMap)) color = v.diffuseMap;\n if (!color && !factor) continue;\n putMat(out, [key, typeof v.mapTo === 'string' ? v.mapTo : '', typeof v.name === 'string' ? v.name : ''], { color, factor, ...(normal ? { normal } : {}), ...(surface ? { surface } : {}) });\n }\n}\n/** PBR of a BeamNG material stage (v1.5): separate single-channel maps and multipliers. */\nfunction pbrOf(s: Obj): SurfaceInfo | undefined {\n const map = (k: string) => (typeof s[k] === 'string' && s[k] && !/null\\./i.test(s[k] as string) ? { map: s[k] as string, channel: 0 as const } : undefined);\n const num = (k: string) => (typeof s[k] === 'number' ? (s[k] as number) : undefined);\n const out: SurfaceInfo = { roughness: map('roughnessMap'), metallic: map('metallicMap'), ao: map('ambientOcclusionMap'), roughnessFactor: num('roughnessFactor'), metallicFactor: num('metallicFactor'), source: 'BeamNG' };\n return out.roughness || out.metallic || out.ao || out.roughnessFactor !== undefined || out.metallicFactor !== undefined ? out : undefined;\n}\n/** The old TorqueScript format: singleton Material(name) { mapTo = \"x\"; diffuseMap[0] = \"…\"; } */\nexport function readMaterialsCs(text: string, out: Map<string, MaterialTex>): void {\n const re = /(?:singleton|new)\\s+Material\\s*\\(\\s*([\\w.]+)\\s*\\)\\s*\\{([\\s\\S]*?)\\n\\s*\\};?/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(text))) {\n const body = m[2]!, mapTo = /mapTo\\s*=\\s*\"([^\"]*)\"/.exec(body)?.[1];\n const maps = [...body.matchAll(/(?:diffuseMap|colorMap|baseColorMap)\\s*(?:\\[\\s*(\\d+)\\s*\\])?\\s*=\\s*\"([^\"]*)\"/g)]\n .map((x) => ({ layer: +(x[1] ?? 0), path: x[2]! })).filter((x) => x.path && !PLACEHOLDER.test(x.path)).sort((a, b) => a.layer - b.layer);\n const f = /(?:baseColorFactor|diffuseColor)\\s*(?:\\[\\s*0\\s*\\])?\\s*=\\s*\"([^\"]*)\"/.exec(body)?.[1]?.trim().split(/\\s+/).map(Number);\n const factor = f && f.length >= 3 && f.every(Number.isFinite) ? f.slice(0, 3) : undefined;\n if (!maps.length && !factor) continue;\n const normal = [...body.matchAll(/normalMap\\s*(?:\\[\\s*(\\d+)\\s*\\])?\\s*=\\s*\"([^\"]*)\"/g)].map((x) => x[2]!).find((n) => n && !/generic_n|null\\./i.test(n));\n const csMap = (k: string) => { const v = new RegExp(`${k}\\\\s*\\\\[\\\\s*0\\\\s*\\\\]\\\\s*=\\\\s*\"([^\"]*)\"`).exec(body)?.[1]; return v && !/null\\./i.test(v) ? { map: v, channel: 0 as const } : undefined; };\n const csNum = (k: string) => { const v = new RegExp(`${k}\\\\s*\\\\[\\\\s*0\\\\s*\\\\]\\\\s*=\\\\s*\"([\\\\d.]+)\"`).exec(body)?.[1]; return v !== undefined ? +v : undefined; };\n const sf: SurfaceInfo = { roughness: csMap('roughnessMap'), metallic: csMap('metallicMap'), ao: csMap('ambientOcclusionMap'), roughnessFactor: csNum('roughnessFactor'), metallicFactor: csNum('metallicFactor'), source: 'BeamNG' };\n const surface = sf.roughness || sf.metallic || sf.ao || sf.roughnessFactor !== undefined || sf.metallicFactor !== undefined ? sf : undefined;\n putMat(out, [m[1]!, mapTo], { color: maps[0]?.path ?? '', factor, ...(normal ? { normal } : {}), ...(surface ? { surface } : {}) });\n }\n}\n\n/** All materials from the open archives (json takes precedence over cs: json is read first). */\nexport async function collectMaterials(archives: readonly Archive[]): Promise<Map<string, MaterialTex>> {\n const out = new Map<string, MaterialTex>();\n for (const pass of ['json', 'cs'] as const)\n for (const a of archives)\n for (const e of a.entries) {\n const ok = pass === 'json' ? /materials\\.json$/.test(e.path) : /(^|\\/)materials\\.cs$/.test(e.path);\n if (!ok) continue;\n const text = decodeText(await a.read(e));\n if (pass === 'json') readMaterialsJson(text, out); else readMaterialsCs(text, out);\n }\n return out;\n}\nexport { evalValue };\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmvjs/torque3d",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "gmv: Torque3D (BeamNG)",
5
5
  "keywords": [
6
6
  "gmv",
@@ -44,8 +44,8 @@
44
44
  "build": "node ../../scripts/build-package.mjs"
45
45
  },
46
46
  "dependencies": {
47
- "@gmvjs/collada": "^0.1.0",
48
- "@gmvjs/core": "^0.1.0"
47
+ "@gmvjs/collada": "^0.1.1",
48
+ "@gmvjs/core": "^0.1.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@gmvjs/test-utils": "^0.1.0"