roavatar-renderer 1.5.11 → 1.5.13

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.d.ts CHANGED
@@ -341,6 +341,7 @@ export declare const API: {
341
341
  */
342
342
  GetRBX: (url: string, headers?: HeadersInit, contentRepresentationPriorityList?: any) => Promise<Response | RBX>;
343
343
  GetMesh: (url: string, headers?: HeadersInit, readOnly?: boolean) => Promise<FileMesh | Response>;
344
+ GetAssetTypeId: (assetId: string | number) => Promise<number | Response>;
344
345
  IsLayered: (id: number) => Promise<boolean | Response>;
345
346
  };
346
347
  Catalog: {
@@ -1439,12 +1440,16 @@ export declare const FLAGS: {
1439
1440
  AUTO_RESTORE_CONTEXT: boolean;
1440
1441
  /**RenderTarget textures are converted to CanvasTextures which can be exported */
1441
1442
  RENDERTARGET_TO_CANVASTEXTURE: boolean;
1442
- /**Amount of time thumbnail generator will wait after no assets are being loaded to resolve, should be a little time at least so particles can render */
1443
+ /**Amount of time thumbnail generator will wait after no assets are being loaded to resolve, should be a little time at least so particles can render OR just set PARTICLES_START_FULL = true */
1443
1444
  THUMBNAIL_TIMEOUT: number;
1444
1445
  /**Always render attachments even when theyre set to not be visible */
1445
1446
  ALWAYS_SHOW_ATTACHMENTS: boolean;
1446
1447
  /**Multiplier for delta time used by renderer things (does not affect datamodel side of things but particles and such instead) */
1447
1448
  RENDERER_DELTA_TIME_MULTIPLIER: number;
1449
+ /**How many seconds particles will presimulate (mainly so they arent incomplete in thumbnail generations) */
1450
+ PARTICLES_START_FULL: number;
1451
+ /**Framerate of presimulation particles */
1452
+ PARTICLES_START_FULL_FRAMERATE: number;
1448
1453
  /**shows ThreeJS SkeletonHelper */
1449
1454
  SHOW_SKELETON_HELPER: boolean;
1450
1455
  /**if set the skeleton helper will only show if the instance has the name */
@@ -1565,9 +1570,12 @@ export declare function generateModelThumbnail(auth: Authentication, renderScene
1565
1570
  * **Example on generating 1000x1000 webp thumbnail for blank outfit**
1566
1571
  * ```ts
1567
1572
  * const outfit = new Outfit()
1573
+ * FLAGS.THUMBNAIL_TIMEOUT = 1 //makes thumbnail generation faster
1574
+ * FLAGS.PARTICLES_START_FULL = 10 //makes emitters appear as if theyve been there for some time (10 seconds)
1568
1575
  * FLAGS.RENDERTARGET_TO_CANVASTEXTURE = true //required for gltf export
1569
1576
  * const result = await generateOutfitThumbnail(new Authentication(), outfit, [1000,1000], "webp", 0.99)
1570
1577
  * FLAGS.RENDERTARGET_TO_CANVASTEXTURE = false
1578
+ * FLAGS.PARTICLES_START_FULL = 0
1571
1579
  * console.log(result)
1572
1580
  * ```
1573
1581
  */
@@ -2718,6 +2726,7 @@ export declare class OutfitRenderer {
2718
2726
  currentRigType: AvatarType;
2719
2727
  doCameraUpdateOnLoad: boolean; /**Makes camera update when new avatar has loaded */
2720
2728
  doCameraUpdate: boolean; /**Does camera update every frame */
2729
+ doAddInstance: boolean; /**If outfitRenderer should call RBXRenderer.addInstance() */
2721
2730
  currentlyChangingRig: boolean;
2722
2731
  currentlyUpdating: boolean;
2723
2732
  hasNewUpdate: boolean;
@@ -2762,6 +2771,10 @@ export declare class OutfitRenderer {
2762
2771
  * Starts updating the animation of the outfit per frame
2763
2772
  */
2764
2773
  startAnimating(): void;
2774
+ /**
2775
+ * Updates the animation once
2776
+ */
2777
+ animateOnce(deltaTimeOverride?: number): void;
2765
2778
  /**
2766
2779
  * Stops updating the animation of the outfit per frame
2767
2780
  */
@@ -2769,8 +2782,9 @@ export declare class OutfitRenderer {
2769
2782
  /**
2770
2783
  * Sets the current animation being played
2771
2784
  * @param name The name of the animation, for example "idle", "run" or "emote.1234"
2785
+ * @returns If the animation started playing, it may start playing later if it has been queued despite returning false
2772
2786
  */
2773
- setMainAnimation(name: string): void;
2787
+ setMainAnimation(name: string): Promise<boolean>;
2774
2788
  /**Calls destroy on the rig and stops animating, the OutfitRenderer should not be interacted with after this */
2775
2789
  destroy(): void;
2776
2790
  }
package/dist/index.js CHANGED
@@ -29864,6 +29864,8 @@ const FLAGS = {
29864
29864
  THUMBNAIL_TIMEOUT: 500,
29865
29865
  ALWAYS_SHOW_ATTACHMENTS: false,
29866
29866
  RENDERER_DELTA_TIME_MULTIPLIER: 1,
29867
+ PARTICLES_START_FULL: 0,
29868
+ PARTICLES_START_FULL_FRAMERATE: 20,
29867
29869
  //skeleton
29868
29870
  SHOW_SKELETON_HELPER: false,
29869
29871
  SKELETON_HELPER_INSTANCE_NAME: void 0,
@@ -33766,6 +33768,27 @@ class Outfit {
33766
33768
  }
33767
33769
  }
33768
33770
  await Promise.all(assetPromises);
33771
+ const assetDeliveryPromises = [];
33772
+ for (const asset of assetsToAdd) {
33773
+ const assetId = asset.id;
33774
+ if (assetId && !this.getAssetId(assetId)) {
33775
+ assetDeliveryPromises.push(new Promise((resolve) => {
33776
+ API.Asset.GetAssetTypeId(assetId).then((assetTypeId) => {
33777
+ if (!(assetTypeId instanceof Response)) {
33778
+ if (Number(assetTypeId) === assetTypeId) {
33779
+ this.addAsset(assetId, assetTypeId, `${AssetTypes[assetTypeId] || "Asset"} (${assetId})`);
33780
+ resolve(void 0);
33781
+ } else {
33782
+ resolve(void 0);
33783
+ }
33784
+ } else {
33785
+ resolve(void 0);
33786
+ }
33787
+ });
33788
+ }));
33789
+ }
33790
+ }
33791
+ await Promise.all(assetDeliveryPromises);
33769
33792
  for (const assetToAdd of assetsToAdd) {
33770
33793
  let asset = void 0;
33771
33794
  for (const assetIn of this.assets) {
@@ -36348,6 +36371,14 @@ const API = {
36348
36371
  }
36349
36372
  }
36350
36373
  },
36374
+ GetAssetTypeId: async function(assetId) {
36375
+ const response = await RBLXGet("https://assetdelivery.roblox.com/v2/asset?id=" + assetId);
36376
+ if (response.status !== 200) {
36377
+ return response;
36378
+ }
36379
+ const body = await response.json();
36380
+ return body.assetTypeId;
36381
+ },
36351
36382
  IsLayered: async function(id) {
36352
36383
  const cached = CACHE.IsLayered.get(id);
36353
36384
  if (cached !== void 0) {
@@ -50278,6 +50309,14 @@ class EmitterGroupDesc extends RenderDesc {
50278
50309
  this.disposeMeshes(scene, originalResults);
50279
50310
  this.disposeRenderLists(renderer);
50280
50311
  }
50312
+ if (FLAGS.PARTICLES_START_FULL) {
50313
+ for (const emitterDesc of this.emitterDescs) {
50314
+ for (let i = 0; i < Math.min(emitterDesc.lifetime.Max * FLAGS.PARTICLES_START_FULL_FRAMERATE, FLAGS.PARTICLES_START_FULL * FLAGS.PARTICLES_START_FULL_FRAMERATE); i++) {
50315
+ emitterDesc.tick(1 / FLAGS.PARTICLES_START_FULL_FRAMERATE, this);
50316
+ emitterDesc.updateResult(this.renderScene);
50317
+ }
50318
+ }
50319
+ }
50281
50320
  return this.results;
50282
50321
  }
50283
50322
  updateResults() {
@@ -51484,6 +51523,8 @@ class OutfitRenderer {
51484
51523
  /**Makes camera update when new avatar has loaded */
51485
51524
  doCameraUpdate = false;
51486
51525
  /**Does camera update every frame */
51526
+ doAddInstance = true;
51527
+ /**If outfitRenderer should call RBXRenderer.addInstance() */
51487
51528
  currentlyChangingRig = false;
51488
51529
  currentlyUpdating = false;
51489
51530
  hasNewUpdate = false;
@@ -51534,7 +51575,7 @@ class OutfitRenderer {
51534
51575
  dataModel.Destroy();
51535
51576
  this.currentRig = newRig;
51536
51577
  this.currentlyChangingRig = false;
51537
- RBXRenderer.addInstance(this.currentRig, this.auth, this.renderScene);
51578
+ if (this.doAddInstance) RBXRenderer.addInstance(this.currentRig, this.auth, this.renderScene);
51538
51579
  resolve(newRig);
51539
51580
  } else {
51540
51581
  this.onError.Fire("rig");
@@ -51572,7 +51613,7 @@ class OutfitRenderer {
51572
51613
  }
51573
51614
  this.currentlyUpdating = false;
51574
51615
  if (this.currentRig) {
51575
- RBXRenderer.addInstance(this.currentRig, this.auth, this.renderScene);
51616
+ if (this.doAddInstance) RBXRenderer.addInstance(this.currentRig, this.auth, this.renderScene);
51576
51617
  if (this.doCameraUpdateOnLoad) {
51577
51618
  this.centerCamera();
51578
51619
  }
@@ -51590,9 +51631,11 @@ class OutfitRenderer {
51590
51631
  }
51591
51632
  });
51592
51633
  } else {
51634
+ this.onError.Fire("rig");
51593
51635
  this.currentlyUpdating = false;
51594
51636
  }
51595
51637
  } else {
51638
+ this.onError.Fire("rig");
51596
51639
  this.currentlyUpdating = false;
51597
51640
  }
51598
51641
  });
@@ -51633,21 +51676,27 @@ class OutfitRenderer {
51633
51676
  if (this.currentRig && this.doCameraUpdate) {
51634
51677
  this.centerCamera();
51635
51678
  }
51636
- if (this.currentRig && this.auth) {
51637
- const humanoid = this.currentRig.FindFirstChildOfClass("Humanoid");
51638
- if (humanoid) {
51639
- const animator = humanoid.FindFirstChildOfClass("Animator");
51640
- if (animator) {
51641
- const deltaTime = (Date.now() / 1e3 - this.lastFrameTime) * this.deltaTimeMultiplier;
51642
- this.lastFrameTime = Date.now() / 1e3;
51643
- const animatorW = new AnimatorWrapper(animator);
51644
- animatorW.renderAnimation(deltaTime);
51645
- this.currentRig.preRender();
51646
- RBXRenderer.addInstance(this.currentRig, this.auth, this.renderScene);
51647
- }
51679
+ this.animateOnce();
51680
+ }, 1e3 / this.animationFPS);
51681
+ }
51682
+ /**
51683
+ * Updates the animation once
51684
+ */
51685
+ animateOnce(deltaTimeOverride) {
51686
+ if (this.currentRig && this.auth) {
51687
+ const humanoid = this.currentRig.FindFirstChildOfClass("Humanoid");
51688
+ if (humanoid) {
51689
+ const animator = humanoid.FindFirstChildOfClass("Animator");
51690
+ if (animator) {
51691
+ const deltaTime = deltaTimeOverride || (Date.now() / 1e3 - this.lastFrameTime) * this.deltaTimeMultiplier;
51692
+ this.lastFrameTime = Date.now() / 1e3;
51693
+ const animatorW = new AnimatorWrapper(animator);
51694
+ animatorW.renderAnimation(deltaTime);
51695
+ this.currentRig.preRender();
51696
+ if (this.doAddInstance) RBXRenderer.addInstance(this.currentRig, this.auth, this.renderScene);
51648
51697
  }
51649
51698
  }
51650
- }, 1e3 / this.animationFPS);
51699
+ }
51651
51700
  }
51652
51701
  /**
51653
51702
  * Stops updating the animation of the outfit per frame
@@ -51661,26 +51710,36 @@ class OutfitRenderer {
51661
51710
  /**
51662
51711
  * Sets the current animation being played
51663
51712
  * @param name The name of the animation, for example "idle", "run" or "emote.1234"
51713
+ * @returns If the animation started playing, it may start playing later if it has been queued despite returning false
51664
51714
  */
51665
51715
  setMainAnimation(name) {
51666
- if (this.currentRig) {
51667
- const humanoid = this.currentRig.FindFirstChildOfClass("Humanoid");
51668
- if (humanoid) {
51669
- const animator = humanoid.FindFirstChildOfClass("Animator");
51670
- if (animator) {
51671
- const animatorW = new AnimatorWrapper(animator);
51672
- const successfullyPlayed = animatorW.playAnimation(name);
51673
- if (!successfullyPlayed && name.startsWith("emote.") && name) {
51674
- const emoteId = BigInt(name.split(".")[1]);
51675
- animatorW.loadAvatarAnimation(emoteId, true, true).then(() => {
51676
- animatorW.playAnimation(name);
51677
- });
51716
+ return new Promise((resolve) => {
51717
+ if (this.currentRig) {
51718
+ const humanoid = this.currentRig.FindFirstChildOfClass("Humanoid");
51719
+ if (humanoid) {
51720
+ const animator = humanoid.FindFirstChildOfClass("Animator");
51721
+ if (animator) {
51722
+ const animatorW = new AnimatorWrapper(animator);
51723
+ const successfullyPlayed = animatorW.playAnimation(name);
51724
+ if (!successfullyPlayed && name.startsWith("emote.") && name) {
51725
+ const emoteId = BigInt(name.split(".")[1]);
51726
+ animatorW.loadAvatarAnimation(emoteId, true, true).then(() => {
51727
+ resolve(animatorW.playAnimation(name));
51728
+ });
51729
+ } else {
51730
+ resolve(false);
51731
+ }
51732
+ } else {
51733
+ resolve(false);
51678
51734
  }
51735
+ } else {
51736
+ resolve(false);
51679
51737
  }
51738
+ } else {
51739
+ this._queuedMainAnimation = name;
51740
+ resolve(false);
51680
51741
  }
51681
- } else {
51682
- this._queuedMainAnimation = name;
51683
- }
51742
+ });
51684
51743
  }
51685
51744
  /**Calls destroy on the rig and stops animating, the OutfitRenderer should not be interacted with after this */
51686
51745
  destroy() {
@@ -51755,15 +51814,26 @@ async function generateOutfitThumbnail(auth, outfit, size = [150, 150], type = "
51755
51814
  const renderScene = RBXRenderer.addScene();
51756
51815
  setupThumbnailScene(renderScene);
51757
51816
  const outfitRenderer = new OutfitRenderer(auth, outfit, renderScene);
51817
+ outfitRenderer.doAddInstance = false;
51758
51818
  if (outfit.playerAvatarType === AvatarType.R6) outfitRenderer.deltaTimeMultiplier = 0;
51759
- outfitRenderer.startAnimating();
51760
- if (!outfit.containsAssetType("Gear")) {
51761
- if (outfit.playerAvatarType === AvatarType.R15) {
51762
- outfitRenderer.setMainAnimation("pose");
51819
+ API.Misc.startCurrentlyLoadingAssets("thumbnail-animation");
51820
+ outfitRenderer.onSuccess.Connect(() => {
51821
+ if (!outfit.containsAssetType("Gear")) {
51822
+ if (outfit.playerAvatarType === AvatarType.R15) {
51823
+ outfitRenderer.setMainAnimation("pose").then(() => {
51824
+ API.Misc.stopCurrentlyLoadingAssets("thumbnail-animation");
51825
+ });
51826
+ } else {
51827
+ outfitRenderer.setMainAnimation("idle").then(() => {
51828
+ API.Misc.stopCurrentlyLoadingAssets("thumbnail-animation");
51829
+ });
51830
+ }
51831
+ } else {
51832
+ outfitRenderer.setMainAnimation("toolnone").then(() => {
51833
+ API.Misc.stopCurrentlyLoadingAssets("thumbnail-animation");
51834
+ });
51763
51835
  }
51764
- } else {
51765
- outfitRenderer.setMainAnimation("toolnone");
51766
- }
51836
+ });
51767
51837
  let exportTimeout = !API.Misc.getCurrentlyLoading() ? setTimeout(doExport, FLAGS.THUMBNAIL_TIMEOUT) : void 0;
51768
51838
  const onLoadingConnection = API.Events.OnLoadingAssets.Connect((currentlyLoading) => {
51769
51839
  if (exportTimeout) {
@@ -51777,10 +51847,10 @@ async function generateOutfitThumbnail(auth, outfit, size = [150, 150], type = "
51777
51847
  async function doExport() {
51778
51848
  onLoadingConnection.Disconnect();
51779
51849
  if (outfitRenderer.currentRig) {
51850
+ outfitRenderer.animateOnce(!outfit.containsAssetType("Gear") && outfit.playerAvatarType === AvatarType.R6 ? 0 : 1);
51780
51851
  const thumbnailResult = await generateModelThumbnail(auth, renderScene, outfitRenderer.currentRig, size, type, quality, gltfAutoDownload);
51781
51852
  console.log("Generated outfit thumbnail after seconds:", (performance.now() - startTime) / 1e3);
51782
51853
  resolve(thumbnailResult);
51783
- outfitRenderer.stopAnimating();
51784
51854
  if (outfitRenderer.currentRig) outfitRenderer.currentRig.Destroy();
51785
51855
  } else {
51786
51856
  resolve(void 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roavatar-renderer",
3
- "version": "1.5.11",
3
+ "version": "1.5.13",
4
4
  "description": "A renderer for Roblox avatars, used by the RoAvatar extension.",
5
5
  "author": "steinan",
6
6
  "type": "module",