roavatar-renderer 1.6.2 → 1.6.4

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
@@ -27,6 +27,7 @@ Basic example on how to load an avatar using OutfitRenderer (to make it simpler)
27
27
  FLAGS.RIG_PATH = chrome.runtime.getURL("assets/")
28
28
  }
29
29
  //if layered assets dont work set this to false (workers improve performance)
30
+ //or set FLAGS.GET_WORKER_FUNC to a working function, check source for example
30
31
  FLAGS.USE_WORKERS = true
31
32
 
32
33
  //setup RBXRenderer
@@ -44,19 +45,20 @@ Basic example on how to load an avatar using OutfitRenderer (to make it simpler)
44
45
  //add renderer to document
45
46
  document.body.appendChild(RBXRenderer.getRendererElement())
46
47
 
47
- //get avatar data for the user with id 1
48
- const outfit = await API.Avatar.GetAvatarDetails(1)
49
- if (!(outfit instanceof Outfit)) throw new Error("Failed to get outfit")
48
+ //get the OutfitModel for the user with id 1
49
+ const outfitModel = await API.Avatar.GetUserAvatarModel(1)
50
+ if (!(outfitModel instanceof OutfitModel)) throw new Error("Failed to get outfitModel")
50
51
 
51
52
  //create renderer for outfit
52
53
  //used by api
53
54
  const auth = new Authentication()
54
55
  //manages outfit rendering for you
55
- const outfitRenderer = new OutfitRenderer(auth, outfit)
56
+ const outfitRenderer = new OutfitRenderer(auth, outfitModel)
56
57
  outfitRenderer.startAnimating()
57
58
  outfitRenderer.setMainAnimation("idle")
58
59
  ```
59
60
  **RESULT:**
60
61
  <img src="https://devforum-uploads.s3.dualstack.us-east-2.amazonaws.com/uploads/original/5X/9/f/a/d/9fadf25d9770b63e8a2e480369930cad94ad04aa.png">
61
62
 
62
- Also the OutfitRenderer code or RoAvatar source code is useful, especially ```avatarPreview.ts```
63
+ For more examples check out the OutfitRenderer, BackgroundRenderer, RoAvatar (avatarPreview.tsx) or RoValra (ItemRender.ts) source code
64
+ There is also a discord server you can ask for help in: https://discord.gg/PHa5Vgtbva
package/dist/index.d.ts CHANGED
@@ -404,6 +404,7 @@ export declare const API: {
404
404
  GetUserLooks: (userId: number, cursor?: string) => Promise<Response | UserLooks_Result>;
405
405
  CreateLook: (auth: Authentication, outfit: Outfit, name: string, description: string) => Promise<Response>;
406
406
  DeleteLook: (auth: Authentication, lookId: string) => Promise<Response>;
407
+ PatchLook: (auth: Authentication, lookId: string, data: Partial<Look_Result["look"]>) => Promise<Response>;
407
408
  };
408
409
  PremiumFeatures: {
409
410
  GetSubscription: (userId: number) => Promise<Response | GetSubscription_Result>;
@@ -685,6 +686,10 @@ export declare function awaitTimeout<T>(promise: Promise<T>, time?: number): Pro
685
686
  */
686
687
  export declare function awaitTimeoutThrows<T>(promise: Promise<T>, time?: number): Promise<T>;
687
688
 
689
+ /**
690
+ * Used internally by OutfitRenderer to render backgrounds
691
+ * @category Renderer
692
+ */
688
693
  export declare class BackgroundRenderer {
689
694
  auth: Authentication;
690
695
  avatarCyclorama?: Instance;
@@ -695,9 +700,11 @@ export declare class BackgroundRenderer {
695
700
  hasNewUpdate: boolean;
696
701
  affectSceneAppearance: boolean;
697
702
  cameraAffectsTransparency: boolean;
703
+ cameraAffectsRotation: boolean;
698
704
  lastFrameTime: number;
699
705
  animationInterval?: NodeJS.Timeout;
700
706
  animationFPS: number;
707
+ originalPartCFrames: Map<Instance, CFrame>;
701
708
  renderScene: RBXRendererScene;
702
709
  private _renderSceneCompiledConnection?;
703
710
  private _renderSceneFailedConnection?;
@@ -733,6 +740,7 @@ export declare class BackgroundRenderer {
733
740
  _loadBackgroundData(): Promise<void>;
734
741
  _loadAvatarCyclorama(): Promise<void>;
735
742
  _applyBackgroundData(): void;
743
+ _applyBackgroundRotation(): void;
736
744
  /**
737
745
  * Starts updating the animation of the background per frame
738
746
  */
@@ -1573,6 +1581,8 @@ export declare const FLAGS: {
1573
1581
  API_REQUEST_RETRY: boolean;
1574
1582
  /**the fetch function the api will use */
1575
1583
  FETCH_FUNC: ((input: URL | RequestInfo, init?: RequestInit) => Promise<Response>) | undefined;
1584
+ /**a function all image urls will go through before being loaded, the return value is the new image url */
1585
+ IMAGE_FUNC: ((input: string) => Promise<string>) | undefined;
1576
1586
  /**loads assets from assetdelivery instead of local files */
1577
1587
  ONLINE_ASSETS: boolean;
1578
1588
  /**path to rbxasset:// local files*/
@@ -1736,6 +1746,14 @@ export declare function gaussian_rbf(v0: Vec3, v1: Vec3, sigma?: number): number
1736
1746
  */
1737
1747
  export declare function generateModelThumbnail(auth: Authentication, renderScene: RBXRendererScene, model: Instance, size?: Vec2, type?: ThumbnailType, quality?: number, gltfAutoDownload?: boolean, includeAnimations?: boolean): Promise<ThumbnailResult>;
1738
1748
 
1749
+ /**
1750
+ * @deprecated This has the exact same behavior as generateOutfitThumbnail which also supports outfitModels
1751
+ * @category ThumbnailGenerator
1752
+ * @param auth
1753
+ * @param outfitModel
1754
+ * @param options
1755
+ * @returns
1756
+ */
1739
1757
  export declare function generateOutfitModelThumbnail(auth: Authentication, outfitModel: OutfitModel, options: Partial<OutfitModelThumbnailOptions>): Promise<ThumbnailResult>;
1740
1758
 
1741
1759
  /**
@@ -1748,7 +1766,6 @@ export declare function generateOutfitModelThumbnail(auth: Authentication, outfi
1748
1766
  * @param gltfAutoDownload Automatically download gltf file
1749
1767
  * @returns ThumbnailResult, always a string for 2d thumbnails, 3d can be ArrayBuffer (glb, binary) or {[key: string]: unknown} (gltf, json)
1750
1768
  *
1751
- * @deprecated Use new Thumbnails category instead
1752
1769
  * @category ThumbnailGenerator
1753
1770
  *
1754
1771
  * @example
@@ -2889,6 +2906,8 @@ export declare function offsetMesh(mesh: FileMesh, cframe: CFrame): void;
2889
2906
  export declare function offsetMeshWithRotation(mesh: FileMesh, cframe: CFrame): void;
2890
2907
 
2891
2908
  /**
2909
+ * It is recommended to use an OutfitModel that contains an Outfit instead of using an Outfit directly as they cannot contain things such as avatar backgrounds
2910
+ *
2892
2911
  * Usually obtained from API
2893
2912
  * @category Outfit
2894
2913
  */
@@ -2952,7 +2971,7 @@ export declare class Outfit {
2952
2971
  getNextOrder(order: number): number;
2953
2972
  addAssetId(assetId: number, auth: Authentication): Promise<boolean>;
2954
2973
  addAssetIdEconomy(assetId: number): Promise<boolean>;
2955
- addBundleId(bundleId: number): Promise<boolean>;
2974
+ addBundleId(bundleId: number, auth?: Authentication): Promise<boolean>;
2956
2975
  getAssetId(assetId: number): Asset | undefined;
2957
2976
  fromLook(look: Look_Result["look"], auth: Authentication): Promise<boolean>;
2958
2977
  fromBuffer(buffer: ArrayBuffer, auth: Authentication): Promise<Response | Outfit>;
@@ -2976,6 +2995,10 @@ export declare type OutfitJson = {
2976
2995
  scales?: ScaleJson;
2977
2996
  };
2978
2997
 
2998
+ /**
2999
+ * Usually obtained from API, not to be confused with Outfit which cannot contain things such as backgrounds
3000
+ * @category Outfit
3001
+ */
2979
3002
  export declare class OutfitModel {
2980
3003
  outfit: Outfit;
2981
3004
  emotes: Map<number, Asset>;
@@ -3671,6 +3694,12 @@ declare class RenderDesc extends DisposableDesc {
3671
3694
 
3672
3695
  export declare function replaceBodyPart(rig: Instance, child: Instance): void;
3673
3696
 
3697
+ /**@category DataModelEnum */
3698
+ export declare const ResamplerMode: {
3699
+ Default: number;
3700
+ Pixelated: number;
3701
+ };
3702
+
3674
3703
  export declare function rgbToHex(r: number, g: number, b: number): string;
3675
3704
 
3676
3705
  export declare type RigData = {
@@ -3976,6 +4005,9 @@ declare type ThreePoseCorrective = Vec3;
3976
4005
  * @category ThumbnailGenerator */
3977
4006
  export declare type ThumbnailCameraType = "default" | "avatarHeadshot" | "avatarFullbody" | "fullbody";
3978
4007
 
4008
+ /**
4009
+ * @category Outfit
4010
+ */
3979
4011
  export declare class ThumbnailCustomization {
3980
4012
  thumbnailType: number;
3981
4013
  emoteAssetId: number;
package/dist/index.js CHANGED
@@ -22,6 +22,275 @@ function cloneSearch_Payload(data) {
22
22
  return newData;
23
23
  }
24
24
  const AllAvatarModelOutfitUpdateTypes = ["UpdateBodyColors", "UpdateAssets", "UpdateAvatarType", "UpdateScales", "UpdateBackground"];
25
+ const AssetTypes = [
26
+ "",
27
+ "Image",
28
+ "TShirt",
29
+ "Audio",
30
+ "Mesh",
31
+ "Lua",
32
+ "HTML",
33
+ "Text",
34
+ "Hat",
35
+ "Place",
36
+ "Model",
37
+ "Shirt",
38
+ "Pants",
39
+ "Decal",
40
+ "",
41
+ "",
42
+ "Avatar",
43
+ "Head",
44
+ "Face",
45
+ "Gear",
46
+ "",
47
+ "Badge",
48
+ "GroupEmblem",
49
+ "",
50
+ "Animation",
51
+ "Arms",
52
+ "Legs",
53
+ "Torso",
54
+ "RightArm",
55
+ "LeftArm",
56
+ "LeftLeg",
57
+ "RightLeg",
58
+ "Package",
59
+ "YouTubeVideo",
60
+ "GamePass",
61
+ "App",
62
+ "",
63
+ "Code",
64
+ "Plugin",
65
+ "SolidModel",
66
+ "MeshPart",
67
+ "HairAccessory",
68
+ "FaceAccessory",
69
+ "NeckAccessory",
70
+ "ShoulderAccessory",
71
+ "FrontAccessory",
72
+ "BackAccessory",
73
+ "WaistAccessory",
74
+ "ClimbAnimation",
75
+ "DeathAnimation",
76
+ "FallAnimation",
77
+ "IdleAnimation",
78
+ "JumpAnimation",
79
+ "RunAnimation",
80
+ "SwimAnimation",
81
+ "WalkAnimation",
82
+ "PoseAnimation",
83
+ "",
84
+ "",
85
+ "LocalizationTableManifest",
86
+ "LocalizationTableTranslation",
87
+ "EmoteAnimation",
88
+ "Video",
89
+ "TexturePack",
90
+ "TShirtAccessory",
91
+ "ShirtAccessory",
92
+ "PantsAccessory",
93
+ "JacketAccessory",
94
+ "SweaterAccessory",
95
+ "ShortsAccessory",
96
+ "LeftShoeAccessory",
97
+ "RightShoeAccessory",
98
+ "DressSkirtAccessory",
99
+ "FontFamily",
100
+ "FontFace",
101
+ "MeshHiddenSurfaceRemoval",
102
+ "EyebrowAccessory",
103
+ "EyelashAccessory",
104
+ "MoodAnimation",
105
+ "DynamicHead",
106
+ //79
107
+ "CodeSnippet",
108
+ "AdsVideo",
109
+ "OtaUpdate",
110
+ "Screenshot",
111
+ "RuntimePropertySet",
112
+ "StorePreviewVideo",
113
+ "GamePreviewVideo",
114
+ "CreatorExperienceConfig",
115
+ "FaceMakeup",
116
+ "LipMakeup",
117
+ "EyeMakeup",
118
+ "VoxelFragment",
119
+ "AvatarBackground",
120
+ "TextDocument"
121
+ ];
122
+ const WearableAssetTypes = [
123
+ "TShirt",
124
+ "Hat",
125
+ "Shirt",
126
+ "Pants",
127
+ "Head",
128
+ "Face",
129
+ "Gear",
130
+ "Torso",
131
+ "RightArm",
132
+ "LeftArm",
133
+ "LeftLeg",
134
+ "RightLeg",
135
+ "HairAccessory",
136
+ "FaceAccessory",
137
+ "NeckAccessory",
138
+ "ShoulderAccessory",
139
+ "FrontAccessory",
140
+ "BackAccessory",
141
+ "WaistAccessory",
142
+ "ClimbAnimation",
143
+ "FallAnimation",
144
+ "IdleAnimation",
145
+ "JumpAnimation",
146
+ "RunAnimation",
147
+ "SwimAnimation",
148
+ "WalkAnimation",
149
+ "TShirtAccessory",
150
+ "ShirtAccessory",
151
+ "PantsAccessory",
152
+ "JacketAccessory",
153
+ "SweaterAccessory",
154
+ "ShortsAccessory",
155
+ "LeftShoeAccessory",
156
+ "RightShoeAccessory",
157
+ "DressSkirtAccessory",
158
+ "EyebrowAccessory",
159
+ "EyelashAccessory",
160
+ "MoodAnimation",
161
+ "DynamicHead",
162
+ "FaceMakeup",
163
+ "LipMakeup",
164
+ "EyeMakeup",
165
+ "PoseAnimation",
166
+ "EarAccessory",
167
+ "EyeAccessory",
168
+ "DeathAnimation"
169
+ ];
170
+ const AccessoryAssetTypes = [
171
+ "Hat",
172
+ "HairAccessory",
173
+ "FaceAccessory",
174
+ "NeckAccessory",
175
+ "ShoulderAccessory",
176
+ "FrontAccessory",
177
+ "BackAccessory",
178
+ "WaistAccessory"
179
+ ];
180
+ const LayeredAssetTypes = [
181
+ "TShirtAccessory",
182
+ "ShirtAccessory",
183
+ "PantsAccessory",
184
+ "JacketAccessory",
185
+ "SweaterAccessory",
186
+ "ShortsAccessory",
187
+ "LeftShoeAccessory",
188
+ "RightShoeAccessory",
189
+ "DressSkirtAccessory",
190
+ "EyebrowAccessory",
191
+ "EyelashAccessory",
192
+ "HairAccessory",
193
+ "FaceMakeup",
194
+ "LipMakeup",
195
+ "EyeMakeup"
196
+ ];
197
+ const SpecialLayeredAssetTypes = [
198
+ "EyebrowAccessory",
199
+ "EyelashAccessory",
200
+ "HairAccessory"
201
+ ];
202
+ const MaxOneOfAssetTypes = [
203
+ "TShirt",
204
+ "Shirt",
205
+ "Pants",
206
+ "Head",
207
+ "Face",
208
+ "Gear",
209
+ "Torso",
210
+ "RightArm",
211
+ "LeftArm",
212
+ "LeftLeg",
213
+ "RightLeg",
214
+ "ClimbAnimation",
215
+ "DeathAnimation",
216
+ "FallAnimation",
217
+ "IdleAnimation",
218
+ "JumpAnimation",
219
+ "RunAnimation",
220
+ "SwimAnimation",
221
+ "WalkAnimation",
222
+ "PoseAnimation",
223
+ "MoodAnimation",
224
+ "DynamicHead",
225
+ "EyebrowAccessory",
226
+ "EyelashAccessory"
227
+ ];
228
+ const ToRemoveBeforeBundleType = {
229
+ "DynamicHead": ["MoodAnimation", "DynamicHead", "EyebrowAccessory", "EyelashAccessory", "Head"],
230
+ "Shoes": ["LeftShoeAccessory", "RightShoeAccessory"],
231
+ "AnimationPack": [
232
+ "ClimbAnimation",
233
+ "DeathAnimation",
234
+ "FallAnimation",
235
+ "IdleAnimation",
236
+ "JumpAnimation",
237
+ "RunAnimation",
238
+ "SwimAnimation",
239
+ "WalkAnimation",
240
+ "PoseAnimation",
241
+ "MoodAnimation"
242
+ ],
243
+ "Character": [
244
+ "DynamicHead",
245
+ "Head",
246
+ "Torso",
247
+ "LeftArm",
248
+ "RightArm",
249
+ "LeftLeg",
250
+ "RightLeg"
251
+ ],
252
+ "MakeupLook": [
253
+ "FaceMakeup",
254
+ "EyeMakeup",
255
+ "LipMakeup",
256
+ "EyebrowAccessory",
257
+ "EyelashAccessory"
258
+ ]
259
+ };
260
+ const AssetTypeNameToId = /* @__PURE__ */ new Map();
261
+ for (let i = 0; i < AssetTypes.length; i++) {
262
+ const name = AssetTypes[i];
263
+ AssetTypeNameToId.set(name, i);
264
+ }
265
+ const ActualBundleTypes = [
266
+ //names used by Roblox
267
+ "",
268
+ "Avatar",
269
+ //traditional bundle
270
+ "DynamicHead",
271
+ "Avatar",
272
+ //outfit
273
+ "Shoes",
274
+ "Avatar"
275
+ //animation pack
276
+ ];
277
+ const BundleTypes = [
278
+ "",
279
+ "Character",
280
+ "DynamicHead",
281
+ "Outfit",
282
+ "Shoes",
283
+ "AnimationPack",
284
+ "",
285
+ "MakeupLook"
286
+ ];
287
+ const CatalogBundleTypes = [
288
+ "",
289
+ "Character",
290
+ "AnimationPack",
291
+ "Shoes",
292
+ "DynamicHead"
293
+ ];
25
294
  const BrickColors = {
26
295
  1: "#F2F3F3",
27
296
  2: "#A1A5A2",
@@ -338,7 +607,9 @@ const MaxPerAsset = {
338
607
  "WalkAnimation": 1,
339
608
  "LayeredClothing": 5,
340
609
  //group type
341
- "MoodAnimation": 1
610
+ "MoodAnimation": 1,
611
+ "EyebrowAccessory": 1,
612
+ "EyelashAccessory": 1
342
613
  };
343
614
  const OutfitOrigin = {
344
615
  "WebAvatar": "WebAvatar",
@@ -30279,6 +30550,10 @@ class RBXSimpleView {
30279
30550
  }
30280
30551
  const magic = "<roblox!";
30281
30552
  const xmlMagic = "<roblox ";
30553
+ const ResamplerMode = {
30554
+ "Default": 0,
30555
+ "Pixelated": 1
30556
+ };
30282
30557
  const CameraType = {
30283
30558
  "Fixed": 0,
30284
30559
  "Attach": 1,
@@ -32404,7 +32679,7 @@ function nearestSearch(node, target, best = { dist: Infinity, index: -1 }) {
32404
32679
  }
32405
32680
  return best;
32406
32681
  }
32407
- 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';
32682
+ const jsContent = '(function() {\n "use strict";\n new TextDecoder();\n new TextDecoder();\n const AssetTypes = [\n "",\n "Image",\n "TShirt",\n "Audio",\n "Mesh",\n "Lua",\n "HTML",\n "Text",\n "Hat",\n "Place",\n "Model",\n "Shirt",\n "Pants",\n "Decal",\n "",\n "",\n "Avatar",\n "Head",\n "Face",\n "Gear",\n "",\n "Badge",\n "GroupEmblem",\n "",\n "Animation",\n "Arms",\n "Legs",\n "Torso",\n "RightArm",\n "LeftArm",\n "LeftLeg",\n "RightLeg",\n "Package",\n "YouTubeVideo",\n "GamePass",\n "App",\n "",\n "Code",\n "Plugin",\n "SolidModel",\n "MeshPart",\n "HairAccessory",\n "FaceAccessory",\n "NeckAccessory",\n "ShoulderAccessory",\n "FrontAccessory",\n "BackAccessory",\n "WaistAccessory",\n "ClimbAnimation",\n "DeathAnimation",\n "FallAnimation",\n "IdleAnimation",\n "JumpAnimation",\n "RunAnimation",\n "SwimAnimation",\n "WalkAnimation",\n "PoseAnimation",\n "",\n "",\n "LocalizationTableManifest",\n "LocalizationTableTranslation",\n "EmoteAnimation",\n "Video",\n "TexturePack",\n "TShirtAccessory",\n "ShirtAccessory",\n "PantsAccessory",\n "JacketAccessory",\n "SweaterAccessory",\n "ShortsAccessory",\n "LeftShoeAccessory",\n "RightShoeAccessory",\n "DressSkirtAccessory",\n "FontFamily",\n "FontFace",\n "MeshHiddenSurfaceRemoval",\n "EyebrowAccessory",\n "EyelashAccessory",\n "MoodAnimation",\n "DynamicHead",\n //79\n "CodeSnippet",\n "AdsVideo",\n "OtaUpdate",\n "Screenshot",\n "RuntimePropertySet",\n "StorePreviewVideo",\n "GamePreviewVideo",\n "CreatorExperienceConfig",\n "FaceMakeup",\n "LipMakeup",\n "EyeMakeup",\n "VoxelFragment",\n "AvatarBackground",\n "TextDocument"\n ];\n const AssetTypeNameToId = /* @__PURE__ */ new Map();\n for (let i = 0; i < AssetTypes.length; i++) {\n const name = AssetTypes[i];\n AssetTypeNameToId.set(name, i);\n }\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';
32408
32683
  const blob = typeof self !== "undefined" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", jsContent], { type: "text/javascript;charset=utf-8" });
32409
32684
  function WorkerWrapper(options) {
32410
32685
  let objURL;
@@ -32660,6 +32935,7 @@ const FLAGS = {
32660
32935
  INCLUDE_REQUEST_CREDENTIALS_OVERRIDE: "include",
32661
32936
  API_REQUEST_RETRY: true,
32662
32937
  FETCH_FUNC: void 0,
32938
+ IMAGE_FUNC: void 0,
32663
32939
  //assets
32664
32940
  ONLINE_ASSETS: false,
32665
32941
  ASSETS_PATH: "../assets/rbxasset/",
@@ -33167,9 +33443,7 @@ class CFrame {
33167
33443
  this.Position = [x, y, z];
33168
33444
  }
33169
33445
  static Angles(x, y, z) {
33170
- const cf = new CFrame();
33171
- cf.Orientation = [x, y, z];
33172
- return cf;
33446
+ return CFrame.fromEulerAngles(x, y, z, "XYZ");
33173
33447
  }
33174
33448
  clone() {
33175
33449
  const cloneCF = new CFrame(this.Position[0], this.Position[1], this.Position[2]);
@@ -35124,275 +35398,6 @@ function toVecXYZ(vec) {
35124
35398
  Z: vec.z
35125
35399
  };
35126
35400
  }
35127
- const AssetTypes = [
35128
- "",
35129
- "Image",
35130
- "TShirt",
35131
- "Audio",
35132
- "Mesh",
35133
- "Lua",
35134
- "HTML",
35135
- "Text",
35136
- "Hat",
35137
- "Place",
35138
- "Model",
35139
- "Shirt",
35140
- "Pants",
35141
- "Decal",
35142
- "",
35143
- "",
35144
- "Avatar",
35145
- "Head",
35146
- "Face",
35147
- "Gear",
35148
- "",
35149
- "Badge",
35150
- "GroupEmblem",
35151
- "",
35152
- "Animation",
35153
- "Arms",
35154
- "Legs",
35155
- "Torso",
35156
- "RightArm",
35157
- "LeftArm",
35158
- "LeftLeg",
35159
- "RightLeg",
35160
- "Package",
35161
- "YouTubeVideo",
35162
- "GamePass",
35163
- "App",
35164
- "",
35165
- "Code",
35166
- "Plugin",
35167
- "SolidModel",
35168
- "MeshPart",
35169
- "HairAccessory",
35170
- "FaceAccessory",
35171
- "NeckAccessory",
35172
- "ShoulderAccessory",
35173
- "FrontAccessory",
35174
- "BackAccessory",
35175
- "WaistAccessory",
35176
- "ClimbAnimation",
35177
- "DeathAnimation",
35178
- "FallAnimation",
35179
- "IdleAnimation",
35180
- "JumpAnimation",
35181
- "RunAnimation",
35182
- "SwimAnimation",
35183
- "WalkAnimation",
35184
- "PoseAnimation",
35185
- "",
35186
- "",
35187
- "LocalizationTableManifest",
35188
- "LocalizationTableTranslation",
35189
- "EmoteAnimation",
35190
- "Video",
35191
- "TexturePack",
35192
- "TShirtAccessory",
35193
- "ShirtAccessory",
35194
- "PantsAccessory",
35195
- "JacketAccessory",
35196
- "SweaterAccessory",
35197
- "ShortsAccessory",
35198
- "LeftShoeAccessory",
35199
- "RightShoeAccessory",
35200
- "DressSkirtAccessory",
35201
- "FontFamily",
35202
- "FontFace",
35203
- "MeshHiddenSurfaceRemoval",
35204
- "EyebrowAccessory",
35205
- "EyelashAccessory",
35206
- "MoodAnimation",
35207
- "DynamicHead",
35208
- //79
35209
- "CodeSnippet",
35210
- "AdsVideo",
35211
- "OtaUpdate",
35212
- "Screenshot",
35213
- "RuntimePropertySet",
35214
- "StorePreviewVideo",
35215
- "GamePreviewVideo",
35216
- "CreatorExperienceConfig",
35217
- "FaceMakeup",
35218
- "LipMakeup",
35219
- "EyeMakeup",
35220
- "VoxelFragment",
35221
- "AvatarBackground",
35222
- "TextDocument"
35223
- ];
35224
- const WearableAssetTypes = [
35225
- "TShirt",
35226
- "Hat",
35227
- "Shirt",
35228
- "Pants",
35229
- "Head",
35230
- "Face",
35231
- "Gear",
35232
- "Torso",
35233
- "RightArm",
35234
- "LeftArm",
35235
- "LeftLeg",
35236
- "RightLeg",
35237
- "HairAccessory",
35238
- "FaceAccessory",
35239
- "NeckAccessory",
35240
- "ShoulderAccessory",
35241
- "FrontAccessory",
35242
- "BackAccessory",
35243
- "WaistAccessory",
35244
- "ClimbAnimation",
35245
- "FallAnimation",
35246
- "IdleAnimation",
35247
- "JumpAnimation",
35248
- "RunAnimation",
35249
- "SwimAnimation",
35250
- "WalkAnimation",
35251
- "TShirtAccessory",
35252
- "ShirtAccessory",
35253
- "PantsAccessory",
35254
- "JacketAccessory",
35255
- "SweaterAccessory",
35256
- "ShortsAccessory",
35257
- "LeftShoeAccessory",
35258
- "RightShoeAccessory",
35259
- "DressSkirtAccessory",
35260
- "EyebrowAccessory",
35261
- "EyelashAccessory",
35262
- "MoodAnimation",
35263
- "DynamicHead",
35264
- "FaceMakeup",
35265
- "LipMakeup",
35266
- "EyeMakeup",
35267
- "PoseAnimation",
35268
- "EarAccessory",
35269
- "EyeAccessory",
35270
- "DeathAnimation"
35271
- ];
35272
- const AccessoryAssetTypes = [
35273
- "Hat",
35274
- "HairAccessory",
35275
- "FaceAccessory",
35276
- "NeckAccessory",
35277
- "ShoulderAccessory",
35278
- "FrontAccessory",
35279
- "BackAccessory",
35280
- "WaistAccessory"
35281
- ];
35282
- const LayeredAssetTypes = [
35283
- "TShirtAccessory",
35284
- "ShirtAccessory",
35285
- "PantsAccessory",
35286
- "JacketAccessory",
35287
- "SweaterAccessory",
35288
- "ShortsAccessory",
35289
- "LeftShoeAccessory",
35290
- "RightShoeAccessory",
35291
- "DressSkirtAccessory",
35292
- "EyebrowAccessory",
35293
- "EyelashAccessory",
35294
- "HairAccessory",
35295
- "FaceMakeup",
35296
- "LipMakeup",
35297
- "EyeMakeup"
35298
- ];
35299
- const SpecialLayeredAssetTypes = [
35300
- "EyebrowAccessory",
35301
- "EyelashAccessory",
35302
- "HairAccessory"
35303
- ];
35304
- const MaxOneOfAssetTypes = [
35305
- "TShirt",
35306
- "Shirt",
35307
- "Pants",
35308
- "Head",
35309
- "Face",
35310
- "Gear",
35311
- "Torso",
35312
- "RightArm",
35313
- "LeftArm",
35314
- "LeftLeg",
35315
- "RightLeg",
35316
- "ClimbAnimation",
35317
- "DeathAnimation",
35318
- "FallAnimation",
35319
- "IdleAnimation",
35320
- "JumpAnimation",
35321
- "RunAnimation",
35322
- "SwimAnimation",
35323
- "WalkAnimation",
35324
- "PoseAnimation",
35325
- "MoodAnimation",
35326
- "DynamicHead",
35327
- "EyebrowAccessory",
35328
- "EyelashAccessory"
35329
- ];
35330
- const ToRemoveBeforeBundleType = {
35331
- "DynamicHead": ["MoodAnimation", "DynamicHead", "EyebrowAccessory", "EyelashAccessory", "Head"],
35332
- "Shoes": ["LeftShoeAccessory", "RightShoeAccessory"],
35333
- "AnimationPack": [
35334
- "ClimbAnimation",
35335
- "DeathAnimation",
35336
- "FallAnimation",
35337
- "IdleAnimation",
35338
- "JumpAnimation",
35339
- "RunAnimation",
35340
- "SwimAnimation",
35341
- "WalkAnimation",
35342
- "PoseAnimation",
35343
- "MoodAnimation"
35344
- ],
35345
- "Character": [
35346
- "DynamicHead",
35347
- "Head",
35348
- "Torso",
35349
- "LeftArm",
35350
- "RightArm",
35351
- "LeftLeg",
35352
- "RightLeg"
35353
- ],
35354
- "MakeupLook": [
35355
- "FaceMakeup",
35356
- "EyeMakeup",
35357
- "LipMakeup",
35358
- "EyebrowAccessory",
35359
- "EyelashAccessory"
35360
- ]
35361
- };
35362
- const AssetTypeNameToId = /* @__PURE__ */ new Map();
35363
- for (let i = 0; i < AssetTypes.length; i++) {
35364
- const name = AssetTypes[i];
35365
- AssetTypeNameToId.set(name, i);
35366
- }
35367
- const ActualBundleTypes = [
35368
- //names used by Roblox
35369
- "",
35370
- "Avatar",
35371
- //traditional bundle
35372
- "DynamicHead",
35373
- "Avatar",
35374
- //outfit
35375
- "Shoes",
35376
- "Avatar"
35377
- //animation pack
35378
- ];
35379
- const BundleTypes = [
35380
- "",
35381
- "Character",
35382
- "DynamicHead",
35383
- "Outfit",
35384
- "Shoes",
35385
- "AnimationPack",
35386
- "",
35387
- "MakeupLook"
35388
- ];
35389
- const CatalogBundleTypes = [
35390
- "",
35391
- "Character",
35392
- "AnimationPack",
35393
- "Shoes",
35394
- "DynamicHead"
35395
- ];
35396
35401
  class ItemInfo {
35397
35402
  itemType;
35398
35403
  type;
@@ -36035,7 +36040,7 @@ class Outfit {
36035
36040
  });
36036
36041
  }
36037
36042
  }
36038
- if (LayeredAssetTypes.includes(asset.assetType.name) && !MakeupAssetTypes.includes(asset.assetType.name)) {
36043
+ if (LayeredAssetTypes.includes(asset.assetType.name) && !MakeupAssetTypes.includes(asset.assetType.name) && !MaxOneOfAssetTypes.includes(asset.assetType.name)) {
36039
36044
  totalLayered += 1;
36040
36045
  if (totalLayered > 10) {
36041
36046
  issues.push({
@@ -36489,7 +36494,7 @@ class Outfit {
36489
36494
  this.addAsset(assetId, assetDetails.AssetTypeId, assetDetails.Name);
36490
36495
  return true;
36491
36496
  }
36492
- async addBundleId(bundleId) {
36497
+ async addBundleId(bundleId, auth) {
36493
36498
  const bundleDetails = await API.Catalog.GetBundleDetails(bundleId);
36494
36499
  if (!(bundleDetails instanceof Response)) {
36495
36500
  const bundleType = CatalogBundleTypes[bundleDetails.bundleType];
@@ -36508,6 +36513,18 @@ class Outfit {
36508
36513
  break;
36509
36514
  }
36510
36515
  }
36516
+ if (!bundleDetails.bundledItems.find((v) => {
36517
+ return v.type === "UserOutfit";
36518
+ })) {
36519
+ const promises = [];
36520
+ for (const item of bundleDetails.bundledItems) {
36521
+ if (item.type !== "Asset") continue;
36522
+ const promise = auth ? this.addAssetId(item.id, auth) : this.addAssetIdEconomy(item.id);
36523
+ promises.push(promise);
36524
+ }
36525
+ await Promise.all(promises);
36526
+ return true;
36527
+ }
36511
36528
  } else {
36512
36529
  warn(true, "Failed to get bundleDetails", bundleDetails);
36513
36530
  }
@@ -38944,6 +38961,7 @@ function createContentMap() {
38944
38961
  ContentMap.set("roavatar://AvatarCyclorama.rbxm", "79116945688799");
38945
38962
  }
38946
38963
  let CachedRoAvatarData = void 0;
38964
+ const thumbnailTypesWithBackground = ["Outfit", "Avatar", "AvatarHeadshot"];
38947
38965
  let ThumbnailsToBatch = [];
38948
38966
  const API = {
38949
38967
  "Misc": {
@@ -39058,7 +39076,13 @@ const API = {
39058
39076
  CACHE.Image.set(cacheURL, void 0);
39059
39077
  };
39060
39078
  image.crossOrigin = "anonymous";
39061
- image.src = fetchStr;
39079
+ if (FLAGS.IMAGE_FUNC) {
39080
+ FLAGS.IMAGE_FUNC(fetchStr).then((str) => {
39081
+ image.src = str;
39082
+ });
39083
+ } else {
39084
+ image.src = fetchStr;
39085
+ }
39062
39086
  });
39063
39087
  }));
39064
39088
  }
@@ -39203,13 +39227,19 @@ const API = {
39203
39227
  if (response.status !== 200) return response;
39204
39228
  const body = await response.json();
39205
39229
  const outfitModel = new OutfitModel().fromJson(body);
39230
+ outfitModel.outfit.creatorId = userId;
39231
+ outfitModel.outfit.origin = OutfitOrigin.WebAvatar;
39206
39232
  return outfitModel;
39207
39233
  },
39208
39234
  GetAvatarModel: async function() {
39235
+ const userInfoPromise = API.Users.GetUserInfo();
39209
39236
  const response = await RBLXGet("https://avatar.roblox.com/v4/avatar?selectionTypes=0&selectionTypes=1&selectionTypes=2&selectionTypes=3&selectionTypes=4&selectionTypes=5&selectionTypes=6");
39210
39237
  if (response.status !== 200) return response;
39211
39238
  const body = await response.json();
39212
39239
  const outfitModel = new OutfitModel().fromJson(body);
39240
+ const userInfo = await userInfoPromise;
39241
+ if (userInfo) outfitModel.outfit.creatorId = userInfo.id;
39242
+ outfitModel.outfit.origin = OutfitOrigin.WebAvatar;
39213
39243
  return outfitModel;
39214
39244
  },
39215
39245
  UpdateAvatarModel: async function(auth, model, updateTypes = AllAvatarModelOutfitUpdateTypes) {
@@ -39461,7 +39491,11 @@ const API = {
39461
39491
  if (response instanceof ArrayBuffer) {
39462
39492
  const buffer2 = response;
39463
39493
  const rbx = new RBX();
39464
- rbx.fromBuffer(buffer2);
39494
+ try {
39495
+ rbx.fromBuffer(buffer2);
39496
+ } catch {
39497
+ return new Response();
39498
+ }
39465
39499
  if (FLAGS.ENABLE_API_CACHE && FLAGS.ENABLE_API_RBX_CACHE) {
39466
39500
  CACHE.RBX.set(cacheStr, rbx.clone());
39467
39501
  }
@@ -39492,7 +39526,7 @@ const API = {
39492
39526
  try {
39493
39527
  await mesh.fromBuffer(buffer2);
39494
39528
  if (FLAGS.ENABLE_API_CACHE && FLAGS.ENABLE_API_MESH_CACHE) {
39495
- CACHE.Mesh.set(cacheStr, mesh.clone());
39529
+ CACHE.Mesh.set(cacheStr, readOnly ? mesh : mesh.clone());
39496
39530
  }
39497
39531
  return mesh;
39498
39532
  } catch {
@@ -39673,7 +39707,7 @@ const API = {
39673
39707
  },
39674
39708
  "Users": {
39675
39709
  GetUserInfo: async function() {
39676
- if (CACHE.UserInfo) {
39710
+ if (CACHE.UserInfo !== void 0) {
39677
39711
  return CACHE.UserInfo;
39678
39712
  }
39679
39713
  const response = await RBLXGet("https://users.roblox.com/v1/users/authenticated");
@@ -39717,7 +39751,7 @@ const API = {
39717
39751
  id,
39718
39752
  size,
39719
39753
  headShape,
39720
- includeBackground: type === "Outfit"
39754
+ includeBackground: thumbnailTypesWithBackground.includes(type)
39721
39755
  };
39722
39756
  const cachedThumbnail = CACHE.Thumbnails.get(requestIdFromThumbnailInfo(thisThumbnailInfo));
39723
39757
  if (CACHE.Thumbnails.has(requestIdFromThumbnailInfo(thisThumbnailInfo))) {
@@ -39742,7 +39776,7 @@ const API = {
39742
39776
  attempt: 0,
39743
39777
  lastTryTimestamp: 0,
39744
39778
  headShape,
39745
- includeBackground: type === "Outfit"
39779
+ includeBackground: thumbnailTypesWithBackground.includes(type)
39746
39780
  });
39747
39781
  });
39748
39782
  },
@@ -39752,7 +39786,7 @@ const API = {
39752
39786
  id,
39753
39787
  size,
39754
39788
  headShape,
39755
- includeBackground: type === "Outfit"
39789
+ includeBackground: thumbnailTypesWithBackground.includes(type)
39756
39790
  };
39757
39791
  CACHE.Thumbnails.delete(requestIdFromThumbnailInfo(thisThumbnailInfo));
39758
39792
  },
@@ -39853,6 +39887,10 @@ const API = {
39853
39887
  DeleteLook: async function(auth, lookId) {
39854
39888
  const response = await RBLXDelete(`https://apis.roblox.com/look-api/v1/looks/${lookId}`, auth, {});
39855
39889
  return response;
39890
+ },
39891
+ PatchLook: async function(auth, lookId, data) {
39892
+ const response = await RBLXPatch(`https://apis.roblox.com/look-api/v1/looks/${lookId}`, auth, data);
39893
+ return response;
39856
39894
  }
39857
39895
  },
39858
39896
  "PremiumFeatures": {
@@ -46469,6 +46507,7 @@ class MaterialDesc {
46469
46507
  transparency = 0;
46470
46508
  doubleSided = false;
46471
46509
  visible = true;
46510
+ resampleMode = ResamplerMode.Default;
46472
46511
  emissiveStrength = 1;
46473
46512
  emissiveTint = new Color3(1, 1, 1);
46474
46513
  bodyPart;
@@ -46481,7 +46520,7 @@ class MaterialDesc {
46481
46520
  result;
46482
46521
  isSame(other) {
46483
46522
  if (this.dirty || other.dirty) return false;
46484
- const propertiesSame = this.isDecal === other.isDecal && this.transparent === other.transparent && Math.round(this.transparency * 100) === Math.round(other.transparency * 100) && this.doubleSided === other.doubleSided && this.visible === other.visible && this.canHaveMipmaps === other.canHaveMipmaps;
46523
+ const propertiesSame = this.isDecal === other.isDecal && this.transparent === other.transparent && Math.round(this.transparency * 100) === Math.round(other.transparency * 100) && this.doubleSided === other.doubleSided && this.visible === other.visible && this.canHaveMipmaps === other.canHaveMipmaps && this.resampleMode === other.resampleMode;
46485
46524
  let layersSame = true;
46486
46525
  if (this.layers.length !== other.layers.length) {
46487
46526
  layersSame = false;
@@ -46496,7 +46535,7 @@ class MaterialDesc {
46496
46535
  }
46497
46536
  needsRegeneration(other) {
46498
46537
  if (this.dirty || other.dirty) return true;
46499
- const propertiesSame = this.isDecal === other.isDecal && this.doubleSided === other.doubleSided && this.visible === other.visible && this.canHaveMipmaps === other.canHaveMipmaps;
46538
+ const propertiesSame = this.isDecal === other.isDecal && this.doubleSided === other.doubleSided && this.visible === other.visible && this.canHaveMipmaps === other.canHaveMipmaps && this.resampleMode === other.resampleMode;
46500
46539
  let layersSame = true;
46501
46540
  if (this.layers.length !== other.layers.length) {
46502
46541
  layersSame = false;
@@ -46795,6 +46834,7 @@ class MaterialDesc {
46795
46834
  if (texture !== lineartexture) {
46796
46835
  lineartexture.dispose();
46797
46836
  }
46837
+ texture.magFilter = this.resampleMode === ResamplerMode.Default ? LinearFilter : NearestFilter;
46798
46838
  let hasTransparency = false;
46799
46839
  const rbxRenderer = RBXRenderer.getRenderer();
46800
46840
  if (!hasColorLayer && rbxRenderer || FLAGS.RENDERTARGET_TO_CANVASTEXTURE && rbxRenderer) {
@@ -46815,6 +46855,7 @@ class MaterialDesc {
46815
46855
  texture.minFilter = ogTexture.minFilter;
46816
46856
  texture.magFilter = ogTexture.magFilter;
46817
46857
  texture.generateMipmaps = ogTexture.generateMipmaps;
46858
+ ogTexture.dispose();
46818
46859
  }
46819
46860
  }
46820
46861
  if (!this.transparent) {
@@ -46880,6 +46921,7 @@ class MaterialDesc {
46880
46921
  } else {
46881
46922
  hasTransparency = false;
46882
46923
  }
46924
+ texture.magFilter = this.resampleMode === ResamplerMode.Default ? LinearFilter : NearestFilter;
46883
46925
  texture.needsUpdate = true;
46884
46926
  return [texture, hasTransparency];
46885
46927
  }
@@ -46904,6 +46946,7 @@ class MaterialDesc {
46904
46946
  texture.wrapT = RepeatWrapping;
46905
46947
  texture.colorSpace = textureType === "color" ? SRGBColorSpace : NoColorSpace;
46906
46948
  texture.generateMipmaps = this.canHaveMipmaps;
46949
+ texture.magFilter = this.resampleMode === ResamplerMode.Default ? LinearFilter : NearestFilter;
46907
46950
  texture.needsUpdate = true;
46908
46951
  return [texture, hasTransparency];
46909
46952
  }
@@ -47294,6 +47337,7 @@ class MaterialDesc {
47294
47337
  if (surfaceAppearance.Prop("AlphaMode") === AlphaMode.Transparency) {
47295
47338
  this.transparent = true;
47296
47339
  }
47340
+ this.resampleMode = surfaceAppearance.PropOrDefault("ResampleMode", this.resampleMode);
47297
47341
  affectedByHumanoid = false;
47298
47342
  }
47299
47343
  if (affectedByHumanoid && child.parent && !surfaceAppearance) {
@@ -52765,11 +52809,11 @@ class LightDesc extends RenderDesc {
52765
52809
  if (light instanceof PointLight || light instanceof SpotLight) {
52766
52810
  light.decay = 0.4;
52767
52811
  light.visible = this.enabled;
52768
- light.intensity = this.brightness;
52812
+ light.intensity = this.brightness * 4;
52769
52813
  light.distance = this.range + 0.5;
52770
52814
  light.castShadow = this.shadows;
52771
52815
  light.shadow.intensity = 0.5;
52772
- light.color = new Color().setRGB(this.color.R, this.color.G, this.color.B);
52816
+ light.color = new Color().setRGB(this.color.R, this.color.G, this.color.B, SRGBColorSpace);
52773
52817
  const resultCF = this.cframe;
52774
52818
  const targetCF = new CFrame();
52775
52819
  if (light instanceof SpotLight) {
@@ -57786,7 +57830,11 @@ class RBXRenderer {
57786
57830
  directionalLight.shadow.camera.bottom = -shadowPhysicalSize + bottomOffset;
57787
57831
  directionalLight.shadow.camera.near = 0.5;
57788
57832
  directionalLight.shadow.camera.far = 25;
57789
- directionalLight.shadow.intensity = 0.5;
57833
+ if (lightingType === "WellLit") directionalLight.shadow.intensity = 0.5;
57834
+ if (lightingType === "Thumbnail") {
57835
+ directionalLight.shadow.radius = 4;
57836
+ directionalLight.shadow.bias = 5e-4;
57837
+ }
57790
57838
  directionalLight.target.position.set(0, 0, 0);
57791
57839
  renderScene.scene.add(directionalLight);
57792
57840
  renderScene.directionalLight = directionalLight;
@@ -58803,9 +58851,11 @@ class BackgroundRenderer {
58803
58851
  hasNewUpdate = false;
58804
58852
  affectSceneAppearance = true;
58805
58853
  cameraAffectsTransparency = true;
58854
+ cameraAffectsRotation = false;
58806
58855
  lastFrameTime = Date.now() / 1e3;
58807
58856
  animationInterval;
58808
58857
  animationFPS = 60;
58858
+ originalPartCFrames = /* @__PURE__ */ new Map();
58809
58859
  renderScene = RBXRenderer.firstScene;
58810
58860
  _renderSceneCompiledConnection;
58811
58861
  _renderSceneFailedConnection;
@@ -58894,6 +58944,12 @@ class BackgroundRenderer {
58894
58944
  const root = rbx.generateTree();
58895
58945
  this.avatarCyclorama = root.GetChildren()[0];
58896
58946
  this.avatarCyclorama.setParent(null);
58947
+ for (const child of this.avatarCyclorama.GetChildren()) {
58948
+ if (child.IsA("BasePart")) {
58949
+ const childCF = child.Prop("CFrame");
58950
+ this.originalPartCFrames.set(child, childCF);
58951
+ }
58952
+ }
58897
58953
  root.Destroy();
58898
58954
  } else {
58899
58955
  this.onError.Fire("avatarCyclorama");
@@ -58935,6 +58991,25 @@ class BackgroundRenderer {
58935
58991
  }
58936
58992
  this.fireFullyRenderedIfNeeded();
58937
58993
  }
58994
+ _applyBackgroundRotation() {
58995
+ if (this.avatarCyclorama && this.cameraAffectsRotation) {
58996
+ const cameraCF = RBXRenderer.getCameraCFrame(this.renderScene);
58997
+ const cameraLook = cameraCF.lookVector();
58998
+ cameraLook[0] = -cameraLook[0];
58999
+ cameraLook[1] = 0;
59000
+ cameraLook[2] = -cameraLook[2];
59001
+ const cameraAngle = CFrame.lookAt([0, 0, 0], cameraLook);
59002
+ for (const child of this.avatarCyclorama.GetChildren()) {
59003
+ if (child.IsA("BasePart")) {
59004
+ const ogChildCF = this.originalPartCFrames.get(child);
59005
+ if (ogChildCF) {
59006
+ const newChildCF = cameraAngle.multiply(ogChildCF);
59007
+ child.setProperty("CFrame", newChildCF);
59008
+ }
59009
+ }
59010
+ }
59011
+ }
59012
+ }
58938
59013
  /**
58939
59014
  * Starts updating the animation of the background per frame
58940
59015
  */
@@ -58949,6 +59024,7 @@ class BackgroundRenderer {
58949
59024
  * Updates the animation once
58950
59025
  */
58951
59026
  animateOnce() {
59027
+ this._applyBackgroundRotation();
58952
59028
  this._applyBackgroundData();
58953
59029
  }
58954
59030
  /**
@@ -59334,6 +59410,7 @@ class OutfitRenderer {
59334
59410
  this.doAddInstance = false;
59335
59411
  this.backgroundRenderer.affectSceneAppearance = false;
59336
59412
  this.backgroundRenderer.cameraAffectsTransparency = false;
59413
+ this.backgroundRenderer.cameraAffectsRotation = true;
59337
59414
  if (this.outfit.playerAvatarType === AvatarType.R6) this.deltaTimeMultiplier = 0;
59338
59415
  await new Promise((resolve) => {
59339
59416
  this.onSuccess.Connect(() => {
@@ -59341,7 +59418,7 @@ class OutfitRenderer {
59341
59418
  if (animatorW) {
59342
59419
  animatorW.data.forceTransitionTime = 0;
59343
59420
  }
59344
- if (!this.outfit.containsAssetType("Gear")) {
59421
+ if (!this.outfit.containsAssetType("Gear") || this.outfit.playerAvatarType === AvatarType.R15 && !this.hasAnimationSetAnimation("pose")) {
59345
59422
  if (this.outfit.playerAvatarType === AvatarType.R15) {
59346
59423
  if (this.hasAnimationSetAnimation("pose")) {
59347
59424
  this.setMainAnimation("pose").then(() => {
@@ -59509,6 +59586,14 @@ async function generateOutfitThumbnail(auth, outfit, size = [150, 150], type = "
59509
59586
  renderScene.destroy();
59510
59587
  return void 0;
59511
59588
  }
59589
+ if (thumbnailCameraType === "default" && outfitRenderer.backgroundRenderer.backgroundId) {
59590
+ thumbnailCameraType = "avatarFullbody";
59591
+ }
59592
+ if (outfitRenderer.backgroundRenderer.backgroundId) {
59593
+ renderScene.directionalLight.castShadow = true;
59594
+ renderScene.directionalLight.position.set(-0.55828 * 10, 0.72756 * 10, -0.39873 * 10);
59595
+ renderScene.directionalLight2.position.set(0.55828 * 10, -0.72756 * 10, 0.39873 * 10);
59596
+ }
59512
59597
  if (outfitRenderer.currentRig) {
59513
59598
  let cameraCFrame = new CFrame();
59514
59599
  switch (thumbnailCameraType) {
@@ -59516,10 +59601,10 @@ async function generateOutfitThumbnail(auth, outfit, size = [150, 150], type = "
59516
59601
  cameraCFrame = getThumbnailCameraCFrame(outfitRenderer.currentRig, renderScene.camera.fov) || cameraCFrame;
59517
59602
  break;
59518
59603
  case "avatarHeadshot":
59519
- cameraCFrame = getCameraCFrameForHeadshotCustomized(outfitRenderer.currentRig, 28, 0, 1) || cameraCFrame;
59604
+ cameraCFrame = getCameraCFrameForHeadshotCustomized(outfitRenderer.currentRig, 30, 0, 1) || cameraCFrame;
59520
59605
  break;
59521
59606
  case "avatarFullbody":
59522
- cameraCFrame = getCameraCFrameForAvatarCustomized(outfitRenderer.currentRig, 28, 0) || cameraCFrame;
59607
+ cameraCFrame = getCameraCFrameForAvatarCustomized(outfitRenderer.currentRig, 30, 0) || cameraCFrame;
59523
59608
  break;
59524
59609
  case "fullbody":
59525
59610
  cameraCFrame = getFullBodyCameraCFrame(outfitRenderer.currentRig) || cameraCFrame;
@@ -59527,10 +59612,11 @@ async function generateOutfitThumbnail(auth, outfit, size = [150, 150], type = "
59527
59612
  }
59528
59613
  if (cameraCFrame) {
59529
59614
  RBXRenderer.setCameraCFrame(cameraCFrame, renderScene);
59530
- RBXRenderer.setCameraFov(thumbnailCameraType === "default" ? 70 : thumbnailCameraType === "fullbody" ? 56 : 28, renderScene);
59615
+ RBXRenderer.setCameraFov(thumbnailCameraType === "default" ? 70 : thumbnailCameraType === "fullbody" ? 56 : 30, renderScene);
59531
59616
  renderScene.camera.updateProjectionMatrix();
59532
59617
  }
59533
59618
  outfitRenderer.updateParticleMatrix();
59619
+ outfitRenderer.backgroundRenderer.animateOnce();
59534
59620
  const result = type === "gltf" || type === "glb" ? await modelThumbnailClick(renderScene, type, {
59535
59621
  includeAnimations
59536
59622
  }) : await imageThumbnailClick(renderScene, size[0], size[1], type, {
@@ -59779,6 +59865,7 @@ export {
59779
59865
  Ray3 as Ray,
59780
59866
  RegisterWrappers,
59781
59867
  RegularBodyColors,
59868
+ ResamplerMode,
59782
59869
  RoAvatarData,
59783
59870
  RoAvatarDataError,
59784
59871
  RoAvatarVersions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roavatar-renderer",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
4
4
  "description": "A renderer for Roblox avatars, used by the RoAvatar extension.",
5
5
  "author": "steinan",
6
6
  "type": "module",