roavatar-renderer 1.2.14 → 1.2.16

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,21 +1,25 @@
1
1
  # Renderer for RoAvatar
2
- This is the Roblox avatar renderer made for https://github.com/steinann/RoAvatar
2
+ The Roblox Avatar renderer used by https://github.com/steinann/RoAvatar
3
3
 
4
- It was originally a part of that repository but has now gained independence
4
+ Links: [npm](https://www.npmjs.com/package/roavatar-renderer) | [GitHub](https://github.com/steinann/RoAvatar-Renderer)
5
5
 
6
- Also available on npm: https://www.npmjs.com/package/roavatar-renderer
6
+ > **IMPORTANT** <br>
7
+ > Assets are *NOT* included in the npm module or this repository, you have to get them from the main RoAvatar repository OR set
8
+ > ```FLAGS.ONLINE_ASSETS = true```
7
9
 
8
- IMPORTANT: Assets are NOT included in the npm module or this repository, you have to get them from the main RoAvatar repository OR set FLAGS.ONLINE_ASSETS = true
10
+ Basic example on how to load an avatar using OutfitRenderer (to make it simpler):
9
11
 
10
- Basic example on how to load an avatar, that is also untested:
12
+ **HTML:**
11
13
  ```html
12
- <!--I know this is kind of terrible but... it has to be included-->
14
+ <!--This script has to be loaded in before loading any meshes-->
13
15
  <script src="/draco_decoder.js"></script>
14
16
  ```
17
+ **TYPESCRIPT:**
15
18
  ```ts
16
19
  //setup flags that are compatible with you environment
17
- FLAGS.ONLINE_ASSETS = false //set this to true if you want assets to be loaded from roblox instead of locally
20
+ FLAGS.ONLINE_ASSETS = true //set false to true if you want assets to be loaded locally
18
21
 
22
+ //if we arent using online assets we have to provide the renderer with the paths
19
23
  if (!FLAGS.ONLINE_ASSETS) {
20
24
  //path to asset files from RoAvatar
21
25
  FLAGS.ASSETS_PATH = chrome.runtime.getURL("assets/rbxasset/")
@@ -47,9 +51,11 @@ if (!(outfit instanceof Outfit)) throw new Error("Failed to get outfit")
47
51
  //used by api
48
52
  const auth = new Authentication()
49
53
  //manages outfit rendering for you
50
- const outfitRenderer = new OutfitRenderer(auth, outfit, "roavatar://")
54
+ const outfitRenderer = new OutfitRenderer(auth, outfit)
51
55
  outfitRenderer.startAnimating()
52
56
  outfitRenderer.setMainAnimation("idle")
53
57
  ```
54
58
 
55
- See the RoAvatar source code to know more, especially avatarPreview.tsx is useful
59
+ More info available in ```/docs```
60
+
61
+ Also the OutfitRenderer code or RoAvatar source code is useful, especially ```avatarPreview.ts```
package/dist/index.d.ts CHANGED
@@ -295,6 +295,10 @@ export declare const API: {
295
295
  PremiumFeatures: {
296
296
  GetSubscription: (userId: number) => Promise<Response | GetSubscription_Result>;
297
297
  };
298
+ RBLXGet: typeof RBLXGet;
299
+ RBLXPost: typeof RBLXPost;
300
+ RBLXDelete: typeof RBLXDelete;
301
+ RBLXPatch: typeof RBLXPatch;
298
302
  };
299
303
 
300
304
  export declare function arrayBufferToBase64(buffer: ArrayBuffer): string;
@@ -880,6 +884,10 @@ declare class Event_2 {
880
884
  }
881
885
  export { Event_2 as Event }
882
886
 
887
+ export declare function exposeAPI(): void;
888
+
889
+ export declare function exposeMesh(): void;
890
+
883
891
  export declare const FaceControlNames: string[];
884
892
 
885
893
  export declare class FaceControlsWrapper extends InstanceWrapper {
@@ -1279,7 +1287,7 @@ export declare class Instance {
1279
1287
  PropOrDefault(name: string, def: unknown): unknown;
1280
1288
  PropertyType(name: string): number | undefined;
1281
1289
  getPropertyNames(): string[];
1282
- setParent(instance: Instance | undefined | null): void;
1290
+ setParent(instance: Instance | undefined | null, fireEvents?: boolean): void;
1283
1291
  Destroy(): void;
1284
1292
  GetFullName(): string;
1285
1293
  GetChildren(): Instance[];
@@ -1541,6 +1549,8 @@ export declare const magic = "<roblox!";
1541
1549
 
1542
1550
  export declare function magnitude(v: Vec3): number;
1543
1551
 
1552
+ export declare const MakeupAssetTypes: string[];
1553
+
1544
1554
  export declare class MakeupDescriptionWrapper extends InstanceWrapper {
1545
1555
  static className: string;
1546
1556
  static requiredProperties: string[];
@@ -1882,13 +1892,15 @@ export declare class OutfitRenderer {
1882
1892
  hasNewUpdate: boolean;
1883
1893
  lastFrameTime: number;
1884
1894
  animationInterval?: NodeJS.Timeout;
1895
+ animationFPS: number;
1896
+ deltaTimeMultiplier: number;
1885
1897
  /**
1886
1898
  * Creates a new OutfitRenderer which makes it easy to render outfits
1887
1899
  * @param auth The authentication object, you should have one you use for everything
1888
1900
  * @param outfit The outfit you want to render, it can be updated later by calling setOutfit()
1889
- * @param rigPath The path that contains RigR6.rbxm and RigR15.rbxm, for example "../assets/"
1901
+ * @param rigPath The path that contains RigR6.rbxm and RigR15.rbxm, should always be "roavatar://" as rig path is now controlled by FLAGS
1890
1902
  */
1891
- constructor(auth: Authentication, outfit: Outfit, rigPath: string);
1903
+ constructor(auth: Authentication, outfit: Outfit, rigPath?: string);
1892
1904
  /**
1893
1905
  * Updates the current rig, called internally by _updateOutfit()
1894
1906
  */
@@ -2054,6 +2066,14 @@ export declare class RBFDeformerPatch {
2054
2066
  deformMesh(): void;
2055
2067
  }
2056
2068
 
2069
+ declare function RBLXDelete(url: string, auth: Authentication, body: any, attempt?: number): Promise<Response>;
2070
+
2071
+ declare function RBLXGet(url: string, headers?: any, includeCredentials?: boolean): Promise<Response>;
2072
+
2073
+ declare function RBLXPatch(url: string, auth: Authentication, body: any, attempt?: number): Promise<Response>;
2074
+
2075
+ declare function RBLXPost(url: string, auth: Authentication | undefined, body: any, attempt?: number, method?: string): Promise<Response>;
2076
+
2057
2077
  export declare class RBX {
2058
2078
  classCount: number;
2059
2079
  instanceCount: number;
@@ -2604,7 +2624,7 @@ declare type ValidationIssue = {
2604
2624
  assetIndex?: number;
2605
2625
  };
2606
2626
 
2607
- declare type ValidationIssueType = "AccessoryLimit" | "LayeredLimit" | "OneOfTypeLimit" | "DuplicateId" | "NotWearable" | "MissingLayeredMeta" | "InvalidAsset";
2627
+ declare type ValidationIssueType = "AccessoryLimit" | "LayeredLimit" | "OneOfTypeLimit" | "DuplicateId" | "NotWearable" | "MissingLayeredMeta" | "InvalidAsset" | "MakeupLimit";
2608
2628
 
2609
2629
  export declare type Vec2 = [number, number];
2610
2630
 
package/dist/index.js CHANGED
@@ -287,6 +287,11 @@ const LayeredClothingAssetOrder = {
287
287
  90: 16
288
288
  // EyeMakeup
289
289
  };
290
+ const MakeupAssetTypes = [
291
+ "FaceMakeup",
292
+ "LipMakeup",
293
+ "EyeMakeup"
294
+ ];
290
295
  const MaxPerAsset = {
291
296
  "Head": 1,
292
297
  "TShirt": 1,
@@ -26700,6 +26705,7 @@ class WebGLRenderer {
26700
26705
  gl.unpackColorSpace = ColorManagement._getUnpackColorSpace();
26701
26706
  }
26702
26707
  }
26708
+ const textDecoder$1 = new TextDecoder();
26703
26709
  class SimpleView {
26704
26710
  view;
26705
26711
  viewOffset;
@@ -26757,7 +26763,7 @@ class SimpleView {
26757
26763
  if (!stringLength) {
26758
26764
  stringLength = this.readUint32();
26759
26765
  }
26760
- const string = new TextDecoder().decode(new Uint8Array(this.view.buffer).subarray(this.viewOffset, this.viewOffset + stringLength));
26766
+ const string = textDecoder$1.decode(new Uint8Array(this.view.buffer).subarray(this.viewOffset, this.viewOffset + stringLength));
26761
26767
  this.viewOffset += stringLength;
26762
26768
  return string;
26763
26769
  }
@@ -26889,6 +26895,7 @@ function intToRgb(colorInt) {
26889
26895
  const B = colorInt & 255;
26890
26896
  return { R, G, B };
26891
26897
  }
26898
+ const textDecoder = new TextDecoder();
26892
26899
  class RBXSimpleView {
26893
26900
  view;
26894
26901
  viewOffset;
@@ -26926,7 +26933,7 @@ class RBXSimpleView {
26926
26933
  if (!stringLength) {
26927
26934
  stringLength = this.readUint32();
26928
26935
  }
26929
- const string = new TextDecoder().decode(new Uint8Array(this.view.buffer).subarray(this.viewOffset, this.viewOffset + stringLength));
26936
+ const string = textDecoder.decode(new Uint8Array(this.view.buffer).subarray(this.viewOffset, this.viewOffset + stringLength));
26930
26937
  this.viewOffset += stringLength;
26931
26938
  return string;
26932
26939
  }
@@ -29238,7 +29245,7 @@ function nearestSearch(node, target, best = { dist: Infinity, index: -1 }) {
29238
29245
  }
29239
29246
  return best;
29240
29247
  }
29241
- const jsContent = '(function() {\n "use strict";\n const FullBodyColorPalette = [\n {\n "brickColorId": 361,\n "hexColor": "#564236",\n "name": "Dirt brown"\n },\n {\n "brickColorId": 192,\n "hexColor": "#694028",\n "name": "Reddish brown"\n },\n {\n "brickColorId": 217,\n "hexColor": "#7C5C46",\n "name": "Brown"\n },\n {\n "brickColorId": 153,\n "hexColor": "#957977",\n "name": "Sand red"\n },\n {\n "brickColorId": 359,\n "hexColor": "#AF9483",\n "name": "Linen"\n },\n {\n "brickColorId": 352,\n "hexColor": "#C7AC78",\n "name": "Burlap"\n },\n {\n "brickColorId": 5,\n "hexColor": "#D7C59A",\n "name": "Brick yellow"\n },\n {\n "brickColorId": 101,\n "hexColor": "#DA867A",\n "name": "Medium red"\n },\n {\n "brickColorId": 1007,\n "hexColor": "#A34B4B",\n "name": "Dusty Rose"\n },\n {\n "brickColorId": 1014,\n "hexColor": "#AA5500",\n "name": "CGA brown"\n },\n {\n "brickColorId": 38,\n "hexColor": "#A05F35",\n "name": "Dark orange"\n },\n {\n "brickColorId": 18,\n "hexColor": "#CC8E69",\n "name": "Nougat"\n },\n {\n "brickColorId": 125,\n "hexColor": "#EAB892",\n "name": "Light orange"\n },\n {\n "brickColorId": 1030,\n "hexColor": "#FFCC99",\n "name": "Pastel brown"\n },\n {\n "brickColorId": 133,\n "hexColor": "#D5733D",\n "name": "Neon orange"\n },\n {\n "brickColorId": 106,\n "hexColor": "#DA8541",\n "name": "Bright orange"\n },\n {\n "brickColorId": 105,\n "hexColor": "#E29B40",\n "name": "Br. yellowish orange"\n },\n {\n "brickColorId": 1017,\n "hexColor": "#FFAF00",\n "name": "Deep orange"\n },\n {\n "brickColorId": 24,\n "hexColor": "#F5CD30",\n "name": "Bright yellow"\n },\n {\n "brickColorId": 334,\n "hexColor": "#F8D96D",\n "name": "Daisy orange"\n },\n {\n "brickColorId": 226,\n "hexColor": "#FDEA8D",\n "name": "Cool yellow"\n },\n {\n "brickColorId": 141,\n "hexColor": "#27462D",\n "name": "Earth green"\n },\n {\n "brickColorId": 1021,\n "hexColor": "#3A7D15",\n "name": "Camo"\n },\n {\n "brickColorId": 28,\n "hexColor": "#287F47",\n "name": "Dark green"\n },\n {\n "brickColorId": 37,\n "hexColor": "#4B974B",\n "name": "Bright green"\n },\n {\n "brickColorId": 310,\n "hexColor": "#5B9A4C",\n "name": "Shamrock"\n },\n {\n "brickColorId": 317,\n "hexColor": "#7C9C6B",\n "name": "Moss"\n },\n {\n "brickColorId": 119,\n "hexColor": "#A4BD47",\n "name": "Br. yellowish green"\n },\n {\n "brickColorId": 1011,\n "hexColor": "#002060",\n "name": "Navy blue"\n },\n {\n "brickColorId": 1012,\n "hexColor": "#2154B9",\n "name": "Deep blue"\n },\n {\n "brickColorId": 1010,\n "hexColor": "#0000FF",\n "name": "Really blue"\n },\n {\n "brickColorId": 23,\n "hexColor": "#0D69AC",\n "name": "Bright blue"\n },\n {\n "brickColorId": 305,\n "hexColor": "#527CAE",\n "name": "Steel blue"\n },\n {\n "brickColorId": 102,\n "hexColor": "#6E99CA",\n "name": "Medium blue"\n },\n {\n "brickColorId": 45,\n "hexColor": "#B4D2E4",\n "name": "Light blue"\n },\n {\n "brickColorId": 107,\n "hexColor": "#008F9C",\n "name": "Bright bluish green"\n },\n {\n "brickColorId": 1018,\n "hexColor": "#12EED4",\n "name": "Teal"\n },\n {\n "brickColorId": 1027,\n "hexColor": "#9FF3E9",\n "name": "Pastel blue-green"\n },\n {\n "brickColorId": 1019,\n "hexColor": "#00FFFF",\n "name": "Toothpaste"\n },\n {\n "brickColorId": 1013,\n "hexColor": "#04AFEC",\n "name": "Cyan"\n },\n {\n "brickColorId": 11,\n "hexColor": "#80BBDC",\n "name": "Pastel Blue"\n },\n {\n "brickColorId": 1024,\n "hexColor": "#AFDDFF",\n "name": "Pastel light blue"\n },\n {\n "brickColorId": 104,\n "hexColor": "#6B327C",\n "name": "Bright violet"\n },\n {\n "brickColorId": 1023,\n "hexColor": "#8C5B9F",\n "name": "Lavender"\n },\n {\n "brickColorId": 321,\n "hexColor": "#A75E9B",\n "name": "Lilac"\n },\n {\n "brickColorId": 1015,\n "hexColor": "#AA00AA",\n "name": "Magenta"\n },\n {\n "brickColorId": 1031,\n "hexColor": "#6225D1",\n "name": "Royal purple"\n },\n {\n "brickColorId": 1006,\n "hexColor": "#B480FF",\n "name": "Alder"\n },\n {\n "brickColorId": 1026,\n "hexColor": "#B1A7FF",\n "name": "Pastel violet"\n },\n {\n "brickColorId": 21,\n "hexColor": "#C4281C",\n "name": "Bright red"\n },\n {\n "brickColorId": 1004,\n "hexColor": "#FF0000",\n "name": "Really red"\n },\n {\n "brickColorId": 1032,\n "hexColor": "#FF00BF",\n "name": "Hot pink"\n },\n {\n "brickColorId": 1016,\n "hexColor": "#FF66CC",\n "name": "Pink"\n },\n {\n "brickColorId": 330,\n "hexColor": "#FF98DC",\n "name": "Carnation pink"\n },\n {\n "brickColorId": 9,\n "hexColor": "#E8BAC8",\n "name": "Light reddish violet"\n },\n {\n "brickColorId": 1025,\n "hexColor": "#FFC9C9",\n "name": "Pastel orange"\n },\n {\n "brickColorId": 364,\n "hexColor": "#5A4C42",\n "name": "Dark taupe"\n },\n {\n "brickColorId": 351,\n "hexColor": "#BC9B5D",\n "name": "Cork"\n },\n {\n "brickColorId": 1008,\n "hexColor": "#C1BE42",\n "name": "Olive"\n },\n {\n "brickColorId": 29,\n "hexColor": "#A1C48C",\n "name": "Medium green"\n },\n {\n "brickColorId": 1022,\n "hexColor": "#7F8E64",\n "name": "Grime"\n },\n {\n "brickColorId": 151,\n "hexColor": "#789082",\n "name": "Sand green"\n },\n {\n "brickColorId": 135,\n "hexColor": "#74869D",\n "name": "Sand blue"\n },\n {\n "brickColorId": 1020,\n "hexColor": "#00FF00",\n "name": "Lime green"\n },\n {\n "brickColorId": 1028,\n "hexColor": "#CCFFCC",\n "name": "Pastel green"\n },\n {\n "brickColorId": 1009,\n "hexColor": "#FFFF00",\n "name": "New Yeller"\n },\n {\n "brickColorId": 1029,\n "hexColor": "#FFFFCC",\n "name": "Pastel yellow"\n },\n {\n "brickColorId": 1003,\n "hexColor": "#111111",\n "name": "Really black"\n },\n {\n "brickColorId": 26,\n "hexColor": "#1B2A35",\n "name": "Black"\n },\n {\n "brickColorId": 199,\n "hexColor": "#635F62",\n "name": "Dark stone grey"\n },\n {\n "brickColorId": 194,\n "hexColor": "#A3A2A5",\n "name": "Medium stone grey"\n },\n {\n "brickColorId": 1002,\n "hexColor": "#CDCDCD",\n "name": "Mid gray"\n },\n {\n "brickColorId": 208,\n "hexColor": "#E5E4DF",\n "name": "Light stone grey"\n },\n {\n "brickColorId": 1,\n "hexColor": "#F2F3F3",\n "name": "White"\n },\n {\n "brickColorId": 1001,\n "hexColor": "#F8F8F8",\n "name": "Institutional white"\n }\n ];\n const RegularBodyColors = [\n "5A4C42",\n "7C5C46",\n "AF9483",\n "CC8E69",\n "EAB892",\n "564236",\n "694028",\n "BC9B5D",\n "c7ac78",\n "d7c59a",\n "957977",\n "a34b4b",\n "da867a",\n "ffc9c9",\n "ff98dc",\n "74869d",\n "527cae",\n "80bbdc",\n "b1a7ff",\n "a75e9b",\n "008f9c",\n "5b9a4c",\n "7c9c6b",\n "a1c48c",\n "e29b40",\n "f5cd30",\n "f8d96d",\n "635f62",\n "cdcdcd",\n "f8f8f8"\n ];\n for (let i = 0; i < RegularBodyColors.length; i++) {\n const color = RegularBodyColors[i];\n RegularBodyColors[i] = color.toUpperCase();\n }\n const FullBodyColors = [];\n for (const colorDetails of FullBodyColorPalette) {\n FullBodyColors.push(colorDetails.hexColor.substring(1));\n }\n function calculateMagnitude3D(x, y, z) {\n return Math.sqrt(x * x + y * y + z * z);\n }\n function magnitude(v) {\n return calculateMagnitude3D(v[0], v[1], v[2]);\n }\n function minus(v0, v1) {\n return [v0[0] - v1[0], v0[1] - v1[1], v0[2] - v1[2]];\n }\n function distance(v0, v1) {\n return magnitude(minus(v1, v0));\n }\n class KDNode {\n point;\n index;\n axis;\n left = null;\n right = null;\n constructor(point, index, axis) {\n this.point = point;\n this.index = index;\n this.axis = axis;\n }\n }\n function buildKDTree(points, indices, depth = 0) {\n if (points.length === 0) return null;\n const axis = depth % 3;\n const sorted = points.map((p, i) => ({ p, index: indices[i] })).sort((a, b) => a.p[axis] - b.p[axis]);\n const mid = Math.floor(sorted.length / 2);\n const node = new KDNode(sorted[mid].p, sorted[mid].index, axis);\n const leftPoints = sorted.slice(0, mid).map((x) => x.p);\n const leftIndices = sorted.slice(0, mid).map((x) => x.index);\n const rightPoints = sorted.slice(mid + 1).map((x) => x.p);\n const rightIndices = sorted.slice(mid + 1).map((x) => x.index);\n node.left = buildKDTree(leftPoints, leftIndices, depth + 1);\n node.right = buildKDTree(rightPoints, rightIndices, depth + 1);\n return node;\n }\n function siftUp(heap, i) {\n while (i > 0) {\n const p = i - 1 >> 1;\n if (heap[p].dist >= heap[i].dist) break;\n const tmp = heap[p];\n heap[p] = heap[i];\n heap[i] = tmp;\n i = p;\n }\n }\n function siftDown(heap, i) {\n const n = heap.length;\n while (true) {\n let largest = i;\n const l = (i << 1) + 1;\n const r = l + 1;\n if (l < n && heap[l].dist > heap[largest].dist) largest = l;\n if (r < n && heap[r].dist > heap[largest].dist) largest = r;\n if (largest === i) break;\n const tmp = heap[i];\n heap[i] = heap[largest];\n heap[largest] = tmp;\n i = largest;\n }\n }\n function heapPushMax(heap, item, k) {\n if (heap.length < k) {\n heap.push(item);\n siftUp(heap, heap.length - 1);\n return;\n }\n if (item.dist >= heap[0].dist) return;\n heap[0] = item;\n siftDown(heap, 0);\n }\n function distSq(a, b) {\n const dx = a[0] - b[0];\n const dy = a[1] - b[1];\n const dz = a[2] - b[2];\n return dx * dx + dy * dy + dz * dz;\n }\n function knnSearch(node, target, k, heap = []) {\n if (!node) return heap;\n const axis = node.axis;\n const dist = distSq(target, node.point);\n heapPushMax(heap, { dist, index: node.index }, k);\n const diff = target[axis] - node.point[axis];\n const primary = diff < 0 ? node.left : node.right;\n const secondary = diff < 0 ? node.right : node.left;\n knnSearch(primary, target, k, heap);\n const worstDist = heap.length < k ? Infinity : heap[0].dist;\n if (diff * diff < worstDist) {\n knnSearch(secondary, target, k, heap);\n }\n if (node === null) {\n heap.sort((a, b) => a.dist - b.dist);\n }\n return heap;\n }\n function nearestSearch(node, target, best = { dist: Infinity, index: -1 }) {\n if (!node) return best;\n const dist = distance(target, node.point);\n if (dist < best.dist) {\n best = { dist, index: node.index };\n }\n const axis = node.axis;\n const diff = target[axis] - node.point[axis];\n const primary = diff < 0 ? node.left : node.right;\n const secondary = diff < 0 ? node.right : node.left;\n best = nearestSearch(primary, target, best);\n if (Math.abs(diff) < best.dist) {\n best = nearestSearch(secondary, target, best);\n }\n return best;\n }\n function luDecompose(A) {\n const n = A.length;\n const LU = A;\n const P = new Int32Array(n);\n for (let i = 0; i < n; i++) P[i] = i;\n for (let k = 0; k < n; k++) {\n let pivot = k;\n for (let i = k + 1; i < n; i++) {\n if (Math.abs(LU[i][k]) > Math.abs(LU[pivot][k])) {\n pivot = i;\n }\n }\n if (pivot !== k) {\n const tmpRow = LU[k];\n LU[k] = LU[pivot];\n LU[pivot] = tmpRow;\n const tmpP = P[k];\n P[k] = P[pivot];\n P[pivot] = tmpP;\n }\n const pivotVal = LU[k][k];\n for (let i = k + 1; i < n; i++) {\n LU[i][k] /= pivotVal;\n const mult = LU[i][k];\n const rowI = LU[i];\n const rowK = LU[k];\n for (let j = k + 1; j < n; j++) {\n rowI[j] -= mult * rowK[j];\n }\n }\n }\n return { LU, P };\n }\n function luSolve({ LU, P }, b) {\n const n = LU.length;\n const x = new Float32Array(n);\n for (let i = 0; i < n; i++) {\n x[i] = b[P[i]];\n }\n for (let i = 0; i < n; i++) {\n const row = LU[i];\n let sum = x[i];\n for (let j = 0; j < i; j++) {\n sum -= row[j] * x[j];\n }\n x[i] = sum;\n }\n for (let i = n - 1; i >= 0; i--) {\n const row = LU[i];\n let sum = x[i];\n for (let j = i + 1; j < n; j++) {\n sum -= row[j] * x[j];\n }\n x[i] = sum / row[i];\n }\n return x;\n }\n function patchRBFWorkerFunc([_A, _bx, _by, _bz]) {\n const A = _A.map((r) => new Float32Array(r));\n const bx = new Float32Array(_bx);\n const by = new Float32Array(_by);\n const bz = new Float32Array(_bz);\n const LU = luDecompose(A);\n const wx = luSolve(LU, bx);\n const wy = luSolve(LU, by);\n const wz = luSolve(LU, bz);\n const n = wx.length;\n const result = new Float32Array(n * 3);\n for (let i = 0; i < n; i++) {\n result[i * 3 + 0] = wx[i];\n result[i * 3 + 1] = wy[i];\n result[i * 3 + 2] = wz[i];\n }\n return result.buffer;\n }\n function RBFDeformerSolveAsync([patchCount, detailsCount, epsilon, importantIndicesBuf, refVertsBuf, distVertsBuf, meshVertsBuf, meshBonesBuf]) {\n const importantIndices = new Uint16Array(importantIndicesBuf);\n const refVerts = new Float32Array(refVertsBuf);\n const distVerts = new Float32Array(distVertsBuf);\n const meshVerts = new Float32Array(meshVertsBuf);\n const meshBones = new Float32Array(meshBonesBuf);\n const refCount = refVerts.length / 3;\n const meshVertCount = meshVerts.length / 3;\n const meshBoneCount = meshBones.length / 3;\n const points = new Array(refCount);\n const indices = new Array(refCount);\n for (let i = 0; i < refCount; i++) {\n const refPos = [refVerts[i * 3 + 0], refVerts[i * 3 + 1], refVerts[i * 3 + 2]];\n points[i] = refPos;\n indices[i] = i;\n }\n const controlKD = buildKDTree(points, indices);\n const step = Math.max(1, Math.floor(refCount / patchCount));\n const patchCenters = [];\n for (let i = 0; i < refCount; i += step) {\n const refPos = [refVerts[i * 3 + 0], refVerts[i * 3 + 1], refVerts[i * 3 + 2]];\n patchCenters.push(refPos);\n if (patchCenters.length >= patchCount) break;\n }\n const neighborIndices = new Array(patchCenters.length);\n const weights = new Array(patchCenters.length);\n const patchIndices = patchCenters.map((_, i) => i);\n const patchKD = buildKDTree(patchCenters, patchIndices);\n const isUsedArr = new Array(patchCenters.length).fill(false);\n const nearestPatch = new Uint16Array(meshVertCount + meshBoneCount);\n for (let i = 0; i < meshVertCount; i++) {\n const vec = [meshVerts[i * 3 + 0], meshVerts[i * 3 + 1], meshVerts[i * 3 + 2]];\n const nearestPatchNode = nearestSearch(patchKD, vec);\n isUsedArr[nearestPatchNode.index] = true;\n nearestPatch[i] = nearestPatchNode.index;\n }\n for (let i = 0; i < meshBoneCount; i++) {\n const vec = [meshBones[i * 3 + 0], meshBones[i * 3 + 1], meshBones[i * 3 + 2]];\n const nearestPatchNode = nearestSearch(patchKD, vec);\n isUsedArr[nearestPatchNode.index] = true;\n nearestPatch[i + meshVertCount] = nearestPatchNode.index;\n }\n for (let i = 0; i < patchCenters.length; i++) {\n if (!isUsedArr[i]) {\n continue;\n }\n const centerPos = patchCenters[i];\n const neighbors = knnSearch(controlKD, centerPos, detailsCount);\n const foundNeighborIndices = neighbors.map((n) => n.index);\n for (const important of importantIndices) {\n if (!foundNeighborIndices.includes(important)) {\n foundNeighborIndices.push(important);\n }\n }\n neighborIndices[i] = new Uint16Array(foundNeighborIndices);\n }\n const A_array = new Array(patchCenters.length);\n for (let p = 0; p < patchCenters.length; p++) {\n const patchNeighborIndices = neighborIndices[p];\n if (!patchNeighborIndices) continue;\n const K = patchNeighborIndices.length;\n const positions = new Array(patchNeighborIndices.length);\n for (let i = 0; i < patchNeighborIndices.length; i++) {\n const j = patchNeighborIndices[i];\n const refPos = [refVerts[j * 3 + 0], refVerts[j * 3 + 1], refVerts[j * 3 + 2]];\n positions[i] = refPos;\n }\n const A = new Array(K);\n for (let i = 0; i < K; i++) {\n A[i] = new Float32Array(K);\n }\n for (let i = 0; i < K; i++) {\n const [pix, piy, piz] = positions[i];\n for (let j = i + 1; j < K; j++) {\n const [pjx, pjy, pjz] = positions[j];\n const dist = Math.sqrt((pix - pjx) * (pix - pjx) + (piy - pjy) * (piy - pjy) + (piz - pjz) * (piz - pjz));\n A[i][j] = dist;\n A[j][i] = dist;\n }\n A[i][i] = epsilon;\n }\n A_array[p] = A;\n }\n for (let p = 0; p < patchCenters.length; p++) {\n if (!isUsedArr[p]) {\n continue;\n }\n const patchNeighborIndices = neighborIndices[p];\n const K = patchNeighborIndices.length;\n const usedRef = new Array(K);\n const usedDist = new Array(K);\n for (let i = 0; i < K; i++) {\n const idx = patchNeighborIndices[i];\n const j = idx;\n const refPos = [refVerts[j * 3 + 0], refVerts[j * 3 + 1], refVerts[j * 3 + 2]];\n const distPos = [distVerts[j * 3 + 0], distVerts[j * 3 + 1], distVerts[j * 3 + 2]];\n usedRef[i] = refPos;\n usedDist[i] = distPos;\n }\n const A = A_array[p];\n const bx = new Float32Array(K);\n const by = new Float32Array(K);\n const bz = new Float32Array(K);\n for (let i = 0; i < K; i++) {\n const dr = usedDist[i];\n const rr = usedRef[i];\n bx[i] = dr[0] - rr[0];\n by[i] = dr[1] - rr[1];\n bz[i] = dr[2] - rr[2];\n }\n const Abuffers = A.map((r) => r.buffer);\n weights[p] = new Float32Array(patchRBFWorkerFunc([Abuffers, bx.buffer, by.buffer, bz.buffer]));\n }\n const neighborIndicesBuf = neighborIndices.map((a) => {\n return a.buffer;\n });\n const weightsBuf = weights.map((a) => {\n return a.buffer;\n });\n return [neighborIndicesBuf, weightsBuf, nearestPatch.buffer];\n }\n const WorkerTypeToFunction = {\n "patchRBF": patchRBFWorkerFunc,\n "RBFDeformerSolveAsync": RBFDeformerSolveAsync\n };\n function getWorkerOnMessage() {\n return function(event) {\n const [id, type, data] = event.data;\n const func = WorkerTypeToFunction[type];\n self.postMessage([id, func(data)]);\n };\n }\n onmessage = getWorkerOnMessage();\n})();\n';
29248
+ const jsContent = '(function() {\n "use strict";\n new TextDecoder();\n new TextDecoder();\n const FullBodyColorPalette = [\n {\n "brickColorId": 361,\n "hexColor": "#564236",\n "name": "Dirt brown"\n },\n {\n "brickColorId": 192,\n "hexColor": "#694028",\n "name": "Reddish brown"\n },\n {\n "brickColorId": 217,\n "hexColor": "#7C5C46",\n "name": "Brown"\n },\n {\n "brickColorId": 153,\n "hexColor": "#957977",\n "name": "Sand red"\n },\n {\n "brickColorId": 359,\n "hexColor": "#AF9483",\n "name": "Linen"\n },\n {\n "brickColorId": 352,\n "hexColor": "#C7AC78",\n "name": "Burlap"\n },\n {\n "brickColorId": 5,\n "hexColor": "#D7C59A",\n "name": "Brick yellow"\n },\n {\n "brickColorId": 101,\n "hexColor": "#DA867A",\n "name": "Medium red"\n },\n {\n "brickColorId": 1007,\n "hexColor": "#A34B4B",\n "name": "Dusty Rose"\n },\n {\n "brickColorId": 1014,\n "hexColor": "#AA5500",\n "name": "CGA brown"\n },\n {\n "brickColorId": 38,\n "hexColor": "#A05F35",\n "name": "Dark orange"\n },\n {\n "brickColorId": 18,\n "hexColor": "#CC8E69",\n "name": "Nougat"\n },\n {\n "brickColorId": 125,\n "hexColor": "#EAB892",\n "name": "Light orange"\n },\n {\n "brickColorId": 1030,\n "hexColor": "#FFCC99",\n "name": "Pastel brown"\n },\n {\n "brickColorId": 133,\n "hexColor": "#D5733D",\n "name": "Neon orange"\n },\n {\n "brickColorId": 106,\n "hexColor": "#DA8541",\n "name": "Bright orange"\n },\n {\n "brickColorId": 105,\n "hexColor": "#E29B40",\n "name": "Br. yellowish orange"\n },\n {\n "brickColorId": 1017,\n "hexColor": "#FFAF00",\n "name": "Deep orange"\n },\n {\n "brickColorId": 24,\n "hexColor": "#F5CD30",\n "name": "Bright yellow"\n },\n {\n "brickColorId": 334,\n "hexColor": "#F8D96D",\n "name": "Daisy orange"\n },\n {\n "brickColorId": 226,\n "hexColor": "#FDEA8D",\n "name": "Cool yellow"\n },\n {\n "brickColorId": 141,\n "hexColor": "#27462D",\n "name": "Earth green"\n },\n {\n "brickColorId": 1021,\n "hexColor": "#3A7D15",\n "name": "Camo"\n },\n {\n "brickColorId": 28,\n "hexColor": "#287F47",\n "name": "Dark green"\n },\n {\n "brickColorId": 37,\n "hexColor": "#4B974B",\n "name": "Bright green"\n },\n {\n "brickColorId": 310,\n "hexColor": "#5B9A4C",\n "name": "Shamrock"\n },\n {\n "brickColorId": 317,\n "hexColor": "#7C9C6B",\n "name": "Moss"\n },\n {\n "brickColorId": 119,\n "hexColor": "#A4BD47",\n "name": "Br. yellowish green"\n },\n {\n "brickColorId": 1011,\n "hexColor": "#002060",\n "name": "Navy blue"\n },\n {\n "brickColorId": 1012,\n "hexColor": "#2154B9",\n "name": "Deep blue"\n },\n {\n "brickColorId": 1010,\n "hexColor": "#0000FF",\n "name": "Really blue"\n },\n {\n "brickColorId": 23,\n "hexColor": "#0D69AC",\n "name": "Bright blue"\n },\n {\n "brickColorId": 305,\n "hexColor": "#527CAE",\n "name": "Steel blue"\n },\n {\n "brickColorId": 102,\n "hexColor": "#6E99CA",\n "name": "Medium blue"\n },\n {\n "brickColorId": 45,\n "hexColor": "#B4D2E4",\n "name": "Light blue"\n },\n {\n "brickColorId": 107,\n "hexColor": "#008F9C",\n "name": "Bright bluish green"\n },\n {\n "brickColorId": 1018,\n "hexColor": "#12EED4",\n "name": "Teal"\n },\n {\n "brickColorId": 1027,\n "hexColor": "#9FF3E9",\n "name": "Pastel blue-green"\n },\n {\n "brickColorId": 1019,\n "hexColor": "#00FFFF",\n "name": "Toothpaste"\n },\n {\n "brickColorId": 1013,\n "hexColor": "#04AFEC",\n "name": "Cyan"\n },\n {\n "brickColorId": 11,\n "hexColor": "#80BBDC",\n "name": "Pastel Blue"\n },\n {\n "brickColorId": 1024,\n "hexColor": "#AFDDFF",\n "name": "Pastel light blue"\n },\n {\n "brickColorId": 104,\n "hexColor": "#6B327C",\n "name": "Bright violet"\n },\n {\n "brickColorId": 1023,\n "hexColor": "#8C5B9F",\n "name": "Lavender"\n },\n {\n "brickColorId": 321,\n "hexColor": "#A75E9B",\n "name": "Lilac"\n },\n {\n "brickColorId": 1015,\n "hexColor": "#AA00AA",\n "name": "Magenta"\n },\n {\n "brickColorId": 1031,\n "hexColor": "#6225D1",\n "name": "Royal purple"\n },\n {\n "brickColorId": 1006,\n "hexColor": "#B480FF",\n "name": "Alder"\n },\n {\n "brickColorId": 1026,\n "hexColor": "#B1A7FF",\n "name": "Pastel violet"\n },\n {\n "brickColorId": 21,\n "hexColor": "#C4281C",\n "name": "Bright red"\n },\n {\n "brickColorId": 1004,\n "hexColor": "#FF0000",\n "name": "Really red"\n },\n {\n "brickColorId": 1032,\n "hexColor": "#FF00BF",\n "name": "Hot pink"\n },\n {\n "brickColorId": 1016,\n "hexColor": "#FF66CC",\n "name": "Pink"\n },\n {\n "brickColorId": 330,\n "hexColor": "#FF98DC",\n "name": "Carnation pink"\n },\n {\n "brickColorId": 9,\n "hexColor": "#E8BAC8",\n "name": "Light reddish violet"\n },\n {\n "brickColorId": 1025,\n "hexColor": "#FFC9C9",\n "name": "Pastel orange"\n },\n {\n "brickColorId": 364,\n "hexColor": "#5A4C42",\n "name": "Dark taupe"\n },\n {\n "brickColorId": 351,\n "hexColor": "#BC9B5D",\n "name": "Cork"\n },\n {\n "brickColorId": 1008,\n "hexColor": "#C1BE42",\n "name": "Olive"\n },\n {\n "brickColorId": 29,\n "hexColor": "#A1C48C",\n "name": "Medium green"\n },\n {\n "brickColorId": 1022,\n "hexColor": "#7F8E64",\n "name": "Grime"\n },\n {\n "brickColorId": 151,\n "hexColor": "#789082",\n "name": "Sand green"\n },\n {\n "brickColorId": 135,\n "hexColor": "#74869D",\n "name": "Sand blue"\n },\n {\n "brickColorId": 1020,\n "hexColor": "#00FF00",\n "name": "Lime green"\n },\n {\n "brickColorId": 1028,\n "hexColor": "#CCFFCC",\n "name": "Pastel green"\n },\n {\n "brickColorId": 1009,\n "hexColor": "#FFFF00",\n "name": "New Yeller"\n },\n {\n "brickColorId": 1029,\n "hexColor": "#FFFFCC",\n "name": "Pastel yellow"\n },\n {\n "brickColorId": 1003,\n "hexColor": "#111111",\n "name": "Really black"\n },\n {\n "brickColorId": 26,\n "hexColor": "#1B2A35",\n "name": "Black"\n },\n {\n "brickColorId": 199,\n "hexColor": "#635F62",\n "name": "Dark stone grey"\n },\n {\n "brickColorId": 194,\n "hexColor": "#A3A2A5",\n "name": "Medium stone grey"\n },\n {\n "brickColorId": 1002,\n "hexColor": "#CDCDCD",\n "name": "Mid gray"\n },\n {\n "brickColorId": 208,\n "hexColor": "#E5E4DF",\n "name": "Light stone grey"\n },\n {\n "brickColorId": 1,\n "hexColor": "#F2F3F3",\n "name": "White"\n },\n {\n "brickColorId": 1001,\n "hexColor": "#F8F8F8",\n "name": "Institutional white"\n }\n ];\n const RegularBodyColors = [\n "5A4C42",\n "7C5C46",\n "AF9483",\n "CC8E69",\n "EAB892",\n "564236",\n "694028",\n "BC9B5D",\n "c7ac78",\n "d7c59a",\n "957977",\n "a34b4b",\n "da867a",\n "ffc9c9",\n "ff98dc",\n "74869d",\n "527cae",\n "80bbdc",\n "b1a7ff",\n "a75e9b",\n "008f9c",\n "5b9a4c",\n "7c9c6b",\n "a1c48c",\n "e29b40",\n "f5cd30",\n "f8d96d",\n "635f62",\n "cdcdcd",\n "f8f8f8"\n ];\n for (let i = 0; i < RegularBodyColors.length; i++) {\n const color = RegularBodyColors[i];\n RegularBodyColors[i] = color.toUpperCase();\n }\n const FullBodyColors = [];\n for (const colorDetails of FullBodyColorPalette) {\n FullBodyColors.push(colorDetails.hexColor.substring(1));\n }\n function calculateMagnitude3D(x, y, z) {\n return Math.sqrt(x * x + y * y + z * z);\n }\n function magnitude(v) {\n return calculateMagnitude3D(v[0], v[1], v[2]);\n }\n function minus(v0, v1) {\n return [v0[0] - v1[0], v0[1] - v1[1], v0[2] - v1[2]];\n }\n function distance(v0, v1) {\n return magnitude(minus(v1, v0));\n }\n class KDNode {\n point;\n index;\n axis;\n left = null;\n right = null;\n constructor(point, index, axis) {\n this.point = point;\n this.index = index;\n this.axis = axis;\n }\n }\n function buildKDTree(points, indices, depth = 0) {\n if (points.length === 0) return null;\n const axis = depth % 3;\n const sorted = points.map((p, i) => ({ p, index: indices[i] })).sort((a, b) => a.p[axis] - b.p[axis]);\n const mid = Math.floor(sorted.length / 2);\n const node = new KDNode(sorted[mid].p, sorted[mid].index, axis);\n const leftPoints = sorted.slice(0, mid).map((x) => x.p);\n const leftIndices = sorted.slice(0, mid).map((x) => x.index);\n const rightPoints = sorted.slice(mid + 1).map((x) => x.p);\n const rightIndices = sorted.slice(mid + 1).map((x) => x.index);\n node.left = buildKDTree(leftPoints, leftIndices, depth + 1);\n node.right = buildKDTree(rightPoints, rightIndices, depth + 1);\n return node;\n }\n function siftUp(heap, i) {\n while (i > 0) {\n const p = i - 1 >> 1;\n if (heap[p].dist >= heap[i].dist) break;\n const tmp = heap[p];\n heap[p] = heap[i];\n heap[i] = tmp;\n i = p;\n }\n }\n function siftDown(heap, i) {\n const n = heap.length;\n while (true) {\n let largest = i;\n const l = (i << 1) + 1;\n const r = l + 1;\n if (l < n && heap[l].dist > heap[largest].dist) largest = l;\n if (r < n && heap[r].dist > heap[largest].dist) largest = r;\n if (largest === i) break;\n const tmp = heap[i];\n heap[i] = heap[largest];\n heap[largest] = tmp;\n i = largest;\n }\n }\n function heapPushMax(heap, item, k) {\n if (heap.length < k) {\n heap.push(item);\n siftUp(heap, heap.length - 1);\n return;\n }\n if (item.dist >= heap[0].dist) return;\n heap[0] = item;\n siftDown(heap, 0);\n }\n function distSq(a, b) {\n const dx = a[0] - b[0];\n const dy = a[1] - b[1];\n const dz = a[2] - b[2];\n return dx * dx + dy * dy + dz * dz;\n }\n function knnSearch(node, target, k, heap = []) {\n if (!node) return heap;\n const axis = node.axis;\n const dist = distSq(target, node.point);\n heapPushMax(heap, { dist, index: node.index }, k);\n const diff = target[axis] - node.point[axis];\n const primary = diff < 0 ? node.left : node.right;\n const secondary = diff < 0 ? node.right : node.left;\n knnSearch(primary, target, k, heap);\n const worstDist = heap.length < k ? Infinity : heap[0].dist;\n if (diff * diff < worstDist) {\n knnSearch(secondary, target, k, heap);\n }\n if (node === null) {\n heap.sort((a, b) => a.dist - b.dist);\n }\n return heap;\n }\n function nearestSearch(node, target, best = { dist: Infinity, index: -1 }) {\n if (!node) return best;\n const dist = distance(target, node.point);\n if (dist < best.dist) {\n best = { dist, index: node.index };\n }\n const axis = node.axis;\n const diff = target[axis] - node.point[axis];\n const primary = diff < 0 ? node.left : node.right;\n const secondary = diff < 0 ? node.right : node.left;\n best = nearestSearch(primary, target, best);\n if (Math.abs(diff) < best.dist) {\n best = nearestSearch(secondary, target, best);\n }\n return best;\n }\n function luDecompose(A) {\n const n = A.length;\n const LU = A;\n const P = new Int32Array(n);\n for (let i = 0; i < n; i++) P[i] = i;\n for (let k = 0; k < n; k++) {\n let pivot = k;\n for (let i = k + 1; i < n; i++) {\n if (Math.abs(LU[i][k]) > Math.abs(LU[pivot][k])) {\n pivot = i;\n }\n }\n if (pivot !== k) {\n const tmpRow = LU[k];\n LU[k] = LU[pivot];\n LU[pivot] = tmpRow;\n const tmpP = P[k];\n P[k] = P[pivot];\n P[pivot] = tmpP;\n }\n const pivotVal = LU[k][k];\n for (let i = k + 1; i < n; i++) {\n LU[i][k] /= pivotVal;\n const mult = LU[i][k];\n const rowI = LU[i];\n const rowK = LU[k];\n for (let j = k + 1; j < n; j++) {\n rowI[j] -= mult * rowK[j];\n }\n }\n }\n return { LU, P };\n }\n function luSolve({ LU, P }, b) {\n const n = LU.length;\n const x = new Float32Array(n);\n for (let i = 0; i < n; i++) {\n x[i] = b[P[i]];\n }\n for (let i = 0; i < n; i++) {\n const row = LU[i];\n let sum = x[i];\n for (let j = 0; j < i; j++) {\n sum -= row[j] * x[j];\n }\n x[i] = sum;\n }\n for (let i = n - 1; i >= 0; i--) {\n const row = LU[i];\n let sum = x[i];\n for (let j = i + 1; j < n; j++) {\n sum -= row[j] * x[j];\n }\n x[i] = sum / row[i];\n }\n return x;\n }\n function patchRBFWorkerFunc([_A, _bx, _by, _bz]) {\n const A = _A.map((r) => new Float32Array(r));\n const bx = new Float32Array(_bx);\n const by = new Float32Array(_by);\n const bz = new Float32Array(_bz);\n const LU = luDecompose(A);\n const wx = luSolve(LU, bx);\n const wy = luSolve(LU, by);\n const wz = luSolve(LU, bz);\n const n = wx.length;\n const result = new Float32Array(n * 3);\n for (let i = 0; i < n; i++) {\n result[i * 3 + 0] = wx[i];\n result[i * 3 + 1] = wy[i];\n result[i * 3 + 2] = wz[i];\n }\n return result.buffer;\n }\n function RBFDeformerSolveAsync([patchCount, detailsCount, epsilon, importantIndicesBuf, refVertsBuf, distVertsBuf, meshVertsBuf, meshBonesBuf]) {\n const importantIndices = new Uint16Array(importantIndicesBuf);\n const refVerts = new Float32Array(refVertsBuf);\n const distVerts = new Float32Array(distVertsBuf);\n const meshVerts = new Float32Array(meshVertsBuf);\n const meshBones = new Float32Array(meshBonesBuf);\n const refCount = refVerts.length / 3;\n const meshVertCount = meshVerts.length / 3;\n const meshBoneCount = meshBones.length / 3;\n const points = new Array(refCount);\n const indices = new Array(refCount);\n for (let i = 0; i < refCount; i++) {\n const refPos = [refVerts[i * 3 + 0], refVerts[i * 3 + 1], refVerts[i * 3 + 2]];\n points[i] = refPos;\n indices[i] = i;\n }\n const controlKD = buildKDTree(points, indices);\n const step = Math.max(1, Math.floor(refCount / patchCount));\n const patchCenters = [];\n for (let i = 0; i < refCount; i += step) {\n const refPos = [refVerts[i * 3 + 0], refVerts[i * 3 + 1], refVerts[i * 3 + 2]];\n patchCenters.push(refPos);\n if (patchCenters.length >= patchCount) break;\n }\n const neighborIndices = new Array(patchCenters.length);\n const weights = new Array(patchCenters.length);\n const patchIndices = patchCenters.map((_, i) => i);\n const patchKD = buildKDTree(patchCenters, patchIndices);\n const isUsedArr = new Array(patchCenters.length).fill(false);\n const nearestPatch = new Uint16Array(meshVertCount + meshBoneCount);\n for (let i = 0; i < meshVertCount; i++) {\n const vec = [meshVerts[i * 3 + 0], meshVerts[i * 3 + 1], meshVerts[i * 3 + 2]];\n const nearestPatchNode = nearestSearch(patchKD, vec);\n isUsedArr[nearestPatchNode.index] = true;\n nearestPatch[i] = nearestPatchNode.index;\n }\n for (let i = 0; i < meshBoneCount; i++) {\n const vec = [meshBones[i * 3 + 0], meshBones[i * 3 + 1], meshBones[i * 3 + 2]];\n const nearestPatchNode = nearestSearch(patchKD, vec);\n isUsedArr[nearestPatchNode.index] = true;\n nearestPatch[i + meshVertCount] = nearestPatchNode.index;\n }\n for (let i = 0; i < patchCenters.length; i++) {\n if (!isUsedArr[i]) {\n continue;\n }\n const centerPos = patchCenters[i];\n const neighbors = knnSearch(controlKD, centerPos, detailsCount);\n const foundNeighborIndices = neighbors.map((n) => n.index);\n for (const important of importantIndices) {\n if (!foundNeighborIndices.includes(important)) {\n foundNeighborIndices.push(important);\n }\n }\n neighborIndices[i] = new Uint16Array(foundNeighborIndices);\n }\n const A_array = new Array(patchCenters.length);\n for (let p = 0; p < patchCenters.length; p++) {\n const patchNeighborIndices = neighborIndices[p];\n if (!patchNeighborIndices) continue;\n const K = patchNeighborIndices.length;\n const positions = new Array(patchNeighborIndices.length);\n for (let i = 0; i < patchNeighborIndices.length; i++) {\n const j = patchNeighborIndices[i];\n const refPos = [refVerts[j * 3 + 0], refVerts[j * 3 + 1], refVerts[j * 3 + 2]];\n positions[i] = refPos;\n }\n const A = new Array(K);\n for (let i = 0; i < K; i++) {\n A[i] = new Float32Array(K);\n }\n for (let i = 0; i < K; i++) {\n const [pix, piy, piz] = positions[i];\n for (let j = i + 1; j < K; j++) {\n const [pjx, pjy, pjz] = positions[j];\n const dist = Math.sqrt((pix - pjx) * (pix - pjx) + (piy - pjy) * (piy - pjy) + (piz - pjz) * (piz - pjz));\n A[i][j] = dist;\n A[j][i] = dist;\n }\n A[i][i] = epsilon;\n }\n A_array[p] = A;\n }\n for (let p = 0; p < patchCenters.length; p++) {\n if (!isUsedArr[p]) {\n continue;\n }\n const patchNeighborIndices = neighborIndices[p];\n const K = patchNeighborIndices.length;\n const usedRef = new Array(K);\n const usedDist = new Array(K);\n for (let i = 0; i < K; i++) {\n const idx = patchNeighborIndices[i];\n const j = idx;\n const refPos = [refVerts[j * 3 + 0], refVerts[j * 3 + 1], refVerts[j * 3 + 2]];\n const distPos = [distVerts[j * 3 + 0], distVerts[j * 3 + 1], distVerts[j * 3 + 2]];\n usedRef[i] = refPos;\n usedDist[i] = distPos;\n }\n const A = A_array[p];\n const bx = new Float32Array(K);\n const by = new Float32Array(K);\n const bz = new Float32Array(K);\n for (let i = 0; i < K; i++) {\n const dr = usedDist[i];\n const rr = usedRef[i];\n bx[i] = dr[0] - rr[0];\n by[i] = dr[1] - rr[1];\n bz[i] = dr[2] - rr[2];\n }\n const Abuffers = A.map((r) => r.buffer);\n weights[p] = new Float32Array(patchRBFWorkerFunc([Abuffers, bx.buffer, by.buffer, bz.buffer]));\n }\n const neighborIndicesBuf = neighborIndices.map((a) => {\n return a.buffer;\n });\n const weightsBuf = weights.map((a) => {\n return a.buffer;\n });\n return [neighborIndicesBuf, weightsBuf, nearestPatch.buffer];\n }\n const WorkerTypeToFunction = {\n "patchRBF": patchRBFWorkerFunc,\n "RBFDeformerSolveAsync": RBFDeformerSolveAsync\n };\n function getWorkerOnMessage() {\n return function(event) {\n const [id, type, data] = event.data;\n const func = WorkerTypeToFunction[type];\n self.postMessage([id, func(data)]);\n };\n }\n onmessage = getWorkerOnMessage();\n})();\n';
29242
29249
  const blob = typeof self !== "undefined" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", jsContent], { type: "text/javascript;charset=utf-8" });
29243
29250
  function WorkerWrapper(options) {
29244
29251
  let objURL;
@@ -29537,16 +29544,16 @@ const FLAGS = {
29537
29544
  };
29538
29545
  function log(critical, ...args) {
29539
29546
  if (critical || FLAGS.VERBOSE_LOGGING) {
29540
- console.log(args);
29547
+ console.log(...args);
29541
29548
  }
29542
29549
  }
29543
29550
  function warn(critical, ...args) {
29544
29551
  if (critical || FLAGS.VERBOSE_LOGGING) {
29545
- console.warn(args);
29552
+ console.warn(...args);
29546
29553
  }
29547
29554
  }
29548
29555
  function error(...args) {
29549
- console.error(args);
29556
+ console.error(...args);
29550
29557
  }
29551
29558
  function time(label) {
29552
29559
  if (FLAGS.VERBOSE_LOGGING) {
@@ -30266,7 +30273,7 @@ class Instance {
30266
30273
  getPropertyNames() {
30267
30274
  return Array.from(this._properties.keys());
30268
30275
  }
30269
- setParent(instance) {
30276
+ setParent(instance, fireEvents = true) {
30270
30277
  if (!instance) {
30271
30278
  instance = void 0;
30272
30279
  }
@@ -30286,10 +30293,14 @@ class Instance {
30286
30293
  }
30287
30294
  if (instance) {
30288
30295
  instance.children.push(this);
30289
- instance.ChildAdded.Fire(this);
30290
- instance.AncestryChanged.Fire(this, instance);
30291
30296
  }
30292
- this.AncestryChanged.Fire(this, instance);
30297
+ if (fireEvents) {
30298
+ if (instance) {
30299
+ instance.ChildAdded.Fire(this);
30300
+ instance.AncestryChanged.Fire(this, instance);
30301
+ }
30302
+ this.AncestryChanged.Fire(this, instance);
30303
+ }
30293
30304
  }
30294
30305
  Destroy() {
30295
30306
  for (const connection of this._connectionReferences) {
@@ -30344,8 +30355,8 @@ class Instance {
30344
30355
  return childrenList;
30345
30356
  }
30346
30357
  GetDescendants() {
30347
- let descendants = this.children;
30348
- for (const child of this.children) {
30358
+ let descendants = this.GetChildren();
30359
+ for (const child of this.GetChildren()) {
30349
30360
  descendants = descendants.concat(child.GetDescendants());
30350
30361
  }
30351
30362
  return descendants;
@@ -30840,6 +30851,7 @@ class RBX {
30840
30851
  const content = new Content();
30841
30852
  content.sourceType = sourceTypes[i];
30842
30853
  switch (content.sourceType) {
30854
+ //2 - Uri, 0 - Object/None,
30843
30855
  case 0:
30844
30856
  break;
30845
30857
  case 1:
@@ -32583,6 +32595,7 @@ class Outfit {
32583
32595
  const usedIds = [];
32584
32596
  let totalAccessories = 0;
32585
32597
  let totalLayered = 0;
32598
+ let totalMakeup = 0;
32586
32599
  for (let i = 0; i < this.assets.length; i++) {
32587
32600
  const asset = this.assets[i];
32588
32601
  if (!(asset instanceof Asset)) {
@@ -32623,7 +32636,7 @@ class Outfit {
32623
32636
  });
32624
32637
  }
32625
32638
  }
32626
- if (LayeredAssetTypes.includes(asset.assetType.name)) {
32639
+ if (LayeredAssetTypes.includes(asset.assetType.name) && !MakeupAssetTypes.includes(asset.assetType.name)) {
32627
32640
  totalLayered += 1;
32628
32641
  if (totalLayered > 10) {
32629
32642
  issues.push({
@@ -32633,6 +32646,16 @@ class Outfit {
32633
32646
  });
32634
32647
  }
32635
32648
  }
32649
+ if (MakeupAssetTypes.includes(asset.assetType.name)) {
32650
+ totalMakeup += 1;
32651
+ if (totalMakeup > 6) {
32652
+ issues.push({
32653
+ "type": "MakeupLimit",
32654
+ "text": "Too much makeup",
32655
+ assetIndex: i
32656
+ });
32657
+ }
32658
+ }
32636
32659
  if (MaxOneOfAssetTypes.includes(asset.assetType.name)) {
32637
32660
  for (let j = 0; j < this.assets.length; j++) {
32638
32661
  const otherAsset = this.assets[j];
@@ -34527,7 +34550,7 @@ class FileMesh {
34527
34550
  decoderModule.destroy(faceArray);
34528
34551
  decoderModule.destroy(mesh);
34529
34552
  decoderModule.destroy(decoder);
34530
- console.log(this.coreMesh);
34553
+ log(false, this.coreMesh);
34531
34554
  }
34532
34555
  }
34533
34556
  readChunkFACS(view, version) {
@@ -36069,7 +36092,11 @@ const API = {
36069
36092
  }
36070
36093
  return await response.json();
36071
36094
  }
36072
- }
36095
+ },
36096
+ "RBLXGet": RBLXGet,
36097
+ "RBLXPost": RBLXPost,
36098
+ "RBLXDelete": RBLXDelete,
36099
+ "RBLXPatch": RBLXPatch
36073
36100
  };
36074
36101
  let currentLoadingThumbnails = false;
36075
36102
  function requestIdFromThumbnailInfo(thumbnailInfo) {
@@ -41185,7 +41212,7 @@ function TraverseRigFromAttachmentsInternal(self2, part, characterParts, buildJo
41185
41212
  if (attachment.className === "Attachment") {
41186
41213
  const attachmentName = attachment.Prop("Name");
41187
41214
  const findPos = attachmentName.indexOf(rigAttachmentName);
41188
- if (findPos) {
41215
+ if (findPos !== -1) {
41189
41216
  const jointName = attachmentName.substring(0, findPos);
41190
41217
  const joint = part.FindFirstChild(jointName);
41191
41218
  if (!joint || joint.className !== "Motor6D") {
@@ -41193,6 +41220,7 @@ function TraverseRigFromAttachmentsInternal(self2, part, characterParts, buildJo
41193
41220
  if (part !== characterPart) {
41194
41221
  const matchingAttachment = characterPart.FindFirstChild(attachmentName);
41195
41222
  if (matchingAttachment && matchingAttachment.className === "Attachment") {
41223
+ log(false, "matchingAtt", part, characterPart, attachmentName);
41196
41224
  AdjustRootRigAttachmentPosition(self2, part, characterPart, attachment, matchingAttachment);
41197
41225
  {
41198
41226
  createJoint(jointName, attachment, matchingAttachment);
@@ -42630,6 +42658,26 @@ class MeshDesc {
42630
42658
  const meshIdStr = child.Property("MeshId");
42631
42659
  this.mesh = meshIdStr;
42632
42660
  this.scaleIsRelative = true;
42661
+ if (!FLAGS.AVATAR_JOINT_UPGRADE) {
42662
+ if (child.FindFirstChildOfClass("Bone")) {
42663
+ this.canHaveSkinning = false;
42664
+ } else {
42665
+ if (child.Prop("Name").includes("Arm")) {
42666
+ const rig = child.parent;
42667
+ if (rig) {
42668
+ const humanoid = rig.FindFirstChildOfClass("Humanoid");
42669
+ if (humanoid) {
42670
+ const side = child.Prop("Name").startsWith("Right") ? "Right" : "Left";
42671
+ const handName = side + "Hand";
42672
+ const hand = rig.FindFirstChild(handName);
42673
+ if (hand && hand.FindFirstChildOfClass("Bone")) {
42674
+ this.canHaveSkinning = false;
42675
+ }
42676
+ }
42677
+ }
42678
+ }
42679
+ }
42680
+ }
42633
42681
  const surfaceAppearance = child.FindLastChildOfClass("SurfaceAppearance");
42634
42682
  if (surfaceAppearance) {
42635
42683
  const color = surfaceAppearance.HasProperty("Color") ? surfaceAppearance.Prop("Color") : new Color3(1, 1, 1);
@@ -43980,6 +44028,7 @@ function setBoneToCFrame$1(bone, cf) {
43980
44028
  bone.rotation.y = rad(cf.Orientation[1]);
43981
44029
  bone.rotation.z = rad(cf.Orientation[2]);
43982
44030
  }
44031
+ const BaseR15Bones = ["Root", "HumanoidRootNode", "LowerTorso", "UpperTorso", "RightUpperArm", "RightLowerArm", "RightHand", "LeftUpperArm", "LeftLowerArm", "LeftHand", "Head", "DynamicHead"];
43983
44032
  function getJointForInstances$1(parent, child, includeTransform) {
43984
44033
  const childMotor = child.FindFirstChildOfClass("Motor6D");
43985
44034
  const parentMotor = parent.FindFirstChildOfClass("Motor6D");
@@ -44007,6 +44056,21 @@ function boneIsChildOf(bone, parentName) {
44007
44056
  }
44008
44057
  return false;
44009
44058
  }
44059
+ function getBoneBaseR15Parent(bone) {
44060
+ let currentParent = bone.parent;
44061
+ if (!currentParent) return;
44062
+ while (currentParent && !BaseR15Bones.includes(currentParent.name)) {
44063
+ currentParent = currentParent.parent;
44064
+ }
44065
+ if (!currentParent) {
44066
+ return bone.parent;
44067
+ } else {
44068
+ return currentParent;
44069
+ }
44070
+ }
44071
+ function boneIsBaseR15(bone) {
44072
+ return BaseR15Bones.includes(bone.name);
44073
+ }
44010
44074
  function getMotorsInRig(rigChildren) {
44011
44075
  const motors = [];
44012
44076
  for (const child of rigChildren) {
@@ -44025,15 +44089,17 @@ function getBoneDependencies(rig) {
44025
44089
  let currentSearchOrigin = hrp ? ["Root"] : [];
44026
44090
  const children = rig.GetChildren();
44027
44091
  const motors = getMotorsInRig(children);
44092
+ const searchedParts = [];
44028
44093
  while (currentSearch.length > 0 && currentSearch[0]) {
44029
44094
  const newCurrentSearch = [];
44030
44095
  const newCurrentSearchOrigin = [];
44031
44096
  for (let i = 0; i < currentSearch.length; i++) {
44032
44097
  const toSearch = currentSearch[i];
44098
+ searchedParts.push(toSearch);
44033
44099
  const selfName = toSearch === hrp ? "HumanoidRootNode" : toSearch.Prop("Name");
44034
44100
  names.set(selfName, currentSearchOrigin[i]);
44035
44101
  for (const motor of motors) {
44036
- if (motor.Prop("Part0") === toSearch) {
44102
+ if (motor.Prop("Part0") === toSearch && !searchedParts.includes(motor.parent)) {
44037
44103
  newCurrentSearch.push(motor.parent);
44038
44104
  newCurrentSearchOrigin.push(selfName);
44039
44105
  }
@@ -44237,7 +44303,8 @@ let SkeletonDesc$1 = class SkeletonDesc {
44237
44303
  for (let i = 0; i < this.bones.length; i++) {
44238
44304
  const bone = this.bones[i];
44239
44305
  const partEquivalent = this.getPartEquivalent(selfInstance, bone.name);
44240
- const parentPartEquivalent = bone.parent ? bone.parent.name !== "HumanoidRootNode" ? this.getPartEquivalent(selfInstance, bone.parent.name) : humanoidRootPartEquivalent : void 0;
44306
+ const boneParent = boneIsBaseR15(bone) ? getBoneBaseR15Parent(bone) : bone.parent;
44307
+ const parentPartEquivalent = boneParent ? boneParent.name !== "HumanoidRootNode" ? this.getPartEquivalent(selfInstance, boneParent.name) : humanoidRootPartEquivalent : void 0;
44241
44308
  let rootBoneCF = new CFrame();
44242
44309
  if (bone.name === "Root") {
44243
44310
  rootBoneCF = rootBoneCFog;
@@ -46129,7 +46196,6 @@ class AnimationTrack {
46129
46196
  partKeyframe.easingStyle = pose.Prop("EasingStyle");
46130
46197
  }
46131
46198
  } else {
46132
- warn(false, `Missing either part0 or part1 with names: ${part0Name} ${part1Name}`);
46133
46199
  return [void 0, void 0, void 0];
46134
46200
  }
46135
46201
  if (!motorName || !motorParentName || !partKeyframe) {
@@ -47206,6 +47272,32 @@ function isSameAccessoryDesc(desc0, desc1) {
47206
47272
  function isSameMakeupDesc(desc0, desc1) {
47207
47273
  return hasSameVal(desc0, desc1, "AssetId") && hasSameVal(desc0, desc1, "MakeupType") && hasSameVal(desc0, desc1, "Order");
47208
47274
  }
47275
+ function moveAttachmentsToBase(rigPart) {
47276
+ let nextCollapse = rigPart.GetChildren();
47277
+ let cframeHierarchy = new Array(nextCollapse.length).fill(new CFrame());
47278
+ while (nextCollapse.length > 0) {
47279
+ const newNextCollapse = [];
47280
+ const newCFrameHierarchy = [];
47281
+ for (let i = 0; i < nextCollapse.length; i++) {
47282
+ const child = nextCollapse[i];
47283
+ if (child.className !== "Bone" && child.className !== "Attachment" || !child.HasProperty("CFrame")) {
47284
+ continue;
47285
+ }
47286
+ const selfCFrameHierarchy = cframeHierarchy[i];
47287
+ const originalCFrame = child.Prop("CFrame");
47288
+ const newCFrame = selfCFrameHierarchy.multiply(originalCFrame);
47289
+ child.setProperty("CFrame", newCFrame);
47290
+ const childChildren = child.GetChildren();
47291
+ for (const childChild of childChildren) {
47292
+ newNextCollapse.push(childChild);
47293
+ newCFrameHierarchy.push(newCFrame);
47294
+ }
47295
+ child.setParent(rigPart);
47296
+ }
47297
+ nextCollapse = newNextCollapse;
47298
+ cframeHierarchy = newCFrameHierarchy;
47299
+ }
47300
+ }
47209
47301
  class HumanoidDescriptionWrapper extends InstanceWrapper {
47210
47302
  static className = "HumanoidDescription";
47211
47303
  static requiredProperties = [
@@ -47860,6 +47952,9 @@ class HumanoidDescriptionWrapper extends InstanceWrapper {
47860
47952
  if (R15Folder) {
47861
47953
  const children = R15Folder.GetChildren();
47862
47954
  for (const child of children) {
47955
+ if (!FLAGS.AVATAR_JOINT_UPGRADE) {
47956
+ moveAttachmentsToBase(child);
47957
+ }
47863
47958
  replaceBodyPart(rig, child);
47864
47959
  }
47865
47960
  }
@@ -49473,6 +49568,14 @@ class HSR {
49473
49568
  }
49474
49569
  }
49475
49570
  }
49571
+ function exposeAPI() {
49572
+ globalThis.API = API;
49573
+ globalThis.APICACHE = CACHE;
49574
+ globalThis.Authentication = Authentication;
49575
+ }
49576
+ function exposeMesh() {
49577
+ globalThis.fileMeshToTHREEGeometry = fileMeshToTHREEGeometry;
49578
+ }
49476
49579
  function getCorners(cframe, size) {
49477
49580
  const halfX = size.X / 2;
49478
49581
  const halfY = size.Y / 2;
@@ -49586,13 +49689,15 @@ class OutfitRenderer {
49586
49689
  hasNewUpdate = false;
49587
49690
  lastFrameTime = Date.now() / 100;
49588
49691
  animationInterval;
49692
+ animationFPS = 60;
49693
+ deltaTimeMultiplier = 1;
49589
49694
  /**
49590
49695
  * Creates a new OutfitRenderer which makes it easy to render outfits
49591
49696
  * @param auth The authentication object, you should have one you use for everything
49592
49697
  * @param outfit The outfit you want to render, it can be updated later by calling setOutfit()
49593
- * @param rigPath The path that contains RigR6.rbxm and RigR15.rbxm, for example "../assets/"
49698
+ * @param rigPath The path that contains RigR6.rbxm and RigR15.rbxm, should always be "roavatar://" as rig path is now controlled by FLAGS
49594
49699
  */
49595
- constructor(auth, outfit, rigPath) {
49700
+ constructor(auth, outfit, rigPath = "roavatar://") {
49596
49701
  this.auth = auth;
49597
49702
  this.outfit = outfit;
49598
49703
  this.currentRigType = outfit.playerAvatarType;
@@ -49703,7 +49808,7 @@ class OutfitRenderer {
49703
49808
  */
49704
49809
  startAnimating() {
49705
49810
  if (this.animationInterval !== void 0) return;
49706
- this.lastFrameTime = Date.now() / 100;
49811
+ this.lastFrameTime = Date.now() / 1e3;
49707
49812
  this.animationInterval = setInterval(() => {
49708
49813
  if (this.currentRig && this.doCameraUpdate) {
49709
49814
  this.centerCamera();
@@ -49713,7 +49818,7 @@ class OutfitRenderer {
49713
49818
  if (humanoid) {
49714
49819
  const animator = humanoid.FindFirstChildOfClass("Animator");
49715
49820
  if (animator) {
49716
- const deltaTime = Date.now() / 1e3 - this.lastFrameTime;
49821
+ const deltaTime = (Date.now() / 1e3 - this.lastFrameTime) * this.deltaTimeMultiplier;
49717
49822
  this.lastFrameTime = Date.now() / 1e3;
49718
49823
  const animatorW = new AnimatorWrapper(animator);
49719
49824
  animatorW.renderAnimation(deltaTime);
@@ -49722,7 +49827,7 @@ class OutfitRenderer {
49722
49827
  }
49723
49828
  }
49724
49829
  }
49725
- }, 1e3 / 60);
49830
+ }, 1e3 / this.animationFPS);
49726
49831
  }
49727
49832
  /**
49728
49833
  * Stops updating the animation of the outfit per frame
@@ -49730,6 +49835,7 @@ class OutfitRenderer {
49730
49835
  stopAnimating() {
49731
49836
  if (this.animationInterval) {
49732
49837
  clearInterval(this.animationInterval);
49838
+ this.animationInterval = void 0;
49733
49839
  }
49734
49840
  }
49735
49841
  /**
@@ -49828,6 +49934,7 @@ export {
49828
49934
  LayeredAssetTypes,
49829
49935
  LayeredClothingAssetOrder,
49830
49936
  LocalOutfit,
49937
+ MakeupAssetTypes,
49831
49938
  MakeupDescriptionWrapper,
49832
49939
  MakeupType,
49833
49940
  MaxOneOfAssetTypes,
@@ -49913,6 +50020,8 @@ export {
49913
50020
  divide,
49914
50021
  dot,
49915
50022
  download,
50023
+ exposeAPI,
50024
+ exposeMesh,
49916
50025
  floor,
49917
50026
  gaussian_rbf,
49918
50027
  generateUUIDv4,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roavatar-renderer",
3
- "version": "1.2.14",
3
+ "version": "1.2.16",
4
4
  "description": "A renderer for Roblox avatars, used by the RoAvatar extension.",
5
5
  "author": "steinan",
6
6
  "type": "module",