roavatar-renderer 1.4.0 → 1.4.2

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
@@ -29514,6 +29514,7 @@ const FLAGS = {
29514
29514
  ROAVATAR_TRYON_PLACE: 135979364355750,
29515
29515
  ASSETDELIVERY_V2: true,
29516
29516
  ASSET_REQUEST_PRIORITY: "high",
29517
+ API_DOMAIN: "roblox.com",
29517
29518
  //assets
29518
29519
  ONLINE_ASSETS: false,
29519
29520
  ASSETS_PATH: "../assets/rbxasset/",
@@ -29630,10 +29631,19 @@ class InstanceWrapper {
29630
29631
  log(false, "Registered InstanceWrapper:", ClassNameToWrapper);
29631
29632
  }
29632
29633
  //virtual functions
29634
+ /**
29635
+ * @virtual
29636
+ */
29633
29637
  created() {
29634
29638
  }
29639
+ /**
29640
+ * @virtual
29641
+ */
29635
29642
  destroy() {
29636
29643
  }
29644
+ /**
29645
+ * @virtual
29646
+ */
29637
29647
  preRender() {
29638
29648
  }
29639
29649
  }
@@ -29764,6 +29774,9 @@ class Color3 {
29764
29774
  clone() {
29765
29775
  return new Color3(this.R, this.G, this.B);
29766
29776
  }
29777
+ isSame(other) {
29778
+ return this.R === other.R && this.G === other.G && this.B === other.B;
29779
+ }
29767
29780
  toColor3uint8() {
29768
29781
  return new Color3uint8(Math.round(this.R * 255), Math.round(this.G * 255), Math.round(this.B * 255));
29769
29782
  }
@@ -29808,6 +29821,9 @@ class NumberSequenceKeypoint {
29808
29821
  clone() {
29809
29822
  return new NumberSequenceKeypoint(this.time, this.value, this.envelope);
29810
29823
  }
29824
+ isSame(other) {
29825
+ return this.time === other.time && this.value === other.value && this.envelope === other.envelope;
29826
+ }
29811
29827
  }
29812
29828
  class NumberSequence {
29813
29829
  keypoints = [];
@@ -29821,6 +29837,13 @@ class NumberSequence {
29821
29837
  }
29822
29838
  return copy;
29823
29839
  }
29840
+ isSame(other) {
29841
+ if (this.keypoints.length !== other.keypoints.length) return false;
29842
+ for (let i = 0; i < this.keypoints.length; i++) {
29843
+ if (!this.keypoints[i].isSame(other.keypoints[i])) return false;
29844
+ }
29845
+ return true;
29846
+ }
29824
29847
  getLowerKey(time2) {
29825
29848
  let resultKey = null;
29826
29849
  for (const key of this.keypoints) {
@@ -29876,6 +29899,9 @@ class ColorSequenceKeypoint {
29876
29899
  clone() {
29877
29900
  return new ColorSequenceKeypoint(this.time, this.value.R, this.value.G, this.value.B);
29878
29901
  }
29902
+ isSame(other) {
29903
+ return this.time === other.time && this.value.isSame(other.value);
29904
+ }
29879
29905
  }
29880
29906
  class ColorSequence {
29881
29907
  keypoints = [];
@@ -29891,6 +29917,13 @@ class ColorSequence {
29891
29917
  }
29892
29918
  return copy;
29893
29919
  }
29920
+ isSame(other) {
29921
+ if (this.keypoints.length !== other.keypoints.length) return false;
29922
+ for (let i = 0; i < this.keypoints.length; i++) {
29923
+ if (!this.keypoints[i].isSame(other.keypoints[i])) return false;
29924
+ }
29925
+ return true;
29926
+ }
29894
29927
  getLowerKey(time2) {
29895
29928
  let resultKey = null;
29896
29929
  for (const key of this.keypoints) {
@@ -30078,17 +30111,23 @@ let lastInstanceId = 0;
30078
30111
  const AllInstances = [];
30079
30112
  class Instance {
30080
30113
  _id;
30114
+ /**
30115
+ * @deprecated Use .Prop("Name") instead
30116
+ */
30081
30117
  _name;
30082
30118
  //USED TO MAKE VIEWING EASIER
30083
30119
  className;
30120
+ /**
30121
+ * @deprecated Do not use this directly
30122
+ */
30084
30123
  _properties = /* @__PURE__ */ new Map();
30085
30124
  _referencedBy = [];
30086
30125
  _connectionReferences = [];
30087
- children = [];
30126
+ _children = [];
30088
30127
  parent = void 0;
30089
30128
  destroyed = false;
30090
- hasWrappered = false;
30091
- canGC = true;
30129
+ _hasWrappered = false;
30130
+ //private _canGC: boolean = true
30092
30131
  classID;
30093
30132
  //dont use this to identify instance class, it is only used during file loading
30094
30133
  objectFormat;
@@ -30123,8 +30162,8 @@ class Instance {
30123
30162
  }
30124
30163
  createWrapper() {
30125
30164
  const wrapper = GetWrapperForInstance(this);
30126
- if (wrapper && !this.hasWrappered) {
30127
- this.hasWrappered = true;
30165
+ if (wrapper && !this._hasWrappered) {
30166
+ this._hasWrappered = true;
30128
30167
  wrapper.created();
30129
30168
  }
30130
30169
  }
@@ -30242,6 +30281,13 @@ class Instance {
30242
30281
  name = this.fixPropertyName(name);
30243
30282
  return !!this._properties.get(name);
30244
30283
  }
30284
+ /**
30285
+ * Returns the value of a property
30286
+ * @param name Name of property
30287
+ * @returns Property's value
30288
+ *
30289
+ * @throws When property doesn't exist, PropOrDefault is a safer alternative
30290
+ */
30245
30291
  Property(name) {
30246
30292
  let property = this._properties.get(name);
30247
30293
  if (property) return property.value;
@@ -30285,6 +30331,13 @@ class Instance {
30285
30331
  throw new Error(`Property: ${name} does not exist`);
30286
30332
  }
30287
30333
  }
30334
+ /**
30335
+ * Returns the value of a property
30336
+ * @param name Name of property
30337
+ * @returns Property's value
30338
+ *
30339
+ * @throws When property doesn't exist, PropOrDefault is a safer alternative
30340
+ */
30288
30341
  Prop(name) {
30289
30342
  return this.Property(name);
30290
30343
  }
@@ -30305,9 +30358,9 @@ class Instance {
30305
30358
  throw new Error("Cannot set parent of instance to a destroyed instance");
30306
30359
  }
30307
30360
  if (this.parent) {
30308
- const index = this.parent.children.indexOf(this);
30361
+ const index = this.parent._children.indexOf(this);
30309
30362
  if (index !== -1) {
30310
- this.parent.children.splice(index, 1);
30363
+ this.parent._children.splice(index, 1);
30311
30364
  }
30312
30365
  }
30313
30366
  const originalParent = this.parent;
@@ -30316,7 +30369,7 @@ class Instance {
30316
30369
  originalParent.ChildRemoved.Fire(this);
30317
30370
  }
30318
30371
  if (instance) {
30319
- instance.children.push(this);
30372
+ instance._children.push(this);
30320
30373
  }
30321
30374
  if (fireEvents) {
30322
30375
  if (instance) {
@@ -30373,7 +30426,7 @@ class Instance {
30373
30426
  }
30374
30427
  GetChildren() {
30375
30428
  const childrenList = [];
30376
- for (const child of this.children) {
30429
+ for (const child of this._children) {
30377
30430
  childrenList.push(child);
30378
30431
  }
30379
30432
  return childrenList;
@@ -30403,7 +30456,7 @@ class Instance {
30403
30456
  return this.FindFirstChild(name);
30404
30457
  }
30405
30458
  FindFirstChildOfClass(className) {
30406
- for (const child of this.children) {
30459
+ for (const child of this._children) {
30407
30460
  if (child.className === className) {
30408
30461
  return child;
30409
30462
  }
@@ -30411,7 +30464,7 @@ class Instance {
30411
30464
  }
30412
30465
  FindLastChildOfClass(className) {
30413
30466
  let lastChild = void 0;
30414
- for (const child of this.children) {
30467
+ for (const child of this._children) {
30415
30468
  if (child.className === className) {
30416
30469
  lastChild = child;
30417
30470
  }
@@ -30527,7 +30580,7 @@ class RBX {
30527
30580
  treeGenerated = false;
30528
30581
  xmlString;
30529
30582
  get instances() {
30530
- return this.dataModel.children;
30583
+ return this.dataModel.GetChildren();
30531
30584
  }
30532
30585
  constructor() {
30533
30586
  this.reset();
@@ -31421,6 +31474,10 @@ class RBX {
31421
31474
  }
31422
31475
  return buffer2;
31423
31476
  }
31477
+ /**
31478
+ * Generates if needed hierarchy and returns root instance
31479
+ * @returns Root instance
31480
+ */
31424
31481
  generateTree() {
31425
31482
  if (this.treeGenerated) {
31426
31483
  warn(false, "Tree already generated");
@@ -32722,6 +32779,10 @@ class Outfit {
32722
32779
  }
32723
32780
  return issues;
32724
32781
  }
32782
+ /**
32783
+ * @deprecated Outdated
32784
+ * @returns
32785
+ */
32725
32786
  async toHumanoidDescription() {
32726
32787
  const response = await fetch("/assets/HumanoidDescriptionTemplate.xml");
32727
32788
  if (response.status !== 200)
@@ -32876,10 +32937,12 @@ class Outfit {
32876
32937
  return HumanoidDescription;
32877
32938
  }
32878
32939
  //TODO: Implement
32940
+ /** @deprecated */
32879
32941
  async fromHumanoidDescription(rootDocument) {
32880
32942
  const humanoidDescription = rootDocument.querySelector(".HumanoidDescription");
32881
32943
  log(false, humanoidDescription);
32882
32944
  }
32945
+ /** @deprecated */
32883
32946
  async downloadHumanoidDescription() {
32884
32947
  const humanoidDescription = await this.toHumanoidDescription();
32885
32948
  if (humanoidDescription) {
@@ -33022,6 +33085,9 @@ class Outfit {
33022
33085
  asset.assetType.name = typeName;
33023
33086
  this.assets.push(asset);
33024
33087
  }
33088
+ /**
33089
+ * Fixes multiple layered assets having the same order value
33090
+ */
33025
33091
  fixOrders() {
33026
33092
  for (const asset of this.assets.slice().reverse()) {
33027
33093
  if (!AccessoryAssetTypes.includes(asset.assetType.name) && LayeredAssetTypes.includes(asset.assetType.name)) {
@@ -35133,6 +35199,9 @@ class Authentication {
35133
35199
  }
35134
35200
  }
35135
35201
  async function RBLXPost(url, auth, body, attempt = 0, method = "POST") {
35202
+ if (url.match(/https?:\/\/[a-z]+.roblox.com/)) {
35203
+ url = url.replace("roblox.com", FLAGS.API_DOMAIN);
35204
+ }
35136
35205
  if (typeof body !== "string") {
35137
35206
  body = JSON.stringify(body);
35138
35207
  }
@@ -35179,6 +35248,9 @@ async function RBLXPost(url, auth, body, attempt = 0, method = "POST") {
35179
35248
  });
35180
35249
  }
35181
35250
  async function RBLXGet(url, headers, includeCredentials = true) {
35251
+ if (url.match(/https?:\/\/[a-z]+.roblox.com/)) {
35252
+ url = url.replace("roblox.com", FLAGS.API_DOMAIN);
35253
+ }
35182
35254
  return new Promise((resolve) => {
35183
35255
  let newHeaders = {
35184
35256
  "Content-Type": "application/json"
@@ -36374,6 +36446,16 @@ class RBFDeformerPatch {
36374
36446
  // avoid matrix from being singular
36375
36447
  affectBones = true;
36376
36448
  id = rbfDeformerIdCount++;
36449
+ /**
36450
+ * Creates the deformer and prepares for deformation
36451
+ * @param refMesh The reference cage
36452
+ * @param distMesh The destination cage
36453
+ * @param mesh The mesh to deform
36454
+ * @param ignoredIndices Indices in the reference cage to ignore
36455
+ * @param patchCount Amount of patches spread around the mesh, each one solves a linear equation
36456
+ * @param detailsCount Amount of close vertices in each patch
36457
+ * @param importantsCount Amount of far away vertices in each patch
36458
+ */
36377
36459
  constructor(refMesh, distMesh, mesh, ignoredIndices = [], patchCount = FLAGS.RBF_PATCH_COUNT, detailsCount = FLAGS.RBF_PATCH_DETAIL_SAMPLES, importantsCount = FLAGS.RBF_PATCH_SHAPE_SAMPLES) {
36378
36460
  time(`RBFDeformerPatch.constructor.${this.id}`);
36379
36461
  this.mesh = mesh;
@@ -36423,6 +36505,10 @@ class RBFDeformerPatch {
36423
36505
  this.patchCount = patchCount;
36424
36506
  timeEnd(`RBFDeformerPatch.constructor.${this.id}`);
36425
36507
  }
36508
+ /**
36509
+ * Solves the linear equations asynchronously, required before deformation
36510
+ * @returns void
36511
+ */
36426
36512
  async solveAsync() {
36427
36513
  if (this.refVerts.length === 0) {
36428
36514
  return;
@@ -36448,6 +36534,12 @@ class RBFDeformerPatch {
36448
36534
  this.nearestPatch = new Uint16Array(nearestPatchBuf);
36449
36535
  timeEnd(`RBFDeformerPatch.solveAsync.unpack.${this.id}`);
36450
36536
  }
36537
+ /**
36538
+ * solveAsync() needs to be called before this
36539
+ * Deforms the position of something inside the mesh
36540
+ * @param i The index of the vert to deform, if it is outside the range of vertices it will be a bone instead. For example vertices range from 1 - 100, and bones range from 101 - 110. Then index 101 will represent the first bone
36541
+ * @returns New position after deformation
36542
+ */
36451
36543
  deform(i) {
36452
36544
  if (!this.nearestPatch || !this.neighborIndices || !this.weights) {
36453
36545
  throw new Error("RBF has not been solved");
@@ -36471,6 +36563,10 @@ class RBFDeformerPatch {
36471
36563
  }
36472
36564
  return add(vec, [dx, dy, dz]);
36473
36565
  }
36566
+ /**
36567
+ * solveAsync() needs to be called before this
36568
+ * Deforms vertices and bones in mesh (if this.affectBones = true)
36569
+ */
36474
36570
  deformMesh() {
36475
36571
  if (this.refVerts.length === 0) {
36476
36572
  return;
@@ -44926,6 +45022,12 @@ class RenderDesc extends DisposableDesc {
44926
45022
  }
44927
45023
  this.virtualFromRenderDesc(other);
44928
45024
  }
45025
+ transferFrom(other) {
45026
+ this.virtualTransferFrom(other);
45027
+ }
45028
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
45029
+ virtualTransferFrom(_other) {
45030
+ }
44929
45031
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
44930
45032
  virtualFromRenderDesc(_other) {
44931
45033
  throw new Error("Virtual method virtualFromRenderDesc called");
@@ -45331,7 +45433,6 @@ class EmitterDesc extends DisposableDesc {
45331
45433
  passedTime = 0;
45332
45434
  lockedToPart = false;
45333
45435
  lifetime = new NumberRange(1, 1);
45334
- rate = 10;
45335
45436
  spreadAngle = new Vector22(0, 0);
45336
45437
  speed = new NumberRange(1, 1);
45337
45438
  rotation = new NumberRange(0, 0);
@@ -45344,9 +45445,6 @@ class EmitterDesc extends DisposableDesc {
45344
45445
  zOffset = 0;
45345
45446
  offset = new Vector32();
45346
45447
  shapeInOut = 0;
45347
- colorTexture;
45348
- alphaTexture;
45349
- texture;
45350
45448
  opacity = 1;
45351
45449
  lightEmission = 1;
45352
45450
  blending = AdditiveBlending;
@@ -45354,16 +45452,52 @@ class EmitterDesc extends DisposableDesc {
45354
45452
  size = new NumberSequence();
45355
45453
  transparency = new NumberSequence([new NumberSequenceKeypoint(0, 0, 0)]);
45356
45454
  normalizeSizeKeypointTime = true;
45455
+ //requires recompilation
45456
+ rate = 10;
45457
+ colorTexture;
45458
+ alphaTexture;
45459
+ texture;
45460
+ //results
45357
45461
  instanceOpacityBuffer;
45358
45462
  instanceColorBuffer;
45359
45463
  instanceSeedTimeBuffer;
45360
45464
  result;
45361
45465
  particles = [];
45466
+ initialParticleCount = 0;
45362
45467
  get maxCount() {
45363
- return Math.max(Math.ceil(this.lifetime.Max * this.rate) * 2, 1);
45468
+ const calculatedMax = Math.max(Math.ceil(this.lifetime.Max * this.rate) * 2, 1);
45469
+ const particleMax = this.initialParticleCount + calculatedMax;
45470
+ return particleMax;
45471
+ }
45472
+ needsRegeneration(other) {
45473
+ return this.texture === other.texture && this.alphaTexture === other.alphaTexture && this.colorTexture === other.colorTexture && this.rate === other.rate;
45364
45474
  }
45365
45475
  isSame(other) {
45366
- return this.lifetime.isSame(other.lifetime) && this.rate === other.rate && this.texture === other.texture;
45476
+ return !this.needsRegeneration(other) && this.lockedToPart === other.lockedToPart && this.lifetime.isSame(other.lifetime) && this.spreadAngle.isSame(other.spreadAngle) && this.speed.isSame(other.speed) && this.rotation.isSame(other.rotation) && this.rotationSpeed.isSame(other.rotationSpeed) && this.localAcceleration.isSame(other.localAcceleration) && this.acceleration.isSame(other.acceleration) && this.drag === other.drag && this.timeScale === other.timeScale && this.orientation === other.orientation && this.zOffset === other.zOffset && this.offset.isSame(other.offset) && this.shapeInOut === other.shapeInOut && this.opacity === other.opacity && this.lightEmission === other.lightEmission && this.blending === other.blending && this.color.isSame(other.color) && this.size.isSame(other.size) && this.transparency.isSame(other.transparency) && this.normalizeSizeKeypointTime === other.normalizeSizeKeypointTime;
45477
+ }
45478
+ fromEmitterDesc(other) {
45479
+ this.lockedToPart = other.lockedToPart;
45480
+ this.lifetime = other.lifetime.clone();
45481
+ this.rate = other.rate;
45482
+ this.spreadAngle = other.spreadAngle.clone();
45483
+ this.speed = other.speed.clone();
45484
+ this.rotation = other.rotation.clone();
45485
+ this.rotationSpeed = other.rotationSpeed.clone();
45486
+ this.localAcceleration = other.localAcceleration.clone();
45487
+ this.acceleration = other.acceleration.clone();
45488
+ this.drag = other.drag;
45489
+ this.timeScale = other.timeScale;
45490
+ this.orientation = other.orientation;
45491
+ this.zOffset = other.zOffset;
45492
+ this.offset = other.offset.clone();
45493
+ this.shapeInOut = other.shapeInOut;
45494
+ this.opacity = other.opacity;
45495
+ this.lightEmission = other.lightEmission;
45496
+ this.blending = other.blending;
45497
+ this.color = other.color.clone();
45498
+ this.size = other.size.clone();
45499
+ this.transparency = other.transparency.clone();
45500
+ this.normalizeSizeKeypointTime = other.normalizeSizeKeypointTime;
45367
45501
  }
45368
45502
  dispose(renderer, scene) {
45369
45503
  const mesh = this.result;
@@ -45589,17 +45723,14 @@ class EmitterGroupDesc extends RenderDesc {
45589
45723
  if (this.needsRegeneration(other)) {
45590
45724
  return false;
45591
45725
  }
45592
- if (this.time !== other.time) {
45593
- return false;
45594
- }
45595
- return true;
45726
+ return this.time === other.time;
45596
45727
  }
45597
45728
  needsRegeneration(other) {
45598
45729
  if (this.emitterDescs.length !== other.emitterDescs.length) {
45599
45730
  return true;
45600
45731
  }
45601
45732
  for (let i = 0; i < this.emitterDescs.length; i++) {
45602
- if (!this.emitterDescs[i].isSame(other.emitterDescs[i])) {
45733
+ if (!this.emitterDescs[i].needsRegeneration(other.emitterDescs[i])) {
45603
45734
  return true;
45604
45735
  }
45605
45736
  }
@@ -45611,6 +45742,17 @@ class EmitterGroupDesc extends RenderDesc {
45611
45742
  this.lowerBound = other.lowerBound;
45612
45743
  this.higherBound = other.higherBound;
45613
45744
  this.emitterDir = other.emitterDir;
45745
+ for (let i = 0; i < this.emitterDescs.length; i++) {
45746
+ this.emitterDescs[i].fromEmitterDesc(other.emitterDescs[i]);
45747
+ }
45748
+ }
45749
+ virtualTransferFrom(other) {
45750
+ if (this.emitterDescs.length === other.emitterDescs.length) {
45751
+ for (let i = 0; i < this.emitterDescs.length; i++) {
45752
+ this.emitterDescs[i].particles = other.emitterDescs[i].particles;
45753
+ this.emitterDescs[i].initialParticleCount = this.emitterDescs[i].particles.length;
45754
+ }
45755
+ }
45614
45756
  }
45615
45757
  fromInstance(child) {
45616
45758
  this.instance = child;
@@ -46897,6 +47039,11 @@ class AnimatorWrapper extends InstanceWrapper {
46897
47039
  this._switchToolAnimation(this.data.currentToolAnimation);
46898
47040
  }
46899
47041
  }
47042
+ /**
47043
+ * Resets all joints in the rig to be in their rest pose
47044
+ * @param includeMotors If motors (body movement joints) should be set to rest pose
47045
+ * @param includeFACS If FACS (face movement bones) should be set to rest pose
47046
+ */
46900
47047
  restPose(includeMotors = true, includeFACS = true) {
46901
47048
  const rig = this.instance.parent?.parent;
46902
47049
  if (!rig) {
@@ -46916,6 +47063,12 @@ class AnimatorWrapper extends InstanceWrapper {
46916
47063
  }
46917
47064
  }
46918
47065
  }
47066
+ /**
47067
+ * Renders animation pose
47068
+ * @param addTime Time to add to the current time
47069
+ * @param forceTime Time to force animation to be at, -1 is in the middle of the animation
47070
+ * @param forceKeyframe Keyframe to force animation to be at
47071
+ */
46919
47072
  renderAnimation(addTime = 1 / 60, forceTime, forceKeyframe) {
46920
47073
  const humanoid = this.instance.parent;
46921
47074
  if (!humanoid) {
@@ -46988,11 +47141,22 @@ class AnimatorWrapper extends InstanceWrapper {
46988
47141
  }
46989
47142
  }
46990
47143
  }
47144
+ /**
47145
+ *
47146
+ * @returns Currently playing animation track
47147
+ */
46991
47148
  getCurrentAnimationTrack() {
46992
47149
  if (this.data.currentAnimationTrack) {
46993
47150
  return this.data.currentAnimationTrack;
46994
47151
  }
46995
47152
  }
47153
+ /**
47154
+ * Loads a new animation
47155
+ * @param id
47156
+ * @param isEmote
47157
+ * @param forceLoop Forces animation track to loop
47158
+ * @returns undefined on success
47159
+ */
46996
47160
  async loadAvatarAnimation(id, isEmote = false, forceLoop = false) {
46997
47161
  const humanoid = this.instance.parent;
46998
47162
  if (!humanoid) {
@@ -47103,6 +47267,12 @@ class AnimatorWrapper extends InstanceWrapper {
47103
47267
  }
47104
47268
  }
47105
47269
  }
47270
+ /**
47271
+ * Switches to new animation
47272
+ * @param name Animation name, such as "idle", "walk" or "emote.1234"
47273
+ * @param type
47274
+ * @returns If animation sucessfully played
47275
+ */
47106
47276
  playAnimation(name, type = "main") {
47107
47277
  const humanoid = this.instance.parent;
47108
47278
  if (!humanoid) {
@@ -47753,6 +47923,11 @@ class HumanoidDescriptionWrapper extends InstanceWrapper {
47753
47923
  return void 0;
47754
47924
  }
47755
47925
  }
47926
+ /**
47927
+ * Update the data of the HumanoidDescription to match that inside an Outfit
47928
+ * @param outfit
47929
+ * @returns HumanoidDescription or Response if it fails
47930
+ */
47756
47931
  async fromOutfit(outfit) {
47757
47932
  this.instance.setProperty("BodyTypeScale", outfit.scale.bodyType);
47758
47933
  this.instance.setProperty("ProportionScale", outfit.scale.proportion);
@@ -49161,14 +49336,17 @@ class RBXRendererScene {
49161
49336
  ambientLight;
49162
49337
  directionalLight;
49163
49338
  directionalLight2;
49339
+ /** Forces viewport to be within bounds */
49164
49340
  setRect(bounds) {
49165
49341
  this.viewport = [bounds.left, window.innerHeight - bounds.bottom, bounds.width, bounds.height];
49166
49342
  this.scissor = [...this.viewport];
49167
49343
  }
49344
+ /** Makes viewport size 0x0, invisible */
49168
49345
  noRect() {
49169
49346
  this.viewport = [0, 0, 0, 0];
49170
49347
  this.scissor = [0, 0, 0, 0];
49171
49348
  }
49349
+ /** Destroys all renderDescs but does not call Destroy on instances */
49172
49350
  destroy() {
49173
49351
  if (this.destroyed) return;
49174
49352
  this.destroyed = true;
@@ -49185,6 +49363,12 @@ class RBXRendererScene {
49185
49363
  this.shadowPlane = void 0;
49186
49364
  }
49187
49365
  }
49366
+ /**
49367
+ * Exports scene as a GLTF or GLB
49368
+ * @param name Name of the resulting file if autoDownload is true
49369
+ * @param autoDownload If resulting file should be auto downloaded
49370
+ * @returns The GLB (buffer) or GLTF (object)
49371
+ */
49188
49372
  async exportGLTF(name = "scene", autoDownload = true) {
49189
49373
  return new Promise((resolve, reject) => {
49190
49374
  const exporter = new GLTFExporter();
@@ -49667,6 +49851,7 @@ class RBXRenderer {
49667
49851
  }
49668
49852
  } else {
49669
49853
  if (!renderScene.isRenderingMesh.get(instance)) {
49854
+ if (oldDesc) newDesc.transferFrom(oldDesc);
49670
49855
  newDesc.results = oldDesc?.results;
49671
49856
  renderScene.renderDescs.set(instance, newDesc);
49672
49857
  renderScene.isRenderingMesh.set(instance, true);
@@ -49793,6 +49978,9 @@ class RBXRenderer {
49793
49978
  static getRendererControls() {
49794
49979
  return RBXRenderer.controls;
49795
49980
  }
49981
+ /**
49982
+ * @returns ThreeJS renderer
49983
+ */
49796
49984
  static getRenderer() {
49797
49985
  return RBXRenderer.renderer;
49798
49986
  }
@@ -49800,7 +49988,8 @@ class RBXRenderer {
49800
49988
  static getScene() {
49801
49989
  return RBXRenderer.scene;
49802
49990
  }
49803
- /**@deprecated
49991
+ /**
49992
+ * @deprecated Superseded by RBXRendererScene.exportGLTF()
49804
49993
  * This function is unstable and can throw errors, but might work
49805
49994
  */
49806
49995
  static exportScene(renderScene = RBXRenderer.firstScene) {
@@ -50096,6 +50285,7 @@ class OutfitRenderer {
50096
50285
  currentlyChangingRig = false;
50097
50286
  currentlyUpdating = false;
50098
50287
  hasNewUpdate = false;
50288
+ _queuedMainAnimation;
50099
50289
  lastFrameTime = Date.now() / 100;
50100
50290
  animationInterval;
50101
50291
  animationFPS = 60;
@@ -50162,6 +50352,10 @@ class OutfitRenderer {
50162
50352
  const humanoid = this.currentRig.FindFirstChildOfClass("Humanoid");
50163
50353
  if (humanoid) {
50164
50354
  hrpWrapper.applyDescription(humanoid).then((result) => {
50355
+ if (this._queuedMainAnimation) {
50356
+ this.setMainAnimation(this._queuedMainAnimation);
50357
+ this._queuedMainAnimation = void 0;
50358
+ }
50165
50359
  this.currentlyUpdating = false;
50166
50360
  if (this.currentRig) {
50167
50361
  RBXRenderer.addInstance(this.currentRig, this.auth, this.renderScene);
@@ -50268,6 +50462,8 @@ class OutfitRenderer {
50268
50462
  }
50269
50463
  }
50270
50464
  }
50465
+ } else {
50466
+ this._queuedMainAnimation = name;
50271
50467
  }
50272
50468
  }
50273
50469
  }
@@ -50463,10 +50659,12 @@ export {
50463
50659
  FindFirstMatchingAttachment,
50464
50660
  FullBodyColors,
50465
50661
  GetAttachedPart,
50662
+ GetWrapperForInstance,
50466
50663
  HSR,
50467
50664
  HumanoidDescriptionWrapper,
50468
50665
  HumanoidRigType,
50469
50666
  Instance,
50667
+ InstanceWrapper,
50470
50668
  ItemInfo,
50471
50669
  ItemSort,
50472
50670
  LayeredAssetTypes,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roavatar-renderer",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "A renderer for Roblox avatars, used by the RoAvatar extension.",
5
5
  "author": "steinan",
6
6
  "type": "module",
@@ -46,6 +46,7 @@
46
46
  "eslint-plugin-react-hooks": "^5.2.0",
47
47
  "eslint-plugin-react-refresh": "^0.4.22",
48
48
  "globals": "^16.4.0",
49
+ "typedoc": "^0.28.19",
49
50
  "typescript": "~5.9.3",
50
51
  "typescript-eslint": "^8.45.0",
51
52
  "vite": "^7.1.7",