roavatar-renderer 1.5.18 → 1.5.19
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 +148 -19
- package/dist/index.js +504 -299
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -653,7 +653,7 @@ export declare interface AvatarModel_Result {
|
|
|
653
653
|
avatarConfigurations: AvatarConfigurations | null;
|
|
654
654
|
}
|
|
655
655
|
|
|
656
|
-
export declare type AvatarModelUpdateType = "UpdateName" | "UpdateBodyColors" | "UpdateAssets" | "UpdateScales" | "UpdateBackground";
|
|
656
|
+
export declare type AvatarModelUpdateType = "UpdateName" | "UpdateBodyColors" | "UpdateAssets" | "UpdateScales" | "UpdateBackground" | "UpdateAvatarType";
|
|
657
657
|
|
|
658
658
|
/** @category Outfit */
|
|
659
659
|
export declare type AvatarType = "R6" | "R15";
|
|
@@ -1493,9 +1493,15 @@ export declare const FLAGS: {
|
|
|
1493
1493
|
ALWAYS_SHOW_ATTACHMENTS: boolean;
|
|
1494
1494
|
/**Multiplier for delta time used by renderer things (does not affect datamodel side of things but particles and such instead) */
|
|
1495
1495
|
RENDERER_DELTA_TIME_MULTIPLIER: number;
|
|
1496
|
-
/**
|
|
1496
|
+
/**
|
|
1497
|
+
* How many seconds particles will presimulate (mainly so they arent incomplete in thumbnail generations)
|
|
1498
|
+
* @deprecated This can now be set per-scene
|
|
1499
|
+
* */
|
|
1497
1500
|
PARTICLES_START_FULL: number;
|
|
1498
|
-
/**
|
|
1501
|
+
/**
|
|
1502
|
+
* Framerate of presimulation particles
|
|
1503
|
+
* @deprecated This can now be set per-scene
|
|
1504
|
+
* */
|
|
1499
1505
|
PARTICLES_START_FULL_FRAMERATE: number;
|
|
1500
1506
|
/**shows ThreeJS SkeletonHelper */
|
|
1501
1507
|
SHOW_SKELETON_HELPER: boolean;
|
|
@@ -1621,12 +1627,9 @@ export declare function generateOutfitModelThumbnail(auth: Authentication, outfi
|
|
|
1621
1627
|
* **Example on generating 1000x1000 webp thumbnail for blank outfit**
|
|
1622
1628
|
* ```ts
|
|
1623
1629
|
* const outfit = new Outfit()
|
|
1624
|
-
* FLAGS.THUMBNAIL_TIMEOUT = 1 //makes thumbnail generation faster
|
|
1625
|
-
* FLAGS.PARTICLES_START_FULL = 10 //makes emitters appear as if theyve been there for some time (10 seconds)
|
|
1626
1630
|
* FLAGS.RENDERTARGET_TO_CANVASTEXTURE = true //required for gltf export
|
|
1627
1631
|
* const result = await generateOutfitThumbnail(new Authentication(), outfit, [1000,1000], "webp", 0.99)
|
|
1628
1632
|
* FLAGS.RENDERTARGET_TO_CANVASTEXTURE = false
|
|
1629
|
-
* FLAGS.PARTICLES_START_FULL = 0
|
|
1630
1633
|
* console.log(result)
|
|
1631
1634
|
* ```
|
|
1632
1635
|
*/
|
|
@@ -1661,21 +1664,20 @@ export declare function getCameraCFrameForHeadshotCustomized(rig: Instance, fov:
|
|
|
1661
1664
|
export declare function getDistIndexArray(ref: FileMesh, dist: FileMesh): (number | undefined)[];
|
|
1662
1665
|
|
|
1663
1666
|
/**
|
|
1664
|
-
* @deprecated Use new Thumbnails category instead
|
|
1665
1667
|
* @category ThumbnailGenerator */
|
|
1666
1668
|
export declare function getExtents(cframe: CFrame, parts: Instance[]): [Vector3, Vector3];
|
|
1667
1669
|
|
|
1668
1670
|
/**
|
|
1669
|
-
* @deprecated Use new Thumbnails category instead
|
|
1670
1671
|
* @category ThumbnailGenerator */
|
|
1671
1672
|
export declare function getExtentsCenter(extents: [Vector3, Vector3]): Vector3;
|
|
1672
1673
|
|
|
1673
1674
|
/**
|
|
1674
|
-
* @deprecated Use new Thumbnails category instead
|
|
1675
1675
|
* @category ThumbnailGenerator
|
|
1676
1676
|
* */
|
|
1677
1677
|
export declare function getExtentsForParts(parts: Instance[], includeTransform?: boolean): [Vector3, Vector3];
|
|
1678
1678
|
|
|
1679
|
+
export declare function getExtentsWorld(rig: Instance): [Vector3, Vector3];
|
|
1680
|
+
|
|
1679
1681
|
export declare function getHeadExtents(rig: Instance): [Vector3, Vector3] | undefined;
|
|
1680
1682
|
|
|
1681
1683
|
/**@category API */
|
|
@@ -1707,8 +1709,6 @@ export declare function getPartsInAssembly_Generate(part: Instance): Instance[];
|
|
|
1707
1709
|
|
|
1708
1710
|
export declare function getRandomBetweenInclusive(min: number, max: number, randomVal?: number): number;
|
|
1709
1711
|
|
|
1710
|
-
export declare function getRigExtentsWorld(rig: Instance): [Vector3, Vector3];
|
|
1711
|
-
|
|
1712
1712
|
export declare function getRootAssemblyPart_Generate(part: Instance): Instance;
|
|
1713
1713
|
|
|
1714
1714
|
/**@category API */
|
|
@@ -1731,7 +1731,6 @@ export declare interface GetSubscription_Result {
|
|
|
1731
1731
|
* Calculates the CFrame the camera should be at when generating a thumbnail
|
|
1732
1732
|
* @param model The model-like instance to get thumbnail camera for
|
|
1733
1733
|
* @returns Thumbnail camera cframe
|
|
1734
|
-
* @deprecated Use new Thumbnails category instead
|
|
1735
1734
|
* @category ThumbnailGenerator
|
|
1736
1735
|
*/
|
|
1737
1736
|
export declare function getThumbnailCameraCFrame(model: Instance, fov: number, forceAngle?: Vec3): CFrame | undefined;
|
|
@@ -1921,6 +1920,31 @@ export declare const HumanoidRigType: {
|
|
|
1921
1920
|
R15: number;
|
|
1922
1921
|
};
|
|
1923
1922
|
|
|
1923
|
+
/**
|
|
1924
|
+
* Renders a scene to an image and returns a data url
|
|
1925
|
+
* @param renderScene Scene that will be rendered
|
|
1926
|
+
* @param width Width in pixels
|
|
1927
|
+
* @param height Height in pixels
|
|
1928
|
+
* @param format Type of image
|
|
1929
|
+
* @param options
|
|
1930
|
+
* @returns data url
|
|
1931
|
+
*
|
|
1932
|
+
* @category ThumbnailGenerator
|
|
1933
|
+
*/
|
|
1934
|
+
export declare function imageThumbnailClick(renderScene: RBXRendererScene, width: number, height: number, format: ImageThumbnailFormat, options?: Partial<ImageThumbnailOptions>): Promise<string | undefined>;
|
|
1935
|
+
|
|
1936
|
+
/**
|
|
1937
|
+
* @category ThumbnailGenerator
|
|
1938
|
+
*/
|
|
1939
|
+
export declare type ImageThumbnailFormat = "png" | "webp" | "jpeg";
|
|
1940
|
+
|
|
1941
|
+
/**
|
|
1942
|
+
* @category ThumbnailGenerator
|
|
1943
|
+
*/
|
|
1944
|
+
export declare interface ImageThumbnailOptions {
|
|
1945
|
+
quality: number;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1924
1948
|
export declare function imageUrlToDataUrl(imageUrl: string): Promise<string>;
|
|
1925
1949
|
|
|
1926
1950
|
/**
|
|
@@ -2476,6 +2500,31 @@ export declare const minimumDeltaEBodyColorDifference = 11.4;
|
|
|
2476
2500
|
*/
|
|
2477
2501
|
export declare function minus(v0: Vec3, v1: Vec3): Vec3;
|
|
2478
2502
|
|
|
2503
|
+
/**
|
|
2504
|
+
* Generates a 3D model from the scene
|
|
2505
|
+
* @param renderScene Scene that will be rendered
|
|
2506
|
+
* @param format Format of resulting model
|
|
2507
|
+
* @param options
|
|
2508
|
+
* @returns ArrayBuffer for glb or Object for gltf
|
|
2509
|
+
*
|
|
2510
|
+
* @category ThumbnailGenerator
|
|
2511
|
+
*/
|
|
2512
|
+
export declare function modelThumbnailClick(renderScene: RBXRendererScene, format: ModelThumbnailFormat, options?: Partial<ModelThumbnailOptions>): Promise<ArrayBuffer | {
|
|
2513
|
+
[key: string]: unknown;
|
|
2514
|
+
}>;
|
|
2515
|
+
|
|
2516
|
+
/**
|
|
2517
|
+
* @category ThumbnailGenerator
|
|
2518
|
+
*/
|
|
2519
|
+
export declare type ModelThumbnailFormat = "gltf" | "glb";
|
|
2520
|
+
|
|
2521
|
+
/**
|
|
2522
|
+
* @category ThumbnailGenerator
|
|
2523
|
+
*/
|
|
2524
|
+
export declare interface ModelThumbnailOptions {
|
|
2525
|
+
includeAnimations: boolean;
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2479
2528
|
/**
|
|
2480
2529
|
* @category InstanceWrapper
|
|
2481
2530
|
*/
|
|
@@ -2811,6 +2860,37 @@ export declare const OutfitOrigin: {
|
|
|
2811
2860
|
|
|
2812
2861
|
/**
|
|
2813
2862
|
* @category Renderer
|
|
2863
|
+
* @example
|
|
2864
|
+
* **Example on usage**
|
|
2865
|
+
* ```ts
|
|
2866
|
+
* const auth = new Authentication()
|
|
2867
|
+
* const outfit = new Outfit()
|
|
2868
|
+
* const renderScene = RBXRenderer.addScene()
|
|
2869
|
+
*
|
|
2870
|
+
* const outfitRenderer = new OutfitRenderer(auth, outfit, renderScene)
|
|
2871
|
+
* outfitRenderer.startAnimating()
|
|
2872
|
+
* outfitRenderer.setMainAnimation("anim.1234") //animation with id 1234, check documentation for other possible values
|
|
2873
|
+
*
|
|
2874
|
+
* outfitRenderer.onRenderSuccess.Connect(() => {
|
|
2875
|
+
* //handle Instance tree + renderDesc being successfuly
|
|
2876
|
+
* })
|
|
2877
|
+
* outfitRenderer.onError.Connect(() => {
|
|
2878
|
+
* //handle Instance tree error
|
|
2879
|
+
* })
|
|
2880
|
+
* outfitRenderer.onRenderError.Connect(() => {
|
|
2881
|
+
* //handle renderDesc error
|
|
2882
|
+
* })
|
|
2883
|
+
*
|
|
2884
|
+
* //onRenderSuccess is fired once after some time
|
|
2885
|
+
*
|
|
2886
|
+
* outfitRenderer.setOutfit(new Outfit())
|
|
2887
|
+
*
|
|
2888
|
+
* //onRenderSuccess is fired again once after some time
|
|
2889
|
+
*
|
|
2890
|
+
* //when we're done rendering
|
|
2891
|
+
* outfitRenderer.destroy()
|
|
2892
|
+
* renderScene.destroy()
|
|
2893
|
+
* ```
|
|
2814
2894
|
*/
|
|
2815
2895
|
export declare class OutfitRenderer {
|
|
2816
2896
|
auth: Authentication;
|
|
@@ -2820,6 +2900,7 @@ export declare class OutfitRenderer {
|
|
|
2820
2900
|
doCameraUpdateOnLoad: boolean; /**Makes camera update when new avatar has loaded */
|
|
2821
2901
|
doCameraUpdate: boolean; /**Does camera update every frame */
|
|
2822
2902
|
doAddInstance: boolean; /**If outfitRenderer should call RBXRenderer.addInstance() */
|
|
2903
|
+
forceAnimationLoop: boolean; /**If future loaded animations should be set to loop */
|
|
2823
2904
|
currentlyChangingRig: boolean;
|
|
2824
2905
|
currentlyUpdating: boolean;
|
|
2825
2906
|
hasNewUpdate: boolean;
|
|
@@ -2829,14 +2910,34 @@ export declare class OutfitRenderer {
|
|
|
2829
2910
|
animationFPS: number;
|
|
2830
2911
|
deltaTimeMultiplier: number;
|
|
2831
2912
|
renderScene: RBXRendererScene;
|
|
2832
|
-
|
|
2913
|
+
private _renderSceneCompiledConnection?;
|
|
2914
|
+
private _renderSceneFailedConnection?;
|
|
2915
|
+
/**Event is fired if a new outfit failed to load (specifically the Instance tree, not the rendering part)
|
|
2916
|
+
*
|
|
2917
|
+
* Simple: Fired when, Instance tree = fail
|
|
2833
2918
|
* @returns OutfitRendererErrorType
|
|
2834
2919
|
*/
|
|
2835
2920
|
onError: Event_2;
|
|
2836
|
-
/**Event is fired if a new outfit successfully loaded
|
|
2921
|
+
/**Event is fired if a new outfit successfully loaded (specifically the Instance tree, not the rendering part)
|
|
2922
|
+
*
|
|
2923
|
+
* Simple: Fired when, Instance tree = done
|
|
2837
2924
|
* @returns void
|
|
2838
2925
|
*/
|
|
2839
2926
|
onSuccess: Event_2;
|
|
2927
|
+
/**Event is fired when all the renderDescs have been compiled, if any renderDescs fail to render this wil never be fired (can only be fired after Instance tree is done)
|
|
2928
|
+
*
|
|
2929
|
+
* Simple: Fired when, Instance tree = done & Rendering = done (AKA everything)
|
|
2930
|
+
*
|
|
2931
|
+
* NOTE: If FLAGS.LAYERED_CLOTHING_COOLDOWN > 0 (non-default), this may be fired too early and later fire again
|
|
2932
|
+
* @returns void
|
|
2933
|
+
*/
|
|
2934
|
+
onRenderSuccess: Event_2;
|
|
2935
|
+
/**Event is fired if a renderDesc fails to compile, it can be fired multiple times
|
|
2936
|
+
*
|
|
2937
|
+
* Simple: Fired when, Rendering = fail
|
|
2938
|
+
* @returns void
|
|
2939
|
+
*/
|
|
2940
|
+
onRenderError: Event_2;
|
|
2840
2941
|
/**
|
|
2841
2942
|
* Creates a new OutfitRenderer which makes it easy to render outfits
|
|
2842
2943
|
* @param auth The authentication object, you should have one you use for everything
|
|
@@ -2874,11 +2975,22 @@ export declare class OutfitRenderer {
|
|
|
2874
2975
|
stopAnimating(): void;
|
|
2875
2976
|
/**
|
|
2876
2977
|
* Sets the current animation being played
|
|
2877
|
-
* @param name The name of the animation, for example "idle", "run" or "
|
|
2978
|
+
* @param name The name of the animation, for example "idle", "run", "emote.1234" or "anim.1234"
|
|
2878
2979
|
* @returns If the animation started playing, it may start playing later if it has been queued despite returning false
|
|
2879
2980
|
*/
|
|
2880
2981
|
setMainAnimation(name: string): Promise<boolean>;
|
|
2881
|
-
|
|
2982
|
+
fireFullyRenderedIfNeeded(): void;
|
|
2983
|
+
/**
|
|
2984
|
+
* Prepares the OutfitRenderer to be used for a thumbnail, call IMMEDIATELY after creating the OutfitRenderer
|
|
2985
|
+
* @returns true on success
|
|
2986
|
+
*/
|
|
2987
|
+
prepareForThumbnail(): Promise<boolean>;
|
|
2988
|
+
private _prepareForThumbnail;
|
|
2989
|
+
/**
|
|
2990
|
+
* Updates rendered particles so they face the camera without simulating them (usually used when generating thumbnails)
|
|
2991
|
+
*/
|
|
2992
|
+
updateParticleMatrix(): void;
|
|
2993
|
+
/**Calls destroy on the rig, stops animating and disconnects connections. The OutfitRenderer should not be interacted with after this */
|
|
2882
2994
|
destroy(): void;
|
|
2883
2995
|
}
|
|
2884
2996
|
|
|
@@ -3261,9 +3373,15 @@ export declare class RBXRendererScene {
|
|
|
3261
3373
|
effectComposer: EffectComposer | undefined;
|
|
3262
3374
|
scissor?: [number, number, number, number];
|
|
3263
3375
|
viewport?: [number, number, number, number];
|
|
3376
|
+
addedInstances: Instance[];
|
|
3264
3377
|
isRenderingMesh: Map<Instance, boolean>;
|
|
3265
3378
|
renderDescs: Map<Instance, RenderDesc>;
|
|
3266
3379
|
destroyConnections: Map<Instance, Connection>;
|
|
3380
|
+
compiledRenderDesc: Event_2;
|
|
3381
|
+
failedRenderDesc: Event_2;
|
|
3382
|
+
forceAccurateNeedsRegeneration: boolean;
|
|
3383
|
+
particlesStartFull?: number;
|
|
3384
|
+
particlesStartFullFramerate: number;
|
|
3267
3385
|
lookAwayVector: Vec3;
|
|
3268
3386
|
lookAwayDistance: number;
|
|
3269
3387
|
shadowEnabled: boolean;
|
|
@@ -3282,6 +3400,16 @@ export declare class RBXRendererScene {
|
|
|
3282
3400
|
noRect(): void;
|
|
3283
3401
|
/** Destroys all renderDescs but does not call Destroy on instances */
|
|
3284
3402
|
destroy(): void;
|
|
3403
|
+
/**
|
|
3404
|
+
* Checks that renderDescs for provided instances are compiled, ignores instances that shouldn't be compiled or haven't been added
|
|
3405
|
+
* @returns true if all provided instances's renderDescs have been compiled
|
|
3406
|
+
*/
|
|
3407
|
+
areInstancesCompiled(instances: Instance[]): boolean;
|
|
3408
|
+
/**
|
|
3409
|
+
* Checks that all renderDescs are compiled
|
|
3410
|
+
* @return true if all have been compiled
|
|
3411
|
+
*/
|
|
3412
|
+
isFullyCompiled(): boolean;
|
|
3285
3413
|
/**
|
|
3286
3414
|
* Exports scene as a GLTF or GLB
|
|
3287
3415
|
* @param name Name of the resulting file if autoDownload is true
|
|
@@ -3341,6 +3469,8 @@ declare class RenderDesc extends DisposableDesc {
|
|
|
3341
3469
|
renderScene: RBXRendererScene;
|
|
3342
3470
|
results?: THREE.Object3D[];
|
|
3343
3471
|
instance?: Instance;
|
|
3472
|
+
compiled: boolean;
|
|
3473
|
+
failed: boolean;
|
|
3344
3474
|
constructor(renderScene: RBXRendererScene);
|
|
3345
3475
|
isSame(_other: RenderDesc): boolean;
|
|
3346
3476
|
needsRegeneration(_other: RenderDesc): boolean;
|
|
@@ -3544,7 +3674,6 @@ export declare interface Search_Result {
|
|
|
3544
3674
|
* Gives a scene the default appearance for thumbnails
|
|
3545
3675
|
* @param renderScene RBXRenderScene to setup
|
|
3546
3676
|
*
|
|
3547
|
-
* @deprecated Use new Thumbnails category instead
|
|
3548
3677
|
* @category ThumbnailGenerator
|
|
3549
3678
|
*/
|
|
3550
3679
|
export declare function setupThumbnailScene(renderScene: RBXRendererScene): void;
|
|
@@ -3925,8 +4054,6 @@ export declare const WrapLayerAutoSkin: {
|
|
|
3925
4054
|
|
|
3926
4055
|
export declare const xmlMagic = "<roblox ";
|
|
3927
4056
|
|
|
3928
|
-
export declare function zoomExtents(cameraCFrame: CFrame, modelCFrame: CFrame, modelSize: Vector3, targetFOV: number, distanceScale: number, sizeType?: ZoomExtentsSizeType): void;
|
|
3929
|
-
|
|
3930
4057
|
/**
|
|
3931
4058
|
* Makes model fit inside camera
|
|
3932
4059
|
* @param cameraCFrame Original camera cframe
|
|
@@ -3936,6 +4063,8 @@ export declare function zoomExtents(cameraCFrame: CFrame, modelCFrame: CFrame, m
|
|
|
3936
4063
|
* @param distanceScale Distance is multiplied by this
|
|
3937
4064
|
* @category ThumbnailGenerator
|
|
3938
4065
|
*/
|
|
4066
|
+
export declare function zoomExtents(cameraCFrame: CFrame, modelCFrame: CFrame, modelSize: Vector3, targetFOV: number, distanceScale: number, sizeType?: ZoomExtentsSizeType): void;
|
|
4067
|
+
|
|
3939
4068
|
export declare type ZoomExtentsSizeType = "largestAxis" | "calculate";
|
|
3940
4069
|
|
|
3941
4070
|
/**
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ function cloneSearch_Payload(data) {
|
|
|
20
20
|
}
|
|
21
21
|
return newData;
|
|
22
22
|
}
|
|
23
|
-
const AllAvatarModelOutfitUpdateTypes = ["UpdateBodyColors", "UpdateAssets", "UpdateScales", "UpdateBackground"];
|
|
23
|
+
const AllAvatarModelOutfitUpdateTypes = ["UpdateBodyColors", "UpdateAssets", "UpdateAvatarType", "UpdateScales", "UpdateBackground"];
|
|
24
24
|
const BrickColors = {
|
|
25
25
|
1: "#F2F3F3",
|
|
26
26
|
2: "#A1A5A2",
|
|
@@ -42568,6 +42568,8 @@ class RenderDesc extends DisposableDesc {
|
|
|
42568
42568
|
renderScene;
|
|
42569
42569
|
results;
|
|
42570
42570
|
instance;
|
|
42571
|
+
compiled = false;
|
|
42572
|
+
failed = false;
|
|
42571
42573
|
constructor(renderScene) {
|
|
42572
42574
|
super();
|
|
42573
42575
|
this.renderScene = renderScene;
|
|
@@ -47092,8 +47094,10 @@ class ObjectDesc extends RenderDesc {
|
|
|
47092
47094
|
return this.cframe.isSame(other.cframe) && this.meshDesc.isSame(other.meshDesc) && this.materialDesc.isSame(other.materialDesc) && this.isBodyPart === other.isBodyPart && this.size.isSame(other.size) && this.skeletonDesc === other.skeletonDesc;
|
|
47093
47095
|
}
|
|
47094
47096
|
needsRegeneration(other) {
|
|
47095
|
-
if (this.
|
|
47096
|
-
|
|
47097
|
+
if (!this.renderScene.forceAccurateNeedsRegeneration) {
|
|
47098
|
+
if (this.meshDesc.layerDesc && Date.now() / 1e3 - this.meshDesc.compilationTimestamp < FLAGS.LAYERED_CLOTHING_COOLDOWN) {
|
|
47099
|
+
return false;
|
|
47100
|
+
}
|
|
47097
47101
|
}
|
|
47098
47102
|
return !this.meshDesc.isSame(other.meshDesc) || this.materialDesc.needsRegeneration(other.materialDesc);
|
|
47099
47103
|
}
|
|
@@ -48643,6 +48647,7 @@ class AnimatorWrapper extends InstanceWrapper {
|
|
|
48643
48647
|
}
|
|
48644
48648
|
}
|
|
48645
48649
|
}
|
|
48650
|
+
await Promise.all(promises);
|
|
48646
48651
|
dataModel.Destroy();
|
|
48647
48652
|
}
|
|
48648
48653
|
/**
|
|
@@ -51449,18 +51454,20 @@ class EmitterGroupDesc extends RenderDesc {
|
|
|
51449
51454
|
this.disposeMeshes(scene, originalResults);
|
|
51450
51455
|
this.disposeRenderLists(renderer);
|
|
51451
51456
|
}
|
|
51452
|
-
|
|
51457
|
+
const startFull = this.renderScene.particlesStartFull || FLAGS.PARTICLES_START_FULL;
|
|
51458
|
+
const startFullFramerate = this.renderScene.particlesStartFullFramerate;
|
|
51459
|
+
if (startFull) {
|
|
51453
51460
|
for (const emitterDesc of this.emitterDescs) {
|
|
51454
|
-
for (let i = 0; i < Math.min(emitterDesc.lifetime.Max * 2 *
|
|
51455
|
-
emitterDesc.tick(1 /
|
|
51461
|
+
for (let i = 0; i < Math.min(emitterDesc.lifetime.Max * 2 * startFullFramerate, startFull * startFullFramerate); i++) {
|
|
51462
|
+
emitterDesc.tick(1 / startFullFramerate, this);
|
|
51456
51463
|
}
|
|
51457
51464
|
emitterDesc.updateResult(this.renderScene);
|
|
51458
51465
|
}
|
|
51459
51466
|
}
|
|
51460
51467
|
return this.results;
|
|
51461
51468
|
}
|
|
51462
|
-
updateResults() {
|
|
51463
|
-
const dt = specialClamp(this.time - this.lastTime, 0, 1 / 10) * FLAGS.RENDERER_DELTA_TIME_MULTIPLIER;
|
|
51469
|
+
updateResults(forceDeltaTime) {
|
|
51470
|
+
const dt = forceDeltaTime !== void 0 ? forceDeltaTime : specialClamp(this.time - this.lastTime, 0, 1 / 10) * FLAGS.RENDERER_DELTA_TIME_MULTIPLIER;
|
|
51464
51471
|
this.lastTime = this.time;
|
|
51465
51472
|
for (const emitterDesc of this.emitterDescs) {
|
|
51466
51473
|
emitterDesc.tick(dt, this);
|
|
@@ -52189,9 +52196,15 @@ class RBXRendererScene {
|
|
|
52189
52196
|
scissor;
|
|
52190
52197
|
viewport;
|
|
52191
52198
|
//renderables data
|
|
52199
|
+
addedInstances = [];
|
|
52192
52200
|
isRenderingMesh = /* @__PURE__ */ new Map();
|
|
52193
52201
|
renderDescs = /* @__PURE__ */ new Map();
|
|
52194
52202
|
destroyConnections = /* @__PURE__ */ new Map();
|
|
52203
|
+
compiledRenderDesc = new Event();
|
|
52204
|
+
failedRenderDesc = new Event();
|
|
52205
|
+
forceAccurateNeedsRegeneration = false;
|
|
52206
|
+
particlesStartFull = void 0;
|
|
52207
|
+
particlesStartFullFramerate = FLAGS.PARTICLES_START_FULL_FRAMERATE;
|
|
52195
52208
|
//scene appearance config
|
|
52196
52209
|
lookAwayVector = [0.406, 0.306, -0.819];
|
|
52197
52210
|
lookAwayDistance = 6;
|
|
@@ -52240,6 +52253,27 @@ class RBXRendererScene {
|
|
|
52240
52253
|
this.shadowPlane = void 0;
|
|
52241
52254
|
}
|
|
52242
52255
|
}
|
|
52256
|
+
/**
|
|
52257
|
+
* Checks that renderDescs for provided instances are compiled, ignores instances that shouldn't be compiled or haven't been added
|
|
52258
|
+
* @returns true if all provided instances's renderDescs have been compiled
|
|
52259
|
+
*/
|
|
52260
|
+
areInstancesCompiled(instances) {
|
|
52261
|
+
for (const instance of instances) {
|
|
52262
|
+
if (!this.addedInstances.includes(instance)) continue;
|
|
52263
|
+
const renderDesc = this.renderDescs.get(instance);
|
|
52264
|
+
if (!renderDesc || !renderDesc.compiled || this.isRenderingMesh.get(instance)) {
|
|
52265
|
+
return false;
|
|
52266
|
+
}
|
|
52267
|
+
}
|
|
52268
|
+
return true;
|
|
52269
|
+
}
|
|
52270
|
+
/**
|
|
52271
|
+
* Checks that all renderDescs are compiled
|
|
52272
|
+
* @return true if all have been compiled
|
|
52273
|
+
*/
|
|
52274
|
+
isFullyCompiled() {
|
|
52275
|
+
return this.areInstancesCompiled(this.addedInstances);
|
|
52276
|
+
}
|
|
52243
52277
|
/**
|
|
52244
52278
|
* Exports scene as a GLTF or GLB
|
|
52245
52279
|
* @param name Name of the resulting file if autoDownload is true
|
|
@@ -52828,12 +52862,18 @@ class RBXRenderer {
|
|
|
52828
52862
|
/**Removes an instance from the renderer */
|
|
52829
52863
|
static removeInstance(instance, renderScene = RBXRenderer.firstScene) {
|
|
52830
52864
|
if (!RBXRenderer.renderer) return;
|
|
52865
|
+
const index = renderScene.addedInstances.indexOf(instance);
|
|
52866
|
+
if (index >= 0) {
|
|
52867
|
+
renderScene.addedInstances[index] = renderScene.addedInstances[renderScene.addedInstances.length - 1];
|
|
52868
|
+
renderScene.addedInstances.splice(renderScene.addedInstances.length - 1, 1);
|
|
52869
|
+
}
|
|
52831
52870
|
const desc = renderScene.renderDescs.get(instance);
|
|
52832
52871
|
if (desc) {
|
|
52833
52872
|
desc.dispose(RBXRenderer.renderer, renderScene.scene);
|
|
52834
52873
|
}
|
|
52835
52874
|
renderScene.renderDescs.delete(instance);
|
|
52836
52875
|
renderScene.isRenderingMesh.delete(instance);
|
|
52876
|
+
renderScene.compiledRenderDesc.Fire(instance);
|
|
52837
52877
|
for (const child of instance.GetChildren()) {
|
|
52838
52878
|
RBXRenderer.removeInstance(child, renderScene);
|
|
52839
52879
|
}
|
|
@@ -52851,7 +52891,11 @@ class RBXRenderer {
|
|
|
52851
52891
|
const oldDesc = renderScene.renderDescs.get(instance);
|
|
52852
52892
|
const newDesc = new DescClass(renderScene);
|
|
52853
52893
|
newDesc.fromInstance(instance);
|
|
52854
|
-
if (oldDesc && !oldDesc.needsRegeneration(newDesc)) {
|
|
52894
|
+
if (oldDesc && !oldDesc.needsRegeneration(newDesc) && !renderScene.isRenderingMesh.get(instance)) {
|
|
52895
|
+
if (!oldDesc.compiled && !oldDesc.failed) {
|
|
52896
|
+
oldDesc.compiled = true;
|
|
52897
|
+
renderScene.compiledRenderDesc.Fire(instance);
|
|
52898
|
+
}
|
|
52855
52899
|
if (!oldDesc.isSame(newDesc)) {
|
|
52856
52900
|
oldDesc.fromRenderDesc(newDesc);
|
|
52857
52901
|
oldDesc.updateResults();
|
|
@@ -52892,6 +52936,9 @@ class RBXRenderer {
|
|
|
52892
52936
|
newDesc.dispose(RBXRenderer.renderer, renderScene.scene);
|
|
52893
52937
|
}
|
|
52894
52938
|
} else {
|
|
52939
|
+
newDesc.failed = true;
|
|
52940
|
+
renderScene.isRenderingMesh.set(instance, false);
|
|
52941
|
+
renderScene.failedRenderDesc.Fire(instance);
|
|
52895
52942
|
warn(false, "Failed to compile mesh", this, results);
|
|
52896
52943
|
}
|
|
52897
52944
|
});
|
|
@@ -52911,6 +52958,7 @@ class RBXRenderer {
|
|
|
52911
52958
|
if (renderScene.destroyed) return;
|
|
52912
52959
|
const RenderDescType = getRenderDescForInstance(instance);
|
|
52913
52960
|
if (RenderDescType) {
|
|
52961
|
+
if (renderScene.addedInstances.indexOf(instance) < 0) renderScene.addedInstances.push(instance);
|
|
52914
52962
|
RBXRenderer._addRenderDesc(instance, auth, RenderDescType, renderScene);
|
|
52915
52963
|
}
|
|
52916
52964
|
for (const child of instance.GetChildren()) {
|
|
@@ -53101,209 +53149,6 @@ class HSR {
|
|
|
53101
53149
|
}
|
|
53102
53150
|
}
|
|
53103
53151
|
}
|
|
53104
|
-
function getCorners(cframe, size) {
|
|
53105
|
-
const halfX = size.X / 2;
|
|
53106
|
-
const halfY = size.Y / 2;
|
|
53107
|
-
const halfZ = size.Z / 2;
|
|
53108
|
-
return [
|
|
53109
|
-
cframe.multiply(new CFrame(halfX, halfY, halfZ)),
|
|
53110
|
-
cframe.multiply(new CFrame(halfX, halfY, -halfZ)),
|
|
53111
|
-
cframe.multiply(new CFrame(-halfX, halfY, halfZ)),
|
|
53112
|
-
cframe.multiply(new CFrame(-halfX, halfY, -halfZ)),
|
|
53113
|
-
cframe.multiply(new CFrame(halfX, -halfY, halfZ)),
|
|
53114
|
-
cframe.multiply(new CFrame(halfX, -halfY, -halfZ)),
|
|
53115
|
-
cframe.multiply(new CFrame(-halfX, -halfY, halfZ)),
|
|
53116
|
-
cframe.multiply(new CFrame(-halfX, -halfY, -halfZ))
|
|
53117
|
-
];
|
|
53118
|
-
}
|
|
53119
|
-
function getLower(a, b) {
|
|
53120
|
-
return new Vector32(
|
|
53121
|
-
a.X < b.X ? a.X : b.X,
|
|
53122
|
-
a.Y < b.Y ? a.Y : b.Y,
|
|
53123
|
-
a.Z < b.Z ? a.Z : b.Z
|
|
53124
|
-
);
|
|
53125
|
-
}
|
|
53126
|
-
function getHigher(a, b) {
|
|
53127
|
-
return new Vector32(
|
|
53128
|
-
a.X > b.X ? a.X : b.X,
|
|
53129
|
-
a.Y > b.Y ? a.Y : b.Y,
|
|
53130
|
-
a.Z > b.Z ? a.Z : b.Z
|
|
53131
|
-
);
|
|
53132
|
-
}
|
|
53133
|
-
function getExtentsForParts(parts, includeTransform) {
|
|
53134
|
-
let lowerExtents = new Vector32(0, 0, 0);
|
|
53135
|
-
let higherExtents = new Vector32(0, 0, 0);
|
|
53136
|
-
for (const child of parts) {
|
|
53137
|
-
if (child.createWrapper()?.IsA("BasePart")) {
|
|
53138
|
-
const cframe = traverseRigCFrame(child, includeTransform, true);
|
|
53139
|
-
const size = child.Prop("Size");
|
|
53140
|
-
const corners = getCorners(cframe, size);
|
|
53141
|
-
for (const corner of corners) {
|
|
53142
|
-
lowerExtents = getLower(lowerExtents, new Vector32().fromVec3(corner.Position));
|
|
53143
|
-
higherExtents = getHigher(higherExtents, new Vector32().fromVec3(corner.Position));
|
|
53144
|
-
}
|
|
53145
|
-
}
|
|
53146
|
-
}
|
|
53147
|
-
return [lowerExtents, higherExtents];
|
|
53148
|
-
}
|
|
53149
|
-
function getExtents(cframe, parts) {
|
|
53150
|
-
const inverseCF = cframe.inverse();
|
|
53151
|
-
let lowerExtents = new Vector32(0, 0, 0);
|
|
53152
|
-
let higherExtents = new Vector32(0, 0, 0);
|
|
53153
|
-
for (const child of parts) {
|
|
53154
|
-
if (child.createWrapper()?.IsA("BasePart")) {
|
|
53155
|
-
const partCF = child.Prop("CFrame");
|
|
53156
|
-
const partSize = child.Prop("Size");
|
|
53157
|
-
const corners = getCorners(inverseCF.multiply(partCF), partSize);
|
|
53158
|
-
for (const corner of corners) {
|
|
53159
|
-
lowerExtents = getLower(lowerExtents, new Vector32().fromVec3(corner.Position));
|
|
53160
|
-
higherExtents = getHigher(higherExtents, new Vector32().fromVec3(corner.Position));
|
|
53161
|
-
}
|
|
53162
|
-
}
|
|
53163
|
-
}
|
|
53164
|
-
return [lowerExtents, higherExtents];
|
|
53165
|
-
}
|
|
53166
|
-
function getExtentsCenter(extents) {
|
|
53167
|
-
return extents[1].minus(extents[0]).divide(new Vector32(2, 2, 2)).add(extents[0]);
|
|
53168
|
-
}
|
|
53169
|
-
function zoomExtents(cameraCFrame, modelCFrame, modelSize, targetFOV, distanceScale, sizeType = "calculate") {
|
|
53170
|
-
let largestSize = Math.max(modelSize.X, modelSize.Y, modelSize.Z);
|
|
53171
|
-
if (sizeType === "calculate") {
|
|
53172
|
-
largestSize = modelSize.magnitude() / 2 / Math.sin(rad(targetFOV / 2));
|
|
53173
|
-
}
|
|
53174
|
-
const fovMultiplier = sizeType === "largestAxis" ? 70 / targetFOV : 1;
|
|
53175
|
-
const lookDir = multiply(normalize(minus(cameraCFrame.Position, modelCFrame.Position)), [distanceScale, distanceScale, distanceScale]);
|
|
53176
|
-
cameraCFrame.Position = add(modelCFrame.Position, multiply(multiply(lookDir, [largestSize, largestSize, largestSize]), [fovMultiplier, fovMultiplier, fovMultiplier]));
|
|
53177
|
-
}
|
|
53178
|
-
function getCameraOffset(fov2, extentsSize) {
|
|
53179
|
-
const halfSize = extentsSize.magnitude() / 2;
|
|
53180
|
-
const fovDivisor = Math.tan(rad(fov2 / 2));
|
|
53181
|
-
return halfSize / fovDivisor;
|
|
53182
|
-
}
|
|
53183
|
-
function zoomToExtents(cameraCFrame, modelCFrame, modelSize, fov2 = 70) {
|
|
53184
|
-
const cameraOffset = getCameraOffset(fov2, modelSize);
|
|
53185
|
-
const cameraRotation = new CFrame();
|
|
53186
|
-
cameraRotation.Orientation = cameraCFrame.Orientation;
|
|
53187
|
-
const instancePosition = modelCFrame.Position;
|
|
53188
|
-
cameraCFrame.Position = add(instancePosition, multiply(minus([0, 0, 0], cameraRotation.lookVector()), [cameraOffset, cameraOffset, cameraOffset]));
|
|
53189
|
-
}
|
|
53190
|
-
function getHeadExtents(rig) {
|
|
53191
|
-
const head = rig.FindFirstChild("Head");
|
|
53192
|
-
if (!head) return;
|
|
53193
|
-
const headParts = [];
|
|
53194
|
-
for (const child of rig.GetDescendants()) {
|
|
53195
|
-
if (child === head) {
|
|
53196
|
-
headParts.push(head);
|
|
53197
|
-
} else {
|
|
53198
|
-
const weld = child.FindFirstChildOfClass("Weld");
|
|
53199
|
-
if (weld && child.parent && child.parent.className === "Accessory") {
|
|
53200
|
-
if (weld.Prop("Part0") === head || weld.Prop("Part1") === head) {
|
|
53201
|
-
headParts.push(child);
|
|
53202
|
-
}
|
|
53203
|
-
}
|
|
53204
|
-
}
|
|
53205
|
-
}
|
|
53206
|
-
const extents = getExtents(head.Prop("CFrame"), headParts);
|
|
53207
|
-
return extents;
|
|
53208
|
-
}
|
|
53209
|
-
function getRigExtentsWorld(rig) {
|
|
53210
|
-
const rigParts = [];
|
|
53211
|
-
for (const child of rig.GetDescendants()) {
|
|
53212
|
-
if (child.createWrapper()?.IsA("BasePart")) {
|
|
53213
|
-
rigParts.push(child);
|
|
53214
|
-
}
|
|
53215
|
-
}
|
|
53216
|
-
const extents = getExtents(new CFrame(), rigParts);
|
|
53217
|
-
return extents;
|
|
53218
|
-
}
|
|
53219
|
-
function getCameraCFrameForHeadshotCustomized(rig, fov2, yRot, distance2) {
|
|
53220
|
-
const head = rig.FindFirstChild("Head");
|
|
53221
|
-
if (!head) return;
|
|
53222
|
-
const headCF = head.PropOrDefault("CFrame", new CFrame());
|
|
53223
|
-
const headLocalExtents = getHeadExtents(rig);
|
|
53224
|
-
if (!headLocalExtents) return;
|
|
53225
|
-
const headCenterPosLocal = headLocalExtents[0].add(headLocalExtents[1].minus(headLocalExtents[0]).divide(new Vector32(2, 2, 2)));
|
|
53226
|
-
const headCenterPos = new Vector32().fromVec3(headCF.multiply(new CFrame(...headCenterPosLocal.toVec3())).Position);
|
|
53227
|
-
const headCenterCF = new CFrame(...headCenterPos.toVec3());
|
|
53228
|
-
let lookVector = headCF.lookVector();
|
|
53229
|
-
if (Math.abs(lookVector[1]) > 0.95) {
|
|
53230
|
-
lookVector = [0, 0, -1];
|
|
53231
|
-
} else {
|
|
53232
|
-
lookVector[1] = 0;
|
|
53233
|
-
lookVector = normalize(lookVector);
|
|
53234
|
-
}
|
|
53235
|
-
let lookCF = CFrame.lookAt([0, 0, 0], lookVector);
|
|
53236
|
-
lookCF = lookCF.multiply(CFrame.fromEulerAngles(0, rad(yRot), 0, "ZXY"));
|
|
53237
|
-
lookVector = lookCF.lookVector();
|
|
53238
|
-
const fovMultiplier = 70 / fov2;
|
|
53239
|
-
lookCF.Position = add(headCenterCF.Position, multiply(multiply([10, 10, 10], lookVector), [fovMultiplier, fovMultiplier, fovMultiplier]));
|
|
53240
|
-
lookCF = CFrame.lookAt(lookCF.Position, headCenterCF.Position);
|
|
53241
|
-
const cameraCF = lookCF.clone();
|
|
53242
|
-
zoomExtents(cameraCF, headCenterCF, headLocalExtents[1].minus(headLocalExtents[0]), fov2, distance2, "largestAxis");
|
|
53243
|
-
return cameraCF;
|
|
53244
|
-
}
|
|
53245
|
-
function getCameraCFrameForAvatarNonCustomized(rig) {
|
|
53246
|
-
const thumbnailCamera = rig.FindFirstChildOfClass("Camera");
|
|
53247
|
-
if (thumbnailCamera) return thumbnailCamera.PropOrDefault("CFrame", new CFrame());
|
|
53248
|
-
let rootPart = rig.PropOrDefault("PrimaryPart", void 0);
|
|
53249
|
-
if (!rootPart) rootPart = rig.FindFirstChildOfClass("Part");
|
|
53250
|
-
if (!rootPart) rootPart = rig.FindFirstChildOfClass("MeshPart");
|
|
53251
|
-
if (!rootPart) return;
|
|
53252
|
-
const rootPartCF = rootPart.PropOrDefault("CFrame", new CFrame()).clone();
|
|
53253
|
-
const worldExtents = getRigExtentsWorld(rig);
|
|
53254
|
-
if (!worldExtents) return;
|
|
53255
|
-
const extentsSize = worldExtents[1].minus(worldExtents[0]);
|
|
53256
|
-
rootPartCF.Position = getExtentsCenter(worldExtents).toVec3();
|
|
53257
|
-
let lookVector = rootPartCF.lookVector();
|
|
53258
|
-
if (Math.abs(lookVector[1]) > 0.95) {
|
|
53259
|
-
lookVector = [0, 0, -1];
|
|
53260
|
-
} else {
|
|
53261
|
-
lookVector[1] = 0;
|
|
53262
|
-
lookVector = normalize(lookVector);
|
|
53263
|
-
}
|
|
53264
|
-
let lookCF = CFrame.lookAt([0, 0, 0], lookVector);
|
|
53265
|
-
lookCF = lookCF.multiply(CFrame.fromEulerAngles(25 * Math.PI / 180, 27.5 * Math.PI / 180, 0, "ZXY"));
|
|
53266
|
-
lookVector = lookCF.lookVector();
|
|
53267
|
-
lookCF.Position = add(rootPartCF.Position, multiply([10, 10, 10], lookVector));
|
|
53268
|
-
lookCF = CFrame.lookAt(lookCF.Position, rootPartCF.Position);
|
|
53269
|
-
const cameraCF = lookCF.clone();
|
|
53270
|
-
zoomExtents(cameraCF, rootPartCF, extentsSize, 70, 1);
|
|
53271
|
-
return cameraCF;
|
|
53272
|
-
}
|
|
53273
|
-
function getThumbnailCameraCFrame(model, fov2, forceAngle) {
|
|
53274
|
-
const thumbnailCamera = model.FindFirstChildOfClass("Camera");
|
|
53275
|
-
if (thumbnailCamera) return thumbnailCamera.PropOrDefault("CFrame", new CFrame());
|
|
53276
|
-
let rootPart = model.PropOrDefault("PrimaryPart", void 0);
|
|
53277
|
-
if (!rootPart) rootPart = model.FindFirstChildOfClass("Part");
|
|
53278
|
-
if (!rootPart) rootPart = model.FindFirstChildOfClass("MeshPart");
|
|
53279
|
-
if (!rootPart) return;
|
|
53280
|
-
const rootPartCF = rootPart.PropOrDefault("CFrame", new CFrame()).clone();
|
|
53281
|
-
const worldExtents = getRigExtentsWorld(model);
|
|
53282
|
-
if (!worldExtents) return;
|
|
53283
|
-
const extentsSize = worldExtents[1].minus(worldExtents[0]);
|
|
53284
|
-
rootPartCF.Position = getExtentsCenter(worldExtents).toVec3();
|
|
53285
|
-
let lookVector = rootPartCF.lookVector();
|
|
53286
|
-
if (Math.abs(lookVector[1]) > 0.95) {
|
|
53287
|
-
lookVector = [0, 0, -1];
|
|
53288
|
-
} else {
|
|
53289
|
-
lookVector[1] = 0;
|
|
53290
|
-
lookVector = normalize(lookVector);
|
|
53291
|
-
}
|
|
53292
|
-
let lookCF = CFrame.lookAt([0, 0, 0], lookVector);
|
|
53293
|
-
if (!forceAngle) {
|
|
53294
|
-
lookCF = lookCF.multiply(CFrame.fromEulerAngles(0, 0, rad(45)));
|
|
53295
|
-
lookCF = lookCF.multiply(CFrame.fromEulerAngles(rad(35), 0, 0));
|
|
53296
|
-
lookCF = lookCF.multiply(CFrame.fromEulerAngles(0, 0, 0));
|
|
53297
|
-
} else {
|
|
53298
|
-
lookCF = lookCF.multiply(CFrame.fromEulerAngles(rad(forceAngle[0]), rad(forceAngle[1]), rad(forceAngle[2])));
|
|
53299
|
-
}
|
|
53300
|
-
lookVector = lookCF.lookVector();
|
|
53301
|
-
lookCF.Position = add(rootPartCF.Position, multiply([10, 10, 10], lookVector));
|
|
53302
|
-
lookCF = CFrame.lookAt(lookCF.Position, rootPartCF.Position);
|
|
53303
|
-
const cameraCF = lookCF.clone();
|
|
53304
|
-
zoomExtents(cameraCF, rootPartCF, extentsSize, fov2, 1);
|
|
53305
|
-
return cameraCF;
|
|
53306
|
-
}
|
|
53307
53152
|
class OutfitRenderer {
|
|
53308
53153
|
auth;
|
|
53309
53154
|
outfit;
|
|
@@ -53316,6 +53161,8 @@ class OutfitRenderer {
|
|
|
53316
53161
|
/**Does camera update every frame */
|
|
53317
53162
|
doAddInstance = true;
|
|
53318
53163
|
/**If outfitRenderer should call RBXRenderer.addInstance() */
|
|
53164
|
+
forceAnimationLoop = true;
|
|
53165
|
+
/**If future loaded animations should be set to loop */
|
|
53319
53166
|
currentlyChangingRig = false;
|
|
53320
53167
|
currentlyUpdating = false;
|
|
53321
53168
|
hasNewUpdate = false;
|
|
@@ -53325,14 +53172,34 @@ class OutfitRenderer {
|
|
|
53325
53172
|
animationFPS = 60;
|
|
53326
53173
|
deltaTimeMultiplier = 1;
|
|
53327
53174
|
renderScene = RBXRenderer.firstScene;
|
|
53328
|
-
|
|
53175
|
+
_renderSceneCompiledConnection;
|
|
53176
|
+
_renderSceneFailedConnection;
|
|
53177
|
+
/**Event is fired if a new outfit failed to load (specifically the Instance tree, not the rendering part)
|
|
53178
|
+
*
|
|
53179
|
+
* Simple: Fired when, Instance tree = fail
|
|
53329
53180
|
* @returns OutfitRendererErrorType
|
|
53330
53181
|
*/
|
|
53331
53182
|
onError = new Event();
|
|
53332
|
-
/**Event is fired if a new outfit successfully loaded
|
|
53333
|
-
|
|
53334
|
-
|
|
53183
|
+
/**Event is fired if a new outfit successfully loaded (specifically the Instance tree, not the rendering part)
|
|
53184
|
+
*
|
|
53185
|
+
* Simple: Fired when, Instance tree = done
|
|
53186
|
+
* @returns void
|
|
53187
|
+
*/
|
|
53335
53188
|
onSuccess = new Event();
|
|
53189
|
+
/**Event is fired when all the renderDescs have been compiled, if any renderDescs fail to render this wil never be fired (can only be fired after Instance tree is done)
|
|
53190
|
+
*
|
|
53191
|
+
* Simple: Fired when, Instance tree = done & Rendering = done (AKA everything)
|
|
53192
|
+
*
|
|
53193
|
+
* NOTE: If FLAGS.LAYERED_CLOTHING_COOLDOWN > 0 (non-default), this may be fired too early and later fire again
|
|
53194
|
+
* @returns void
|
|
53195
|
+
*/
|
|
53196
|
+
onRenderSuccess = new Event();
|
|
53197
|
+
/**Event is fired if a renderDesc fails to compile, it can be fired multiple times
|
|
53198
|
+
*
|
|
53199
|
+
* Simple: Fired when, Rendering = fail
|
|
53200
|
+
* @returns void
|
|
53201
|
+
*/
|
|
53202
|
+
onRenderError = new Event();
|
|
53336
53203
|
/**
|
|
53337
53204
|
* Creates a new OutfitRenderer which makes it easy to render outfits
|
|
53338
53205
|
* @param auth The authentication object, you should have one you use for everything
|
|
@@ -53344,6 +53211,13 @@ class OutfitRenderer {
|
|
|
53344
53211
|
this.outfit = outfit;
|
|
53345
53212
|
this.currentRigType = outfit.playerAvatarType;
|
|
53346
53213
|
this.renderScene = renderScene;
|
|
53214
|
+
this._renderSceneCompiledConnection = this.renderScene.compiledRenderDesc.Connect(() => {
|
|
53215
|
+
this.fireFullyRenderedIfNeeded();
|
|
53216
|
+
});
|
|
53217
|
+
this._renderSceneFailedConnection = this.renderScene.failedRenderDesc.Connect(() => {
|
|
53218
|
+
console.log("OutfitRenderer failed");
|
|
53219
|
+
this.onRenderError.Fire();
|
|
53220
|
+
});
|
|
53347
53221
|
this._updateOutfit();
|
|
53348
53222
|
}
|
|
53349
53223
|
/**
|
|
@@ -53404,7 +53278,10 @@ class OutfitRenderer {
|
|
|
53404
53278
|
}
|
|
53405
53279
|
this.currentlyUpdating = false;
|
|
53406
53280
|
if (this.currentRig) {
|
|
53407
|
-
if (this.doAddInstance)
|
|
53281
|
+
if (this.doAddInstance) {
|
|
53282
|
+
RBXRenderer.addInstance(this.currentRig, this.auth, this.renderScene);
|
|
53283
|
+
this.fireFullyRenderedIfNeeded();
|
|
53284
|
+
}
|
|
53408
53285
|
if (this.doCameraUpdateOnLoad) {
|
|
53409
53286
|
this.centerCamera();
|
|
53410
53287
|
}
|
|
@@ -53479,7 +53356,7 @@ class OutfitRenderer {
|
|
|
53479
53356
|
if (humanoid) {
|
|
53480
53357
|
const animator = humanoid.FindFirstChildOfClass("Animator");
|
|
53481
53358
|
if (animator) {
|
|
53482
|
-
const deltaTime = deltaTimeOverride
|
|
53359
|
+
const deltaTime = deltaTimeOverride !== void 0 ? deltaTimeOverride : (Date.now() / 1e3 - this.lastFrameTime) * this.deltaTimeMultiplier;
|
|
53483
53360
|
this.lastFrameTime = Date.now() / 1e3;
|
|
53484
53361
|
const animatorW = new AnimatorWrapper(animator);
|
|
53485
53362
|
animatorW.renderAnimation(deltaTime);
|
|
@@ -53500,7 +53377,7 @@ class OutfitRenderer {
|
|
|
53500
53377
|
}
|
|
53501
53378
|
/**
|
|
53502
53379
|
* Sets the current animation being played
|
|
53503
|
-
* @param name The name of the animation, for example "idle", "run" or "
|
|
53380
|
+
* @param name The name of the animation, for example "idle", "run", "emote.1234" or "anim.1234"
|
|
53504
53381
|
* @returns If the animation started playing, it may start playing later if it has been queued despite returning false
|
|
53505
53382
|
*/
|
|
53506
53383
|
setMainAnimation(name) {
|
|
@@ -53514,7 +53391,12 @@ class OutfitRenderer {
|
|
|
53514
53391
|
const successfullyPlayed = animatorW.playAnimation(name);
|
|
53515
53392
|
if (!successfullyPlayed && name.startsWith("emote.") && name) {
|
|
53516
53393
|
const emoteId = BigInt(name.split(".")[1]);
|
|
53517
|
-
animatorW.loadAvatarAnimation(emoteId, true,
|
|
53394
|
+
animatorW.loadAvatarAnimation(emoteId, true, this.forceAnimationLoop).then(() => {
|
|
53395
|
+
resolve(animatorW.playAnimation(name));
|
|
53396
|
+
});
|
|
53397
|
+
} else if (!successfullyPlayed && name.startsWith("id.") && name) {
|
|
53398
|
+
const animId = BigInt(name.split(".")[1]);
|
|
53399
|
+
animatorW.loadAnimation(animId, this.forceAnimationLoop).then(() => {
|
|
53518
53400
|
resolve(animatorW.playAnimation(name));
|
|
53519
53401
|
});
|
|
53520
53402
|
} else {
|
|
@@ -53532,13 +53414,93 @@ class OutfitRenderer {
|
|
|
53532
53414
|
}
|
|
53533
53415
|
});
|
|
53534
53416
|
}
|
|
53535
|
-
|
|
53417
|
+
fireFullyRenderedIfNeeded() {
|
|
53418
|
+
if (!this.currentlyChangingRig && !this.currentlyUpdating && !this.hasNewUpdate && this.currentRig && this.renderScene.areInstancesCompiled(this.currentRig.GetDescendants())) {
|
|
53419
|
+
this.onRenderSuccess.Fire();
|
|
53420
|
+
}
|
|
53421
|
+
}
|
|
53422
|
+
/**
|
|
53423
|
+
* Prepares the OutfitRenderer to be used for a thumbnail, call IMMEDIATELY after creating the OutfitRenderer
|
|
53424
|
+
* @returns true on success
|
|
53425
|
+
*/
|
|
53426
|
+
async prepareForThumbnail() {
|
|
53427
|
+
const connections = [];
|
|
53428
|
+
const result = await Promise.race([
|
|
53429
|
+
this._prepareForThumbnail(),
|
|
53430
|
+
new Promise((resolve) => {
|
|
53431
|
+
connections.push(this.onError.Connect(() => {
|
|
53432
|
+
resolve(false);
|
|
53433
|
+
}));
|
|
53434
|
+
}),
|
|
53435
|
+
new Promise((resolve) => {
|
|
53436
|
+
connections.push(this.onRenderError.Connect(() => {
|
|
53437
|
+
resolve(false);
|
|
53438
|
+
}));
|
|
53439
|
+
})
|
|
53440
|
+
]);
|
|
53441
|
+
for (const connection of connections) {
|
|
53442
|
+
connection.Disconnect();
|
|
53443
|
+
}
|
|
53444
|
+
return result;
|
|
53445
|
+
}
|
|
53446
|
+
async _prepareForThumbnail() {
|
|
53447
|
+
this.doAddInstance = false;
|
|
53448
|
+
if (this.outfit.playerAvatarType === AvatarType.R6) this.deltaTimeMultiplier = 0;
|
|
53449
|
+
await new Promise((resolve) => {
|
|
53450
|
+
this.onSuccess.Connect(() => {
|
|
53451
|
+
if (!this.outfit.containsAssetType("Gear")) {
|
|
53452
|
+
if (this.outfit.playerAvatarType === AvatarType.R15) {
|
|
53453
|
+
this.setMainAnimation("pose").then(() => {
|
|
53454
|
+
resolve(void 0);
|
|
53455
|
+
});
|
|
53456
|
+
} else {
|
|
53457
|
+
this.setMainAnimation("idle").then(() => {
|
|
53458
|
+
resolve(void 0);
|
|
53459
|
+
});
|
|
53460
|
+
}
|
|
53461
|
+
} else {
|
|
53462
|
+
this.setMainAnimation("toolnone").then(() => {
|
|
53463
|
+
resolve(void 0);
|
|
53464
|
+
});
|
|
53465
|
+
}
|
|
53466
|
+
});
|
|
53467
|
+
});
|
|
53468
|
+
this.animateOnce(!this.outfit.containsAssetType("Gear") && this.outfit.playerAvatarType === AvatarType.R6 ? 0 : 1);
|
|
53469
|
+
if (this.currentRig) RBXRenderer.addInstance(this.currentRig, this.auth, this.renderScene);
|
|
53470
|
+
await new Promise((resolve) => {
|
|
53471
|
+
const connection = this.onRenderSuccess.Connect(() => {
|
|
53472
|
+
resolve(void 0);
|
|
53473
|
+
connection.Disconnect();
|
|
53474
|
+
});
|
|
53475
|
+
});
|
|
53476
|
+
return true;
|
|
53477
|
+
}
|
|
53478
|
+
/**
|
|
53479
|
+
* Updates rendered particles so they face the camera without simulating them (usually used when generating thumbnails)
|
|
53480
|
+
*/
|
|
53481
|
+
updateParticleMatrix() {
|
|
53482
|
+
if (!this.currentRig) return;
|
|
53483
|
+
for (const instance of this.currentRig.GetDescendants()) {
|
|
53484
|
+
const renderDesc = this.renderScene.renderDescs.get(instance);
|
|
53485
|
+
if (renderDesc && renderDesc instanceof EmitterGroupDesc) {
|
|
53486
|
+
renderDesc.updateResults(0);
|
|
53487
|
+
}
|
|
53488
|
+
}
|
|
53489
|
+
}
|
|
53490
|
+
/**Calls destroy on the rig, stops animating and disconnects connections. The OutfitRenderer should not be interacted with after this */
|
|
53536
53491
|
destroy() {
|
|
53537
53492
|
this.stopAnimating();
|
|
53538
53493
|
this.currentRig?.Destroy();
|
|
53494
|
+
this.currentRig = void 0;
|
|
53495
|
+
this._renderSceneCompiledConnection?.Disconnect();
|
|
53496
|
+
this._renderSceneFailedConnection?.Disconnect();
|
|
53497
|
+
this.onError.Clear();
|
|
53498
|
+
this.onSuccess.Clear();
|
|
53499
|
+
this.onRenderError.Clear();
|
|
53500
|
+
this.onRenderSuccess.Clear();
|
|
53539
53501
|
}
|
|
53540
53502
|
}
|
|
53541
|
-
function renderToRenderTarget(width, height, renderScene) {
|
|
53503
|
+
function renderToRenderTarget$1(width, height, renderScene) {
|
|
53542
53504
|
const renderTarget = new WebGLRenderTarget(width, height, {
|
|
53543
53505
|
colorSpace: SRGBColorSpace,
|
|
53544
53506
|
generateMipmaps: false,
|
|
@@ -53553,7 +53515,7 @@ function renderToRenderTarget(width, height, renderScene) {
|
|
|
53553
53515
|
rbxRenderer.render(renderScene.scene, renderScene.camera);
|
|
53554
53516
|
return renderTarget;
|
|
53555
53517
|
}
|
|
53556
|
-
async function renderTargetToCanvas(renderTarget) {
|
|
53518
|
+
async function renderTargetToCanvas$1(renderTarget) {
|
|
53557
53519
|
const rbxRenderer = RBXRenderer.getRenderer();
|
|
53558
53520
|
if (!rbxRenderer) return;
|
|
53559
53521
|
const width = renderTarget.width;
|
|
@@ -53562,45 +53524,210 @@ async function renderTargetToCanvas(renderTarget) {
|
|
|
53562
53524
|
await rbxRenderer.readRenderTargetPixelsAsync(renderTarget, 0, 0, width, height, data);
|
|
53563
53525
|
return imageDataToCanvas(data, width, height);
|
|
53564
53526
|
}
|
|
53565
|
-
async function
|
|
53566
|
-
|
|
53567
|
-
|
|
53527
|
+
async function imageThumbnailClick(renderScene, width, height, format, options) {
|
|
53528
|
+
const resultOptions = {
|
|
53529
|
+
quality: 1
|
|
53530
|
+
};
|
|
53531
|
+
if (options) Object.assign(resultOptions, options);
|
|
53532
|
+
const renderTarget = renderToRenderTarget$1(width, height, renderScene);
|
|
53533
|
+
const canvas = await renderTargetToCanvas$1(renderTarget);
|
|
53534
|
+
renderTarget.dispose();
|
|
53535
|
+
if (canvas) {
|
|
53536
|
+
return canvas.toDataURL(`image/${format}`, resultOptions.quality);
|
|
53537
|
+
} else {
|
|
53538
|
+
return void 0;
|
|
53539
|
+
}
|
|
53540
|
+
}
|
|
53541
|
+
async function modelThumbnailClick(renderScene, format, options) {
|
|
53542
|
+
const resultOptions = {
|
|
53543
|
+
includeAnimations: false
|
|
53544
|
+
};
|
|
53545
|
+
if (options) Object.assign(resultOptions, options);
|
|
53546
|
+
return await renderScene.exportGLTF(`result`, false, {
|
|
53547
|
+
includeAnimations: resultOptions.includeAnimations,
|
|
53548
|
+
binary: format === "glb"
|
|
53549
|
+
});
|
|
53550
|
+
}
|
|
53551
|
+
function getCorners(cframe, size) {
|
|
53552
|
+
const halfX = size.X / 2;
|
|
53553
|
+
const halfY = size.Y / 2;
|
|
53554
|
+
const halfZ = size.Z / 2;
|
|
53555
|
+
return [
|
|
53556
|
+
cframe.multiply(new CFrame(halfX, halfY, halfZ)),
|
|
53557
|
+
cframe.multiply(new CFrame(halfX, halfY, -halfZ)),
|
|
53558
|
+
cframe.multiply(new CFrame(-halfX, halfY, halfZ)),
|
|
53559
|
+
cframe.multiply(new CFrame(-halfX, halfY, -halfZ)),
|
|
53560
|
+
cframe.multiply(new CFrame(halfX, -halfY, halfZ)),
|
|
53561
|
+
cframe.multiply(new CFrame(halfX, -halfY, -halfZ)),
|
|
53562
|
+
cframe.multiply(new CFrame(-halfX, -halfY, halfZ)),
|
|
53563
|
+
cframe.multiply(new CFrame(-halfX, -halfY, -halfZ))
|
|
53564
|
+
];
|
|
53565
|
+
}
|
|
53566
|
+
function getLower(a, b) {
|
|
53567
|
+
return new Vector32(
|
|
53568
|
+
a.X < b.X ? a.X : b.X,
|
|
53569
|
+
a.Y < b.Y ? a.Y : b.Y,
|
|
53570
|
+
a.Z < b.Z ? a.Z : b.Z
|
|
53571
|
+
);
|
|
53572
|
+
}
|
|
53573
|
+
function getHigher(a, b) {
|
|
53574
|
+
return new Vector32(
|
|
53575
|
+
a.X > b.X ? a.X : b.X,
|
|
53576
|
+
a.Y > b.Y ? a.Y : b.Y,
|
|
53577
|
+
a.Z > b.Z ? a.Z : b.Z
|
|
53578
|
+
);
|
|
53579
|
+
}
|
|
53580
|
+
function getExtentsForParts(parts, includeTransform) {
|
|
53581
|
+
let lowerExtents = new Vector32(0, 0, 0);
|
|
53582
|
+
let higherExtents = new Vector32(0, 0, 0);
|
|
53583
|
+
for (const child of parts) {
|
|
53584
|
+
if (child.createWrapper()?.IsA("BasePart")) {
|
|
53585
|
+
const cframe = traverseRigCFrame(child, includeTransform, true);
|
|
53586
|
+
const size = child.Prop("Size");
|
|
53587
|
+
const corners = getCorners(cframe, size);
|
|
53588
|
+
for (const corner of corners) {
|
|
53589
|
+
lowerExtents = getLower(lowerExtents, new Vector32().fromVec3(corner.Position));
|
|
53590
|
+
higherExtents = getHigher(higherExtents, new Vector32().fromVec3(corner.Position));
|
|
53591
|
+
}
|
|
53592
|
+
}
|
|
53593
|
+
}
|
|
53594
|
+
return [lowerExtents, higherExtents];
|
|
53595
|
+
}
|
|
53596
|
+
function getExtents(cframe, parts) {
|
|
53597
|
+
const inverseCF = cframe.inverse();
|
|
53598
|
+
let lowerExtents = new Vector32(0, 0, 0);
|
|
53599
|
+
let higherExtents = new Vector32(0, 0, 0);
|
|
53600
|
+
for (const child of parts) {
|
|
53601
|
+
if (child.createWrapper()?.IsA("BasePart")) {
|
|
53602
|
+
const partCF = child.Prop("CFrame");
|
|
53603
|
+
const partSize = child.Prop("Size");
|
|
53604
|
+
const corners = getCorners(inverseCF.multiply(partCF), partSize);
|
|
53605
|
+
for (const corner of corners) {
|
|
53606
|
+
lowerExtents = getLower(lowerExtents, new Vector32().fromVec3(corner.Position));
|
|
53607
|
+
higherExtents = getHigher(higherExtents, new Vector32().fromVec3(corner.Position));
|
|
53608
|
+
}
|
|
53609
|
+
}
|
|
53610
|
+
}
|
|
53611
|
+
return [lowerExtents, higherExtents];
|
|
53612
|
+
}
|
|
53613
|
+
function getExtentsWorld(rig) {
|
|
53614
|
+
const rigParts = [];
|
|
53615
|
+
for (const child of rig.GetDescendants()) {
|
|
53616
|
+
if (child.createWrapper()?.IsA("BasePart")) {
|
|
53617
|
+
rigParts.push(child);
|
|
53618
|
+
}
|
|
53619
|
+
}
|
|
53620
|
+
const extents = getExtents(new CFrame(), rigParts);
|
|
53621
|
+
return extents;
|
|
53622
|
+
}
|
|
53623
|
+
function getExtentsCenter(extents) {
|
|
53624
|
+
return extents[1].minus(extents[0]).divide(new Vector32(2, 2, 2)).add(extents[0]);
|
|
53625
|
+
}
|
|
53626
|
+
function zoomExtents(cameraCFrame, modelCFrame, modelSize, targetFOV, distanceScale, sizeType = "calculate") {
|
|
53627
|
+
let largestSize = Math.max(modelSize.X, modelSize.Y, modelSize.Z);
|
|
53628
|
+
if (sizeType === "calculate") {
|
|
53629
|
+
largestSize = modelSize.magnitude() / 2 / Math.sin(rad(targetFOV / 2));
|
|
53630
|
+
}
|
|
53631
|
+
const fovMultiplier = sizeType === "largestAxis" ? 70 / targetFOV : 1;
|
|
53632
|
+
const lookDir = multiply(normalize(minus(cameraCFrame.Position, modelCFrame.Position)), [distanceScale, distanceScale, distanceScale]);
|
|
53633
|
+
cameraCFrame.Position = add(modelCFrame.Position, multiply(multiply(lookDir, [largestSize, largestSize, largestSize]), [fovMultiplier, fovMultiplier, fovMultiplier]));
|
|
53634
|
+
}
|
|
53635
|
+
function getCameraOffset(fov2, extentsSize) {
|
|
53636
|
+
const halfSize = extentsSize.magnitude() / 2;
|
|
53637
|
+
const fovDivisor = Math.tan(rad(fov2 / 2));
|
|
53638
|
+
return halfSize / fovDivisor;
|
|
53639
|
+
}
|
|
53640
|
+
function zoomToExtents(cameraCFrame, modelCFrame, modelSize, fov2 = 70) {
|
|
53641
|
+
const cameraOffset = getCameraOffset(fov2, modelSize);
|
|
53642
|
+
const cameraRotation = new CFrame();
|
|
53643
|
+
cameraRotation.Orientation = cameraCFrame.Orientation;
|
|
53644
|
+
const instancePosition = modelCFrame.Position;
|
|
53645
|
+
cameraCFrame.Position = add(instancePosition, multiply(minus([0, 0, 0], cameraRotation.lookVector()), [cameraOffset, cameraOffset, cameraOffset]));
|
|
53646
|
+
}
|
|
53647
|
+
function getThumbnailCameraCFrame(model, fov2, forceAngle) {
|
|
53648
|
+
const thumbnailCamera = model.FindFirstChildOfClass("Camera");
|
|
53649
|
+
if (thumbnailCamera) return thumbnailCamera.PropOrDefault("CFrame", new CFrame());
|
|
53650
|
+
let rootPart = model.PropOrDefault("PrimaryPart", void 0);
|
|
53651
|
+
if (!rootPart) rootPart = model.FindFirstChildOfClass("Part");
|
|
53652
|
+
if (!rootPart) rootPart = model.FindFirstChildOfClass("MeshPart");
|
|
53653
|
+
if (!rootPart) return;
|
|
53654
|
+
const rootPartCF = rootPart.PropOrDefault("CFrame", new CFrame()).clone();
|
|
53655
|
+
const worldExtents = getExtentsWorld(model);
|
|
53656
|
+
if (!worldExtents) return;
|
|
53657
|
+
const extentsSize = worldExtents[1].minus(worldExtents[0]);
|
|
53658
|
+
rootPartCF.Position = getExtentsCenter(worldExtents).toVec3();
|
|
53659
|
+
let lookVector = rootPartCF.lookVector();
|
|
53660
|
+
if (Math.abs(lookVector[1]) > 0.95) {
|
|
53661
|
+
lookVector = [0, 0, -1];
|
|
53662
|
+
} else {
|
|
53663
|
+
lookVector[1] = 0;
|
|
53664
|
+
lookVector = normalize(lookVector);
|
|
53665
|
+
}
|
|
53666
|
+
let lookCF = CFrame.lookAt([0, 0, 0], lookVector);
|
|
53667
|
+
if (!forceAngle) {
|
|
53668
|
+
lookCF = lookCF.multiply(CFrame.fromEulerAngles(0, 0, rad(45)));
|
|
53669
|
+
lookCF = lookCF.multiply(CFrame.fromEulerAngles(rad(35), 0, 0));
|
|
53670
|
+
lookCF = lookCF.multiply(CFrame.fromEulerAngles(0, 0, 0));
|
|
53671
|
+
} else {
|
|
53672
|
+
lookCF = lookCF.multiply(CFrame.fromEulerAngles(rad(forceAngle[0]), rad(forceAngle[1]), rad(forceAngle[2])));
|
|
53673
|
+
}
|
|
53674
|
+
lookVector = lookCF.lookVector();
|
|
53675
|
+
lookCF.Position = add(rootPartCF.Position, multiply([10, 10, 10], lookVector));
|
|
53676
|
+
lookCF = CFrame.lookAt(lookCF.Position, rootPartCF.Position);
|
|
53677
|
+
const cameraCF = lookCF.clone();
|
|
53678
|
+
zoomExtents(cameraCF, rootPartCF, extentsSize, fov2, 1);
|
|
53679
|
+
return cameraCF;
|
|
53680
|
+
}
|
|
53681
|
+
function setupThumbnailScene(renderScene) {
|
|
53682
|
+
renderScene.particlesStartFull = 4;
|
|
53683
|
+
renderScene.particlesStartFullFramerate = 20;
|
|
53684
|
+
renderScene.forceAccurateNeedsRegeneration = true;
|
|
53685
|
+
renderScene.shouldAnimate = false;
|
|
53686
|
+
renderScene.wellLitDirectionalLightIntensity *= 2;
|
|
53687
|
+
renderScene.shadowEnabled = false;
|
|
53688
|
+
RBXRenderer.setupScene("Thumbnail", 16777215, renderScene);
|
|
53689
|
+
if (renderScene.plane) renderScene.scene.remove(renderScene.plane);
|
|
53690
|
+
if (renderScene.shadowPlane) renderScene.scene.remove(renderScene.shadowPlane);
|
|
53691
|
+
renderScene.scene.background = null;
|
|
53692
|
+
}
|
|
53693
|
+
async function generateOutfitThumbnail(auth, outfit, size = [150, 150], type = "png", quality = 1, gltfAutoDownload = false, includeAnimations = false, renderSceneParam) {
|
|
53694
|
+
const renderScene = renderSceneParam || RBXRenderer.addScene();
|
|
53695
|
+
if (renderScene !== renderSceneParam) {
|
|
53696
|
+
setupThumbnailScene(renderScene);
|
|
53697
|
+
}
|
|
53698
|
+
const outfitRenderer = new OutfitRenderer(auth, outfit, renderScene);
|
|
53699
|
+
const outfitSuccess = await outfitRenderer.prepareForThumbnail();
|
|
53700
|
+
if (!outfitSuccess) {
|
|
53701
|
+
outfitRenderer.destroy();
|
|
53702
|
+
renderScene.destroy();
|
|
53703
|
+
return void 0;
|
|
53704
|
+
}
|
|
53705
|
+
if (outfitRenderer.currentRig) {
|
|
53706
|
+
const cameraCFrame = getThumbnailCameraCFrame(outfitRenderer.currentRig, renderScene.camera.fov);
|
|
53568
53707
|
if (cameraCFrame) {
|
|
53569
53708
|
RBXRenderer.setCameraCFrame(cameraCFrame, renderScene);
|
|
53570
53709
|
}
|
|
53571
|
-
|
|
53572
|
-
|
|
53573
|
-
|
|
53574
|
-
|
|
53575
|
-
|
|
53576
|
-
exportTimeout = void 0;
|
|
53577
|
-
}
|
|
53578
|
-
if (!currentlyLoading) {
|
|
53579
|
-
exportTimeout = setTimeout(doExport, FLAGS.THUMBNAIL_TIMEOUT);
|
|
53580
|
-
}
|
|
53710
|
+
outfitRenderer.updateParticleMatrix();
|
|
53711
|
+
const result = type === "gltf" || type === "glb" ? await modelThumbnailClick(renderScene, type, {
|
|
53712
|
+
includeAnimations
|
|
53713
|
+
}) : await imageThumbnailClick(renderScene, size[0], size[1], type, {
|
|
53714
|
+
quality
|
|
53581
53715
|
});
|
|
53582
|
-
|
|
53583
|
-
|
|
53584
|
-
|
|
53585
|
-
if (!FLAGS.RENDERTARGET_TO_CANVASTEXTURE && FLAGS.USE_RENDERTARGET) {
|
|
53586
|
-
warn(true, "FLAGS.RENDERTARGET_TO_CANVASTEXTURE is false, GLTF export cannot export render target textures, consider setting this flag to true");
|
|
53587
|
-
}
|
|
53588
|
-
resolve(await renderScene.exportGLTF(`result`, gltfAutoDownload, {
|
|
53589
|
-
includeAnimations,
|
|
53590
|
-
binary: type === "glb"
|
|
53591
|
-
}));
|
|
53716
|
+
if (gltfAutoDownload && (type === "gltf" || type === "glb")) {
|
|
53717
|
+
if (result instanceof ArrayBuffer) {
|
|
53718
|
+
saveByteArray([result], `result.glb`);
|
|
53592
53719
|
} else {
|
|
53593
|
-
|
|
53594
|
-
const canvasTarget = await renderTargetToCanvas(renderTarget);
|
|
53595
|
-
if (canvasTarget) {
|
|
53596
|
-
resolve(canvasTarget.toDataURL(`image/${type}`, quality));
|
|
53597
|
-
} else {
|
|
53598
|
-
resolve(void 0);
|
|
53599
|
-
}
|
|
53720
|
+
download(`result.gltf`, JSON.stringify(result));
|
|
53600
53721
|
}
|
|
53601
|
-
renderScene.destroy();
|
|
53602
53722
|
}
|
|
53603
|
-
|
|
53723
|
+
outfitRenderer.destroy();
|
|
53724
|
+
renderScene.destroy();
|
|
53725
|
+
return result;
|
|
53726
|
+
} else {
|
|
53727
|
+
outfitRenderer.destroy();
|
|
53728
|
+
renderScene.destroy();
|
|
53729
|
+
return void 0;
|
|
53730
|
+
}
|
|
53604
53731
|
}
|
|
53605
53732
|
async function generateOutfitModelThumbnail(auth, outfitModel, options) {
|
|
53606
53733
|
const defaultOptions = {
|
|
@@ -53643,34 +53770,37 @@ async function generateOutfitModelThumbnail(auth, outfitModel, options) {
|
|
|
53643
53770
|
}
|
|
53644
53771
|
return generateOutfitThumbnail(auth, outfitModel.outfit, defaultOptions.size, defaultOptions.type, defaultOptions.quality, defaultOptions.gltfAutoDownload, defaultOptions.includeAnimations, renderScene);
|
|
53645
53772
|
}
|
|
53646
|
-
|
|
53773
|
+
function renderToRenderTarget(width, height, renderScene) {
|
|
53774
|
+
const renderTarget = new WebGLRenderTarget(width, height, {
|
|
53775
|
+
colorSpace: SRGBColorSpace,
|
|
53776
|
+
generateMipmaps: false,
|
|
53777
|
+
minFilter: LinearFilter,
|
|
53778
|
+
magFilter: LinearFilter,
|
|
53779
|
+
type: UnsignedByteType,
|
|
53780
|
+
samples: 4
|
|
53781
|
+
});
|
|
53782
|
+
const rbxRenderer = RBXRenderer.getRenderer();
|
|
53783
|
+
if (!rbxRenderer) return renderTarget;
|
|
53784
|
+
rbxRenderer.setRenderTarget(renderTarget);
|
|
53785
|
+
rbxRenderer.render(renderScene.scene, renderScene.camera);
|
|
53786
|
+
return renderTarget;
|
|
53787
|
+
}
|
|
53788
|
+
async function renderTargetToCanvas(renderTarget) {
|
|
53789
|
+
const rbxRenderer = RBXRenderer.getRenderer();
|
|
53790
|
+
if (!rbxRenderer) return;
|
|
53791
|
+
const width = renderTarget.width;
|
|
53792
|
+
const height = renderTarget.height;
|
|
53793
|
+
const data = new Uint8Array(width * height * 4);
|
|
53794
|
+
await rbxRenderer.readRenderTargetPixelsAsync(renderTarget, 0, 0, width, height, data);
|
|
53795
|
+
return imageDataToCanvas(data, width, height);
|
|
53796
|
+
}
|
|
53797
|
+
async function generateModelThumbnail(auth, renderScene, model, size = [150, 150], type = "png", quality = 1, gltfAutoDownload = false, includeAnimations = false) {
|
|
53647
53798
|
return new Promise((resolve) => {
|
|
53648
|
-
const
|
|
53649
|
-
|
|
53650
|
-
|
|
53651
|
-
|
|
53652
|
-
|
|
53653
|
-
const outfitRenderer = new OutfitRenderer(auth, outfit, renderScene);
|
|
53654
|
-
outfitRenderer.doAddInstance = false;
|
|
53655
|
-
if (outfit.playerAvatarType === AvatarType.R6) outfitRenderer.deltaTimeMultiplier = 0;
|
|
53656
|
-
API.Misc.startCurrentlyLoadingAssets("thumbnail-animation");
|
|
53657
|
-
outfitRenderer.onSuccess.Connect(() => {
|
|
53658
|
-
if (!outfit.containsAssetType("Gear")) {
|
|
53659
|
-
if (outfit.playerAvatarType === AvatarType.R15) {
|
|
53660
|
-
outfitRenderer.setMainAnimation("pose").then(() => {
|
|
53661
|
-
API.Misc.stopCurrentlyLoadingAssets("thumbnail-animation");
|
|
53662
|
-
});
|
|
53663
|
-
} else {
|
|
53664
|
-
outfitRenderer.setMainAnimation("idle").then(() => {
|
|
53665
|
-
API.Misc.stopCurrentlyLoadingAssets("thumbnail-animation");
|
|
53666
|
-
});
|
|
53667
|
-
}
|
|
53668
|
-
} else {
|
|
53669
|
-
outfitRenderer.setMainAnimation("toolnone").then(() => {
|
|
53670
|
-
API.Misc.stopCurrentlyLoadingAssets("thumbnail-animation");
|
|
53671
|
-
});
|
|
53672
|
-
}
|
|
53673
|
-
});
|
|
53799
|
+
const cameraCFrame = getThumbnailCameraCFrame(model, renderScene.camera.fov);
|
|
53800
|
+
if (cameraCFrame) {
|
|
53801
|
+
RBXRenderer.setCameraCFrame(cameraCFrame, renderScene);
|
|
53802
|
+
}
|
|
53803
|
+
RBXRenderer.addInstance(model, auth, renderScene);
|
|
53674
53804
|
let exportTimeout = !API.Misc.getCurrentlyLoading() ? setTimeout(doExport, FLAGS.THUMBNAIL_TIMEOUT) : void 0;
|
|
53675
53805
|
const onLoadingConnection = API.Events.OnLoadingAssets.Connect((currentlyLoading) => {
|
|
53676
53806
|
if (exportTimeout) {
|
|
@@ -53683,27 +53813,27 @@ async function generateOutfitThumbnail(auth, outfit, size = [150, 150], type = "
|
|
|
53683
53813
|
});
|
|
53684
53814
|
async function doExport() {
|
|
53685
53815
|
onLoadingConnection.Disconnect();
|
|
53686
|
-
if (
|
|
53687
|
-
|
|
53688
|
-
|
|
53689
|
-
|
|
53690
|
-
resolve(
|
|
53691
|
-
|
|
53816
|
+
if (type === "gltf" || type === "glb") {
|
|
53817
|
+
if (!FLAGS.RENDERTARGET_TO_CANVASTEXTURE && FLAGS.USE_RENDERTARGET) {
|
|
53818
|
+
warn(true, "FLAGS.RENDERTARGET_TO_CANVASTEXTURE is false, GLTF export cannot export render target textures, consider setting this flag to true");
|
|
53819
|
+
}
|
|
53820
|
+
resolve(await renderScene.exportGLTF(`result`, gltfAutoDownload, {
|
|
53821
|
+
includeAnimations,
|
|
53822
|
+
binary: type === "glb"
|
|
53823
|
+
}));
|
|
53692
53824
|
} else {
|
|
53693
|
-
|
|
53825
|
+
const renderTarget = renderToRenderTarget(...size, renderScene);
|
|
53826
|
+
const canvasTarget = await renderTargetToCanvas(renderTarget);
|
|
53827
|
+
if (canvasTarget) {
|
|
53828
|
+
resolve(canvasTarget.toDataURL(`image/${type}`, quality));
|
|
53829
|
+
} else {
|
|
53830
|
+
resolve(void 0);
|
|
53831
|
+
}
|
|
53694
53832
|
}
|
|
53833
|
+
renderScene.destroy();
|
|
53695
53834
|
}
|
|
53696
53835
|
});
|
|
53697
53836
|
}
|
|
53698
|
-
function setupThumbnailScene(renderScene) {
|
|
53699
|
-
renderScene.shouldAnimate = false;
|
|
53700
|
-
renderScene.wellLitDirectionalLightIntensity *= 2;
|
|
53701
|
-
renderScene.shadowEnabled = false;
|
|
53702
|
-
RBXRenderer.setupScene("Thumbnail", 16777215, renderScene);
|
|
53703
|
-
if (renderScene.plane) renderScene.scene.remove(renderScene.plane);
|
|
53704
|
-
if (renderScene.shadowPlane) renderScene.scene.remove(renderScene.shadowPlane);
|
|
53705
|
-
renderScene.scene.background = null;
|
|
53706
|
-
}
|
|
53707
53837
|
function exposeAPI() {
|
|
53708
53838
|
globalThis.API = API;
|
|
53709
53839
|
globalThis.APICACHE = CACHE;
|
|
@@ -53721,6 +53851,79 @@ function exposeThumbnailGenerator() {
|
|
|
53721
53851
|
globalThis.setupThumbnailScene = setupThumbnailScene;
|
|
53722
53852
|
globalThis.getThumbnailCameraCFrame = getThumbnailCameraCFrame;
|
|
53723
53853
|
}
|
|
53854
|
+
function getHeadExtents(rig) {
|
|
53855
|
+
const head = rig.FindFirstChild("Head");
|
|
53856
|
+
if (!head) return;
|
|
53857
|
+
const headParts = [];
|
|
53858
|
+
for (const child of rig.GetDescendants()) {
|
|
53859
|
+
if (child === head) {
|
|
53860
|
+
headParts.push(head);
|
|
53861
|
+
} else {
|
|
53862
|
+
const weld = child.FindFirstChildOfClass("Weld");
|
|
53863
|
+
if (weld && child.parent && child.parent.className === "Accessory") {
|
|
53864
|
+
if (weld.Prop("Part0") === head || weld.Prop("Part1") === head) {
|
|
53865
|
+
headParts.push(child);
|
|
53866
|
+
}
|
|
53867
|
+
}
|
|
53868
|
+
}
|
|
53869
|
+
}
|
|
53870
|
+
const extents = getExtents(head.Prop("CFrame"), headParts);
|
|
53871
|
+
return extents;
|
|
53872
|
+
}
|
|
53873
|
+
function getCameraCFrameForHeadshotCustomized(rig, fov2, yRot, distance2) {
|
|
53874
|
+
const head = rig.FindFirstChild("Head");
|
|
53875
|
+
if (!head) return;
|
|
53876
|
+
const headCF = head.PropOrDefault("CFrame", new CFrame());
|
|
53877
|
+
const headLocalExtents = getHeadExtents(rig);
|
|
53878
|
+
if (!headLocalExtents) return;
|
|
53879
|
+
const headCenterPosLocal = headLocalExtents[0].add(headLocalExtents[1].minus(headLocalExtents[0]).divide(new Vector32(2, 2, 2)));
|
|
53880
|
+
const headCenterPos = new Vector32().fromVec3(headCF.multiply(new CFrame(...headCenterPosLocal.toVec3())).Position);
|
|
53881
|
+
const headCenterCF = new CFrame(...headCenterPos.toVec3());
|
|
53882
|
+
let lookVector = headCF.lookVector();
|
|
53883
|
+
if (Math.abs(lookVector[1]) > 0.95) {
|
|
53884
|
+
lookVector = [0, 0, -1];
|
|
53885
|
+
} else {
|
|
53886
|
+
lookVector[1] = 0;
|
|
53887
|
+
lookVector = normalize(lookVector);
|
|
53888
|
+
}
|
|
53889
|
+
let lookCF = CFrame.lookAt([0, 0, 0], lookVector);
|
|
53890
|
+
lookCF = lookCF.multiply(CFrame.fromEulerAngles(0, rad(yRot), 0, "ZXY"));
|
|
53891
|
+
lookVector = lookCF.lookVector();
|
|
53892
|
+
const fovMultiplier = 70 / fov2;
|
|
53893
|
+
lookCF.Position = add(headCenterCF.Position, multiply(multiply([10, 10, 10], lookVector), [fovMultiplier, fovMultiplier, fovMultiplier]));
|
|
53894
|
+
lookCF = CFrame.lookAt(lookCF.Position, headCenterCF.Position);
|
|
53895
|
+
const cameraCF = lookCF.clone();
|
|
53896
|
+
zoomExtents(cameraCF, headCenterCF, headLocalExtents[1].minus(headLocalExtents[0]), fov2, distance2, "largestAxis");
|
|
53897
|
+
return cameraCF;
|
|
53898
|
+
}
|
|
53899
|
+
function getCameraCFrameForAvatarNonCustomized(rig) {
|
|
53900
|
+
const thumbnailCamera = rig.FindFirstChildOfClass("Camera");
|
|
53901
|
+
if (thumbnailCamera) return thumbnailCamera.PropOrDefault("CFrame", new CFrame());
|
|
53902
|
+
let rootPart = rig.PropOrDefault("PrimaryPart", void 0);
|
|
53903
|
+
if (!rootPart) rootPart = rig.FindFirstChildOfClass("Part");
|
|
53904
|
+
if (!rootPart) rootPart = rig.FindFirstChildOfClass("MeshPart");
|
|
53905
|
+
if (!rootPart) return;
|
|
53906
|
+
const rootPartCF = rootPart.PropOrDefault("CFrame", new CFrame()).clone();
|
|
53907
|
+
const worldExtents = getExtentsWorld(rig);
|
|
53908
|
+
if (!worldExtents) return;
|
|
53909
|
+
const extentsSize = worldExtents[1].minus(worldExtents[0]);
|
|
53910
|
+
rootPartCF.Position = getExtentsCenter(worldExtents).toVec3();
|
|
53911
|
+
let lookVector = rootPartCF.lookVector();
|
|
53912
|
+
if (Math.abs(lookVector[1]) > 0.95) {
|
|
53913
|
+
lookVector = [0, 0, -1];
|
|
53914
|
+
} else {
|
|
53915
|
+
lookVector[1] = 0;
|
|
53916
|
+
lookVector = normalize(lookVector);
|
|
53917
|
+
}
|
|
53918
|
+
let lookCF = CFrame.lookAt([0, 0, 0], lookVector);
|
|
53919
|
+
lookCF = lookCF.multiply(CFrame.fromEulerAngles(25 * Math.PI / 180, 27.5 * Math.PI / 180, 0, "ZXY"));
|
|
53920
|
+
lookVector = lookCF.lookVector();
|
|
53921
|
+
lookCF.Position = add(rootPartCF.Position, multiply([10, 10, 10], lookVector));
|
|
53922
|
+
lookCF = CFrame.lookAt(lookCF.Position, rootPartCF.Position);
|
|
53923
|
+
const cameraCF = lookCF.clone();
|
|
53924
|
+
zoomExtents(cameraCF, rootPartCF, extentsSize, 70, 1);
|
|
53925
|
+
return cameraCF;
|
|
53926
|
+
}
|
|
53724
53927
|
export {
|
|
53725
53928
|
API,
|
|
53726
53929
|
AbbreviationToFaceControlProperty,
|
|
@@ -53931,6 +54134,7 @@ export {
|
|
|
53931
54134
|
getExtents,
|
|
53932
54135
|
getExtentsCenter,
|
|
53933
54136
|
getExtentsForParts,
|
|
54137
|
+
getExtentsWorld,
|
|
53934
54138
|
getHeadExtents,
|
|
53935
54139
|
getOffsetArray,
|
|
53936
54140
|
getOriginalAttachmentOrientation,
|
|
@@ -53939,7 +54143,6 @@ export {
|
|
|
53939
54143
|
getPartAssemblyScore,
|
|
53940
54144
|
getPartsInAssembly_Generate,
|
|
53941
54145
|
getRandomBetweenInclusive,
|
|
53942
|
-
getRigExtentsWorld,
|
|
53943
54146
|
getRootAssemblyPart_Generate,
|
|
53944
54147
|
getThumbnailCameraCFrame,
|
|
53945
54148
|
getUVtoIndexMap,
|
|
@@ -53952,6 +54155,7 @@ export {
|
|
|
53952
54155
|
hashVec3Safe,
|
|
53953
54156
|
hexToColor3,
|
|
53954
54157
|
hexToRgb,
|
|
54158
|
+
imageThumbnailClick,
|
|
53955
54159
|
imageUrlToDataUrl,
|
|
53956
54160
|
inheritSkeleton,
|
|
53957
54161
|
inheritUV,
|
|
@@ -53972,6 +54176,7 @@ export {
|
|
|
53972
54176
|
mergeTargetWithReference,
|
|
53973
54177
|
minimumDeltaEBodyColorDifference,
|
|
53974
54178
|
minus,
|
|
54179
|
+
modelThumbnailClick,
|
|
53975
54180
|
mount,
|
|
53976
54181
|
mountElement,
|
|
53977
54182
|
multiply,
|