roavatar-renderer 1.4.4 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -15585,6 +15585,207 @@ class LightShadow {
15585
15585
  return object;
15586
15586
  }
15587
15587
  }
15588
+ class SpotLightShadow extends LightShadow {
15589
+ /**
15590
+ * Constructs a new spot light shadow.
15591
+ */
15592
+ constructor() {
15593
+ super(new PerspectiveCamera(50, 1, 0.5, 500));
15594
+ this.isSpotLightShadow = true;
15595
+ this.focus = 1;
15596
+ this.aspect = 1;
15597
+ }
15598
+ updateMatrices(light) {
15599
+ const camera = this.camera;
15600
+ const fov2 = RAD2DEG * 2 * light.angle * this.focus;
15601
+ const aspect2 = this.mapSize.width / this.mapSize.height * this.aspect;
15602
+ const far = light.distance || camera.far;
15603
+ if (fov2 !== camera.fov || aspect2 !== camera.aspect || far !== camera.far) {
15604
+ camera.fov = fov2;
15605
+ camera.aspect = aspect2;
15606
+ camera.far = far;
15607
+ camera.updateProjectionMatrix();
15608
+ }
15609
+ super.updateMatrices(light);
15610
+ }
15611
+ copy(source) {
15612
+ super.copy(source);
15613
+ this.focus = source.focus;
15614
+ return this;
15615
+ }
15616
+ }
15617
+ class SpotLight extends Light {
15618
+ /**
15619
+ * Constructs a new spot light.
15620
+ *
15621
+ * @param {(number|Color|string)} [color=0xffffff] - The light's color.
15622
+ * @param {number} [intensity=1] - The light's strength/intensity measured in candela (cd).
15623
+ * @param {number} [distance=0] - Maximum range of the light. `0` means no limit.
15624
+ * @param {number} [angle=Math.PI/3] - Maximum angle of light dispersion from its direction whose upper bound is `Math.PI/2`.
15625
+ * @param {number} [penumbra=0] - Percent of the spotlight cone that is attenuated due to penumbra. Value range is `[0,1]`.
15626
+ * @param {number} [decay=2] - The amount the light dims along the distance of the light.
15627
+ */
15628
+ constructor(color, intensity, distance2 = 0, angle = Math.PI / 3, penumbra = 0, decay = 2) {
15629
+ super(color, intensity);
15630
+ this.isSpotLight = true;
15631
+ this.type = "SpotLight";
15632
+ this.position.copy(Object3D.DEFAULT_UP);
15633
+ this.updateMatrix();
15634
+ this.target = new Object3D();
15635
+ this.distance = distance2;
15636
+ this.angle = angle;
15637
+ this.penumbra = penumbra;
15638
+ this.decay = decay;
15639
+ this.map = null;
15640
+ this.shadow = new SpotLightShadow();
15641
+ }
15642
+ /**
15643
+ * The light's power. Power is the luminous power of the light measured in lumens (lm).
15644
+ * Changing the power will also change the light's intensity.
15645
+ *
15646
+ * @type {number}
15647
+ */
15648
+ get power() {
15649
+ return this.intensity * Math.PI;
15650
+ }
15651
+ set power(power) {
15652
+ this.intensity = power / Math.PI;
15653
+ }
15654
+ dispose() {
15655
+ this.shadow.dispose();
15656
+ }
15657
+ copy(source, recursive) {
15658
+ super.copy(source, recursive);
15659
+ this.distance = source.distance;
15660
+ this.angle = source.angle;
15661
+ this.penumbra = source.penumbra;
15662
+ this.decay = source.decay;
15663
+ this.target = source.target.clone();
15664
+ this.shadow = source.shadow.clone();
15665
+ return this;
15666
+ }
15667
+ }
15668
+ const _projScreenMatrix = /* @__PURE__ */ new Matrix4();
15669
+ const _lightPositionWorld = /* @__PURE__ */ new Vector3$1();
15670
+ const _lookTarget = /* @__PURE__ */ new Vector3$1();
15671
+ class PointLightShadow extends LightShadow {
15672
+ /**
15673
+ * Constructs a new point light shadow.
15674
+ */
15675
+ constructor() {
15676
+ super(new PerspectiveCamera(90, 1, 0.5, 500));
15677
+ this.isPointLightShadow = true;
15678
+ this._frameExtents = new Vector2$1(4, 2);
15679
+ this._viewportCount = 6;
15680
+ this._viewports = [
15681
+ // These viewports map a cube-map onto a 2D texture with the
15682
+ // following orientation:
15683
+ //
15684
+ // xzXZ
15685
+ // y Y
15686
+ //
15687
+ // X - Positive x direction
15688
+ // x - Negative x direction
15689
+ // Y - Positive y direction
15690
+ // y - Negative y direction
15691
+ // Z - Positive z direction
15692
+ // z - Negative z direction
15693
+ // positive X
15694
+ new Vector4(2, 1, 1, 1),
15695
+ // negative X
15696
+ new Vector4(0, 1, 1, 1),
15697
+ // positive Z
15698
+ new Vector4(3, 1, 1, 1),
15699
+ // negative Z
15700
+ new Vector4(1, 1, 1, 1),
15701
+ // positive Y
15702
+ new Vector4(3, 0, 1, 1),
15703
+ // negative Y
15704
+ new Vector4(1, 0, 1, 1)
15705
+ ];
15706
+ this._cubeDirections = [
15707
+ new Vector3$1(1, 0, 0),
15708
+ new Vector3$1(-1, 0, 0),
15709
+ new Vector3$1(0, 0, 1),
15710
+ new Vector3$1(0, 0, -1),
15711
+ new Vector3$1(0, 1, 0),
15712
+ new Vector3$1(0, -1, 0)
15713
+ ];
15714
+ this._cubeUps = [
15715
+ new Vector3$1(0, 1, 0),
15716
+ new Vector3$1(0, 1, 0),
15717
+ new Vector3$1(0, 1, 0),
15718
+ new Vector3$1(0, 1, 0),
15719
+ new Vector3$1(0, 0, 1),
15720
+ new Vector3$1(0, 0, -1)
15721
+ ];
15722
+ }
15723
+ /**
15724
+ * Update the matrices for the camera and shadow, used internally by the renderer.
15725
+ *
15726
+ * @param {Light} light - The light for which the shadow is being rendered.
15727
+ * @param {number} [viewportIndex=0] - The viewport index.
15728
+ */
15729
+ updateMatrices(light, viewportIndex = 0) {
15730
+ const camera = this.camera;
15731
+ const shadowMatrix = this.matrix;
15732
+ const far = light.distance || camera.far;
15733
+ if (far !== camera.far) {
15734
+ camera.far = far;
15735
+ camera.updateProjectionMatrix();
15736
+ }
15737
+ _lightPositionWorld.setFromMatrixPosition(light.matrixWorld);
15738
+ camera.position.copy(_lightPositionWorld);
15739
+ _lookTarget.copy(camera.position);
15740
+ _lookTarget.add(this._cubeDirections[viewportIndex]);
15741
+ camera.up.copy(this._cubeUps[viewportIndex]);
15742
+ camera.lookAt(_lookTarget);
15743
+ camera.updateMatrixWorld();
15744
+ shadowMatrix.makeTranslation(-_lightPositionWorld.x, -_lightPositionWorld.y, -_lightPositionWorld.z);
15745
+ _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
15746
+ this._frustum.setFromProjectionMatrix(_projScreenMatrix, camera.coordinateSystem, camera.reversedDepth);
15747
+ }
15748
+ }
15749
+ class PointLight extends Light {
15750
+ /**
15751
+ * Constructs a new point light.
15752
+ *
15753
+ * @param {(number|Color|string)} [color=0xffffff] - The light's color.
15754
+ * @param {number} [intensity=1] - The light's strength/intensity measured in candela (cd).
15755
+ * @param {number} [distance=0] - Maximum range of the light. `0` means no limit.
15756
+ * @param {number} [decay=2] - The amount the light dims along the distance of the light.
15757
+ */
15758
+ constructor(color, intensity, distance2 = 0, decay = 2) {
15759
+ super(color, intensity);
15760
+ this.isPointLight = true;
15761
+ this.type = "PointLight";
15762
+ this.distance = distance2;
15763
+ this.decay = decay;
15764
+ this.shadow = new PointLightShadow();
15765
+ }
15766
+ /**
15767
+ * The light's power. Power is the luminous power of the light measured in lumens (lm).
15768
+ * Changing the power will also change the light's intensity.
15769
+ *
15770
+ * @type {number}
15771
+ */
15772
+ get power() {
15773
+ return this.intensity * 4 * Math.PI;
15774
+ }
15775
+ set power(power) {
15776
+ this.intensity = power / (4 * Math.PI);
15777
+ }
15778
+ dispose() {
15779
+ this.shadow.dispose();
15780
+ }
15781
+ copy(source, recursive) {
15782
+ super.copy(source, recursive);
15783
+ this.distance = source.distance;
15784
+ this.decay = source.decay;
15785
+ this.shadow = source.shadow.clone();
15786
+ return this;
15787
+ }
15788
+ }
15588
15789
  class OrthographicCamera extends Camera {
15589
15790
  /**
15590
15791
  * Constructs a new orthographic camera.
@@ -25266,7 +25467,7 @@ class WebGLRenderer {
25266
25467
  const _frustum = new Frustum();
25267
25468
  let _clippingEnabled = false;
25268
25469
  let _localClippingEnabled = false;
25269
- const _projScreenMatrix = new Matrix4();
25470
+ const _projScreenMatrix2 = new Matrix4();
25270
25471
  const _vector32 = new Vector3$1();
25271
25472
  const _vector4 = new Vector4();
25272
25473
  const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true };
@@ -25775,8 +25976,8 @@ class WebGLRenderer {
25775
25976
  currentRenderState = renderStates.get(scene, renderStateStack.length);
25776
25977
  currentRenderState.init(camera);
25777
25978
  renderStateStack.push(currentRenderState);
25778
- _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
25779
- _frustum.setFromProjectionMatrix(_projScreenMatrix, WebGLCoordinateSystem, camera.reversedDepth);
25979
+ _projScreenMatrix2.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
25980
+ _frustum.setFromProjectionMatrix(_projScreenMatrix2, WebGLCoordinateSystem, camera.reversedDepth);
25780
25981
  _localClippingEnabled = this.localClippingEnabled;
25781
25982
  _clippingEnabled = clipping.init(this.clippingPlanes, _localClippingEnabled);
25782
25983
  currentRenderList = renderLists.get(scene, renderListStack.length);
@@ -25862,7 +26063,7 @@ class WebGLRenderer {
25862
26063
  } else if (object.isSprite) {
25863
26064
  if (!object.frustumCulled || _frustum.intersectsSprite(object)) {
25864
26065
  if (sortObjects) {
25865
- _vector4.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix);
26066
+ _vector4.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix2);
25866
26067
  }
25867
26068
  const geometry = objects.update(object);
25868
26069
  const material = object.material;
@@ -25882,7 +26083,7 @@ class WebGLRenderer {
25882
26083
  if (geometry.boundingSphere === null) geometry.computeBoundingSphere();
25883
26084
  _vector4.copy(geometry.boundingSphere.center);
25884
26085
  }
25885
- _vector4.applyMatrix4(object.matrixWorld).applyMatrix4(_projScreenMatrix);
26086
+ _vector4.applyMatrix4(object.matrixWorld).applyMatrix4(_projScreenMatrix2);
25886
26087
  }
25887
26088
  if (Array.isArray(material)) {
25888
26089
  const groups = geometry.groups;
@@ -27110,6 +27311,7 @@ const magic = "<roblox!";
27110
27311
  const xmlMagic = "<roblox ";
27111
27312
  const ObjectDescClassTypes = ["Part", "MeshPart", "Decal"];
27112
27313
  const EmitterGroupDescClassTypes = ["ParticleEmitter", "Sparkles", "Fire", "Smoke"];
27314
+ const LightDescClassTypes = ["PointLight", "SpotLight", "SurfaceLight"];
27113
27315
  const ParticleOrientation = {
27114
27316
  "FacingCamera": 0,
27115
27317
  "FacingCameraWorldUp": 1,
@@ -29548,7 +29750,7 @@ const FLAGS = {
29548
29750
  USE_RENDERTARGET: true,
29549
29751
  AUTO_RESTORE_CONTEXT: true,
29550
29752
  RENDERTARGET_TO_CANVASTEXTURE: false,
29551
- THUMBNAIL_TIMEOUT: 750,
29753
+ THUMBNAIL_TIMEOUT: 500,
29552
29754
  //skeleton
29553
29755
  SHOW_SKELETON_HELPER: false,
29554
29756
  UPDATE_SKELETON: true,
@@ -30168,6 +30370,7 @@ class Instance {
30168
30370
  this._hasWrappered = true;
30169
30371
  wrapper.created();
30170
30372
  }
30373
+ return wrapper;
30171
30374
  }
30172
30375
  addConnectionReference(connection) {
30173
30376
  if (!this._connectionReferences.includes(connection)) {
@@ -30307,6 +30510,9 @@ class Instance {
30307
30510
  const hex = BrickColors[this.Prop("BrickColor")];
30308
30511
  const rgb = hexToRgb(hex);
30309
30512
  const color3uint8 = new Color3uint8(rgb?.r, rgb?.g, rgb?.b);
30513
+ color3uint8.R *= 255;
30514
+ color3uint8.G *= 255;
30515
+ color3uint8.B *= 255;
30310
30516
  return color3uint8;
30311
30517
  }
30312
30518
  break;
@@ -31802,6 +32008,22 @@ function snapToNumber(value, numbers) {
31802
32008
  }
31803
32009
  return closestNumber;
31804
32010
  }
32011
+ async function awaitTimeoutThrows(promise, time2 = 15e3) {
32012
+ const timeoutPromise = new Promise((_resolve, reject) => {
32013
+ setTimeout(() => {
32014
+ reject(`Promise timed out after ${time2} milliseconds`);
32015
+ }, time2);
32016
+ });
32017
+ return Promise.race([promise, timeoutPromise]);
32018
+ }
32019
+ async function awaitTimeout(promise, time2 = 15e3) {
32020
+ const timeoutPromise = new Promise((resolve) => {
32021
+ setTimeout(() => {
32022
+ resolve(new Response(`Promise timed out after ${time2} milliseconds`));
32023
+ }, time2);
32024
+ });
32025
+ return Promise.race([promise, timeoutPromise]);
32026
+ }
31805
32027
  function getXMLProperty(doc, propertyName) {
31806
32028
  const propertyNode = doc.querySelector('[name="' + propertyName + '"]');
31807
32029
  if (!propertyNode) {
@@ -35288,14 +35510,15 @@ async function RBLXPatch(url, auth, body, attempt = 0) {
35288
35510
  return RBLXPost(url, auth, body, attempt, "PATCH");
35289
35511
  }
35290
35512
  async function getAssetBufferInternal(url, headers, extraStr) {
35291
- API.Misc.startCurrentlyLoadingAssets();
35513
+ const loadingLabel = `getAssetBufferInternal-${url}-${extraStr}`;
35514
+ API.Misc.startCurrentlyLoadingAssets(loadingLabel);
35292
35515
  const fetchStr = await API.Misc.assetURLToCDNURL(url, headers, extraStr);
35293
35516
  if (fetchStr instanceof Response) {
35294
- API.Misc.stopCurrentlyLoadingAssets();
35517
+ API.Misc.stopCurrentlyLoadingAssets(loadingLabel);
35295
35518
  return fetchStr;
35296
35519
  }
35297
35520
  const response = await RBLXGet(fetchStr, void 0, false);
35298
- API.Misc.stopCurrentlyLoadingAssets();
35521
+ API.Misc.stopCurrentlyLoadingAssets(loadingLabel);
35299
35522
  if (response.status === 200) {
35300
35523
  const data = await response.arrayBuffer();
35301
35524
  return data;
@@ -35303,14 +35526,10 @@ async function getAssetBufferInternal(url, headers, extraStr) {
35303
35526
  return response;
35304
35527
  }
35305
35528
  }
35306
- let isCurrentlyLoading = false;
35307
- let currentlyLoadingAssets = 0;
35308
- function _updateCurrentlyLoadingAssets() {
35309
- const newCurrentlyLoading = currentlyLoadingAssets > 0;
35310
- if (isCurrentlyLoading !== newCurrentlyLoading) {
35311
- API.Events.OnLoadingAssets.Fire(newCurrentlyLoading);
35312
- }
35313
- isCurrentlyLoading = newCurrentlyLoading;
35529
+ const currentlyLoadingAssets = [];
35530
+ function _updateCurrentlyLoadingAssets(type, label) {
35531
+ const newCurrentlyLoading = currentlyLoadingAssets.length > 0;
35532
+ API.Events.OnLoadingAssets.Fire(newCurrentlyLoading, type, label);
35314
35533
  }
35315
35534
  const CACHE = {
35316
35535
  "AssetBuffer": /* @__PURE__ */ new Map(),
@@ -35424,18 +35643,25 @@ function createContentMap() {
35424
35643
  ContentMap.set("roavatar://RigR6.rbxm", FLAGS.RIG_PATH + "RigR6.rbxm");
35425
35644
  ContentMap.set("roavatar://RigR15.rbxm", FLAGS.RIG_PATH + "RigR15.rbxm");
35426
35645
  }
35646
+ ContentMap.set("roavatar://AvatarEditorScene.rbxm", "74148511291027");
35647
+ ContentMap.set("roavatar://AvatarSceneNew.rbxm", "130507237273896");
35427
35648
  }
35428
35649
  let CachedRoAvatarData = void 0;
35429
35650
  let ThumbnailsToBatch = [];
35430
35651
  const API = {
35431
35652
  "Misc": {
35432
- "startCurrentlyLoadingAssets": function() {
35433
- currentlyLoadingAssets += 1;
35434
- _updateCurrentlyLoadingAssets();
35653
+ "startCurrentlyLoadingAssets": function(label) {
35654
+ currentlyLoadingAssets.push(label);
35655
+ _updateCurrentlyLoadingAssets("start", label);
35435
35656
  },
35436
- "stopCurrentlyLoadingAssets": function() {
35437
- currentlyLoadingAssets -= 1;
35438
- _updateCurrentlyLoadingAssets();
35657
+ "stopCurrentlyLoadingAssets": function(label) {
35658
+ const labelIndex = currentlyLoadingAssets.indexOf(label);
35659
+ if (labelIndex > -1) {
35660
+ currentlyLoadingAssets.splice(labelIndex, 1);
35661
+ _updateCurrentlyLoadingAssets("finish", label);
35662
+ } else {
35663
+ throw new Error(`Invalid loading label: ${label}`);
35664
+ }
35439
35665
  },
35440
35666
  "idFromStr": function(str) {
35441
35667
  const numStrs = str.match(/\d+(\.\d+)?/g) || [];
@@ -35465,7 +35691,7 @@ const API = {
35465
35691
  } else if (str.startsWith(".")) {
35466
35692
  url = str;
35467
35693
  } else {
35468
- warn(false, `Failed to parse path of ${str}`);
35694
+ warn(false, `Failed to parse path of ${str}, leaving as is`);
35469
35695
  }
35470
35696
  if (FLAGS.ASSETDELIVERY_V2) {
35471
35697
  if (url.includes("/v1/")) {
@@ -35497,6 +35723,12 @@ const API = {
35497
35723
  }
35498
35724
  const cdnURL = await API.Misc.getCDNURLFromAssetDelivery(fetchStr, headers);
35499
35725
  return cdnURL;
35726
+ },
35727
+ "getCurrentlyLoading": function() {
35728
+ return currentlyLoadingAssets.length > 0;
35729
+ },
35730
+ "getCurrentlyLoadingLabels": function() {
35731
+ return currentlyLoadingAssets;
35500
35732
  }
35501
35733
  },
35502
35734
  "Events": {
@@ -35514,6 +35746,7 @@ const API = {
35514
35746
  API.Misc.assetURLToCDNURL(url).then((fetchStr) => {
35515
35747
  if (fetchStr instanceof Response) {
35516
35748
  resolve(void 0);
35749
+ cacheResolve(void 0);
35517
35750
  return;
35518
35751
  }
35519
35752
  const image = new Image();
@@ -35523,7 +35756,7 @@ const API = {
35523
35756
  CACHE.Image.set(cacheURL, image);
35524
35757
  };
35525
35758
  image.onerror = () => {
35526
- cacheResolve(image);
35759
+ cacheResolve(void 0);
35527
35760
  resolve(void 0);
35528
35761
  CACHE.Image.set(cacheURL, void 0);
35529
35762
  };
@@ -36447,6 +36680,7 @@ class RBFDeformerPatch {
36447
36680
  epsilon = 1e-6;
36448
36681
  // avoid matrix from being singular
36449
36682
  affectBones = true;
36683
+ hasSolved = false;
36450
36684
  id = rbfDeformerIdCount++;
36451
36685
  /**
36452
36686
  * Creates the deformer and prepares for deformation
@@ -36512,29 +36746,37 @@ class RBFDeformerPatch {
36512
36746
  * @returns void
36513
36747
  */
36514
36748
  async solveAsync() {
36749
+ this.hasSolved = true;
36515
36750
  if (this.refVerts.length === 0) {
36516
36751
  return;
36517
36752
  }
36518
- const [neighborIndicesBuf, weightsBuf, nearestPatchBuf] = await WorkerPool.instance.work(
36519
- "RBFDeformerSolveAsync",
36520
- [this.patchCount, this.K, this.epsilon, this.importantIndices.buffer, this.refVerts.buffer, this.distVerts.buffer, this.meshVerts.buffer, this.meshBones.buffer],
36521
- [
36522
- this.importantIndices.buffer,
36523
- /*this.refVerts.buffer,*/
36524
- this.distVerts.buffer,
36525
- this.meshVerts.buffer,
36526
- this.meshBones.buffer
36527
- ]
36528
- );
36529
- time(`RBFDeformerPatch.solveAsync.unpack.${this.id}`);
36530
- this.neighborIndices = neighborIndicesBuf.map((a) => {
36531
- return new Uint16Array(a);
36532
- });
36533
- this.weights = weightsBuf.map((a) => {
36534
- return new Float32Array(a);
36535
- });
36536
- this.nearestPatch = new Uint16Array(nearestPatchBuf);
36537
- timeEnd(`RBFDeformerPatch.solveAsync.unpack.${this.id}`);
36753
+ const loadingLabel = `rbfDeform-${this.id}`;
36754
+ API.Misc.startCurrentlyLoadingAssets(loadingLabel);
36755
+ try {
36756
+ const [neighborIndicesBuf, weightsBuf, nearestPatchBuf] = await awaitTimeoutThrows(WorkerPool.instance.work(
36757
+ "RBFDeformerSolveAsync",
36758
+ [this.patchCount, this.K, this.epsilon, this.importantIndices.buffer, this.refVerts.buffer, this.distVerts.buffer, this.meshVerts.buffer, this.meshBones.buffer],
36759
+ [
36760
+ this.importantIndices.buffer,
36761
+ /*this.refVerts.buffer,*/
36762
+ this.distVerts.buffer,
36763
+ this.meshVerts.buffer,
36764
+ this.meshBones.buffer
36765
+ ]
36766
+ ));
36767
+ time(`RBFDeformerPatch.solveAsync.unpack.${this.id}`);
36768
+ this.neighborIndices = neighborIndicesBuf.map((a) => {
36769
+ return new Uint16Array(a);
36770
+ });
36771
+ this.weights = weightsBuf.map((a) => {
36772
+ return new Float32Array(a);
36773
+ });
36774
+ this.nearestPatch = new Uint16Array(nearestPatchBuf);
36775
+ timeEnd(`RBFDeformerPatch.solveAsync.unpack.${this.id}`);
36776
+ } catch (e) {
36777
+ error(true, "RBFDeformerPatch.solveAsync() failed", e);
36778
+ }
36779
+ API.Misc.stopCurrentlyLoadingAssets(loadingLabel);
36538
36780
  }
36539
36781
  /**
36540
36782
  * solveAsync() needs to be called before this
@@ -36543,11 +36785,15 @@ class RBFDeformerPatch {
36543
36785
  * @returns New position after deformation
36544
36786
  */
36545
36787
  deform(i) {
36546
- if (!this.nearestPatch || !this.neighborIndices || !this.weights) {
36547
- throw new Error("RBF has not been solved");
36548
- }
36549
36788
  const vertLen = this.mesh.coreMesh.numverts;
36550
36789
  const vec = i < vertLen ? this.mesh.coreMesh.getPos(i) : this.mesh.skinning.bones[i - vertLen].position;
36790
+ if (!this.nearestPatch || !this.neighborIndices || !this.weights) {
36791
+ if (this.hasSolved) {
36792
+ return vec;
36793
+ } else {
36794
+ throw new Error("RBF has not been solved");
36795
+ }
36796
+ }
36551
36797
  const idx = this.nearestPatch[i];
36552
36798
  const neighborIndices = this.neighborIndices[idx];
36553
36799
  const weights = this.weights[idx];
@@ -42261,6 +42507,50 @@ function getModelHSRDesc(model) {
42261
42507
  return newHSRDesc;
42262
42508
  }
42263
42509
  }
42510
+ function addTri(mesh, totalVerts, totalFaces, a, b, c, an, bn, cn, auv = [0, 0], buv = [0, 0], cuv = [0, 0]) {
42511
+ if (!bn) bn = an;
42512
+ if (!cn) cn = an;
42513
+ mesh.coreMesh.setPos(totalVerts++, a);
42514
+ mesh.coreMesh.setPos(totalVerts++, b);
42515
+ mesh.coreMesh.setPos(totalVerts++, c);
42516
+ totalVerts -= 3;
42517
+ mesh.coreMesh.setNormal(totalVerts++, an);
42518
+ mesh.coreMesh.setNormal(totalVerts++, bn);
42519
+ mesh.coreMesh.setNormal(totalVerts++, cn);
42520
+ totalVerts -= 3;
42521
+ mesh.coreMesh.setUV(totalVerts++, auv);
42522
+ mesh.coreMesh.setUV(totalVerts++, buv);
42523
+ mesh.coreMesh.setUV(totalVerts++, cuv);
42524
+ mesh.coreMesh.setFace(totalFaces, [totalVerts - 3, totalVerts - 2, totalVerts - 1]);
42525
+ return totalVerts;
42526
+ }
42527
+ function addQuad(mesh, totalVerts, totalFaces, a, b, c, d, an, bn, cn, dn, auv = [0, 0], buv = [0, 0], cuv = [0, 0], duv = [0, 0]) {
42528
+ if (!bn) bn = an;
42529
+ if (!cn) cn = an;
42530
+ if (!dn) dn = an;
42531
+ totalVerts = addTri(mesh, totalVerts, totalFaces, c, b, a, cn, bn, an, cuv, buv, auv);
42532
+ totalVerts = addTri(mesh, totalVerts, totalFaces + 1, c, a, d, cn, an, dn, cuv, auv, duv);
42533
+ return totalVerts;
42534
+ }
42535
+ function buildCube(x, y, z) {
42536
+ const mesh = new FileMesh();
42537
+ mesh.coreMesh.increaseVerts(3 * 2 * 6);
42538
+ mesh.coreMesh.increaseFaces(2 * 6);
42539
+ let totalVerts = 0;
42540
+ let totalFaces = 0;
42541
+ totalVerts = addQuad(mesh, totalVerts, totalFaces, [-x, y, -z], [x, y, -z], [x, y, z], [-x, y, z], [0, 1, 0], void 0, void 0, void 0, [0, 0], [1, 0], [1, 1], [0, 1]);
42542
+ totalFaces += 2;
42543
+ totalVerts = addQuad(mesh, totalVerts, totalFaces, [-x, -y, z], [x, -y, z], [x, -y, -z], [-x, -y, -z], [0, -1, 0], void 0, void 0, void 0, [0, 0], [1, 0], [1, 1], [0, 1]);
42544
+ totalFaces += 2;
42545
+ totalVerts = addQuad(mesh, totalVerts, totalFaces, [-x, y, z], [x, y, z], [x, -y, z], [-x, -y, z], [0, 0, 1], void 0, void 0, void 0, [0, 0], [1, 0], [1, 1], [0, 1]);
42546
+ totalFaces += 2;
42547
+ totalVerts = addQuad(mesh, totalVerts, totalFaces, [x, y, -z], [-x, y, -z], [-x, -y, -z], [x, -y, -z], [0, 0, -1], void 0, void 0, void 0, [0, 0], [1, 0], [1, 1], [0, 1]);
42548
+ totalFaces += 2;
42549
+ totalVerts = addQuad(mesh, totalVerts, totalFaces, [-x, y, -z], [-x, y, z], [-x, -y, z], [-x, -y, -z], [-1, 0, 0], void 0, void 0, void 0, [0, 0], [1, 0], [1, 1], [0, 1]);
42550
+ totalFaces += 2;
42551
+ addQuad(mesh, totalVerts, totalFaces, [x, y, z], [x, y, -z], [x, -y, -z], [x, -y, z], [1, 0, 0], void 0, void 0, void 0, [0, 0], [1, 0], [1, 1], [0, 1]);
42552
+ return mesh;
42553
+ }
42264
42554
  const HSR_CACHE = /* @__PURE__ */ new Map();
42265
42555
  function doHSR(totalUvToHits, targetCage, mesh, moveVerts = true, cacheStr) {
42266
42556
  let closestVertIndexArr = void 0;
@@ -42720,12 +43010,16 @@ class MeshDesc {
42720
43010
  }
42721
43011
  }
42722
43012
  async compileMesh() {
42723
- if (!this.mesh) return;
43013
+ let mesh = void 0;
42724
43014
  const meshToLoad = this.mesh;
42725
- const mesh = await API.Asset.GetMesh(meshToLoad, void 0);
42726
- if (mesh instanceof Response) {
42727
- warn(true, "Failed to get mesh for compileMesh", mesh);
42728
- return mesh;
43015
+ if (meshToLoad) {
43016
+ mesh = await API.Asset.GetMesh(meshToLoad, void 0);
43017
+ if (mesh instanceof Response) {
43018
+ warn(true, "Failed to get mesh for compileMesh", meshToLoad, mesh);
43019
+ return mesh;
43020
+ }
43021
+ } else {
43022
+ mesh = buildCube(0.5, 0.5, 0.5);
42729
43023
  }
42730
43024
  if (!mesh.facs && this.headMesh && mesh.skinning.skinnings.length > 0) {
42731
43025
  const headMesh = await API.Asset.GetMesh(this.headMesh, void 0, true);
@@ -43358,6 +43652,7 @@ class TextureLayer {
43358
43652
  class MaterialDesc {
43359
43653
  layers = [];
43360
43654
  isDecal = false;
43655
+ canHaveMipmaps = true;
43361
43656
  transparent = false;
43362
43657
  transparency = 0;
43363
43658
  doubleSided = false;
@@ -43372,7 +43667,7 @@ class MaterialDesc {
43372
43667
  createdTextures = [];
43373
43668
  isSame(other) {
43374
43669
  if (this.dirty || other.dirty) return false;
43375
- 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;
43670
+ 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;
43376
43671
  let layersSame = true;
43377
43672
  if (this.layers.length !== other.layers.length) {
43378
43673
  layersSame = false;
@@ -43464,6 +43759,7 @@ class MaterialDesc {
43464
43759
  const texturesToDestroy = [];
43465
43760
  let noMipmaps = false;
43466
43761
  let hasColorLayer = false;
43762
+ if (!this.canHaveMipmaps) noMipmaps = true;
43467
43763
  for (const layer of this.layers) {
43468
43764
  if (layer instanceof TextureLayer && layer[textureType]) {
43469
43765
  const layerImage = layerTextures.get(layer[textureType]);
@@ -43690,6 +43986,7 @@ class MaterialDesc {
43690
43986
  texture.wrapT = ogTexture.wrapT;
43691
43987
  texture.minFilter = ogTexture.minFilter;
43692
43988
  texture.magFilter = ogTexture.magFilter;
43989
+ texture.generateMipmaps = ogTexture.generateMipmaps;
43693
43990
  }
43694
43991
  }
43695
43992
  if (!this.transparent) {
@@ -43721,7 +44018,7 @@ class MaterialDesc {
43721
44018
  texture.colorSpace = textureType === "color" ? SRGBColorSpace : NoColorSpace;
43722
44019
  texture.wrapS = RepeatWrapping;
43723
44020
  texture.wrapT = RepeatWrapping;
43724
- if (this.isDecal) {
44021
+ if (this.isDecal || !this.canHaveMipmaps) {
43725
44022
  texture.generateMipmaps = false;
43726
44023
  }
43727
44024
  for (const layer of this.layers) {
@@ -43778,6 +44075,7 @@ class MaterialDesc {
43778
44075
  texture.wrapS = RepeatWrapping;
43779
44076
  texture.wrapT = RepeatWrapping;
43780
44077
  texture.colorSpace = textureType === "color" ? SRGBColorSpace : NoColorSpace;
44078
+ texture.generateMipmaps = this.canHaveMipmaps;
43781
44079
  texture.needsUpdate = true;
43782
44080
  return [texture, hasTransparency];
43783
44081
  }
@@ -43872,7 +44170,7 @@ class MaterialDesc {
43872
44170
  if (normalTexture || roughnessTexture || metalnessTexture || emissiveTexture) {
43873
44171
  material = new MeshStandardMaterial({
43874
44172
  ...textureTemplate,
43875
- emissiveIntensity: hasEmissive ? this.emissiveStrength : 0,
44173
+ emissiveIntensity: hasEmissive ? 1 / Math.sqrt(40) * Math.sqrt(this.emissiveStrength) : 0,
43876
44174
  emissive: hasEmissive ? new Color(this.emissiveTint.R, this.emissiveTint.G, this.emissiveTint.B) : new Color(0, 0, 0),
43877
44175
  transparent: hasTransparency,
43878
44176
  opacity: 1 - this.transparency,
@@ -44023,6 +44321,7 @@ class MaterialDesc {
44023
44321
  decalLayer.roughness = roughnessMap;
44024
44322
  }
44025
44323
  decalLayer.uvType = "Normal";
44324
+ this.canHaveMipmaps = false;
44026
44325
  let ZIndex = 1;
44027
44326
  if (decal.HasProperty("ZIndex")) {
44028
44327
  ZIndex = decal.Prop("ZIndex");
@@ -44174,6 +44473,7 @@ class MaterialDesc {
44174
44473
  }
44175
44474
  if (child.Prop("Name") === "Head" && isAffectedByHumanoid(child)) {
44176
44475
  decalLayer.uvType = "Normal";
44476
+ this.canHaveMipmaps = false;
44177
44477
  } else {
44178
44478
  decalLayer.uvType = "Decal";
44179
44479
  decalLayer.face = decal.Prop("Face");
@@ -44251,12 +44551,73 @@ class FaceControlsWrapper extends InstanceWrapper {
44251
44551
  }
44252
44552
  }
44253
44553
  }
44254
- function setBoneToCFrame$1(bone, cf) {
44255
- bone.position.set(...cf.Position);
44256
- bone.rotation.order = "YXZ";
44257
- bone.rotation.x = rad(cf.Orientation[0]);
44258
- bone.rotation.y = rad(cf.Orientation[1]);
44259
- bone.rotation.z = rad(cf.Orientation[2]);
44554
+ function setTHREEObjectCF(threeObject, cframe) {
44555
+ threeObject.position.set(cframe.Position[0], cframe.Position[1], cframe.Position[2]);
44556
+ threeObject.rotation.order = "YXZ";
44557
+ threeObject.rotation.x = rad(cframe.Orientation[0]);
44558
+ threeObject.rotation.y = rad(cframe.Orientation[1]);
44559
+ threeObject.rotation.z = rad(cframe.Orientation[2]);
44560
+ }
44561
+ class DisposableDesc {
44562
+ disposeMesh(scene, mesh) {
44563
+ disposeMesh(scene, mesh);
44564
+ }
44565
+ disposeMeshes(scene, meshes) {
44566
+ for (const mesh of meshes) {
44567
+ this.disposeMesh(scene, mesh);
44568
+ }
44569
+ }
44570
+ disposeRenderLists(renderer) {
44571
+ renderer.renderLists.dispose();
44572
+ }
44573
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
44574
+ dispose(_renderer, _scene) {
44575
+ throw new Error("Virtual method dispose called");
44576
+ }
44577
+ }
44578
+ class RenderDesc extends DisposableDesc {
44579
+ renderScene;
44580
+ results;
44581
+ instance;
44582
+ constructor(renderScene) {
44583
+ super();
44584
+ this.renderScene = renderScene;
44585
+ }
44586
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
44587
+ isSame(_other) {
44588
+ throw new Error("Virtual method isSame called");
44589
+ }
44590
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
44591
+ needsRegeneration(_other) {
44592
+ throw new Error("Virtual method needsRegeneration called");
44593
+ }
44594
+ fromRenderDesc(other) {
44595
+ if (this.needsRegeneration(other)) {
44596
+ throw new Error("These RenderableDesc objects have differences that require recompilation");
44597
+ }
44598
+ this.virtualFromRenderDesc(other);
44599
+ }
44600
+ transferFrom(other) {
44601
+ this.virtualTransferFrom(other);
44602
+ }
44603
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
44604
+ virtualTransferFrom(_other) {
44605
+ }
44606
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
44607
+ virtualFromRenderDesc(_other) {
44608
+ throw new Error("Virtual method virtualFromRenderDesc called");
44609
+ }
44610
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
44611
+ fromInstance(_child) {
44612
+ throw new Error("Virtual method fromInstance called");
44613
+ }
44614
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
44615
+ async compileResults(_renderer, _scene) {
44616
+ throw new Error("Virtual method compileResults called");
44617
+ }
44618
+ updateResults() {
44619
+ throw new Error("Virtual method updateResults called");
44620
+ }
44260
44621
  }
44261
44622
  const BaseR15Bones = ["Root", "HumanoidRootNode", "LowerTorso", "UpperTorso", "RightUpperArm", "RightLowerArm", "RightHand", "LeftUpperArm", "LeftLowerArm", "LeftHand", "Head", "DynamicHead"];
44262
44623
  function getJointForInstances$1(parent, child, includeTransform) {
@@ -44393,14 +44754,14 @@ let SkeletonDesc$1 = class SkeletonDesc {
44393
44754
  } else if (threeBone.name === "DynamicHead") {
44394
44755
  this.originalDynamicHeadCFrame = boneCF;
44395
44756
  }
44396
- setBoneToCFrame$1(threeBone, boneCF);
44757
+ setTHREEObjectCF(threeBone, boneCF);
44397
44758
  parentThreeBone.add(threeBone);
44398
44759
  } else {
44399
44760
  rootBone = threeBone;
44400
44761
  const boneCF = new CFrame(...bone.position);
44401
44762
  boneCF.fromRotationMatrix(...bone.rotationMatrix);
44402
44763
  this.originalBoneCFrames.push(boneCF);
44403
- setBoneToCFrame$1(threeBone, boneCF);
44764
+ setTHREEObjectCF(threeBone, boneCF);
44404
44765
  }
44405
44766
  }
44406
44767
  if (!rootBone) {
@@ -44542,27 +44903,27 @@ let SkeletonDesc$1 = class SkeletonDesc {
44542
44903
  rootBoneCF = rootBoneCFog.inverse();
44543
44904
  }
44544
44905
  if (partEquivalent && parentPartEquivalent) {
44545
- setBoneToCFrame$1(bone, rootBoneCF.multiply(getJointForInstances$1(parentPartEquivalent, partEquivalent, includeTransform)));
44906
+ setTHREEObjectCF(bone, rootBoneCF.multiply(getJointForInstances$1(parentPartEquivalent, partEquivalent, includeTransform)));
44546
44907
  } else if (partEquivalent) {
44547
44908
  if (includeTransform) {
44548
- setBoneToCFrame$1(bone, rootBoneCF.multiply(partEquivalent.Prop("CFrame")));
44909
+ setTHREEObjectCF(bone, rootBoneCF.multiply(partEquivalent.Prop("CFrame")));
44549
44910
  } else {
44550
44911
  let hrpCF = new CFrame();
44551
44912
  const hrp = humanoidRootPartEquivalent;
44552
44913
  if (hrp) {
44553
44914
  hrpCF = hrp.Prop("CFrame");
44554
44915
  }
44555
- setBoneToCFrame$1(bone, rootBoneCF.multiply(hrpCF.multiply(traverseRigCFrame(partEquivalent))));
44916
+ setTHREEObjectCF(bone, rootBoneCF.multiply(hrpCF.multiply(traverseRigCFrame(partEquivalent))));
44556
44917
  }
44557
44918
  } else if (bone.name === "Root") {
44558
- setBoneToCFrame$1(bone, rootBoneCF.multiply(new CFrame()));
44919
+ setTHREEObjectCF(bone, rootBoneCF.multiply(new CFrame()));
44559
44920
  } else if (bone.name === "HumanoidRootNode") {
44560
44921
  let rootCF = new CFrame();
44561
44922
  const rootPart = humanoidRootPartEquivalent;
44562
44923
  if (rootPart) {
44563
44924
  rootCF = rootPart.Prop("CFrame");
44564
44925
  }
44565
- setBoneToCFrame$1(bone, rootBoneCF.multiply(rootCF));
44926
+ setTHREEObjectCF(bone, rootBoneCF.multiply(rootCF));
44566
44927
  } else if (bone.name === "DynamicHead" && isHead) {
44567
44928
  const head = this.getPartEquivalent(selfInstance, "Head");
44568
44929
  if (head) {
@@ -44587,7 +44948,7 @@ let SkeletonDesc$1 = class SkeletonDesc {
44587
44948
  neckCF = new CFrame();
44588
44949
  }
44589
44950
  const totalCF = neckCF.inverse().multiply(scaledCF);
44590
- setBoneToCFrame$1(bone, totalCF);
44951
+ setTHREEObjectCF(bone, totalCF);
44591
44952
  }
44592
44953
  } else if (!isHead || boneIsChildOf(bone, "DynamicHead")) {
44593
44954
  const ogCF = this.originalBoneCFrames[i];
@@ -44607,7 +44968,7 @@ let SkeletonDesc$1 = class SkeletonDesc {
44607
44968
  headOffset.Orientation = [0, 0, 0];
44608
44969
  }
44609
44970
  const finalCF = headOffset.multiply(scaledCF);
44610
- setBoneToCFrame$1(bone, finalCF);
44971
+ setTHREEObjectCF(bone, finalCF);
44611
44972
  }
44612
44973
  }
44613
44974
  }
@@ -44701,7 +45062,7 @@ let SkeletonDesc$1 = class SkeletonDesc {
44701
45062
  euler.reorder("YXZ");
44702
45063
  resultCF.Orientation = [deg(euler.x), deg(euler.y), deg(euler.z)];
44703
45064
  resultCF.Position = multiply(totalPosition.toVec3(), headScale);
44704
- setBoneToCFrame$1(bone, jointCF.multiply(resultCF));
45065
+ setTHREEObjectCF(bone, jointCF.multiply(resultCF));
44705
45066
  break;
44706
45067
  }
44707
45068
  }
@@ -44985,74 +45346,6 @@ class SkeletonDesc2 {
44985
45346
  return meshDesc.canHaveSkinning && meshDesc.fileMesh && meshDesc.fileMesh.skinning && meshDesc.fileMesh.skinning.subsets.length > 0 && meshDesc.fileMesh.skinning.skinnings.length > 0;
44986
45347
  }
44987
45348
  }
44988
- class DisposableDesc {
44989
- disposeMesh(scene, mesh) {
44990
- disposeMesh(scene, mesh);
44991
- }
44992
- disposeMeshes(scene, meshes) {
44993
- for (const mesh of meshes) {
44994
- this.disposeMesh(scene, mesh);
44995
- }
44996
- }
44997
- disposeRenderLists(renderer) {
44998
- renderer.renderLists.dispose();
44999
- }
45000
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45001
- dispose(_renderer, _scene) {
45002
- throw new Error("Virtual method dispose called");
45003
- }
45004
- }
45005
- class RenderDesc extends DisposableDesc {
45006
- renderScene;
45007
- results;
45008
- instance;
45009
- constructor(renderScene) {
45010
- super();
45011
- this.renderScene = renderScene;
45012
- }
45013
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45014
- isSame(_other) {
45015
- throw new Error("Virtual method isSame called");
45016
- }
45017
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45018
- needsRegeneration(_other) {
45019
- throw new Error("Virtual method needsRegeneration called");
45020
- }
45021
- fromRenderDesc(other) {
45022
- if (this.needsRegeneration(other)) {
45023
- throw new Error("These RenderableDesc objects have differences that require recompilation");
45024
- }
45025
- this.virtualFromRenderDesc(other);
45026
- }
45027
- transferFrom(other) {
45028
- this.virtualTransferFrom(other);
45029
- }
45030
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45031
- virtualTransferFrom(_other) {
45032
- }
45033
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45034
- virtualFromRenderDesc(_other) {
45035
- throw new Error("Virtual method virtualFromRenderDesc called");
45036
- }
45037
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45038
- fromInstance(_child) {
45039
- throw new Error("Virtual method fromInstance called");
45040
- }
45041
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45042
- async compileResults(_renderer, _scene) {
45043
- throw new Error("Virtual method compileResults called");
45044
- }
45045
- updateResults() {
45046
- throw new Error("Virtual method updateResults called");
45047
- }
45048
- }
45049
- function setTHREEMeshCF(threeMesh, cframe) {
45050
- threeMesh.position.set(cframe.Position[0], cframe.Position[1], cframe.Position[2]);
45051
- threeMesh.rotation.order = "YXZ";
45052
- threeMesh.rotation.x = rad(cframe.Orientation[0]);
45053
- threeMesh.rotation.y = rad(cframe.Orientation[1]);
45054
- threeMesh.rotation.z = rad(cframe.Orientation[2]);
45055
- }
45056
45349
  class ObjectDesc extends RenderDesc {
45057
45350
  cframe = new CFrame();
45058
45351
  size = new Vector32(1, 1, 1);
@@ -45108,6 +45401,7 @@ class ObjectDesc extends RenderDesc {
45108
45401
  if (part) {
45109
45402
  switch (part.className) {
45110
45403
  case "Part": {
45404
+ if (!isAffectedByHumanoid(part)) this.size = part.PropOrDefault("Size", this.size);
45111
45405
  const specialMesh = part.FindFirstChildOfClass("SpecialMesh");
45112
45406
  if (specialMesh) {
45113
45407
  this.size = specialMesh.Property("Scale");
@@ -45164,7 +45458,8 @@ class ObjectDesc extends RenderDesc {
45164
45458
  }
45165
45459
  }
45166
45460
  async compileResults(renderer, scene) {
45167
- API.Misc.startCurrentlyLoadingAssets();
45461
+ const loadingLabel = this.instance ? this.instance.GetFullName() : "unknown";
45462
+ API.Misc.startCurrentlyLoadingAssets(loadingLabel);
45168
45463
  const originalResult = this.results;
45169
45464
  const originalSkeletonDesc = this.skeletonDesc;
45170
45465
  this.results = void 0;
@@ -45175,7 +45470,8 @@ class ObjectDesc extends RenderDesc {
45175
45470
  ];
45176
45471
  const [threeMesh, threeMaterial] = await Promise.all(promises);
45177
45472
  if (!(threeMesh instanceof Mesh)) {
45178
- API.Misc.stopCurrentlyLoadingAssets();
45473
+ warn(true, "Failed to get mesh for objectDesc", this.instance ? this.instance.GetFullName() : "unknown");
45474
+ API.Misc.stopCurrentlyLoadingAssets(loadingLabel);
45179
45475
  return threeMesh;
45180
45476
  }
45181
45477
  if (threeMesh instanceof SkinnedMesh) {
@@ -45211,7 +45507,7 @@ class ObjectDesc extends RenderDesc {
45211
45507
  if (originalResult) {
45212
45508
  this.disposeRenderLists(renderer);
45213
45509
  }
45214
- API.Misc.stopCurrentlyLoadingAssets();
45510
+ API.Misc.stopCurrentlyLoadingAssets(loadingLabel);
45215
45511
  return this.results;
45216
45512
  }
45217
45513
  getScale() {
@@ -45240,7 +45536,7 @@ class ObjectDesc extends RenderDesc {
45240
45536
  }
45241
45537
  for (const result of this.results) {
45242
45538
  result.scale.set(...this.getScale().toVec3());
45243
- setTHREEMeshCF(result, resultCF);
45539
+ setTHREEObjectCF(result, resultCF);
45244
45540
  result.updateMatrix();
45245
45541
  result.updateMatrixWorld(true);
45246
45542
  if (this.skeletonDesc && this.instance) {
@@ -46914,7 +47210,7 @@ class AnimatorWrapper extends InstanceWrapper {
46914
47210
  }
46915
47211
  this.data.currentAnimation = name;
46916
47212
  let toPlayTrack = void 0;
46917
- if (!name.startsWith("emote.")) {
47213
+ if (!name.startsWith("emote.") && !name.startsWith("id.")) {
46918
47214
  const entries = this.data.animationSet[name];
46919
47215
  if (entries && entries.length > 0) {
46920
47216
  const entry = this._pickRandom(entries);
@@ -46922,12 +47218,15 @@ class AnimatorWrapper extends InstanceWrapper {
46922
47218
  toPlayTrack = this._getTrack(entry.id);
46923
47219
  }
46924
47220
  }
46925
- } else {
47221
+ } else if (name.startsWith("emote.")) {
46926
47222
  const emoteId = BigInt(name.split(".")[1]);
46927
47223
  const entry = this.data.emotes.get(emoteId);
46928
47224
  if (entry) {
46929
47225
  toPlayTrack = this._getTrack(entry.id);
46930
47226
  }
47227
+ } else {
47228
+ const animId = BigInt(name.split(".")[1]);
47229
+ toPlayTrack = this.data.animationTracks.get(animId);
46931
47230
  }
46932
47231
  if (toPlayTrack !== this.data.currentAnimationTrack) {
46933
47232
  if (toPlayTrack || name === "") {
@@ -47153,7 +47452,39 @@ class AnimatorWrapper extends InstanceWrapper {
47153
47452
  }
47154
47453
  }
47155
47454
  /**
47156
- * Loads a new animation
47455
+ * Loads an animation (not to be confused with an avatar animation like those found on the catalog)
47456
+ * @param id
47457
+ * @param forceLoop Forces animation track to loop
47458
+ * @returns AnimationTrack on success
47459
+ */
47460
+ async loadAnimation(id, forceLoop = false) {
47461
+ const humanoid = this.instance.parent;
47462
+ if (!humanoid) {
47463
+ throw new Error("Parent is missing from Animator");
47464
+ }
47465
+ const result = await API.Asset.GetRBX(`rbxassetid://${id}`, void 0);
47466
+ if (result instanceof RBX) {
47467
+ log(false, "loading anim", id);
47468
+ const animTrackInstance = result.generateTree().GetChildren()[0];
47469
+ if (animTrackInstance && humanoid.parent) {
47470
+ const animTrack = new AnimationTrack().loadAnimation(humanoid.parent, animTrackInstance);
47471
+ if (forceLoop) {
47472
+ animTrack.looped = true;
47473
+ }
47474
+ if (this.data.animationTracks.get(id)) {
47475
+ throw new Error("Animation was already loaded");
47476
+ }
47477
+ this.data.animationTracks.set(id, animTrack);
47478
+ this.instance.setProperty("_HasLoadedAnimation", true);
47479
+ return animTrack;
47480
+ }
47481
+ } else {
47482
+ return result;
47483
+ }
47484
+ return void 0;
47485
+ }
47486
+ /**
47487
+ * Loads a new avatar animation (catalog run animation or emote, not to be confused with a creator store animation)
47157
47488
  * @param id
47158
47489
  * @param isEmote
47159
47490
  * @param forceLoop Forces animation track to loop
@@ -47197,32 +47528,15 @@ class AnimatorWrapper extends InstanceWrapper {
47197
47528
  });
47198
47529
  } else {
47199
47530
  promises.push(new Promise((resolve) => {
47200
- API.Asset.GetRBX(`rbxassetid://${subAnimId}`, void 0).then((result) => {
47201
- if (result instanceof RBX) {
47202
- log(false, "loading anim", subAnimId);
47203
- const animTrackInstance = result.generateTree().GetChildren()[0];
47204
- if (animTrackInstance && humanoid.parent) {
47205
- const animTrack = new AnimationTrack().loadAnimation(humanoid.parent, animTrackInstance);
47206
- if (forceLoop) {
47207
- animTrack.looped = true;
47208
- }
47209
- if (!this.data.animationSet[animName]) {
47210
- this.data.animationSet[animName] = [];
47211
- }
47212
- this.data.animationSet[animName].push({
47213
- id: `rbxassetid://${subAnimId}`,
47214
- weight: subWeight
47215
- });
47216
- if (this.data.animationTracks.get(subAnimId)) {
47217
- throw new Error("Animation was already loaded");
47218
- }
47219
- this.data.animationTracks.set(subAnimId, animTrack);
47220
- this.instance.setProperty("_HasLoadedAnimation", true);
47221
- }
47222
- resolve(void 0);
47223
- } else {
47224
- resolve(result);
47531
+ this.loadAnimation(subAnimId, forceLoop).then((result) => {
47532
+ if (!this.data.animationSet[animName]) {
47533
+ this.data.animationSet[animName] = [];
47225
47534
  }
47535
+ this.data.animationSet[animName].push({
47536
+ id: `rbxassetid://${subAnimId}`,
47537
+ weight: subWeight
47538
+ });
47539
+ resolve(result instanceof Response ? result : void 0);
47226
47540
  });
47227
47541
  }));
47228
47542
  }
@@ -47244,25 +47558,12 @@ class AnimatorWrapper extends InstanceWrapper {
47244
47558
  });
47245
47559
  } else {
47246
47560
  promises.push(new Promise((resolve) => {
47247
- API.Asset.GetRBX(`rbxassetid://${animId}`, void 0).then((result) => {
47248
- if (result instanceof RBX) {
47249
- const animTrackInstance = result.generateTree().GetChildren()[0];
47250
- if (animTrackInstance && humanoid.parent) {
47251
- const animTrack = new AnimationTrack().loadAnimation(humanoid.parent, animTrackInstance);
47252
- if (forceLoop) {
47253
- animTrack.looped = true;
47254
- }
47255
- this.data.emotes.set(id, {
47256
- id: `rbxassetid://${animId}`,
47257
- weight: 1
47258
- });
47259
- this.data.animationTracks.set(animId, animTrack);
47260
- this.instance.setProperty("_HasLoadedAnimation", true);
47261
- }
47262
- resolve(void 0);
47263
- } else {
47264
- resolve(result);
47265
- }
47561
+ this.loadAnimation(animId, forceLoop).then((result) => {
47562
+ this.data.emotes.set(id, {
47563
+ id: `rbxassetid://${animId}`,
47564
+ weight: 1
47565
+ });
47566
+ resolve(result instanceof Response ? result : void 0);
47266
47567
  });
47267
47568
  }));
47268
47569
  }
@@ -47271,7 +47572,7 @@ class AnimatorWrapper extends InstanceWrapper {
47271
47572
  }
47272
47573
  /**
47273
47574
  * Switches to new animation
47274
- * @param name Animation name, such as "idle", "walk" or "emote.1234"
47575
+ * @param name Animation name, such as "idle", "walk", "emote.1234" or "id.1234"
47275
47576
  * @param type
47276
47577
  * @returns If animation sucessfully played
47277
47578
  */
@@ -48747,10 +49048,34 @@ class HumanoidDescriptionWrapper extends InstanceWrapper {
48747
49048
  }
48748
49049
  return void 0;
48749
49050
  }
49051
+ async _loadDefaultAnimation(animationProp, avatarType, animatorW, promises) {
49052
+ const animName = AnimationPropToName[animationProp];
49053
+ const animationSetEntries = avatarType === AvatarType.R15 ? animNamesR15[animName] : animNamesR6[animName];
49054
+ animatorW.data.animationSet[animName] = [];
49055
+ if (animationSetEntries) {
49056
+ for (const subAnim of animationSetEntries) {
49057
+ const subAnimId = BigInt(API.Misc.idFromStr(subAnim.id));
49058
+ if (!animatorW.data.animationTracks.has(subAnimId)) {
49059
+ promises.push(new Promise((resolve) => {
49060
+ animatorW.loadAnimation(subAnimId, true).then((result) => {
49061
+ if (!animatorW.data.animationSet[animName]) {
49062
+ animatorW.data.animationSet[animName] = [];
49063
+ }
49064
+ animatorW.data.animationSet[animName].push(subAnim);
49065
+ resolve(result instanceof Response ? result : void 0);
49066
+ });
49067
+ }));
49068
+ } else {
49069
+ animatorW.data.animationSet[animName].push(subAnim);
49070
+ }
49071
+ }
49072
+ } else {
49073
+ warn(false, `No default found for animation ${animName}`);
49074
+ }
49075
+ }
48750
49076
  /**
48751
49077
  * @returns undefined on success
48752
49078
  */
48753
- //TODO: CLEAN UP THIS CODE, the comments are not enough!
48754
49079
  async _applyAnimations(humanoid, toChange = AllAnimations) {
48755
49080
  const animator = humanoid.FindFirstChildOfClass("Animator");
48756
49081
  if (!animator) {
@@ -48760,7 +49085,7 @@ class HumanoidDescriptionWrapper extends InstanceWrapper {
48760
49085
  const animatorW = new AnimatorWrapper(animator);
48761
49086
  const promises = [];
48762
49087
  for (const animationProp of toChange) {
48763
- if (this.instance.HasProperty(animationProp) && this.instance.Prop(animationProp) > 0n && avatarType === AvatarType.R15) {
49088
+ if (this.instance.PropOrDefault(animationProp, 0n) > 0n && avatarType === AvatarType.R15) {
48764
49089
  const id = this.instance.Prop(animationProp);
48765
49090
  promises.push(new Promise((resolve) => {
48766
49091
  animatorW.loadAvatarAnimation(id, false, true).then((result) => {
@@ -48768,35 +49093,7 @@ class HumanoidDescriptionWrapper extends InstanceWrapper {
48768
49093
  });
48769
49094
  }));
48770
49095
  } else {
48771
- const animName = AnimationPropToName[animationProp];
48772
- const animationSetEntries = avatarType === AvatarType.R15 ? animNamesR15[animName] : animNamesR6[animName];
48773
- animatorW.data.animationSet[animName] = [];
48774
- if (animationSetEntries) {
48775
- for (const subAnim of animationSetEntries) {
48776
- promises.push(new Promise((resolve) => {
48777
- API.Asset.GetRBX(subAnim.id, void 0).then((result) => {
48778
- if (result instanceof RBX) {
48779
- const animTrackInstance = result.generateTree().GetChildren()[0];
48780
- if (animTrackInstance && humanoid.parent) {
48781
- const animTrack = new AnimationTrack().loadAnimation(humanoid.parent, animTrackInstance);
48782
- animTrack.looped = true;
48783
- animatorW.data.animationTracks.set(BigInt(API.Misc.idFromStr(subAnim.id)), animTrack);
48784
- if (!animatorW.data.animationSet[animName]) {
48785
- animatorW.data.animationSet[animName] = [];
48786
- }
48787
- animatorW.data.animationSet[animName].push(subAnim);
48788
- animatorW.instance.setProperty("_HasLoadedAnimation", true);
48789
- }
48790
- resolve(void 0);
48791
- } else {
48792
- resolve(result);
48793
- }
48794
- });
48795
- }));
48796
- }
48797
- } else {
48798
- warn(false, `No default found for animation ${animName}`);
48799
- }
49096
+ this._loadDefaultAnimation(animationProp, avatarType, animatorW, promises);
48800
49097
  }
48801
49098
  }
48802
49099
  const values = await Promise.all(promises);
@@ -49067,15 +49364,21 @@ class SoundWrapperData {
49067
49364
  }
49068
49365
  class SoundWrapper extends InstanceWrapper {
49069
49366
  static className = "Sound";
49070
- static requiredProperties = ["Name", "_data"];
49367
+ static requiredProperties = ["Name", "Looped", "Playing", "Volume", "_data"];
49071
49368
  setup() {
49072
49369
  if (!this.instance.HasProperty("Name")) this.instance.addProperty(new Property("Name", DataType.String), this.instance.className);
49370
+ if (!this.instance.HasProperty("Looped")) this.instance.addProperty(new Property("Looped", DataType.Bool), false);
49371
+ if (!this.instance.HasProperty("Playing")) this.instance.addProperty(new Property("Playing", DataType.Bool), false);
49372
+ if (!this.instance.HasProperty("Volume")) this.instance.addProperty(new Property("Volume", DataType.Float32), false);
49073
49373
  if (!this.instance.HasProperty("_data")) this.instance.addProperty(new Property("_data", DataType.NonSerializable), new SoundWrapperData());
49074
49374
  }
49075
49375
  get data() {
49076
49376
  return this.instance.Prop("_data");
49077
49377
  }
49078
49378
  created() {
49379
+ if (this.instance.Prop("Playing")) {
49380
+ this.Play();
49381
+ }
49079
49382
  this.instance.Destroying.Connect(() => {
49080
49383
  if (this.data.playingSource) {
49081
49384
  this.Stop();
@@ -49093,6 +49396,9 @@ class SoundWrapper extends InstanceWrapper {
49093
49396
  }
49094
49397
  }
49095
49398
  }
49399
+ setPlaying(value) {
49400
+ this.instance.setProperty("Playing", value);
49401
+ }
49096
49402
  playSource() {
49097
49403
  if (!this.data.audioContext || !this.data.gainNode || !this.data.buffer) return;
49098
49404
  this.data.playingSource = this.data.audioContext.createBufferSource();
@@ -49101,10 +49407,18 @@ class SoundWrapper extends InstanceWrapper {
49101
49407
  this.data.gainNode.connect(this.data.audioContext.destination);
49102
49408
  this._updateVolume();
49103
49409
  this.data.playingSource.start(0);
49410
+ this.data.playingSource.onended = (() => {
49411
+ if (this.instance.Prop("Looped")) {
49412
+ this.Play();
49413
+ } else {
49414
+ this.Stop();
49415
+ }
49416
+ });
49104
49417
  }
49105
49418
  Play() {
49106
49419
  if (!FLAGS.AUDIO_ENABLED) return;
49107
49420
  this.Stop();
49421
+ this.setPlaying(true);
49108
49422
  if (!this.data.audioContext) {
49109
49423
  this.data.audioContext = new AudioContext();
49110
49424
  }
@@ -49134,6 +49448,7 @@ class SoundWrapper extends InstanceWrapper {
49134
49448
  }
49135
49449
  }
49136
49450
  Stop() {
49451
+ this.setPlaying(false);
49137
49452
  if (this.data.playingSource) {
49138
49453
  this.data.playingSource.stop();
49139
49454
  this.data.playingSource = void 0;
@@ -49274,6 +49589,142 @@ function RegisterWrappers() {
49274
49589
  BodyColorsWrapper.register();
49275
49590
  AccessoryWrapper.register();
49276
49591
  }
49592
+ function disposeLight(scene, light) {
49593
+ if (light.shadow && light.shadow.map) {
49594
+ light.shadow.map.dispose();
49595
+ }
49596
+ scene.remove(light);
49597
+ }
49598
+ class LightDesc extends RenderDesc {
49599
+ enabled = true;
49600
+ cframe = new CFrame();
49601
+ shadows = false;
49602
+ color = new Color3(1, 1, 1);
49603
+ brightness = 1;
49604
+ range = 8;
49605
+ lightType = "point";
49606
+ //spot and face only
49607
+ angle = 90;
49608
+ face = NormalId.Front;
49609
+ isSame(other) {
49610
+ return this.enabled === other.enabled && this.shadows === other.shadows && this.color.isSame(other.color) && this.brightness === other.brightness && this.range === other.range && this.lightType === other.lightType && this.angle === other.angle && this.face === other.face && this.cframe.isSame(other.cframe);
49611
+ }
49612
+ needsRegeneration(other) {
49613
+ return this.lightType !== other.lightType;
49614
+ }
49615
+ virtualFromRenderDesc(other) {
49616
+ this.enabled = other.enabled;
49617
+ this.shadows = other.shadows;
49618
+ this.color = other.color.clone();
49619
+ this.brightness = other.brightness;
49620
+ this.range = other.range;
49621
+ this.angle = other.angle;
49622
+ this.face = other.face;
49623
+ this.cframe = other.cframe.clone();
49624
+ }
49625
+ fromInstance(child) {
49626
+ switch (child.className) {
49627
+ case "PointLight":
49628
+ this.lightType = "point";
49629
+ break;
49630
+ case "SpotLight":
49631
+ this.lightType = "spot";
49632
+ break;
49633
+ case "SurfaceLight":
49634
+ this.lightType = "surface";
49635
+ break;
49636
+ }
49637
+ if (child.parent) {
49638
+ if (child.parent.className === "Attachment") {
49639
+ const attachmentW = new AttachmentWrapper(child.parent);
49640
+ this.cframe = attachmentW.getWorldCFrame();
49641
+ } else {
49642
+ this.cframe = child.parent.PropOrDefault("CFrame", this.cframe).clone();
49643
+ }
49644
+ }
49645
+ this.enabled = child.PropOrDefault("Enabled", this.enabled);
49646
+ this.color = child.PropOrDefault("Color", this.color);
49647
+ this.brightness = child.PropOrDefault("Brightness", this.brightness);
49648
+ this.range = child.PropOrDefault("Range", this.range);
49649
+ this.angle = child.PropOrDefault("Angle", this.angle);
49650
+ this.face = child.PropOrDefault("Face", this.face);
49651
+ }
49652
+ async compileResults(_renderer, scene) {
49653
+ if (this.results) {
49654
+ for (const light of this.results) {
49655
+ disposeLight(scene, light);
49656
+ }
49657
+ }
49658
+ this.results = [];
49659
+ switch (this.lightType) {
49660
+ case "point": {
49661
+ const pointLight = new PointLight();
49662
+ this.results.push(
49663
+ pointLight
49664
+ /*, pointLightHelper*/
49665
+ );
49666
+ break;
49667
+ }
49668
+ case "spot":
49669
+ case "surface": {
49670
+ const spotLight = new SpotLight();
49671
+ spotLight.add(spotLight.target);
49672
+ this.results.push();
49673
+ break;
49674
+ }
49675
+ }
49676
+ this.updateResults();
49677
+ return this.results;
49678
+ }
49679
+ updateResults() {
49680
+ if (!this.results) return;
49681
+ for (const light of this.results) {
49682
+ if (light instanceof PointLight || light instanceof SpotLight) {
49683
+ light.decay = 0.4;
49684
+ light.visible = this.enabled;
49685
+ light.intensity = this.brightness;
49686
+ light.distance = this.range + 0.5;
49687
+ light.castShadow = this.shadows;
49688
+ light.shadow.intensity = 0.5;
49689
+ light.color = new Color().setRGB(this.color.R, this.color.G, this.color.B);
49690
+ const resultCF = this.cframe;
49691
+ const targetCF = new CFrame();
49692
+ if (light instanceof SpotLight) {
49693
+ light.angle = rad(this.angle);
49694
+ switch (this.face) {
49695
+ case NormalId.Front:
49696
+ targetCF.Position = [0, 0, -1];
49697
+ break;
49698
+ case NormalId.Back:
49699
+ targetCF.Position = [0, 0, 1];
49700
+ break;
49701
+ case NormalId.Right:
49702
+ targetCF.Position = [1, 0, 0];
49703
+ break;
49704
+ case NormalId.Left:
49705
+ targetCF.Position = [-1, 0, 0];
49706
+ break;
49707
+ case NormalId.Top:
49708
+ targetCF.Position = [0, 1, 0];
49709
+ break;
49710
+ case NormalId.Bottom:
49711
+ targetCF.Position = [0, -1, 0];
49712
+ break;
49713
+ }
49714
+ light.target.position.set(...targetCF.Position);
49715
+ }
49716
+ setTHREEObjectCF(light, resultCF);
49717
+ }
49718
+ }
49719
+ }
49720
+ dispose(_renderer, scene) {
49721
+ if (this.results) {
49722
+ for (const result of this.results) {
49723
+ disposeLight(scene, result);
49724
+ }
49725
+ }
49726
+ }
49727
+ }
49277
49728
  function disposeMesh(scene, mesh) {
49278
49729
  if (mesh.material) {
49279
49730
  const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
@@ -49635,8 +50086,7 @@ class RBXRenderer {
49635
50086
  RBXRenderer.backgroundColorHex = backgroundColorHex;
49636
50087
  const backgroundColor = new Color(backgroundColorHex);
49637
50088
  renderScene.scene.background = backgroundColor;
49638
- let thumbnailAmbientVal = 138;
49639
- thumbnailAmbientVal = 128;
50089
+ const thumbnailAmbientVal = 128;
49640
50090
  let ambientLightColor = void 0;
49641
50091
  if (lightingType === "Thumbnail") {
49642
50092
  ambientLightColor = new Color(thumbnailAmbientVal / 255, thumbnailAmbientVal / 255, thumbnailAmbientVal / 255);
@@ -49647,7 +50097,7 @@ class RBXRenderer {
49647
50097
  renderScene.scene.add(ambientLight);
49648
50098
  renderScene.ambientLight = ambientLight;
49649
50099
  let directionalLightColor = void 0;
49650
- const directionalLightVal = 0.7 * 0.9 * 2 * 0.4;
50100
+ const directionalLightVal = Math.PI;
49651
50101
  if (lightingType === "Thumbnail") {
49652
50102
  directionalLightColor = new Color(directionalLightVal, directionalLightVal, directionalLightVal);
49653
50103
  } else if (lightingType === "WellLit") {
@@ -49687,7 +50137,7 @@ class RBXRenderer {
49687
50137
  renderScene.scene.add(directionalLight2);
49688
50138
  renderScene.directionalLight2 = directionalLight2;
49689
50139
  } else if (lightingType === "Thumbnail") {
49690
- const directionalLight2 = new DirectionalLight(directionalLightColor, directionalLightIntensity * 0.5);
50140
+ const directionalLight2 = new DirectionalLight(directionalLightColor, directionalLightVal * 0.1);
49691
50141
  directionalLight2.position.set(-0.47489210963249207 * -10, 0.8225368857383728 * -10, 0.3129066228866577 * -10);
49692
50142
  directionalLight2.target.position.set(0, 0, 0);
49693
50143
  renderScene.scene.add(directionalLight2);
@@ -49919,6 +50369,8 @@ class RBXRenderer {
49919
50369
  RBXRenderer._addRenderDesc(instance, auth, ObjectDesc, renderScene);
49920
50370
  } else if (EmitterGroupDescClassTypes.includes(instance.className)) {
49921
50371
  RBXRenderer._addRenderDesc(instance, auth, EmitterGroupDesc, renderScene);
50372
+ } else if (LightDescClassTypes.includes(instance.className)) {
50373
+ RBXRenderer._addRenderDesc(instance, auth, LightDesc, renderScene);
49922
50374
  }
49923
50375
  for (const child of instance.GetChildren()) {
49924
50376
  RBXRenderer.addInstance(child, auth, renderScene);
@@ -50173,8 +50625,11 @@ function getExtents(cframe, parts) {
50173
50625
  function getExtentsCenter(extents) {
50174
50626
  return extents[1].minus(extents[0]).divide(new Vector32(2, 2, 2)).add(extents[0]);
50175
50627
  }
50176
- function zoomExtents(cameraCFrame, modelCFrame, modelSize, targetFOV, distanceScale) {
50177
- const largestSize = Math.max(modelSize.X, modelSize.Y, modelSize.Z);
50628
+ function zoomExtents(cameraCFrame, modelCFrame, modelSize, targetFOV, distanceScale, sizeType = "calculate") {
50629
+ let largestSize = Math.max(modelSize.X, modelSize.Y, modelSize.Z);
50630
+ if (sizeType === "calculate") {
50631
+ largestSize = modelSize.magnitude() / 2 / Math.sin(rad(targetFOV / 2));
50632
+ }
50178
50633
  const fovMultiplier = 70 / targetFOV;
50179
50634
  const lookDir = multiply(normalize(minus(cameraCFrame.Position, modelCFrame.Position)), [distanceScale, distanceScale, distanceScale]);
50180
50635
  cameraCFrame.Position = add(modelCFrame.Position, multiply(multiply(lookDir, [largestSize, largestSize, largestSize]), [fovMultiplier, fovMultiplier, fovMultiplier]));
@@ -50243,7 +50698,7 @@ function getCameraCFrameForHeadshotCustomized(rig, fov2, yRot, distance2) {
50243
50698
  lookCF.Position = add(headCenterCF.Position, multiply(multiply([10, 10, 10], lookVector), [fovMultiplier, fovMultiplier, fovMultiplier]));
50244
50699
  lookCF = CFrame.lookAt(lookCF.Position, headCenterCF.Position);
50245
50700
  const cameraCF = lookCF.clone();
50246
- zoomExtents(cameraCF, headCenterCF, headLocalExtents[1].minus(headLocalExtents[0]), fov2, distance2);
50701
+ zoomExtents(cameraCF, headCenterCF, headLocalExtents[1].minus(headLocalExtents[0]), fov2, distance2, "largestAxis");
50247
50702
  return cameraCF;
50248
50703
  }
50249
50704
  function getCameraCFrameForAvatarNonCustomized(rig) {
@@ -50274,6 +50729,36 @@ function getCameraCFrameForAvatarNonCustomized(rig) {
50274
50729
  zoomExtents(cameraCF, rootPartCF, extentsSize, 70, 1);
50275
50730
  return cameraCF;
50276
50731
  }
50732
+ function getThumbnailCameraCFrame(model) {
50733
+ const thumbnailCamera = model.FindFirstChildOfClass("Camera");
50734
+ if (thumbnailCamera) return thumbnailCamera.PropOrDefault("CFrame", new CFrame());
50735
+ let rootPart = model.PropOrDefault("PrimaryPart", void 0);
50736
+ if (!rootPart) rootPart = model.FindFirstChildOfClass("Part");
50737
+ if (!rootPart) rootPart = model.FindFirstChildOfClass("MeshPart");
50738
+ if (!rootPart) return;
50739
+ const rootPartCF = rootPart.PropOrDefault("CFrame", new CFrame()).clone();
50740
+ const worldExtents = getRigExtentsWorld(model);
50741
+ if (!worldExtents) return;
50742
+ const extentsSize = worldExtents[1].minus(worldExtents[0]);
50743
+ rootPartCF.Position = getExtentsCenter(worldExtents).toVec3();
50744
+ let lookVector = rootPartCF.lookVector();
50745
+ if (Math.abs(lookVector[1]) > 0.95) {
50746
+ lookVector = [0, 0, -1];
50747
+ } else {
50748
+ lookVector[1] = 0;
50749
+ lookVector = normalize(lookVector);
50750
+ }
50751
+ let lookCF = CFrame.lookAt([0, 0, 0], lookVector);
50752
+ lookCF = lookCF.multiply(CFrame.fromEulerAngles(0, 0, rad(45)));
50753
+ lookCF = lookCF.multiply(CFrame.fromEulerAngles(rad(35), 0, 0));
50754
+ lookCF = lookCF.multiply(CFrame.fromEulerAngles(0, 0, 0));
50755
+ lookVector = lookCF.lookVector();
50756
+ lookCF.Position = add(rootPartCF.Position, multiply([10, 10, 10], lookVector));
50757
+ lookCF = CFrame.lookAt(lookCF.Position, rootPartCF.Position);
50758
+ const cameraCF = lookCF.clone();
50759
+ zoomExtents(cameraCF, rootPartCF, extentsSize, 70, 1);
50760
+ return cameraCF;
50761
+ }
50277
50762
  class OutfitRenderer {
50278
50763
  auth;
50279
50764
  outfit;
@@ -50475,7 +50960,8 @@ function renderToRenderTarget(width, height, renderScene) {
50475
50960
  generateMipmaps: false,
50476
50961
  minFilter: LinearFilter,
50477
50962
  magFilter: LinearFilter,
50478
- type: UnsignedByteType
50963
+ type: UnsignedByteType,
50964
+ samples: 4
50479
50965
  });
50480
50966
  const rbxRenderer = RBXRenderer.getRenderer();
50481
50967
  if (!rbxRenderer) return renderTarget;
@@ -50494,12 +50980,12 @@ async function renderTargetToCanvas(renderTarget) {
50494
50980
  }
50495
50981
  async function generateModelThumbnail(auth, renderScene, model, size = [150, 150], type = "png", quality = 1, gltfAutoDownload = false) {
50496
50982
  return new Promise((resolve) => {
50497
- const cameraCFrame = getCameraCFrameForAvatarNonCustomized(model);
50983
+ const cameraCFrame = getThumbnailCameraCFrame(model);
50498
50984
  if (cameraCFrame) {
50499
50985
  RBXRenderer.setCameraCFrame(cameraCFrame, renderScene);
50500
50986
  }
50501
50987
  RBXRenderer.addInstance(model, auth, renderScene);
50502
- let exportTimeout = setTimeout(doExport, FLAGS.THUMBNAIL_TIMEOUT);
50988
+ let exportTimeout = !API.Misc.getCurrentlyLoading() ? setTimeout(doExport, FLAGS.THUMBNAIL_TIMEOUT) : void 0;
50503
50989
  const onLoadingConnection = API.Events.OnLoadingAssets.Connect((currentlyLoading) => {
50504
50990
  if (exportTimeout) {
50505
50991
  clearTimeout(exportTimeout);
@@ -50531,12 +51017,20 @@ async function generateModelThumbnail(auth, renderScene, model, size = [150, 150
50531
51017
  }
50532
51018
  async function generateOutfitThumbnail(auth, outfit, size = [150, 150], type = "png", quality = 1, gltfAutoDownload = false) {
50533
51019
  return new Promise((resolve) => {
51020
+ const startTime = performance.now();
50534
51021
  const renderScene = RBXRenderer.addScene();
50535
51022
  setupThumbnailScene(renderScene);
50536
51023
  const outfitRenderer = new OutfitRenderer(auth, outfit, renderScene);
50537
51024
  if (outfit.playerAvatarType === AvatarType.R6) outfitRenderer.deltaTimeMultiplier = 0;
50538
51025
  outfitRenderer.startAnimating();
50539
- let exportTimeout = setTimeout(doExport, FLAGS.THUMBNAIL_TIMEOUT);
51026
+ if (!outfit.containsAssetType("Gear")) {
51027
+ if (outfit.playerAvatarType === AvatarType.R15) {
51028
+ outfitRenderer.setMainAnimation("pose");
51029
+ }
51030
+ } else {
51031
+ outfitRenderer.setMainAnimation("toolnone");
51032
+ }
51033
+ let exportTimeout = !API.Misc.getCurrentlyLoading() ? setTimeout(doExport, FLAGS.THUMBNAIL_TIMEOUT) : void 0;
50540
51034
  const onLoadingConnection = API.Events.OnLoadingAssets.Connect((currentlyLoading) => {
50541
51035
  if (exportTimeout) {
50542
51036
  clearTimeout(exportTimeout);
@@ -50548,15 +51042,9 @@ async function generateOutfitThumbnail(auth, outfit, size = [150, 150], type = "
50548
51042
  });
50549
51043
  async function doExport() {
50550
51044
  onLoadingConnection.Disconnect();
50551
- if (!outfit.containsAssetType("Gear")) {
50552
- if (outfit.playerAvatarType === AvatarType.R15) {
50553
- outfitRenderer.setMainAnimation("pose");
50554
- }
50555
- } else {
50556
- outfitRenderer.setMainAnimation("toolnone");
50557
- }
50558
51045
  if (outfitRenderer.currentRig) {
50559
51046
  const thumbnailResult = await generateModelThumbnail(auth, renderScene, outfitRenderer.currentRig, size, type, quality, gltfAutoDownload);
51047
+ console.log("Generated outfit thumbnail after seconds:", (performance.now() - startTime) / 1e3);
50560
51048
  resolve(thumbnailResult);
50561
51049
  outfitRenderer.stopAnimating();
50562
51050
  if (outfitRenderer.currentRig) outfitRenderer.currentRig.Destroy();
@@ -50570,7 +51058,7 @@ function setupThumbnailScene(renderScene) {
50570
51058
  renderScene.shouldAnimate = false;
50571
51059
  renderScene.wellLitDirectionalLightIntensity *= 2;
50572
51060
  renderScene.shadowEnabled = false;
50573
- RBXRenderer.setupScene("WellLit", 16777215, renderScene);
51061
+ RBXRenderer.setupScene("Thumbnail", 16777215, renderScene);
50574
51062
  if (renderScene.plane) renderScene.scene.remove(renderScene.plane);
50575
51063
  if (renderScene.shadowPlane) renderScene.scene.remove(renderScene.shadowPlane);
50576
51064
  renderScene.scene.background = null;
@@ -50590,6 +51078,7 @@ function exposeThumbnailGenerator() {
50590
51078
  globalThis.generateOutfitThumbnail = generateOutfitThumbnail;
50591
51079
  globalThis.generateModelThumbnail = generateModelThumbnail;
50592
51080
  globalThis.setupThumbnailScene = setupThumbnailScene;
51081
+ globalThis.getThumbnailCameraCFrame = getThumbnailCameraCFrame;
50593
51082
  }
50594
51083
  export {
50595
51084
  API,
@@ -50635,6 +51124,7 @@ export {
50635
51124
  BundleTypes,
50636
51125
  CACHE,
50637
51126
  CFrame,
51127
+ COREMESH,
50638
51128
  CatalogBundleTypes,
50639
51129
  CategoryDictionary,
50640
51130
  Color3,
@@ -50652,10 +51142,12 @@ export {
50652
51142
  DefaultSearchData,
50653
51143
  EmitterGroupDescClassTypes,
50654
51144
  Event,
51145
+ FACS,
50655
51146
  FLAGS,
50656
51147
  FaceControlNames,
50657
51148
  FaceControlsWrapper,
50658
51149
  FileMesh,
51150
+ FileMeshBone,
50659
51151
  FileMeshSkinning,
50660
51152
  FileMeshSubset,
50661
51153
  FindFirstMatchingAttachment,
@@ -50663,14 +51155,17 @@ export {
50663
51155
  GetAttachedPart,
50664
51156
  GetWrapperForInstance,
50665
51157
  HSR,
51158
+ HSRAVIS,
50666
51159
  HumanoidDescriptionWrapper,
50667
51160
  HumanoidRigType,
50668
51161
  Instance,
50669
51162
  InstanceWrapper,
50670
51163
  ItemInfo,
50671
51164
  ItemSort,
51165
+ LODS,
50672
51166
  LayeredAssetTypes,
50673
51167
  LayeredClothingAssetOrder,
51168
+ LightDescClassTypes,
50674
51169
  LocalOutfit,
50675
51170
  MakeupAssetTypes,
50676
51171
  MakeupDescriptionWrapper,
@@ -50705,6 +51200,7 @@ export {
50705
51200
  RoAvatarData,
50706
51201
  RoAvatarDataError,
50707
51202
  RoAvatarVersions,
51203
+ SKINNING,
50708
51204
  Scale,
50709
51205
  ScaleAccessory,
50710
51206
  ScaleAccessoryForRig,
@@ -50736,6 +51232,8 @@ export {
50736
51232
  animNamesR6,
50737
51233
  arrayBufferToBase64,
50738
51234
  averageVec3,
51235
+ awaitTimeout,
51236
+ awaitTimeoutThrows,
50739
51237
  barycentric,
50740
51238
  base64ToArrayBuffer,
50741
51239
  browserOpenURL,
@@ -50775,7 +51273,6 @@ export {
50775
51273
  generateUUIDv4,
50776
51274
  getCameraCFrameForAvatarNonCustomized,
50777
51275
  getCameraCFrameForHeadshotCustomized,
50778
- getCameraOffset,
50779
51276
  getDistIndexArray,
50780
51277
  getExtents,
50781
51278
  getExtentsCenter,
@@ -50787,6 +51284,7 @@ export {
50787
51284
  getOriginalSize,
50788
51285
  getRandomBetweenInclusive,
50789
51286
  getRigExtentsWorld,
51287
+ getThumbnailCameraCFrame,
50790
51288
  getUVtoIndexMap,
50791
51289
  getUVtoIndicesMap,
50792
51290
  getWorkerOnMessage,