roavatar-renderer 1.4.5 → 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,
@@ -30308,6 +30510,9 @@ class Instance {
30308
30510
  const hex = BrickColors[this.Prop("BrickColor")];
30309
30511
  const rgb = hexToRgb(hex);
30310
30512
  const color3uint8 = new Color3uint8(rgb?.r, rgb?.g, rgb?.b);
30513
+ color3uint8.R *= 255;
30514
+ color3uint8.G *= 255;
30515
+ color3uint8.B *= 255;
30311
30516
  return color3uint8;
30312
30517
  }
30313
30518
  break;
@@ -31803,6 +32008,22 @@ function snapToNumber(value, numbers) {
31803
32008
  }
31804
32009
  return closestNumber;
31805
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
+ }
31806
32027
  function getXMLProperty(doc, propertyName) {
31807
32028
  const propertyNode = doc.querySelector('[name="' + propertyName + '"]');
31808
32029
  if (!propertyNode) {
@@ -35289,14 +35510,15 @@ async function RBLXPatch(url, auth, body, attempt = 0) {
35289
35510
  return RBLXPost(url, auth, body, attempt, "PATCH");
35290
35511
  }
35291
35512
  async function getAssetBufferInternal(url, headers, extraStr) {
35292
- API.Misc.startCurrentlyLoadingAssets();
35513
+ const loadingLabel = `getAssetBufferInternal-${url}-${extraStr}`;
35514
+ API.Misc.startCurrentlyLoadingAssets(loadingLabel);
35293
35515
  const fetchStr = await API.Misc.assetURLToCDNURL(url, headers, extraStr);
35294
35516
  if (fetchStr instanceof Response) {
35295
- API.Misc.stopCurrentlyLoadingAssets();
35517
+ API.Misc.stopCurrentlyLoadingAssets(loadingLabel);
35296
35518
  return fetchStr;
35297
35519
  }
35298
35520
  const response = await RBLXGet(fetchStr, void 0, false);
35299
- API.Misc.stopCurrentlyLoadingAssets();
35521
+ API.Misc.stopCurrentlyLoadingAssets(loadingLabel);
35300
35522
  if (response.status === 200) {
35301
35523
  const data = await response.arrayBuffer();
35302
35524
  return data;
@@ -35304,14 +35526,10 @@ async function getAssetBufferInternal(url, headers, extraStr) {
35304
35526
  return response;
35305
35527
  }
35306
35528
  }
35307
- let isCurrentlyLoading = false;
35308
- let currentlyLoadingAssets = 0;
35309
- function _updateCurrentlyLoadingAssets() {
35310
- const newCurrentlyLoading = currentlyLoadingAssets > 0;
35311
- if (isCurrentlyLoading !== newCurrentlyLoading) {
35312
- API.Events.OnLoadingAssets.Fire(newCurrentlyLoading);
35313
- }
35314
- isCurrentlyLoading = newCurrentlyLoading;
35529
+ const currentlyLoadingAssets = [];
35530
+ function _updateCurrentlyLoadingAssets(type, label) {
35531
+ const newCurrentlyLoading = currentlyLoadingAssets.length > 0;
35532
+ API.Events.OnLoadingAssets.Fire(newCurrentlyLoading, type, label);
35315
35533
  }
35316
35534
  const CACHE = {
35317
35535
  "AssetBuffer": /* @__PURE__ */ new Map(),
@@ -35425,18 +35643,25 @@ function createContentMap() {
35425
35643
  ContentMap.set("roavatar://RigR6.rbxm", FLAGS.RIG_PATH + "RigR6.rbxm");
35426
35644
  ContentMap.set("roavatar://RigR15.rbxm", FLAGS.RIG_PATH + "RigR15.rbxm");
35427
35645
  }
35646
+ ContentMap.set("roavatar://AvatarEditorScene.rbxm", "74148511291027");
35647
+ ContentMap.set("roavatar://AvatarSceneNew.rbxm", "130507237273896");
35428
35648
  }
35429
35649
  let CachedRoAvatarData = void 0;
35430
35650
  let ThumbnailsToBatch = [];
35431
35651
  const API = {
35432
35652
  "Misc": {
35433
- "startCurrentlyLoadingAssets": function() {
35434
- currentlyLoadingAssets += 1;
35435
- _updateCurrentlyLoadingAssets();
35653
+ "startCurrentlyLoadingAssets": function(label) {
35654
+ currentlyLoadingAssets.push(label);
35655
+ _updateCurrentlyLoadingAssets("start", label);
35436
35656
  },
35437
- "stopCurrentlyLoadingAssets": function() {
35438
- currentlyLoadingAssets -= 1;
35439
- _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
+ }
35440
35665
  },
35441
35666
  "idFromStr": function(str) {
35442
35667
  const numStrs = str.match(/\d+(\.\d+)?/g) || [];
@@ -35466,7 +35691,7 @@ const API = {
35466
35691
  } else if (str.startsWith(".")) {
35467
35692
  url = str;
35468
35693
  } else {
35469
- warn(false, `Failed to parse path of ${str}`);
35694
+ warn(false, `Failed to parse path of ${str}, leaving as is`);
35470
35695
  }
35471
35696
  if (FLAGS.ASSETDELIVERY_V2) {
35472
35697
  if (url.includes("/v1/")) {
@@ -35500,7 +35725,10 @@ const API = {
35500
35725
  return cdnURL;
35501
35726
  },
35502
35727
  "getCurrentlyLoading": function() {
35503
- return currentlyLoadingAssets > 0;
35728
+ return currentlyLoadingAssets.length > 0;
35729
+ },
35730
+ "getCurrentlyLoadingLabels": function() {
35731
+ return currentlyLoadingAssets;
35504
35732
  }
35505
35733
  },
35506
35734
  "Events": {
@@ -35518,6 +35746,7 @@ const API = {
35518
35746
  API.Misc.assetURLToCDNURL(url).then((fetchStr) => {
35519
35747
  if (fetchStr instanceof Response) {
35520
35748
  resolve(void 0);
35749
+ cacheResolve(void 0);
35521
35750
  return;
35522
35751
  }
35523
35752
  const image = new Image();
@@ -35527,7 +35756,7 @@ const API = {
35527
35756
  CACHE.Image.set(cacheURL, image);
35528
35757
  };
35529
35758
  image.onerror = () => {
35530
- cacheResolve(image);
35759
+ cacheResolve(void 0);
35531
35760
  resolve(void 0);
35532
35761
  CACHE.Image.set(cacheURL, void 0);
35533
35762
  };
@@ -36451,6 +36680,7 @@ class RBFDeformerPatch {
36451
36680
  epsilon = 1e-6;
36452
36681
  // avoid matrix from being singular
36453
36682
  affectBones = true;
36683
+ hasSolved = false;
36454
36684
  id = rbfDeformerIdCount++;
36455
36685
  /**
36456
36686
  * Creates the deformer and prepares for deformation
@@ -36516,31 +36746,37 @@ class RBFDeformerPatch {
36516
36746
  * @returns void
36517
36747
  */
36518
36748
  async solveAsync() {
36749
+ this.hasSolved = true;
36519
36750
  if (this.refVerts.length === 0) {
36520
36751
  return;
36521
36752
  }
36522
- API.Misc.startCurrentlyLoadingAssets();
36523
- const [neighborIndicesBuf, weightsBuf, nearestPatchBuf] = await WorkerPool.instance.work(
36524
- "RBFDeformerSolveAsync",
36525
- [this.patchCount, this.K, this.epsilon, this.importantIndices.buffer, this.refVerts.buffer, this.distVerts.buffer, this.meshVerts.buffer, this.meshBones.buffer],
36526
- [
36527
- this.importantIndices.buffer,
36528
- /*this.refVerts.buffer,*/
36529
- this.distVerts.buffer,
36530
- this.meshVerts.buffer,
36531
- this.meshBones.buffer
36532
- ]
36533
- );
36534
- time(`RBFDeformerPatch.solveAsync.unpack.${this.id}`);
36535
- this.neighborIndices = neighborIndicesBuf.map((a) => {
36536
- return new Uint16Array(a);
36537
- });
36538
- this.weights = weightsBuf.map((a) => {
36539
- return new Float32Array(a);
36540
- });
36541
- this.nearestPatch = new Uint16Array(nearestPatchBuf);
36542
- timeEnd(`RBFDeformerPatch.solveAsync.unpack.${this.id}`);
36543
- API.Misc.stopCurrentlyLoadingAssets();
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);
36544
36780
  }
36545
36781
  /**
36546
36782
  * solveAsync() needs to be called before this
@@ -36549,11 +36785,15 @@ class RBFDeformerPatch {
36549
36785
  * @returns New position after deformation
36550
36786
  */
36551
36787
  deform(i) {
36552
- if (!this.nearestPatch || !this.neighborIndices || !this.weights) {
36553
- throw new Error("RBF has not been solved");
36554
- }
36555
36788
  const vertLen = this.mesh.coreMesh.numverts;
36556
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
+ }
36557
36797
  const idx = this.nearestPatch[i];
36558
36798
  const neighborIndices = this.neighborIndices[idx];
36559
36799
  const weights = this.weights[idx];
@@ -42267,6 +42507,50 @@ function getModelHSRDesc(model) {
42267
42507
  return newHSRDesc;
42268
42508
  }
42269
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
+ }
42270
42554
  const HSR_CACHE = /* @__PURE__ */ new Map();
42271
42555
  function doHSR(totalUvToHits, targetCage, mesh, moveVerts = true, cacheStr) {
42272
42556
  let closestVertIndexArr = void 0;
@@ -42726,12 +43010,16 @@ class MeshDesc {
42726
43010
  }
42727
43011
  }
42728
43012
  async compileMesh() {
42729
- if (!this.mesh) return;
43013
+ let mesh = void 0;
42730
43014
  const meshToLoad = this.mesh;
42731
- const mesh = await API.Asset.GetMesh(meshToLoad, void 0);
42732
- if (mesh instanceof Response) {
42733
- warn(true, "Failed to get mesh for compileMesh", mesh);
42734
- 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);
42735
43023
  }
42736
43024
  if (!mesh.facs && this.headMesh && mesh.skinning.skinnings.length > 0) {
42737
43025
  const headMesh = await API.Asset.GetMesh(this.headMesh, void 0, true);
@@ -43364,6 +43652,7 @@ class TextureLayer {
43364
43652
  class MaterialDesc {
43365
43653
  layers = [];
43366
43654
  isDecal = false;
43655
+ canHaveMipmaps = true;
43367
43656
  transparent = false;
43368
43657
  transparency = 0;
43369
43658
  doubleSided = false;
@@ -43378,7 +43667,7 @@ class MaterialDesc {
43378
43667
  createdTextures = [];
43379
43668
  isSame(other) {
43380
43669
  if (this.dirty || other.dirty) return false;
43381
- 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;
43382
43671
  let layersSame = true;
43383
43672
  if (this.layers.length !== other.layers.length) {
43384
43673
  layersSame = false;
@@ -43470,6 +43759,7 @@ class MaterialDesc {
43470
43759
  const texturesToDestroy = [];
43471
43760
  let noMipmaps = false;
43472
43761
  let hasColorLayer = false;
43762
+ if (!this.canHaveMipmaps) noMipmaps = true;
43473
43763
  for (const layer of this.layers) {
43474
43764
  if (layer instanceof TextureLayer && layer[textureType]) {
43475
43765
  const layerImage = layerTextures.get(layer[textureType]);
@@ -43696,6 +43986,7 @@ class MaterialDesc {
43696
43986
  texture.wrapT = ogTexture.wrapT;
43697
43987
  texture.minFilter = ogTexture.minFilter;
43698
43988
  texture.magFilter = ogTexture.magFilter;
43989
+ texture.generateMipmaps = ogTexture.generateMipmaps;
43699
43990
  }
43700
43991
  }
43701
43992
  if (!this.transparent) {
@@ -43727,7 +44018,7 @@ class MaterialDesc {
43727
44018
  texture.colorSpace = textureType === "color" ? SRGBColorSpace : NoColorSpace;
43728
44019
  texture.wrapS = RepeatWrapping;
43729
44020
  texture.wrapT = RepeatWrapping;
43730
- if (this.isDecal) {
44021
+ if (this.isDecal || !this.canHaveMipmaps) {
43731
44022
  texture.generateMipmaps = false;
43732
44023
  }
43733
44024
  for (const layer of this.layers) {
@@ -43784,6 +44075,7 @@ class MaterialDesc {
43784
44075
  texture.wrapS = RepeatWrapping;
43785
44076
  texture.wrapT = RepeatWrapping;
43786
44077
  texture.colorSpace = textureType === "color" ? SRGBColorSpace : NoColorSpace;
44078
+ texture.generateMipmaps = this.canHaveMipmaps;
43787
44079
  texture.needsUpdate = true;
43788
44080
  return [texture, hasTransparency];
43789
44081
  }
@@ -43878,7 +44170,7 @@ class MaterialDesc {
43878
44170
  if (normalTexture || roughnessTexture || metalnessTexture || emissiveTexture) {
43879
44171
  material = new MeshStandardMaterial({
43880
44172
  ...textureTemplate,
43881
- emissiveIntensity: hasEmissive ? this.emissiveStrength : 0,
44173
+ emissiveIntensity: hasEmissive ? 1 / Math.sqrt(40) * Math.sqrt(this.emissiveStrength) : 0,
43882
44174
  emissive: hasEmissive ? new Color(this.emissiveTint.R, this.emissiveTint.G, this.emissiveTint.B) : new Color(0, 0, 0),
43883
44175
  transparent: hasTransparency,
43884
44176
  opacity: 1 - this.transparency,
@@ -44029,6 +44321,7 @@ class MaterialDesc {
44029
44321
  decalLayer.roughness = roughnessMap;
44030
44322
  }
44031
44323
  decalLayer.uvType = "Normal";
44324
+ this.canHaveMipmaps = false;
44032
44325
  let ZIndex = 1;
44033
44326
  if (decal.HasProperty("ZIndex")) {
44034
44327
  ZIndex = decal.Prop("ZIndex");
@@ -44180,6 +44473,7 @@ class MaterialDesc {
44180
44473
  }
44181
44474
  if (child.Prop("Name") === "Head" && isAffectedByHumanoid(child)) {
44182
44475
  decalLayer.uvType = "Normal";
44476
+ this.canHaveMipmaps = false;
44183
44477
  } else {
44184
44478
  decalLayer.uvType = "Decal";
44185
44479
  decalLayer.face = decal.Prop("Face");
@@ -44257,12 +44551,73 @@ class FaceControlsWrapper extends InstanceWrapper {
44257
44551
  }
44258
44552
  }
44259
44553
  }
44260
- function setBoneToCFrame$1(bone, cf) {
44261
- bone.position.set(...cf.Position);
44262
- bone.rotation.order = "YXZ";
44263
- bone.rotation.x = rad(cf.Orientation[0]);
44264
- bone.rotation.y = rad(cf.Orientation[1]);
44265
- 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
+ }
44266
44621
  }
44267
44622
  const BaseR15Bones = ["Root", "HumanoidRootNode", "LowerTorso", "UpperTorso", "RightUpperArm", "RightLowerArm", "RightHand", "LeftUpperArm", "LeftLowerArm", "LeftHand", "Head", "DynamicHead"];
44268
44623
  function getJointForInstances$1(parent, child, includeTransform) {
@@ -44399,14 +44754,14 @@ let SkeletonDesc$1 = class SkeletonDesc {
44399
44754
  } else if (threeBone.name === "DynamicHead") {
44400
44755
  this.originalDynamicHeadCFrame = boneCF;
44401
44756
  }
44402
- setBoneToCFrame$1(threeBone, boneCF);
44757
+ setTHREEObjectCF(threeBone, boneCF);
44403
44758
  parentThreeBone.add(threeBone);
44404
44759
  } else {
44405
44760
  rootBone = threeBone;
44406
44761
  const boneCF = new CFrame(...bone.position);
44407
44762
  boneCF.fromRotationMatrix(...bone.rotationMatrix);
44408
44763
  this.originalBoneCFrames.push(boneCF);
44409
- setBoneToCFrame$1(threeBone, boneCF);
44764
+ setTHREEObjectCF(threeBone, boneCF);
44410
44765
  }
44411
44766
  }
44412
44767
  if (!rootBone) {
@@ -44548,27 +44903,27 @@ let SkeletonDesc$1 = class SkeletonDesc {
44548
44903
  rootBoneCF = rootBoneCFog.inverse();
44549
44904
  }
44550
44905
  if (partEquivalent && parentPartEquivalent) {
44551
- setBoneToCFrame$1(bone, rootBoneCF.multiply(getJointForInstances$1(parentPartEquivalent, partEquivalent, includeTransform)));
44906
+ setTHREEObjectCF(bone, rootBoneCF.multiply(getJointForInstances$1(parentPartEquivalent, partEquivalent, includeTransform)));
44552
44907
  } else if (partEquivalent) {
44553
44908
  if (includeTransform) {
44554
- setBoneToCFrame$1(bone, rootBoneCF.multiply(partEquivalent.Prop("CFrame")));
44909
+ setTHREEObjectCF(bone, rootBoneCF.multiply(partEquivalent.Prop("CFrame")));
44555
44910
  } else {
44556
44911
  let hrpCF = new CFrame();
44557
44912
  const hrp = humanoidRootPartEquivalent;
44558
44913
  if (hrp) {
44559
44914
  hrpCF = hrp.Prop("CFrame");
44560
44915
  }
44561
- setBoneToCFrame$1(bone, rootBoneCF.multiply(hrpCF.multiply(traverseRigCFrame(partEquivalent))));
44916
+ setTHREEObjectCF(bone, rootBoneCF.multiply(hrpCF.multiply(traverseRigCFrame(partEquivalent))));
44562
44917
  }
44563
44918
  } else if (bone.name === "Root") {
44564
- setBoneToCFrame$1(bone, rootBoneCF.multiply(new CFrame()));
44919
+ setTHREEObjectCF(bone, rootBoneCF.multiply(new CFrame()));
44565
44920
  } else if (bone.name === "HumanoidRootNode") {
44566
44921
  let rootCF = new CFrame();
44567
44922
  const rootPart = humanoidRootPartEquivalent;
44568
44923
  if (rootPart) {
44569
44924
  rootCF = rootPart.Prop("CFrame");
44570
44925
  }
44571
- setBoneToCFrame$1(bone, rootBoneCF.multiply(rootCF));
44926
+ setTHREEObjectCF(bone, rootBoneCF.multiply(rootCF));
44572
44927
  } else if (bone.name === "DynamicHead" && isHead) {
44573
44928
  const head = this.getPartEquivalent(selfInstance, "Head");
44574
44929
  if (head) {
@@ -44593,7 +44948,7 @@ let SkeletonDesc$1 = class SkeletonDesc {
44593
44948
  neckCF = new CFrame();
44594
44949
  }
44595
44950
  const totalCF = neckCF.inverse().multiply(scaledCF);
44596
- setBoneToCFrame$1(bone, totalCF);
44951
+ setTHREEObjectCF(bone, totalCF);
44597
44952
  }
44598
44953
  } else if (!isHead || boneIsChildOf(bone, "DynamicHead")) {
44599
44954
  const ogCF = this.originalBoneCFrames[i];
@@ -44613,7 +44968,7 @@ let SkeletonDesc$1 = class SkeletonDesc {
44613
44968
  headOffset.Orientation = [0, 0, 0];
44614
44969
  }
44615
44970
  const finalCF = headOffset.multiply(scaledCF);
44616
- setBoneToCFrame$1(bone, finalCF);
44971
+ setTHREEObjectCF(bone, finalCF);
44617
44972
  }
44618
44973
  }
44619
44974
  }
@@ -44707,7 +45062,7 @@ let SkeletonDesc$1 = class SkeletonDesc {
44707
45062
  euler.reorder("YXZ");
44708
45063
  resultCF.Orientation = [deg(euler.x), deg(euler.y), deg(euler.z)];
44709
45064
  resultCF.Position = multiply(totalPosition.toVec3(), headScale);
44710
- setBoneToCFrame$1(bone, jointCF.multiply(resultCF));
45065
+ setTHREEObjectCF(bone, jointCF.multiply(resultCF));
44711
45066
  break;
44712
45067
  }
44713
45068
  }
@@ -44991,74 +45346,6 @@ class SkeletonDesc2 {
44991
45346
  return meshDesc.canHaveSkinning && meshDesc.fileMesh && meshDesc.fileMesh.skinning && meshDesc.fileMesh.skinning.subsets.length > 0 && meshDesc.fileMesh.skinning.skinnings.length > 0;
44992
45347
  }
44993
45348
  }
44994
- class DisposableDesc {
44995
- disposeMesh(scene, mesh) {
44996
- disposeMesh(scene, mesh);
44997
- }
44998
- disposeMeshes(scene, meshes) {
44999
- for (const mesh of meshes) {
45000
- this.disposeMesh(scene, mesh);
45001
- }
45002
- }
45003
- disposeRenderLists(renderer) {
45004
- renderer.renderLists.dispose();
45005
- }
45006
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45007
- dispose(_renderer, _scene) {
45008
- throw new Error("Virtual method dispose called");
45009
- }
45010
- }
45011
- class RenderDesc extends DisposableDesc {
45012
- renderScene;
45013
- results;
45014
- instance;
45015
- constructor(renderScene) {
45016
- super();
45017
- this.renderScene = renderScene;
45018
- }
45019
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45020
- isSame(_other) {
45021
- throw new Error("Virtual method isSame called");
45022
- }
45023
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45024
- needsRegeneration(_other) {
45025
- throw new Error("Virtual method needsRegeneration called");
45026
- }
45027
- fromRenderDesc(other) {
45028
- if (this.needsRegeneration(other)) {
45029
- throw new Error("These RenderableDesc objects have differences that require recompilation");
45030
- }
45031
- this.virtualFromRenderDesc(other);
45032
- }
45033
- transferFrom(other) {
45034
- this.virtualTransferFrom(other);
45035
- }
45036
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45037
- virtualTransferFrom(_other) {
45038
- }
45039
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45040
- virtualFromRenderDesc(_other) {
45041
- throw new Error("Virtual method virtualFromRenderDesc called");
45042
- }
45043
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45044
- fromInstance(_child) {
45045
- throw new Error("Virtual method fromInstance called");
45046
- }
45047
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45048
- async compileResults(_renderer, _scene) {
45049
- throw new Error("Virtual method compileResults called");
45050
- }
45051
- updateResults() {
45052
- throw new Error("Virtual method updateResults called");
45053
- }
45054
- }
45055
- function setTHREEMeshCF(threeMesh, cframe) {
45056
- threeMesh.position.set(cframe.Position[0], cframe.Position[1], cframe.Position[2]);
45057
- threeMesh.rotation.order = "YXZ";
45058
- threeMesh.rotation.x = rad(cframe.Orientation[0]);
45059
- threeMesh.rotation.y = rad(cframe.Orientation[1]);
45060
- threeMesh.rotation.z = rad(cframe.Orientation[2]);
45061
- }
45062
45349
  class ObjectDesc extends RenderDesc {
45063
45350
  cframe = new CFrame();
45064
45351
  size = new Vector32(1, 1, 1);
@@ -45114,6 +45401,7 @@ class ObjectDesc extends RenderDesc {
45114
45401
  if (part) {
45115
45402
  switch (part.className) {
45116
45403
  case "Part": {
45404
+ if (!isAffectedByHumanoid(part)) this.size = part.PropOrDefault("Size", this.size);
45117
45405
  const specialMesh = part.FindFirstChildOfClass("SpecialMesh");
45118
45406
  if (specialMesh) {
45119
45407
  this.size = specialMesh.Property("Scale");
@@ -45170,7 +45458,8 @@ class ObjectDesc extends RenderDesc {
45170
45458
  }
45171
45459
  }
45172
45460
  async compileResults(renderer, scene) {
45173
- API.Misc.startCurrentlyLoadingAssets();
45461
+ const loadingLabel = this.instance ? this.instance.GetFullName() : "unknown";
45462
+ API.Misc.startCurrentlyLoadingAssets(loadingLabel);
45174
45463
  const originalResult = this.results;
45175
45464
  const originalSkeletonDesc = this.skeletonDesc;
45176
45465
  this.results = void 0;
@@ -45181,7 +45470,8 @@ class ObjectDesc extends RenderDesc {
45181
45470
  ];
45182
45471
  const [threeMesh, threeMaterial] = await Promise.all(promises);
45183
45472
  if (!(threeMesh instanceof Mesh)) {
45184
- API.Misc.stopCurrentlyLoadingAssets();
45473
+ warn(true, "Failed to get mesh for objectDesc", this.instance ? this.instance.GetFullName() : "unknown");
45474
+ API.Misc.stopCurrentlyLoadingAssets(loadingLabel);
45185
45475
  return threeMesh;
45186
45476
  }
45187
45477
  if (threeMesh instanceof SkinnedMesh) {
@@ -45217,7 +45507,7 @@ class ObjectDesc extends RenderDesc {
45217
45507
  if (originalResult) {
45218
45508
  this.disposeRenderLists(renderer);
45219
45509
  }
45220
- API.Misc.stopCurrentlyLoadingAssets();
45510
+ API.Misc.stopCurrentlyLoadingAssets(loadingLabel);
45221
45511
  return this.results;
45222
45512
  }
45223
45513
  getScale() {
@@ -45246,7 +45536,7 @@ class ObjectDesc extends RenderDesc {
45246
45536
  }
45247
45537
  for (const result of this.results) {
45248
45538
  result.scale.set(...this.getScale().toVec3());
45249
- setTHREEMeshCF(result, resultCF);
45539
+ setTHREEObjectCF(result, resultCF);
45250
45540
  result.updateMatrix();
45251
45541
  result.updateMatrixWorld(true);
45252
45542
  if (this.skeletonDesc && this.instance) {
@@ -46920,7 +47210,7 @@ class AnimatorWrapper extends InstanceWrapper {
46920
47210
  }
46921
47211
  this.data.currentAnimation = name;
46922
47212
  let toPlayTrack = void 0;
46923
- if (!name.startsWith("emote.")) {
47213
+ if (!name.startsWith("emote.") && !name.startsWith("id.")) {
46924
47214
  const entries = this.data.animationSet[name];
46925
47215
  if (entries && entries.length > 0) {
46926
47216
  const entry = this._pickRandom(entries);
@@ -46928,12 +47218,15 @@ class AnimatorWrapper extends InstanceWrapper {
46928
47218
  toPlayTrack = this._getTrack(entry.id);
46929
47219
  }
46930
47220
  }
46931
- } else {
47221
+ } else if (name.startsWith("emote.")) {
46932
47222
  const emoteId = BigInt(name.split(".")[1]);
46933
47223
  const entry = this.data.emotes.get(emoteId);
46934
47224
  if (entry) {
46935
47225
  toPlayTrack = this._getTrack(entry.id);
46936
47226
  }
47227
+ } else {
47228
+ const animId = BigInt(name.split(".")[1]);
47229
+ toPlayTrack = this.data.animationTracks.get(animId);
46937
47230
  }
46938
47231
  if (toPlayTrack !== this.data.currentAnimationTrack) {
46939
47232
  if (toPlayTrack || name === "") {
@@ -47159,7 +47452,39 @@ class AnimatorWrapper extends InstanceWrapper {
47159
47452
  }
47160
47453
  }
47161
47454
  /**
47162
- * 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)
47163
47488
  * @param id
47164
47489
  * @param isEmote
47165
47490
  * @param forceLoop Forces animation track to loop
@@ -47203,32 +47528,15 @@ class AnimatorWrapper extends InstanceWrapper {
47203
47528
  });
47204
47529
  } else {
47205
47530
  promises.push(new Promise((resolve) => {
47206
- API.Asset.GetRBX(`rbxassetid://${subAnimId}`, void 0).then((result) => {
47207
- if (result instanceof RBX) {
47208
- log(false, "loading anim", subAnimId);
47209
- const animTrackInstance = result.generateTree().GetChildren()[0];
47210
- if (animTrackInstance && humanoid.parent) {
47211
- const animTrack = new AnimationTrack().loadAnimation(humanoid.parent, animTrackInstance);
47212
- if (forceLoop) {
47213
- animTrack.looped = true;
47214
- }
47215
- if (!this.data.animationSet[animName]) {
47216
- this.data.animationSet[animName] = [];
47217
- }
47218
- this.data.animationSet[animName].push({
47219
- id: `rbxassetid://${subAnimId}`,
47220
- weight: subWeight
47221
- });
47222
- if (this.data.animationTracks.get(subAnimId)) {
47223
- throw new Error("Animation was already loaded");
47224
- }
47225
- this.data.animationTracks.set(subAnimId, animTrack);
47226
- this.instance.setProperty("_HasLoadedAnimation", true);
47227
- }
47228
- resolve(void 0);
47229
- } else {
47230
- resolve(result);
47531
+ this.loadAnimation(subAnimId, forceLoop).then((result) => {
47532
+ if (!this.data.animationSet[animName]) {
47533
+ this.data.animationSet[animName] = [];
47231
47534
  }
47535
+ this.data.animationSet[animName].push({
47536
+ id: `rbxassetid://${subAnimId}`,
47537
+ weight: subWeight
47538
+ });
47539
+ resolve(result instanceof Response ? result : void 0);
47232
47540
  });
47233
47541
  }));
47234
47542
  }
@@ -47250,25 +47558,12 @@ class AnimatorWrapper extends InstanceWrapper {
47250
47558
  });
47251
47559
  } else {
47252
47560
  promises.push(new Promise((resolve) => {
47253
- API.Asset.GetRBX(`rbxassetid://${animId}`, void 0).then((result) => {
47254
- if (result instanceof RBX) {
47255
- const animTrackInstance = result.generateTree().GetChildren()[0];
47256
- if (animTrackInstance && humanoid.parent) {
47257
- const animTrack = new AnimationTrack().loadAnimation(humanoid.parent, animTrackInstance);
47258
- if (forceLoop) {
47259
- animTrack.looped = true;
47260
- }
47261
- this.data.emotes.set(id, {
47262
- id: `rbxassetid://${animId}`,
47263
- weight: 1
47264
- });
47265
- this.data.animationTracks.set(animId, animTrack);
47266
- this.instance.setProperty("_HasLoadedAnimation", true);
47267
- }
47268
- resolve(void 0);
47269
- } else {
47270
- resolve(result);
47271
- }
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);
47272
47567
  });
47273
47568
  }));
47274
47569
  }
@@ -47277,7 +47572,7 @@ class AnimatorWrapper extends InstanceWrapper {
47277
47572
  }
47278
47573
  /**
47279
47574
  * Switches to new animation
47280
- * @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"
47281
47576
  * @param type
47282
47577
  * @returns If animation sucessfully played
47283
47578
  */
@@ -48753,10 +49048,34 @@ class HumanoidDescriptionWrapper extends InstanceWrapper {
48753
49048
  }
48754
49049
  return void 0;
48755
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
+ }
48756
49076
  /**
48757
49077
  * @returns undefined on success
48758
49078
  */
48759
- //TODO: CLEAN UP THIS CODE, the comments are not enough!
48760
49079
  async _applyAnimations(humanoid, toChange = AllAnimations) {
48761
49080
  const animator = humanoid.FindFirstChildOfClass("Animator");
48762
49081
  if (!animator) {
@@ -48766,7 +49085,7 @@ class HumanoidDescriptionWrapper extends InstanceWrapper {
48766
49085
  const animatorW = new AnimatorWrapper(animator);
48767
49086
  const promises = [];
48768
49087
  for (const animationProp of toChange) {
48769
- if (this.instance.HasProperty(animationProp) && this.instance.Prop(animationProp) > 0n && avatarType === AvatarType.R15) {
49088
+ if (this.instance.PropOrDefault(animationProp, 0n) > 0n && avatarType === AvatarType.R15) {
48770
49089
  const id = this.instance.Prop(animationProp);
48771
49090
  promises.push(new Promise((resolve) => {
48772
49091
  animatorW.loadAvatarAnimation(id, false, true).then((result) => {
@@ -48774,35 +49093,7 @@ class HumanoidDescriptionWrapper extends InstanceWrapper {
48774
49093
  });
48775
49094
  }));
48776
49095
  } else {
48777
- const animName = AnimationPropToName[animationProp];
48778
- const animationSetEntries = avatarType === AvatarType.R15 ? animNamesR15[animName] : animNamesR6[animName];
48779
- animatorW.data.animationSet[animName] = [];
48780
- if (animationSetEntries) {
48781
- for (const subAnim of animationSetEntries) {
48782
- promises.push(new Promise((resolve) => {
48783
- API.Asset.GetRBX(subAnim.id, void 0).then((result) => {
48784
- if (result instanceof RBX) {
48785
- const animTrackInstance = result.generateTree().GetChildren()[0];
48786
- if (animTrackInstance && humanoid.parent) {
48787
- const animTrack = new AnimationTrack().loadAnimation(humanoid.parent, animTrackInstance);
48788
- animTrack.looped = true;
48789
- animatorW.data.animationTracks.set(BigInt(API.Misc.idFromStr(subAnim.id)), animTrack);
48790
- if (!animatorW.data.animationSet[animName]) {
48791
- animatorW.data.animationSet[animName] = [];
48792
- }
48793
- animatorW.data.animationSet[animName].push(subAnim);
48794
- animatorW.instance.setProperty("_HasLoadedAnimation", true);
48795
- }
48796
- resolve(void 0);
48797
- } else {
48798
- resolve(result);
48799
- }
48800
- });
48801
- }));
48802
- }
48803
- } else {
48804
- warn(false, `No default found for animation ${animName}`);
48805
- }
49096
+ this._loadDefaultAnimation(animationProp, avatarType, animatorW, promises);
48806
49097
  }
48807
49098
  }
48808
49099
  const values = await Promise.all(promises);
@@ -49073,15 +49364,21 @@ class SoundWrapperData {
49073
49364
  }
49074
49365
  class SoundWrapper extends InstanceWrapper {
49075
49366
  static className = "Sound";
49076
- static requiredProperties = ["Name", "_data"];
49367
+ static requiredProperties = ["Name", "Looped", "Playing", "Volume", "_data"];
49077
49368
  setup() {
49078
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);
49079
49373
  if (!this.instance.HasProperty("_data")) this.instance.addProperty(new Property("_data", DataType.NonSerializable), new SoundWrapperData());
49080
49374
  }
49081
49375
  get data() {
49082
49376
  return this.instance.Prop("_data");
49083
49377
  }
49084
49378
  created() {
49379
+ if (this.instance.Prop("Playing")) {
49380
+ this.Play();
49381
+ }
49085
49382
  this.instance.Destroying.Connect(() => {
49086
49383
  if (this.data.playingSource) {
49087
49384
  this.Stop();
@@ -49099,6 +49396,9 @@ class SoundWrapper extends InstanceWrapper {
49099
49396
  }
49100
49397
  }
49101
49398
  }
49399
+ setPlaying(value) {
49400
+ this.instance.setProperty("Playing", value);
49401
+ }
49102
49402
  playSource() {
49103
49403
  if (!this.data.audioContext || !this.data.gainNode || !this.data.buffer) return;
49104
49404
  this.data.playingSource = this.data.audioContext.createBufferSource();
@@ -49107,10 +49407,18 @@ class SoundWrapper extends InstanceWrapper {
49107
49407
  this.data.gainNode.connect(this.data.audioContext.destination);
49108
49408
  this._updateVolume();
49109
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
+ });
49110
49417
  }
49111
49418
  Play() {
49112
49419
  if (!FLAGS.AUDIO_ENABLED) return;
49113
49420
  this.Stop();
49421
+ this.setPlaying(true);
49114
49422
  if (!this.data.audioContext) {
49115
49423
  this.data.audioContext = new AudioContext();
49116
49424
  }
@@ -49140,6 +49448,7 @@ class SoundWrapper extends InstanceWrapper {
49140
49448
  }
49141
49449
  }
49142
49450
  Stop() {
49451
+ this.setPlaying(false);
49143
49452
  if (this.data.playingSource) {
49144
49453
  this.data.playingSource.stop();
49145
49454
  this.data.playingSource = void 0;
@@ -49280,6 +49589,142 @@ function RegisterWrappers() {
49280
49589
  BodyColorsWrapper.register();
49281
49590
  AccessoryWrapper.register();
49282
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
+ }
49283
49728
  function disposeMesh(scene, mesh) {
49284
49729
  if (mesh.material) {
49285
49730
  const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
@@ -49924,6 +50369,8 @@ class RBXRenderer {
49924
50369
  RBXRenderer._addRenderDesc(instance, auth, ObjectDesc, renderScene);
49925
50370
  } else if (EmitterGroupDescClassTypes.includes(instance.className)) {
49926
50371
  RBXRenderer._addRenderDesc(instance, auth, EmitterGroupDesc, renderScene);
50372
+ } else if (LightDescClassTypes.includes(instance.className)) {
50373
+ RBXRenderer._addRenderDesc(instance, auth, LightDesc, renderScene);
49927
50374
  }
49928
50375
  for (const child of instance.GetChildren()) {
49929
50376
  RBXRenderer.addInstance(child, auth, renderScene);
@@ -50718,6 +51165,7 @@ export {
50718
51165
  LODS,
50719
51166
  LayeredAssetTypes,
50720
51167
  LayeredClothingAssetOrder,
51168
+ LightDescClassTypes,
50721
51169
  LocalOutfit,
50722
51170
  MakeupAssetTypes,
50723
51171
  MakeupDescriptionWrapper,
@@ -50784,6 +51232,8 @@ export {
50784
51232
  animNamesR6,
50785
51233
  arrayBufferToBase64,
50786
51234
  averageVec3,
51235
+ awaitTimeout,
51236
+ awaitTimeoutThrows,
50787
51237
  barycentric,
50788
51238
  base64ToArrayBuffer,
50789
51239
  browserOpenURL,