xushiting před 3 roky
revize
9a4e7bc441
100 změnil soubory, kde provedl 47983 přidání a 0 odebrání
  1. 486 0
      ActionsHandler.js
  2. 31 0
      AxiosCanceler.js
  3. 191 0
      BaseTable.js
  4. 68 0
      BreathPoint.js
  5. 25 0
      Broadcast.js
  6. 201 0
      Camera.js
  7. 106 0
      CircularArray.js
  8. 43 0
      DataStorage.js
  9. 121 0
      Debug.js
  10. 623 0
      EngineProxy.js
  11. 62 0
      EventsController.js
  12. 114 0
      EventsManager.js
  13. 24 0
      Heartbeat.js
  14. 133 0
      Http.js
  15. 67 0
      JoyStick.js
  16. 81 0
      Logger.js
  17. 197 0
      ModelManager.js
  18. 14 0
      ModelTable.js
  19. 215 0
      NetworkController.js
  20. 18 0
      NetworkMonitor.js
  21. 86 0
      Panorama.js
  22. 35 0
      PathManager.js
  23. 64 0
      Pool.js
  24. 18 0
      PoolObject.js
  25. 187 0
      Preload.js
  26. 26 0
      Queue.js
  27. 108 0
      Reporter.js
  28. 107 0
      RotationEvent.js
  29. 206 0
      Rtcp.js
  30. 38 0
      RunTimeArray.js
  31. 99 0
      Signal.js
  32. 504 0
      Socket.js
  33. 87 0
      StaticMeshEvent.js
  34. 468 0
      Stats.js
  35. 46 0
      Stream.js
  36. 82 0
      TV.js
  37. 35 0
      Timeout.js
  38. 1157 0
      Workers.js
  39. 265 0
      XAnimationController.js
  40. 408 0
      XAvatar.js
  41. 374 0
      XAvatarBillboardComponent.js
  42. 169 0
      XAvatarComopnent.js
  43. 319 0
      XAvatarLoader.js
  44. 751 0
      XAvatarManager.js
  45. 229 0
      XBillboard.js
  46. 149 0
      XBillboardManager.js
  47. 275 0
      XBreathPointManager.js
  48. 280 0
      XCameraComponent.js
  49. 102 0
      XDecal.js
  50. 230 0
      XDecalManager.js
  51. 108 0
      XDecalMaterial.js
  52. 7 0
      XEngineRunTimeStats.js
  53. 125 0
      XLightManager.js
  54. 315 0
      XMaterialComponent.js
  55. 112 0
      XParticleManager.js
  56. 20 0
      XRain.js
  57. 429 0
      XSceneManager.js
  58. 829 0
      XSequence.js
  59. 27 0
      XSpriteManager.js
  60. 184 0
      XStateMachine.js
  61. 181 0
      XStaticMesh.js
  62. 576 0
      XStaticMeshComponent.js
  63. 125 0
      XStaticMeshFromOneGltf.js
  64. 188 0
      XStats.js
  65. 319 0
      XTelevision.js
  66. 40 0
      XVideoRawYUV.js
  67. 88 0
      Xverse.js
  68. 471 0
      XverseAvatar.js
  69. 363 0
      XverseAvatarManager.js
  70. 10 0
      XverseDatabase.js
  71. 89 0
      XverseEffect.js
  72. 42 0
      XverseEffectManager.js
  73. 270 0
      XverseError.js
  74. 538 0
      XverseRoom.js
  75. 2 0
      libs/Oimo.js
  76. 925 0
      libs/ammo.js
  77. 3 0
      libs/babylon.gui.min.js
  78. 1 0
      libs/babylon.gui.min.js.map
  79. 3 0
      libs/babylon.inspector.bundle.js
  80. 1 0
      libs/babylon.inspector.bundle.js.map
  81. 2 0
      libs/babylon.js
  82. 1 0
      libs/babylon.js.map
  83. 11466 0
      libs/babylonjs.loaders.js
  84. 1 0
      libs/babylonjs.loaders.js.map
  85. 3 0
      libs/babylonjs.materials.min.js
  86. 1 0
      libs/babylonjs.materials.min.js.map
  87. 3 0
      libs/babylonjs.postProcess.min.js
  88. 1 0
      libs/babylonjs.postProcess.min.js.map
  89. 3 0
      libs/babylonjs.proceduralTextures.min.js
  90. 1 0
      libs/babylonjs.proceduralTextures.min.js.map
  91. 2 0
      libs/babylonjs.serializers.min.js
  92. 1 0
      libs/babylonjs.serializers.min.js.map
  93. 13687 0
      libs/cannon.js
  94. 5983 0
      libs/crypto-js.js
  95. 2 0
      libs/dat.gui.min.js
  96. 1 0
      libs/earcut.min.js
  97. 6 0
      libs/jquery-1.10.2.min.js
  98. 683 0
      libs/md5.js
  99. 21 0
      libs/recast.js
  100. 0 0
      libs/socket.2.3.js

+ 486 - 0
ActionsHandler.js

@@ -0,0 +1,486 @@
+const log$p = new Logger("actions-handler")
+  , QueueActions = [Actions.Transfer, Actions.ChangeSkin, Actions.GetOnVehicle, Actions.GetOffVehicle];
+class ActionsHandler {
+    constructor(e) {
+        E(this, "room");
+        E(this, "currentActiveAction");
+        E(this, "avatarComponentsSync", e=>{
+            const t = {
+                action_type: Actions.SetPlayerState,
+                set_player_state_action: {
+                    player_state: {
+                        avatar_components: JSON.stringify(e)
+                    }
+                }
+            };
+            this.sendData({
+                data: t
+            })
+        }
+        );
+        this.room = e
+    }
+    async sendData(e) {
+        await this.beforeSend(e);
+        const t = uuid$1();
+        if (this.room.networkController.sendRtcData(le(oe({}, e.data), {
+            trace_id: t,
+            user_id: this.room.options.userId
+        })),
+        e.track === !1)
+            return Promise.resolve(null);
+        const {sampleRate: r=1, timeout: n=2e3, tag: o, data: a, special: s} = e;
+        return eventsManager.track({
+            timeout: n,
+            traceId: t,
+            event: a.action_type,
+            tag: o,
+            extra: a
+        }, {
+            special: s,
+            sampleRate: r,
+            noReport: this.room.viewMode === "serverless" || this.room.options.viewMode === "serverless"
+        }).finally(()=>{
+            QueueActions.includes(e.data.action_type) && (this.currentActiveAction = void 0)
+        }
+        )
+    }
+    async beforeSend(e) {
+        var o;
+        const t = (o = this.room._userAvatar) == null ? void 0 : o.isMoving
+          , r = e.data.action_type;
+        if (QueueActions.includes(r)) {
+            if (this.currentActiveAction)
+                return log$p.error(`${Actions[this.currentActiveAction]} still pending, reject ${Actions[r]}`),
+                Promise.reject(new FrequencyLimitError(`${Actions[r]} action request frequency limit`));
+            this.currentActiveAction = r
+        }
+        if (t && QueueActions.includes(e.data.action_type))
+            try {
+                await this.stopMoving()
+            } catch (a) {
+                this.currentActiveAction = void 0,
+                log$p.error("before action stopMoving failed", a)
+            }
+    }
+    async moveTo(e) {
+        const {point: t, extra: r="", motionType: n} = e
+          , o = {
+            action_type: Actions.Clicking,
+            clicking_action: {
+                clicking_point: t,
+                clicking_type: ClickType.IgnoreView,
+                extra: encodeURIComponent(r),
+                attitude: n
+            },
+            clicking_state: this.room._currentClickingState
+        };
+        return this.sendData({
+            data: o
+        })
+    }
+    transfer(e) {
+        const {renderType: t, player: r, camera: n, areaName: o, attitude: a, pathName: s, person: l, noMedia: u, timeout: c, tag: h, special: f} = e
+          , d = {
+            data: {
+                action_type: Actions.Transfer,
+                transfer_action: {
+                    render_type: t,
+                    player: r,
+                    camera: n,
+                    areaName: o,
+                    attitude: a,
+                    pathName: s,
+                    person: {
+                        type: l
+                    },
+                    noMedia: u,
+                    tiles: [0, 1, 2, 4]
+                }
+            },
+            special: f,
+            timeout: c || 4e3,
+            tag: h
+        };
+        return this.sendData(d).then(_=>(typeof l != "undefined" && this.room.updateCurrentNetworkOptions({
+            person: l,
+            rotationRenderType: t
+        }),
+        _))
+    }
+    changeRotationRenderType(e) {
+        const {renderType: t, player: r, camera: n, areaName: o, attitude: a, pathName: s} = e;
+        return this.transfer({
+            renderType: t,
+            player: r,
+            camera: n,
+            areaName: o,
+            attitude: a,
+            pathName: s,
+            tag: "changeToRotationVideo"
+        })
+    }
+    requestPanorama(e, t, r) {
+        const {camera: n, player: o, areaName: a, attitude: s, pathName: l, tag: u} = e;
+        return this.transfer({
+            renderType: RenderType.ClientRotationPano,
+            player: o,
+            camera: n,
+            person: Person.First,
+            areaName: a,
+            attitude: s,
+            pathName: l,
+            noMedia: t,
+            timeout: r,
+            tag: u || "requestPanorama",
+            special: !t
+        })
+    }
+    setMotionType(e) {
+        return this.transfer({
+            attitude: e,
+            tag: "setMotionType"
+        })
+    }
+    setNickName(e) {
+        const t = {
+            action_type: Actions.ChangeNickname,
+            change_nickname_action: {
+                nickname: e
+            }
+        };
+        return this.sendData({
+            data: t
+        })
+    }
+    getReserveSeat({routeId: e, name: t}) {
+        const r = {
+            action_type: Actions.ReserveSeat,
+            reserve_seat_action: {
+                route_id: e,
+                name: t
+            }
+        };
+        return this.sendData({
+            data: r
+        })
+    }
+    getReserveStatus({routeId: e, name: t, need_detail: r}) {
+        const n = {
+            action_type: Actions.GetReserveStatus,
+            get_reserve_status_action: {
+                route_id: e,
+                name: t,
+                need_detail: r
+            }
+        };
+        return this.sendData({
+            data: n,
+            timeout: 2e3
+        }).then(o=>o.reserveDetail)
+    }
+    stopMoving() {
+        const e = {
+            action_type: Actions.StopMoving,
+            stop_move_action: {}
+        };
+        return this.sendData({
+            data: e
+        })
+    }
+    getOnVehicle({routeId: e, name: t, camera: r}) {
+        const n = {
+            action_type: Actions.GetOnVehicle,
+            get_on_vehicle_action: {
+                route_id: e,
+                name: t,
+                camera: r
+            }
+        };
+        return this.sendData({
+            data: n
+        })
+    }
+    getOffVehicle({renderType: e, player: t, camera: r}) {
+        const n = {
+            action_type: Actions.GetOffVehicle,
+            get_off_vehicle_action: {
+                render_type: e,
+                player: t,
+                camera: r
+            }
+        };
+        return this.sendData({
+            data: n
+        })
+    }
+    confirmEvent(e) {
+        const t = {
+            action_type: Actions.ConfirmEvent,
+            confirm_event_action: {
+                id: e
+            }
+        };
+        return this.sendData({
+            data: t,
+            track: !1
+        })
+    }
+    echo(e) {
+        const t = {
+            action_type: Actions.Echo,
+            echo_msg: {
+                echoMsg: e
+            }
+        };
+        return this.sendData({
+            data: t,
+            track: !1
+        })
+    }
+    async changeSkin(e) {
+        const t = e.special === void 0 ? e.renderType === RenderType.ClientRotationPano : e.special
+          , {skinId: r, mode: n, landingType: o=LandingType.Stay, landingPoint: a, landingCamera: s, renderType: l, areaName: u, attitude: c, pathName: h, person: f, noMedia: d, timeout: _, roomTypeId: g=""} = e
+          , m = this.room.skinList.filter(y=>y.id === r)[0];
+        if (!m) {
+            const y = `skin ${r} is invalid`;
+            return log$p.error(y),
+            Promise.reject(new ParamError(y))
+        }
+        const v = {
+            action_type: Actions.ChangeSkin,
+            change_skin_action: {
+                skinID: r,
+                mode: n === ChangeMode.Preview ? ChangeMode.Preview : ChangeMode.Confirm,
+                skin_data_version: r + m.versionId,
+                landing_type: o,
+                landing_point: a,
+                landing_camera: s,
+                render_wrapper: {
+                    render_type: l
+                },
+                areaName: u,
+                attitude: c,
+                noMedia: d,
+                person: f,
+                pathName: h,
+                roomTypeId: g
+            }
+        };
+        return this.sendData({
+            data: v,
+            timeout: _ || 6e3,
+            special: t
+        }).then(async y=>{
+            if (l === RenderType.ClientRotationPano && y) {
+                const b = await this.room.modelManager.findRoute(r, h)
+                  , {camera: T} = getRandomItem(b.birthPointList) || {};
+                await this.room.panorama.handleReceivePanorama(y, T)
+            }
+            return this.handleChangeSkin(e)
+        }
+        ).catch(y=>d ? this.handleChangeSkin(e) : Promise.reject(y))
+    }
+    handleChangeSkin(e) {
+        const {skinId: t, mode: r, renderType: n, areaName: o, attitude: a, pathName: s} = e;
+        return this.room.sceneManager.staticmeshComponent.getCgMesh().show(),
+        this.room.sceneManager.cameraComponent.switchToCgCamera(),
+        this.room.engineProxy._updateSkinAssets(t).then(()=>{
+            this.room.sceneManager.staticmeshComponent.getCgMesh().hide(),
+            this.room.sceneManager.cameraComponent.switchToMainCamera(),
+            this.room.pathManager.currentArea = o,
+            log$p.info("changeSkin _updateSkinAssets susccss"),
+            this.room.updateCurrentNetworkOptions({
+                pathName: s,
+                attitude: a,
+                areaName: o
+            }),
+            this.room.skinChangedHook(),
+            this.room.emit("skinChanged", {
+                skin: {
+                    id: t
+                },
+                mode: r
+            }),
+            n === RenderType.ClientRotationPano && this.room.sceneManager.cameraComponent.allowMainCameraController()
+        }
+        )
+    }
+    rotate({pitch: e, yaw: t}) {
+        var n;
+        if (this.room.disableRotate || this.room.isPano || ((n = this.room._userAvatar) == null ? void 0 : n._isChangingComponentsMode))
+            return;
+        const r = {
+            action_type: Actions.Rotation,
+            rotation_action: {
+                vertical_move: e,
+                horizontal_move: -t
+            }
+        };
+        this.sendData({
+            data: r,
+            sampleRate: .02
+        })
+    }
+    turnTo(e) {
+        const {point: t, timeout: r=2e3, offset: n=8} = e || {}
+          , o = {
+            action_type: Actions.TurnTo,
+            turn_to_action: {
+                turn_to_point: t,
+                offset: n
+            }
+        };
+        return this.sendData({
+            data: o,
+            timeout: r
+        })
+    }
+    rotateTo(e) {
+        const {point: t, offset: r=0, speed: n=3} = e || {}
+          , o = {
+            action_type: Actions.RotateTo,
+            rotate_to_action: {
+                rotate_to_point: t,
+                offset: r,
+                speed: n
+            }
+        };
+        return this.sendData({
+            data: o
+        })
+    }
+    broadcast(e) {
+        const {data: t, msgType: r=MessageHandleType.MHT_FollowListMulticast, targetUserIds: n} = e;
+        if (r === MessageHandleType.MHT_CustomTargetSync && !Array.isArray(n))
+            return Promise.reject(new ParamError(`param targetUserIds is required  when msgType is ${MessageHandleType[r]}`));
+        const o = {
+            action_type: Actions.Broadcast,
+            broadcast_action: {
+                data: JSON.stringify(t),
+                user_id: this.room.options.userId,
+                msgType: r
+            }
+        };
+        return Array.isArray(n) && r === MessageHandleType.MHT_CustomTargetSync && (o.broadcast_action.target_user_ids = n),
+        this.room.actionsHandler.sendData({
+            data: o,
+            tag: t.broadcastType
+        })
+    }
+    getNeighborPoints(e) {
+        const {point: t, containSelf: r=!1, searchRange: n=500} = e
+          , o = {
+            action_type: Actions.GetNeighborPoints,
+            get_neighbor_points_action: {
+                point: t,
+                level: 1,
+                containSelf: r,
+                searchRange: n
+            }
+        };
+        return this.sendData({
+            data: o
+        }).then(a=>a.nps)
+    }
+    playCG(e) {
+        const t = {
+            action_type: Actions.PlayCG,
+            play_cg_action: {
+                cg_name: e
+            }
+        };
+        return this.sendData({
+            data: t
+        })
+    }
+    audienceToVisitor(e) {
+        const {avatarId: t, avatarComponents: r, player: n, camera: o} = e
+          , a = {
+            action_type: Actions.AudienceChangeToVisitor,
+            audienceChangeToVisitorAction: {
+                avatarID: t,
+                avatarComponents: r,
+                player: n,
+                camera: o
+            }
+        };
+        return log$p.debug("send data: audience to visitor"),
+        this.sendData({
+            data: a
+        })
+    }
+    visitorToAudience(e) {
+        const {renderType: t, player: r, camera: n, areaName: o, attitude: a, pathName: s, person: l, noMedia: u} = e
+          , c = {
+            action_type: Actions.VisitorChangeToAudience,
+            visitorChangeToAudienceAction: {
+                transferAction: {
+                    render_type: t,
+                    player: r,
+                    camera: n,
+                    areaName: o,
+                    attitude: a,
+                    pathName: s,
+                    person: {
+                        type: l
+                    },
+                    noMedia: u,
+                    tiles: [0, 1, 2, 4]
+                }
+            }
+        };
+        return log$p.debug("send data: visitor to audience"),
+        this.sendData({
+            data: c
+        })
+    }
+    removeVisitor(e) {
+        const {removeType: t, userIDList: r, extraInfo: n=""} = e
+          , o = {
+            action_type: Actions.RemoveVisitor,
+            removeVisitorAction: {
+                removeVisitorEvent: t,
+                userIDList: r,
+                extraInfo: encodeURIComponent(n)
+            }
+        };
+        return log$p.debug("send data: remove visitor"),
+        this.sendData({
+            data: o
+        })
+    }
+    getUserWithAvatar(e, t) {
+        const r = {
+            action_type: Actions.GetUserWithAvatar,
+            getUserWithAvatarAction: {
+                userType: e,
+                roomID: t
+            }
+        };
+        return log$p.debug("send data: get user with avatar"),
+        this.sendData({
+            data: r
+        }).then(n=>n.userWithAvatarList)
+    }
+    joystick(e) {
+        const {degree: t, level: r=1} = e
+          , n = uuid$1();
+        let o = -t + 90 + 360;
+        o >= 360 && (o -= 360);
+        const a = {
+            action_type: Actions.Joystick,
+            dir_action: {
+                move_angle: o,
+                speed_level: r
+            },
+            trace_id: n,
+            user_id: this.room.options.userId,
+            packet_id: n
+        };
+        return this.sendData({
+            data: a,
+            track: !1
+        })
+    }
+}

+ 31 - 0
AxiosCanceler.js

@@ -0,0 +1,31 @@
+class AxiosCanceler {
+    constructor() {
+        E(this, "pendingMap", new Map)
+    }
+    addPending(e) {
+        return new axios.CancelToken(t=>{
+            this.pendingMap.has(e) || this.pendingMap.set(e, t)
+        }
+        )
+    }
+    removeAllPending() {
+        this.pendingMap.forEach(e=>{
+            e && isFunction(e) && e()
+        }
+        ),
+        this.pendingMap.clear()
+    }
+    removePending(e) {
+        if (this.pendingMap.has(e)) {
+            const t = this.pendingMap.get(e);
+            t && t(e),
+            this.pendingMap.delete(e)
+        }
+    }
+    removeCancelToken(e) {
+        this.pendingMap.has(e) && this.pendingMap.delete(e)
+    }
+    reset() {
+        this.pendingMap = new Map
+    }
+}

+ 191 - 0
BaseTable.js

@@ -0,0 +1,191 @@
+const log$e = new Logger("db");
+class BaseTable {
+    constructor(e, t) {
+        E(this, "db");
+        E(this, "isCreatingTable", !1);
+        E(this, "hasCleared", !1);
+        this.dbName = e,
+        this.dbVersion = t
+    }
+    async clearDataBase(e) {
+        return this.hasCleared || (e && (this.hasCleared = !0),
+        !window.indexedDB.databases) ? Promise.resolve() : new Promise((t,r)=>{
+            const n = window.indexedDB.deleteDatabase(this.dbName);
+            n.onsuccess = ()=>{
+                t()
+            }
+            ,
+            n.onerror = r
+        }
+        )
+    }
+    tableName() {
+        throw new Error("Derived class have to override 'tableName', and set a proper table name!")
+    }
+    keyPath() {
+        throw new Error("Derived class have to override 'keyPath', and set a proper index name!")
+    }
+    index() {
+        throw new Error("Derived class have to override 'index', and set a proper index name!")
+    }
+    async checkAndOpenDatabase() {
+        return this.db ? Promise.resolve(this.db) : new Promise((e,t)=>{
+            const n = setTimeout(()=>{
+                log$e.info("wait db to open for", 200),
+                this.db ? e(this.db) : e(this.checkAndOpenDatabase()),
+                clearTimeout(n)
+            }
+            , 200);
+            this.openDatabase(this.dbName, this.dbVersion || 1, ()=>{
+                this.db && !this.isCreatingTable && e(this.db),
+                log$e.info(`successCallback called, this.db: ${!!this.db}, this.isCreatingStore: ${this.isCreatingTable}`),
+                clearTimeout(n)
+            }
+            , ()=>{
+                t(new Error("Failed to open database!")),
+                clearTimeout(n)
+            }
+            , ()=>{
+                this.db && e(this.db),
+                clearTimeout(n),
+                log$e.info(`successCallback called, this.db: ${!!this.db}, this.isCreatingStore: ${this.isCreatingTable}`)
+            }
+            )
+        }
+        )
+    }
+    openDatabase(e, t, r, n, o) {
+        if (this.isCreatingTable)
+            return;
+        this.isCreatingTable = !0,
+        log$e.info(e, t);
+        const a = window.indexedDB.open(e, t)
+          , s = this.tableName();
+        a.onsuccess = l=>{
+            this.db = a.result,
+            log$e.info(`IndexedDb ${e} is opened.`),
+            this.db.objectStoreNames.contains(s) && (this.isCreatingTable = !1),
+            r && r(l)
+        }
+        ,
+        a.onerror = l=>{
+            var u;
+            log$e.error("Failed to open database", (u = l == null ? void 0 : l.srcElement) == null ? void 0 : u.error),
+            this.isCreatingTable = !1,
+            n && n(l),
+            this.clearDataBase(!0)
+        }
+        ,
+        a.onupgradeneeded = l=>{
+            const u = l.target.result
+              , c = this.index();
+            log$e.info(`Creating table ${s}.`);
+            let h = u.objectStoreNames.contains(s);
+            if (h)
+                h = u.transaction([s], "readwrite").objectStore(s);
+            else {
+                const f = this.keyPath();
+                h = u.createObjectStore(s, {
+                    keyPath: f
+                })
+            }
+            c.map(f=>{
+                h.createIndex(f, f, {
+                    unique: !1
+                })
+            }
+            ),
+            this.isCreatingTable = !1,
+            log$e.info(`Table ${s} opened`),
+            o && o(l)
+        }
+    }
+    async add(e) {
+        const t = this.tableName()
+          , o = (await this.checkAndOpenDatabase()).transaction([t], "readwrite").objectStore(t).add(e);
+        return new Promise(function(a, s) {
+            o.onsuccess = l=>{
+                a(l)
+            }
+            ,
+            o.onerror = l=>{
+                var u;
+                log$e.error((u = l.srcElement) == null ? void 0 : u.error),
+                s(l)
+            }
+        }
+        )
+    }
+    async put(e) {
+        const t = this.tableName()
+          , o = (await this.checkAndOpenDatabase()).transaction([t], "readwrite").objectStore(t).put(e);
+        return new Promise(function(a, s) {
+            o.onsuccess = l=>{
+                a(l)
+            }
+            ,
+            o.onerror = l=>{
+                var u;
+                log$e.error("db put error", (u = l.srcElement) == null ? void 0 : u.error),
+                s(l)
+            }
+        }
+        )
+    }
+    delete(e, t, r) {
+        const n = this.tableName();
+        this.checkAndOpenDatabase().then(o=>{
+            const s = o.transaction([n], "readwrite").objectStore(n).delete(e);
+            s.onsuccess = t,
+            s.onerror = r
+        }
+        )
+    }
+    update() {
+        this.checkAndOpenDatabase().then(e=>{}
+        )
+    }
+    async getAllKeys() {
+        const e = this.tableName()
+          , t = await this.checkAndOpenDatabase();
+        return new Promise((r,n)=>{
+            const a = t.transaction([e], "readonly").objectStore(e).getAllKeys();
+            a.onsuccess = s=>{
+                r(s.target.result)
+            }
+            ,
+            a.onerror = s=>{
+                log$e.error("db getAllKeys error", s),
+                n(s)
+            }
+        }
+        )
+    }
+    async query(e, t) {
+        const r = this.tableName()
+          , n = await this.checkAndOpenDatabase();
+        return new Promise((o,a)=>{
+            const u = n.transaction([r], "readonly").objectStore(r).index(e).get(t);
+            u.onsuccess = function(c) {
+                var f;
+                const h = (f = c == null ? void 0 : c.target) == null ? void 0 : f.result;
+                o && o(h)
+            }
+            ,
+            u.onerror = c=>{
+                log$e.error("db query error", c),
+                a(c)
+            }
+        }
+        )
+    }
+    async sleep(e) {
+        return new Promise(t=>{
+            setTimeout(()=>{
+                t("")
+            }
+            , e)
+        }
+        )
+    }
+}

+ 68 - 0
BreathPoint.js

@@ -0,0 +1,68 @@
+class BreathPoint {
+    constructor(e) {
+        E(this, "_staticmesh");
+        E(this, "_id");
+        E(this, "_mat");
+        E(this, "_type");
+        E(this, "_maxVisibleRegion");
+        E(this, "_skinInfo");
+        E(this, "_scene");
+        E(this, "_isInScene");
+        const {mesh: t, id: r, position: n, rotation: o, mat: a, type: s="default", maxVisibleRegion: l=-1, scene: u, skinInfo: c="default"} = e;
+        this._id = r,
+        t.mesh.position = ue4Position2Xverse(n),
+        t.mesh.rotation = ue4Rotation2Xverse(o),
+        this._staticmesh = t,
+        this._mat = a,
+        this._type = s,
+        this._maxVisibleRegion = l,
+        this._scene = u,
+        this._skinInfo = c,
+        this._isInScene = !0
+    }
+    get isInScene() {
+        return this._isInScene
+    }
+    get skinInfo() {
+        return this._skinInfo
+    }
+    get maxvisibleregion() {
+        return this._maxVisibleRegion
+    }
+    getMesh() {
+        return this._staticmesh
+    }
+    get mesh() {
+        return this._staticmesh.mesh
+    }
+    toggleVisibility(e) {
+        e == !0 ? this._staticmesh.show() : this._staticmesh.hide()
+    }
+    changePickable(e) {
+        this._staticmesh.mesh.isPickable = e
+    }
+    removeFromScene() {
+        this._isInScene && (this._staticmesh.mesh != null && this._scene.removeMesh(this._staticmesh.mesh),
+        this._isInScene = !1)
+    }
+    addToScene() {
+        this._isInScene == !1 && (this._staticmesh.mesh != null && this._scene.addMesh(this._staticmesh.mesh),
+        this._isInScene = !0)
+    }
+    dispose() {
+        var e;
+        (e = this._staticmesh.mesh) == null || e.dispose(!1, !1)
+    }
+    set position(e) {
+        this._staticmesh.mesh.position = ue4Position2Xverse(e)
+    }
+    get position() {
+        return xversePosition2Ue4(this._staticmesh.mesh.position)
+    }
+    set rotation(e) {
+        this._staticmesh.mesh.rotation = ue4Rotation2Xverse(e)
+    }
+    get rotation() {
+        return xverseRotation2Ue4(this._staticmesh.mesh.rotation)
+    }
+}

+ 25 - 0
Broadcast.js

@@ -0,0 +1,25 @@
+const log$q = new Logger("xverse-broadcast")
+class Broadcast{
+    constructor(e, t) {
+        this.room = e,
+        Ee.handlers.push(t)
+    }
+    async handleBroadcast(e) {
+        let t = null;
+        try {
+            t = JSON.parse(e.broadcastAction.data)
+        } catch (r) {
+            log$q.error(r);
+            return
+        }
+    }
+    broadcast(e) {
+        const {data: t, msgType: r=MessageHandleType.MHT_FollowListMulticast, targetUserIds: n} = e;
+        return this.room.actionsHandler.broadcast({
+            data: t,
+            msgType: r,
+            targetUserIds: n
+        })
+    }
+}
+;

+ 201 - 0
Camera.js

@@ -0,0 +1,201 @@
+const log$9 = new Logger("camera");
+class Camera extends EventEmitter {
+    constructor(e) {
+        super();
+        E(this, "initialFov", 0);
+        E(this, "_state", CameraStates.Normal);
+        E(this, "_person", Person.Third);
+        E(this, "_room");
+        E(this, "_cameraFollowing", !0);
+        E(this, "checkPointOnLeftOrRight", e=>{
+            const t = ue4Position2Xverse(e);
+            if (!t || this.checkPointInView(e))
+                return;
+            const o = this._room.scene.activeCamera;
+            if (!o)
+                return;
+            const a = [o.target.x, o.target.y, o.target.z]
+              , s = [o.position.x, o.position.y, o.position.z]
+              , {x: l, y: u, z: c} = t
+              , h = calNormVector(s, a)
+              , f = calNormVector(s, [l, u, c]);
+            return vectorCrossMulti(h, f) < 0 ? Direction.Right : Direction.Left
+        }
+        );
+        E(this, "checkPointInView", ({x: e, y: t, z: r})=>{
+            const n = ue4Position2Xverse({
+                x: e,
+                y: t,
+                z: r
+            });
+            if (!n)
+                return !1;
+            for (let o = 0; o < 6; o++)
+                if (this._room.scene.frustumPlanes[o].dotCoordinate(n) < 0)
+                    return !1;
+            return !0
+        }
+        );
+        this._room = e
+    }
+    get person() {
+        return this._person
+    }
+    get state() {
+        return this._state
+    }
+    get pose() {
+        return this._room.currentClickingState.camera
+    }
+    set cameraFollowing(e) {
+        log$9.info("cameraFollowing setter", e),
+        this.setCameraFollowing({
+            isFollowHost: e
+        })
+    }
+    get cameraFollowing() {
+        return this._cameraFollowing
+    }
+    setCameraFollowing({isFollowHost: e}) {}
+    handleRenderInfo(e) {
+        const {cameraStateType: t} = e.renderInfo
+          , r = this._room.sceneManager;
+        if (t !== this._state && (this._state = t,
+        log$9.debug("camera._state changed to", CameraStates[t]),
+        t === CameraStates.CGView ? (r.cameraComponent.switchToCgCamera(),
+        r.staticmeshComponent.getCgMesh().show()) : (r.cameraComponent.switchToMainCamera(),
+        r.staticmeshComponent.getCgMesh().hide()),
+        this.emit("stateChanged", {
+            state: t
+        })),
+        this._room.isHost)
+            return;
+        const {isFollowHost: n} = e.playerState;
+        !!n !== this._cameraFollowing && (this._cameraFollowing = !!n,
+        this.emit("cameraFollowingChanged", {
+            cameraFollowing: !!n
+        }))
+    }
+    setCameraState({state: e}) {
+        if (this._state === e) {
+            log$9.warn(`You are already in ${CameraStates[e]} camera state`);
+            return
+        }
+        e === CameraStates.Normal || this._state === CameraStates.ItemView && log$9.warn("CloseUp camera state can only be triggerd by room internally")
+    }
+    turnToFace({extra: e="", offset: t=0}) {
+        const r = {
+            action_type: Actions.TurnToFace,
+            turn_to_face_action: {
+                offset: t
+            }
+        };
+        return this.emit("viewChanged", {
+            extra: e
+        }),
+        this._room.actionsHandler.sendData({
+            data: r
+        })
+    }
+    isInDefaultView() {
+        if (!this._room.isHost) {
+            log$9.warn("It is recommended to call the function on the host side");
+            return
+        }
+        if (!this._room._currentClickingState)
+            return log$9.error("CurrentState should not be empty"),
+            !1;
+        const {camera: e, player: t} = this._room._currentClickingState;
+        return Math.abs(t.angle.yaw - 180 - e.angle.yaw) % 360 <= 4
+    }
+    async screenShot({name: e, autoSave: t=!1}) {
+        const r = this._room.scene.getEngine()
+          , n = this._room.scene.activeCamera;
+        try {
+            this._room.sceneManager.setImageQuality(EImageQuality.high);
+            const o = await CreateScreenshotAsync(r, n, {
+                precision: 1
+            });
+            return this._room.sceneManager.setImageQuality(EImageQuality.low),
+            t === !0 && downloadFileByBase64(o, e),
+            Promise.resolve(o)
+        } catch (o) {
+            return this._room.sceneManager.setImageQuality(EImageQuality.low),
+            Promise.reject(o)
+        }
+    }
+    changeToFirstPerson(e, t, r) {
+        const {camera: n, player: o, attitude: a, areaName: s, pathName: l} = e;
+        return this._room.actionsHandler.requestPanorama({
+            camera: n,
+            player: o,
+            attitude: a,
+            areaName: s,
+            pathName: l
+        }, t, r).then(()=>{
+            this._room.networkController.rtcp.workers.changePanoMode(!0);
+            const {position: u, angle: c} = o || {};
+            this._room.sceneManager.cameraComponent.changeToFirstPersonView({
+                position: u,
+                rotation: c
+            })
+        }
+        )
+    }
+    setPerson(e, t={
+        camera: this._room._currentClickingState.camera,
+        player: this._room._currentClickingState.player
+    }) {
+        const r = Date.now();
+        return this._setPerson(e, t).then(n=>(log$9.infoAndReportMeasurement({
+            tag: Person[e],
+            startTime: r,
+            metric: "setPerson"
+        }),
+        n)).catch(n=>(log$9.infoAndReportMeasurement({
+            tag: Person[e],
+            startTime: r,
+            metric: "setPerson",
+            error: n
+        }),
+        Promise.reject(n)))
+    }
+    _setPerson(e, t={
+        camera: this._room._currentClickingState.camera,
+        player: this._room._currentClickingState.player
+    }) {
+        return e !== Person.First && e !== Person.Third ? Promise.reject("invalid person " + e) : !t.camera || !t.player ? Promise.reject(new ParamError("wrong camera or player")) : e === Person.First ? this._room.panorama.access({
+            camera: t.camera,
+            player: t.player,
+            tag: "setPerson"
+        }).then(()=>{
+            var o, a;
+            this._person = e,
+            (o = this._room._userAvatar) == null || o.hide();
+            const {position: r, angle: n} = ((a = this._room.currentClickingState) == null ? void 0 : a.camera) || {};
+            !r || !n || this._room.sceneManager.cameraComponent.changeToFirstPersonView({
+                position: r,
+                rotation: n
+            })
+        }
+        ) : this._room.panorama.exit({
+            camera: t.camera,
+            player: t.player
+        }).then(()=>{
+            var r, n;
+            this._person = e,
+            (r = this._room._userAvatar) != null && r.xAvatar && ((n = this._room._userAvatar) == null || n.xAvatar.show())
+        }
+        )
+    }
+    setCameraPose(e) {
+        this._room.sceneManager.cameraComponent.setCameraPose({
+            position: e.position,
+            rotation: e.angle
+        })
+    }
+    setMainCameraRotationLimit(e) {
+        const {limitAxis: t, limitRotation: r} = e;
+        this._room.sceneManager.cameraComponent.setMainCameraRotationLimit(t, r)
+    }
+}

+ 106 - 0
CircularArray.js

@@ -0,0 +1,106 @@
+class CircularArray {
+    constructor(e, t, r) {
+        this.sum = 0,
+        this.incomingSum = 0,
+        this.count = 0,
+        this.incomingCount = 0,
+        this.max = 0,
+        this.incomingMax = 0,
+        this.goodLess = 0,
+        this.wellLess = 0,
+        this.fairLess = 0,
+        this.badLess = 0,
+        this.countLess = !1,
+        this.lessThreshes = [],
+        this.incomingData = [],
+        this.circularData = Array(e).fill(-1),
+        this.circularPtr = 0,
+        this.circularLength = e,
+        t && (this.countLess = !0,
+        this.lessThreshes = r)
+    }
+    add(e) {
+        this.circularData[this.circularPtr] != -1 ? (this.sum -= this.circularData[this.circularPtr],
+        Math.abs(this.circularData[this.circularPtr] - this.max) < .01 && (this.circularData[this.circularPtr] = -1,
+        this.max = this.getMax(!1))) : this.count += 1,
+        this.sum += e,
+        this.incomingSum += e,
+        this.incomingCount += 1,
+        this.max < e && (this.max = e),
+        this.incomingMax < e && (this.incomingMax = e),
+        this.circularData[this.circularPtr] = e,
+        this.circularPtr = (this.circularPtr + 1) % this.circularLength,
+        this.incomingData.push(e),
+        this.incomingData.length > this.circularLength && (this.clearIncoming(),
+        this.incomingCount = 0,
+        this.incomingSum = 0)
+    }
+    computeAvg(e) {
+        return e.reduce(add, 0) / e.reduce(count_valid, 0) || 0
+    }
+    computeMax(e) {
+        return e.reduce(max, 0) || 0
+    }
+    computeThreshPercent(e) {
+        if (this.countLess) {
+            const t = count_less(e, this.lessThreshes[0]) || 0
+              , r = count_less(e, this.lessThreshes[1]) || 0
+              , n = count_less(e, this.lessThreshes[2]) || 0
+              , o = count_less(e, this.lessThreshes[3]) || 0
+              , a = e.reduce(count_valid, 0);
+            return [t, r, n, o, a]
+        } else
+            return [0, 0, 0, 0, 0]
+    }
+    getAvg() {
+        const e = this.sum / this.count || 0
+          , t = this.computeAvg(this.circularData) || 0;
+        return Math.abs(e - t) > .01 && console.error("avg value mismatch: ", e, t),
+        this.computeAvg(this.circularData) || 0
+    }
+    getMax(e=!0) {
+        const t = this.computeMax(this.circularData) || 0;
+        return e && Math.abs(t - this.max) > .01 && console.error("max value mismatch: ", this.max, t),
+        this.computeMax(this.circularData) || 0
+    }
+    getStandardDeviation() {
+        return count_sd(this.circularData, this.getAvg())
+    }
+    getThreshPercent() {
+        return this.computeThreshPercent(this.circularData)
+    }
+    getIncomingMax() {
+        return this.computeMax(this.incomingData) || 0
+    }
+    getIncomingAvg() {
+        return this.computeAvg(this.incomingData) || 0
+    }
+    getIncomingStandardDeviation() {
+        return count_sd(this.incomingData, this.getIncomingAvg())
+    }
+    getIncomingThreshPercent() {
+        return this.computeThreshPercent(this.incomingData)
+    }
+    clearFastComputeItem() {
+        this.sum = 0,
+        this.incomingSum = 0,
+        this.count = 0,
+        this.incomingCount = 0,
+        this.max = 0,
+        this.incomingMax = 0,
+        this.goodLess = 0,
+        this.wellLess = 0,
+        this.fairLess = 0,
+        this.badLess = 0
+    }
+    clearIncoming() {
+        for (; this.incomingData.length > 0; )
+            this.incomingData.pop()
+    }
+    clear() {
+        this.circularData.fill(-1),
+        this.circularPtr = 0,
+        this.clearFastComputeItem(),
+        this.clearIncoming()
+    }
+}

+ 43 - 0
DataStorage.js

@@ -0,0 +1,43 @@
+class DataStorage{
+    static _GetStorage() {
+        try {
+            return localStorage.setItem("test", ""),
+            localStorage.removeItem("test"),
+            localStorage
+        } catch {
+            const e = {};
+            return {
+                getItem: t=>{
+                    const r = e[t];
+                    return r === void 0 ? null : r
+                }
+                ,
+                setItem: (t,r)=>{
+                    e[t] = r
+                }
+            }
+        }
+    }
+    static ReadString(e, t) {
+        const r = this._Storage.getItem(e);
+        return r !== null ? r : t
+    }
+    static WriteString(e, t) {
+        this._Storage.setItem(e, t)
+    }
+    static ReadBoolean(e, t) {
+        const r = this._Storage.getItem(e);
+        return r !== null ? r === "true" : t
+    }
+    static WriteBoolean(e, t) {
+        this._Storage.setItem(e, t ? "true" : "false")
+    }
+    static ReadNumber(e, t) {
+        const r = this._Storage.getItem(e);
+        return r !== null ? parseFloat(r) : t
+    }
+    static WriteNumber(e, t) {
+        this._Storage.setItem(e, t.toString())
+    }
+}
+;

+ 121 - 0
Debug.js

@@ -0,0 +1,121 @@
+const BREATH_POINT_TYPE = "debugBreathPoint"
+  , TAP_BREATH_POINT_TYPE = "debugTapBreathPoint"
+  , DEFAULT_SEARCH_RANGE = 1e3;
+class Debug {
+    constructor(e) {
+        E(this, "isShowNearbyBreathPoints", !1);
+        E(this, "isShowTapBreathPoints", !1);
+        E(this, "isSceneShading", !0);
+        E(this, "searchRange", DEFAULT_SEARCH_RANGE);
+        E(this, "nearbyBreathPointListening", !1);
+        E(this, "tapBreathPointListening", !1);
+        E(this, "dumpStreamTimer", 0);
+        this.room = e
+    }
+    toggleStats() {
+        return this.room.stats.isShow ? this.room.stats.hide() : this.room.stats.show()
+    }
+    toggleNearbyBreathPoint(e=DEFAULT_SEARCH_RANGE) {
+        this.searchRange = e,
+        this.isShowNearbyBreathPoints = !this.isShowNearbyBreathPoints,
+        this.isShowNearbyBreathPoints ? (this.getPointsAndRender(),
+        this.setupNearbyBreathPointListener()) : this.room.breathPointManager.clearBreathPoints(BREATH_POINT_TYPE)
+    }
+    toggleTapBreathPoint() {
+        this.isShowTapBreathPoints = !this.isShowTapBreathPoints,
+        this.isShowTapBreathPoints ? this.setupTapPointListener() : this.room.breathPointManager.clearBreathPoints(TAP_BREATH_POINT_TYPE)
+    }
+    dumpStream(e, t=10 * 1e3) {
+        if (this.dumpStreamTimer)
+            throw new Error("dumpStream running");
+        this.room.networkController.rtcp.workers.saveframe = !0,
+        this.dumpStreamTimer = window.setTimeout(()=>{
+            this.room.networkController.rtcp.workers.SaveMediaStream = !0,
+            this.dumpStreamTimer = 0,
+            e && e()
+        }
+        , t)
+    }
+    toggleSceneshading() {
+        this.isSceneShading = !this.isSceneShading,
+        this.isSceneShading ? this.room.sceneManager.changeVideoShaderForLowModel() : this.room.sceneManager.changeDefaultShaderForLowModel()
+    }
+    setupTapPointListener() {
+        this.tapBreathPointListening || (this.tapBreathPointListening = !0,
+        this.room.on("_coreClick", ({point: e})=>{
+            this.isShowTapBreathPoints && this.renderTapBreathPoint({
+                id: "tapToint",
+                position: e
+            })
+        }
+        ))
+    }
+    renderTapBreathPoint({position: e, id: t}) {
+        let r;
+        if (r = this.room.breathPointManager.breathPoints.get(t)) {
+            r.position = e;
+            return
+        }
+        this.room.breathPointManager.addBreathPoint({
+            id: t,
+            position: e,
+            type: TAP_BREATH_POINT_TYPE,
+            size: .8,
+            forceLeaveGround: !0,
+            billboardMode: !0,
+            rotation: Math.abs(e.z) < 20 ? {
+                pitch: 90,
+                yaw: 0,
+                roll: 0
+            } : {
+                pitch: 0,
+                yaw: 270,
+                roll: 0
+            }
+        })
+    }
+    setupNearbyBreathPointListener() {
+        var e;
+        this.nearbyBreathPointListening || (this.nearbyBreathPointListening = !0,
+        (e = this.room._userAvatar) == null || e.on("stopMoving", ()=>{
+            this.isShowNearbyBreathPoints && this.getPointsAndRender()
+        }
+        ))
+    }
+    async getPointsAndRender() {
+        var r, n;
+        const e = this.searchRange
+          , t = ((r = this.room._userAvatar) == null ? void 0 : r.position) && await this.getNeighborPoints({
+            point: (n = this.room._userAvatar) == null ? void 0 : n.position,
+            containSelf: !0,
+            searchRange: e
+        }) || [];
+        this.room.breathPointManager.breathPoints.forEach(o=>{
+            !!t.find(s=>JSON.stringify(s) === o._id) || this.room.breathPointManager.clearBreathPoints(o._id)
+        }
+        ),
+        t.forEach(o=>{
+            const a = JSON.stringify(o);
+            this.room.breathPointManager.breathPoints.get(a) || this.room.breathPointManager.addBreathPoint({
+                id: a,
+                position: o,
+                type: BREATH_POINT_TYPE,
+                rotation: {
+                    pitch: 90,
+                    yaw: 0,
+                    roll: 0
+                },
+                forceLeaveGround: !0
+            })
+        }
+        )
+    }
+    getNeighborPoints(e) {
+        const {point: t, containSelf: r=!1, searchRange: n=500} = e;
+        return this.room.actionsHandler.getNeighborPoints({
+            point: t,
+            containSelf: r,
+            searchRange: n
+        })
+    }
+}

+ 623 - 0
EngineProxy.js

@@ -0,0 +1,623 @@
+const log$a = new Logger("xverse-bus")
+class EngineProxy{
+    constructor(e) {
+        E(this, "_tvs", []);
+        E(this, "isRenderFirstFrame", !1);
+        E(this, "_idleTime", 0);
+        E(this, "renderTimer");
+        E(this, "lightManager");
+        E(this, "_checkSceneNotReadyCount", 0);
+        E(this, "_checkSceneDurationFrameNum", 0);
+        E(this, "_checkSceneFrameCount", 0);
+        E(this, "timeoutCircularArray", new CircularArray(120,!1,[]));
+        E(this, "frameCircularArray", new CircularArray(120,!1,[]));
+        E(this, "interFrameCircularArray", new CircularArray(120,!1,[]));
+        E(this, "drawCallCntCircularArray", new CircularArray(120,!1,[]));
+        E(this, "activeFacesCircularArray", new CircularArray(120,!1,[]));
+        E(this, "renderTimeCircularArray", new CircularArray(120,!1,[]));
+        E(this, "drawCallTimeCircularArray", new CircularArray(120,!1,[]));
+        E(this, "animationCircularArray", new CircularArray(120,!1,[]));
+        E(this, "meshSelectCircularArray", new CircularArray(120,!1,[]));
+        E(this, "renderTargetCircularArray", new CircularArray(120,!1,[]));
+        E(this, "regBeforeRenderCircularArray", new CircularArray(120,!1,[]));
+        E(this, "regAfterRenderCircularArray", new CircularArray(120,!1,[]));
+        E(this, "renderCnt", 0);
+        E(this, "renderErrorCount", 0);
+        E(this, "engineSloppyCnt", 0);
+        E(this, "systemStuckCnt", 0);
+        E(this, "frameRenderNumber", 0);
+        E(this, "_setFPS", (e,t=25)=>{
+            log$a.info("Set fps to", t);
+            const r = t > 60 ? 60 : t < 24 ? 24 : t;
+            e.Engine.stopRenderLoop();
+            const n = 1e3 / r;
+            let o = Date.now()
+              , a = Date.now()
+              , s = n
+              , l = 1;
+            const u = ()=>{
+                var T;
+                const c = Date.now()
+                  , h = c - o
+                  , f = c - a;
+                a = c,
+                this.frameCircularArray.add(f),
+                h - s > n && (this.systemStuckCnt += 1);
+                const d = h / s;
+                l = .9 * l + .1 * d;
+                const _ = Date.now();
+                let g = 0
+                  , m = 0;
+                if (this.room.isUpdatedRawYUVData || this.room.isPano) {
+                    if (this.isRenderFirstFrame = !0,
+                    this._checkSceneDurationFrameNum > 0)
+                        this._checkSceneFrameCount++,
+                        this.room.sceneManager.isReadyToRender({}) && this._checkSceneDurationFrameNum--,
+                        this._checkSceneFrameCount > ye._CHECK_DURATION && (this._checkSceneDurationFrameNum = ye._CHECK_DURATION,
+                        this._checkSceneFrameCount = 0,
+                        this._checkSceneNotReadyCount++,
+                        (this._checkSceneNotReadyCount == 1 || this._checkSceneNotReadyCount % 100 == 0) && log$a.error(`[SDK] Scene not ready, skip render. loop: ${this._checkSceneNotReadyCount}`),
+                        this._checkSceneNotReadyCount > 10 && (log$a.error("[SDK] Scene not ready, reload later"),
+                        this.room.proxyEvents("renderError", {
+                            error: new Error("[SDK] Scene not ready, skip render and reload.")
+                        })),
+                        this.room.stats.assign({
+                            renderErrorCount: this._checkSceneNotReadyCount
+                        }),
+                        log$a.infoAndReportMeasurement({
+                            value: 0,
+                            startTime: Date.now(),
+                            metric: "renderError",
+                            error: new Error("[SDK] Scene not ready, skip render and reload."),
+                            reportOptions: {
+                                sampleRate: .1
+                            }
+                        }));
+                    else
+                        try {
+                            e.render()
+                        } catch (C) {
+                            this.renderErrorCount++,
+                            this.renderErrorCount > 10 && this.room.proxyEvents("renderError", {
+                                error: C
+                            }),
+                            this.room.stats.assign({
+                                renderErrorCount: this.renderErrorCount
+                            }),
+                            log$a.infoAndReportMeasurement({
+                                value: 0,
+                                startTime: Date.now(),
+                                metric: "renderError",
+                                error: C,
+                                reportOptions: {
+                                    sampleRate: .1
+                                }
+                            })
+                        }
+                    g = Date.now() - _,
+                    this.frameRenderNumber < 1e3 && this.frameRenderNumber++,
+                    this.room.networkController.rtcp.workers.UpdateYUV(),
+                    m = Date.now() - _ - g
+                }
+                this.isRenderFirstFrame || this.room.networkController.rtcp.workers.UpdateYUV();
+                const y = Date.now() - _;
+                o = c + y,
+                s = Math.min(Math.max((n - y) / l, 5), 200),
+                y > n && (s = 10,
+                this.engineSloppyCnt += 1),
+                this._idleTime = s;
+                const b = s;
+                if (s > 150 && console.log("lastGap is ", s, ", ratio is ", l, ", usedTimeMs is ", y, ", cpuRenderTime is ", g, ", cpuUpdateYUVTime is ", m),
+                this.timeoutCircularArray.add(b),
+                this.renderCnt % 25 == 0) {
+                    const C = this.frameCircularArray.getAvg()
+                      , A = this.timeoutCircularArray.getAvg()
+                      , S = this.frameCircularArray.getMax()
+                      , P = this.timeoutCircularArray.getMax();
+                    (T = this.room.stats) == null || T.assign({
+                        avgFrameTime: C,
+                        avgTimeoutTime: A,
+                        maxFrameTime: S,
+                        maxTimeoutTime: P,
+                        systemStuckCnt: this.systemStuckCnt
+                    })
+                }
+                this.renderTimer = window.setTimeout(u, s)
+            }
+            ;
+            this.renderTimer = window.setTimeout(u, n / l)
+        }
+        );
+        E(this, "updateStats", ()=>{
+            var e;
+            (e = this.room.stats) == null || e.assign({
+                renderFrameTime: this.renderTimeCircularArray.getAvg(),
+                maxRenderFrameTime: this.renderTimeCircularArray.getMax(),
+                interFrameTime: this.interFrameCircularArray.getAvg(),
+                animationTime: this.animationCircularArray.getAvg(),
+                meshSelectTime: this.meshSelectCircularArray.getAvg(),
+                drawcallTime: this.drawCallTimeCircularArray.getAvg(),
+                idleTime: this._idleTime,
+                registerBeforeRenderTime: this.regBeforeRenderCircularArray.getAvg(),
+                registerAfterRenderTime: this.regAfterRenderCircularArray.getAvg(),
+                renderTargetRenderTime: this.renderTargetCircularArray.getAvg(),
+                fps: (1e3 / (this.renderTimeCircularArray.getAvg() + this.interFrameCircularArray.getAvg())).toFixed(2),
+                drawcall: this.drawCallCntCircularArray.getAvg(),
+                engineSloppyCnt: this.engineSloppyCnt,
+                maxInterFrameTime: this.interFrameCircularArray.getMax(),
+                maxDrawcallTime: this.drawCallTimeCircularArray.getMax(),
+                maxMeshSelectTime: this.meshSelectCircularArray.getMax(),
+                maxAnimationTime: this.animationCircularArray.getMax(),
+                maxRegisterBeforeRenderTime: this.regBeforeRenderCircularArray.getMax(),
+                maxRegisterAfterRenderTime: this.regAfterRenderCircularArray.getMax(),
+                maxRenderTargetRenderTime: this.renderTargetCircularArray.getMax(),
+                avgFrameTime: this.frameCircularArray.getAvg(),
+                avgTimeoutTime: this.timeoutCircularArray.getAvg(),
+                maxFrameTime: this.frameCircularArray.getMax(),
+                maxTimeoutTime: this.timeoutCircularArray.getMax()
+            })
+        }
+        );
+        this.room = e
+    }
+    async initEngine(e) {
+        await this.updateBillboard(),
+        log$a.info("engine version:", VERSION$1);
+        const t = new Logger("engine");
+        t.setLevel(LoggerLevels.Warn);
+        const r = {
+            videoResOriArray: [{
+                width: 720,
+                height: 1280
+            }, {
+                width: 1280,
+                height: 720
+            }, {
+                width: 480,
+                height: 654
+            }, {
+                width: 654,
+                height: 480
+            }, {
+                width: 1920,
+                height: 1080
+            }, {
+                width: 1080,
+                height: 1920
+            }, {
+                width: 414,
+                height: 896
+            }],
+            forceKeepVertical: this.room.options.objectFit !== "cover",
+            panoInfo: {
+                dynamicRange: 1,
+                width: 4096,
+                height: 2048
+            },
+            shaderMode: EShaderMode.videoAndPano,
+            yuvInfo: {
+                width: 1280,
+                height: 720,
+                fov: e.fov || DEFAULT_MAIN_CAMERA_FOV
+            },
+            cameraParam: {
+                maxZ: 1e4
+            },
+            urlTransformer,
+            logger: t,
+            disableWebGL2: this.room.options.disableWebGL2 || !1
+        }
+          , n = this.room.options.resolution;
+        n && (r.videoResOriArray.some(l=>l.width === n.width && l.height === n.height) || r.videoResOriArray.push(n));
+        const o = this.room.sceneManager = getSceneManager(this.room.canvas, r);
+        this.room.setPictureQualityLevel(this.room.options.pictureQualityLevel || "high"),
+        this.room.sceneManager.staticmeshComponent.setRegionLodRule([2, 2, -1, -1, -1]),
+        this.room.scene = o.Scene,
+        this.room.breathPointManager = o.breathPointComponent,
+        this.lightManager = o.lightComponent,
+        this.registerStats(),
+        this.setEnv(e),
+        await this.room.avatarManager.init();
+        const a = this._createAssetList(e);
+        await this.loadAssets(a, ""),
+        this._setFPS(o)
+    }
+    pause() {
+        clearTimeout(this.renderTimer),
+        log$a.info("Invoke room.pause to pause render");
+        const e = {
+            roomId: this.room.id,
+            effects: [],
+            lowPolyModels: [],
+            breathPointsConfig: [],
+            skinId: this.room.skinId
+        };
+        return this.loadAssets(e, this.room.skinId)
+    }
+    async resume() {
+        this._setFPS(this.room.sceneManager),
+        this.room.sceneManager.cameraComponent.cameraFovChange(this.room.sceneManager.yuvInfo),
+        log$a.info("Invoke room.resume to render");
+        const e = this._createAssetList(this.room.skin);
+        await this.loadAssets(e, "")
+    }
+    setEnv(e) {
+        var r;
+        this.lightManager || (this.lightManager = this.room.sceneManager.lightComponent),
+        e = e || this.room.skin;
+        const t = ModelManager.findModel(e.models, AssetTypeName.Config, AssetClassName.Env);
+        return t ? (r = this.lightManager) == null ? void 0 : r.setIBL(t.modelUrl) : (log$a.error("env file not found"),
+        Promise.resolve())
+    }
+    async _parseModelsAndLoad(e, t, r) {
+        log$a.info("Invoke _parseModelsAndLoad start", t);
+        const n = ["airship", "balloon", "default", "ground_feiting", "ground_reqiqiu"]
+          , o = new Map;
+        r == null && (r = "xxxx");
+        let a = !0;
+        for (let u = 0; u < e.length; ++u) {
+            a = !0;
+            for (let c = 0; c < n.length; ++c)
+                if (e[u].modelUrl.toLowerCase().indexOf(n[c]) >= 0) {
+                    const h = o.get(n[c]);
+                    h ? (h.push(e[u]),
+                    o.set(n[c], h)) : o.set(n[c], [e[u]]),
+                    a = !1;
+                    break
+                }
+            if (a) {
+                const c = o.get("default");
+                c ? (c.push(e[u]),
+                o.set("default", c)) : o.set("default", [e[u]])
+            }
+        }
+        let s = o.get(t) || [];
+        if (this.room.viewMode === "simple" && (s = s.filter(u=>!u.modelUrl.endsWith("zip"))),
+        !s)
+            return Promise.reject(`no invalid scene model with group name: ${t}`);
+        const l = [];
+        for (let u = 0; u < s.length; ++u) {
+            const c = s[u];
+            if (c.modelUrl.toLowerCase().endsWith("zip"))
+                c.modelUrl.toLowerCase().endsWith("zip") && l.push(this.room.sceneManager.addNewLowPolyMesh({
+                    url: c.modelUrl,
+                    skinInfo: r
+                }));
+            else {
+                const h = t;
+                l.push(this.room.sceneManager.addNewLowPolyMesh({
+                    url: c.modelUrl,
+                    group: h,
+                    pick: !0,
+                    skinInfo: r
+                }))
+            }
+        }
+        return Promise.all(l)
+    }
+    async _deleteAssetsLowpolyModel(e) {
+        this.room.sceneManager.staticmeshComponent.deleteMeshesBySkinInfo(e),
+        this.room.sceneManager.breathPointComponent.clearBreathPointsBySkinInfo(e),
+        this.room.sceneManager.decalComponent.deleteDecalBySkinInfo(e);
+        const t = [];
+        this.room.sceneManager.Scene.meshes.forEach(r=>{
+            r.xskinInfo == e && t.push(r)
+        }
+        ),
+        t.forEach(r=>{
+            r.dispose(!1, !1)
+        }
+        )
+    }
+    async loadLandAssets() {
+        const e = this._createAssetList(this.room.skin);
+        return this.loadAssets(e, this.room.skinId).catch(()=>this.loadAssets(e, this.room.skinId))
+    }
+    async loadAssets(e, t="", r=8e3) {
+        const n = Date.now();
+        return this._loadAssets(e, t)._timeout(r, new InitEngineTimeoutError(`loadAssets timeout(${r}ms)`)).then(o=>(log$a.infoAndReportMeasurement({
+            tag: "loadAssets",
+            startTime: n,
+            metric: "loadAssets"
+        }),
+        o)).catch(o=>(log$a.infoAndReportMeasurement({
+            tag: "loadAssets",
+            startTime: n,
+            metric: "loadAssets",
+            error: o
+        }),
+        Promise.reject(o)))
+    }
+    async _loadAssets(e, t="") {
+        try {
+            const r = [];
+            r.push(this._loadAssetsLowpolyModel(e, t)),
+            await Promise.all(r),
+            await this.setEnv(),
+            this._checkSceneDurationFrameNum = ye._CHECK_DURATION,
+            this._checkSceneNotReadyCount = 0,
+            this._checkSceneFrameCount = 0,
+            this.updateAnimationList(),
+            this.room.loadAssetsHook()
+        } catch (r) {
+            return Promise.reject(r)
+        }
+    }
+    updateAnimationList() {
+        if (this.room.avatarManager && this.room.avatarManager.xAvatarManager) {
+            const e = this.room.skin.animationList;
+            if (!e)
+                return;
+            e.forEach(t=>{
+                this.room.avatarManager.xAvatarManager.updateAnimationLists(t.animations, t.avatarId)
+            }
+            )
+        }
+    }
+    async _loadAssetsLowpolyModel(e, t="") {
+        const r = []
+          , n = []
+          , o = [];
+        e.lowPolyModels.forEach(f=>{
+            f.group === "TV" ? n.push({
+                id: "",
+                name: "",
+                thumbnailUrl: "",
+                typeName: AssetTypeName.Model,
+                className: AssetClassName.Tv,
+                modelUrl: f.url
+            }) : f.group === "\u544A\u767D\u5899" ? o.push({
+                id: "",
+                name: "",
+                thumbnailUrl: "",
+                typeName: AssetTypeName.Model,
+                className: AssetClassName.Lpm,
+                modelUrl: f.url
+            }) : r.push({
+                id: "",
+                name: "",
+                thumbnailUrl: "",
+                typeName: AssetTypeName.Model,
+                className: AssetClassName.Lpm,
+                modelUrl: f.url
+            })
+        }
+        ),
+        t != "" && t != null && this._deleteAssetsLowpolyModel(t);
+        const a = e.skinId;
+        log$a.info("====> from ", t, "  to  ", a),
+        this._tvs.forEach(f=>f.clean()),
+        this._tvs = [];
+        let s = EFitMode.cover;
+        a == "10048" && (s = EFitMode.contain),
+        Array.isArray(n) && n.forEach((f,d)=>{
+            this._tvs.push(new TV("squareTv" + d,f.modelUrl,this.room,{
+                fitMode: s
+            }))
+        }
+        ),
+        e.breathPointsConfig.forEach(async f=>{
+            let d;
+            try {
+                d = await urlTransformer(f.imageUrl)
+            } catch (_) {
+                d = f.imageUrl,
+                log$a.error("urlTransformer error", _)
+            }
+            this.room.breathPointManager.addBreathPoint({
+                id: f.id,
+                position: f.position,
+                spriteSheet: d,
+                rotation: f.rotation || {
+                    pitch: 0,
+                    yaw: 270,
+                    roll: 0
+                },
+                billboardMode: !0,
+                type: f.type || "no_type",
+                spriteWidthNumber: f.spriteWidthNum || 1,
+                spriteHeightNumber: f.spriteHeightNum || 1,
+                maxVisibleRegion: f.maxVisibleRegion || 150,
+                width: f.width,
+                height: f.height,
+                skinInfo: f.skinId
+            })
+        }
+        ),
+        o.forEach(f=>{
+            this.room.sceneManager.decalComponent.addDecal({
+                id: f.id || "gbq",
+                meshPath: f.modelUrl,
+                skinInfo: a
+            })
+        }
+        );
+        const u = this.room.sceneManager.staticmeshComponent.lowModel_group
+          , c = Array.from(u.keys()).filter(f=>!f.startsWith("region_"))
+          , h = ["airship", "balloon", "ground_feiting", "ground_reqiqiu", "default"];
+        return new Promise((f,d)=>{
+            Promise.all(h.map(_=>this._parseModelsAndLoad(r, _, a))).then(()=>{
+                let _ = !1;
+                r.forEach(v=>{
+                    v.modelUrl.endsWith("zip") && (_ = !0)
+                }
+                ),
+                _ == !1 && this.room.sceneManager.staticmeshComponent.deleteLastRegionMesh(),
+                this.room.sceneManager.staticmeshComponent.lowModel_group;
+                const g = Array.from(u.keys()).filter(v=>!v.startsWith("region_"))
+                  , m = c.filter(v=>g.indexOf(v) < 0);
+                m.length > 0 && m.forEach(v=>{
+                    this.room.sceneManager.staticmeshComponent.deleteMeshesByGroup(v)
+                }
+                ),
+                f(!0)
+            }
+            ).catch(_=>{
+                d(_)
+            }
+            )
+        }
+        )
+    }
+    async _updateSkinAssets(e) {
+        const t = this.room.lastSkinId
+          , r = await this.room.getSkin(e)
+          , n = this._createAssetList(r);
+        try {
+            await this.loadAssets(n, t),
+            this.room.updateCurrentState({
+                versionId: r.versionId,
+                skinId: r.id,
+                skin: r
+            })
+        } catch {
+            await this.loadAssets(n, t),
+            this.room.updateCurrentState({
+                versionId: r.versionId,
+                skinId: r.id,
+                skin: r
+            })
+        }
+        this.setEnv(r)
+    }
+    _createAssetList(e) {
+        const t = []
+          , r = []
+          , n = [];
+        let o = e.models;
+        const a = this.room.modelManager.config.preload;
+        return this.room.viewMode === "simple" ? a && (o = a.baseUrls.map(l=>(l.modelUrl = l.url,
+        l))) : this.room.viewMode,
+        ModelManager.findModels(o, AssetTypeName.Effects, AssetClassName.Effects).forEach(l=>{
+            t.push({
+                url: l.modelUrl,
+                group: l.className,
+                name: l.name
+            })
+        }
+        ),
+        ModelManager.findModels(o, AssetTypeName.Model, AssetClassName.Lpm).forEach(l=>{
+            r.push({
+                url: l.modelUrl,
+                group: l.className
+            })
+        }
+        ),
+        ModelManager.findModels(o, AssetTypeName.Model, AssetClassName.Gbq).forEach(l=>{
+            r.push({
+                url: l.modelUrl,
+                group: l.className
+            })
+        }
+        ),
+        ModelManager.findModels(o, AssetTypeName.Model, AssetClassName.Tv).forEach(l=>{
+            r.push({
+                url: l.modelUrl,
+                group: l.className
+            })
+        }
+        ),
+        [].forEach(l=>{
+            l.skinId == e.id && n.push(l)
+        }
+        ),
+        {
+            roomId: this.room.id,
+            effects: t,
+            lowPolyModels: r,
+            breathPointsConfig: n,
+            skinId: e.id
+        }
+    }
+    registerStats() {
+        const e = this.room.sceneManager;
+        this.room.scene.registerAfterRender(()=>{
+            var I;
+            const t = e.statisticComponent.getInterFrameTimeCounter()
+              , r = e.statisticComponent.getDrawCall()
+              , n = e.statisticComponent.getActiveFaces()
+              , o = e.statisticComponent.getFrameTimeCounter()
+              , a = e.statisticComponent.getDrawCallTime()
+              , s = e.statisticComponent.getAnimationTime()
+              , l = e.statisticComponent.getActiveMeshEvaluationTime()
+              , u = e.statisticComponent.getRenderTargetRenderTime()
+              , c = e.statisticComponent.getRegisterBeforeRenderTime()
+              , h = e.statisticComponent.getRegisterAfterRenderTime()
+              , f = e.statisticComponent.getActiveParticles()
+              , d = e.statisticComponent.getActiveBones()
+              , _ = e.Scene._activeAnimatables.length
+              , g = e.statisticComponent.getTotalRootNodes()
+              , m = e.Scene.geometries.length
+              , v = e.Scene.onBeforeRenderObservable.observers.length
+              , y = e.Scene.onAfterRenderObservable.observers.length
+              , b = e.statisticComponent.getTotalMeshes()
+              , T = e.statisticComponent.getTotalTextures()
+              , C = e.statisticComponent.getTotalMaterials()
+              , A = e.statisticComponent.getSystemInfo()
+              , S = A.resolution
+              , P = A.driver;
+            A.vender;
+            const R = A.version
+              , M = A.hardwareScalingLevel
+              , x = S + "_" + P + "_" + R + "_" + M;
+            this.interFrameCircularArray.add(t),
+            this.renderTimeCircularArray.add(o),
+            this.animationCircularArray.add(s),
+            this.meshSelectCircularArray.add(l),
+            this.drawCallTimeCircularArray.add(a),
+            this.regAfterRenderCircularArray.add(h),
+            this.regBeforeRenderCircularArray.add(c),
+            this.renderTargetCircularArray.add(u),
+            this.drawCallCntCircularArray.add(r),
+            this.renderCnt += 1,
+            this.renderCnt % 25 == 0 && ((I = this.room.stats) == null || I.assign({
+                renderFrameTime: this.renderTimeCircularArray.getAvg(),
+                maxRenderFrameTime: this.renderTimeCircularArray.getMax(),
+                interFrameTime: this.interFrameCircularArray.getAvg(),
+                animationTime: this.animationCircularArray.getAvg(),
+                meshSelectTime: this.meshSelectCircularArray.getAvg(),
+                drawcallTime: this.drawCallTimeCircularArray.getAvg(),
+                idleTime: this._idleTime,
+                registerBeforeRenderTime: this.regBeforeRenderCircularArray.getAvg(),
+                registerAfterRenderTime: this.regAfterRenderCircularArray.getAvg(),
+                renderTargetRenderTime: this.renderTargetCircularArray.getAvg(),
+                fps: (1e3 / (this.renderTimeCircularArray.getAvg() + this.interFrameCircularArray.getAvg())).toFixed(2),
+                drawcall: this.drawCallCntCircularArray.getAvg(),
+                triangle: n.toString(),
+                engineSloppyCnt: this.engineSloppyCnt,
+                maxInterFrameTime: this.interFrameCircularArray.getMax(),
+                maxDrawcallTime: this.drawCallTimeCircularArray.getMax(),
+                maxMeshSelectTime: this.meshSelectCircularArray.getMax(),
+                maxAnimationTime: this.animationCircularArray.getMax(),
+                maxRegisterBeforeRenderTime: this.regBeforeRenderCircularArray.getMax(),
+                maxRegisterAfterRenderTime: this.regAfterRenderCircularArray.getMax(),
+                maxRenderTargetRenderTime: this.renderTargetCircularArray.getMax(),
+                activeParticles: f,
+                activeBones: d,
+                activeAnimation: _,
+                totalMeshes: b,
+                totalRootNodes: g,
+                totalGeometries: m,
+                totalTextures: T,
+                totalMaterials: C,
+                registerBeforeCount: v,
+                registerAfterCount: y,
+                hardwareInfo: x
+            }))
+        }
+        )
+    }
+    async updateBillboard() {
+        const {options: {skinId: e}} = this.room
+          , r = (await this.room.modelManager.findAssetList(e)).filter(a=>a.typeName === AssetTypeName.Textures && a.className === AssetClassName.SayBubble)
+          , n = ["bubble01", "bubble02", "bubble03"]
+          , o = ["bubble01_npc", "bubble02_npc", "bubble03_npc"];
+        if (r.length) {
+            const a = r.filter(l=>n.includes(l.name)).map(l=>l.url)
+              , s = r.filter(l=>o.includes(l.name)).map(l=>l.url);
+            a.length && (XBillboardManager.userBubbleUrls = a),
+            s.length && (XBillboardManager.npcBubbleUrls = s)
+        }
+    }
+}
+;

+ 62 - 0
EventsController.js

@@ -0,0 +1,62 @@
+const log$6 = new Logger("eventsController");
+class EventsController {
+    constructor(e) {
+        E(this, "staticmeshEvent");
+        E(this, "rotationEvent");
+        E(this, "resize", ()=>{
+            this.room.sceneManager.cameraComponent.cameraFovChange(this.room.sceneManager.yuvInfo)
+        }
+        );
+        E(this, "clickEvent", e=>{
+            const {point: t, name: r, type: n, id: o} = e;
+            log$6.debug("pointEvent", e),
+            this.room.proxyEvents("pointTap", {
+                point: t,
+                meshName: r,
+                type: n,
+                id: o
+            }),
+            this.room.proxyEvents("_coreClick", e)
+        }
+        );
+        E(this, "longPressEvent", e=>{
+            this.room.proxyEvents("_corePress", e)
+        }
+        );
+        E(this, "handleActionResponseTimeout", ({error: e, event: t})=>{
+            this.room.proxyEvents("actionResponseTimeout", {
+                error: e,
+                event: t
+            })
+        }
+        );
+        E(this, "handleNetworkStateChange", e=>{
+            const {state: t, count: r} = e;
+            t == "reconnecting" ? this.room.proxyEvents("reconnecting", {
+                count: r || 1
+            }) : t === "reconnected" ? (this.room.networkController.rtcp.workers.reset(),
+            this.room.proxyEvents("reconnected"),
+            this.room.afterReconnected()) : t === "disconnected" && this.room.proxyEvents("disconnected")
+        }
+        );
+        this.room = e,
+        this.staticmeshEvent = new StaticMeshEvent(this.room.sceneManager),
+        this.rotationEvent = new RotationEvent(e)
+    }
+    bindEvents() {
+        window.addEventListener("orientationchange"in window ? "orientationchange" : "resize", this.resize),
+        this.staticmeshEvent.on("pointTap", this.clickEvent),
+        this.staticmeshEvent.on("longPress", this.longPressEvent),
+        this.rotationEvent.init(),
+        eventsManager.on("actionResponseTimeout", this.handleActionResponseTimeout),
+        this.room.networkController.on("stateChanged", this.handleNetworkStateChange)
+    }
+    clearEvents() {
+        window.removeEventListener("orientationchange"in window ? "orientationchange" : "resize", this.resize),
+        this.staticmeshEvent.off("pointTap", this.clickEvent),
+        this.staticmeshEvent.off("longPress", this.longPressEvent),
+        eventsManager.off("actionResponseTimeout", this.handleActionResponseTimeout),
+        this.room.networkController.off("stateChanged", this.handleNetworkStateChange),
+        this.rotationEvent.clear()
+    }
+}

+ 114 - 0
EventsManager.js

@@ -0,0 +1,114 @@
+class EventsManager extends EventEmitter {
+    constructor() {
+        super(...arguments);
+        E(this, "events", new Map);
+        E(this, "specialEvents", new Map)
+    }
+    remove(e, t, r, n) {
+        if (this.specialEvents.has(e) && !n && t === Codes$1.Success)
+            return;
+        this.events.get(e) && (this.emit(e, {
+            code: t,
+            data: r
+        }),
+        this.events.delete(e),
+        this.specialEvents.delete(e))
+    }
+    async track(e, t) {
+        const r = e.traceId;
+        this.emitTraceIdToDecoder(e);
+        const {sampleRate: n, noReport: o=!1, special: a} = t || {};
+        if (n && Math.random() > n)
+            return Promise.resolve();
+        const s = Actions[e.event] + "Action"
+          , l = e.tag;
+        this.events.set(r, !0),
+        a && this.specialEvents.set(r, !0);
+        const u = Date.now();
+        let c = null;
+        return new Promise((h,f)=>{
+            if (o)
+                return this.off(r),
+                this.events.delete(r),
+                h(void 0);
+            this.on(r, ({code: _, data: g, msg: m})=>{
+                if (_ === Codes$1.Success)
+                    h(g),
+                    this.off(r),
+                    log$r.infoAndReportMeasurement({
+                        metric: s,
+                        tag: l,
+                        extra: e.extra,
+                        startTime: u,
+                        traceId: r
+                    });
+                else {
+                    if (_ === Codes$1.ActionMaybeDelay)
+                        return;
+                    if (_ === Codes$1.DoActionBlocked && e.event === Actions.Rotation) {
+                        log$r.debug(s + " response code: " + _);
+                        return
+                    }
+                    const v = getErrorByCode(_)
+                      , y = new v(m);
+                    this.off(r),
+                    f(y),
+                    this.emit("actionResponseError", {
+                        error: y,
+                        event: e,
+                        tag: l
+                    }),
+                    log$r.infoAndReportMeasurement({
+                        metric: s,
+                        tag: l,
+                        extra: e.extra,
+                        error: y,
+                        startTime: u,
+                        traceId: r
+                    })
+                }
+            }
+            );
+            const d = e.timeout || 2e3;
+            c = window.setTimeout(()=>{
+                if (c && clearTimeout(c),
+                !this.events.get(r))
+                    return;
+                const _ = new ActionResponseTimeoutError(`${s} timeout in ${d}ms`);
+                this.emit("actionResponseTimeout", {
+                    error: _,
+                    event: e,
+                    tag: l
+                }),
+                f(_),
+                this.events.delete(r),
+                this.off(r),
+                log$r.infoAndReportMeasurement({
+                    metric: s,
+                    tag: l,
+                    extra: e.extra,
+                    error: _,
+                    startTime: u,
+                    traceId: r
+                })
+            }
+            , d)
+        }
+        )
+    }
+    emitTraceIdToDecoder(e) {
+        if (e.event === Actions.Rotation || e.event === Actions.Clicking || e.event === Actions.GetOnVehicle || e.event === Actions.GetOffVehicle) {
+            const t = {
+                [Actions.Rotation]: "Rotation",
+                [Actions.GetOnVehicle]: "GetOnVehicle",
+                [Actions.GetOffVehicle]: "GetOffVehicle",
+                [Actions.Clicking]: "MoveTo"
+            };
+            this.emit("traceId", {
+                traceId: e.traceId,
+                timestamp: Date.now(),
+                event: t[e.event]
+            })
+        }
+    }
+}

+ 24 - 0
Heartbeat.js

@@ -0,0 +1,24 @@
+const log$n = new Logger("heartbeat");
+class Heartbeat {
+    constructor(e) {
+        E(this, "_interval", null);
+        E(this, "ping", ()=>{
+            const e = Date.now().toString();
+            this.handler.ping(e)
+        }
+        );
+        this.handler = e
+    }
+    start() {
+        this.stop(),
+        log$n.debug(`Setting ping interval to ${PING_INTERVAL_MS}ms`),
+        this._interval = window.setInterval(this.ping, PING_INTERVAL_MS)
+    }
+    stop() {
+        log$n.debug("stop heartbeat"),
+        this._interval && window.clearInterval(this._interval)
+    }
+    pong(e, t) {
+        !e || typeof e == "string" && this.handler.pong(Date.now() - Number(e), t)
+    }
+}

+ 133 - 0
Http.js

@@ -0,0 +1,133 @@
+const log$b = new Logger("http");
+class Http extends EventEmitter {
+    async get({url: e, useIndexedDb: t=!1, timeout: r=15e3, key: n, isOutPutObjectURL: o=!0}) {
+        if (Xverse$1.NO_CACHE !== void 0 && (t = !Xverse$1.NO_CACHE),
+        t)
+            if (isIndexedDbSupported()) {
+                window.performance.now();
+                let a = null;
+                try {
+                    a = await modelTable.query("url", e)
+                } catch (s) {
+                    return log$b.debug(s),
+                    log$b.warn("cache query error", e),
+                    Promise.resolve(e)
+                }
+                if (a && a.model) {
+                    const s = dataURItoBlob(a.model)
+                      , l = Promise.resolve(o ? URL.createObjectURL(s) : s);
+                    return window.performance.now(),
+                    l
+                } else
+                    return this.request({
+                        url: e,
+                        timeout: r,
+                        contentType: "blob",
+                        key: n
+                    }).then(async s=>{
+                        const l = await blobToDataURI(s.response);
+                        try {
+                            await modelTable.put({
+                                url: e,
+                                model: l
+                            })
+                        } catch (u) {
+                            log$b.warn("unable to add data to indexedDB", u)
+                        }
+                        return Promise.resolve(o ? URL.createObjectURL(s.response) : s.response)
+                    }
+                    )
+            } else
+                return this.request({
+                    url: e,
+                    timeout: r,
+                    contentType: "blob",
+                    key: n
+                }).then(a=>{
+                    const s = a.response;
+                    return Promise.resolve(o ? URL.createObjectURL(s) : s)
+                }
+                ).catch(a=>Promise.reject(a));
+        else
+            return this.request({
+                url: e,
+                timeout: r,
+                key: n
+            }).then(a=>a.getResponseHeader("content-type") === "application/json" ? Promise.resolve(JSON.parse(a.responseText)) : Promise.resolve(a.responseText))
+    }
+    request(e) {
+        const {timeout: t=3e4, contentType: r, key: n, onRequestStart: o} = e
+          , {url: a} = e;
+        return new Promise((s,l)=>{
+            window.performance.now();
+            const u = new XMLHttpRequest;
+            r && (u.responseType = r),
+            u.timeout = t,
+            u.addEventListener("readystatechange", ()=>{
+                if (u.readyState == 4) {
+                    if (u.status == 200)
+                        return window.performance.now(),
+                        this.emit("loadend", {
+                            message: `request ${a} load success`
+                        }),
+                        s(u);
+                    {
+                        const c = `Unable to load the request ${a}`;
+                        return this.emit("error", {
+                            message: c
+                        }),
+                        log$b.error(c),
+                        l(c)
+                    }
+                }
+            }
+            ),
+            o && o(u),
+            u.open("GET", a),
+            u.send()
+        }
+        )
+    }
+}
+
+
+
+const http = new Http
+  , isIndexedDbSupported = ()=>(window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB) !== void 0
+  , blobToDataURI = async i=>new Promise((e,t)=>{
+    const r = new FileReader;
+    r.readAsDataURL(i),
+    r.onload = function(n) {
+        var o;
+        e((o = n.target) == null ? void 0 : o.result)
+    }
+    ,
+    r.onerror = function(n) {
+        t(n)
+    }
+}
+)
+  , dataURItoBlob = i=>{
+    let e;
+    i.split(",")[0].indexOf("base64") >= 0 ? e = atob(i.split(",")[1]) : e = unescape(i.split(",")[1]);
+    const t = i.split(",")[0].split(":")[1].split(";")[0]
+      , r = new Uint8Array(e.length);
+    for (let o = 0; o < e.length; o++)
+        r[o] = e.charCodeAt(o);
+    return new Blob([r],{
+        type: t
+    })
+}
+  , urlMap = new Map
+  , urlTransformer = async(i,e=!1)=>typeof i != "string" ? (console.warn("url transformer error", i),
+i) : i.startsWith("blob:") ? i : e ? http.get({
+    url: i,
+    useIndexedDb: !0,
+    key: "url",
+    isOutPutObjectURL: !1
+}) : urlMap.has(i) ? urlMap.get(i) : http.get({
+    url: i,
+    useIndexedDb: !0,
+    key: "url"
+}).then(t=>(urlMap.set(i, t),
+t));

+ 67 - 0
JoyStick.js

@@ -0,0 +1,67 @@
+class JoyStick {
+    constructor(e) {
+        E(this, "_zone", document.createElement("div"));
+        E(this, "_joystick", null);
+        E(this, "_room");
+        this._room = e
+    }
+    init(e) {
+        const {interval: t=33, triggerDistance: r=25, style: n={
+            left: 0,
+            bottom: 0
+        }} = e
+          , o = (u,c)=>{
+            this._room.actionsHandler.joystick({
+                degree: Math.floor(u),
+                level: Math.floor(c / 5)
+            })
+        }
+          , a = this._zone;
+        document.body.appendChild(a),
+        a.style.position = "absolute",
+        a.style.width = "200px",
+        a.style.height = "200px",
+        a.style.left = String(n.left),
+        a.style.bottom = String(n.bottom),
+        a.style.zIndex = "999",
+        a.style.userSelect = "none",
+        a.style.webkitUserSelect = "none",
+        this._joystick = nipplejs.create({
+            zone: a,
+            mode: "static",
+            position: {
+                left: "50%",
+                top: "50%"
+            },
+            color: "white"
+        });
+        let s, l;
+        return this._joystick.on("move", (u,c)=>{
+            s = c
+        }
+        ),
+        this._joystick.on("start", ()=>{
+            l = window.setInterval(()=>{
+                s && s.distance > r && o && o(s.angle.degree, s.distance)
+            }
+            , t)
+        }
+        ),
+        this._joystick.on("end", ()=>{
+            l && window.clearInterval(l),
+            l = void 0
+        }
+        ),
+        this._joystick
+    }
+    show() {
+        if (!this._joystick)
+            throw new Error("joystick is not created");
+        this._zone.style.display = "block"
+    }
+    hide() {
+        if (!this._joystick)
+            throw new Error("joystick is not created");
+        this._zone.style.display = "none"
+    }
+}

+ 81 - 0
Logger.js

@@ -0,0 +1,81 @@
+class Logger{
+    constructor(e) {
+        E(this, "module", "log");
+        E(this, "level", 3);
+        this.module = e
+    }
+    static setLevel(e) {
+        this.level = e
+    }
+    setLevel(e) {
+        this.level = e
+    }
+    atleast(e) {
+        return e >= this.level && e >= Ce.level
+    }
+    print(e, t, ...r) {
+        if (this.atleast(t)) {
+            const n = e == "debug" ? "info" : e
+              , o = this.prefix(e);
+            console[n].call(null, o, ...r)
+        }
+        if (e !== "debug" && e !== "info") {
+            const n = r.map(o=>{
+                if (o instanceof Object)
+                    try {
+                        return JSON.stringify(o)
+                    } catch {
+                        return o
+                    }
+                else
+                    return o
+            }
+            ).join(",");
+            reporter.report("log", {
+                message: n,
+                level: e,
+                module: this.module
+            })
+        }
+    }
+    debug(...e) {
+        return this.print("debug", 1, ...e)
+    }
+    info(...e) {
+        return this.print("info", 2, ...e)
+    }
+    infoAndReportLog(e, ...t) {
+        const {reportOptions: r} = e;
+        delete e.reportOptions,
+        reporter.report("log", e, r),
+        t.length || (t = [e.message]),
+        this.debug(...t)
+    }
+    infoAndReportMeasurement(e, ...t) {
+        var n;
+        const {reportOptions: r} = e;
+        if (e.startTime) {
+            const o = Date.now();
+            e.value === void 0 && (e.endTime = o),
+            e.value === void 0 && (e.value = o - e.startTime)
+        }
+        if (e.error ? e.code = ((n = e.error) == null ? void 0 : n.code) || Codes$1.Internal : e.code = Codes$1.Success,
+        reporter.report("measurement", e, r),
+        t.length || (t = [e]),
+        e.level === 4 || e.error) {
+            this.error(...t);
+            return
+        }
+        this.warn(...t)
+    }
+    warn(...e) {
+        return this.print("warn", 3, ...e)
+    }
+    error(...e) {
+        return this.print("error", 4, ...e)
+    }
+    prefix(e) {
+        return `[${this.module}][${e}] ${getFormattedDate(new Date)}:`
+    }
+}
+;

+ 197 - 0
ModelManager.js

@@ -0,0 +1,197 @@
+const log$f = new Logger("model-manager")
+class ModelManager{
+    constructor(e, t) {
+        E(this, "avatarModelList", []);
+        E(this, "skinList", []);
+        E(this, "applicationConfig");
+        E(this, "config");
+        E(this, "appId");
+        E(this, "releaseId");
+        this.appId = e,
+        this.releaseId = t
+    }
+    static getInstance(e, t) {
+        return me.instance || (me.instance = new me(e,t)),
+        me.instance
+    }
+    static findModels(e, t, r) {
+        return e.filter(o=>o.typeName === t && o.className === r)
+    }
+    static findModel(e, t, r) {
+        const n = e.filter(o=>o.typeName === t && o.className === r)[0];
+        return n || null
+    }
+    async findSkinConfig(e) {
+        let t = null;
+        if (t = (this.skinList = await this.getSkinsList()).find(n=>n.id === e),
+        t)
+            return t;
+        {
+            const n = `skin is invalid: skinId: ${e}`;
+            return Promise.reject(new ParamError(n))
+        }
+    }
+    async findRoute(e, t) {
+        const n = (await this.findSkinConfig(e)).routeList.find(o=>o.pathName === t);
+        if (!n) {
+            const o = `find path failed: skinId: ${e}, pathName: ${t}`;
+            return Promise.reject(new ParamError(o))
+        }
+        return log$f.debug("find route success", n),
+        n
+    }
+    async findAssetList(e) {
+        const r = (await this.findSkinConfig(e)).assetList;
+        if (!r) {
+            const n = `find path failed: skinId: ${e}`;
+            return Promise.reject(new ParamError(n))
+        }
+        return log$f.debug("find route success", r),
+        r
+    }
+    async findAsset(e, t, r="id") {
+        const n = await this.findSkinConfig(e);
+        if (Array.isArray(t))
+            return t.map(a=>n.models.find(s=>s[r] === a)).filter(Boolean);
+        const o = n.models.find(a=>a[r] === t);
+        if (!o) {
+            const a = `find asset failed: skinId: ${e}, keyValue: ${t}`;
+            return Promise.reject(new ParamError(a))
+        }
+        return log$f.debug("find asset success", o),
+        o
+    }
+    async findPoint(e, t) {
+        const n = (await this.findSkinConfig(e)).pointList.find(o=>o.id === t);
+        if (!n) {
+            const o = `find point failed: skinId: ${e}, id: ${t}`;
+            return Promise.reject(new ParamError(o))
+        }
+        return log$f.debug("find point success", n),
+        n
+    }
+    async requestConfig() {
+        if (this.config)
+            return this.config;
+        let e = `https://static.xverse.cn/console/config/${this.appId}/config.json`;
+        this.releaseId && (e = `https://static.xverse.cn/console/config/${this.appId}/${this.releaseId}/config.json`);
+        const t = Xverse$1.USE_TME_CDN ? "https://static.xverse.cn/tmeland/config/tme_config.json" : e;
+        try {
+            const r = await http$1.get({
+                url: `${t}?t=${Date.now()}`,
+                key: "config",
+                timeout: 6e3,
+                retry: 2
+            })
+              , {config: n, preload: o} = r.data.data || {};
+            if (!n)
+                throw new Error("config data parse error" + r.data);
+            return this.config = {
+                config: n,
+                preload: o
+            },
+            log$f.debug("get config success", this.config),
+            this.config
+        } catch (r) {
+            return Promise.reject(r)
+        }
+    }
+    async getApplicationConfig() {
+        if (this.applicationConfig)
+            return this.applicationConfig;
+        try {
+            const e = await this.requestConfig();
+            return this.applicationConfig = e.config,
+            this.applicationConfig
+        } catch (e) {
+            return Promise.reject(e)
+        }
+    }
+    async getAvatarModelList() {
+        if (this.avatarModelList.length)
+            return this.avatarModelList;
+        try {
+            const {avatars: e} = await this.getApplicationConfig();
+            return this.avatarModelList = e.map(t=>({
+                name: t.name,
+                id: t.id,
+                modelUrl: t.url,
+                gender: t.gender,
+                components: t.components
+            })),
+            this.avatarModelList
+        } catch (e) {
+            return log$f.error(e),
+            []
+        }
+    }
+    async getSkinsList() {
+        if (this.skinList.length)
+            return this.skinList;
+        try {
+            const {skins: e} = await this.getApplicationConfig();
+            return this.skinList = e.map(t=>{
+                var r;
+                return {
+                    name: t.name,
+                    dataVersion: t.id + t.versionId,
+                    id: t.id,
+                    fov: parseInt(t.fov || 90),
+                    models: t.assetList.map(n=>{
+                        const {assetId: o, url: a, thumbnailUrl: s, typeName: l, className: u} = n;
+                        return {
+                            id: o,
+                            modelUrl: a,
+                            name: n.name,
+                            thumbnailUrl: s,
+                            typeName: l,
+                            className: u === "\u4F4E\u6A21" ? "\u7C97\u6A21" : u
+                        }
+                    }
+                    ),
+                    routeList: (r = t.routeList) == null ? void 0 : r.map(n=>{
+                        const {areaName: o, attitude: a, id: s, pathName: l, step: u, birthPointList: c} = n;
+                        return {
+                            areaName: o,
+                            attitude: a,
+                            id: s,
+                            pathName: l,
+                            step: u,
+                            birthPointList: c.map(h=>({
+                                camera: h.camera && {
+                                    position: objectParseFloat(h.camera.position),
+                                    angle: objectParseFloat(h.camera.rotation)
+                                },
+                                player: h.player && {
+                                    position: objectParseFloat(h.player.position),
+                                    angle: objectParseFloat(h.player.rotation)
+                                }
+                            }))
+                        }
+                    }
+                    ),
+                    pointList: t.pointList.map(n=>le(oe({}, n), {
+                        position: objectParseFloat(n.position),
+                        rotation: objectParseFloat(n.rotation)
+                    })),
+                    versionId: t.versionId,
+                    isEnable: t.isEnable,
+                    assetList: t.assetList,
+                    visibleRules: t.visibleRules,
+                    animationList: t.animationList
+                }
+            }
+            ),
+            this.skinList
+        } catch (e) {
+            return log$f.error(e),
+            []
+        }
+    }
+    async getBreathPointTextrueList() {
+        return [{
+            url: TEXTURE_URL
+        }]
+    }
+}
+;

+ 14 - 0
ModelTable.js

@@ -0,0 +1,14 @@
+class ModelTable extends BaseTable {
+    constructor() {
+        super("XverseDatabase", 1)
+    }
+    tableName() {
+        return "models"
+    }
+    index() {
+        return ["url"]
+    }
+    keyPath() {
+        return "url"
+    }
+}

+ 215 - 0
NetworkController.js

@@ -0,0 +1,215 @@
+const log$i = new Logger("NetworkController");
+class NetworkController extends EventEmitter {
+    constructor(e) {
+        super();
+        E(this, "socket");
+        E(this, "rtcp");
+        E(this, "stream");
+        E(this, "_state", "connecting");
+        E(this, "_networkMonitor");
+        E(this, "blockedActions", []);
+        E(this, "reconnectCount", 0);
+        E(this, "startGame", ()=>new Promise((e,t)=>{
+            if (!this.rtcp.connected)
+                return t(new InternalError("Game cannot load. Please refresh"));
+            if (!this.rtcp.inputReady)
+                return t(new InternalError("Game is not ready yet. Please wait"));
+            this.socket.on("gameRoomAvailable", r=>{
+                this.setState("connected"),
+                e(r),
+                this.rtcp.heartbeat.start()
+            }
+            ),
+            this.socket.on("socketClosed", r=>{
+                t(r)
+            }
+            ),
+            this.socket.startGame()
+        }
+        ));
+        this.room = e,
+        this.socket = new Socket(this),
+        this.rtcp = new Rtcp(this),
+        this.stream = new Stream,
+        this._networkMonitor = new NetworkMonitor(()=>{
+            log$i.info("network changed, online:", this._networkMonitor.isOnline),
+            this._state === "disconnected" && this._networkMonitor.isOnline && (log$i.info("network back to online, try to reconnect"),
+            this.reconnect())
+        }
+        ),
+        checkNetworkQuality(this.room.currentNetworkOptions.wsServerUrl),
+        this._networkMonitor.start(),
+        new VisibilityChangeHandler().subscribe(r=>{
+            var n, o;
+            r ? ((o = this.room.stats) == null || o.disable(),
+            log$i.infoAndReportMeasurement({
+                metric: "pageHide",
+                startTime: Date.now()
+            })) : ((n = this.room.stats) == null || n.enable(),
+            log$i.infoAndReportMeasurement({
+                metric: "pageShow",
+                startTime: Date.now(),
+                extra: {
+                    state: this._state
+                }
+            }),
+            this._state === "disconnected" && this.reconnect())
+        }
+        )
+    }
+    addBlockedActions(e) {
+        this.blockedActions.push(...e)
+    }
+    removeBlockedActions(e) {
+        if (!e) {
+            this.blockedActions = [];
+            return
+        }
+        const t = this.blockedActions.indexOf(e);
+        this.blockedActions.splice(t, 1)
+    }
+    setState(e) {
+        this._state !== e && (log$i.info("Set network state to ", e),
+        this._state = e)
+    }
+    async connectAndStart(e) {
+        return this.connect(e).then(this.startGame)
+    }
+    async connect(e=!1) {
+        return this.room.updateCurrentNetworkOptions({
+            reconnect: e
+        }),
+        new Promise((t,r)=>{
+            this.rtcp.on("rtcConnected", ()=>{
+                this.setState("connected"),
+                t()
+            }
+            ),
+            this.rtcp.on("rtcDisconnected", ()=>{
+                log$i.info("rtc disconnected"),
+                this._state === "connecting" ? (this.setState("disconnected"),
+                r(new InternalError("rtc connect failed"))) : (this.setState("disconnected"),
+                log$i.info("rtc disconnected, start to reconnect"),
+                this.reconnect())
+            }
+            ),
+            this.socket.on("socketQuit", ()=>{
+                log$i.info("socket quit success"),
+                this.setState("closed")
+            }
+            ),
+            this.socket.on("socketClosed", n=>{
+                this._state === "connecting" && (this.setState("disconnected"),
+                r(n)),
+                r(n)
+            }
+            ),
+            this.socket.start()
+        }
+        )
+    }
+    reconnect() {
+        if (this.room.viewMode === "observer")
+            return;
+        const e = Date.now();
+        if (this.reconnectCount++,
+        this.reconnectCount > MAX_RECONNECT_COUNT) {
+            log$i.error("reconnect failed, reached max reconnect count", MAX_RECONNECT_COUNT),
+            this.reconnectCount = 0,
+            this.emit("stateChanged", {
+                state: "disconnected"
+            });
+            return
+        }
+        return log$i.info("start reconnect, count:", this.reconnectCount),
+        this._reconnect().then(()=>{
+            log$i.infoAndReportMeasurement({
+                startTime: e,
+                metric: "reconnect"
+            })
+        }
+        ).catch(t=>{
+            if (log$i.infoAndReportMeasurement({
+                startTime: e,
+                metric: "reconnect",
+                error: t
+            }),
+            t.code === Codes$1.RepeatLogin) {
+                this.room.handleRepetLogin();
+                return
+            }
+            const r = 1e3;
+            log$i.info("reconnect failed, wait " + r + " ms for next reconnect"),
+            setTimeout(()=>{
+                this.reconnect()
+            }
+            , r)
+        }
+        )
+    }
+    _reconnect() {
+        return this._state === "closed" ? (log$i.warn("connection closed already"),
+        Promise.reject()) : this._state === "connecting" ? (log$i.warn("connection is already in connecting state"),
+        Promise.reject()) : this._state !== "disconnected" ? Promise.reject() : (this.prepareReconnect(),
+        this._state = "connecting",
+        this.emit("stateChanged", {
+            state: "reconnecting",
+            count: this.reconnectCount
+        }),
+        this.socket.off("gameRoomAvailable"),
+        this.socket.off("socketClosed"),
+        this.rtcp.off("rtcDisconnected"),
+        this.rtcp.off("rtcConnected"),
+        this.connectAndStart(!0).then(({session_id: e})=>{
+            this.room.updateCurrentNetworkOptions({
+                sessionId: e
+            }),
+            reporter.updateBody({
+                serverSession: e
+            }),
+            log$i.info("reconnect success"),
+            this.setState("connected"),
+            this.reconnectCount = 0,
+            this.emit("stateChanged", {
+                state: "reconnected"
+            })
+        }
+        ))
+    }
+    prepareReconnect() {
+        this.rtcp.disconnect(),
+        this.socket.prepareReconnect(),
+        this.prepareReconnectOptions()
+    }
+    prepareReconnectOptions() {
+        const {camera: e, player: t} = this.room.currentClickingState || {};
+        e && t && this.room.updateCurrentNetworkOptions({
+            camera: e,
+            player: t
+        })
+    }
+    sendRtcData(e) {
+        if (this.blockedActions.includes(e.action_type)) {
+            log$i.info(`action: ${Actions[e.action_type]} was blocked`);
+            return
+        }
+        this.rtcp.sendData(e)
+    }
+    sendSocketData(e) {
+        log$i.debug("ws send ->", e),
+        this.socket.send(e)
+    }
+    quit() {
+        const e = uuid$1()
+          , t = {
+            action_type: Actions.Exit,
+            trace_id: e,
+            exit_action: {},
+            user_id: this.room.options.userId,
+            packet_id: e
+        };
+        this.setState("closed"),
+        this.socket.quit(),
+        this.sendRtcData(t)
+    }
+}

+ 18 - 0
NetworkMonitor.js

@@ -0,0 +1,18 @@
+class NetworkMonitor {
+    constructor(e) {
+        E(this, "_listener");
+        this._listener = e
+    }
+    get isOnline() {
+        const e = window.navigator;
+        return typeof e.onLine == "boolean" ? e.onLine : !0
+    }
+    start() {
+        window.addEventListener("online", this._listener),
+        window.addEventListener("offline", this._listener)
+    }
+    stop() {
+        window.removeEventListener("online", this._listener),
+        window.removeEventListener("offline", this._listener)
+    }
+}

+ 86 - 0
Panorama.js

@@ -0,0 +1,86 @@
+const log$5 = new Logger("panorama");
+class Panorama {
+    constructor(e) {
+        E(this, "_actived", !1);
+        E(this, "handleReceivePanorama", async(e,t)=>{
+            log$5.warn("handle panorama", e.uuid, e.pos, e.finished);
+            const r = {
+                data: e.data,
+                pose: {
+                    position: e.pos
+                }
+            }
+              , n = this.room.sceneManager;
+            if (this.room.networkController.rtcp.workers.changePanoMode(!0),
+            await n.materialComponent.changePanoImg(0, r),
+            !!e.finished)
+                if (await n.changePanoShaderForLowModel(0),
+                this.room.isPano = !0,
+                this._actived = !0,
+                t)
+                    this.room.sceneManager.cameraComponent.changeToFirstPersonView({
+                        position: t.position,
+                        rotation: t.angle
+                    });
+                else {
+                    const {skinId: o, pathName: a} = this.room.currentState;
+                    if (!o || !a)
+                        return;
+                    const s = await this.room.modelManager.findRoute(o, a)
+                      , {camera: l} = getRandomItem(s.birthPointList) || {};
+                    l && this.room.sceneManager.cameraComponent.changeToFirstPersonView(le(oe({}, l), {
+                        rotation: l.angle
+                    }))
+                }
+        }
+        );
+        this.room = e
+    }
+    get actived() {
+        return this._actived
+    }
+    bindListener(e) {
+        this.room.networkController.rtcp.workers.registerFunction("panorama", r=>{
+            log$5.warn("receive panorama", r.uuid, r.pos),
+            r.uuid && eventsManager.remove(r.uuid, Codes$1.Success, r, !0),
+            this.room.isFirstDataUsed || (this.room.isFirstDataUsed = !0,
+            this.handleReceivePanorama(r, this.room.options.camera).then(e))
+        }
+        )
+    }
+    access(e, t, r) {
+        const {camera: n, player: o, attitude: a, areaName: s, pathName: l, tag: u} = e;
+        return this.room.actionsHandler.requestPanorama({
+            camera: n,
+            player: o,
+            attitude: a,
+            areaName: s,
+            pathName: l,
+            tag: u
+        }, t, r).then(c=>this.handleReceivePanorama(c, o))
+    }
+    exit(e) {
+        const {camera: t, player: r, attitude: n, areaName: o, pathName: a} = e;
+        return this.room.networkController.rtcp.workers.changePanoMode(!1),
+        this.room.actionsHandler.changeRotationRenderType({
+            renderType: RenderType.RotationVideo,
+            player: r,
+            camera: t,
+            attitude: n,
+            areaName: o,
+            pathName: a
+        }).then(()=>this.handleExitPanorama()).catch(s=>(this.room.networkController.rtcp.workers.changePanoMode(!0),
+        Promise.reject(s)))
+    }
+    handleExitPanorama() {
+        var e, t, r, n, o, a;
+        this.room.isPano = !1,
+        this._actived = !1,
+        (n = (e = this.room.sceneManager) == null ? void 0 : e.cameraComponent) == null || n.forceChangeSavedCameraPose({
+            position: (t = this.room._currentClickingState) == null ? void 0 : t.camera.position,
+            rotation: (r = this.room._currentClickingState) == null ? void 0 : r.camera.angle
+        }),
+        this.room.sceneManager.changeVideoShaderForLowModel(),
+        (a = (o = this.room.sceneManager) == null ? void 0 : o.cameraComponent) == null || a.changeToThirdPersonView()
+    }
+}

+ 35 - 0
PathManager.js

@@ -0,0 +1,35 @@
+class PathManager {
+    constructor() {
+        E(this, "currentArea", "");
+        E(this, "currentPathName", "");
+        E(this, "currentAttitude", "");
+        E(this, "speed", 0)
+    }
+    getSpeed(e) {
+        const t = {
+            guangchang: {
+                [MotionType.Walk]: 17,
+                [MotionType.Run]: 51
+            },
+            tower: {
+                [MotionType.Walk]: 12.5,
+                [MotionType.Run]: 25
+            },
+            zhiboting: {
+                [MotionType.Walk]: 12.5,
+                [MotionType.Run]: 25
+            },
+            youxiting: {
+                [MotionType.Walk]: 12.5,
+                [MotionType.Run]: 25
+            },
+            diqing: {
+                [MotionType.Walk]: 12.5,
+                [MotionType.Run]: 25
+            }
+        }
+          , r = t[this.currentArea] || t.guangchang;
+        return this.speed = r[e] * 30,
+        this.speed
+    }
+}

+ 64 - 0
Pool.js

@@ -0,0 +1,64 @@
+class Pool {
+    constructor(e, t, r, n, ...o) {
+        E(this, "objCreator");
+        E(this, "objReseter");
+        E(this, "_pool");
+        E(this, "lastFree");
+        E(this, "nextFree");
+        E(this, "capacity");
+        this._pool = [],
+        this.objCreator = e,
+        this.objReseter = t;
+        for (let a = 0; a < n; a++)
+            this.addNewObject(this.newPoolObject(...o));
+        this.capacity = r
+    }
+    addNewObject(e) {
+        return this._pool.push(e),
+        this.release(e),
+        e
+    }
+    release(e) {
+        e.free = !0,
+        e.nextFree = null,
+        e.previousFree = this.lastFree,
+        this.lastFree ? this.lastFree.nextFree = e : this.nextFree = e,
+        this.lastFree = e,
+        this.objReseter(e)
+    }
+    getFree(...e) {
+        const t = this.nextFree ? this.nextFree : this.addNewObject(this.newPoolObject(...e));
+        return t.free = !1,
+        this.nextFree = t.nextFree,
+        this.nextFree || (this.lastFree = null),
+        t
+    }
+    newPoolObject(...e) {
+        const t = this.objCreator(...e);
+        return new PoolObject(t,this.nextFree,this.lastFree)
+    }
+    releaseAll() {
+        this._pool.forEach(e=>this.release(e))
+    }
+    clean(e=0, ...t) {
+        let r = this.nextFree;
+        if (!r)
+            return;
+        let n = 0;
+        for (; r; )
+            n += 1,
+            r = r.nextFree;
+        let o = !1;
+        if (n > e && this._pool.length > this.capacity && (o = !0),
+        o)
+            for (r = this.nextFree; r; ) {
+                r.free = !1,
+                this.nextFree = r.nextFree;
+                const a = this._pool.indexOf(r);
+                this._pool.splice(a, 1),
+                this.nextFree || (this.lastFree = null),
+                r == null || r.dispose(),
+                r = this.nextFree
+            }
+    }
+}

+ 18 - 0
PoolObject.js

@@ -0,0 +1,18 @@
+class PoolObject {
+    constructor(e, t, r, n=!0) {
+        E(this, "data");
+        E(this, "nextFree");
+        E(this, "previousFree");
+        E(this, "free");
+        this.data = e,
+        this.nextFree = t,
+        this.previousFree = r,
+        this.free = n
+    }
+    dispose() {
+        this.data && this.data instanceof Mesh && this.data.dispose(!0, !0),
+        this.previousFree = null,
+        this.nextFree = null,
+        this.data = null
+    }
+}

+ 187 - 0
Preload.js

@@ -0,0 +1,187 @@
+const log$d = new Logger("preload");
+class Preload {
+    constructor(e) {
+        E(this, "config");
+        E(this, "allKeys", []);
+        E(this, "oldResourcesDeleted", !1);
+        E(this, "requests", {
+            simple: {
+                stopped: !0,
+                requests: {}
+            },
+            observer: {
+                stopped: !0,
+                requests: {}
+            },
+            full: {
+                stopped: !0,
+                requests: {}
+            }
+        });
+        this.modelManager = e,
+        this.init(e.appId)
+    }
+    init(e) {
+        reporter.updateBody({
+            appId: e
+        })
+    }
+    static getTimeoutBySize(e) {
+        return e ? e < 500 * 1e3 ? 30 * 1e3 : e < 1e3 * 1e3 ? 60 * 1e3 : 100 * 1e3 : 100 * 1e3
+    }
+    async getConfig(e) {
+        if (this.config)
+            return this.config;
+        const {preload: t} = await this.modelManager.requestConfig();
+        return t ? (this.config = t,
+        Promise.resolve(t)) : Promise.reject("no preload config")
+    }
+    async getAllKeys() {
+        if (this.allKeys.length)
+            return this.allKeys;
+        try {
+            const e = await modelTable.getAllKeys();
+            return this.allKeys = e,
+            e
+        } catch {
+            const t = "preload getAllKeys error";
+            return log$d.error(t),
+            Promise.reject(t)
+        }
+    }
+    stop(e) {
+        e === "serverless" && (e = "observer"),
+        this.requests[e].stopped = !0;
+        const t = this.requests[e].requests;
+        Object.keys(t).forEach(r=>{
+            http$1.canceler.removePending(r),
+            delete t[r]
+        }
+        )
+    }
+    clearPreload(e) {
+        this.requests[e].stopped = !1,
+        this.allKeys = []
+    }
+    async start(e, t, r) {
+        let n = Date.now()
+          , o = 0;
+        try {
+            if (e === "serverless" && (e = "observer"),
+            !this.requests[e])
+                return Promise.reject(new ParamError("invalid stage name: " + e));
+            this.clearPreload(e);
+            const a = await this.getConfig(e)
+              , s = await this.getAllKeys();
+            try {
+                await this.deleteOldResources(a.assetUrls.map(d=>d.url), s)
+            } catch (d) {
+                log$d.error(d)
+            }
+            const {baseUrls: l, assetUrls: u, observeUrls: c} = a;
+            let h;
+            switch (e) {
+            case "simple":
+                h = l;
+                break;
+            case "observer":
+                h = u;
+                break;
+            case "full":
+                h = u;
+                break;
+            default:
+                h = u
+            }
+            let f = h.filter(d=>!s.includes(d.url));
+            r && isFunction(r) && (f = f.filter(r)),
+            o = f.length,
+            log$d.debug("keysNeedToPreload", f),
+            f.length || t && t(h.length, h.length),
+            n = Date.now(),
+            await this._preload(e, f, t),
+            log$d.infoAndReportMeasurement({
+                tag: e,
+                startTime: n,
+                metric: "assetsPreload",
+                extra: {
+                    total: o
+                }
+            });
+            return
+        } catch (a) {
+            let s = a;
+            return (this.requests[e].stopped || axios.isCancel(a)) && (s = new PreloadCanceledError),
+            log$d.infoAndReportMeasurement({
+                tag: e,
+                startTime: n,
+                metric: "assetsPreload",
+                extra: {
+                    total: o
+                },
+                error: s,
+                reportOptions: {
+                    immediate: !0
+                }
+            }),
+            Promise.reject(s)
+        }
+    }
+    deleteOldResources(e, t) {
+        if (!this.oldResourcesDeleted)
+            this.oldResourcesDeleted = !0;
+        else
+            return Promise.resolve();
+        const r = t.filter(n=>!e.includes(n));
+        return log$d.debug("keysNeedToDelete", r),
+        log$d.warn("keysNeedToDelete", r.length),
+        Promise.all(r.map(n=>modelTable.delete(n)))
+    }
+    async _preload(e, t, r) {
+        const n = t.length;
+        if (!n)
+            return Promise.resolve();
+        let o = 0;
+        const a = window.setInterval(()=>{
+            r && r(o, n),
+            o >= n && window.clearInterval(a)
+        }
+        , 1e3);
+        return mapLimit(t, 10, async s=>{
+            const {size: l, url: u} = s;
+            return this.requests[e].stopped ? Promise.reject(new PreloadCanceledError) : http$1.get({
+                url: u,
+                timeout: Preload.getTimeoutBySize(l),
+                responseType: "blob",
+                retry: 2,
+                beforeRequest: ()=>{
+                    this.requests[e].requests[u] = !0
+                }
+            }).then(async c=>{
+                const h = c.data;
+                if (!(h instanceof Blob))
+                    return log$d.error("request blob failed, type:", typeof h, u),
+                    Promise.reject("request blob failed " + u);
+                const f = await blobToDataURI$1(h);
+                try {
+                    await modelTable.put({
+                        url: u,
+                        model: f
+                    });
+                    return
+                } catch (d) {
+                    return log$d.error("unable to add data to indexedDB", d),
+                    Promise.reject(new InternalError("preload db error"))
+                }
+            }
+            ).then(()=>{
+                o++,
+                delete this.requests[e].requests[u]
+            }
+            , c=>(delete this.requests[e].requests[u],
+            window.clearInterval(a),
+            Promise.reject(c)))
+        }
+        )
+    }
+}

+ 26 - 0
Queue.js

@@ -0,0 +1,26 @@
+class Queue {
+    constructor() {
+        E(this, "queue", []);
+        E(this, "currentAction")
+    }
+    async append(e) {
+        var t, r;
+        this.queue.length === 0 || ((t = this.currentAction) == null ? void 0 : t.type) === e.type && this.queue.length === 1 ? (this.queue = [],
+        this.queue.push(e),
+        await this.go()) : (((r = this.queue[this.queue.length - 1]) == null ? void 0 : r.type) === e.type && this.queue.pop(),
+        this.queue.push(e))
+    }
+    async go() {
+        if (this.queue.length !== 0) {
+            const e = this.queue[0];
+            this.currentAction = e,
+            await e.action(),
+            this.currentAction = void 0,
+            this.queue.splice(0, 1),
+            await this.go()
+        }
+    }
+    async reject() {
+        this.queue = []
+    }
+}

+ 108 - 0
Reporter.js

@@ -0,0 +1,108 @@
+const REPORT_NUM_PER_REQUEST$1 = 20;
+const SERVER_URLS = {
+    DEV: "wss://sit-eks.xverse.cn/ws",
+    PROD: "wss://eks.xverse.cn/ws"
+}
+  , REPORT_URL = {
+    DEV: "https://xa.xverse.cn:6680/collect",
+    PROD: "https://xa.xverse.cn/collect"
+}
+  , MAX_RECONNECT_COUNT = 3
+  , DEFAULT_JOINROOM_TIMEOUT = 15e3
+  , DEFAULT_MAIN_CAMERA_FOV = 50
+  , DEFAULT_AVATAR_SCALE = 1
+  , REPORT_NUM_PER_REQUEST = 20
+  , DEFAULT_OPEN_TIMEOUT_MS = 6e3
+  , WS_CLOSE_NORMAL = 1e3
+  , WS_CLOSE_RECONNECT = 3008
+  , PING_INTERVAL_MS = 1e3
+  , TEXTURE_URL = "https://static.xverse.cn/qqktv/texture.png"
+  , REPORT_MODULE_TYPE = "xverse-js"
+  , authenticationErrorCodes = [3001, 3002, 3003, 3005]
+  , RTT_MAX_VALUE = 200
+  , HB_MAX_VALUE = 500
+  , DURATION = 10
+  , NET_INTERVAL = 1;
+class Reporter extends EventEmitter {
+    constructor() {
+        super();
+        E(this, "_header", {});
+        E(this, "_body", {});
+        E(this, "_queue", []);
+        E(this, "_disabled", !1);
+        E(this, "_interval", null);
+        E(this, "_reportUrl");
+        E(this, "isDocumentLoaded", ()=>document.readyState === "complete");
+        this._header.logModuleId = REPORT_MODULE_TYPE,
+        this._header.url = location.href,
+        this._header.enviroment = ENV,
+        this._header.networkType = window.navigator.connection ? window.navigator.connection.type : "unknown",
+        this._interval = window.setInterval(()=>{
+            this._flushReport()
+        }
+        , 10 * 1e3)
+    }
+    disable() {
+        this._disabled = !0,
+        this._interval && window.clearInterval(this._interval)
+    }
+    updateHeader(e) {
+        Object.assign(this._header, e)
+    }
+    updateBody(e) {
+        Object.assign(this._body, e)
+    }
+    updateReportUrl(e) {
+        this._reportUrl = e
+    }
+    report(e, t, r) {
+        if (this._disabled)
+            return;
+        r || (r = {});
+        const {immediate: n, sampleRate: o} = r;
+        if (o && o > Math.random())
+            return;
+        this.updateBody({
+            logTime: getFormattedDate(new Date),
+            logTimestamp: Date.now()
+        });
+        const a = s=>{
+            const l = oe(le(oe({}, this._body), {
+                type: e
+            }), s);
+            this._queue.push(l),
+            e === "measurement" && this.emit("report", s)
+        }
+        ;
+        Array.isArray(t) ? t.forEach(s=>a(s)) : a(t),
+        (n || this._queue.length >= REPORT_NUM_PER_REQUEST) && this._flushReport()
+    }
+    _flushReport() {
+        if (this._disabled || !this._queue.length || !this.isDocumentLoaded())
+            return;
+        const e = {
+            header: this._header,
+            body: this._queue.splice(0, REPORT_NUM_PER_REQUEST)
+        };
+        this._post(e)
+    }
+    _post(e) {
+        const t = this._reportUrl || REPORT_URL.DEV;
+        return new Promise((r,n)=>{
+            const o = new XMLHttpRequest;
+            o.open("POST", t),
+            o.setRequestHeader("Content-Type", "application/json");
+            try {
+                o.send(JSON.stringify(e))
+            } catch (a) {
+                console.error(a)
+            }
+            o.addEventListener("readystatechange", ()=>{
+                if (o.readyState == 4)
+                    return o.status == 200 ? r(o) : n("Unable to send log")
+            }
+            )
+        }
+        )
+    }
+}

+ 107 - 0
RotationEvent.js

@@ -0,0 +1,107 @@
+class RotationEvent {
+    constructor(e) {
+        E(this, "touchStartX");
+        E(this, "touchStartY");
+        E(this, "handelResize");
+        E(this, "_room");
+        E(this, "_canvas");
+        E(this, "handleTouchStart", e=>{
+            const t = e.touches[0];
+            this.touchStartX = t.pageX,
+            this.touchStartY = t.pageY,
+            this._room.emit("touchStart", {
+                event: e
+            })
+        }
+        );
+        E(this, "handleMouseDown", e=>{
+            this.touchStartX = e.pageX,
+            this.touchStartY = e.pageY
+        }
+        );
+        E(this, "handleMouseMove", e=>{
+            if (!this.touchStartX || !this.touchStartY)
+                return;
+            const t = e.pageX
+              , r = e.pageY
+              , n = t - this.touchStartX
+              , o = r - this.touchStartY
+              , a = this._room.options.canvas.offsetHeight
+              , s = this._room.options.canvas.offsetWidth;
+            let l = 2 * o / a
+              , u = 2 * n / s;
+            l > 1 && (l = 1),
+            u > 1 && (u = 1),
+            this._room.actionsHandler.rotate({
+                pitch: l,
+                yaw: u
+            }),
+            this.touchStartX = t,
+            this.touchStartY = r
+        }
+        );
+        E(this, "handleMouseUp", ()=>{
+            this.touchStartX = void 0,
+            this.touchStartY = void 0
+        }
+        );
+        E(this, "handleTouchMove", e=>{
+            if (!this.touchStartX || !this.touchStartY)
+                return;
+            const t = e.touches[0]
+              , r = t.pageX
+              , n = t.pageY
+              , o = r - this.touchStartX
+              , a = n - this.touchStartY
+              , s = this._room.options.canvas.offsetHeight
+              , l = this._room.options.canvas.offsetWidth;
+            let u = 2 * a / s
+              , c = 2 * o / l;
+            u > 1 && (u = 1),
+            c > 1 && (c = 1),
+            this._room.actionsHandler.rotate({
+                pitch: u,
+                yaw: c
+            }),
+            this.touchStartX = r,
+            this.touchStartY = n,
+            this._room.emit("touchMove", {
+                pitch: u,
+                yaw: c,
+                event: e
+            })
+        }
+        );
+        E(this, "handleTouchEnd", e=>{
+            this._room.emit("touchEnd", {
+                event: e
+            })
+        }
+        );
+        this._room = e,
+        this._canvas = e.canvas,
+        this.handelResize = this.reiszeChange()
+    }
+    init() {
+        this._canvas.addEventListener("touchstart", this.handleTouchStart),
+        this._canvas.addEventListener("touchmove", this.handleTouchMove),
+        this._canvas.addEventListener("touchend", this.handleTouchEnd),
+        this._room.scene.preventDefaultOnPointerDown = !1,
+        this._room.scene.preventDefaultOnPointerUp = !1,
+        this._canvas.addEventListener("mousedown", this.handleMouseDown),
+        this._canvas.addEventListener("mousemove", this.handleMouseMove),
+        this._canvas.addEventListener("mouseup", this.handleMouseUp)
+    }
+    clear() {
+        this._canvas.removeEventListener("touchstart", this.handleTouchStart),
+        this._canvas.removeEventListener("touchmove", this.handleTouchMove),
+        this._canvas.removeEventListener("touchend", this.handleTouchEnd),
+        this._canvas.removeEventListener("mousedown", this.handleMouseDown),
+        this._canvas.removeEventListener("mousemove", this.handleMouseMove),
+        this._canvas.removeEventListener("mouseup", this.handleMouseUp)
+    }
+    reiszeChange() {
+        window.addEventListener("resize", ()=>{}
+        )
+    }
+}

+ 206 - 0
Rtcp.js

@@ -0,0 +1,206 @@
+const log$l = new Logger("rtcp");
+class Rtcp extends EventEmitter {
+    constructor(e) {
+        super();
+        E(this, "connection", null);
+        E(this, "inputChannel", null);
+        E(this, "mediaStream");
+        E(this, "socket");
+        E(this, "connected", !1);
+        E(this, "candidates", []);
+        E(this, "isAnswered", !1);
+        E(this, "isFlushing", !1);
+        E(this, "inputReady", !1);
+        E(this, "workers");
+        E(this, "actived", !0);
+        E(this, "heartbeat");
+        E(this, "onIcecandidate", e=>{
+            if (e.candidate != null) {
+                const t = JSON.stringify(e.candidate);
+                log$l.debug(`Got ice candidate: ${t}`),
+                this.network.socket.send({
+                    id: "ice_candidate",
+                    data: btoa(t)
+                })
+            }
+        }
+        );
+        E(this, "onIcecandidateerror", e=>{
+            log$l.error("onicecandidateerror", e.errorCode, e.errorText, e)
+        }
+        );
+        E(this, "onIceStateChange", e=>{
+            switch (e.target.iceGatheringState) {
+            case "gathering":
+                log$l.info("ice gathering");
+                break;
+            case "complete":
+                log$l.info("Ice gathering completed")
+            }
+        }
+        );
+        E(this, "onIceConnectionStateChange", ()=>{
+            if (!!this.connection)
+                switch (log$l.info(`iceConnectionState: ${this.connection.iceConnectionState}`),
+                this.connection.iceConnectionState) {
+                case "connected":
+                    {
+                        this.connected = !0;
+                        break
+                    }
+                case "disconnected":
+                    {
+                        this.connected = !1,
+                        this.emit("rtcDisconnected");
+                        break
+                    }
+                case "failed":
+                    {
+                        this.emit("rtcDisconnected"),
+                        this.connected = !1;
+                        break
+                    }
+                }
+        }
+        );
+        E(this, "setRemoteDescription", async(e,t)=>{
+            var a, s, l;
+            if (!this.connection)
+                return;
+            const r = JSON.parse(atob(e))
+              , n = new RTCSessionDescription(r);
+            await this.connection.setRemoteDescription(n);
+            const o = await this.connection.createAnswer();
+            if (o.sdp = (a = o.sdp) == null ? void 0 : a.replace(/(a=fmtp:111 .*)/g, "$1;stereo=1;sprop-stereo=1"),
+            ((l = (s = o.sdp) == null ? void 0 : s.match(/a=mid:1/g)) == null ? void 0 : l.length) == 2) {
+                const u = o.sdp.lastIndexOf("a=mid:1");
+                o.sdp = o.sdp.slice(0, u) + "a=mid:2" + o.sdp.slice(u + 7)
+            }
+            try {
+                await this.connection.setLocalDescription(o)
+            } catch (u) {
+                log$l.error("error", u)
+            }
+            this.isAnswered = !0,
+            this.network.rtcp.flushCandidate(),
+            this.network.socket.send({
+                id: "answer",
+                data: btoa(JSON.stringify(o))
+            }),
+            t.srcObject = this.mediaStream
+        }
+        );
+        E(this, "flushCandidate", ()=>{
+            this.isFlushing || !this.isAnswered || (this.isFlushing = !0,
+            this.candidates.forEach(e=>{
+                const t = atob(e)
+                  , r = JSON.parse(t);
+                if (/172\./.test(r.candidate))
+                    return;
+                const n = new RTCIceCandidate(r);
+                this.connection && this.connection.addIceCandidate(n).then(()=>{}
+                , o=>{
+                    log$l.info("add candidate failed", o)
+                }
+                )
+            }
+            ),
+            this.isFlushing = !1)
+        }
+        );
+        E(this, "input", e=>{
+            var t;
+            !this.actived || !this.inputChannel || this.inputChannel.readyState === "open" && ((t = this.inputChannel) == null || t.send(e))
+        }
+        );
+        this.network = e,
+        this.workers = new Workers(this,new Logger("decode")),
+        this.workers.registerLogger(new Logger("decode")),
+        this.workers.registerFunction("data", t=>{
+            this.emit("data", t)
+        }
+        ),
+        this.heartbeat = new Heartbeat({
+            ping: t=>{
+                e.room.actionsHandler.echo(t)
+            }
+            ,
+            pong(t, r) {
+                var n;
+                r && t > 500 && log$l.warn(`high hb value ${t}, traceId:` + r),
+                (n = e.room.stats) == null || n.assign({
+                    hb: t
+                })
+            }
+        })
+    }
+    start() {
+        this.connection = new RTCPeerConnection;
+        const e = Date.now();
+        this.connection.ondatachannel = t=>{
+            log$l.info(`ondatachannel: ${t.channel.label}`),
+            this.inputChannel = t.channel,
+            this.inputChannel.onopen = ()=>{
+                var r;
+                log$l.info("The input channel has opened, id:", (r = this.inputChannel) == null ? void 0 : r.id),
+                this.inputReady = !0,
+                this.emit("rtcConnected"),
+                this.network.room.currentNetworkOptions.reconnect || (log$l.infoAndReportMeasurement({
+                    metric: "datachannelOpenedAt",
+                    startTime: this.network.room._startTime,
+                    group: "joinRoom"
+                }),
+                log$l.infoAndReportMeasurement({
+                    metric: "datachannelOpenedCost",
+                    startTime: e,
+                    group: "joinRoom"
+                }))
+            }
+            ,
+            this.inputChannel.onclose = ()=>{
+                var r;
+                return log$l.info("The input channel has closed, id:", (r = this.inputChannel) == null ? void 0 : r.id)
+            }
+            ,
+            this.inputChannel.onmessage = r=>{
+                this.workers.dataHandle(r.data)
+            }
+        }
+        ,
+        this.connection.oniceconnectionstatechange = this.onIceConnectionStateChange,
+        this.connection.onicegatheringstatechange = this.onIceStateChange,
+        this.connection.onicecandidate = this.onIcecandidate,
+        this.connection.onicecandidateerror = this.onIcecandidateerror,
+        this.network.socket.send({
+            id: "init_webrtc",
+            data: JSON.stringify({
+                is_mobile: !0
+            })
+        })
+    }
+    addCandidate(e) {
+        e === "" ? this.network.rtcp.flushCandidate() : this.candidates.push(e)
+    }
+    disconnect() {
+        var e, t, r;
+        this.heartbeat.stop(),
+        log$l.info("ready to close datachannel, id", (e = this.inputChannel) == null ? void 0 : e.id),
+        (t = this.inputChannel) == null || t.close(),
+        (r = this.connection) == null || r.close(),
+        this.connection = null,
+        this.inputChannel = null
+    }
+    sendStringData(e) {
+        this.input(e)
+    }
+    sendData(e) {
+        let t = "";
+        try {
+            t = JSON.stringify(e)
+        } catch (r) {
+            log$l.error(r);
+            return
+        }
+        this.input(t)
+    }
+}

+ 38 - 0
RunTimeArray.js

@@ -0,0 +1,38 @@
+class RunTimeArray {
+    constructor() {
+        E(this, "circularData");
+        this.circularData = []
+    }
+    add(e) {
+        this.circularData.length > 1e3 && this.circularData.shift(),
+        this.circularData.push(e)
+    }
+    getAvg() {
+        let e = 0;
+        for (let t = 0; t < this.circularData.length; t++)
+            e += this.circularData[t];
+        return {
+            sum: e,
+            avg: e / this.circularData.length || 0
+        }
+    }
+    getMax() {
+        let e = 0;
+        for (let t = 0; t < this.circularData.length; t++)
+            e < this.circularData[t] && (e = this.circularData[t]);
+        return e || 0
+    }
+    clear() {
+        this.circularData = []
+    }
+    getStat() {
+        const e = this.getAvg()
+          , t = {
+            sum: e.sum,
+            avg: e.avg,
+            max: this.getMax()
+        };
+        return this.clear(),
+        t
+    }
+}

+ 99 - 0
Signal.js

@@ -0,0 +1,99 @@
+const log$4 = new Logger("xverse-signal");
+class Signal {
+    constructor(e) {
+        E(this, "_room");
+        E(this, "signalHandleActived", !0);
+        E(this, "isUpdatedYUV", !0);
+        this._room = e
+    }
+    handleSignal(e) {
+        var a, s, l;
+        if (!this.signalHandleActived)
+            return;
+        const {signal: t, alreadyUpdateYUV: r} = e;
+        if (this.handleActionResponses(t),
+        this._room.handleSignalHook(t),
+        !r) {
+            const u = (a = t.newUserStates) == null ? void 0 : a.find(c=>c.userId === this._room.userId);
+            if ((u == null ? void 0 : u.renderInfo) && ((s = this._room._userAvatar) == null ? void 0 : s.isMoving)) {
+                log$4.debug("stream stoped, make avatar to stop");
+                const {isMoving: c, isRotating: h} = u.renderInfo;
+                this._room.avatarManager._updateAvatarMovingStatus({
+                    id: u.userId,
+                    isMoving: !!c,
+                    isRotating: !!h
+                })
+            }
+            return
+        }
+        this.isUpdatedYUV = r;
+        const n = t;
+        if (!t) {
+            log$4.warn("metadata signal is empty");
+            return
+        }
+        if (n.code === Codes$1.RepeatLogin) {
+            this._room.handleRepetLogin();
+            return
+        }
+        n.code !== void 0 && n.code !== Codes$1.Success && n.code !== Codes$1.ActionMaybeDelay && n.code !== Codes$1.DoActionBlocked && n.code !== Codes$1.GetOnVehicle && (log$4.error("signal errcode: ", n),
+        this._room.emit("error", n));
+        const o = (l = n.newUserStates) == null ? void 0 : l.find(u=>u.userId === this._room.userId);
+        if (n.broadcastAction)
+            try {
+                const u = JSON.parse(n.broadcastAction.data);
+                Broadcast.handlers.forEach(c=>c(u))
+            } catch (u) {
+                log$4.error(u)
+            }
+        if (n.newUserStates && n.newUserStates.length > 0 && this._room.avatarManager.handleAvatar(n),
+        o != null && o.playerState) {
+            this._room._currentClickingState = o.playerState;
+            const {pathName: u, attitude: c, areaName: h, skinId: f} = o.playerState;
+            if (u && (this._room.pathManager.currentPathName = u,
+            this._room.updateCurrentState({
+                pathName: u
+            })),
+            f && this.udpateSkinInfo(f),
+            h && this._room.updateCurrentState({
+                areaName: h
+            }),
+            c) {
+                const d = this._room.skin.routeList.find(g=>g.areaName === this._room.currentState.areaName)
+                  , _ = ((d == null ? void 0 : d.step) || 7.5) * 30;
+                this._room.updateCurrentState({
+                    speed: _,
+                    attitude: c
+                }),
+                this._room.pathManager.currentAttitude = c,
+                this._room._userAvatar && (this._room._userAvatar.motionType = c)
+            }
+            this._room.sceneManager.getCurrentShaderMode() !== ECurrentShaderMode.pano && !this._room.isPano && o.playerState.camera && this._room.camera.setCameraPose(o.playerState.camera)
+        }
+        if (o != null && o.renderInfo && this._room.camera.handleRenderInfo(o),
+        n.actionType !== void 0) {
+            const {actionType: u, code: c, echoMsg: h, traceId: f} = n;
+            u === Actions.Echo && c === Codes$1.Success && this._room.networkController.rtcp.heartbeat.pong(h, f),
+            c !== Codes$1.Success ? eventsManager.remove(f, c) : [Actions.GetReserveStatus, Actions.Broadcast, Actions.ChangeNickname, Actions.ConfirmEvent, Actions.ReserveSeat, Actions.Rotation, Actions.TurnTo, Actions.RotateTo, Actions.SetPlayerState, Actions.GetNeighborPoints, Actions.TurnToFace, Actions.AudienceChangeToVisitor, Actions.RemoveVisitor, Actions.GetUserWithAvatar].includes(u) && eventsManager.remove(f, c, n)
+        }
+    }
+    handleActionResponses(e) {
+        !(e != null && e.actionResponses) || e.actionResponses.length === 0 || e.actionResponses.forEach(t=>{
+            if (t.actionType == null)
+                return;
+            const {pointType: r, extra: n, actionType: o, traceId: a, code: s, msg: l} = t;
+            o === Actions.GetNeighborPoints ? eventsManager.remove(a, s, t.nps) : o === Actions.GetUserWithAvatar ? eventsManager.remove(a, s, t.userWithAvatarList) : eventsManager.remove(a, s, l),
+            r === PointType.Path && o === Actions.Clicking && (this._room.moveToExtra = decodeURIComponent(n))
+        }
+        )
+    }
+    async udpateSkinInfo(e) {
+        this._room.updateCurrentState({
+            skinId: e
+        });
+        const t = await this._room.skinList.find(r=>r.id === e);
+        t && this._room.updateCurrentState({
+            skin: t
+        })
+    }
+}

+ 504 - 0
Socket.js

@@ -0,0 +1,504 @@
+class Socket extends EventEmitter {
+    constructor(e) {
+        super();
+        E(this, "_ws");
+        E(this, "_openTimer");
+        E(this, "connected", !1);
+        E(this, "_hasTimeout", !1);
+        E(this, "heartbeat");
+        E(this, "latency", (e,t)=>this.send({
+            id: "checkLatency",
+            data: JSON.stringify(e),
+            packet_id: tconst log$k = new Logger("ws");
+class Socket extends EventEmitter {
+    constructor(e) {
+        super();
+        E(this, "_ws");
+        E(this, "_openTimer");
+        E(this, "connected", !1);
+        E(this, "_hasTimeout", !1);
+        E(this, "heartbeat");
+        E(this, "latency", (e,t)=>this.send({
+            id: "checkLatency",
+            data: JSON.stringify(e),
+            packet_id: t
+        }));
+        E(this, "send", e=>{
+            if (this.wsNoReady())
+                return;
+            const t = JSON.stringify(e);
+            e.id !== "heartbeat" && log$k.info("send ws frame", t),
+            this._ws.send(t)
+        }
+        );
+        E(this, "startGame", ()=>{
+            const {roomId: e, userId: t, avatarId: r, skinId: n, role: o, avatarComponents: a, versionId: s, rotationRenderType: l, isAllSync: u, nickname: c, avatarScale: h, appId: f, camera: d, player: _, firends: g, syncByEvent: m, areaName: v, attitude: y, pathName: b, person: T, roomTypeId: C="", syncToOthers: A, hasAvatar: S, prioritySync: P, extra: R={}, removeWhenDisconnected: M} = this.network.room.currentNetworkOptions;
+            R.removeWhenDisconnected = M;
+            const x = {
+                id: "start",
+                room_id: e,
+                user_id: t,
+                trace_id: uuid$1(),
+                data: JSON.stringify({
+                    avatar_components: JSON.stringify(a),
+                    avatar_id: r,
+                    skin_id: n,
+                    is_host: o ? o == "host" : !0,
+                    skin_data_version: n !== void 0 && s !== void 0 ? n + s : void 0,
+                    rotation_render_type: l,
+                    is_all_sync: u,
+                    nick_name: encodeURIComponent(c || ""),
+                    app_id: f,
+                    camera: d,
+                    player: _,
+                    person: T,
+                    firends: JSON.stringify(g),
+                    sync_by_event: m,
+                    area_name: v,
+                    path_name: b,
+                    attitude: y,
+                    room_type_id: C,
+                    syncToOthers: A,
+                    hasAvatar: S,
+                    avatarSize: h,
+                    prioritySync: P,
+                    extra: JSON.stringify(R)
+                })
+            };
+            this.send(x),
+            log$k.warn("startGame", le(oe({}, x), {
+                data: JSON.parse(x.data)
+            }))
+        }
+        );
+        this.network = e,
+        this.heartbeat = new Heartbeat({
+            ping: t=>{
+                var r;
+                if (!this.connected) {
+                    this.heartbeat.stop(),
+                    (r = e.room.stats) == null || r.assign({
+                        rtt: 0
+                    });
+                    return
+                }
+                this.send({
+                    id: "heartbeat",
+                    data: t
+                })
+            }
+            ,
+            pong(t) {
+                var r;
+                (r = e.room.stats) == null || r.assign({
+                    rtt: t
+                })
+            }
+        })
+    }
+    get connection() {
+        return this._ws
+    }
+    start() {
+        this._hasTimeout = !1;
+        const e = this.getAddress();
+        log$k.info(`connecting to ${e}`);
+        const t = Date.now();
+        this._ws = new WebSocket(e),
+        this._openTimer = new Timeout(()=>{
+            const r = `Failed to open websocket in ${DEFAULT_OPEN_TIMEOUT_MS} ms`;
+            this._hasTimeout = !0,
+            this.emit("socketClosed", new InitNetworkTimeoutError(r))
+        }
+        ,DEFAULT_OPEN_TIMEOUT_MS),
+        this._ws.onopen = ()=>{
+            var r;
+            (r = this._openTimer) == null || r.clear(),
+            this.connected = !0,
+            this.heartbeat.start(),
+            this.network.room.currentNetworkOptions.reconnect || (log$k.infoAndReportMeasurement({
+                metric: "wsOpenedAt",
+                group: "joinRoom",
+                startTime: this.network.room._startTime
+            }),
+            log$k.infoAndReportMeasurement({
+                metric: "wsOpenedCost",
+                group: "joinRoom",
+                startTime: t
+            }))
+        }
+        ,
+        this.handleWSEvent()
+    }
+    getAddress() {
+        const {wsServerUrl: e, reconnect: t, sessionId: r, token: n, roomId: o, userId: a, pageSession: s} = this.network.room.currentNetworkOptions
+          , l = this.network.room.skinId;
+        let u = e;
+        t && (u = u + `?reconnect=true&lastSessionID=${r}`);
+        const c = `userId=${a}&roomId=${o}&pageSession=${s}` + (this.network.room.isHost ? `&skinId=${l}` : "") + (n ? `&token=${n}` : "");
+        return u = u.indexOf("?") > -1 ? u + "&" + c : u + "?" + c,
+        u
+    }
+    handleWSEvent() {
+        const e = this._ws;
+        e.addEventListener("error", t=>{
+            this.connected = !1,
+            log$k.error("webscoket error", t),
+            this.emit("socketClosed", new InternalError("connect to address error: " + this.network.room.currentNetworkOptions.wsServerUrl))
+        }
+        ),
+        e.addEventListener("close", t=>{
+            this.connected = !1,
+            this._onClose(t)
+        }
+        ),
+        e.addEventListener("message", t=>{
+            if (!t || this._hasTimeout || !this.connected)
+                return;
+            let r = null;
+            try {
+                r = JSON.parse(t.data)
+            } catch (o) {
+                log$k.error(o);
+                return
+            }
+            if (!r)
+                return;
+            const n = r.id;
+            if (!!n)
+                switch (n !== "heartbeat" && log$k.info(`receive ws frame: ${t.data}`),
+                n) {
+                case "fail":
+                    break;
+                case "init":
+                    try {
+                        const o = r.data.slice(-37, -1);
+                        reporter.updateBody({
+                            serverSession: o
+                        })
+                    } catch (o) {
+                        console.error(o)
+                    }
+                    this.network.rtcp.start();
+                    break;
+                case "heartbeat":
+                    this.heartbeat.pong(r.data);
+                    break;
+                case "offer":
+                    this.network.rtcp.setRemoteDescription(r.data, this.network.stream.el);
+                    break;
+                case "ice_candidate":
+                    this.network.rtcp.addCandidate(r.data);
+                    break;
+                case "start":
+                    this.emit("gameRoomAvailable", r);
+                    break;
+                case "error":
+                    try {
+                        const {Code: o, Msg: a} = JSON.parse(r.data);
+                        if (o) {
+                            if (o == 3003)
+                                return this.emit("socketClosed", new TokenExpiredError);
+                            if (authenticationErrorCodes.indexOf(o) > -1)
+                                return this.emit("socketClosed", new AuthenticationError("\u9274\u6743\u9519\u8BEF:" + a));
+                            {
+                                const s = getErrorByCode(o);
+                                this.emit("socketClosed", new s(a))
+                            }
+                        }
+                    } catch (o) {
+                        log$k.error(o),
+                        this.emit("socketClosed", new InternalError(r.data))
+                    }
+                    break;
+                case "checkLatency":
+                    {
+                        const o = r.packet_id
+                          , a = r.data.split(",");
+                        this.onLatencyCheck({
+                            packetId: o,
+                            addresses: a
+                        });
+                        break
+                    }
+                default:
+                    log$k.warn("unkown ws message type", n, r)
+                }
+        }
+        )
+    }
+    onLatencyCheck(e) {
+        const t = [...new Set(e.addresses || [])];
+        Promise.all(t.map(r=>({
+            [r]: 9999
+        }))).then(r=>{
+            const n = Object.assign({}, ...r);
+            this.latency(n, e.packetId)
+        }
+        )
+    }
+    wsNoReady() {
+        return this._ws.readyState == WebSocket.CLOSED || this._ws.readyState == WebSocket.CLOSING || this._ws.readyState == WebSocket.CONNECTING
+    }
+    prepareReconnect() {
+        this._close({
+            code: WS_CLOSE_RECONNECT,
+            reason: "reconnect"
+        })
+    }
+    _onClose({code: e, reason: t}) {
+        this._openTimer && this._openTimer.clear(),
+        log$k.warn(`ws closed: ${e} ` + t),
+        [WS_CLOSE_RECONNECT, WS_CLOSE_NORMAL].includes(e) || this.emit("socketClosed", new InternalError("Websocket error"))
+    }
+    _close({code: e, reason: t}) {
+        var r;
+        (r = this._ws) == null || r.close(e, t)
+    }
+    quit() {
+        this._close({
+            code: WS_CLOSE_NORMAL,
+            reason: "quit"
+        })
+    }
+}
+        }));
+        E(this, "send", e=>{
+            if (this.wsNoReady())
+                return;
+            const t = JSON.stringify(e);
+            e.id !== "heartbeat" && log$k.info("send ws frame", t),
+            this._ws.send(t)
+        }
+        );
+        E(this, "startGame", ()=>{
+            const {roomId: e, userId: t, avatarId: r, skinId: n, role: o, avatarComponents: a, versionId: s, rotationRenderType: l, isAllSync: u, nickname: c, avatarScale: h, appId: f, camera: d, player: _, firends: g, syncByEvent: m, areaName: v, attitude: y, pathName: b, person: T, roomTypeId: C="", syncToOthers: A, hasAvatar: S, prioritySync: P, extra: R={}, removeWhenDisconnected: M} = this.network.room.currentNetworkOptions;
+            R.removeWhenDisconnected = M;
+            const x = {
+                id: "start",
+                room_id: e,
+                user_id: t,
+                trace_id: uuid$1(),
+                data: JSON.stringify({
+                    avatar_components: JSON.stringify(a),
+                    avatar_id: r,
+                    skin_id: n,
+                    is_host: o ? o == "host" : !0,
+                    skin_data_version: n !== void 0 && s !== void 0 ? n + s : void 0,
+                    rotation_render_type: l,
+                    is_all_sync: u,
+                    nick_name: encodeURIComponent(c || ""),
+                    app_id: f,
+                    camera: d,
+                    player: _,
+                    person: T,
+                    firends: JSON.stringify(g),
+                    sync_by_event: m,
+                    area_name: v,
+                    path_name: b,
+                    attitude: y,
+                    room_type_id: C,
+                    syncToOthers: A,
+                    hasAvatar: S,
+                    avatarSize: h,
+                    prioritySync: P,
+                    extra: JSON.stringify(R)
+                })
+            };
+            this.send(x),
+            log$k.warn("startGame", le(oe({}, x), {
+                data: JSON.parse(x.data)
+            }))
+        }
+        );
+        this.network = e,
+        this.heartbeat = new Heartbeat({
+            ping: t=>{
+                var r;
+                if (!this.connected) {
+                    this.heartbeat.stop(),
+                    (r = e.room.stats) == null || r.assign({
+                        rtt: 0
+                    });
+                    return
+                }
+                this.send({
+                    id: "heartbeat",
+                    data: t
+                })
+            }
+            ,
+            pong(t) {
+                var r;
+                (r = e.room.stats) == null || r.assign({
+                    rtt: t
+                })
+            }
+        })
+    }
+    get connection() {
+        return this._ws
+    }
+    start() {
+        this._hasTimeout = !1;
+        const e = this.getAddress();
+        log$k.info(`connecting to ${e}`);
+        const t = Date.now();
+        this._ws = new WebSocket(e),
+        this._openTimer = new Timeout(()=>{
+            const r = `Failed to open websocket in ${DEFAULT_OPEN_TIMEOUT_MS} ms`;
+            this._hasTimeout = !0,
+            this.emit("socketClosed", new InitNetworkTimeoutError(r))
+        }
+        ,DEFAULT_OPEN_TIMEOUT_MS),
+        this._ws.onopen = ()=>{
+            var r;
+            (r = this._openTimer) == null || r.clear(),
+            this.connected = !0,
+            this.heartbeat.start(),
+            this.network.room.currentNetworkOptions.reconnect || (log$k.infoAndReportMeasurement({
+                metric: "wsOpenedAt",
+                group: "joinRoom",
+                startTime: this.network.room._startTime
+            }),
+            log$k.infoAndReportMeasurement({
+                metric: "wsOpenedCost",
+                group: "joinRoom",
+                startTime: t
+            }))
+        }
+        ,
+        this.handleWSEvent()
+    }
+    getAddress() {
+        const {wsServerUrl: e, reconnect: t, sessionId: r, token: n, roomId: o, userId: a, pageSession: s} = this.network.room.currentNetworkOptions
+          , l = this.network.room.skinId;
+        let u = e;
+        t && (u = u + `?reconnect=true&lastSessionID=${r}`);
+        const c = `userId=${a}&roomId=${o}&pageSession=${s}` + (this.network.room.isHost ? `&skinId=${l}` : "") + (n ? `&token=${n}` : "");
+        return u = u.indexOf("?") > -1 ? u + "&" + c : u + "?" + c,
+        u
+    }
+    handleWSEvent() {
+        const e = this._ws;
+        e.addEventListener("error", t=>{
+            this.connected = !1,
+            log$k.error("webscoket error", t),
+            this.emit("socketClosed", new InternalError("connect to address error: " + this.network.room.currentNetworkOptions.wsServerUrl))
+        }
+        ),
+        e.addEventListener("close", t=>{
+            this.connected = !1,
+            this._onClose(t)
+        }
+        ),
+        e.addEventListener("message", t=>{
+            if (!t || this._hasTimeout || !this.connected)
+                return;
+            let r = null;
+            try {
+                r = JSON.parse(t.data)
+            } catch (o) {
+                log$k.error(o);
+                return
+            }
+            if (!r)
+                return;
+            const n = r.id;
+            if (!!n)
+                switch (n !== "heartbeat" && log$k.info(`receive ws frame: ${t.data}`),
+                n) {
+                case "fail":
+                    break;
+                case "init":
+                    try {
+                        const o = r.data.slice(-37, -1);
+                        reporter.updateBody({
+                            serverSession: o
+                        })
+                    } catch (o) {
+                        console.error(o)
+                    }
+                    this.network.rtcp.start();
+                    break;
+                case "heartbeat":
+                    this.heartbeat.pong(r.data);
+                    break;
+                case "offer":
+                    this.network.rtcp.setRemoteDescription(r.data, this.network.stream.el);
+                    break;
+                case "ice_candidate":
+                    this.network.rtcp.addCandidate(r.data);
+                    break;
+                case "start":
+                    this.emit("gameRoomAvailable", r);
+                    break;
+                case "error":
+                    try {
+                        const {Code: o, Msg: a} = JSON.parse(r.data);
+                        if (o) {
+                            if (o == 3003)
+                                return this.emit("socketClosed", new TokenExpiredError);
+                            if (authenticationErrorCodes.indexOf(o) > -1)
+                                return this.emit("socketClosed", new AuthenticationError("\u9274\u6743\u9519\u8BEF:" + a));
+                            {
+                                const s = getErrorByCode(o);
+                                this.emit("socketClosed", new s(a))
+                            }
+                        }
+                    } catch (o) {
+                        log$k.error(o),
+                        this.emit("socketClosed", new InternalError(r.data))
+                    }
+                    break;
+                case "checkLatency":
+                    {
+                        const o = r.packet_id
+                          , a = r.data.split(",");
+                        this.onLatencyCheck({
+                            packetId: o,
+                            addresses: a
+                        });
+                        break
+                    }
+                default:
+                    log$k.warn("unkown ws message type", n, r)
+                }
+        }
+        )
+    }
+    onLatencyCheck(e) {
+        const t = [...new Set(e.addresses || [])];
+        Promise.all(t.map(r=>({
+            [r]: 9999
+        }))).then(r=>{
+            const n = Object.assign({}, ...r);
+            this.latency(n, e.packetId)
+        }
+        )
+    }
+    wsNoReady() {
+        return this._ws.readyState == WebSocket.CLOSED || this._ws.readyState == WebSocket.CLOSING || this._ws.readyState == WebSocket.CONNECTING
+    }
+    prepareReconnect() {
+        this._close({
+            code: WS_CLOSE_RECONNECT,
+            reason: "reconnect"
+        })
+    }
+    _onClose({code: e, reason: t}) {
+        this._openTimer && this._openTimer.clear(),
+        log$k.warn(`ws closed: ${e} ` + t),
+        [WS_CLOSE_RECONNECT, WS_CLOSE_NORMAL].includes(e) || this.emit("socketClosed", new InternalError("Websocket error"))
+    }
+    _close({code: e, reason: t}) {
+        var r;
+        (r = this._ws) == null || r.close(e, t)
+    }
+    quit() {
+        this._close({
+            code: WS_CLOSE_NORMAL,
+            reason: "quit"
+        })
+    }
+}

+ 87 - 0
StaticMeshEvent.js

@@ -0,0 +1,87 @@
+const LongPressMesh = [EMeshType.XAvatar];
+class StaticMeshEvent extends EventEmitter {
+    constructor(e) {
+        super();
+        E(this, "scene");
+        E(this, "_staringPointerTime", -1);
+        E(this, "_pickedMeshID", "0");
+        E(this, "_pointerDownTime", -1);
+        E(this, "_currentPickPoint");
+        E(this, "_longPressDelay", 500);
+        E(this, "_pointerTapDelay", 200);
+        E(this, "_pickedMeshType");
+        E(this, "registerEvent", ()=>{
+            this.scene.onPrePointerObservable.add(this.onDown, PointerEventTypes.POINTERDOWN),
+            this.scene.onPrePointerObservable.add(this.onUp, PointerEventTypes.POINTERUP),
+            this.scene.onPrePointerObservable.add(this.onDoubleTap, PointerEventTypes.POINTERDOUBLETAP),
+            this.scene.onDispose = ()=>{
+                this.scene.onPrePointerObservable.removeCallback(this.onUp),
+                this.scene.onPrePointerObservable.removeCallback(this.onDown),
+                this.scene.onPrePointerObservable.removeCallback(this.onDoubleTap)
+            }
+        }
+        );
+        E(this, "onUp", ()=>{
+            if (Date.now() - this._pointerDownTime < this._pointerTapDelay && !this.scene._inputManager._isPointerSwiping()) {
+                this.scene._inputManager._totalPointersPressed = 0;
+                let e = this._currentPickPoint;
+                e != null && LongPressMesh.indexOf(e.type) == -1 && this.scene._inputManager._totalPointersPressed == 0 && this.emit("pointTap", e),
+                e != null && LongPressMesh.indexOf(e.type) != -1 && (e = this.onPointerTap(t=>t.isPickable && LongPressMesh.indexOf(t.xtype) == -1),
+                e != null && this.emit("pointTap", e))
+            }
+        }
+        );
+        E(this, "onDown", ()=>{
+            let e = this.onPointerTap(t=>t.isPickable);
+            this._currentPickPoint = e,
+            this._pointerDownTime = Date.now(),
+            e != null && LongPressMesh.indexOf(e.type) != -1 && (this._staringPointerTime = Date.now(),
+            this._pickedMeshID = e.id,
+            this._pickedMeshType = e.type,
+            window.setTimeout(()=>{
+                e = this.onPointerTap(t=>t.isPickable && t.xtype == this._pickedMeshType && t.xid == this._pickedMeshID),
+                e !== null && Date.now() - this._staringPointerTime > this._longPressDelay && !this.scene._inputManager._isPointerSwiping() && this.scene._inputManager._totalPointersPressed !== 0 && (this._staringPointerTime = 0,
+                this.emit("longPress", e))
+            }
+            , this._longPressDelay))
+        }
+        );
+        E(this, "onDoubleTap", ()=>{
+            const e = this.onPointerTap(void 0);
+            e != null && this.emit("pointDoubleTap", e)
+        }
+        );
+        this.manager = e,
+        this.scene = e.Scene,
+        this.registerEvent(),
+        this._currentPickPoint = null,
+        this._pickedMeshType = null
+    }
+    onPointerTap(e, t=!1) {
+        var n, o;
+        let r = new PickingInfo;
+        if (t) {
+            const a = this.scene.multiPick(this.scene.pointerX, this.scene.pointerY, e, void 0, void 0);
+            a && a.length > 1 ? r = a[1] : a && (r = a[0])
+        } else
+            r = this.scene.pick(this.scene.pointerX, this.scene.pointerY, e, !1, null);
+        if (r.hit) {
+            const a = (n = r == null ? void 0 : r.pickedPoint) == null ? void 0 : n.asArray();
+            if (a) {
+                const [s,l,u] = a
+                  , c = xversePosition2Ue4({
+                    x: s,
+                    y: l,
+                    z: u
+                });
+                return {
+                    name: (o = r.pickedMesh) == null ? void 0 : o.name,
+                    type: r.pickedMesh.xtype,
+                    id: r.pickedMesh.xid,
+                    point: c
+                }
+            }
+        }
+        return null
+    }
+}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 468 - 0
Stats.js


+ 46 - 0
Stream.js

@@ -0,0 +1,46 @@
+const log$j = new Logger("stream");
+class Stream {
+    constructor(e) {
+        E(this, "el");
+        E(this, "_streamPlayTimer", null);
+        E(this, "play", ()=>new Promise((e,t)=>{
+            this._streamPlayTimer = new Timeout(()=>{
+                t(new InternalError("Stream play timeout"))
+            }
+            ,5e3),
+            this.el && this.el.play().then(()=>{
+                var r;
+                e(),
+                log$j.info("Media can autoplay"),
+                (r = this._streamPlayTimer) == null || r.clear()
+            }
+            ).catch(r=>{
+                var n;
+                log$j.error("Media Failed to autoplay"),
+                log$j.error(r),
+                t(new InternalError("Media Failed to autoplay")),
+                (n = this._streamPlayTimer) == null || n.clear()
+            }
+            )
+        }
+        ));
+        if (!e) {
+            this.el = this.createVideoElement();
+            return
+        }
+        this.el = e
+    }
+    createVideoElement() {
+        const e = document.createElement("video");
+        return e.muted = !0,
+        e.autoplay = !1,
+        e.playsInline = !0,
+        e.setAttribute("autostart", "false"),
+        e.setAttribute("controls", "controls"),
+        e.setAttribute("muted", "true"),
+        e.setAttribute("preload", "auto"),
+        e.setAttribute("hidden", "hidden"),
+        document.body.appendChild(e),
+        e
+    }
+}

+ 82 - 0
TV.js

@@ -0,0 +1,82 @@
+class TV extends XTelevision {
+    constructor(e, t, r, n) {
+        super(r.scene, t, r.sceneManager, n);
+        E(this, "decal");
+        E(this, "id");
+        E(this, "imageUrl");
+        E(this, "mode", "video");
+        E(this, "room");
+        E(this, "setVideo", (e,t=!1,r=!0)=>super.setVideo(e, t, r).then(()=>this));
+        this.id = e,
+        this.room = r,
+        this.decal = new XDecalManager(r.sceneManager)
+    }
+    show() {
+        this.mode === "video" ? this.toggle(!0) : this.mode === "poster" && this.showPoster()
+    }
+    hide() {
+        this.mode === "video" ? this.toggle(!1) : this.mode === "poster" && this.hidePoster()
+    }
+    showVideo() {
+        this.mode = "video",
+        this.toggle(!0)
+    }
+    hideVideo() {
+        this.toggle(!1)
+    }
+    showPoster() {
+        const e = this.imageUrl;
+        if (!e)
+            return Promise.reject("set poster url before show it");
+        if (!this.decal)
+            return Promise.reject("decal was not found");
+        const t = this.id;
+        return this.decal.addDecal({
+            id: t,
+            meshPath: this.meshPath
+        }).then(()=>{
+            var r;
+            this.mode = "poster",
+            (r = this.decal) == null || r.setDecalTexture({
+                id: t,
+                buffer: e
+            }).then(()=>{
+                var n;
+                (n = this.decal) == null || n.toggle(t, !0)
+            }
+            )
+        }
+        )
+    }
+    setPoster(e) {
+        return this.imageUrl = e,
+        this.showPoster()
+    }
+    hidePoster() {
+        return this.decal ? this.decal.toggle(this.id, !1) : Promise.reject("decal was not found")
+    }
+    setUrl(e) {
+        const {url: t, loop: r, muted: n} = e || {};
+        return t ? super.setUrl({
+            url: t,
+            bLoop: r,
+            bMuted: n
+        }).then(()=>(this.videoElement && (this.videoElement.crossOrigin = "anonymous",
+        this.videoElement.playsInline = !0,
+        this.videoElement.load()),
+        this.mode = "video",
+        this)) : Promise.reject("tv url is required")
+    }
+    mirrorFrom(e) {
+        const t = e.getVideoMat();
+        return this.setSameVideo(t).then(()=>{
+            this.toggle(!0)
+        }
+        )
+    }
+    clean() {
+        var e;
+        this.cleanTv(!1, !0),
+        (e = this.decal) == null || e.deleteDecal(this.id)
+    }
+}

+ 35 - 0
Timeout.js

@@ -0,0 +1,35 @@
+class Timeout {
+    constructor(e, t, r=!0) {
+        E(this, "_fn");
+        E(this, "_delay");
+        E(this, "_timeout");
+        this._fn = e,
+        this._delay = t,
+        r && this.start()
+    }
+    get delay() {
+        return this._delay
+    }
+    get isSet() {
+        return !!this._timeout
+    }
+    setDelay(e) {
+        this._delay = e
+    }
+    start() {
+        this.isSet || (this._timeout = window.setTimeout(()=>{
+            const e = this._fn;
+            this.clear(),
+            e()
+        }
+        , this._delay))
+    }
+    clear() {
+        window.clearTimeout(this._timeout),
+        this._timeout = void 0
+    }
+    reset() {
+        this.clear(),
+        this.start()
+    }
+}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1157 - 0
Workers.js


+ 265 - 0
XAnimationController.js

@@ -0,0 +1,265 @@
+const avatarLoader = new XAvatarLoader
+  , log$J = new Logger$1("AnimationController");
+class XAnimationController {
+    constructor(e) {
+        E(this, "iBodyAnim");
+        E(this, "animations", []);
+        E(this, "defaultAnimation", "Idle");
+        E(this, "onPlay", "Idle");
+        E(this, "loop", !0);
+        E(this, "animationExtras", []);
+        E(this, "enableBlend", !1);
+        E(this, "enableSkLod", !1);
+        E(this, "_boneMap", new Map);
+        E(this, "_lodMask", new Map);
+        E(this, "activeFaceAnimation");
+        E(this, "iFaceAnim");
+        E(this, "_scene");
+        E(this, "_avatar");
+        E(this, "onPlayObservable", new Observable);
+        E(this, "postObserver");
+        E(this, "playAnimation", (e,t,r=0,n,o,a)=>new Promise((s,l)=>{
+            if (this._isPlaying(e, r) || (this._registerAnimInfo(e, t, r, n, o, a),
+            !this._isAnimate()))
+                return s(null);
+            this._prerocess(e, t),
+            this._avatar.avatarManager.loadAnimation(this._avatar.avatarType, e).then(u=>{
+                if (!u)
+                    return l(new AvatarAnimationError("animation group does not exist"));
+                const c = this._mappingSkeleton(u);
+                if (!c)
+                    return l(new AvatarAnimationError("mapping animation failed"));
+                if (c && this._isAnimationValid(c))
+                    return c.dispose(),
+                    l(new AvatarAnimationError("mapping animation failed"));
+                if (this.enableSkLod && this.skeletonMask(c, r),
+                this.detachAnimation(r),
+                r == 0 ? this.iBodyAnim.animGroup = c : r == 1 && (this.iFaceAnim.animGroup = c),
+                !this._playAnimation(r))
+                    return l(new AvatarAnimationError("[Engine] play animation failed, animtion resource does not match current character"));
+                this._playEffect(),
+                this.postObserver = c.onAnimationEndObservable.addOnce(()=>(this._postprocess(r),
+                s(null)))
+            }
+            )
+        }
+        ));
+        E(this, "stopAnimation", (e=0)=>{
+            var t, r, n, o;
+            switch (e) {
+            case 0:
+                this.iBodyAnim && this.iBodyAnim.animGroup && ((t = this.iBodyAnim) == null || t.animGroup.stop());
+                break;
+            case 1:
+                this.iFaceAnim && this.iFaceAnim.animGroup && ((r = this.iFaceAnim) == null || r.animGroup.stop());
+                break;
+            case 2:
+                this.iBodyAnim && this.iBodyAnim.animGroup && ((n = this.iBodyAnim) == null || n.animGroup.stop()),
+                this.iFaceAnim && this.iFaceAnim.animGroup && ((o = this.iFaceAnim) == null || o.animGroup.stop());
+                break
+            }
+        }
+        );
+        E(this, "pauseAnimation", (e=0)=>{
+            var t, r, n, o;
+            switch (e) {
+            case 0:
+                this.iBodyAnim && this.iBodyAnim.animGroup && ((t = this.iBodyAnim) == null || t.animGroup.pause());
+                break;
+            case 1:
+                this.iFaceAnim && this.iFaceAnim.animGroup && ((r = this.iFaceAnim) == null || r.animGroup.pause());
+                break;
+            case 2:
+                this.iBodyAnim && this.iBodyAnim.animGroup && ((n = this.iBodyAnim) == null || n.animGroup.pause()),
+                this.iFaceAnim && this.iFaceAnim.animGroup && ((o = this.iFaceAnim) == null || o.animGroup.pause());
+                break
+            }
+        }
+        );
+        E(this, "resetAnimation", (e=0)=>{
+            var t, r, n, o;
+            switch (e) {
+            case 0:
+                this.iBodyAnim && this.iBodyAnim.animGroup && ((t = this.iBodyAnim) == null || t.animGroup.reset());
+                break;
+            case 1:
+                this.iFaceAnim && this.iFaceAnim.animGroup && ((r = this.iFaceAnim) == null || r.animGroup.reset());
+                break;
+            case 2:
+                this.iBodyAnim && this.iBodyAnim.animGroup && ((n = this.iBodyAnim) == null || n.animGroup.reset()),
+                this.iFaceAnim && this.iFaceAnim.animGroup && ((o = this.iFaceAnim) == null || o.animGroup.reset());
+                break
+            }
+        }
+        );
+        this._avatar = e,
+        this._scene = e.avatarManager.scene,
+        this.animationExtras.push(action.Cheering.animName),
+        this._boneMap = new Map
+    }
+    _isPlaying(e, t) {
+        return t == 0 && this.iBodyAnim != null && this.iBodyAnim.animGroup && e == this.iBodyAnim.name ? !0 : !!(t == 1 && this.iFaceAnim != null && this.iFaceAnim.animGroup && e == this.iFaceAnim.name)
+    }
+    activeAnimation(e=0) {
+        var t, r;
+        switch (e) {
+        case 0:
+            return (t = this.iBodyAnim) == null ? void 0 : t.animGroup;
+        case 1:
+            return (r = this.iFaceAnim) == null ? void 0 : r.animGroup;
+        default:
+            return
+        }
+    }
+    enableAnimationBlend(e=.1, t=0) {
+        var r, n, o, a;
+        if (t == 0 && ((r = this.iBodyAnim) == null ? void 0 : r.animGroup))
+            for (const s of (n = this.iBodyAnim) == null ? void 0 : n.animGroup.targetedAnimations)
+                s.animation.enableBlending = !0,
+                s.animation.blendingSpeed = e;
+        else if (t == 0 && ((o = this.iFaceAnim) == null ? void 0 : o.animGroup))
+            for (const s of (a = this.iFaceAnim) == null ? void 0 : a.animGroup.targetedAnimations)
+                s.animation.enableBlending = !0,
+                s.animation.blendingSpeed = e
+    }
+    disableAnimationBlend(e=0) {
+        var t, r, n, o;
+        if (e == 0 && ((t = this.iBodyAnim) == null ? void 0 : t.animGroup))
+            for (const a of (r = this.iBodyAnim) == null ? void 0 : r.animGroup.targetedAnimations)
+                a.animation.enableBlending = !1;
+        else if (e == 0 && ((n = this.iFaceAnim) == null ? void 0 : n.animGroup))
+            for (const a of (o = this.iFaceAnim) == null ? void 0 : o.animGroup.targetedAnimations)
+                a.animation.enableBlending = !1
+    }
+    skeletonMask(e, t=0) {
+        if (t == 0) {
+            const r = this._lodMask.get(this._avatar.distLevel);
+            if (r)
+                for (let n = 0; n < e.targetedAnimations.length; ++n)
+                    r.includes(e.targetedAnimations[n].target.name) || (e.targetedAnimations.splice(n, 1),
+                    n--);
+            return !0
+        }
+        return !1
+    }
+    detachAnimation(e=2) {
+        var t, r;
+        switch (e) {
+        case 0:
+            this.iBodyAnim && this.iBodyAnim.animGroup && (this.iBodyAnim.animGroup._parentContainer.xReferenceCount && this.iBodyAnim.animGroup._parentContainer.xReferenceCount--,
+            this.iBodyAnim.animGroup.stop(),
+            this.iBodyAnim.animGroup.dispose(),
+            this.iBodyAnim.animGroup = void 0);
+            break;
+        case 1:
+            this.iFaceAnim && this.iFaceAnim.animGroup && (this.iFaceAnim.animGroup._parentContainer.xReferenceCount && this.iFaceAnim.animGroup._parentContainer.xReferenceCount--,
+            this.iFaceAnim.animGroup.stop(),
+            this.iFaceAnim.animGroup.dispose(),
+            this.iFaceAnim.animGroup = void 0);
+            break;
+        case 2:
+            this.iBodyAnim && this.iBodyAnim.animGroup && (this.iBodyAnim.animGroup._parentContainer.xReferenceCount && this.iBodyAnim.animGroup._parentContainer.xReferenceCount--,
+            (t = this.iBodyAnim) == null || t.animGroup.stop(),
+            (r = this.iBodyAnim) == null || r.animGroup.dispose(),
+            this.iBodyAnim.animGroup = void 0),
+            this.iFaceAnim && this.iFaceAnim.animGroup && (this.iFaceAnim.animGroup._parentContainer.xReferenceCount && this.iFaceAnim.animGroup._parentContainer.xReferenceCount--,
+            this.iFaceAnim.animGroup.stop(),
+            this.iFaceAnim.animGroup.dispose(),
+            this.iFaceAnim.animGroup = void 0);
+            break
+        }
+    }
+    blendAnimation() {}
+    getAnimation(e, t) {
+        return avatarLoader.animations.get(getAnimationKey(t, e))
+    }
+    _mappingSkeleton(e) {
+        if (e) {
+            const t = e.clone(e.name, r=>{
+                var o, a, s;
+                const n = r.name.split(" ").length > 2 ? r.name.split(" ")[2] : r.name;
+                if (this._boneMap.size === ((o = this._avatar.skeleton) == null ? void 0 : o.bones.length))
+                    return this._boneMap.get(n);
+                {
+                    const l = (s = (a = this._avatar.skeleton) == null ? void 0 : a.bones.find(u=>u.name === r.name || u.name === r.name.split(" ")[2])) == null ? void 0 : s.getTransformNode();
+                    return l && (l.name = n,
+                    this._boneMap.set(n, l)),
+                    l
+                }
+            }
+            );
+            return t._parentContainer = e._parentContainer,
+            t
+        } else
+            return
+    }
+    removeAnimation(e) {
+        const t = avatarLoader.containers.get(e.name);
+        t && (t.dispose(),
+        avatarLoader.containers.delete(e.name),
+        avatarLoader.animations.delete(getAnimationKey(e.name, e.skType)))
+    }
+    _setPosition(e, t) {
+        this._avatar.priority === 0 && this._avatar.isRender && e === this.defaultAnimation && e != this.onPlay && !this._avatar.isSelected && this._avatar.setPosition(this._avatar.position, !0)
+    }
+    _registerAnimInfo(e, t, r=0, n, o, a) {
+        const s = {
+            name: e,
+            skType: this._avatar.avatarType,
+            loop: t,
+            playSpeed: n,
+            currentFrame: 0,
+            startFrame: o,
+            endFrame: a
+        };
+        r == 0 ? this.iBodyAnim == null ? this.iBodyAnim = s : (this.iBodyAnim.name = e,
+        this.iBodyAnim.skType = this._avatar.avatarType,
+        this.iBodyAnim.loop = t,
+        this.iBodyAnim.playSpeed = n,
+        this.iBodyAnim.currentFrame = 0,
+        this.iBodyAnim.startFrame = o,
+        this.iBodyAnim.endFrame = a) : r == 1 && (this.iFaceAnim == null ? this.iFaceAnim = s : (this.iFaceAnim.name = e,
+        this.iFaceAnim.skType = this._avatar.avatarType,
+        this.iFaceAnim.loop = t,
+        this.iFaceAnim.playSpeed = n,
+        this.iFaceAnim.currentFrame = 0,
+        this.iFaceAnim.startFrame = o,
+        this.iFaceAnim.endFrame = a)),
+        this.onPlay = e,
+        this.loop = t
+    }
+    _isAnimate() {
+        var e;
+        return !(!this._avatar.isRender || !this._avatar.skeleton || ((e = this._avatar.rootNode) == null ? void 0 : e.getChildMeshes().length) == 0)
+    }
+    _prerocess(e, t) {
+        this._avatar.isRayCastEnable && this._setPosition(e, t),
+        this._avatar.priority === 0 && log$J.info(`start play animation: ${e} on avatar ${this._avatar.id}`)
+    }
+    _playEffect() {
+        this.animationExtras.indexOf(this.iBodyAnim.name) != -1 && action.Cheering.attachPair.forEach(t=>{
+            this._avatar.attachExtraProp(t.obj, t.bone, new Vector3(t.offset.x,t.offset.y,t.offset.z), new Vector3(t.rotate.x,t.rotate.y,t.rotate.z)),
+            this._avatar.showExtra(t.obj)
+        }
+        )
+    }
+    _playAnimation(e=0) {
+        var t, r;
+        return e == 0 && this.iBodyAnim && ((t = this.iBodyAnim) == null ? void 0 : t.animGroup) ? (this.onPlayObservable.notifyObservers(this._scene),
+        this.iBodyAnim.animGroup.start(this.loop, this.iBodyAnim.playSpeed, this.iBodyAnim.startFrame, this.iBodyAnim.endFrame, !1),
+        !0) : e == 1 && this.iFaceAnim && ((r = this.iFaceAnim) == null ? void 0 : r.animGroup) ? (this.iFaceAnim.animGroup.start(this.loop, this.iFaceAnim.playSpeed, this.iFaceAnim.startFrame, this.iFaceAnim.endFrame, !1),
+        !0) : !1
+    }
+    _postprocess(e) {
+        var r, n;
+        let t;
+        e == 0 ? t = (r = this.iBodyAnim) == null ? void 0 : r.name : e == 1 && (t = (n = this.iFaceAnim) == null ? void 0 : n.name),
+        t === action.Cheering.animName && this._avatar.disposeExtra()
+    }
+    _isAnimationValid(e) {
+        for (let t = 0; t < e.targetedAnimations.length; ++t)
+            if (e.targetedAnimations[t].target)
+                return !1;
+        return !0
+    }
+}

+ 408 - 0
XAvatar.js

@@ -0,0 +1,408 @@
+const log$E = new Logger$1("Avatar")
+  , castRayOffsetY = .01
+  , castRayTeleportationOffset = 10;
+class XAvatar {
+    constructor({id: e, avatarType: t, priority: r, avatarManager: n, assets: o, status: a}) {
+        E(this, "id", "-1");
+        E(this, "priority", 0);
+        E(this, "isRender", !1);
+        E(this, "distLevel", 0);
+        E(this, "isInLoadingList", !1);
+        E(this, "isHide", !1);
+        E(this, "component");
+        E(this, "controller");
+        E(this, "stateMachine");
+        E(this, "bbComponent");
+        E(this, "_avatarType");
+        E(this, "clothesList", []);
+        E(this, "isSelected", !1);
+        E(this, "pendingLod", !1);
+        E(this, "_previousReceivedPosition", new Vector3(0,1e4,0));
+        E(this, "_avatarPosition");
+        E(this, "_avatarRotation");
+        E(this, "_avatarScale");
+        E(this, "rootNode");
+        E(this, "distToCam", 1e11);
+        E(this, "enableNickname", !0);
+        E(this, "distance", 1e11);
+        E(this, "isCulling", !1);
+        E(this, "reslevel", 0);
+        E(this, "isInLoadingQueue", !1);
+        E(this, "_isRayCastEnable");
+        E(this, "_scene");
+        E(this, "_avatarManager");
+        E(this, "_transparent", 0);
+        E(this, "hide", ()=>(this.isHide = !0,
+        this._hide(),
+        !this.isRender));
+        E(this, "_show", ()=>{
+            var e;
+            this.isHide || (this.setIsPickable(!0),
+            this.priority == 0 && (this.rootNode.setEnabled(!0),
+            this.isRender = !0,
+            this.avatarManager._updateBillboardStatus(this, BillboardStatus.SHOW),
+            (e = this.controller) == null || e.playAnimation(this.controller.onPlay, this.controller.loop)))
+        }
+        );
+        E(this, "show", ()=>(this.isHide = !1,
+        this._show(),
+        !!this.isRender));
+        E(this, "setAnimations", e=>{
+            this.controller.animations = e
+        }
+        );
+        E(this, "attachToAvatar", (e,t=!1,r={
+            x: 0,
+            y: 0,
+            z: 0
+        },n=!1,o,a)=>this.bbComponent.attachToAvatar(this, e, t, r, n, o, a));
+        E(this, "detachFromAvatar", (e,t=!1)=>this.bbComponent.detachFromAvatar(this, e, t));
+        E(this, "getBbox", (e={})=>this.bbComponent.getBbox(this, e));
+        this.id = e,
+        this._avatarManager = n,
+        this._scene = this.avatarManager.scene,
+        this.clothesList = o,
+        this._avatarType = t,
+        this.priority = r || 0,
+        this.controller = new XAnimationController(this),
+        this.component = new XAvatarComopnent,
+        this.stateMachine = new XStateMachine(this._scene),
+        this.bbComponent = new XAvatarBillboardComponent(this._scene),
+        this.rootNode = new TransformNode(e,this._avatarManager.scene),
+        this._avatarScale = a.avatarScale == null ? 1 : a.avatarScale,
+        this._avatarRotation = a.avatarRotation == null ? {
+            pitch: 0,
+            yaw: 0,
+            roll: 0
+        } : a.avatarRotation,
+        this._avatarPosition = a.avatarPosition == null ? {
+            x: 0,
+            y: 0,
+            z: 0
+        } : a.avatarPosition,
+        this.setPosition(this._avatarPosition),
+        this.setRotation(this._avatarRotation),
+        this.setScale(this.scale),
+        this._isRayCastEnable = avatarSetting.isRayCastEnable,
+        this._scene.registerBeforeRender(()=>{
+            this.tick()
+        }
+        )
+    }
+    tick() {
+        this.cullingTick()
+    }
+    cullingTick() {
+        var e;
+        this.isCulling && ((e = this.rootNode) == null || e.getChildMeshes().forEach(t=>{
+            this.distToCam < 50 ? t.visibility = 0 : t.visibility = this._transparent
+        }
+        ))
+    }
+    setTransParentThresh(e) {
+        this._transparent = e
+    }
+    get isNameVisible() {
+        return this.bbComponent.isNameVisible
+    }
+    get isBubbleVisible() {
+        return this.bbComponent.isBubbleVisible
+    }
+    get isGiftButtonsVisible() {
+        return this.bbComponent.isGiftButtonsVisible
+    }
+    get words() {
+        return this.bbComponent.words
+    }
+    get nickName() {
+        return this.bbComponent.nickName
+    }
+    get giftButtons() {
+        return this.bbComponent.giftButtons
+    }
+    get bubble() {
+        return this.bbComponent.bubble
+    }
+    get nameBoard() {
+        return this.bbComponent.nameBoard
+    }
+    get avatarManager() {
+        return this._avatarManager
+    }
+    set withinVisibleRange(e) {
+        this.bbComponent.withinVisualRange = e
+    }
+    setNicknameStatus(e) {
+        return this.bbComponent.setNicknameStatus(e)
+    }
+    setBubbleStatus(e) {
+        return this.bbComponent.setBubbleStatus(e)
+    }
+    setButtonsStatus(e) {
+        return this.bbComponent.setBubbleStatus(e)
+    }
+    setGiftButtonsVisible(e) {
+        return this.bbComponent.setGiftButtonsVisible(e)
+    }
+    get avatarType() {
+        return this._avatarType
+    }
+    attachBody(e) {
+        return this.component.addBodyComp(this, e)
+    }
+    attachDecoration(e) {
+        return this.component.addClothesComp(this, e)
+    }
+    detachDecoration(e) {
+        return this.component.clearClothesComp(e)
+    }
+    detachDecorationAll() {
+        return this.component.clearAllClothesComps()
+    }
+    get skeleton() {
+        return this.component.skeleton
+    }
+    get position() {
+        return this._avatarPosition
+    }
+    get rotation() {
+        return this._avatarRotation
+    }
+    get scale() {
+        return this._avatarScale
+    }
+    _hide_culling() {
+        this.bbComponent.updateBillboardStatus(this, BillboardStatus.HIDE),
+        this.isCulling = !0
+    }
+    _show_culling() {
+        this.isCulling && (this.rootNode && this.rootNode.getChildMeshes().forEach(e=>{
+            e.visibility = 1
+        }
+        ),
+        this.bbComponent.updateBillboardStatus(this, BillboardStatus.SHOW),
+        this.isCulling = !1)
+    }
+    _hide() {
+        !this.isHide || (this.setIsPickable(!1),
+        this.priority == 0 ? (this.rootNode.setEnabled(!1),
+        this.isRender = !1,
+        this.bbComponent.updateBillboardStatus(this, BillboardStatus.HIDE)) : this.isRender && (this.avatarManager.currentLODUsers[this.distLevel]--,
+        this.removeAvatarFromScene()))
+    }
+    rotate(e, t, r) {
+        return this.stateMachine.roll(this, e, t, r)
+    }
+    set isRayCastEnable(e) {
+        this._isRayCastEnable = e
+    }
+    get isRayCastEnable() {
+        return this._isRayCastEnable
+    }
+    getAvatarId() {
+        return this.id
+    }
+    getAvaliableAnimations() {
+        const e = avatarLoader.avaliableAnimation.get(this.avatarType);
+        return e || []
+    }
+    setPosition(e, t=!1) {
+        if (this._avatarPosition = e,
+        this.rootNode) {
+            const r = ue4Position2Xverse(this._avatarPosition);
+            let n = !1;
+            this.avatarManager.getMainAvatar() && (this.id != this.avatarManager.getMainAvatar().id || (Math.abs(r.y - this._previousReceivedPosition.y) > castRayOffsetY && (n = !0),
+            r.subtract(this._previousReceivedPosition).length() > castRayTeleportationOffset && (n = !0))),
+            this._isRayCastEnable ? n || t ? this._castRay(e).then(o=>{
+                this.rootNode.position = r,
+                this.rootNode.position.y -= o
+            }
+            ).catch(o=>{
+                Promise.reject(o)
+            }
+            ) : (this.rootNode.position.x = r.x,
+            this.rootNode.position.z = r.z) : this.rootNode.position = r,
+            this._previousReceivedPosition = r.clone()
+        }
+        return Promise.resolve(e)
+    }
+    setRotation(e) {
+        if (this._avatarRotation = e,
+        this.rootNode) {
+            const t = {
+                pitch: e.pitch,
+                yaw: e.yaw + 180,
+                roll: e.roll
+            }
+              , r = ue4Rotation2Xverse(t);
+            this.rootNode.rotation = r
+        }
+    }
+    setAvatarVisible(e) {
+        this.rootNode && (this.rootNode.setEnabled(e),
+        this.rootNode.getChildMeshes().forEach(t=>{
+            t.setEnabled(e)
+        }
+        ))
+    }
+    setScale(e) {
+        this._avatarScale = e,
+        this.rootNode && (this.rootNode.scaling = new Vector3(e,e,e)),
+        this.bbComponent.bbox && this.getBbox()
+    }
+    _removeAvatarFromScene() {
+        var e, t;
+        this.isRender = !1,
+        (e = this.controller) == null || e.detachAnimation(),
+        this.component.dispose(this),
+        (t = this.avatarManager.sceneManager) == null || t.lightComponent.removeShadow(this)
+    }
+    removeAvatarFromScene() {
+        this._removeAvatarFromScene(),
+        this._disposeBillBoard()
+    }
+    _disposeBillBoard() {
+        this.bbComponent.disposeBillBoard(this)
+    }
+    addComponent(e, t, r, n) {
+        return this.component.changeClothesComp(this, e, t, r, n)
+    }
+    _castRay(e) {
+        return new Promise((t,r)=>{
+            var d;
+            const n = ue4Position2Xverse(e)
+              , o = new Vector3(0,-1,0)
+              , a = 1.5 * this.scale
+              , s = 100 * a
+              , l = a
+              , u = new Vector3(n.x,n.y + l,+n.z)
+              , c = new Ray(u,o,s)
+              , h = (d = this.avatarManager.sceneManager) == null ? void 0 : d.getGround(e);
+            if (!h || h.length <= 0)
+                return log$E.warn(`\u89D2\u8272 id= ${this.id} \u627E\u4E0D\u5230\u5730\u9762\uFF0C\u5F53\u524D\u9AD8\u5EA6\u4E3A\u4E0B\u53D1\u9AD8\u5EA6`),
+                t(0);
+            let f = c.intersectsMeshes(h);
+            if (f.length > 0)
+                return t(f[0].distance - l);
+            if (o.y = 1,
+            f = c.intersectsMeshes(h),
+            f.length > 0)
+                return t(-(f[0].distance - l))
+        }
+        )
+    }
+    setPickBoxScale(e) {
+        return this.bbComponent.setPickBoxScale(e)
+    }
+    setIsPickable(e) {
+        return this.bbComponent.setIsPickable(this, e)
+    }
+    createPickBoundingbox(e) {
+        return this.bbComponent.createPickBoundingbox(this, e)
+    }
+    scaleBbox(e) {
+        this.bbComponent.bbox && this.bbComponent.bbox.scale(e)
+    }
+    rotateTo(e, t, r) {
+        return this.stateMachine.rotateTo(this, e, t, r)
+    }
+    faceTo(e, t) {
+        return this.stateMachine.lookAt(this, e, t)
+    }
+    removeObserver() {
+        this.stateMachine.disposeObsever()
+    }
+    move(e, t, r, n, o) {
+        return this.stateMachine.moveTo(this, e, t, r, n, o)
+    }
+    initNameboard(e=1) {
+        return this.bbComponent.initNameboard(this, e)
+    }
+    initBubble(e=1) {
+        return this.bbComponent.initBubble(this, e)
+    }
+    say(e, {id: t, isUser: r, background: n, font: o="Arial", fontsize: a=38, fontcolor: s="#ffffff", fontstyle: l="bold", linesize: u=22, linelimit: c, offsets: h={
+        x: 0,
+        y: 0,
+        z: 40
+    }, scale: f=this._avatarScale, compensationZ: d=11.2, reregistAnyway: _=!0}) {
+        return this.bbComponent.say(this, e, {
+            id: t,
+            isUser: r,
+            background: n,
+            font: o,
+            fontsize: a,
+            fontcolor: s,
+            fontstyle: l,
+            linesize: u,
+            linelimit: c,
+            offsets: h,
+            scale: f,
+            compensationZ: d,
+            reregistAnyway: _
+        })
+    }
+    silent() {
+        return this.bbComponent.silent()
+    }
+    setNickName(e, {id: t, isUser: r, background: n, font: o="Arial", fontsize: a=40, fontcolor: s="#ffffff", fontstyle: l="bold", linesize: u=22, linelimit: c, offsets: h={
+        x: 0,
+        y: 0,
+        z: 15
+    }, scale: f=this._avatarScale, compensationZ: d=0, reregistAnyway: _=!1}) {
+        return this.bbComponent.setNickName(this, e, {
+            id: t,
+            isUser: r,
+            background: n,
+            font: o,
+            fontsize: a,
+            fontcolor: s,
+            fontstyle: l,
+            linesize: u,
+            linelimit: c,
+            offsets: h,
+            scale: f,
+            compensationZ: d,
+            reregistAnyway: _
+        })
+    }
+    generateButtons(e=null, t=this._avatarScale, r=85) {
+        return this.bbComponent.generateButtons(this, e, t, r)
+    }
+    clearButtons() {
+        return this.bbComponent.clearButtons()
+    }
+    attachExtraProp(e, t, r, n) {
+        return this.component.addDecoComp(this, e, t, r, n)
+    }
+    showExtra(e) {
+        return this.component.showExtra(e)
+    }
+    hideExtra(e) {
+        return this.component.hideExtra(e)
+    }
+    disposeExtra() {
+        return this.component.disposeExtra()
+    }
+    getSkeletonPositionByName(e) {
+        var t;
+        if (this.skeleton) {
+            const r = this.skeleton.bones.find(n=>n.name.replace("Clone of ", "") == e);
+            if (r && r.getTransformNode() && ((t = r.getTransformNode()) == null ? void 0 : t.position)) {
+                const n = r.getTransformNode().position;
+                return xversePosition2Ue4({
+                    x: n.x,
+                    y: n.y,
+                    z: n.z
+                })
+            }
+        }
+    }
+    shootTo(e, t, r=2, n=10, o={
+        x: 0,
+        y: 0,
+        z: 150
+    }) {
+        return this.stateMachine.sendObjectTo(this, e, t, r, n, o)
+    }
+}

+ 374 - 0
XAvatarBillboardComponent.js

@@ -0,0 +1,374 @@
+const log$F = new Logger$1("XAvatarBillboardComponent");
+class XAvatarBillboardComponent {
+    constructor(e) {
+        E(this, "_nickName", "");
+        E(this, "_words", "");
+        E(this, "_isNameVisible", !0);
+        E(this, "_isBubbleVisible", !0);
+        E(this, "_isGiftButtonsVisible", !1);
+        E(this, "withinVisualRange", !1);
+        E(this, "_bubble");
+        E(this, "_nameBoard");
+        E(this, "_giftButtons", new Map);
+        E(this, "_buttonTex", new Map);
+        E(this, "_nameLinesLimit", 2);
+        E(this, "_nameLengthPerLine", 16);
+        E(this, "_scene");
+        E(this, "_pickBbox", null);
+        E(this, "bbox");
+        E(this, "_height", .26);
+        E(this, "_attachmentObservers", new Map);
+        E(this, "attachToAvatar", (e,t,r=!1,n={
+            x: 0,
+            y: 0,
+            z: 0
+        },o=!1,a,s)=>{
+            const l = e.rootNode;
+            if (this.bbox || e.getBbox(),
+            t && l) {
+                const u = a || t.uniqueId;
+                let c = this._attachmentObservers.get(u);
+                if (c)
+                    if (o)
+                        this._scene.onBeforeRenderObservable.remove(c),
+                        this._attachmentObservers.delete(u);
+                    else
+                        return;
+                const h = ue4Position2Xverse(n);
+                r ? (t.setParent(l),
+                t.position = h) : (c = this._scene.onBeforeRenderObservable.add(()=>{
+                    let f = 0;
+                    s ? (f = e.rootNode.rotation.y / Math.PI * 180 + 90,
+                    e.rootNode.rotation.y && (t.rotation.y = e.rootNode.rotation.y)) : f = e.avatarManager.sceneManager.cameraComponent.getCameraPose().rotation.yaw,
+                    f || (f = 0);
+                    const d = new Vector3(0,this._height,0);
+                    e.controller && e.controller.activeAnimation() && e.controller.activeAnimation().animatables[0] && (this._height = d.y = (e.controller.activeAnimation().animatables[0].target.position.y * .01 - .66) * e.scale),
+                    d.y < .07 * e.scale && (d.y = 0),
+                    t.position.x = l.position.x + h.x * Math.sin(f * Math.PI / 180) + h.z * Math.cos(f * Math.PI / 180),
+                    t.position.z = l.position.z + h.x * Math.cos(f * Math.PI / 180) - h.z * Math.sin(f * Math.PI / 180),
+                    t.position.y = l.position.y + this.bbox.maximum.y + h.y + d.y
+                }
+                ),
+                this._attachmentObservers.set(u, c))
+            } else
+                log$F.error("avatar or attachment not found!")
+        }
+        );
+        E(this, "detachFromAvatar", (e,t,r=!1)=>{
+            const n = this._attachmentObservers.get(t.uniqueId);
+            n && this._scene.onBeforeRenderObservable.remove(n),
+            e.rootNode ? (t.setEnabled(!1),
+            t.parent = null,
+            r && t.dispose()) : log$F.error("avatar not found!")
+        }
+        );
+        E(this, "getBbox", (e,t={})=>{
+            const {isConst: r=!1, changeWithAvatar: n=!1} = t;
+            let {localCenter: o={
+                x: 0,
+                y: 0,
+                z: 75
+            }, width: a=1.32, height: s=1.5, depth: l=.44} = t;
+            if (n) {
+                const u = e.scale;
+                o = {
+                    x: o.x * u,
+                    y: o.y * u,
+                    z: o.z * u
+                },
+                a *= u,
+                s *= u,
+                l *= u
+            }
+            if (e.rootNode) {
+                let u = new Vector3(0,0,0)
+                  , c = new Vector3(0,0,0);
+                if (r) {
+                    const f = ue4Position2Xverse(o);
+                    u = u.add(f.add(new Vector3(-a / 2,-s / 2,-l / 2))),
+                    c = c.add(f.add(new Vector3(a / 2,s / 2,l / 2)))
+                } else if (u = u.add(new Vector3(Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY)),
+                c = c.add(new Vector3(Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY)),
+                e.isRender) {
+                    e.rootNode.getChildMeshes().forEach(_=>{
+                        const g = _.getBoundingInfo().boundingBox.minimum
+                          , m = _.getBoundingInfo().boundingBox.maximum;
+                        u.x = Math.min(u.x, g.x),
+                        c.x = Math.max(c.x, m.x),
+                        u.y = Math.min(u.y, g.y),
+                        c.y = Math.max(c.y, m.y),
+                        u.z = Math.min(u.z, g.z),
+                        c.z = Math.max(c.z, m.z)
+                    }
+                    );
+                    const f = c.x - u.x
+                      , d = c.z - u.z;
+                    u.x -= e.scale * f / 2,
+                    c.x += e.scale * f / 2,
+                    c.y *= e.scale,
+                    u.z -= e.scale * d / 2,
+                    c.z += e.scale * d / 2
+                } else {
+                    const f = e.avatarManager.getMainAvatar();
+                    f && f.bbComponent.bbox && (u.x = f.bbComponent.bbox.minimum.x,
+                    c.x = f.bbComponent.bbox.maximum.x,
+                    u.y = f.bbComponent.bbox.minimum.y,
+                    c.y = f.bbComponent.bbox.maximum.y,
+                    u.z = f.bbComponent.bbox.minimum.z,
+                    c.z = f.bbComponent.bbox.maximum.z)
+                }
+                const h = e.rootNode.computeWorldMatrix(!0);
+                if (this.bbox ? this.bbox.reConstruct(u, c, h) : this.bbox = new BoundingBox(u,c,h),
+                this._pickBbox == null) {
+                    const f = this.createPickBoundingbox(e, this.bbox);
+                    this.attachToAvatar(e, f.data, !1, {
+                        x: 0,
+                        y: 0,
+                        z: 0
+                    }, !1, "pickbox"),
+                    this._pickBbox = f
+                }
+            } else
+                log$F.error("avatar not found!")
+        }
+        );
+        this._scene = e
+    }
+    get isNameVisible() {
+        return this._isNameVisible
+    }
+    get isBubbleVisible() {
+        return this._isBubbleVisible
+    }
+    get isGiftButtonsVisible() {
+        return this._isGiftButtonsVisible
+    }
+    get words() {
+        return this._words
+    }
+    get nickName() {
+        return this._nickName
+    }
+    get giftButtons() {
+        return this._giftButtons
+    }
+    get bubble() {
+        return this._bubble
+    }
+    get nameBoard() {
+        return this._nameBoard
+    }
+    setNicknameStatus(e) {
+        if (this.nameBoard && this.nameBoard.setStatus(e),
+        e == BillboardStatus.DISPOSE) {
+            const t = this._attachmentObservers.get("nickname");
+            t && (this._scene.onBeforeRenderObservable.remove(t),
+            this._attachmentObservers.delete("nickname"))
+        }
+    }
+    setBubbleStatus(e) {
+        if (this.bubble && this.bubble.setStatus(e),
+        e == BillboardStatus.DISPOSE) {
+            const t = this._attachmentObservers.get("bubble");
+            t && (this._scene.onBeforeRenderObservable.remove(t),
+            this._attachmentObservers.delete("bubble"))
+        }
+    }
+    setButtonsStatus(e) {
+        this.giftButtons && this.giftButtons.size != 0 && this.giftButtons.forEach(t=>{
+            if (t.setStatus(e),
+            e == BillboardStatus.DISPOSE && t.getMesh()) {
+                const r = "button_" + t.getMesh().xid
+                  , n = this._attachmentObservers.get(r);
+                n && (this._scene.onBeforeRenderObservable.remove(n),
+                this._attachmentObservers.delete(r))
+            }
+        }
+        )
+    }
+    setGiftButtonsVisible(e) {
+        this.setButtonsStatus(e ? BillboardStatus.SHOW : BillboardStatus.DISPOSE)
+    }
+    dispose(e) {
+        this._attachmentObservers.forEach(t=>{
+            this._scene.onBeforeRenderObservable.remove(t)
+        }
+        ),
+        this._attachmentObservers.clear(),
+        this.updateBillboardStatus(e, BillboardStatus.DISPOSE),
+        this._buttonTex.clear(),
+        this._pickBbox && (e.avatarManager.bboxMeshPool.release(this._pickBbox),
+        this._pickBbox = null)
+    }
+    updateBillboardStatus(e, t) {
+        this.bbox || e.getBbox(),
+        e.isRender ? (e.setBubbleStatus(t),
+        e.setButtonsStatus(t),
+        e.setNicknameStatus(t)) : (e.setBubbleStatus(BillboardStatus.DISPOSE),
+        e.setButtonsStatus(BillboardStatus.DISPOSE),
+        e.enableNickname ? e.setNicknameStatus(t) : e.setNicknameStatus(BillboardStatus.DISPOSE))
+    }
+    disposeBillBoard(e) {
+        this._attachmentObservers.forEach(t=>{
+            this._scene.onBeforeRenderObservable.remove(t)
+        }
+        ),
+        this._attachmentObservers.clear(),
+        this.updateBillboardStatus(e, BillboardStatus.DISPOSE),
+        this._buttonTex.clear(),
+        this._pickBbox && (e.avatarManager.bboxMeshPool.release(this._pickBbox),
+        this._pickBbox = null)
+    }
+    setPickBoxScale(e) {
+        this._pickBbox && this._pickBbox.data && (this._pickBbox.data.scaling = new Vector3(e,e,e))
+    }
+    setIsPickable(e, t) {
+        e.rootNode && e.rootNode.getChildMeshes().forEach(r=>{
+            r.isPickable = t
+        }
+        ),
+        this._pickBbox && this._pickBbox.data && (this._pickBbox.data.isPickable = t)
+    }
+    initNameboard(e, t=1) {
+        this._nameBoard == null && (this._nameBoard = e.avatarManager.sceneManager.billboardComponent.addBillboard("name-" + e.id, !1, !0)),
+        this._nameBoard.init("nickname", t / 300, t / 300)
+    }
+    initBubble(e, t=1) {
+        this._bubble == null && (this._bubble = e.avatarManager.sceneManager.billboardComponent.addBillboard("bubble-" + e.id, !1, !0)),
+        e.isRender && this._bubble.init("bubble", t / 250, t / 250)
+    }
+    say(e, t=this._words, {id: r, isUser: n, background: o, font: a="Arial", fontsize: s=38, fontcolor: l="#ffffff", fontstyle: u="bold", linesize: c=22, linelimit: h, offsets: f={
+        x: 0,
+        y: 0,
+        z: 40
+    }, scale: d, compensationZ: _=11.2, reregistAnyway: g=!0}) {
+        (!this.bubble || this.bubble.getMesh() == null) && e.initBubble(),
+        this._words = t;
+        let m;
+        n != null && (m = n ? XBillboardManager.userBubbleUrls : XBillboardManager.npcBubbleUrls),
+        this._bubble && (this._bubble.DEFAULT_CONFIGS = {
+            id: r,
+            isUser: n,
+            background: o || m,
+            font: a,
+            fontsize: s,
+            fontcolor: l,
+            fontstyle: u,
+            linesize: c,
+            linelimit: h,
+            offsets: f,
+            scale: d,
+            compensationZ: _,
+            reregistAnyway: g
+        },
+        this._bubble.getMesh() && (this._bubble.drawBillboard({
+            imageList: o || m
+        }, {
+            texts: this._words,
+            font: a,
+            fontsize: s,
+            fontcolor: l,
+            fontstyle: u,
+            linesize: c
+        }, {
+            offsets: f,
+            scale: d,
+            compensationZ: _
+        }),
+        this.attachToAvatar(e, this._bubble.getMesh(), !1, this._bubble.offsets, g, "bubble"),
+        r && this._bubble.setId(r))),
+        this.setButtonsStatus(BillboardStatus.DISPOSE)
+    }
+    silent() {
+        this.setBubbleStatus(BillboardStatus.DISPOSE),
+        this._words = ""
+    }
+    setNickName(e, t, {id: r, isUser: n, background: o, font: a="Arial", fontsize: s=40, fontcolor: l="#ffffff", fontstyle: u="bold", linesize: c=22, linelimit: h, offsets: f={
+        x: 0,
+        y: 0,
+        z: 15
+    }, scale: d, compensationZ: _=0, reregistAnyway: g=!1}) {
+        this._nickName = t,
+        (!this.nameBoard || this.nameBoard.getMesh() == null) && this.initNameboard(e),
+        this._nameBoard && this._nameBoard.getMesh() && (this._nameBoard.DEFAULT_CONFIGS = {
+            id: r,
+            isUser: n,
+            background: o,
+            font: a,
+            fontsize: s,
+            fontcolor: l,
+            fontstyle: u,
+            linesize: c,
+            linelimit: h,
+            offsets: f,
+            scale: d,
+            compensationZ: _,
+            reregistAnyway: g
+        },
+        this._nameBoard.drawBillboard({}, {
+            texts: this._nickName,
+            font: a,
+            fontsize: s,
+            fontcolor: l,
+            fontstyle: u,
+            linesize: c,
+            linelimit: h
+        }, {
+            offsets: f,
+            scale: d,
+            compensationZ: 0
+        }),
+        this.attachToAvatar(e, this._nameBoard.getMesh(), !1, this._nameBoard.offsets, g, "nickname"),
+        r && this._nameBoard.setId(r))
+    }
+    generateButtons(e, t=null, r, n=85) {
+        if (t && (this._buttonTex = t,
+        this.clearButtons()),
+        this._buttonTex.size == 0)
+            return;
+        let o = (this._buttonTex.size - 1) / 2;
+        this._buttonTex.forEach((a,s)=>{
+            let l = this._giftButtons.get(s);
+            l || (l = e.avatarManager.sceneManager.billboardComponent.addBillboard("button-" + s + e.id, !0, !1),
+            l.init(s, r / 240, r / 240));
+            const u = {
+                x: r * o * 70,
+                y: 0,
+                z: r * (n - 20 * (o * o))
+            };
+            l.drawBillboard({
+                imageList: [a]
+            }, {}, {
+                offsets: u,
+                scale: r
+            }),
+            this.attachToAvatar(e, l.getMesh(), !1, l.offsets, !0, "button_" + s),
+            this._giftButtons.set(s, l),
+            o -= 1
+        }
+        ),
+        this.setBubbleStatus(BillboardStatus.DISPOSE)
+    }
+    clearButtons() {
+        this._giftButtons.forEach(e=>{
+            e.dispose()
+        }
+        ),
+        this._giftButtons.clear()
+    }
+    createPickBoundingbox(e, t) {
+        const r = t.extendSize.x * 2
+          , n = t.extendSize.y * 2
+          , o = t.extendSize.z * 2
+          , a = this._scene
+          , s = Math.max(r, o)
+          , l = e.avatarManager.bboxMeshPool.getFree(a, s, n, s)
+          , u = l.data;
+        return u && (u.position = t.centerWorld,
+        u.setEnabled(!1),
+        u.isPickable = !0,
+        u.xtype = EMeshType.XAvatar,
+        u.xid = e.id),
+        l
+    }
+}

+ 169 - 0
XAvatarComopnent.js

@@ -0,0 +1,169 @@
+const log$H = new Logger$1("XAvatarComopnent");
+class XAvatarComopnent {
+    constructor() {
+        E(this, "resourceIdList", []);
+        E(this, "skeleton");
+        E(this, "extraProp");
+        E(this, "extras", []);
+        E(this, "body")
+    }
+    addBodyComp(e, t) {
+        return !e.rootNode || t.root.getChildMeshes().length === 0 ? (t.isRender = !1,
+        !1) : (this.body = t,
+        this.body.root.parent = e.rootNode,
+        t.isRender = !0,
+        this.body.root.getChildMeshes()[0] && (this.body.root.getChildMeshes()[0].xtype = EMeshType.XAvatar,
+        this.body.root.getChildMeshes()[0].xid = e.id),
+        this.skeleton = t.skeleton,
+        !0)
+    }
+    addClothesComp(e, t) {
+        return !e.rootNode || !this.skeleton || !t.root ? (t.isRender = !1,
+        !1) : (t.root.xtype = EMeshType.XAvatar,
+        t.root.xid = e.id,
+        t.isRender = !0,
+        t.root.parent = e.rootNode.getChildMeshes()[0],
+        this.resourceIdList.push(t),
+        t.root.skeleton = this.skeleton,
+        t.root.getChildMeshes().forEach(r=>{
+            r.skeleton = this.skeleton
+        }
+        ),
+        !0)
+    }
+    clearClothesComp(e) {
+        e.root.getChildMeshes().forEach(t=>{
+            t.skeleton = null,
+            t.dispose(),
+            t.xid = void 0
+        }
+        ),
+        e.root.dispose(),
+        this.resourceIdList = this.resourceIdList.filter(t=>t.uId != e.uId)
+    }
+    clearAllClothesComps() {
+        this.resourceIdList.forEach(e=>{
+            var t;
+            e.root.parent = null,
+            e.root._parentContainer.xReferenceCount && (e.root._parentContainer.xReferenceCount--,
+            e.root._parentContainer = null),
+            e.isRender = !1,
+            e.isSelected = !1,
+            e.root.getChildMeshes().forEach(r=>{
+                r.skeleton = null,
+                r.dispose()
+            }
+            ),
+            (t = e.root.skeleton) == null || t.dispose(),
+            e.root.dispose()
+        }
+        ),
+        this.resourceIdList = []
+    }
+    dispose(e) {
+        this.body ? (this.body.root._parentContainer.xReferenceCount && (this.body.root._parentContainer.xReferenceCount--,
+        this.body.root._parentContainer = null),
+        this.clearAllClothesComps(),
+        this.body.isRender = !1,
+        this.body.skeleton.dispose(),
+        this.body.skeleton = null,
+        this.body.root.dispose(),
+        this.body = void 0,
+        this.skeleton && (this.skeleton.dispose(),
+        this.skeleton = void 0)) : log$H.warn("[Engine] no body to dispose")
+    }
+    changeClothesComp(e, t, r, n, o) {
+        return new Promise(a=>{
+            if (r === "pendant" || this.resourceIdList.some(s=>s.name === t))
+                return a();
+            if (e.isHide || !e.isRender)
+                o.concat(r).forEach(l=>{
+                    e.clothesList = e.clothesList.filter(c=>c.type != l);
+                    const u = {
+                        type: r,
+                        id: t,
+                        url: n,
+                        lod: 0
+                    };
+                    e.clothesList.push(u)
+                }
+                ),
+                a();
+            else {
+                const s = o.concat(r);
+                e.avatarManager.loadDecoration(r, t, 0).then(l=>{
+                    if (l) {
+                        e.attachDecoration(l);
+                        const u = {
+                            type: r,
+                            id: t,
+                            url: n
+                        };
+                        e.clothesList.push(u),
+                        l.root.setEnabled(!0),
+                        s.forEach(c=>{
+                            const h = this.resourceIdList.filter(f=>f.type === c);
+                            if (h.length > 1) {
+                                const f = h.filter(d=>d.name === t);
+                                if (f.length > 1)
+                                    for (let d = 1; d < f.length; ++d) {
+                                        e.detachDecoration(f[d]),
+                                        e.clothesList = e.clothesList.filter(g=>g.id != f[d].name);
+                                        const _ = {
+                                            type: r,
+                                            id: t,
+                                            url: n
+                                        };
+                                        e.clothesList.push(_)
+                                    }
+                            }
+                            h[0] && h[0].name != t && this._readyToDetach(e, r) && (e.detachDecoration(h[0]),
+                            e.clothesList = e.clothesList.filter(f=>f.id != h[0].name))
+                        }
+                        )
+                    }
+                    return a()
+                }
+                )
+            }
+        }
+        )
+    }
+    _readyToDetach(e, t) {
+        return !((t == "clothes" || t == "pants") && e.clothesList.filter(n=>n.type === "suit").length == 1 && (!e.clothesList.some(n=>n.type === "pants") || !e.clothesList.some(n=>n.type === "clothes")))
+    }
+    addDecoComp(e, t, r, n, o) {
+        if (e.isRender) {
+            const a = e.avatarManager.extraComps.get(t)
+              , s = a == null ? void 0 : a.clone(t, void 0);
+            if (!a) {
+                log$H.error("\u6CA1\u6709\u5BF9\u5E94\u7684\u7EC4\u4EF6");
+                return
+            }
+            this.extras.push(s);
+            const l = this.skeleton.bones.find(u=>u.name === r);
+            s.position = n,
+            s.rotation = o,
+            s.attachToBone(l, e.rootNode.getChildMeshes()[0])
+        }
+    }
+    showExtra(e) {
+        this.extras.forEach(t=>{
+            t.name.indexOf(e) > 0 && t.setEnabled(!0)
+        }
+        )
+    }
+    hideExtra(e) {
+        this.extras.forEach(t=>{
+            t.name.indexOf(e) > 0 && t.setEnabled(!1)
+        }
+        )
+    }
+    disposeExtra() {
+        this.extras.forEach(e=>{
+            e.dispose()
+        }
+        ),
+        this.extras = []
+    }
+}

+ 319 - 0
XAvatarLoader.js

@@ -0,0 +1,319 @@
+class XAvatarLoader {
+    constructor() {
+        E(this, "containers", new Map);
+        E(this, "meshes", new Map);
+        E(this, "animations", new Map);
+        E(this, "aniPath", new Map);
+        E(this, "binPath", new Map);
+        E(this, "texPath", new Map);
+        E(this, "matPath", new Map);
+        E(this, "mshPath", new Map);
+        E(this, "rootPath", new Map);
+        E(this, "meshTexList", new Map);
+        E(this, "_enableIdb", !0);
+        E(this, "_mappings", new Map);
+        E(this, "_sharedTex", new Map);
+        E(this, "avaliableAnimation", new Map);
+        E(this, "enableShareTexture", !0);
+        E(this, "enableShareAnimation", !0);
+        E(this, "fillEmptyLod", !0);
+        const e = new GLTFFileLoader;
+        SceneLoader.RegisterPlugin(e),
+        e.preprocessUrlAsync = function(t) {
+            const r = avatarLoader._mappings.get(t);
+            return r ? Promise.resolve(r) : Promise.resolve(t)
+        }
+    }
+    getParsedUrl(e, t, r, n="") {
+        return new Promise((o,a)=>{
+            if (!r || r.indexOf(".zip") === -1)
+                return o(r);
+            const s = this.rootPath.get(r);
+            if (s)
+                return o(s);
+            {
+                const l = ".zip"
+                  , u = r.replace(l, "") + COMPONENT_LIST_PREFIX;
+                e.urlTransformer(u, !0).then(c=>{
+                    if (!c)
+                        return a("Loading Failed");
+                    new Response(c).json().then(h=>{
+                        var _, g, m, v, y, b, T;
+                        const f = r.replace(l, "")
+                          , d = f + ((_ = h == null ? void 0 : h.components) == null ? void 0 : _.url.replace("./", ""));
+                        if (this.rootPath.set(r, d),
+                        h.components ? (h.components.url && this.mshPath.set(t, f + "/" + ((g = h == null ? void 0 : h.components) == null ? void 0 : g.url.replace("./", ""))),
+                        h.components.url_lod2 && this.mshPath.set(t + "_" + avatarSetting.lod[1].level, f + "/" + ((m = h == null ? void 0 : h.components) == null ? void 0 : m.url_lod2.replace("./", ""))),
+                        h.components.url_lod4 && this.mshPath.set(t + "_" + avatarSetting.lod[2].level, f + "/" + ((v = h == null ? void 0 : h.components) == null ? void 0 : v.url_lod4.replace("./", "")))) : (h.meshes.url && this.mshPath.set(t, f + "/" + ((y = h == null ? void 0 : h.meshes) == null ? void 0 : y.url.replace("./", ""))),
+                        h.meshes.url_lod2 && this.mshPath.set(t + "_" + avatarSetting.lod[1].level, f + "/" + ((b = h == null ? void 0 : h.meshes) == null ? void 0 : b.url_lod2.replace("./", ""))),
+                        h.meshes.url_lod4 && this.mshPath.set(t + "_" + avatarSetting.lod[2].level, f + "/" + ((T = h == null ? void 0 : h.meshes) == null ? void 0 : T.url_lod4.replace("./", "")))),
+                        h.materials && h.materials.forEach(C=>{
+                            const A = f + "/" + C.url;
+                            this.matPath.set(C.name, A)
+                        }
+                        ),
+                        h.bin) {
+                            const C = f + "/" + h.bin.url;
+                            this.binPath.set(t, C);
+                            const A = f + "/" + h.bin.url_lod2;
+                            this.binPath.set(t + "_" + avatarSetting.lod[1].level, A);
+                            const S = f + "/" + h.bin.url_lod4;
+                            this.binPath.set(t + "_" + avatarSetting.lod[2].level, S)
+                        }
+                        return h.textures && h.textures.forEach(C=>{
+                            const A = f + "/" + C.url;
+                            this.texPath.set(C.url, A);
+                            const S = this.meshTexList.get(h.components.url);
+                            C.type === "png" && (S ? S.find(P=>P === C.name) || S.push(C.url) : this.meshTexList.set(t, [C.name]))
+                        }
+                        ),
+                        o(d)
+                    }
+                    ).catch(h=>{
+                        log$K.error(`[Engine] parse json file error,${h}`)
+                    }
+                    )
+                }
+                ).catch(c=>{
+                    log$K.error(`[Engine] ulrtransform error, cannot find resource in db,${c}`)
+                }
+                )
+            }
+        }
+        )
+    }
+    async parse(e, t) {
+        const r = [];
+        t.forEach(n=>{
+            this._setAnimationList(n.id, n.animations),
+            r.push(this.getParsedUrl(e, n.id, n.url)),
+            n.components.forEach(o=>{
+                o.units.forEach(a=>{
+                    r.push(this.getParsedUrl(e, a.name, a.url))
+                }
+                )
+            }
+            )
+        }
+        ),
+        await Promise.all(r)
+    }
+    _setAnimationList(e, t) {
+        t ? t.forEach(r=>{
+            this.aniPath.set(e + "_" + r.name, r.url)
+        }
+        ) : log$K.error("[Engine] no animation list exist, please check config for details")
+    }
+    disposeContainer() {
+        this.containers.forEach((e,t)=>{
+            e.xReferenceCount < 1 && (this.enableShareTexture && e.textures.length > 0 && e.textures[0].xReferenceCount != null && (e.textures[0].xReferenceCount--,
+            e.textures = []),
+            e.dispose(),
+            this.containers.delete(t))
+        }
+        ),
+        this._sharedTex.forEach((e,t)=>{
+            e.xReferenceCount == 0 && (e.dispose(),
+            this._sharedTex.delete(t))
+        }
+        )
+    }
+    set enableIdb(e) {
+        this._enableIdb = e
+    }
+    getGlbPath(e) {
+        return this.aniPath.get(e + ".glb")
+    }
+    getGltfPath(e) {
+        return this.mshPath.get(e + ".gltf")
+    }
+    getPngUrl(e) {
+        return this.texPath.get(e + ".png")
+    }
+    getMeshUrl(e) {
+        return this.mshPath.get(e)
+    }
+    _getSourceKey(e, t) {
+        return t && avatarSetting.lod[t] ? e + avatarSetting.lod[t].fileName.split(".")[0] : e
+    }
+    _getAnimPath(e, t) {
+        let r = this.aniPath.get(t + "_animations_" + t.split("_")[1]);
+        return r || (r = this.aniPath.get(t + "_" + e)),
+        r
+    }
+    load(e, t, r, n) {
+        return new Promise((o,a)=>{
+            this.loadGlb(e, t, r).then(s=>s ? o(s) : a("[Engine] container load failed")).catch(()=>a("[Engine] container load failed"))
+        }
+        )
+    }
+    _searchAnimation(e, t) {
+        let r;
+        return this.containers.forEach((n,o)=>{
+            const a = t.split("_")[0];
+            o.indexOf(a) != -1 && o.indexOf(e) != -1 && (r = n)
+        }
+        ),
+        r
+    }
+    loadAnimRes(e, t, r) {
+        return new Promise((n,o)=>{
+            const a = this._getAnimPath(t, r)
+              , s = getAnimationKey(t, r);
+            if (a && this.containers.get(a))
+                return n(this.containers.get(a));
+            if (a)
+                this._loadGlbFromBlob(e, s, a).then(l=>l.animationGroups.length == 0 ? (this.containers.delete(s),
+                l.dispose(),
+                Promise.reject("container does not contains animation data")) : n(l));
+            else
+                return o("no such url")
+        }
+        )
+    }
+    loadGlb(e, t, r) {
+        let n = this.getMeshUrl(this._getSourceKey(t, r));
+        return !n && this.fillEmptyLod && (r = 0,
+        n = this.getMeshUrl(this._getSourceKey(t, r))),
+        n && this.containers.get(n) ? Promise.resolve(this.containers.get(n)) : n ? this._enableIdb ? Promise.resolve(this._loadGlbFromBlob(e, this._getSourceKey(t, r), n)) : Promise.resolve(this._loadGlbFromUrl(e, this._getSourceKey(t, r), n)) : Promise.reject("no such url")
+    }
+    loadGltf(e, t, r, n) {
+        const o = this._getSourceKey(t, r || 0);
+        let a = this.getGltfPath(o);
+        return !a && this.fillEmptyLod && (a = this.getGltfPath(t)),
+        a && this.containers.get(a) ? Promise.resolve(this.containers.get(a)) : this._enableIdb ? this._loadGltfFromBlob(e, t, r, n) : a ? this._loadGltfFromUrl(e, t, a.replace(t + ".gltf", "")) : Promise.reject()
+    }
+    loadSubsequence() {}
+    loadVAT() {}
+    getResourceName(e) {
+        return this.meshTexList.get(e)
+    }
+    _loadGltfFromUrl(e, t, r) {
+        return SceneLoader.LoadAssetContainerAsync(r, t + ".gltf", e.Scene, null, ".gltf")
+    }
+    _loadGlbFromBlob(e, t, r) {
+        return new Promise((n,o)=>{
+            e.urlTransformer(r).then(a=>{
+                SceneLoader.LoadAssetContainerAsync("", a, e.Scene, null, ".glb").then(s=>{
+                    if (s) {
+                        if (this.enableShareTexture && s.textures.length > 0) {
+                            const l = t.indexOf("_lod") != -1 ? t.slice(0, -5) : t
+                              , u = this._sharedTex.get(l);
+                            u ? (s.meshes[1].material._albedoTexture = u,
+                            s.textures.forEach(c=>{
+                                c.dispose()
+                            }
+                            ),
+                            s.textures = [u],
+                            u.xReferenceCount++) : (this._sharedTex.set(l, s.textures[0]),
+                            s.textures[0].xReferenceCount = 1)
+                        }
+                        return s.addAllToScene(),
+                        s.xReferenceCount = 0,
+                        s.meshes.forEach(l=>{
+                            l.setEnabled(!1)
+                        }
+                        ),
+                        this.containers.set(r, s),
+                        n(s)
+                    } else
+                        o("glb file load failed")
+                }
+                )
+            }
+            )
+        }
+        )
+    }
+    _loadGlbFromUrl(e, t, r) {
+        return new Promise((n,o)=>{
+            SceneLoader.LoadAssetContainerAsync("", r, e.Scene, null, ".glb").then(a=>{
+                if (a) {
+                    if (a.addAllToScene(),
+                    a.meshes.forEach(s=>{
+                        s.setEnabled(!1)
+                    }
+                    ),
+                    this.enableShareTexture && a.textures.length > 0) {
+                        const s = t.indexOf("_lod") != -1 ? t.slice(0, -5) : t
+                          , l = this._sharedTex.get(s);
+                        l ? (a.meshes[1].material._albedoTexture = l,
+                        a.textures.forEach(u=>{
+                            u.dispose()
+                        }
+                        ),
+                        a.textures = [l],
+                        l.xReferenceCount++) : (this._sharedTex.set(s, a.textures[0]),
+                        a.textures[0].xReferenceCount = 1)
+                    }
+                    return a.xReferenceCount = 0,
+                    this.containers.set(r, a),
+                    n(a)
+                } else
+                    o("glb file load failed")
+            }
+            )
+        }
+        )
+    }
+    _loadGltfFromBlob(e, t, r, n) {
+        return new Promise((o,a)=>{
+            const s = [];
+            let l = this._getSourceKey(t, r)
+              , u = this.getGltfPath(l);
+            if (!u && this.fillEmptyLod && (r = 0,
+            l = this._getSourceKey(t, r),
+            u = this.getGltfPath(l)),
+            !u)
+                return a(`[Engine] gltf path incorrect ${l},cancel.`);
+            const c = this.mshPath.get(l + ".gltf");
+            if (!c)
+                return a("cannot find asset mshPath");
+            const h = this.binPath.get(l + ".bin");
+            if (!h)
+                return a("cannot find asset binPath");
+            if (!n) {
+                const _ = this.meshTexList.get(t);
+                if (!_ || _.length == 0)
+                    return a("cannot find texture");
+                n = _[0]
+            }
+            const f = this.texPath.get(n + ".png");
+            if (!f)
+                return a();
+            const d = this.texPath.get(n + "-astc.ktx");
+            if (!d)
+                return a();
+            s.push(this._blobMapping(e, c)),
+            s.push(this._blobMapping(e, h)),
+            s.push(this._blobMapping(e, f)),
+            s.push(this._blobMapping(e, d)),
+            Promise.all(s).then(()=>{
+                const _ = u.replace(l + ".gltf", "");
+                SceneLoader.LoadAssetContainerAsync(_, l + ".gltf", e.Scene, null, ".gltf").then(g=>{
+                    var v;
+                    this.containers.set(u, g),
+                    g.addAllToScene(),
+                    g.meshes.forEach(y=>{
+                        y.setEnabled(!1)
+                    }
+                    );
+                    const m = this._sharedTex.get(t);
+                    m ? ((v = g.meshes[1].material._albedoTexture) == null || v.dispose(),
+                    g.meshes[1].material._albedoTexture = m) : this._sharedTex.set(t, g.meshes[1].material._albedoTexture),
+                    o(g)
+                }
+                )
+            }
+            )
+        }
+        )
+    }
+    _blobMapping(e, t) {
+        return new Promise((r,n)=>{
+            e.urlTransformer(t).then(o=>o ? (this._mappings.set(t, o),
+            r(t)) : n(`[Engine] url urlTransformer parse error ${t}`))
+        }
+        )
+    }
+}

+ 751 - 0
XAvatarManager.js

@@ -0,0 +1,751 @@
+const log$D = new Logger$1("AvatarManager");
+var EAvatarRelationRank = (i=>(i[i.Self = 0] = "Self",
+i[i.Npc = 1] = "Npc",
+i[i.Friend = 2] = "Friend",
+i[i.Stranger = 3] = "Stranger",
+i[i.Robot = 4] = "Robot",
+i[i.Unknown = 5] = "Unknown",
+i))(EAvatarRelationRank || {});
+class XAvatarManager {
+    constructor(e) {
+        E(this, "characterMap", new Map);
+        E(this, "curAnimList", []);
+        E(this, "extraComps", new Map);
+        E(this, "_mainUser");
+        E(this, "_scene");
+        E(this, "_sceneManager");
+        E(this, "_lodSettings");
+        E(this, "maxBillBoardDist", 0);
+        E(this, "maxAvatarNum", 0);
+        E(this, "currentLODUsers", []);
+        E(this, "bboxMeshPool");
+        E(this, "_distLevels", []);
+        E(this, "_maxLODUsers", []);
+        E(this, "_cullingDistance", 0);
+        E(this, "_maxDistRange");
+        E(this, "_delayTime", 100);
+        E(this, "_queueLength", -1);
+        E(this, "_queue", []);
+        E(this, "_processList", []);
+        E(this, "_process");
+        E(this, "_updateLoopObserver");
+        E(this, "_avatarInDistance", []);
+        E(this, "_renderedAvatar", []);
+        E(this, "_enableNickname", !0);
+        E(this, "_tickObserver");
+        E(this, "_tickInterval");
+        E(this, "_defaultAnims");
+        E(this, "_tickDispose", 0);
+        E(this, "_disposeTime", 100);
+        E(this, "avatarLoader", avatarLoader);
+        this._scene = e.mainScene,
+        this._sceneManager = e,
+        this.initAvatarMap(),
+        this._initSettings(),
+        this._maxDistRange = this._distLevels[this._distLevels.length - 1],
+        this.bboxMeshPool = new Pool(this.createBboxAsset,this.resetBboxAsset,0,this.maxAvatarNum,this._sceneManager.Scene,0,0,0),
+        this._tickInterval = 250;
+        let t = 0;
+        this._tickObserver = this._scene.onAfterRenderObservable.add(()=>{
+            t += 1,
+            t == this._tickInterval && (this.tick(),
+            t = 0)
+        }
+        )
+    }
+    tick() {
+        this.bboxMeshPool.clean(0)
+    }
+    createBboxAsset(e, t, r, n) {
+        return MeshBuilder.CreateBox("avatarBbox", {
+            width: t,
+            height: r,
+            depth: n
+        }, e)
+    }
+    resetBboxAsset(e) {
+        const t = e.data;
+        return t.setEnabled(!1),
+        t.isPickable = !1,
+        e
+    }
+    _initSettings() {
+        this._defaultAnims = avatarSetting.defaultIdle,
+        this._lodSettings = avatarSetting.lod,
+        this._distLevels = avatarSetting.lod.map(e=>e.dist),
+        this._maxLODUsers = avatarSetting.lod.map(e=>e.quota),
+        this.currentLODUsers = new Array(this._distLevels.length).fill(0),
+        this.maxAvatarNum = avatarSetting.maxAvatarNum,
+        this.maxBillBoardDist = avatarSetting.maxBillBoardDist,
+        this._cullingDistance = avatarSetting.cullingDistance
+    }
+    maxRenderNum() {
+        let e = 0;
+        return this._maxLODUsers.forEach(t=>{
+            e += t
+        }
+        ),
+        e
+    }
+    curRenderNum() {
+        let e = 0;
+        return this.currentLODUsers.forEach(t=>{
+            e += t
+        }
+        ),
+        e
+    }
+    setLoDLevels(e) {
+        this._distLevels = e
+    }
+    set cullingDistance(e) {
+        this._cullingDistance = e
+    }
+    get cullingDistance() {
+        return this._cullingDistance
+    }
+    getLoDLevels() {
+        return this._distLevels
+    }
+    setLodUserLimits(e, t) {
+        this._maxLODUsers.length > e && (this._maxLODUsers[e] = t)
+    }
+    setLodDist(e, t) {
+        this._distLevels[e] = t
+    }
+    setMaxDistRange(e) {
+        this._maxDistRange = e,
+        this._distLevels[this._distLevels.length - 1] = e
+    }
+    get scene() {
+        return this._scene
+    }
+    setMainAvatar(e) {
+        var t;
+        this._mainUser = (t = this.characterMap.get(0)) == null ? void 0 : t.get(e)
+    }
+    getMainAvatar() {
+        return this._mainUser
+    }
+    enableAllNickname(e) {
+        return this.characterMap.forEach((t,r)=>{
+            r != 0 && t.forEach((n,o)=>{
+                this._updateBillboardStatus(n, e ? BillboardStatus.SHOW : BillboardStatus.HIDE)
+            }
+            )
+        }
+        ),
+        this._enableNickname = e
+    }
+    getAvatarById(e) {
+        let t;
+        return this.characterMap.forEach((r,n)=>{
+            r.get(e) && (t = r.get(e))
+        }
+        ),
+        t
+    }
+    getAvatarNums() {
+        let e = 0;
+        return this.characterMap.forEach((t,r)=>{
+            e += t.size
+        }
+        ),
+        e
+    }
+    registerAvatar(e) {
+        this.characterMap.get(e.priority).set(e.id, e)
+    }
+    unregisterAvatar(e) {
+        this.characterMap.get(e.priority).delete(e.id)
+    }
+    initAvatarMap() {
+        this.characterMap.set(0, new Map),
+        this.characterMap.set(1, new Map),
+        this.characterMap.set(2, new Map),
+        this.characterMap.set(3, new Map),
+        this.characterMap.set(4, new Map),
+        this.characterMap.set(5, new Map)
+    }
+    loadAvatar({id: e, avatarType: t, priority: r, avatarManager: n, assets: o, status: a}) {
+        return new Promise((s,l)=>{
+            if (this.getAvatarById(e))
+                return l(new DuplicateAvatarIDError(`[Engine] cannot init avatar with the same id = ${e}`));
+            if (this.getAvatarNums() > this.maxAvatarNum)
+                return l(new ExceedMaxAvatarNumError(`[Engine] \u8D85\u51FA\u6700\u5927\u89D2\u8272\u9650\u5236 ${this.maxAvatarNum}`));
+            const u = new XAvatar({
+                id: e,
+                avatarType: t,
+                priority: r,
+                avatarManager: n,
+                assets: o,
+                status: a
+            });
+            if (this.registerAvatar(u),
+            r == 0)
+                this.setMainAvatar(u.id),
+                this.addAvatarToScene(u, 0).then(c=>(log$D.debug(`[Engine] avatar ${u.id} has been added to scene`),
+                c ? (this._updateBillboardStatus(c, BillboardStatus.SHOW),
+                setTimeout(()=>{
+                    this.launchProcessLoadingLoop()
+                }
+                , this._delayTime),
+                s(c)) : (u.removeAvatarFromScene(),
+                l(new AvatarAssetLoadingError)))).catch(c=>(u.removeAvatarFromScene(),
+                l(new AvatarAssetLoadingError(c))));
+            else
+                return s(u)
+        }
+        )
+    }
+    deleteAvatar(e) {
+        return e.isRender ? (e.removeAvatarFromScene(),
+        this.currentLODUsers[e.distLevel]--) : e.bbComponent.disposeBillBoard(e),
+        this._processList = this._processList.filter(t=>t.id !== e.id),
+        this.unregisterAvatar(e),
+        e.rootNode && (e.rootNode.dispose(),
+        e.rootNode = void 0),
+        e.bbComponent.bbox && e.bbComponent.bbox.dispose(),
+        e.removeObserver(),
+        e
+    }
+    _checkLODLevel(e) {
+        if (e < this._distLevels[0])
+            return 0;
+        for (let t = 1; t < this._distLevels.length; ++t)
+            if (e >= this._distLevels[t - 1] && e < this._distLevels[t])
+                return t;
+        return this._distLevels.length - 1
+    }
+    get sceneManager() {
+        return this._sceneManager
+    }
+    launchProcessLoadingLoop() {
+        this._updateAvatarStatus()
+    }
+    stopProcessLoadingLoop() {
+        var e;
+        this._updateLoopObserver && ((e = this._scene) == null || e.onBeforeRenderObservable.remove(this._updateLoopObserver))
+    }
+    _distToMain(e) {
+        var n;
+        const t = (n = this._mainUser) == null ? void 0 : n.position
+          , r = e.position;
+        if (r && t) {
+            const o = this.sceneManager.cameraComponent.MainCamera.getFrontPosition(1).subtract(this.sceneManager.cameraComponent.MainCamera.position).normalize()
+              , a = e.rootNode.position.subtract(this.sceneManager.cameraComponent.MainCamera.position).normalize();
+            let s = 1;
+            if (o && a) {
+                const l = a.multiply(o);
+                s = Math.acos(l.x + l.y + l.z) < this.sceneManager.cameraComponent.MainCamera.fov / 2 ? 1 : 1e11
+            }
+            return calcDistance3D(t, r) * s
+        } else
+            return log$D.warn("user position or camera position is not correct!"),
+            1e11
+    }
+    _distToCamera(e) {
+        var n;
+        const t = (n = this._sceneManager) == null ? void 0 : n.cameraComponent.getCameraPose().position
+          , r = e.position;
+        return r && t ? calcDistance3D(t, r) : (log$D.warn("user position or camera position is not correct!"),
+        1e11)
+    }
+    showAll(e) {
+        this.characterMap.forEach((t,r)=>{
+            e && r == 0 && t.forEach((n,o)=>{
+                n.show()
+            }
+            ),
+            r != 0 && t.forEach((n,o)=>{
+                n.show()
+            }
+            )
+        }
+        )
+    }
+    hideAll(e) {
+        this.characterMap.forEach((t,r)=>{
+            e && r == 0 && t.forEach((n,o)=>{
+                n.hide()
+            }
+            ),
+            r != 0 && t.forEach((n,o)=>{
+                n.hide()
+            }
+            )
+        }
+        )
+    }
+    _assemblyAvatar(e, t) {
+        var n, o;
+        const r = e.get(avatarSetting.body);
+        if (r && !t.attachBody(r)) {
+            t.isInLoadingList = !1,
+            e.clear();
+            return
+        }
+        for (const a of e)
+            if (a[0] != avatarSetting.body && a[0] != avatarSetting.animations && !t.attachDecoration(a[1])) {
+                t.isInLoadingList = !1,
+                t.removeAvatarFromScene(),
+                e.clear();
+                return
+            }
+        t.isRender = !0,
+        (n = t.controller) == null || n.playAnimation(t.controller.onPlay, t.controller.loop),
+        (o = t.controller) == null || o.onPlayObservable.addOnce(()=>{
+            var a, s;
+            if (!this.getAvatarById(t.id)) {
+                t.isInLoadingList = !1,
+                t.removeAvatarFromScene(),
+                this.currentLODUsers[t.distLevel]--;
+                return
+            }
+            if (this.getAvatarById(t.id).rootNode.getChildMeshes().length < e.size) {
+                log$D.error(`this avatar does not have complete components, render failed. current list ${(a = this.getAvatarById(t.id)) == null ? void 0 : a.clothesList},avatar: ${t.id},${t.nickName}`),
+                t.isInLoadingList = !1,
+                t.removeAvatarFromScene(),
+                this.currentLODUsers[t.distLevel]--;
+                return
+            }
+            t.setIsPickable(!0),
+            t.isInLoadingList = !1,
+            t.setAvatarVisible(!0),
+            (s = this._sceneManager) == null || s.lightComponent.setShadow(t),
+            t.getBbox(),
+            t.nameBoard && t.nickName.length > 0 && t.setNickName(t.nickName, t.nameBoard.DEFAULT_CONFIGS),
+            t.bubble && t.words.length > 0 && t.say(t.words, t.bubble.DEFAULT_CONFIGS),
+            log$D.debug(`[Engine] avatar ${t.id} has been added to scene, current number of users : ${this.currentLODUsers}`)
+        }
+        )
+    }
+    _disposeUnusedAssets() {
+        this._tickDispose++,
+        this._tickDispose > this._disposeTime && (avatarLoader.disposeContainer(),
+        this._tickDispose = 0)
+    }
+    _addResourcesToList(e, t) {
+        return e.clothesList.forEach(r=>{
+            r.lod = t,
+            this._queue.push(r)
+        }
+        ),
+        this._queue.push({
+            type: avatarSetting.animations,
+            id: this._defaultAnims
+        }),
+        this._queue.push({
+            type: avatarSetting.body,
+            id: e.avatarType,
+            lod: t
+        }),
+        !0
+    }
+    _updateBillboardStatus(e, t) {
+        e.bbComponent.updateBillboardStatus(e, t)
+    }
+    _processLayer(e) {
+        const t = this.characterMap.get(e)
+          , r = [];
+        for (t == null || t.forEach(n=>{
+            n.distToCam = this._distToCamera(n);
+            const o = n.distToCam < this._cullingDistance;
+            if (n.isRender && (!n.isHide && o ? n._hide_culling() : n._show_culling()),
+            n.priority != 0) {
+                n.distance = this._distToMain(n);
+                let a = BillboardStatus.SHOW;
+                n.distance < this._maxDistRange && (o ? a = BillboardStatus.HIDE : n._show_culling(),
+                this._updateBillboardStatus(n, a)),
+                n.isHide || (n.isInLoadingList ? this.currentLODUsers[n.distLevel]++ : r.push(n))
+            }
+        }
+        ),
+        r.sort((n,o)=>o.distance - n.distance); r.length > 0 && this.curRenderNum() < this.maxRenderNum(); ) {
+            const n = r.pop();
+            let o = this._checkLODLevel(n.distance)
+              , a = !1;
+            for (let s = 0; s < this._maxLODUsers.length; ++s)
+                if (this.currentLODUsers[s] < this._maxLODUsers[s]) {
+                    o = s,
+                    a = !0;
+                    break
+                }
+            if (!a || n.distance > this._maxDistRange) {
+                if (n.isRender) {
+                    n._removeAvatarFromScene();
+                    let s = BillboardStatus.HIDE;
+                    n.distance < this._maxDistRange && (s = BillboardStatus.SHOW),
+                    this._updateBillboardStatus(n, s)
+                }
+                break
+            }
+            o != n.distLevel ? (n.isRender && (n.pendingLod = !0),
+            n.distLevel = o,
+            this._processList.push(n),
+            n.isInLoadingList = !0) : n.isRender || (this._processList.push(n),
+            n.isInLoadingList = !0),
+            this.currentLODUsers[o]++
+        }
+        return this.curRenderNum() >= this.maxRenderNum() && r.forEach(n=>{
+            if (n.isRender) {
+                n._removeAvatarFromScene();
+                let o = BillboardStatus.HIDE;
+                n.distance < this._maxDistRange && (o = BillboardStatus.SHOW),
+                this._updateBillboardStatus(n, o)
+            }
+        }
+        ),
+        this.curRenderNum() < this.maxRenderNum()
+    }
+    _updateAvatar() {
+        this.currentLODUsers = [0, 0, 0];
+        const e = [5, 4, 3, 2, 1, 0];
+        for (; e.length > 0; ) {
+            const t = e.pop();
+            if (!this._processLayer(t)) {
+                e.forEach(n=>{
+                    var o;
+                    (o = this.characterMap.get(n)) == null || o.forEach(a=>{
+                        a.distance = this._distToMain(a);
+                        let s = BillboardStatus.HIDE;
+                        a.distToCam < this._maxDistRange && (s = BillboardStatus.SHOW,
+                        a.isRender && a._removeAvatarFromScene()),
+                        this._updateBillboardStatus(a, s)
+                    }
+                    )
+                }
+                );
+                break
+            }
+        }
+    }
+    _updateAvatarStatus() {
+        const e = new Map;
+        this._updateLoopObserver = this.scene.onBeforeRenderObservable.add(()=>{
+            var t;
+            if (this._disposeUnusedAssets(),
+            !(this.getAvatarNums() <= 0)) {
+                if (!this._process && this._processList.length == 0 && this._updateAvatar(),
+                !this._process && this._processList.length > 0) {
+                    const r = this._processList.shift();
+                    r != this._process && !r.isCulling ? this._addResourcesToList(r, r.distLevel) ? (this._process = r,
+                    this._queueLength = this._queue.length) : (this._process = void 0,
+                    this._queue = [],
+                    r.isInLoadingList = !1) : r.isInLoadingList = !1
+                }
+                if (e.size === this._queueLength && this._process) {
+                    this._process.pendingLod && (this._process.pendingLod = !1,
+                    this._process._removeAvatarFromScene());
+                    const r = Date.now();
+                    this._assemblyAvatar(e, this._process),
+                    (t = this._sceneManager) == null || t.engineRunTimeStats.timeArray_addAvatarToScene.add(Date.now() - r),
+                    this._updateBillboardStatus(this._process, BillboardStatus.SHOW),
+                    e.clear(),
+                    this._queue = [],
+                    this._process.isInLoadingList = !1,
+                    this._process = void 0,
+                    this._disposeUnusedAssets()
+                }
+                this._loadResByList(e)
+            }
+        }
+        )
+    }
+    _loadResByList(e) {
+        let t = 0;
+        const r = 5;
+        if (!this._process) {
+            e.clear();
+            return
+        }
+        for (; t < r && this._queue.length > 0; ) {
+            const n = Date.now()
+              , o = this._queue.pop();
+            setTimeout(()=>{
+                o ? o.type === avatarSetting.body ? this.loadBody(o.type, o.id, o.lod).then(a=>{
+                    a && e.set(avatarSetting.body, a),
+                    t += Date.now() - n
+                }
+                ).catch(a=>{
+                    this._process && (this._process.isHide = !0,
+                    this.currentLODUsers[this._process.distLevel]--,
+                    e.clear(),
+                    this._queue = [],
+                    this._process.isInLoadingList = !1,
+                    this._process = void 0,
+                    t += 100),
+                    log$D.warn(`[Engine] body ${o.id} uri error, type ${o.type}, avatar has been hided` + a)
+                }
+                ) : o.type === avatarSetting.animations ? this.loadAnimation(this._process.avatarType, o.id).then(a=>{
+                    a && e.set(avatarSetting.animations, a),
+                    t += Date.now() - n
+                }
+                ).catch(a=>{
+                    this._process && (this._process.isHide = !0,
+                    this.currentLODUsers[this._process.distLevel]--,
+                    e.clear(),
+                    this._queue = [],
+                    this._process.isInLoadingList = !1,
+                    this._process = void 0,
+                    t += 100),
+                    log$D.warn(`animation  ${o.id} uri error, type ${o.type}, avatar has been hided` + a)
+                }
+                ) : this.loadDecoration(o.type, o.id, o.lod).then(a=>{
+                    a && e.set(a.type, a),
+                    t += Date.now() - n
+                }
+                ).catch(a=>{
+                    this._process && (this._process.isHide = !0,
+                    this.currentLODUsers[this._process.distLevel]--,
+                    e.clear(),
+                    this._queue = [],
+                    this._process.isInLoadingList = !1,
+                    this._process = void 0,
+                    t += 100),
+                    log$D.warn(`component ${o.id} uri error, type ${o.type}, avatar has been hided` + a)
+                }
+                ) : t += 100
+            }
+            , 0)
+        }
+    }
+    _validateContainer(e) {
+        return !e.meshes || e.meshes.length <= 1 ? (log$D.warn("import container has no valid meshes"),
+        !1) : !e.skeletons || e.skeletons.length == 0 ? (log$D.warn("import container has no valid skeletons"),
+        !1) : !0
+    }
+    _getAssetContainer(e, t) {
+        return new Promise((r,n)=>{
+            const o = this._getSourceKey(e, t || 0)
+              , a = avatarLoader.containers.get(o);
+            if (a)
+                return r(a);
+            avatarLoader.load(this.sceneManager, e, t).then(s=>s ? this._validateContainer(s) ? (avatarLoader.containers.set(o, s),
+            r(s)) : n(new ContainerLoadingFailedError(`[Engine] :: cannot load body type ${e}.`)) : n(new ContainerLoadingFailedError(`[Engine] container load failed cannot load body type ${e}.`))).catch(s=>n(new ContainerLoadingFailedError(`[Engine] ${s} :: cannot load body type ${e}.`)))
+        }
+        )
+    }
+    _clipContainerRes(e) {
+        e.transformNodes.forEach(t=>{
+            t.dispose()
+        }
+        ),
+        e.transformNodes = [],
+        e.skeletons.forEach(t=>{
+            t.dispose()
+        }
+        ),
+        e.skeletons = []
+    }
+    loadBody(e, t, r) {
+        return new Promise((n,o)=>avatarLoader.load(this.sceneManager, t, r).then(a=>{
+            if (a) {
+                const s = a.instantiateModelsToScene();
+                a.xReferenceCount++;
+                const l = {
+                    isRender: !1,
+                    uId: Math.random(),
+                    root: s.rootNodes[0],
+                    skeletonType: e,
+                    name: t,
+                    animations: s.animationGroups,
+                    skeleton: s.skeletons[0],
+                    lod: r
+                };
+                return s.rootNodes[0]._parentContainer = a,
+                s.rootNodes[0].setEnabled(!1),
+                n(l)
+            } else
+                return o(new ContainerLoadingFailedError("[Engine] container failed instanciates failed"))
+        }
+        ).catch(()=>o(new ContainerLoadingFailedError(`[Engine] body type ${e} instanciates failed`))))
+    }
+    updateAnimationLists(e, t) {
+        return new Promise((r,n)=>(avatarLoader.avaliableAnimation.set(t, e),
+        r()))
+    }
+    loadAnimation(e, t) {
+        return new Promise((r,n)=>avatarLoader.loadAnimRes(this.sceneManager, t, e).then(o=>{
+            if (o) {
+                let a;
+                const s = this.avatarLoader.animations;
+                return o.animationGroups.forEach(l=>{
+                    l.stop(),
+                    l.name === t && (a = l,
+                    a.pContainer = o),
+                    s.set(getAnimationKey(l.name, e), l)
+                }
+                ),
+                this._clipContainerRes(o),
+                o.xReferenceCount++,
+                r(a)
+            } else
+                return n(new ContainerLoadingFailedError("[Engine] container failed instanciates failed"))
+        }
+        ))
+    }
+    loadDecoration(e, t, r) {
+        return new Promise((n,o)=>avatarLoader.load(this.sceneManager, t, r).then(a=>{
+            if (a) {
+                this._clipContainerRes(a);
+                const s = a.meshes[1].clone(a.meshes[1].name, null);
+                if (!s) {
+                    log$D.warn("[Engine] decoration does not exist!"),
+                    n(null);
+                    return
+                }
+                const l = {
+                    isRender: !1,
+                    uId: Math.random(),
+                    root: s,
+                    type: e,
+                    name: t,
+                    isSelected: !1,
+                    lod: r
+                };
+                if (a.xReferenceCount++,
+                s._parentContainer = a,
+                a.meshes.length > 1)
+                    for (let u = 2; u < a.meshes.length; u++)
+                        s.addChild(a.meshes[u].clone(a.meshes[u].name, null));
+                s.setEnabled(!1),
+                l.isSelected = !0,
+                n(l)
+            } else
+                return o(new ContainerLoadingFailedError("[Engine] container failed, instanciates failed."))
+        }
+        ).catch(()=>o(new ContainerLoadingFailedError(`[Engine] body type ${e} instanciates failed.`))))
+    }
+    _getSourceKey(e, t) {
+        return t && avatarSetting.lod[t] ? e + avatarSetting.lod[t].fileName.split(".")[0] : e
+    }
+    addAvatarToScene(e, t) {
+        const r = Date.now();
+        return new Promise((n,o)=>{
+            this.loadBody(e.avatarType, e.avatarType, t).then(a=>{
+                var s;
+                if (!a)
+                    return e.isInLoadingList = !1,
+                    o(new ContainerLoadingFailedError(`[Engine] avatar ${e.id} instanciates failed`));
+                if (e.attachBody(a),
+                a.animations.length > 0)
+                    return a.animations.forEach(l=>{
+                        l.stop()
+                    }
+                    ),
+                    e.setAnimations(a.animations),
+                    (s = e.controller) == null || s.playAnimation(e.controller.onPlay, !0),
+                    e.isRender = !0,
+                    e.isInLoadingList = !1,
+                    e.setAvatarVisible(!0),
+                    n(e);
+                this.loadAnimation(e.avatarType, this._defaultAnims).then(l=>{
+                    if (!l)
+                        return e.removeAvatarFromScene(),
+                        e.isInLoadingList = !1,
+                        o(new AvatarAnimationError);
+                    const u = [];
+                    e.clothesList.length > 0 && e.clothesList.forEach(c=>{
+                        u.push(this.loadDecoration(c.type, c.id, t))
+                    }
+                    ),
+                    Promise.all(u).then(c=>{
+                        var d, _, g, m;
+                        c.forEach(v=>{
+                            if (v && !v.isRender)
+                                e.attachDecoration(v);
+                            else
+                                return e.isInLoadingList = !1,
+                                e.removeAvatarFromScene(),
+                                o(new AvatarAssetLoadingError)
+                        }
+                        ),
+                        e.isRender = !0,
+                        (d = e.controller) == null || d.playAnimation(e.controller.onPlay, e.controller.loop),
+                        e.setAvatarVisible(!0);
+                        const h = avatarLoader.mshPath.get("meshes/ygb.glb")
+                          , f = avatarLoader.matPath.get(avatarResources.ygb.mesh);
+                        h && f ? this.loadExtra(f, h).then(v=>{
+                            var y;
+                            e.isRender = !0,
+                            e.isInLoadingList = !1,
+                            e.distLevel = t,
+                            (y = this._sceneManager) == null || y.engineRunTimeStats.timeArray_addAvatarToScene.add(Date.now() - r),
+                            n(e)
+                        }
+                        ) : (e.isRender = !0,
+                        e.isInLoadingList = !1,
+                        e.distLevel = t,
+                        (_ = this._sceneManager) == null || _.engineRunTimeStats.timeArray_addAvatarToScene.add(Date.now() - r),
+                        n(e)),
+                        (g = this._sceneManager) == null || g.lightComponent.setShadow(e),
+                        e.isInLoadingList = !1,
+                        e.distLevel = t,
+                        (m = this._sceneManager) == null || m.engineRunTimeStats.timeArray_addAvatarToScene.add(Date.now() - r),
+                        n(e)
+                    }
+                    ).catch(()=>o(new AvatarAssetLoadingError(`[Engine] avatar ${e.id} instanciates failed.`)))
+                }
+                ).catch(()=>o(new AvatarAssetLoadingError(`[Engine] avatar ${e.id} instanciates failed.`)))
+            }
+            ).catch(()=>o(new AvatarAssetLoadingError(`[Engine] avatar ${e.id} instanciates failed.`)))
+        }
+        )
+    }
+    loadExtra(e, t) {
+        const r = avatarResources.ygb.name;
+        return new Promise((n,o)=>{
+            var a;
+            (a = this.sceneManager) == null || a.urlTransformer(e).then(s=>{
+                SceneLoader.LoadAssetContainerAsync("", s, this.scene, null, avatarSetting.fileType).then(l=>{
+                    var c;
+                    this.extraComps.set(r, l.meshes[0]);
+                    const u = new NodeMaterial(`material_${r}`,this._scene,{
+                        emitComments: !1
+                    });
+                    (c = this.sceneManager) == null || c.urlTransformer(t).then(h=>{
+                        u.loadAsync(h).then(()=>{
+                            l.meshes[2].material.dispose(!0, !0),
+                            u.build(!1),
+                            l.meshes[2].material = u,
+                            n(l.meshes[2])
+                        }
+                        )
+                    }
+                    )
+                }
+                )
+            }
+            )
+        }
+        )
+    }
+    getAvatarList() {
+        const e = [];
+        return this.characterMap.forEach((t,r)=>{
+            t.forEach((n,o)=>{
+                e.push(n)
+            }
+            )
+        }
+        ),
+        e
+    }
+    _debug_avatar() {
+        var t, r;
+        console.error("===>currentLODUsers", this.currentLODUsers),
+        console.error("===>maxLODUsers", this._maxLODUsers),
+        console.error("===>Loddist", this.getLoDLevels()),
+        console.error("===> main character loc", (r = (t = this._mainUser) == null ? void 0 : t.rootNode) == null ? void 0 : r.position);
+        let e = 0;
+        this.getAvatarList().forEach(n=>{
+            n.isRender && (console.error(`avatar id : ${n.id},lod ${n.distLevel},is Hide ${n.isHide}, distance ${n.distance}, is pending ${n.isInLoadingList}`),
+            e++)
+        }
+        ),
+        console.error("========= avatar num", e),
+        console.error("loop:", this._updateLoopObserver ? "on" : "false", "=> process", this._process, "===> comp", this._processList),
+        console.error("===>maxLODUsers", this._maxLODUsers)
+    }
+}

+ 229 - 0
XBillboard.js

@@ -0,0 +1,229 @@
+const log$I = new Logger$1("Billboard");
+var BillboardStatus = (i=>(i[i.SHOW = 1] = "SHOW",
+i[i.HIDE = 0] = "HIDE",
+i[i.DISPOSE = -1] = "DISPOSE",
+i))(BillboardStatus || {});
+class XBillboard {
+    constructor(e, t=!1, r=!1) {
+        E(this, "_mesh", null);
+        E(this, "_texture", null);
+        E(this, "_scalingFactor", 1);
+        E(this, "offsets", null);
+        E(this, "_pickable");
+        E(this, "_background", null);
+        E(this, "_billboardManager");
+        E(this, "poolobj", null);
+        E(this, "_usePool");
+        E(this, "_initMeshScale", new Vector3(1,1,1));
+        E(this, "_status", -1);
+        E(this, "_stageChanged", !1);
+        E(this, "DEFAULT_CONFIGS", {});
+        this._billboardManager = e,
+        this._pickable = t,
+        this._usePool = r
+    }
+    set scalingFactor(e) {
+        this._scalingFactor = e
+    }
+    set background(e) {
+        this._background = e
+    }
+    get size() {
+        return -1
+    }
+    setStatus(e) {
+        e != this._status && (this._stageChanged = !0,
+        this._status = e)
+    }
+    get status() {
+        return this._status
+    }
+    get stageChanged() {
+        return this._stageChanged
+    }
+    set stageChanged(e) {
+        this._stageChanged = e
+    }
+    init(e="", t=.001, r=.001, n=!1) {
+        const o = this._billboardManager.sceneManager.Scene;
+        if (this._usePool) {
+            const a = this._billboardManager.billboardPool.getFree(o, t, r, n);
+            this._mesh = a.data,
+            this._mesh.isPickable = this._pickable,
+            this._mesh.xid = e,
+            this._mesh.xtype = EMeshType.XBillboard,
+            this._texture = this._mesh.material.diffuseTexture,
+            this.poolobj = a
+        } else
+            this._mesh = this._billboardManager.createBillboardAsset(o, n);
+        this._mesh.isPickable = this._pickable,
+        this._initMeshScale.x = t * 1e3,
+        this._initMeshScale.y = r * 1e3,
+        this._mesh.xid = e,
+        this._mesh.xtype = EMeshType.XBillboard,
+        this._texture = this._mesh.material.diffuseTexture,
+        this.setStatus(1),
+        this._stageChanged = !0
+    }
+    dispose() {
+        this._usePool ? this.poolobj && (this._billboardManager.billboardPool.release(this.poolobj),
+        this._mesh = null,
+        this._texture = null,
+        this.poolobj = null) : this._mesh && (this._mesh.dispose(!0, !0),
+        this._mesh = null,
+        this._texture = null),
+        this._background = null
+    }
+    getMesh() {
+        return this._mesh
+    }
+    updateImage(e) {
+        return new Promise(t=>{
+            if (this._texture == null) {
+                log$I.error("[Engine]Billboard texture not found");
+                return
+            }
+            const r = this._mesh
+              , n = this._texture
+              , o = this._scalingFactor
+              , a = this._initMeshScale.x
+              , s = this._initMeshScale.y
+              , l = this._texture.getContext()
+              , u = this._texture.getSize();
+            l.clearRect(0, 0, u.width, u.height);
+            const c = new Image;
+            c.crossOrigin = "anonymous",
+            c.src = e,
+            c.onload = ()=>{
+                const h = c.width * o
+                  , f = c.height * o;
+                r.scaling.x = h * a,
+                r.scaling.y = f * s,
+                n.scaleTo(h, f),
+                l.drawImage(c, 0, 0, h, f),
+                n.hasAlpha = !0,
+                n.update(),
+                t()
+            }
+        }
+        )
+    }
+    show() {
+        this._mesh && (this._mesh.setEnabled(!0),
+        this._mesh.isPickable = this._pickable)
+    }
+    hide() {
+        this._mesh && (this._mesh.setEnabled(!1),
+        this._mesh.isPickable = !1)
+    }
+    setId(e) {
+        this._mesh && (this._mesh.xid = e)
+    }
+    setPosition(e) {
+        if (e && this._mesh) {
+            const t = ue4Position2Xverse(e);
+            this._mesh.position = t
+        }
+    }
+    updateText(e, t, r=!0, n=[], o=30, a="monospace", s="black", l="bold", u) {
+        if (this._texture == null) {
+            log$I.error("[Engine]Billboard texture not found");
+            return
+        }
+        const c = this._texture
+          , h = this._mesh
+          , f = this._scalingFactor
+          , d = this._initMeshScale.x
+          , _ = this._initMeshScale.y;
+        if (e != "") {
+            const g = this._texture.getContext()
+              , m = this._texture.getSize();
+            g.clearRect(0, 0, m.width, m.height);
+            const v = new Image;
+            if (r) {
+                t != null ? t ? this._background = this._billboardManager.userBackGroundBlob : this._background = this._billboardManager.npcBackGroundBlob : this._background || (this._background = this._billboardManager.userBackGroundBlob);
+                let y = e
+                  , b = u && u < n.length - 1 ? u : n.length - 1;
+                if (this._background) {
+                    if (b > this._background.length) {
+                        for (let T = 0; T < b - this._background.length; T++)
+                            n.pop();
+                        b = n.length - 1,
+                        y = e.slice(0, n[b] - 1) + String.fromCharCode(8230)
+                    }
+                    v.crossOrigin = "anonymous",
+                    v.src = this._background[b - 1],
+                    v.onload = function() {
+                        const T = v.width * f
+                          , C = v.height * f;
+                        h.scaling.x = T * d,
+                        h.scaling.y = C * _,
+                        c.scaleTo(T, C),
+                        g.textAlign = "center",
+                        g.textBaseline = "middle",
+                        g.drawImage(v, 0, 0, T, C);
+                        for (let A = 0; A < b; A++)
+                            c.drawText(y.slice(n[0 + A], n[1 + A]), T / 2, C * (A + 1) / (b + 1) + (A - (b - 1) / 2) * f * 10, l + " " + o * f + "px " + a, s, "transparent", !0);
+                        c.hasAlpha = !0
+                    }
+                }
+            } else {
+                const y = u && u < n.length - 1 ? u : n.length - 1
+                  , b = 480 * f
+                  , T = 60 * f * y;
+                this._mesh.scaling = new Vector3(b * d,T * _,1),
+                c.scaleTo(b, T);
+                const C = c.getContext();
+                C.textAlign = "center",
+                C.textBaseline = "middle";
+                for (let A = 0; A < y; A++)
+                    c.drawText(e.slice(n[0 + A], n[1 + A]), b / 2 + 2 * f, T * (A + 1) / (y + 1) + (A - (y - 1) / 2) * f * 10 + 2 * f, l + " " + o * f + "px " + a, "#333333", "transparent", !0),
+                    c.drawText(e.slice(n[0 + A], n[1 + A]), b / 2, T * (A + 1) / (y + 1) + (A - (y - 1) / 2) * f * 10, l + " " + o * f + "px " + a, s, "transparent", !0);
+                c.hasAlpha = !0
+            }
+        } else
+            this.clearText()
+    }
+    drawBillboard(e, t, r) {
+        var m;
+        const {imageList: n} = e
+          , {texts: o, font: a="monospace", fontsize: s=40, fontcolor: l="#ffffff", fontstyle: u="", linesize: c=20, linelimit: h} = t
+          , {position: f, offsets: d, scale: _, compensationZ: g=0} = r;
+        if (this.scalingFactor = _ || 1,
+        d && (this.offsets = {
+            x: d.x * this._scalingFactor,
+            y: d.y * this._scalingFactor,
+            z: d.z * this._scalingFactor
+        }),
+        this.offsets || (this.offsets = {
+            x: 0,
+            y: 0,
+            z: 0
+        }),
+        this.setPosition(f),
+        n && !o)
+            (m = this._billboardManager.sceneManager) == null || m.urlTransformer(n[0]).then(v=>{
+                this.updateImage(v)
+            }
+            );
+        else if (o && !n) {
+            const [v,y] = getStringBoundaries(o, c, XBillboardManager.alphaWidthMap);
+            this.offsets.z += this._scalingFactor * g * (y.length - 1),
+            this.updateText(v, void 0, !1, y, s, a, l, u, h)
+        } else if (o && n) {
+            this.background = n;
+            const [v,y] = getStringBoundaries(o, c, XBillboardManager.alphaWidthMap);
+            this.offsets.z += this._scalingFactor * g * (y.length - 1),
+            this.updateText(v, void 0, !0, y, s, a, l, u, h)
+        }
+        this.setStatus(1)
+    }
+    clearText() {
+        if (this._texture != null) {
+            const e = this._texture.getContext()
+              , t = this._texture.getSize();
+            e.clearRect(0, 0, t.width, t.height),
+            this._texture.update()
+        }
+    }
+}

+ 149 - 0
XBillboardManager.js

@@ -0,0 +1,149 @@
+const texRootDir = "https://app-asset-1258211750.file.myqcloud.com/1/textures/"
+class XBillboardManager {
+    constructor(e) {
+        E(this, "billboardMap", new Map);
+        E(this, "sceneManager");
+        E(this, "billboardPool");
+        E(this, "userBackGroundBlob", new Array);
+        E(this, "npcBackGroundBlob", new Array);
+        E(this, "tickObserver");
+        E(this, "tickInterval");
+        E(this, "_updateLoopObserver");
+        this.sceneManager = e,
+        this.billboardPool = new Pool(this.createBillboardAsset,this.resetBillboardAsset,0,60,this.sceneManager.Scene,!1),
+        this.tickInterval = 250;
+        let t = 0;
+        this.tickObserver = this.sceneManager.Scene.onAfterRenderObservable.add(()=>{
+            t += 1,
+            t == this.tickInterval && (this.tick(),
+            t = 0)
+        }
+        ),
+        this.launchBillboardStatusLoop()
+    }
+    tick() {
+        this.billboardPool.clean(0, this.sceneManager.Scene, !1)
+    }
+    createBillboardAsset(e, t=!1) {
+        const r = MeshBuilder.CreatePlane("billboard-", {
+            height: .001,
+            width: .001,
+            sideOrientation: Mesh.DOUBLESIDE
+        }, e);
+        r.isPickable = !0,
+        r.setEnabled(!1);
+        const n = new DynamicTexture("billboard-tex-",{
+            width: .001 + 1,
+            height: .001 + 1
+        },e,t,Texture.BILINEAR_SAMPLINGMODE);
+        n.hasAlpha = !0;
+        const o = new StandardMaterial("billboard-mat-",e);
+        return o.diffuseTexture = n,
+        o.emissiveColor = new Color3(.95,.95,.95),
+        o.useAlphaFromDiffuseTexture = !0,
+        r.material = o,
+        r.billboardMode = Mesh.BILLBOARDMODE_Y,
+        r.position.y = 0,
+        r
+    }
+    resetBillboardAsset(e) {
+        const t = e.data;
+        return t.setEnabled(!1),
+        t.isPickable = !1,
+        e
+    }
+    async loadBackGroundTexToIDB() {
+        ge.userBubbleUrls.forEach(r=>{
+            this.sceneManager.urlTransformer(r).then(n=>{
+                this.userBackGroundBlob.push(n)
+            }
+            )
+        }
+        ),
+        ge.npcBubbleUrls.forEach(r=>{
+            this.sceneManager.urlTransformer(r).then(n=>{
+                this.npcBackGroundBlob.push(n)
+            }
+            )
+        }
+        )
+    }
+    addBillboardToMap(e, t) {
+        this.billboardMap.set(e, t)
+    }
+    addBillboard(e, t, r) {
+        let n = this.getBillboard(e);
+        return n || (n = new XBillboard(this,t,r),
+        this.addBillboardToMap(e, n)),
+        n
+    }
+    generateStaticBillboard(e, {id: t="billboard", isUser: r, background: n, font: o="Arial", fontsize: a=40, fontcolor: s="#ffffff", fontstyle: l="600", linesize: u=16, linelimit: c, scale: h=1, width: f=.01, height: d=.01, position: _={
+        x: 0,
+        y: 0,
+        z: 0
+    }}) {
+        const g = this.addBillboard(t, !1, !0);
+        g.getMesh() == null && g.init(t, f, d);
+        let m;
+        r != null && (m = r ? ge.userBubbleUrls : ge.npcBubbleUrls),
+        g && g.getMesh() && (g.DEFAULT_CONFIGS = {
+            id: t,
+            isUser: r,
+            background: n,
+            font: o,
+            fontsize: a,
+            fontcolor: s,
+            fontstyle: l,
+            linesize: u,
+            linelimit: c,
+            scale: h,
+            width: f,
+            height: d,
+            position: _
+        },
+        g.drawBillboard({
+            imageList: n || m
+        }, {
+            texts: e,
+            font: o,
+            fontsize: a,
+            fontcolor: s,
+            fontstyle: l,
+            linesize: u,
+            linelimit: c
+        }, {
+            position: _,
+            scale: h
+        }),
+        t && g.setId(t),
+        g.setStatus(BillboardStatus.SHOW))
+    }
+    getBillboard(e) {
+        return this.billboardMap.get(e)
+    }
+    toggle(e, t) {
+        var r;
+        (r = this.getBillboard(e)) == null || r.setStatus(t ? BillboardStatus.SHOW : BillboardStatus.HIDE)
+    }
+    removeBillboard(e) {
+        const t = this.getBillboard(e);
+        t && (t.setStatus(BillboardStatus.DISPOSE),
+        this.billboardMap.delete(e))
+    }
+    launchBillboardStatusLoop() {
+        this._updateLoopObserver = this.sceneManager.Scene.onBeforeRenderObservable.add(()=>{
+            this.billboardMap.size <= 0 || this.billboardMap.forEach(e=>{
+                e.stageChanged && (e.status == BillboardStatus.SHOW ? e.show() : e.status == BillboardStatus.HIDE ? e.hide() : (e.hide(),
+                e.dispose()),
+                e.stageChanged = !1)
+            }
+            )
+        }
+        )
+    }
+}
+;
+
+E(XBillboardManager, "alphaWidthMap", new Map),
+E(XBillboardManager, "userBubbleUrls", [texRootDir + "bubble01.png", texRootDir + "bubble02.png", texRootDir + "bubble03.png"]),
+E(XBillboardManager, "npcBubbleUrls", [texRootDir + "bubble01_npc.png", texRootDir + "bubble02_npc.png", texRootDir + "bubble03_npc.png"]);

+ 275 - 0
XBreathPointManager.js

@@ -0,0 +1,275 @@
+class XBreathPointManager {
+    constructor(e) {
+        E(this, "_scene");
+        E(this, "materialMap", new Map);
+        E(this, "breathPoints", new Map);
+        E(this, "_sceneManager");
+        E(this, "_allIds", new Set);
+        E(this, "_loopBPKeys", []);
+        E(this, "addBreathPoint", async e=>{
+            const t = [{
+                url: "https://static.xverse.cn/qqktv/texture.png"
+            }];
+            if (t.length <= 0) {
+                log$w.warn("[Engine] BreathPoint get texture list error: textureList.length <= 0"),
+                new XBreathPointError("[Engine] BreathPoint get texture list error!");
+                return
+            }
+            const r = t[0]
+              , {id: n, spriteSheet: o=r.url, spriteWidthNumber: a=20, spriteHeightNumber: s=1, position: l, rotation: u={
+                pitch: -90,
+                yaw: 270,
+                roll: 0
+            }, size: c=.6, width: h=-1, height: f=-1, fps: d=30, billboardMode: _=!1, forceLeaveGround: g=!1, type: m="default", lifeTime: v=-1, backfaceculling: y=!0, maxVisibleRegion: b=-1, skinInfo: T="default"} = e;
+            if (this.breathPoints.get(n)) {
+                log$w.warn("[Engine] Cannot add breathPoint with an existing id: [" + n + "]"),
+                new XBreathPointError("[Engine] Cannot add breathPoint with an existing id: [" + n + "]");
+                return
+            }
+            if (g) {
+                const I = this.castRay(new Vector3(l.x,l.y,l.z)) * scaleFromUE4toXverse;
+                I != 0 ? l.z = l.z - I + 1 : l.z = l.z + 1
+            }
+            let C;
+            if (this.materialMap.get(m)) {
+                const I = this.materialMap.get(m);
+                I.count = I.count + 1,
+                C = I.mat
+            } else {
+                const I = new Texture(o,this._scene,!0,!0,Texture.BILINEAR_SAMPLINGMODE,null,()=>{
+                    log$w.error("[Engine] Breathpoint create texture error."),
+                    new XBreathPointError("[Engine] Breathpoint create texture error.")
+                }
+                ,null,!0);
+                I.name = "TexBreathPoint_" + n,
+                C = new StandardMaterial(`MaterialBreathPoint_${n}`,this._scene),
+                C.alpha = 1,
+                C.emissiveTexture = I,
+                C.backFaceCulling = y,
+                C.diffuseTexture = I,
+                C.diffuseTexture.hasAlpha = !0,
+                C.useAlphaFromDiffuseTexture = !0,
+                this.materialMap.set(m, {
+                    mat: C,
+                    count: 1,
+                    lastRenderTime: Date.now(),
+                    fps: d,
+                    spriteWidthNumber: a,
+                    spriteHeightNumber: s,
+                    spriteSheet: o,
+                    texture: I
+                })
+            }
+            const A = new Array(6);
+            for (let I = 0; I < 6; I++)
+                A[I] = new Vector4(0,0,0,0);
+            A[0] = new Vector4(0,0,1 / a,1 / s),
+            A[1] = new Vector4(0,0,1 / a,1 / s);
+            let S = {};
+            h > 0 && f > 0 ? S = {
+                width: h,
+                height: f,
+                depth: .01,
+                faceUV: A
+            } : S = {
+                size: c,
+                depth: .01,
+                faceUV: A
+            };
+            const P = MeshBuilder.CreateBox(n, S, this._scene);
+            P.material = C;
+            const R = new XStaticMesh({
+                id: n,
+                mesh: P,
+                xtype: EMeshType.XBreathPoint,
+                skinInfo: T
+            });
+            let M = u;
+            _ && (P.billboardMode = Mesh.BILLBOARDMODE_ALL,
+            R.allowMove(),
+            M = {
+                pitch: 0,
+                yaw: 270,
+                roll: 0
+            });
+            const x = new BreathPoint({
+                type: m,
+                mesh: R,
+                id: n,
+                position: l,
+                rotation: M,
+                mat: C,
+                maxVisibleRegion: b,
+                scene: this._scene,
+                skinInfo: T
+            });
+            this.breathPoints.set(n, x),
+            this._allIds.add(n),
+            v > 0 && setTimeout(()=>{
+                this.clearBreathPoints(n)
+            }
+            , v * 1e3)
+        }
+        );
+        E(this, "reg_breathpoint_update", ()=>{
+            const e = new Date().getTime();
+            if (this.materialMap != null)
+                for (const [t,r] of this.materialMap)
+                    e - r.lastRenderTime > 1e3 / r.fps && (r.lastRenderTime = e,
+                    Math.abs(r.mat.diffuseTexture.uOffset - (1 - 1 / r.spriteWidthNumber)) < 1e-6 ? (r.mat.diffuseTexture.uOffset = 0,
+                    Math.abs(r.mat.diffuseTexture.vOffset - (1 - 1 / r.spriteHeightNumber)) < 1e-6 ? r.mat.diffuseTexture.vOffset = 0 : r.mat.diffuseTexture.vOffset += 1 / r.spriteHeightNumber) : r.mat.diffuseTexture.uOffset += 1 / r.spriteWidthNumber)
+        }
+        );
+        E(this, "reg_breathpoint_autovisible", ()=>{
+            if (this._scene.getFrameId() % 2 == 0)
+                if (this._loopBPKeys.length == 0)
+                    this._loopBPKeys = Array.from(this._allIds);
+                else {
+                    const e = this._getMainPlayerPosition();
+                    for (let t = 0; t < 5 && this._loopBPKeys.length > 0; ++t) {
+                        const r = this._loopBPKeys.pop();
+                        if (r != null) {
+                            const n = this.getBreathPoint(r);
+                            if (n != null && n.maxvisibleregion >= 0 && n.mesh.visibility == 1) {
+                                const o = n.mesh.position;
+                                calcDistance3DVector(e, o) >= n.maxvisibleregion ? n == null || n.removeFromScene() : n == null || n.addToScene()
+                            }
+                        }
+                    }
+                }
+        }
+        );
+        this._sceneManager = e,
+        this._scene = e.Scene,
+        this._scene.registerBeforeRender(this.reg_breathpoint_update),
+        this._scene.registerBeforeRender(this.reg_breathpoint_autovisible)
+    }
+    setAllBreathPointVisibility(e) {
+        for (const [t,r] of this.breathPoints.entries())
+            r.toggleVisibility(e)
+    }
+    toggleBPVisibilityBySkinInfo(e, t) {
+        for (const [r,n] of this.breathPoints.entries())
+            n.skinInfo == e && n.toggleVisibility(t)
+    }
+    toggleBPVisibilityById(e, t) {
+        const r = this.getBreathPoint(e);
+        r != null && r.toggleVisibility(t)
+    }
+    getBreathPointBySkinInfo(e) {
+        const t = [];
+        for (const [r,n] of this.breathPoints.entries())
+            n.skinInfo == e && t.push(n);
+        return t
+    }
+    getAllBreathPoint() {
+        return this.breathPoints
+    }
+    getBreathPoint(e) {
+        return this.breathPoints.get(e)
+    }
+    delete(e) {
+        const t = this.breathPoints.get(e);
+        if (t != null) {
+            t.dispose(),
+            this._allIds.delete(e);
+            const r = this.materialMap.get(t._type);
+            r != null && (r.count = r.count - 1,
+            r.count <= 0 && (r.count = 0,
+            r.texture.dispose(),
+            r.mat.dispose(!0, !0),
+            this.materialMap.delete(t._type))),
+            this.breathPoints.delete(e)
+        }
+    }
+    castRay(e) {
+        var s;
+        e = ue4Position2Xverse({
+            x: e.x,
+            y: e.y,
+            z: e.z
+        });
+        const t = new Vector3(0,-1,0)
+          , r = new Ray(e,t,length)
+          , n = []
+          , o = (s = this._sceneManager) == null ? void 0 : s.getGround({
+            x: e.x,
+            y: e.y,
+            z: e.z
+        });
+        let a = r.intersectsMeshes(o);
+        if (a.length > 0) {
+            const l = a[0];
+            if (l && l.pickedMesh) {
+                const u = l.distance;
+                t.y = 1;
+                const c = r.intersectsMeshes(n);
+                let h = 1e8;
+                if (c.length > 0) {
+                    const f = c[0];
+                    return f && f.pickedMesh && (h = -f.distance),
+                    h == 1e8 ? u : Math.abs(h) < Math.abs(u) ? h : u
+                }
+            }
+        } else if (t.y = 1,
+        a = r.intersectsMeshes(n),
+        a.length > 0) {
+            const l = a[0];
+            if (l && l.pickedMesh)
+                return l.distance
+        }
+        return 0
+    }
+    changePickable(e) {
+        for (const [t,r] of this.breathPoints.entries())
+            r.changePickable(e)
+    }
+    clearBreathPoints(e) {
+        log$w.info(`[Engine] clearBreathPoints: ${e}`);
+        for (const [t,r] of this.breathPoints.entries())
+            (r._type == e || r._id == e) && this.delete(r._id)
+    }
+    clearBreathPointsBySkinInfo(e) {
+        log$w.info(`[Engine] clearBreathPointsBySkinInfo: ${e}`);
+        for (const [t,r] of this.breathPoints.entries())
+            r.skinInfo == e && this.delete(r._id)
+    }
+    clearAllBreathPoints() {
+        log$w.info("[Engine] ClearAllBreathPoints");
+        for (const [e,t] of this.breathPoints.entries())
+            this.delete(t._id)
+    }
+    _getMainPlayerPosition() {
+        var r;
+        const e = this._sceneManager.cameraComponent.MainCamera.position
+          , t = this._sceneManager.avatarComponent.getMainAvatar();
+        if (t != null && t != null) {
+            const n = (r = t == null ? void 0 : t.rootNode) == null ? void 0 : r.position;
+            if (n != null)
+                return n
+        }
+        return e
+    }
+    changeBreathPointPose(e, t, r) {
+        const n = new Vector3(e.position.x,e.position.y,e.position.z);
+        if (this.breathPoints.get(r) != null) {
+            log$w.info(`[Engine] changeBreathPointPose, id:${r}`);
+            const o = this.breathPoints.get(r)
+              , a = o.mesh.position;
+            let s = a.subtract(n);
+            s = Vector3.Normalize(s);
+            const l = Vector3.Distance(a, n)
+              , u = new Ray(n,s,l)
+              , c = this._scene.multiPickWithRay(u);
+            if (c) {
+                for (let h = 0; h < c.length; h++)
+                    if (c[h].pickedMesh != null && t.mesh.name.indexOf(c[h].pickedMesh.name) >= 0) {
+                        const f = c[h].pickedPoint;
+                        o.mesh.position = n.add(f.subtract(n).scale(.99)),
+                        this.breathPoints.set(r, o)
+                    }
+            }
+        } else
+            log$w.warn(`[Engine] changeBreathPointPose, id:${r} is not existing!`)
+    }
+}

+ 280 - 0
XCameraComponent.js

@@ -0,0 +1,280 @@
+class XCameraComponent {
+    constructor(e, t, r) {
+        E(this, "maincameraRotLimitObserver", null);
+        E(this, "mainCamera");
+        E(this, "cgCamera");
+        E(this, "saveCameraPose");
+        E(this, "_cameraPose");
+        E(this, "scene");
+        E(this, "canvas");
+        E(this, "yuvInfo");
+        E(this, "forceKeepVertical", !1);
+        E(this, "initCamera", e=>{
+            const {maxZ: t=1e4, minZ: r=.1, angularSensibility: n=2e3} = e;
+            this.mainCamera = new FreeCamera("camera_main",new Vector3(0,1e3,0),this.scene),
+            this.mainCamera.mode = Camera$1.PERSPECTIVE_CAMERA,
+            this.mainCamera.speed = .1,
+            this.mainCamera.angularSensibility = n,
+            this.mainCamera.setTarget(new Vector3(0,1010,0)),
+            this.mainCamera.minZ = r,
+            this.mainCamera.fov = Math.PI * this.yuvInfo.fov / 180,
+            this.mainCamera.maxZ = t,
+            this.mainCamera.fovMode = Camera$1.FOVMODE_HORIZONTAL_FIXED,
+            this.cgCamera = new FreeCamera("camera_temp",new Vector3(0,1e3,0),this.scene),
+            this.cgCamera.mode = Camera$1.PERSPECTIVE_CAMERA,
+            this.cgCamera.speed = .1,
+            this.cgCamera.setTarget(new Vector3(0,1010,0)),
+            this.cgCamera.maxZ = t,
+            this.cgCamera.minZ = r,
+            this.cgCamera.fovMode = Camera$1.FOVMODE_HORIZONTAL_FIXED,
+            this.cameraFovChange(this.yuvInfo)
+        }
+        );
+        E(this, "cameraFovChange", e=>{
+            this.yuvInfo = e;
+            const t = e.width
+              , r = e.height
+              , n = this.canvas.width
+              , o = this.canvas.height
+              , a = e.fov;
+            if (this.forceKeepVertical == !0) {
+                const s = t / (2 * Math.tan(Math.PI * a / 360))
+                  , l = 2 * Math.atan(r / (2 * s));
+                this.mainCamera.fov = l,
+                this.cgCamera.fov = l,
+                this.mainCamera.fovMode = Camera$1.FOVMODE_VERTICAL_FIXED,
+                this.cgCamera.fovMode = Camera$1.FOVMODE_VERTICAL_FIXED
+            } else if (this.mainCamera.fovMode = Camera$1.FOVMODE_HORIZONTAL_FIXED,
+            this.cgCamera.fovMode = Camera$1.FOVMODE_HORIZONTAL_FIXED,
+            n / o < t / r && this.mainCamera.fov) {
+                const s = o
+                  , l = n
+                  , u = s * t / r / (2 * Math.tan(a * Math.PI / 360))
+                  , c = 2 * Math.atan(l / (2 * u));
+                this.mainCamera.fov = c,
+                this.cgCamera.fov = c
+            } else
+                this.mainCamera.fov = Math.PI * a / 180,
+                this.cgCamera.fov = Math.PI * a / 180
+        }
+        );
+        E(this, "setCameraPose", e=>{
+            var n;
+            const t = ue4Position2Xverse(e.position);
+            let r = null;
+            e.rotation != null && (r = ue4Rotation2Xverse(e.rotation)),
+            this._cameraPose = {
+                position: t
+            },
+            r != null && (this._cameraPose.rotation = r),
+            this.scene.activeCamera === this.mainCamera && !((n = this.mainCamera) != null && n.isDisposed()) && this._setCamPositionRotation(this.mainCamera, this._cameraPose)
+        }
+        );
+        E(this, "_setCamPositionRotation", (e,t)=>{
+            var r, n;
+            t.position && (e.position = (r = t.position) == null ? void 0 : r.clone()),
+            t.rotation && (e.rotation = (n = t.rotation) == null ? void 0 : n.clone())
+        }
+        );
+        E(this, "switchCamera", e=>{
+            var t;
+            (t = this.scene.activeCamera) == null || t.detachControl(this.canvas),
+            this.scene.activeCamera = e
+        }
+        );
+        E(this, "reCalXYZRot", (e,t)=>(e = e % (2 * Math.PI),
+        Math.abs(t - e) >= Math.PI && (e = e - 2 * Math.PI),
+        e));
+        E(this, "_moveCam", (e,t,r,n,o,a,s,l)=>{
+            const u = (v,y,b)=>(v.x = this.reCalXYZRot(v.x, y.x),
+            v.y = this.reCalXYZRot(v.y, y.y),
+            v.z = this.reCalXYZRot(v.z, y.z),
+            new Vector3((y.x - v.x) * b + v.x,(y.y - v.y) * b + v.y,(y.z - v.z) * b + v.z))
+              , c = function(v, y, b) {
+                return new Vector3((y.x - v.x) * b + v.x,(y.y - v.y) * b + v.y,(y.z - v.z) * b + v.z)
+            }
+              , h = new Animation("myAnimation1","position",s,Animation.ANIMATIONTYPE_VECTOR3,Animation.ANIMATIONLOOPMODE_CONSTANT);
+            let f = []
+              , d = t
+              , _ = r;
+            for (let v = 0; v < a; ++v)
+                f.push({
+                    frame: v,
+                    value: c(d, _, v / a)
+                });
+            f.push({
+                frame: f.length,
+                value: c(d, _, 1)
+            }),
+            h.setKeys(f);
+            const g = new Animation("myAnimation2","rotation",s,Animation.ANIMATIONTYPE_VECTOR3,Animation.ANIMATIONLOOPMODE_CONSTANT);
+            f = [],
+            d = n,
+            _ = o;
+            for (let v = 0; v < a; ++v)
+                f.push({
+                    frame: v,
+                    value: u(d, _, v / a)
+                });
+            f.push({
+                frame: f.length,
+                value: u(d, _, 1)
+            }),
+            g.setKeys(f),
+            e.animations.push(g),
+            e.animations.push(h);
+            const m = this.scene.beginAnimation(e, 0, a, !1);
+            m.onAnimationEnd = ()=>{
+                l(),
+                m.stop(),
+                m.animationStarted = !1
+            }
+        }
+        );
+        this.scene = t,
+        this.canvas = e,
+        this.yuvInfo = r.yuvInfo,
+        r.forceKeepVertical != null && (this.forceKeepVertical = r.forceKeepVertical),
+        this.initCamera(r.cameraParam)
+    }
+    get MainCamera() {
+        return this.mainCamera
+    }
+    get CgCamera() {
+        return this.cgCamera
+    }
+    getCameraHorizonFov() {
+        return this.mainCamera.fovMode == Camera$1.FOVMODE_HORIZONTAL_FIXED ? this.mainCamera.fov : Math.PI * this.yuvInfo.fov / 180
+    }
+    changeMainCameraRotationDamping(e=2e3) {
+        this.mainCamera.angularSensibility = e
+    }
+    removeMainCameraRotationLimit() {
+        this.maincameraRotLimitObserver != null && this.mainCamera.onAfterCheckInputsObservable.remove(this.maincameraRotLimitObserver)
+    }
+    setMainCameraInfo(e) {
+        const {maxZ: t=1e4, minZ: r=.1, angularSensibility: n=2e3} = e;
+        this.mainCamera.maxZ = t,
+        this.mainCamera.minZ = r,
+        this.mainCamera.angularSensibility = n
+    }
+    getMainCameraInfo() {
+        return {
+            maxZ: this.mainCamera.maxZ,
+            minZ: this.mainCamera.minZ,
+            angularSensibility: this.mainCamera.angularSensibility
+        }
+    }
+    _limitAngle(e, t) {
+        return Math.abs(Math.abs(t[0] - t[1]) - 360) < 1e-6 || (e = (e % 360 + 360) % 360,
+        t[0] = (t[0] % 360 + 360) % 360,
+        t[1] = (t[1] % 360 + 360) % 360,
+        t[0] > t[1] ? e > t[1] && e < t[0] && (Math.abs(e - t[0]) < Math.abs(e - t[1]) ? e = t[0] : e = t[1]) : e < t[0] ? e = t[0] : e > t[1] && (e = t[1])),
+        e
+    }
+    setMainCameraRotationLimit(e, t) {
+        this.maincameraRotLimitObserver != null && this.removeMainCameraRotationLimit();
+        const r = this.mainCamera
+          , {yaw: n, pitch: o, roll: a} = e
+          , {yaw: s, pitch: l, roll: u} = t;
+        if (s < 0 || l < 0 || u < 0)
+            throw new Error("\u76F8\u673A\u65CB\u8F6C\u9650\u5236\u53EA\u80FD\u8BBE\u7F6E\u4E3A\u5927\u4E8E0");
+        const c = [o - l, o + l]
+          , h = [n - s, n + s]
+          , f = [a - u, a + u];
+        this.maincameraRotLimitObserver = r.onAfterCheckInputsObservable.add(()=>{
+            let {pitch: d, yaw: _, roll: g} = xverseRotation2Ue4(r.rotation);
+            d = this._limitAngle(d, c),
+            _ = this._limitAngle(_, h),
+            g = this._limitAngle(g, f),
+            r.rotation = ue4Rotation2Xverse({
+                pitch: d,
+                yaw: _,
+                roll: g
+            })
+        }
+        )
+    }
+    setMainCameraRotationLimitByAnchor(e, t, r) {
+        this.maincameraRotLimitObserver != null && this.removeMainCameraRotationLimit();
+        const n = this.mainCamera
+          , o = ue4Rotation2Xverse_mesh(t)
+          , a = ue4Rotation2Xverse_mesh(r);
+        a != null && o != null && e.mesh != null && (this.maincameraRotLimitObserver = n.onAfterCheckInputsObservable.add(()=>{
+            const s = e.mesh.rotation;
+            r.yaw > 0 && (n.rotation.y <= s.y - a.y + o.y ? n.rotation.y = s.y - a.y + o.y : n.rotation.y >= s.y + a.y + o.y && (n.rotation.y = s.y + a.y + o.y)),
+            r.pitch > 0 && (n.rotation.x <= s.x - a.x + o.x ? n.rotation.x = s.x - a.x + o.x : n.rotation.x >= s.x + a.x + o.x && (n.rotation.x = s.x + a.x + o.x)),
+            r.roll > 0 && (n.rotation.z <= s.z - a.z + o.z ? n.rotation.z = s.z - a.z + o.z : n.rotation.z >= s.z + a.z + o.z && (n.rotation.z = s.z + a.z + o.z))
+        }
+        ))
+    }
+    getCameraPose() {
+        const e = xversePosition2Ue4({
+            x: this.mainCamera.position.x,
+            y: this.mainCamera.position.y,
+            z: this.mainCamera.position.z
+        })
+          , t = xverseRotation2Ue4({
+            x: this.mainCamera.rotation.x,
+            y: this.mainCamera.rotation.y,
+            z: this.mainCamera.rotation.z
+        });
+        return {
+            position: e,
+            rotation: t
+        }
+    }
+    changeCameraFov(e, t) {
+        this.mainCamera.fov = e,
+        t != null && (this.mainCamera.fovMode = t == 0 ? Camera$1.FOVMODE_HORIZONTAL_FIXED : Camera$1.FOVMODE_VERTICAL_FIXED)
+    }
+    controlCameraRotation(e, t, r=.5, n=.5) {
+        const o = {
+            pitch: n * t * 180,
+            yaw: r * e * 180,
+            roll: 0
+        };
+        this.addRot(o)
+    }
+    addRot(e) {
+        const t = this.mainCamera
+          , r = ue4Rotation2Xverse_mesh(e);
+        r != null && t.rotation.addInPlace(r)
+    }
+    getCameraFov() {
+        return this.mainCamera.fov
+    }
+    allowMainCameraController() {
+        this.mainCamera.attachControl(this.canvas, !0)
+    }
+    detachMainCameraController() {
+        this.mainCamera.detachControl(this.canvas)
+    }
+    forceChangeSavedCameraPose(e) {
+        this.saveCameraPose != null && (e.position != null && (this.saveCameraPose.position = ue4Position2Xverse(e.position)),
+        e.rotation != null && (this.saveCameraPose.rotation = ue4Rotation2Xverse(e.rotation)))
+    }
+    changeToFirstPersonView(e) {
+        this.saveCameraPose = {
+            position: this.mainCamera.position.clone(),
+            rotation: this.mainCamera.rotation.clone()
+        },
+        this.mainCamera.attachControl(this.canvas, !0),
+        e.position != null && (this.mainCamera.position = ue4Position2Xverse(e.position)),
+        e.rotation != null && (this.mainCamera.rotation = ue4Rotation2Xverse(e.rotation))
+    }
+    changeToThirdPersonView() {
+        this.saveCameraPose != null && this.mainCamera != null && (this.mainCamera.position = this.saveCameraPose.position.clone(),
+        this.mainCamera.rotation = this.saveCameraPose.rotation.clone(),
+        this.mainCamera.detachControl(this.canvas))
+    }
+    switchToMainCamera() {
+        this.switchCamera(this.mainCamera)
+    }
+    switchToCgCamera() {
+        this.switchCamera(this.cgCamera)
+    }
+    moveMainCamera(e, t, r, n, o) {
+        this._moveCam(this.mainCamera, this.mainCamera.position, e, this.mainCamera.rotation, t, r, n, o)
+    }
+}

+ 102 - 0
XDecal.js

@@ -0,0 +1,102 @@
+class XDecal {
+    constructor(e) {
+        E(this, "_id");
+        E(this, "meshPath");
+        E(this, "_low_model", []);
+        E(this, "_mat", null);
+        E(this, "scene");
+        E(this, "_skinInfo");
+        E(this, "sourceMatId", "");
+        E(this, "loadModel", async()=>new Promise((e,t)=>{
+            typeof this.meshPath == "string" ? SceneLoader.LoadAssetContainerAsync("", this.meshPath, this.scene, null, ".glb").then(r=>{
+                for (let n = r.materials.length - 1; n >= 0; --n)
+                    r.materials[n].dispose();
+                for (let n = 0; n < r.meshes.length; ++n)
+                    r.meshes[n].visibility = 1,
+                    r.meshes[n].isPickable = !0,
+                    r.meshes[n].checkCollisions = !1,
+                    "hasVertexAlpha"in r.meshes[n] && (r.meshes[n].hasVertexAlpha = !1),
+                    this.scene.addMesh(r.meshes[n]),
+                    this._low_model.push(new XStaticMesh({
+                        id: this._id,
+                        mesh: r.meshes[n],
+                        xtype: EMeshType.Decal,
+                        skinInfo: this._skinInfo
+                    })),
+                    this.toggle(!1);
+                e(!0)
+            }
+            ).catch(r=>{
+                t(new XDecalError(`[Engine] decal load model error! ${r}`))
+            }
+            ) : t(new XDecalError("[Engine] decal inport mesh is not string!"))
+        }
+        ).catch(e=>{
+            new XDecalError(`[Engine] decal loadModel ${e}`)
+        }
+        ));
+        const {id: t, scene: r, meshPath: n, skinInfo: o="default"} = e;
+        this._id = t,
+        this.scene = r,
+        this.meshPath = n,
+        this._skinInfo = o
+    }
+    get skinInfo() {
+        return this._skinInfo
+    }
+    getMesh() {
+        return this._low_model
+    }
+    getMat() {
+        return this._mat
+    }
+    get id() {
+        return this._id
+    }
+    toggle(e) {
+        for (let t = 0; t < this._low_model.length; ++t)
+            e == !0 ? this._low_model[t].show() : this._low_model[t].hide()
+    }
+    setMat(e) {
+        this._mat = e;
+        for (let t = 0; t < this._low_model.length; ++t)
+            this._low_model[t].mesh.material = this._mat;
+        this.toggle(!0)
+    }
+    changeModel(e="") {
+        return e != "" && (this.meshPath = e),
+        this.meshPath == "" ? (log$x.error("[Engine] changeModel Error! meshPath is empty"),
+        Promise.reject(new XDecalTextureError("[Engine] changeModel Error! meshPath is empty"))) : new Promise((t,r)=>SceneLoader.LoadAssetContainerAsync("", this.meshPath, this.scene, null, ".glb").then(n=>{
+            for (let a = n.materials.length - 1; a >= 0; --a)
+                n.materials[a].dispose();
+            const o = [];
+            for (let a = 0; a < n.meshes.length; ++a)
+                n.meshes[a].visibility = 0,
+                n.meshes[a].isPickable = !0,
+                n.meshes[a].checkCollisions = !1,
+                "hasVertexAlpha"in n.meshes[a] && (n.meshes[a].hasVertexAlpha = !1),
+                this._mat != null && (n.meshes[a].material = this._mat),
+                this.scene.addMesh(n.meshes[a]),
+                o.push(new XStaticMesh({
+                    id: this._id,
+                    mesh: n.meshes[a],
+                    xtype: EMeshType.Decal,
+                    skinInfo: this._skinInfo
+                }));
+            e != "" && this.cleanMesh(),
+            this._low_model = o,
+            this._mat != null && this.toggle(!0),
+            t(this)
+        }
+        ).catch(n=>{
+            log$x.error("[Engine] Create decal error! " + n),
+            r(new XDecalError("[Engine] Create decal error! " + n))
+        }
+        ))
+    }
+    cleanMesh(e=!1, t=!1) {
+        log$x.info("[Engine] Decal Model clean mesh");
+        for (let r = 0; r < this._low_model.length; ++r)
+            this._low_model[r].dispose(e, t)
+    }
+}

+ 230 - 0
XDecalManager.js

@@ -0,0 +1,230 @@
+class XDecalManager {
+    constructor(e) {
+        E(this, "scene");
+        E(this, "_decal");
+        E(this, "_mat");
+        E(this, "_sharedMat");
+        E(this, "_scenemanager");
+        this._decal = new Map,
+        this._mat = new Map,
+        this._sharedMat = new Map,
+        this._scenemanager = e,
+        this.scene = e.Scene
+    }
+    get decals() {
+        return Array.from(this._decal.values())
+    }
+    getMesh() {
+        return this._decal
+    }
+    async addDecal(e) {
+        const {id: t, meshPath: r, skinInfo: n="default"} = e;
+        return this._decal.get(t) ? (log$x.warn(`[Engine] Cannot add decal with an existing id: [${t}], meshPath: ${r}, skinInfo:${n}`),
+        Promise.resolve(!0)) : (log$x.info(`[Engine] addDecal wiht id:[${t}], meshPath: ${r}, skinInfo:${n}`),
+        new Promise((o,a)=>this._scenemanager.urlTransformer(r).then(s=>new Promise((l,u)=>{
+            if (this._decal.get(t))
+                l(!0);
+            else {
+                const c = new XDecal({
+                    id: t,
+                    scene: this.scene,
+                    meshPath: s,
+                    skinInfo: n
+                });
+                this._decal.set(t, c),
+                c.loadModel().then(()=>{
+                    l(!0)
+                }
+                ).catch(h=>{
+                    log$x.error(`[Engine] addDecal Error! id: [${t}], meshpath:${r}, skin: ${n}. ${h}`),
+                    u(new XDecalError(`[Engine] addDecal Error! id: [${t}], meshpath:${r}, skin: ${n}. ${h}`))
+                }
+                )
+            }
+        }
+        )).then(s=>{
+            s == !0 ? o(!0) : a(!1)
+        }
+        ).catch(s=>{
+            log$x.error(`[Engine] Add Decal error! id: [${t}], meshpath:${r}, skin:${n}. ${s}`),
+            a(new XDecalError(`[Engine] addDecal  error! id: [${t}], meshpath:${r}, skin:${n}. ${s}`))
+        }
+        )))
+    }
+    setDecalTexture(e) {
+        const {id: t, buffer: r, isDynamic: n=!1, width: o=1100, height: a=25, slots: s=1, visibleSlots: l=1} = e
+          , u = !0;
+        return log$x.info(`[Engine] setDecalTexture wiht id:[${t}]`),
+        new Promise((c,h)=>{
+            const f = this._decal.get(t);
+            if (f != null)
+                if (this._mat.get(t) != null)
+                    this.changeDecalTexture({
+                        id: t,
+                        buffer: r,
+                        isUrl: u,
+                        isDynamic: n,
+                        width: o,
+                        height: a,
+                        slots: s,
+                        visibleSlots: l
+                    }),
+                    c(!0);
+                else {
+                    const d = new XDecalMaterial(t,this.scene);
+                    d.setTexture(r, u, n, o, a, s, l).then(()=>{
+                        f.setMat(d.getMat()),
+                        this._decal.set(t, f),
+                        this._mat.set(t, d),
+                        c(!0)
+                    }
+                    ).catch(_=>{
+                        log$x.error("[Engine] setDecalTexture Error! " + _),
+                        h(new XDecalTextureError(`[Engine] decal set texture error! ${_}`))
+                    }
+                    )
+                }
+            else
+                log$x.error("[Engine] Error! decal id: [" + t + "] is not find!"),
+                h(new XDecalTextureError(`[Engine] decal id: [${t}] is not find!`))
+        }
+        )
+    }
+    async shareDecal(e) {
+        const {idTar: t, meshPath: r, idSrc: n, skinInfo: o="default"} = e;
+        return this._decal.has(n) && !this._decal.has(t) && this._mat.has(n) && !this._mat.has(t) ? (log$x.info(`[Engine] shareDecal wiht idTar:[${t}], idSrc:[${n}], skinInfo: ${o}, meshPath: ${r}`),
+        new Promise((a,s)=>this._scenemanager.urlTransformer(r).then(l=>{
+            const u = new XDecal({
+                id: t,
+                scene: this.scene,
+                meshPath: l,
+                skinInfo: o
+            })
+              , c = this._mat.get(n);
+            c != null && (u.setMat(c.getMat()),
+            u.sourceMatId = n,
+            this._decal.set(t, u),
+            this.addSharedMatCount(n)),
+            a(!0)
+        }
+        ).catch(l=>{
+            s(new XDecalError(`[Engine] decal shareDecal error! ${l}`))
+        }
+        ))) : (log$x.error(`[Engine] shareDecal Error. idSrc: [${n}] not exist! or idTar: [${t}] exists!`),
+        Promise.reject(`[Engine] shareDecal Error. idSrc: [${n}] not exist! or idTar: [${t}] exists!`))
+    }
+    async changeDecalModel(e) {
+        const {id: t, meshPath: r} = e
+          , n = this._decal.get(t);
+        return new Promise((o,a)=>n != null ? (log$x.info(`[Engine] changeDecalModel id:${t}`),
+        n.changeModel(r).then(()=>{
+            this._decal.set(t, n),
+            o(!0)
+        }
+        )) : (log$x.warn(`[Engine] changeDecalModel id:${t} is not exist`),
+        a(`[Engine] changeDecalModel id:${t} is not exist`)))
+    }
+    changeDecalTexture(e) {
+        const {id: t, buffer: r, isUrl: n=!1, isDynamic: o=!1, width: a=1110, height: s=25, slots: l=1, visibleSlots: u=1} = e
+          , c = this._mat.get(t);
+        c != null && this._decal.has(t) ? (c.changeTexture(r, n, o, a, s, l, u),
+        this._mat.set(t, c)) : log$x.error(`[Engine] changeDecalTexture Error. id:${t} is not exist`)
+    }
+    deleteDecal(e) {
+        var t, r;
+        if (this._decal.has(e)) {
+            const n = this._decal.get(e);
+            n != null && n.cleanMesh(),
+            this._sharedMat.get(e) != null ? this.minusSharedMatCount(e) : this._mat.get(e) != null ? ((t = this._mat.get(e)) == null || t.cleanTexture(),
+            this._mat.delete(e)) : ((r = n.sourceMatId) == null ? void 0 : r.length) > 0 && this.minusSharedMatCount(n.sourceMatId),
+            this._decal.delete(e)
+        }
+    }
+    deleteDecalBySkinInfo(e) {
+        for (const [t,r] of this._decal.entries())
+            r.skinInfo == e && this.deleteDecal(t)
+    }
+    addSharedMatCount(e) {
+        const t = this._sharedMat.get(e);
+        t != null ? this._sharedMat.set(e, t + 1) : this._sharedMat.set(e, 1)
+    }
+    minusSharedMatCount(e) {
+        var r;
+        const t = this._sharedMat.get(e);
+        t != null && (this._sharedMat.set(e, t - 1),
+        t == 0 && (this._sharedMat.delete(e),
+        (r = this._mat.get(e)) == null || r.cleanTexture(),
+        this._mat.delete(e)))
+    }
+    toggle(e, t) {
+        const r = this._decal.get(e);
+        r == null || r.toggle(t)
+    }
+    toggleDecalBySkinInfo(e, t) {
+        for (const [r,n] of this._decal.entries())
+            n.skinInfo == e && n.toggle(t)
+    }
+    updateTexAsWords(e, t, r={}) {
+        const {clearArea: n=!0, w: o=480, h: a=480, y: s=a / 2, fontsize: l=70, slots: u=1, visibleSlots: c=1, font: h="black-body", color: f="white", fontweight: d=100} = r;
+        let {x: _=o / 2} = r;
+        const g = this._mat.get(e);
+        if (g) {
+            _ == -1 && (_ = (g.getUOffset() + c / u) % 1 * o * u);
+            const v = g.getMat().diffuseTexture
+              , y = v.getContext();
+            n && y.clearRect(_ - o / 2, s - a / 2, o, a),
+            y.textAlign = "center",
+            y.textBaseline = "middle",
+            v.drawText(t, _, s, d + " " + l + "px " + h, f, "transparent", !0),
+            v.hasAlpha = !0,
+            v.update()
+        }
+    }
+    async updateTexAsImg(e, t, r={}) {
+        const {clearArea: n=!0, w: o=480, h: a=480, x: s=o / 2, y: l=a / 2, clearW: u=o, clearH: c=a} = r;
+        return t == null || t == null || t == "" ? (log$x.error(`[Engine] updateTexAsImg Error. id: [${e}], newBuffer is Null or ""!`),
+        Promise.reject(new XDecalError(`[Engine] updateTexAsImg Error. id: [${e}], newBuffer is Null or ""!`))) : new Promise((h,f)=>this._scenemanager.urlTransformer(t).then(d=>new Promise((_,g)=>{
+            const m = this._mat.get(e);
+            if (m) {
+                const y = m.getMat().diffuseTexture;
+                if (typeof t == "string") {
+                    const b = new Image;
+                    b.crossOrigin = "anonymous",
+                    b.src = d,
+                    b.onload = ()=>{
+                        const T = y.getContext();
+                        n && T.clearRect(s - u / 2, l - c / 2, u, c),
+                        T.drawImage(b, s - o / 2, l - a / 2, o, a),
+                        y.update(),
+                        _(!0)
+                    }
+                    ,
+                    b.onerror = ()=>{
+                        log$x.error(`[Engine] updateTexAsImg Error.newImg load error. id: [${e}], decalMat is Null or undefined!`),
+                        g(new XDecalError(`[Engine] updateTexAsImg Error. id: [${e}], decalMat is Null or undefined!`))
+                    }
+                } else
+                    log$x.error(`[Engine] updateTexAsImg Error. id: [${e}], Buffer is not string!`),
+                    g(new XDecalError(`[Engine] updateTexAsImg Error. id: [${e}], Buffer is not string!`))
+            } else
+                log$x.error(`[Engine] updateTexAsImg Error. id: [${e}], decalMat is Null or undefined!`),
+                g(new XDecalError(`[Engine] updateTexAsImg Error. id: [${e}], decalMat is Null or undefined!`))
+        }
+        ).then(_=>{
+            _ == !0 ? h(!0) : (log$x.error(`[Engine] updateTexAsImg Error. id: [${e}] !`),
+            f(new XDecalError(`[Engine] updateTexAsImg error! id: [${e}]`)))
+        }
+        ).catch(_=>{
+            log$x.error(`[Engine] updateTexAsImg Error. id: [${e}]. ${_}`)
+        }
+        )))
+    }
+    startAnime(e, t) {
+        log$x.info(`[Engine] Decal Start Anime. [${e}]`);
+        const {speed: r=.001, callback: n} = t
+          , o = this._mat.get(e);
+        o ? (o.do_animation(r),
+        n && o.uOffsetObserverable.add(n)) : (log$x.error(`[Engine] startAnime Error. id: [${e}] is not exist!`),
+        new XDecalError(`[Engine] startAnime Error. id: [${e}] is not exist!`))
+    }
+}

+ 108 - 0
XDecalMaterial.js

@@ -0,0 +1,108 @@
+class XDecalMaterial {
+    constructor(e, t) {
+        E(this, "_id");
+        E(this, "_tex");
+        E(this, "scene");
+        E(this, "_mat");
+        E(this, "_speed", .001);
+        E(this, "_slots", 1);
+        E(this, "_visibleSlots", 1);
+        E(this, "_isRegisterAnimation");
+        E(this, "_animeObserver", null);
+        E(this, "_uOffsetObserverable");
+        E(this, "reg_mat_update", ()=>{
+            const e = this._mat.diffuseTexture;
+            e != null && (e.uOffset = e.uOffset + this._speed,
+            e.uOffset > 1 && (e.uOffset -= 1),
+            Math.round(e.uOffset % (1 / this._slots) / this._speed) == 0 && this._uOffsetObserverable.notifyObservers(this))
+        }
+        );
+        E(this, "setTexture", async(e,t=!0,r=!1,n=1,o=1,a=1,s=1)=>new Promise((l,u)=>{
+            this._slots = a,
+            this._visibleSlots = s;
+            const c = this._tex;
+            r ? (this._tex = new DynamicTexture("dyTex",{
+                width: n,
+                height: o
+            },this.scene,!0,Texture.BILINEAR_SAMPLINGMODE),
+            this._tex.name = "decal_dy_" + this._id,
+            this._tex.uScale = s / a,
+            this._tex.vScale = -1,
+            this._tex.vOffset = 1,
+            this._tex.wrapU = 1,
+            this._mat.emissiveColor = new Color3(.95,.95,.95),
+            this._mat.diffuseTexture = this._tex,
+            this._mat.diffuseTexture.hasAlpha = !0,
+            this._mat.useAlphaFromDiffuseTexture = !0,
+            this._mat.backFaceCulling = !1,
+            this._mat.transparencyMode = Material.MATERIAL_ALPHATEST,
+            c != null && c.dispose(),
+            l(!0)) : !r && t && typeof e == "string" ? this._tex = new Texture(e,this.scene,!0,!1,Texture.BILINEAR_SAMPLINGMODE,()=>{
+                this._tex.name = "decal_" + this._id,
+                this._mat.emissiveTexture = this._tex,
+                this._mat.diffuseTexture = this._tex,
+                this._mat.diffuseTexture.hasAlpha = !0,
+                this._mat.useAlphaFromDiffuseTexture = !0,
+                this._mat.transparencyMode = Material.MATERIAL_ALPHATEST,
+                c != null && c.dispose(),
+                l(!0)
+            }
+            ,()=>{
+                log$x.error("[Engine] decal create texture error!"),
+                u(new XDecalTextureError("[Engine] decal create texture error!"))
+            }
+            ,null,!0) : this._tex = new Texture("data:decal_" + this._id,this.scene,!0,!1,Texture.BILINEAR_SAMPLINGMODE,()=>{
+                this._tex.name = "decal_" + this._id,
+                this._mat.emissiveTexture = this._tex,
+                this._mat.diffuseTexture = this._tex,
+                this._mat.diffuseTexture.hasAlpha = !0,
+                this._mat.useAlphaFromDiffuseTexture = !0,
+                this._mat.transparencyMode = Material.MATERIAL_ALPHATEST,
+                c != null && c.dispose(),
+                l(!0)
+            }
+            ,()=>{
+                log$x.error("[Engine] decal create texture error!"),
+                u(new XDecalTextureError("[Engine] decal create texture error!"))
+            }
+            ,e,!0)
+        }
+        ));
+        this._id = e,
+        this.scene = t,
+        this._mat = new StandardMaterial("decalMat_" + this._id,this.scene),
+        this._isRegisterAnimation = !1,
+        this._uOffsetObserverable = new Observable
+    }
+    get uOffsetObserverable() {
+        return this._uOffsetObserverable
+    }
+    getMat() {
+        return this._mat
+    }
+    set speed(e) {
+        this._speed = e
+    }
+    getUOffset() {
+        return this._tex.uOffset
+    }
+    do_animation(e) {
+        this._speed = e,
+        this._isRegisterAnimation == !1 && (this._isRegisterAnimation = !0,
+        this._animeObserver = this.scene.onBeforeRenderObservable.add(()=>{
+            this.reg_mat_update()
+        }
+        ))
+    }
+    changeTexture(e, t=!1, r=!1, n=1, o=1, a=1, s=1) {
+        return this._mat == null || this._tex == null ? (log$x.error("[Engine] Decal Mat is null or tex is null"),
+        Promise.reject(new XDecalTextureError("[Engine] Decal Mat is null or tex is null"))) : this.setTexture(e, t, r, n, o, a, s)
+    }
+    cleanTexture() {
+        log$x.info("[Engine] Decal clean Texture"),
+        this.scene.onBeforeRenderObservable.remove(this._animeObserver),
+        this._uOffsetObserverable.clear(),
+        this._tex.dispose(),
+        this._mat.dispose()
+    }
+}

+ 7 - 0
XEngineRunTimeStats.js

@@ -0,0 +1,7 @@
+class XEngineRunTimeStats {
+    constructor() {
+        E(this, "timeArray_loadStaticMesh", new RunTimeArray);
+        E(this, "timeArray_updateStaticMesh", new RunTimeArray);
+        E(this, "timeArray_addAvatarToScene", new RunTimeArray)
+    }
+}

+ 125 - 0
XLightManager.js

@@ -0,0 +1,125 @@
+const log$C = new Logger$1("XLightManager");
+class XLightManager {
+    constructor(e) {
+        E(this, "_scene");
+        E(this, "_envTexture");
+        E(this, "_shadowLight");
+        E(this, "_shadowGenerator");
+        E(this, "_avatarShadowMeshMap");
+        E(this, "_cullingShadowObservers");
+        E(this, "sceneManager");
+        this.sceneManager = e,
+        this._scene = this.sceneManager.Scene,
+        this._envTexture = null,
+        this.shadowLean = .1;
+        const t = new Vector3(this.shadowLean,-1,0)
+          , r = 1024;
+        this._shadowLight = new DirectionalLight("AvatarLight",t,this._scene),
+        this._shadowLight.shadowMaxZ = 5e3,
+        this._shadowLight.intensity = 0,
+        this.attachLightToCamera(this._shadowLight),
+        this._shadowGenerator = new ShadowGenerator(r,this._shadowLight,!0),
+        this._avatarShadowMeshMap = new Map,
+        this._cullingShadowObservers = new Map
+    }
+    set shadowLean(e) {
+        e = Math.min(e, 1),
+        e = Math.max(e, -1),
+        this._shadowLight && (this._shadowLight.direction = new Vector3(e,-1,0))
+    }
+    setIBL(e) {
+        return new Promise((t,r)=>{
+            this.sceneManager.urlTransformer(e).then(n=>{
+                var o;
+                if (n == ((o = this._envTexture) == null ? void 0 : o.url))
+                    return t("env set success");
+                this._envTexture != null && this.disposeIBL(),
+                this._envTexture = CubeTexture.CreateFromPrefilteredData(n, this._scene, ".env"),
+                this._scene.environmentTexture = this._envTexture,
+                this._envTexture.onLoadObservable.addOnce(()=>{
+                    t("env set success"),
+                    log$C.info("env set success")
+                }
+                )
+            }
+            ).catch(()=>{
+                r("env set fail")
+            }
+            )
+        }
+        )
+    }
+    disposeIBL() {
+        this._envTexture == null ? log$C.info("env not exist") : (this._envTexture.dispose(),
+        this._envTexture = null,
+        this._scene.environmentTexture = null,
+        log$C.info("env dispose success"))
+    }
+    removeShadow(e) {
+        var t;
+        if (this._avatarShadowMeshMap.has(e)) {
+            this._avatarShadowMeshMap.delete(e),
+            this._cullingShadowObservers.get(e) && (this._scene.onBeforeRenderObservable.remove(this._cullingShadowObservers.get(e)),
+            this._cullingShadowObservers.delete(e));
+            const r = e.rootNode;
+            r && ((t = this._shadowGenerator) == null || t.removeShadowCaster(r))
+        } else
+            return
+    }
+    setShadow(e) {
+        if (this._avatarShadowMeshMap.has(e))
+            return;
+        e.rootNode && this._avatarShadowMeshMap.set(e, e.rootNode.getChildMeshes());
+        const t = 20
+          , r = 10
+          , n = this.cullingShadow(t, r, e);
+        this._cullingShadowObservers.set(e, n)
+    }
+    cullingShadow(e, t, r) {
+        let n = 0;
+        const o = ()=>{
+            var s, l;
+            if (n == t) {
+                const u = this._avatarShadowMeshMap.get(r)
+                  , c = (s = r.rootNode) == null ? void 0 : s.getChildMeshes()
+                  , h = this._scene.activeCamera;
+                u == null || u.forEach(f=>{
+                    var d;
+                    (d = this._shadowGenerator) == null || d.removeShadowCaster(f, !1)
+                }
+                ),
+                c == null || c.forEach(f=>{
+                    var d;
+                    (d = this._shadowGenerator) == null || d.addShadowCaster(f, !1)
+                }
+                ),
+                h && r.rootNode && ((l = r.rootNode.position) == null ? void 0 : l.subtract(h.position).length()) > e && (c == null || c.forEach(f=>{
+                    var d;
+                    (d = this._shadowGenerator) == null || d.removeShadowCaster(f, !1)
+                }
+                )),
+                c && this._avatarShadowMeshMap.set(r, c),
+                n = 0
+            } else
+                n += 1
+        }
+        ;
+        return this._scene.onBeforeRenderObservable.add(o)
+    }
+    attachLightToCamera(e) {
+        const t = e
+          , r = 15
+          , n = ()=>{
+            const o = this._scene.activeCamera;
+            if (o) {
+                const a = t.direction
+                  , s = new Vector3(r * a.x,r * a.y,r * a.z)
+                  , l = o.position;
+                t.position = l.subtract(s)
+            }
+        }
+        ;
+        return t && this._scene.registerBeforeRender(n),
+        n
+    }
+}

+ 315 - 0
XMaterialComponent.js

@@ -0,0 +1,315 @@
+var EShaderMode = (i=>(i[i.default = 0] = "default",
+i[i.video = 1] = "video",
+i[i.videoAndPano = 2] = "videoAndPano",
+i))(EShaderMode || {});
+class XMaterialComponent {
+    constructor(e, t) {
+        E(this, "scene");
+        E(this, "engine");
+        E(this, "yuvInfo");
+        E(this, "shaderMode");
+        E(this, "_panoInfo");
+        E(this, "_dynamic_size");
+        E(this, "_dynamic_babylonpose");
+        E(this, "_dynamic_textures");
+        E(this, "_dynamic_shaders");
+        E(this, "_scenemanager");
+        E(this, "_videoTexture");
+        E(this, "_videoElement");
+        E(this, "_lowModelShader");
+        E(this, "_defaultShader");
+        E(this, "_inputYUV420", !0);
+        E(this, "_inputPanoYUV420", !0);
+        E(this, "_videoRawYUVTexArray");
+        E(this, "_isUpdateYUV", !0);
+        E(this, "initMaterial", async()=>new Promise((e,t)=>{
+            this._initDefaultShader(),
+            this.shaderMode == 2 ? this.initDynamicData(this._panoInfo.dynamicRange, this._panoInfo.width, this._panoInfo.height).then(()=>{
+                this._initPureVideoShader(),
+                this._prepareRender(this.yuvInfo)
+            }
+            ) : this.shaderMode == 1 ? (this._initPureVideoShader(),
+            this._prepareRender(this.yuvInfo)) : this.shaderMode == 0,
+            e(!0)
+        }
+        ));
+        E(this, "_initPureVideoContent", e=>{
+            this._inputYUV420 ? this._videoRawYUVTexArray.getVideoYUVTex(0) != null && (this._lowModelShader.setTexture("texture_video", this._videoRawYUVTexArray.getVideoYUVTex(0)),
+            this._lowModelShader.setFloat("isYUV", 1),
+            Texture.WhenAllReady([this._videoRawYUVTexArray.getVideoYUVTex(0)], ()=>{
+                this._changePureVideoLowModelShaderCanvasSize(e)
+            }
+            )) : (this._videoElement = e.videoElement,
+            this._videoTexture || (this._videoTexture = new VideoTexture("InterVideoTexture",this._videoElement,this.scene,!0,!1)),
+            Texture.WhenAllReady([this._videoTexture], ()=>{
+                this._changePureVideoLowModelShaderCanvasSize({
+                    width: this._videoElement.height,
+                    height: this._videoElement.width,
+                    fov: e.fov
+                })
+            }
+            ),
+            this._lowModelShader.setTexture("texture_video", this._videoTexture),
+            this._lowModelShader.setFloat("isYUV", 0))
+        }
+        );
+        E(this, "_changePureVideoLowModelShaderCanvasSize", e=>{
+            var a;
+            const t = e.fov || 50
+              , r = e.width || 720
+              , n = e.height || 1280
+              , o = r / (2 * Math.tan(Math.PI * t / 360));
+            (a = this._lowModelShader) == null || a.setVector3("focal_width_height", new Vector3(o,r,n))
+        }
+        );
+        E(this, "updateRawYUVData", (e,t,r,n=-1)=>{
+            var o, a;
+            if (n == -1 && (n = this.yuvInfo.fov),
+            this._isUpdateYUV == !0) {
+                const s = {
+                    width: t,
+                    height: r,
+                    fov: n
+                }
+                  , l = this._videoRawYUVTexArray.findId(t, r)
+                  , u = this._videoRawYUVTexArray.getCurrentVideoTexId();
+                (u < 0 || l != u || n != this.yuvInfo.fov) && (this.yuvInfo.width = t,
+                this.yuvInfo.height = r,
+                this.yuvInfo.fov = n,
+                this._videoRawYUVTexArray.setCurrentVideoTexId(l),
+                this._changeVideoRes(l),
+                this._changePureVideoLowModelShaderCanvasSize(s),
+                this._scenemanager.cameraComponent.cameraFovChange(s),
+                this._scenemanager.yuvInfo = s),
+                (o = this._videoRawYUVTexArray.getVideoYUVTex(l)) == null || o.update(e),
+                (a = this._videoRawYUVTexArray.getVideoYUVTex(l)) == null || a.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE)
+            }
+        }
+        );
+        E(this, "_changeVideoRes", e=>{
+            this._lowModelShader.setTexture("texture_video", this._videoRawYUVTexArray.getVideoYUVTex(e))
+        }
+        );
+        E(this, "initDynamicData", (e,t,r)=>new Promise((n,o)=>{
+            this.setDynamicSize(e).then(a=>{
+                if (a) {
+                    for (let s = 0; s < e; ++s)
+                        (l=>{
+                            this.initDynamicTexture(l, t, r),
+                            this.initDynamicShaders(l).then(()=>{
+                                this._updatePanoShaderInput(l)
+                            }
+                            )
+                        }
+                        )(s);
+                    n(!0)
+                } else
+                    o(new XMaterialError(`[Engine] DynamicRoomSize (${e}) is too small`))
+            }
+            )
+        }
+        ).catch(n=>log$v.error(`[Engine] ${n}`)));
+        E(this, "_initDefaultShader", ()=>{
+            this._defaultShader == null && (this._defaultShader = new GridMaterial("GridShader",this.scene),
+            this._defaultShader.gridRatio = 50,
+            this._defaultShader.lineColor = new Color3(0,0,.5),
+            this._defaultShader.majorUnitFrequency = 1,
+            this._defaultShader.mainColor = new Color3(.6,.6,.6),
+            this._defaultShader.backFaceCulling = !1)
+        }
+        );
+        E(this, "_initPureVideoShader", ()=>{
+            if (this._lowModelShader == null) {
+                const e = new ShaderMaterial("PureVideoShader",this.scene,{
+                    vertexSource: pureVideoVertex,
+                    fragmentSource: pureVideoFragment
+                },{
+                    attributes: ["uv", "position", "world0", "world1", "world2", "world3"],
+                    uniforms: ["view", "projection", "worldViewProjection", "world"],
+                    defines: ["#define SHADOWFULLFLOAT"]
+                });
+                e.setTexture("shadowSampler", null),
+                e.setMatrix("lightSpaceMatrix", null),
+                e.setFloat("haveShadowLight", 0),
+                e.setTexture("texture_video", null),
+                e.setFloat("isYUV", this._inputYUV420 ? 1 : 0),
+                e.setFloat("fireworkLight", 0),
+                e.setVector3("fireworkLightPosition", new Vector3(0,0,0)),
+                e.setVector3("focal_width_height", new Vector3(772.022491,720,1280)),
+                e.backFaceCulling = !1,
+                this._lowModelShader = e
+            }
+        }
+        );
+        E(this, "setDynamicSize", e=>new Promise((t,r)=>{
+            e >= 1 && e <= 100 ? (this._dynamic_size = e,
+            t(!0)) : (this._dynamic_size = 1,
+            t(!1))
+        }
+        ));
+        E(this, "_isInDynamicRange", e=>e < this._dynamic_size && e >= 0);
+        E(this, "initDynamicTexture", (e,t,r)=>{
+            this._isInDynamicRange(e) && (this._dynamic_textures[e] != null && (this._dynamic_textures[e].dispose(),
+            this._dynamic_textures[e] = null),
+            this._dynamic_textures[e] = new RawTexture(null,t,r * 1.5,Engine.TEXTUREFORMAT_LUMINANCE,this.scene,!1,!0,Texture.NEAREST_SAMPLINGMODE,Engine.TEXTURETYPE_UNSIGNED_BYTE),
+            this._dynamic_textures[e].name = "Pano_Dynamic_" + e + "_" + Date.now())
+        }
+        );
+        E(this, "initDynamicShaders", e=>(log$v.info("[Engine] Material init dynamic shader."),
+        new Promise((t,r)=>{
+            this._dynamic_shaders[e] != null && this._dynamic_shaders[e].dispose();
+            const n = new ShaderMaterial("Pano_Shader_" + e,this.scene,{
+                vertexSource: panoVertex,
+                fragmentSource: panoFragment
+            },{
+                attributes: ["uv", "position", "world0", "world1", "world2", "world3"],
+                uniforms: ["view", "projection", "worldViewProjection", "world"],
+                defines: ["#define SHADOWFULLFLOAT"]
+            });
+            n.setTexture("texture_pano", null),
+            n.setVector3("centre_pose", new Vector3(0,0,0)),
+            n.setFloat("isYUV", this._inputPanoYUV420 ? 1 : 0),
+            n.setTexture("shadowSampler", null),
+            n.setMatrix("lightSpaceMatrix", null),
+            n.setFloat("haveShadowLight", 0),
+            n.setFloat("fireworkLight", 0),
+            n.setVector3("fireworkLightPosition", new Vector3(0,0,0)),
+            n.backFaceCulling = !1,
+            this._dynamic_shaders[e] = n,
+            t(!0)
+        }
+        )));
+        this._scenemanager = e,
+        this.scene = e.Scene,
+        this.engine = this.scene.getEngine(),
+        this.shaderMode = 1,
+        this._dynamic_textures = [],
+        this._dynamic_shaders = [],
+        this._dynamic_babylonpose = [],
+        this._videoRawYUVTexArray = new XVideoRawYUV(this.scene,t.videoResOriArray),
+        this.shaderMode = t.shaderMode,
+        t.yuvInfo != null && (this.yuvInfo = t.yuvInfo),
+        t.panoInfo != null && this.setPanoInfo(t.panoInfo)
+    }
+    stopYUVUpdate() {
+        this._isUpdateYUV = !1
+    }
+    allowYUVUpdate() {
+        this._isUpdateYUV = !0
+    }
+    setPanoInfo(e) {
+        this._panoInfo = e
+    }
+    _prepareRender(e) {
+        e && (this._initPureVideoContent(e),
+        this._updatePureVideoShaderInput())
+    }
+    getPureVideoShader() {
+        return this._lowModelShader
+    }
+    getDefaultShader() {
+        return this._defaultShader
+    }
+    updatePanoPartYUV(e, t, r) {
+        const n = t.subarray(0, r.width * r.height)
+          , o = t.subarray(r.width * r.height, r.width * r.height * 1.25)
+          , a = t.subarray(r.width * r.height * 1.25)
+          , s = this._panoInfo.width
+          , l = this._panoInfo.height;
+        if (this._dynamic_textures[e] != null) {
+            const u = this._dynamic_textures[e].getInternalTexture();
+            if (u != null && u != null) {
+                const c = this.engine._getTextureTarget(u);
+                this.engine._bindTextureDirectly(c, u, !0),
+                this.engine.updateTextureData(u, n, r.startX, l * 1.5 - r.startY - r.height, r.width, r.height),
+                this.engine.updateTextureData(u, o, r.startX * .5, (l - r.startY - r.height) * .5, r.width * .5 - 1, r.height * .5 - 1),
+                this.engine.updateTextureData(u, a, r.startX * .5 + s * .5, (l - r.startY - r.height) * .5, r.width * .5, r.height * .5),
+                this.engine._bindTextureDirectly(c, null)
+            }
+        }
+    }
+    changePanoImg(e, t) {
+        if (log$v.info(`[Engine] changePanoImg, id=${e}, pose=${t.pose.position.x},${t.pose.position.y},${t.pose.position.z}`),
+        !this._isInDynamicRange(e))
+            return log$v.error(`[Engine] ${e} is bigger than dynamic size set in PanoInfo`),
+            Promise.reject(new XMaterialError(`[Engine] ${e} is bigger than dynamic size set in PanoInfo`));
+        const r = ue4Position2Xverse(t.pose.position);
+        return r && (this._dynamic_babylonpose[e] = {
+            position: r
+        }),
+        new Promise((n,o)=>{
+            try {
+                typeof t.data == "string" ? (this.setPanoYUV420(!1),
+                this._dynamic_textures[e].updateURL(t.data, null, ()=>{
+                    this._dynamic_textures[e].updateSamplingMode(Texture.NEAREST_SAMPLINGMODE)
+                }
+                )) : (this.isPanoYUV420() == !1 && this.initDynamicTexture(e, this._panoInfo.width, this._panoInfo.height),
+                this.setPanoYUV420(!0),
+                this._dynamic_textures[e].update(t.data),
+                this._dynamic_textures[e].updateSamplingMode(Texture.NEAREST_SAMPLINGMODE)),
+                n(this)
+            } catch (a) {
+                o(new XMaterialError(`[Engine] ChangePanoImg Error! ${a}`))
+            }
+        }
+        ).then(n=>(t.fov != null && this._scenemanager.cameraComponent.changeCameraFov(t.fov * Math.PI / 180),
+        this._dynamic_shaders[e].setFloat("isYUV", this._inputPanoYUV420 ? 1 : 0),
+        this._dynamic_shaders[e].setTexture("texture_pano", this._dynamic_textures[e]),
+        this._dynamic_shaders[e].setVector3("centre_pose", this._dynamic_babylonpose[e].position),
+        !0))
+    }
+    setYUV420(e) {
+        this._inputYUV420 = e
+    }
+    isYUV420() {
+        return this._inputYUV420
+    }
+    setPanoYUV420(e) {
+        this._inputPanoYUV420 = e
+    }
+    isPanoYUV420() {
+        return this._inputPanoYUV420
+    }
+    getDynamicShader(e) {
+        return this._dynamic_shaders[e]
+    }
+    _updatePureVideoShaderInput() {
+        var e, t, r, n, o, a, s, l, u, c;
+        if (this.scene.getLightByName("AvatarLight") ? ((e = this._lowModelShader) == null || e.setFloat("haveShadowLight", 1),
+        (n = this._lowModelShader) == null || n.setTexture("shadowSampler", (r = (t = this.scene.getLightByName("AvatarLight")) == null ? void 0 : t.getShadowGenerator()) == null ? void 0 : r.getShadowMapForRendering()),
+        (s = this._lowModelShader) == null || s.setMatrix("lightSpaceMatrix", (a = (o = this.scene.getLightByName("AvatarLight")) == null ? void 0 : o.getShadowGenerator()) == null ? void 0 : a.getTransformMatrix())) : ((l = this._lowModelShader) == null || l.setTexture("shadowSampler", this._videoTexture),
+        (u = this._lowModelShader) == null || u.setMatrix("lightSpaceMatrix", new Matrix),
+        (c = this._lowModelShader) == null || c.setFloat("haveShadowLight", 0)),
+        this.scene.getLightByName("fireworkLight"))
+            this.scene.registerBeforeRender(()=>{
+                var h;
+                this._lowModelShader.setFloat("fireworkLight", this.scene.getLightByName("fireworkLight").getScaledIntensity()),
+                this._lowModelShader.setVector3("fireworkLightPosition", (h = this.scene.getLightByName("fireworkLight")) == null ? void 0 : h.position)
+            }
+            );
+        else {
+            const h = new PointLight("fireworkLight",new Vector3(0,0,0),this.scene);
+            h.intensity = 0
+        }
+    }
+    _updatePanoShaderInput(e) {
+        var t, r, n, o, a, s, l, u, c, h;
+        if (this._isInDynamicRange(e))
+            if (this.scene.getLightByName("AvatarLight") ? ((t = this._dynamic_shaders[e]) == null || t.setFloat("haveShadowLight", 1),
+            (o = this._dynamic_shaders[e]) == null || o.setTexture("shadowSampler", (n = (r = this.scene.getLightByName("AvatarLight")) == null ? void 0 : r.getShadowGenerator()) == null ? void 0 : n.getShadowMapForRendering()),
+            (l = this._dynamic_shaders[e]) == null || l.setMatrix("lightSpaceMatrix", (s = (a = this.scene.getLightByName("AvatarLight")) == null ? void 0 : a.getShadowGenerator()) == null ? void 0 : s.getTransformMatrix())) : ((u = this._dynamic_shaders[e]) == null || u.setTexture("shadowSampler", null),
+            (c = this._dynamic_shaders[e]) == null || c.setMatrix("lightSpaceMatrix", new Matrix),
+            (h = this._dynamic_shaders[e]) == null || h.setFloat("haveShadowLight", 0)),
+            this.scene.getLightByName("fireworkLight"))
+                this.scene.registerBeforeRender(()=>{
+                    var f;
+                    this._dynamic_shaders[e].setFloat("fireworkLight", this.scene.getLightByName("fireworkLight").getScaledIntensity()),
+                    this._dynamic_shaders[e].setVector3("fireworkLightPosition", (f = this.scene.getLightByName("fireworkLight")) == null ? void 0 : f.position)
+                }
+                );
+            else {
+                const f = new PointLight("fireworkLight",new Vector3(0,0,0),this.scene);
+                f.intensity = 0
+            }
+    }
+}

+ 112 - 0
XParticleManager.js

@@ -0,0 +1,112 @@
+class XParticleManager{
+    constructor(e) {
+        E(this, "_scene");
+        E(this, "_particles");
+        E(this, "_light");
+        E(this, "load", (e,t,r)=>new Promise(n=>{
+            ParticleSystemSet.BaseAssetsUrl = e;
+            const o = new XMLHttpRequest;
+            o.open("get", e + "/" + t),
+            o.send(null),
+            o.onload = ()=>{
+                if (o.status == 200) {
+                    const a = JSON.parse(o.responseText);
+                    let s = null;
+                    if (Object.keys(a).find(l=>l == "systems") == null) {
+                        const l = ParticleSystem.Parse(a, this._scene, e);
+                        s = new ParticleSystemSet,
+                        s.systems.push(l)
+                    } else
+                        s = ParticleSystemSet.Parse(a, this._scene, !1);
+                    n(s)
+                }
+            }
+        }
+        ));
+        E(this, "get", e=>this._particles.get(e));
+        E(this, "start", e=>{
+            const t = this._particles.get(e);
+            t && t.start()
+        }
+        );
+        E(this, "stop", e=>{
+            var r;
+            const t = ((r = this._particles.get(e)) == null ? void 0 : r.systems) || [];
+            for (let n = 0; n < t.length; n++)
+                t[n].stop()
+        }
+        );
+        E(this, "remove", e=>{
+            const t = this._particles.get(e);
+            t && t.dispose()
+        }
+        );
+        E(this, "setParticlePosition", (e,t)=>{
+            const r = this._particles.get(e);
+            r && (r.emitterNode = t)
+        }
+        );
+        E(this, "setParticleScalingInPlace", (e,t)=>{
+            const r = this._particles.get(e);
+            r == null || r.systems.forEach(n=>{
+                de.scalingInPlace(n, t)
+            }
+            )
+        }
+        );
+        if (this._scene = e,
+        this._particles = new Map,
+        this._light = null,
+        this._scene.getLightByName("fireworkLight"))
+            this._light = this._scene.getLightByName("fireworkLight");
+        else {
+            const t = new PointLight("fireworkLight",new Vector3(0,0,0),e);
+            t.intensity = 0,
+            this._light = t
+        }
+    }
+    _flashBang(e=200) {
+        const t = this._scene.getLightByName("fireworkLight");
+        t.intensity = 1,
+        setTimeout(()=>{
+            t.intensity = 0
+        }
+        , e)
+    }
+}
+;
+
+E(XParticleManager, "disposeParticleSysSet", e=>{
+    !e.systems || (e.systems.forEach(t=>{
+        de.disposeParticleSystem(t)
+    }
+    ),
+    e.dispose())
+}
+),
+E(XParticleManager, "disposeParticleSystem", e=>{
+    e.particleSystem && (e = e.particleSystem),
+    e.subEmitters && e.subEmitters.forEach(t=>{
+        t instanceof Array ? t.forEach(r=>{
+            de.disposeParticleSystem(r)
+        }
+        ) : de.disposeParticleSystem(t)
+    }
+    ),
+    e.dispose()
+}
+),
+E(XParticleManager, "scalingInPlace", (e,t)=>{
+    e.getClassName() === "ParticleSystem" && (e.minSize *= t,
+    e.maxSize *= t,
+    e.subEmitters != null && e.subEmitters.forEach(r=>{
+        r instanceof SubEmitter && de.scalingInPlace(r.particleSystem, t),
+        r instanceof ParticleSystem && de.scalingInPlace(r, t),
+        r instanceof Array && r.forEach(n=>{
+            de.scalingInPlace(n.particleSystem, t)
+        }
+        )
+    }
+    ))
+}
+);

+ 20 - 0
XRain.js

@@ -0,0 +1,20 @@
+class XRain extends XSubSequence {
+    constructor(e, t, r) {
+        super(e, t, r);
+        this.onLoadedObserverable.addOnce(()=>{
+            this._particleGroups.forEach(n=>{
+                const o = n.systems[0];
+                o.getClassName() == "ParticleSystem" && (o.startPositionFunction = function(a, s) {
+                    const u = 2 * Math.random() * Math.PI
+                      , c = Math.random() * 15 * Math.sin(u)
+                      , h = this.minEmitBox.y
+                      , f = Math.random() * 15 * Math.cos(u);
+                    Vector3.TransformCoordinatesFromFloatsToRef(c, h, f, a, s)
+                }
+                )
+            }
+            )
+        }
+        )
+    }
+}

+ 429 - 0
XSceneManager.js

@@ -0,0 +1,429 @@
+const log$t = new Logger$1("XSceneManager");
+var ECurrentShaderMode = (i=>(i[i.default = 0] = "default",
+i[i.video = 1] = "video",
+i[i.pano = 2] = "pano",
+i))(ECurrentShaderMode || {})
+  , EImageQuality = (i=>(i[i.low = 0] = "low",
+i[i.mid = 1] = "mid",
+i[i.high = 2] = "high",
+i))(EImageQuality || {});
+class XSceneManager {
+    constructor(e, t) {
+        E(this, "scene");
+        E(this, "engine");
+        E(this, "canvas");
+        E(this, "gl");
+        E(this, "_yuvInfo");
+        E(this, "cameraParam");
+        E(this, "shaderMode");
+        E(this, "panoInfo");
+        E(this, "_initEngineScaleNumber");
+        E(this, "_forceKeepVertical", !1);
+        E(this, "_currentShader");
+        E(this, "_currentPanoId");
+        E(this, "_renderStatusCheckCount", 0);
+        E(this, "_renderStatusNotChecktCount", 0);
+        E(this, "_nonlinearCanvasResize", !1);
+        E(this, "_bChangeEngineSize", !0);
+        E(this, "_cameraManager");
+        E(this, "_lowpolyManager");
+        E(this, "_materialManager");
+        E(this, "_statisticManager");
+        E(this, "_breathPointManager");
+        E(this, "_skytv");
+        E(this, "_mv", []);
+        E(this, "_decalManager");
+        E(this, "_lightManager");
+        E(this, "_avatarManager");
+        E(this, "urlTransformer");
+        E(this, "_billboardManager");
+        E(this, "_backgroundImg");
+        E(this, "engineRunTimeStats");
+        E(this, "uploadHardwareSystemInfo", ()=>{
+            const e = this.statisticComponent.getHardwareRenderInfo()
+              , t = this.statisticComponent.getSystemInfo()
+              , r = {
+                driver: t.driver,
+                vender: t.vender,
+                webgl: t.version,
+                os: t.os
+            };
+            log$t.warn(JSON.stringify(e)),
+            log$t.warn(JSON.stringify(r))
+        }
+        );
+        E(this, "addNewLowPolyMesh", async(e,t)=>(this._currentShader == null && await this.initSceneManager(),
+        this._lowpolyManager.addNewLowPolyMesh(e, t, this._currentShader)));
+        E(this, "initSceneManager", async()=>(await this._materialManager.initMaterial(),
+        this.applyShader()));
+        E(this, "registerAfterRender", ()=>{
+            var e;
+            if (this._forceKeepVertical) {
+                const t = this.canvas.width
+                  , r = this.canvas.height;
+                let n = 0
+                  , o = [[0, 0, 0, 0], [0, 0, 0, 0]];
+                if (((e = this._cameraManager.MainCamera) == null ? void 0 : e.fovMode) === Camera$1.FOVMODE_HORIZONTAL_FIXED ? (n = Math.ceil((r - this._yuvInfo.height * t / this._yuvInfo.width) / 2),
+                o = [[0, 0, t, n], [0, r - n, t, n]]) : (n = Math.ceil((t - this._yuvInfo.width * r / this._yuvInfo.height) / 2),
+                o = [[0, 0, n, r], [t - n, 0, n, r]]),
+                n > 0) {
+                    this.gl.enable(this.gl.SCISSOR_TEST);
+                    for (let a = 0; a < o.length; ++a)
+                        this.gl.scissor(o[a][0], o[a][1], o[a][2], o[a][3]),
+                        this.gl.clearColor(0, 0, 0, 1),
+                        this.gl.clear(this.gl.COLOR_BUFFER_BIT);
+                    this.gl.disable(this.gl.SCISSOR_TEST)
+                }
+            }
+        }
+        );
+        E(this, "resetRender", ()=>{
+            this.scene.environmentTexture && (this.scene.environmentTexture._texture ? this.lightComponent.setIBL(this.scene.environmentTexture._texture.url) : this.scene.environmentTexture.url && this.lightComponent.setIBL(this.scene.environmentTexture.url))
+        }
+        );
+        const r = /iphone|ipad/gi.test(window.navigator.userAgent) || t.disableWebGL2
+          , n = new Engine(e,!0,{
+            preserveDrawingBuffer: !0,
+            stencil: !0,
+            disableWebGL2Support: r
+        },!0)
+          , o = new Scene(n);
+        this.scene = o,
+        this.engine = n,
+        this.canvas = e,
+        this.scene.clearColor = new Color4(.7,.7,.7,1),
+        this.engine.getCaps().parallelShaderCompile = void 0,
+        this._initEngineScaleNumber = this.engine.getHardwareScalingLevel(),
+        this.engine.enableOfflineSupport = !1,
+        this.engine.doNotHandleContextLost = !0,
+        this.scene.clearCachedVertexData(),
+        this.scene.cleanCachedTextureBuffer(),
+        this.urlTransformer = t.urlTransformer || (s=>Promise.resolve(s)),
+        t.logger && Logger$1.setLogger(t.logger),
+        this.gl = e.getContext("webgl2", {
+            preserveDrawingBuffer: !0
+        }) || e.getContext("webgl", {
+            preserveDrawingBuffer: !0
+        }) || e.getContext("experimental-webgl", {
+            preserveDrawingBuffer: !0
+        }),
+        this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT, 1),
+        this._currentPanoId = 0,
+        t.forceKeepVertical != null && (this._forceKeepVertical = t.forceKeepVertical),
+        t.panoInfo != null && (this.panoInfo = t.panoInfo),
+        t.shaderMode != null && (this.shaderMode = t.shaderMode),
+        t.yuvInfo != null ? this._yuvInfo = t.yuvInfo : this._yuvInfo = {
+            width: t.videoResOriArray[0].width,
+            height: t.videoResOriArray[0].height,
+            fov: 50
+        },
+        t.cameraParam != null && (this.cameraParam = t.cameraParam),
+        t.nonlinearCanvasResize != null && (this._nonlinearCanvasResize = t.nonlinearCanvasResize),
+        this._cameraManager = new XCameraComponent(this.canvas,this.scene,{
+            cameraParam: this.cameraParam,
+            yuvInfo: this._yuvInfo,
+            forceKeepVertical: this._forceKeepVertical
+        }),
+        this._lowpolyManager = new XStaticMeshComponent(this),
+        this._materialManager = new XMaterialComponent(this,{
+            videoResOriArray: t.videoResOriArray,
+            yuvInfo: this._yuvInfo,
+            panoInfo: this.panoInfo,
+            shaderMode: this.shaderMode
+        }),
+        this._statisticManager = new XStats(this),
+        this._breathPointManager = new XBreathPointManager(this),
+        this._decalManager = new XDecalManager(this),
+        this._avatarManager = new XAvatarManager(this),
+        this._billboardManager = new XBillboardManager(this),
+        this.billboardComponent.loadBackGroundTexToIDB(),
+        this._lightManager = new XLightManager(this),
+        this.postprocessing(),
+        this.initSceneManager(),
+        this.engineRunTimeStats = new XEngineRunTimeStats,
+        /iphone/gi.test(window.navigator.userAgent) && window.devicePixelRatio && window.devicePixelRatio === 3 && window.screen.width === 375 && window.screen.height === 812 ? this.engine.setHardwareScalingLevel(this._initEngineScaleNumber * 2) : this.engine.setHardwareScalingLevel(this._initEngineScaleNumber * 1.8),
+        this.scene.registerBeforeRender(()=>{
+            this._nonlinearCanvasResize && this._bChangeEngineSize && (this.setEngineSize(this._yuvInfo),
+            this._bChangeEngineSize = !1)
+        }
+        ),
+        this.scene.registerAfterRender(()=>{
+            this._nonlinearCanvasResize || this.registerAfterRender()
+        }
+        ),
+        window.addEventListener("resize", ()=>{
+            this._nonlinearCanvasResize ? this._bChangeEngineSize = !0 : this.engine.resize()
+        }
+        ),
+        XBillboardManager.alphaWidthMap = getAlphaWidthMap("Arial", this.scene),
+        this.uploadHardwareSystemInfo()
+    }
+    get yuvInfo() {
+        return this.getCurrentShaderMode() == 1 ? this._yuvInfo : {
+            width: -1,
+            height: -1,
+            fov: -1
+        }
+    }
+    set yuvInfo(e) {
+        this.getCurrentShaderMode() == 1 && (this._yuvInfo = e,
+        this._cameraManager.cameraFovChange(e))
+    }
+    get mainScene() {
+        return this.scene
+    }
+    get cameraComponent() {
+        return this._cameraManager
+    }
+    get staticmeshComponent() {
+        return this._lowpolyManager
+    }
+    get materialComponent() {
+        return this._materialManager
+    }
+    get statisticComponent() {
+        return this._statisticManager
+    }
+    get avatarComponent() {
+        return this._avatarManager
+    }
+    get lightComponent() {
+        return this._lightManager
+    }
+    get Engine() {
+        return this.engine
+    }
+    get Scene() {
+        return this.scene
+    }
+    get billboardComponent() {
+        return this._billboardManager
+    }
+    get breathPointComponent() {
+        return this._breathPointManager
+    }
+    get skytvComponent() {
+        return this._skytv
+    }
+    get mvComponent() {
+        return this._mv
+    }
+    get decalComponent() {
+        return this._decalManager
+    }
+    get currentShader() {
+        return this._currentShader
+    }
+    get initEngineScaleNumber() {
+        return this._initEngineScaleNumber
+    }
+    setImageQuality(e) {
+        e == 0 ? (this.engine.setHardwareScalingLevel(this._initEngineScaleNumber * 1.8),
+        log$t.info("[Engine] change image quality to low, [" + this._initEngineScaleNumber * 1.8 + "]")) : e == 1 ? (this.engine.setHardwareScalingLevel(this._initEngineScaleNumber * 1.5),
+        log$t.info("[Engine] change image quality to mid, [" + this._initEngineScaleNumber * 1.5 + "]")) : e == 2 && (this.engine.setHardwareScalingLevel(this._initEngineScaleNumber * 1),
+        log$t.info("[Engine] change image quality to high, [" + this._initEngineScaleNumber * 1 + "]"))
+    }
+    setNonlinearCanvasResize(e) {
+        this._nonlinearCanvasResize = e,
+        this._bChangeEngineSize = e,
+        e || this.engine.resize()
+    }
+    setBackgroundColor(e) {
+        this.scene.clearColor = new Color4(e.r,e.g,e.b,e.a)
+    }
+    setBackgroundImg(e) {
+        return this._backgroundImg != null && this._backgroundImg.url == e ? Promise.resolve(!0) : new Promise((t,r)=>{
+            this.urlTransformer(e).then(n=>{
+                this._backgroundImg == null ? this._backgroundImg = {
+                    layer: new Layer("tex_background_" + Date.now(),n,this.Scene,!0),
+                    url: e
+                } : this._backgroundImg.url != e && this._backgroundImg.layer != null && this._backgroundImg.layer.texture != null && (this._backgroundImg.layer.texture.updateURL(n),
+                this._backgroundImg.layer.name = "tex_background_" + Date.now(),
+                this._backgroundImg.url = e),
+                t(!0)
+            }
+            ).catch(n=>{
+                log$t.error(`[Engine] set background image Error: ${n}`),
+                r(`[Engine] set background image Error: ${n}`)
+            }
+            )
+        }
+        )
+    }
+    cleanTheWholeScene() {
+        const e = this.scene.getFrameId();
+        this.scene.onBeforeRenderObservable.clear(),
+        this.scene.onAfterRenderObservable.clear(),
+        this.scene.clearCachedVertexData(),
+        this.scene.cleanCachedTextureBuffer(),
+        this.scene.registerBeforeRender(()=>{
+            this.scene.getFrameId() - e > 5 && this.scene.dispose()
+        }
+        )
+    }
+    getAreaAvatar(e, t) {
+        const r = [];
+        return this._avatarManager.getAvatarList().forEach(n=>{
+            const o = e
+              , a = n.position;
+            a && o && calcDistance3D(o, a) < t && r.push(n.id)
+        }
+        ),
+        r
+    }
+    setEngineSize(e) {
+        const t = e.width
+          , r = e.height
+          , n = this.canvas.width;
+        this.canvas.height,
+        this.engine.setSize(Math.round(n), Math.round(n * (r / t)))
+    }
+    getCurrentShaderMode() {
+        return this._currentShader === this._materialManager.getDefaultShader() ? 0 : this._currentShader === this._materialManager.getPureVideoShader() ? 1 : 2
+    }
+    addSkyTV(e, t) {
+        return this._skytv = new XTelevision(this.scene,e,this,t),
+        this._skytv
+    }
+    addMv(e, t) {
+        this._mv.push(new XTelevision(this.scene,e,this,t))
+    }
+    addMeshInfo(e) {
+        this._lowpolyManager.setMeshInfo(e)
+    }
+    applyShader() {
+        return new Promise((e,t)=>{
+            this.shaderMode == EShaderMode.videoAndPano || this.shaderMode == EShaderMode.video ? this.changeVideoShaderForLowModel() : this.shaderMode == EShaderMode.default && this.changeDefaultShaderForLowModel(),
+            e(!0)
+        }
+        )
+    }
+    changeHardwareScaling(e) {
+        e < 1 ? e = 1 : e > 2.5 && (e = 2.5),
+        this._bChangeEngineSize = !0,
+        this.engine.setHardwareScalingLevel(this._initEngineScaleNumber * e)
+    }
+    getCurrentUsedPanoId() {
+        return this._currentPanoId
+    }
+    render() {
+        try {
+            this.scene.render()
+        } catch (e) {
+            throw log$t.error(`[Engine] Render Error: ${e}`),
+            e
+        }
+    }
+    isReadyToRender(e) {
+        const {checkMesh: t=!0, checkEffect: r=!1, checkPostProgress: n=!1, checkParticle: o=!1, checkAnimation: a=!1, materialNameWhiteLists: s=[]} = e;
+        if (this.scene._isDisposed)
+            return log$t.error("[Engine] this.scene._isDisposed== false "),
+            !1;
+        let l;
+        const u = this.scene.getEngine();
+        if (r && !u.areAllEffectsReady())
+            return log$t.error("[Engine] engine.areAllEffectsReady == false"),
+            !1;
+        if (a && this.scene._pendingData.length > 0)
+            return log$t.error("[Engine] scene._pendingData.length > 0 && animation error"),
+            !1;
+        if (t) {
+            for (l = 0; l < this.scene.meshes.length; l++) {
+                const c = this.scene.meshes[l];
+                if (!c.isEnabled() || !c.subMeshes || c.subMeshes.length === 0 || c != null && c.material != null && !(c.material.name.startsWith("Pure") || c.material.name.startsWith("Pano")))
+                    continue;
+                if (!c.isReady(!0))
+                    return log$t.error(`[Engine] scene. mesh isReady == false, mesh name:${c.name}, mesh xtype: ${c == null ? void 0 : c.xtype}, mesh xgroup: ${c == null ? void 0 : c.xgroup}, mesh xskinInfo: ${c == null ? void 0 : c.xskinInfo}`),
+                    !1;
+                const h = c.hasThinInstances || c.getClassName() === "InstancedMesh" || c.getClassName() === "InstancedLinesMesh" || u.getCaps().instancedArrays && c.instances.length > 0;
+                for (const f of this.scene._isReadyForMeshStage)
+                    if (!f.action(c, h))
+                        return log$t.error(`[Engine] scene._isReadyForMeshStage == false, mesh name:${c.name}, mesh xtype: ${c == null ? void 0 : c.xtype}, mesh xgroup: ${c == null ? void 0 : c.xgroup}, mesh xskinInfo: ${c == null ? void 0 : c.xskinInfo}`),
+                        !1
+            }
+            for (l = 0; l < this.scene.geometries.length; l++)
+                if (this.scene.geometries[l].delayLoadState === 2)
+                    return log$t.error("[Engine] geometry.delayLoadState === 2"),
+                    !1
+        }
+        if (n) {
+            if (this.scene.activeCameras && this.scene.activeCameras.length > 0) {
+                for (const c of this.scene.activeCameras)
+                    if (!c.isReady(!0))
+                        return log$t.error("[Engine] camera not ready === false, ", c.name),
+                        !1
+            } else if (this.scene.activeCamera && !this.scene.activeCamera.isReady(!0))
+                return log$t.error("[Engine] activeCamera ready === false, ", this.scene.activeCamera.name),
+                !1
+        }
+        if (o) {
+            for (const c of this.scene.particleSystems)
+                if (!c.isReady())
+                    return log$t.error("[Engine] particleSystem ready === false, ", c.name),
+                    !1
+        }
+        return !0
+    }
+    changePanoShaderForLowModel(e) {
+        return log$t.info(`[Engine] changePanoShaderForLowModel: ${e}`),
+        this._materialManager.allowYUVUpdate(),
+        new Promise((t,r)=>{
+            this._materialManager._isInDynamicRange(e) == !1 && r(!1),
+            this._currentPanoId = e,
+            this._currentShader = this._materialManager.getDynamicShader(e),
+            this.changeShaderForLowModel().then(()=>{
+                t(!0)
+            }
+            )
+        }
+        )
+    }
+    changeVideoShaderForLowModel() {
+        return log$t.info("[Engine] changeVideoShaderForLowModel"),
+        this._currentShader = this._materialManager.getPureVideoShader(),
+        this._materialManager.allowYUVUpdate(),
+        this.changeShaderForLowModel()
+    }
+    changeDefaultShaderForLowModel() {
+        return log$t.info("[Engine] changeDefaultShaderForLowModel"),
+        this._currentShader = this._materialManager.getDefaultShader(),
+        this._materialManager.stopYUVUpdate(),
+        this.changeShaderForLowModel()
+    }
+    changeShaderForLowModel() {
+        return new Promise((e,t)=>{
+            this._lowpolyManager.getMeshes().forEach(r=>{
+                r.setMaterial(this._currentShader)
+            }
+            ),
+            this._lowpolyManager.getCgMesh().mesh.material = this._currentShader,
+            e(!0)
+        }
+        )
+    }
+    setIBL(e) {
+        this._lightManager.setIBL(e)
+    }
+    postprocessing() {
+        const e = new DefaultRenderingPipeline("default",!0,this.scene);
+        e.imageProcessingEnabled = !1,
+        e.bloomEnabled = !0,
+        e.bloomThreshold = 1,
+        e.bloomWeight = 1,
+        e.bloomKernel = 64,
+        e.bloomScale = .1
+    }
+    getGround(e) {
+        const t = this._lowpolyManager.getMeshes()
+          , r = [];
+        return t.forEach(n=>{
+            n.mesh.name.indexOf("SM_Stage") >= 0 && r.push(n.mesh)
+        }
+        ),
+        this.Scene.meshes.forEach(n=>{
+            n.name.split("_")[0] === "ground" && r.push(n)
+        }
+        ),
+        r
+    }
+}

+ 829 - 0
XSequence.js

@@ -0,0 +1,829 @@
+class XSubSequence {
+    constructor(e, t, r=DefaultUrlTransformer$1) {
+        E(this, "_scene");
+        E(this, "_meshGroups");
+        E(this, "_animGroup");
+        E(this, "_particleGroups");
+        E(this, "_materialGroups");
+        E(this, "_spriteGroups");
+        E(this, "_glowGroups");
+        E(this, "_highLightGroups");
+        E(this, "_endFrame");
+        E(this, "_centerNode");
+        E(this, "_rootDir");
+        E(this, "_abosoluteUrl");
+        E(this, "_name");
+        E(this, "_pickable", !1);
+        E(this, "urlTransformer");
+        E(this, "onLoadedObserverable", new Observable);
+        E(this, "onSubSequenceTransformationChangeObservable", new Observable);
+        E(this, "onIntersectionObservable", new Observable);
+        E(this, "_loaded");
+        E(this, "_isStarted");
+        E(this, "_isPaused");
+        E(this, "_isDisposing", !1);
+        E(this, "init", ()=>new Promise((e,t)=>{
+            this.urlTransformer(this._abosoluteUrl).then(r=>{
+                const n = new XMLHttpRequest;
+                n.open("get", r),
+                n.send(null),
+                n.onload = ()=>{
+                    if (n.status == 200) {
+                        const o = JSON.parse(n.responseText);
+                        this.load(o).then(()=>{
+                            this.onLoadedObserverable.notifyObservers(this),
+                            this._loaded = !0,
+                            e()
+                        }
+                        , ()=>{
+                            t(),
+                            log$A.error("subSequence: Load ${jsonBlob} json fail")
+                        }
+                        )
+                    }
+                }
+                ,
+                n.onerror = ()=>{
+                    log$A.error("http: Get ${jsonBlob} json fail"),
+                    t()
+                }
+            }
+            )
+        }
+        ));
+        E(this, "play", async(e=!0)=>new Promise(t=>{
+            if (this._animGroup.isPlaying && this._animGroup.stop(),
+            this._particleGroups.forEach(r=>{
+                var n;
+                ((n = r.emitterNode) == null ? void 0 : n.getClassName()) == "Mesh" && r.emitterNode instanceof Mesh ? r.emitterNode.isEnabled() && r.start() : r.start()
+            }
+            ),
+            this._animGroup.targetedAnimations.length == 0) {
+                this.show();
+                let r = 0;
+                this._spriteGroups.forEach(n=>{
+                    n.sprites.forEach(o=>{
+                        o.toIndex > r && (r = o.toIndex)
+                    }
+                    )
+                }
+                ),
+                this._spriteGroups.forEach(n=>{
+                    n.sprites.forEach(o=>{
+                        o.playAnimation(o.fromIndex, o.toIndex, e, o.delay, ()=>{
+                            o.toIndex == r && (this._isPaused = !0,
+                            this.hide(),
+                            t())
+                        }
+                        )
+                    }
+                    )
+                }
+                )
+            } else
+                this._animGroup.play(e),
+                this._spriteGroups.forEach(r=>{
+                    r.sprites.forEach(n=>{
+                        n.playAnimation(n.fromIndex, n.toIndex, e, n.delay)
+                    }
+                    )
+                }
+                ),
+                e ? this._animGroup.onAnimationGroupLoopObservable.addOnce(()=>{
+                    t()
+                }
+                ) : this._animGroup.onAnimationGroupEndObservable.addOnce(()=>{
+                    this._spriteGroups.forEach(r=>{
+                        r.sprites.forEach(n=>{
+                            n.isVisible = !1,
+                            n.isPickable = !1,
+                            n.stopAnimation()
+                        }
+                        )
+                    }
+                    ),
+                    t()
+                }
+                );
+            this._isStarted = !0,
+            this._isPaused = !1
+        }
+        ));
+        E(this, "stop", ()=>{
+            this._animGroup.stop(),
+            this._particleGroups.forEach(e=>{
+                e.systems.forEach(t=>{
+                    t.stop()
+                }
+                )
+            }
+            ),
+            this._spriteGroups.forEach(e=>{
+                e.sprites.forEach(t=>{
+                    t.stopAnimation()
+                }
+                )
+            }
+            ),
+            this._isStarted = !1
+        }
+        );
+        E(this, "clone", (e="Clone")=>{
+            const t = new XSubSequence(this._scene,this._abosoluteUrl);
+            return t._centerNode.name = e + "_" + this._centerNode.name,
+            t._animGroup.name = e + "_" + this._animGroup.name,
+            this._meshGroups.forEach(r=>{
+                const n = r.clone(e + "_", t._centerNode)
+                  , o = n.getChildren(void 0, !1);
+                if (o.forEach(a=>{
+                    a.setEnabled(!0)
+                }
+                ),
+                o.push(n),
+                n) {
+                    const a = r.getChildren(void 0, !1);
+                    a.push(r),
+                    this.animGroup.targetedAnimations.forEach(s=>{
+                        if (s.target instanceof Node$2) {
+                            const l = a.indexOf(s.target);
+                            l != -1 && t._animGroup.addTargetedAnimation(s.animation, o[l])
+                        }
+                    }
+                    )
+                }
+            }
+            ),
+            t._loaded = !0,
+            t
+        }
+        );
+        E(this, "goToFrame", e=>{
+            this._animGroup.start(!0, 1, e, e)
+        }
+        );
+        E(this, "pause", ()=>{
+            this._isPaused = !0,
+            this._animGroup.pause()
+        }
+        );
+        E(this, "reset", ()=>{
+            this._animGroup.reset()
+        }
+        );
+        E(this, "loadTrackToAnim", e=>{
+            const t = Array();
+            let r = !0;
+            e.keyFrame.forEach(o=>{
+                if (o.frame > this._endFrame && (this._endFrame = o.frame),
+                o.value instanceof Array) {
+                    const a = {
+                        frame: o.frame,
+                        value: new Vector3(0,0,0)
+                    }
+                      , s = new Vector3(o.value[0],o.value[1],o.value[2]);
+                    a.value = s,
+                    t.push(a)
+                } else
+                    t.push(o),
+                    r = !1
+            }
+            ),
+            e.loop == null && (e.loop = !1),
+            e.index == null && (e.index = 0);
+            let n = null;
+            if ("blockName"in e) {
+                const o = {
+                    keyFrame: t,
+                    blockName: e.blockName,
+                    property: e.property,
+                    targetName: e.targetName,
+                    index: e.index,
+                    loop: e.loop
+                };
+                n = this.transferTrackToAnim(o, r)
+            } else {
+                const o = {
+                    keyFrame: t,
+                    property: e.property,
+                    targetName: e.targetName,
+                    index: e.index,
+                    loop: e.loop
+                };
+                n = this.transferTrackToAnim(o, r)
+            }
+            return n
+        }
+        );
+        E(this, "transferTrackToAnim", (e,t)=>{
+            let r = null;
+            t ? (r = new Animation(e.targetName + "_" + e.property,e.property,DEFAULT_FRAME_RATE,Animation.ANIMATIONTYPE_VECTOR3,Animation.ANIMATIONLOOPMODE_CYCLE),
+            r.setKeys(e.keyFrame)) : (r = new Animation(e.targetName + "_" + e.property,e.property,DEFAULT_FRAME_RATE,Animation.ANIMATIONTYPE_FLOAT,Animation.ANIMATIONLOOPMODE_CYCLE),
+            r.setKeys(e.keyFrame));
+            let n = null;
+            return "blockName"in e ? n = {
+                animation: r,
+                blockName: e.blockName,
+                targetName: e.targetName,
+                nodeIndex: e.index,
+                loop: e.loop
+            } : n = {
+                animation: r,
+                targetName: e.targetName,
+                nodeIndex: e.index,
+                loop: e.loop
+            },
+            n
+        }
+        );
+        t.indexOf("./") == 0 && (t = t.slice(2)),
+        this._abosoluteUrl = t,
+        this._name = t.split("/").slice(-1)[0].split(".")[0].split("_")[1],
+        this._rootDir = t.split("/").slice(0, -1).join("/") + "/",
+        this._scene = e,
+        this._meshGroups = new Map,
+        this._animGroup = new AnimationGroup("SubSeqAnim_",this._scene),
+        this._particleGroups = new Map,
+        this._materialGroups = new Map,
+        this._glowGroups = new Map,
+        this._highLightGroups = new Map,
+        this._spriteGroups = new Map,
+        this._endFrame = 0,
+        this._centerNode = new TransformNode("__rootSubSeq__",e),
+        this._loaded = !1,
+        this._isPaused = !0,
+        this._isStarted = !1,
+        this._centerNode.setEnabled(!1),
+        this.urlTransformer = r,
+        this._centerNode.onAfterWorldMatrixUpdateObservable.add(()=>{
+            this.onSubSequenceTransformationChangeObservable.notifyObservers(this)
+        }
+        ),
+        this._animGroup.onAnimationGroupPlayObservable.add(()=>{
+            this._particleGroups.forEach(n=>{
+                n.systems.forEach(o=>{
+                    o.isStarted() || o.start()
+                }
+                )
+            }
+            ),
+            this.show()
+        }
+        ),
+        this._animGroup.onAnimationGroupLoopObservable.add(()=>{
+            this._particleGroups.forEach(n=>{
+                n.systems.forEach(o=>{
+                    o.isStarted() || o.start()
+                }
+                )
+            }
+            ),
+            this.show()
+        }
+        ),
+        this._animGroup.onAnimationGroupEndObservable.add(()=>{
+            this.hide()
+        }
+        )
+    }
+    dispose() {
+        this._isDisposing = !0,
+        this._spriteGroups.forEach(e=>{
+            e.dispose()
+        }
+        ),
+        this._glowGroups.forEach(e=>{
+            e.dispose()
+        }
+        ),
+        this._highLightGroups.forEach(e=>{
+            e.dispose()
+        }
+        ),
+        this._particleGroups.forEach(e=>{
+            XParticleManager.disposeParticleSysSet(e)
+        }
+        ),
+        this._animGroup.stop(),
+        this._animGroup.dispose(),
+        this._meshGroups.forEach(e=>{
+            e.getChildren(void 0, !1).forEach(t=>{
+                var r, n;
+                (t.getClassName() === "AbstractMesh" || t.getClassName() === "Mesh") && ((r = t.skeleton) == null || r.dispose(),
+                (n = t.material) == null || n.dispose(!0, !0)),
+                t.dispose(!0, !0)
+            }
+            ),
+            e.dispose(!1, !0)
+        }
+        ),
+        this._centerNode.dispose(!1, !0),
+        this._materialGroups.forEach(e=>{}
+        ),
+        this._materialGroups.clear(),
+        this._spriteGroups.clear(),
+        this._glowGroups.clear(),
+        this._highLightGroups.clear(),
+        this._meshGroups.clear(),
+        this._particleGroups.clear(),
+        this._loaded = !1
+    }
+    get animGroup() {
+        return this._animGroup
+    }
+    get name() {
+        return this._name
+    }
+    get path() {
+        return this._abosoluteUrl
+    }
+    get position() {
+        return xversePosition2Ue4(this.pos)
+    }
+    get rotation() {
+        return xverseRotation2Ue4(this.rot)
+    }
+    get scaling() {
+        return this.scal
+    }
+    get pos() {
+        return this._centerNode.position
+    }
+    get rot() {
+        return this._centerNode.rotation
+    }
+    get scal() {
+        return this._centerNode.scaling
+    }
+    get root() {
+        return this._centerNode
+    }
+    get loaded() {
+        return this._loaded
+    }
+    get isPlaying() {
+        return this._animGroup ? this._animGroup.isPlaying : this._isStarted && !this._isPaused
+    }
+    get isStarted() {
+        return this._animGroup ? this._animGroup.isStarted : this._isStarted
+    }
+    get isPickable() {
+        return this._pickable
+    }
+    set isPickable(e) {
+        this._meshGroups.forEach(t=>{
+            t.getChildMeshes().forEach(r=>{
+                r.isPickable = e
+            }
+            )
+        }
+        ),
+        this._spriteGroups.forEach(t=>{
+            t.isPickable = e,
+            t.sprites.forEach(r=>{
+                r.isPickable = e
+            }
+            )
+        }
+        ),
+        this._pickable = e
+    }
+    addAnimation(e) {
+        this._animGroup.addTargetedAnimation(e, this._centerNode),
+        this._spriteGroups.forEach(t=>{
+            t.sprites.forEach(r=>{
+                this._animGroup.addTargetedAnimation(e, r)
+            }
+            )
+        }
+        )
+    }
+    setStartFrame(e) {
+        this._animGroup.stop(),
+        this._animGroup.targetedAnimations.forEach(t=>{
+            const r = t.animation.getKeys();
+            r.forEach(n=>{
+                e + r[0].frame > 0 ? n.frame += e : n.frame -= r[0].frame
+            }
+            )
+        }
+        )
+    }
+    lookAt(e) {
+        ue4Position2Xverse(e) && this.root.lookAt(ue4Position2Xverse(e))
+    }
+    setPosition(e) {
+        this.setPositionVector(ue4Position2Xverse(e))
+    }
+    setPositionVector(e) {
+        this._centerNode.position = e,
+        this._particleGroups.forEach(t=>{
+            t.emitterNode == null || t.emitterNode instanceof Vector3 ? t.emitterNode = e : this._scene.getMeshByName(t.emitterNode.name) || (t.emitterNode = e)
+        }
+        ),
+        this._spriteGroups.forEach(t=>{
+            t.sprites.forEach((r,n)=>{
+                r.position = e
+            }
+            )
+        }
+        )
+    }
+    setScaling(e) {
+        this.setScalingVector(ue4Scaling2Xverse(e))
+    }
+    setScalingVector(e) {
+        var t;
+        this._centerNode.scaling = e,
+        (t = this._particleGroups) == null || t.forEach(r=>{
+            r.systems.forEach(n=>{
+                XParticleManager.scalingInPlace(n, e.x)
+            }
+            )
+        }
+        ),
+        this._spriteGroups.forEach(r=>{
+            r.sprites.forEach(n=>{
+                n.size *= e.x
+            }
+            )
+        }
+        )
+    }
+    setRotation(e) {
+        this.setRotationVector(ue4Rotation2Xverse(e))
+    }
+    setRotationVector(e) {
+        this._centerNode.rotation = e
+    }
+    hide() {
+        this._centerNode.setEnabled(!1),
+        this._particleGroups.forEach(e=>{
+            e.systems.forEach(t=>{
+                t.isStarted() && t.stop()
+            }
+            )
+        }
+        ),
+        this._spriteGroups.forEach(e=>{
+            e.sprites.forEach(t=>{
+                t.isVisible = !1
+            }
+            )
+        }
+        )
+    }
+    show() {
+        this._centerNode.setEnabled(!0),
+        this._centerNode.getChildren().forEach(e=>{
+            e.setEnabled(!0),
+            e.getChildMeshes().forEach(t=>{
+                t.setEnabled(!0)
+            }
+            )
+        }
+        ),
+        this._particleGroups.forEach(e=>{
+            e.systems.forEach(t=>{
+                t.start()
+            }
+            )
+        }
+        ),
+        this._spriteGroups.forEach(e=>{
+            e.sprites.forEach(t=>{
+                t.isVisible = !0
+            }
+            )
+        }
+        )
+    }
+    get totalFrame() {
+        return this._endFrame
+    }
+    load(e) {
+        return new Promise((t,r)=>{
+            const n = e.Mesh
+              , o = e.Sprite
+              , a = e.Material
+              , s = e.Glow
+              , l = e.HighLight
+              , u = e.Particle
+              , c = e.MeshTrack
+              , h = e.ParticleTrack
+              , f = e.MaterialTrack;
+            this._animGroup.name += e.Type;
+            const d = Date.now();
+            this._centerNode.name += e.Type;
+            const _ = new Array
+              , g = new Array;
+            n != null && n.forEach(m=>{
+                _.push(this.loadMesh(m))
+            }
+            ),
+            o != null && o.forEach(m=>{
+                g.push(this.loadSprite(m))
+            }
+            ),
+            Promise.all(_).then(()=>{
+                a != null && a.forEach(m=>{
+                    g.push(this.loadMaterial(m))
+                }
+                ),
+                u != null && u.forEach(m=>{
+                    g.push(this.loadParticle(m))
+                }
+                ),
+                Promise.all(g).then(()=>{
+                    if (this._isDisposing) {
+                        const v = Date.now() - d;
+                        log$A.info(`subSequence: Load ${e.Type} takes ${v} ms`),
+                        t(this);
+                        return
+                    }
+                    if (s != null)
+                        for (const v of s)
+                            this.loadGlow(v);
+                    if (l != null)
+                        for (const v of l)
+                            this.loadHighLight(v);
+                    c != null && c.forEach(v=>{
+                        const y = this._meshGroups.get(v.targetName);
+                        if (y != null) {
+                            const b = this.loadTrackToAnim(v);
+                            ROOT_MESH_ANIM_PROPERTY.indexOf(b.animation.targetProperty) == -1 ? y.getChildMeshes().forEach(T=>{
+                                b.animation.targetProperty in T && this._animGroup.addTargetedAnimation(b.animation, T)
+                            }
+                            ) : this._animGroup.addTargetedAnimation(b.animation, y)
+                        }
+                    }
+                    ),
+                    h != null && h.forEach(v=>{
+                        var C;
+                        const y = v.index
+                          , b = v.targetName
+                          , T = (C = this._particleGroups.get(b)) == null ? void 0 : C.systems[y];
+                        if (T != null) {
+                            const A = this.loadTrackToAnim(v);
+                            this._animGroup.addTargetedAnimation(A.animation, T)
+                        }
+                    }
+                    ),
+                    f != null && f.forEach(v=>{
+                        const y = this._materialGroups.get(v.targetName);
+                        if (y) {
+                            const b = y[0];
+                            if (b != null)
+                                if (b.getBlockByName(v.blockName) != null) {
+                                    const T = this.loadTrackToAnim(v);
+                                    y == null || y.forEach(C=>{
+                                        this._animGroup.addTargetedAnimation(T.animation, C.getBlockByName(v.blockName))
+                                    }
+                                    )
+                                } else
+                                    console.error("property " + v.property + "is not in " + b.name)
+                        }
+                    }
+                    );
+                    const m = Date.now() - d;
+                    log$A.info(`subSequence: Load ${e.Type} takes ${m} ms`),
+                    t(this)
+                }
+                , ()=>{
+                    log$A.error(`subSequence: Load ${e.Type} fail`),
+                    r()
+                }
+                )
+            }
+            , ()=>{
+                r()
+            }
+            )
+        }
+        )
+    }
+    loadMesh(e) {
+        return new Promise((t,r)=>{
+            const n = this._rootDir + e.uri;
+            this.urlTransformer(n).then(o=>{
+                if (this._isDisposing) {
+                    t();
+                    return
+                }
+                SceneLoader.LoadAssetContainer("", o, this._scene, a=>{
+                    if (this._isDisposing) {
+                        a.removeAllFromScene(),
+                        t();
+                        return
+                    }
+                    a.animationGroups.forEach(l=>{
+                        l.stop()
+                    }
+                    ),
+                    a.animationGroups.length != 0 && (a.animationGroups.forEach(l=>{
+                        l.targetedAnimations.forEach(u=>{
+                            this._animGroup.addTargetedAnimation(u.animation, u.target)
+                        }
+                        ),
+                        l.dispose()
+                    }
+                    ),
+                    a.animationGroups = [],
+                    a.animations = [],
+                    a.materials = []);
+                    const s = new TransformNode("__root__" + e.name,this._scene);
+                    if (e.uri.split(".")[1] == "glb")
+                        a.meshes[0].parent = s;
+                    else if (e.uri.split(".")[1] == "obj") {
+                        const l = new TransformNode("__root__",this._scene);
+                        a.meshes.forEach(u=>{
+                            u.parent = l,
+                            u.Type = MESH_TAG
+                        }
+                        ),
+                        l.parent = s
+                    }
+                    s.getChildMeshes().forEach(l=>{
+                        e.isPickable != null ? l.isPickable = e.isPickable : l.isPickable = !1,
+                        l.xtype = "XSubSequence"
+                    }
+                    ),
+                    this._meshGroups.set(e.name, s),
+                    s.parent = this._centerNode,
+                    a.addAllToScene(),
+                    t()
+                }
+                , ()=>{}
+                , ()=>{
+                    log$A.error("subSequence:Load effect mesh fail"),
+                    log$A.error(`Effect Mesh ${e.name} load error`),
+                    r()
+                }
+                , ".glb")
+            }
+            , ()=>{
+                log$A.error("http:Get effect mesh fail"),
+                log$A.error(`Effect Mesh ${e.name} load error`),
+                r()
+            }
+            )
+        }
+        )
+    }
+    loadSprite(e) {
+        return new Promise((t,r)=>{
+            if (this._isDisposing) {
+                t();
+                return
+            }
+            const n = this._rootDir + e.uri;
+            if (e.uri !== "") {
+                e.name;
+                const o = new XMLHttpRequest;
+                o.open("get", n),
+                o.send(null),
+                o.onload = ()=>{
+                    if (o.status == 200) {
+                        const a = JSON.parse(o.responseText)
+                          , s = XSpriteManager.Parse(a, this._scene, this._rootDir);
+                        s.sprites.forEach(l=>{
+                            l.stopAnimation()
+                        }
+                        ),
+                        this._spriteGroups.set(e.name, s),
+                        t()
+                    } else
+                        log$A.error("subSequence:Load effect sprite fail"),
+                        log$A.error(`Effect Sprite ${e.name} load error`),
+                        r()
+                }
+            }
+        }
+        )
+    }
+    loadMaterial(e) {
+        return new Promise((t,r)=>{
+            if (this._isDisposing) {
+                t();
+                return
+            }
+            const n = this._rootDir + e.uri;
+            if (e.uri !== "") {
+                const o = e.name
+                  , a = new NodeMaterial(`material_${o}`,this._scene,{
+                    emitComments: !1
+                });
+                a.backFaceCulling = !1,
+                this.urlTransformer(n).then(s=>{
+                    if (this._isDisposing) {
+                        a.dispose(!1, !0, !1),
+                        t();
+                        return
+                    }
+                    a.loadAsync(s).then(()=>{
+                        if (this._isDisposing) {
+                            a.dispose(!0, !0, !1),
+                            t();
+                            return
+                        }
+                        a.build(!1);
+                        const l = new Array;
+                        let u = !1;
+                        for (let c = 0; c < e.meshName.length; c++)
+                            this._meshGroups.forEach(h=>{
+                                h.getChildMeshes().forEach(f=>{
+                                    var d;
+                                    if (f.name === e.meshName[c]) {
+                                        u = !0,
+                                        (d = f.material) == null || d.dispose(!0, !0);
+                                        const _ = f;
+                                        if (_.skeleton == null) {
+                                            const g = a;
+                                            _.material = g,
+                                            l.push(g)
+                                        } else if (_.numBoneInfluencers = 4,
+                                        _.computeBonesUsingShaders = !0,
+                                        c == 0) {
+                                            const g = a;
+                                            _.material = g,
+                                            l.push(g)
+                                        } else {
+                                            const g = a.clone(`material_${o}` + String(c), !1);
+                                            _.material = g,
+                                            l.push(g)
+                                        }
+                                    }
+                                }
+                                )
+                            }
+                            );
+                        u ? this._materialGroups.set(e.name, l) : a.dispose(!0, !0),
+                        t()
+                    }
+                    , ()=>{
+                        log$A.error("http:Get effect Material fail"),
+                        log$A.error(`Effect NodeMaterial ${o} load error`),
+                        r()
+                    }
+                    )
+                }
+                )
+            }
+        }
+        )
+    }
+    async loadGlow(e) {
+        const t = new GlowLayer(e.name,this._scene,{
+            blurKernelSize: e.blurKernelSize
+        });
+        t.intensity = e.intensity,
+        e.meshName.forEach(r=>{
+            const n = this._scene.getMeshByName(r);
+            n != null && t.addIncludedOnlyMesh(n)
+        }
+        ),
+        this._glowGroups.set(e.name, t)
+    }
+    loadHighLight(e) {
+        const t = new HighlightLayer(e.name,this._scene);
+        e.meshName.forEach(r=>{
+            const n = this._scene.getMeshByName(r);
+            if (n != null) {
+                const o = new Color3(e.color[0],e.color[1],e.color[2]);
+                t.addMesh(n, o)
+            }
+        }
+        ),
+        this._highLightGroups.set(e.name, t)
+    }
+    loadParticle(e) {
+        return new Promise((t,r)=>{
+            const n = this._rootDir + e.rootDir
+              , o = new XParticleManager(this._scene);
+            this.urlTransformer(n + e.uri).then(a=>{
+                if (this._isDisposing) {
+                    t();
+                    return
+                }
+                o.load(n, e.uri, e.name).then(s=>{
+                    if (this._isDisposing) {
+                        r();
+                        return
+                    }
+                    this._particleGroups.set(e.name, s),
+                    t()
+                }
+                , ()=>{
+                    log$A.error(`SubSequence: ${e.name} particle load fail`),
+                    r()
+                }
+                )
+            }
+            , ()=>{
+                log$A.error(`http: ${n + e.uri} load fail`),
+                r()
+            }
+            )
+        }
+        )
+    }
+}

+ 27 - 0
XSpriteManager.js

@@ -0,0 +1,27 @@
+let log$A = new Logger$1("subSequence")
+  , DEFAULT_FRAME_RATE = 30
+  , ROOT_MESH_ANIM_PROPERTY = ["scaling", "position", "rotation"]
+  , MESH_TAG = "XSubSequence";
+class XSpriteManager extends SpriteManager {
+    constructor(e, t, r, n, o) {
+        super(e, t, r, n, o);
+        E(this, "originalPositions");
+        this.originalPositions = new Array,
+        this.sprites.forEach(a=>{
+            this.originalPositions.push(a.position)
+        }
+        )
+    }
+    static Parse(e, t, r) {
+        const n = new XSpriteManager(e.name,"",e.capacity,{
+            width: e.cellWidth,
+            height: e.cellHeight
+        },t);
+        e.texture ? n.texture = Texture.Parse(e.texture, t, r) : e.textureName && (n.texture = new Texture(r + e.textureUrl,t,!0,e.invertY !== void 0 ? e.invertY : !0));
+        for (const o of e.sprites) {
+            const a = Sprite.Parse(o, n);
+            n.originalPositions.push(a.position)
+        }
+        return n
+    }
+}

+ 184 - 0
XStateMachine.js

@@ -0,0 +1,184 @@
+const log$G = new Logger$1("XStateMachine");
+class XStateMachine {
+    constructor(e) {
+        E(this, "state");
+        E(this, "isMoving");
+        E(this, "isRotating");
+        E(this, "_observer");
+        E(this, "_movingObserver");
+        E(this, "_scene");
+        this._scene = e
+    }
+    rotateTo(e, t, r, n) {
+        return new Promise((o,a)=>{
+            var h;
+            const s = e.avatarManager.scene;
+            if (r && e.setRotation(r),
+            t == r)
+                return o();
+            e.priority === 0 && log$G.info(`avatar ${e.id} starts to rotate from ${r} to ${t}`);
+            let l = 0;
+            const u = 1e3 / 25
+              , c = calcDistance3DAngle(t, e.rotation) / u;
+            this._movingObserver && s.onBeforeRenderObservable.remove(this._movingObserver),
+            (h = e.controller) == null || h.playAnimation(n || "Walking", !0),
+            this._movingObserver = s == null ? void 0 : s.onBeforeRenderObservable.add(()=>{
+                var f;
+                if (l < 1) {
+                    if (!e.rootNode)
+                        return e.setRotation(t),
+                        o();
+                    const d = Vector3.Lerp(e.rootNode.rotation, ue4Rotation2Xverse(t), l);
+                    e.setRotation(xverseRotation2Ue4(d)),
+                    l += u / (c * 1e3)
+                } else
+                    return s.onBeforeRenderObservable.remove(this._movingObserver),
+                    (f = e.controller) == null || f.playAnimation("Idle", !0),
+                    o()
+            }
+            )
+        }
+        )
+    }
+    _filterPathPoint(e) {
+        let t = 0;
+        const r = 1e-4;
+        if (e.length <= 1)
+            return [];
+        for (; t < e.length - 1; )
+            calcDistance3D(e[t], e[t + 1]) < r ? e.splice(t, 1) : t++;
+        return e
+    }
+    moveTo(e, t, r, n, o, a) {
+        return new Promise((s,l)=>{
+            var m;
+            const u = e.avatarManager.scene;
+            e.priority === 0 && log$G.info(`avatar ${e.id} starts to move from ${t} to ${r}`);
+            let c = 0;
+            a ? a = a.concat(r) : a = [r],
+            a = this._filterPathPoint(a);
+            let h = t
+              , f = a.shift();
+            if (!f)
+                return l("[Engine input path error]");
+            let d = calcDistance3D(h, f) / n;
+            const _ = 1e3 / 25;
+            e.rootNode.lookAt(ue4Position2Xverse(f));
+            const g = xverseRotation2Ue4({
+                x: e.rootNode.rotation.x,
+                y: e.rootNode.rotation.y,
+                z: e.rootNode.rotation.z
+            });
+            g && (g.roll = 0,
+            g.pitch = 0,
+            e.setRotation(g)),
+            this._movingObserver && u.onBeforeRenderObservable.remove(this._movingObserver),
+            (m = e.controller) == null || m.playAnimation(o, !0),
+            this._movingObserver = u == null ? void 0 : u.onBeforeRenderObservable.add(()=>{
+                var v;
+                if (c < 1) {
+                    const y = Vector3.Lerp(ue4Position2Xverse(h), ue4Position2Xverse(f), c);
+                    if (e.setPosition(xversePosition2Ue4(y)),
+                    !e.rootNode)
+                        return e.setPosition(r),
+                        s();
+                    c += _ / (d * 1e3)
+                } else if (h = f,
+                f = a.shift(),
+                f) {
+                    d = calcDistance3D(h, f) / n,
+                    e.rootNode.lookAt(ue4Position2Xverse(f));
+                    const y = xverseRotation2Ue4({
+                        x: e.rootNode.rotation.x,
+                        y: e.rootNode.rotation.y,
+                        z: e.rootNode.rotation.z
+                    });
+                    y && (y.roll = 0,
+                    y.pitch = 0,
+                    e.setRotation(y)),
+                    c = 0
+                } else
+                    return u.onBeforeRenderObservable.remove(this._movingObserver),
+                    (v = e.controller) == null || v.playAnimation("Idle", !0),
+                    s()
+            }
+            )
+        }
+        )
+    }
+    lookAt(e, t, r) {
+        return new Promise(n=>{
+            var _, g;
+            const o = ue4Position2Xverse(t)
+              , s = e.rootNode.position.subtract(o).length()
+              , l = new Vector3(s * Math.sin(e.rootNode.rotation.y),0,s * Math.cos(e.rootNode.rotation.y))
+              , u = (_ = e.rootNode) == null ? void 0 : _.position.add(l);
+            let c = 0;
+            const h = r || 1 / 100
+              , f = (g = e.rootNode) == null ? void 0 : g.getScene()
+              , d = f == null ? void 0 : f.onBeforeRenderObservable.add(()=>{
+                var y, b;
+                const m = (y = e.controller) == null ? void 0 : y.animations.find(T=>T.name == "Idle");
+                (m == null ? void 0 : m.isPlaying) != !0 && (m == null || m.play());
+                const v = Vector3.Lerp(u, o, c);
+                c < 1 ? ((b = e.rootNode) == null || b.lookAt(v),
+                c += h) : (d && f.onBeforeRenderObservable.remove(d),
+                n())
+            }
+            )
+        }
+        )
+    }
+    sendObjectTo(e, t, r, n=2, o=10, a={
+        x: 0,
+        y: 0,
+        z: 150
+    }) {
+        return new Promise((s,l)=>{
+            var u;
+            if (!r.loaded)
+                l("Gift has not inited!");
+            else {
+                const c = (u = e.rootNode) == null ? void 0 : u.getScene();
+                let h = 0;
+                const f = 1 / (n * 25)
+                  , d = f
+                  , _ = o / 100
+                  , g = 8 * _ * f * f;
+                let m = .5 * g / f
+                  , v = ue4Position2Xverse(e.position);
+                const y = ue4Position2Xverse(a)
+                  , b = ue4Position2Xverse(e.position)
+                  , T = c == null ? void 0 : c.onBeforeRenderObservable.add(()=>{
+                    (!t || !e.position || !t.position) && (T && c.onBeforeRenderObservable.remove(T),
+                    l("Invalid receiver when shoot gift!")),
+                    r.loaded || (T && c.onBeforeRenderObservable.remove(T),
+                    s());
+                    const C = ue4Position2Xverse(t.position)
+                      , A = new Vector3((C.x - b.x) * f,m,(C.z - b.z) * f);
+                    m = m - g,
+                    h < 1 ? (v = v.add(A),
+                    r.setPositionVector(v.add(y)),
+                    h += d) : (s(),
+                    T && c.onBeforeRenderObservable.remove(T))
+                }
+                )
+            }
+        }
+        )
+    }
+    roll(e, t, r, n) {
+        var o, a;
+        this._observer && ((o = this._scene) == null || o.onBeforeRenderObservable.remove(this._observer)),
+        t && (r = r != null ? r : 1,
+        n = n != null ? n : 1,
+        this._observer = (a = this._scene) == null ? void 0 : a.onBeforeRenderObservable.add(()=>{
+            e.rootNode.rotation.y += r * .1 * n,
+            e.rootNode.rotation.y %= Math.PI * 2
+        }
+        ))
+    }
+    disposeObsever() {
+        this._movingObserver && this._scene.onBeforeRenderObservable.remove(this._movingObserver)
+    }
+}

+ 181 - 0
XStaticMesh.js

@@ -0,0 +1,181 @@
+const log$z = new Logger$1("XStaticMesh");
+class XStaticMesh {
+    constructor({id: e, mesh: t, group: r="default", lod: n=0, xtype: o=EMeshType.XStaticMesh, skinInfo: a="default", url: s=""}) {
+        E(this, "_mesh");
+        E(this, "_id", "-1");
+        E(this, "_group");
+        E(this, "_lod");
+        E(this, "_isMoving", !1);
+        E(this, "_isRotating", !1);
+        E(this, "_isVisible", !0);
+        E(this, "_skinInfo");
+        E(this, "setVisibility", (e,t)=>{
+            Array.isArray(e) ? e.forEach(r=>{
+                this.setVisibility(r, t)
+            }
+            ) : e.isAnInstance || (e.visibility = t)
+        }
+        );
+        E(this, "setPickable", (e,t)=>{
+            Array.isArray(e) ? e.forEach(r=>{
+                this.setPickable(r, t)
+            }
+            ) : ("isPickable"in e && (e.isPickable = t),
+            e.setEnabled(t))
+        }
+        );
+        E(this, "hide", ()=>{
+            var e;
+            this._isVisible = !1,
+            this.mesh && this.setVisibility(this.mesh, 0),
+            this.mesh && this.setPickable(this.mesh, !1),
+            (e = this.mesh) == null || e.getChildMeshes().forEach(t=>{
+                this.setVisibility(t, 0),
+                this.setPickable(t, !1)
+            }
+            )
+        }
+        );
+        E(this, "show", ()=>{
+            var e;
+            this._isVisible = !0,
+            this.mesh && this.setVisibility(this.mesh, 1),
+            this.mesh && this.setPickable(this.mesh, !0),
+            (e = this.mesh) == null || e.getChildMeshes().forEach(t=>{
+                this.setVisibility(t, 1),
+                this.setPickable(t, !0)
+            }
+            )
+        }
+        );
+        E(this, "attachToAvatar", (e,t={
+            x: 0,
+            y: .5,
+            z: 0
+        },r={
+            yaw: 0,
+            pitch: 0,
+            roll: 0
+        },n={
+            x: .35,
+            y: .35,
+            z: .35
+        })=>{
+            const o = ue4Scaling2Xverse(n)
+              , a = ue4Rotation2Xverse(r)
+              , s = ue4Position2Xverse(t)
+              , l = this._mesh;
+            e && l ? (e.setParent(l),
+            e.position = s,
+            e.rotation = a,
+            e.scaling = o) : log$z.error("[Engine] avatar or attachment not found!")
+        }
+        );
+        E(this, "detachFromAvatar", (e,t=!1)=>{
+            this._mesh && e ? this._mesh.removeChild(e) : log$z.error("[Engine] avatar not found!")
+        }
+        );
+        this._id = e,
+        this._mesh = t,
+        this._group = r,
+        this._lod = n,
+        this._skinInfo = a,
+        this.unallowMove(),
+        this._mesh.xtype = o,
+        this._mesh.xid = e,
+        this._mesh.xgroup = this._group,
+        this._mesh.xlod = this._lod,
+        this._mesh.xskinInfo = this._skinInfo,
+        this._mesh.xurl = s
+    }
+    get mesh() {
+        return this._mesh
+    }
+    get position() {
+        var o;
+        if (!this._mesh)
+            return null;
+        const {x: e, y: t, z: r} = (o = this._mesh) == null ? void 0 : o.position;
+        return xversePosition2Ue4({
+            x: e,
+            y: t,
+            z: r
+        })
+    }
+    get id() {
+        return this._id
+    }
+    get group() {
+        return this._group
+    }
+    get isMoving() {
+        return this._isMoving
+    }
+    get isVisible() {
+        return this._isVisible
+    }
+    get isRotating() {
+        return this._isRotating
+    }
+    get skinInfo() {
+        return this._skinInfo
+    }
+    allowMove() {
+        this._mesh != null && (this._mesh.getChildMeshes().forEach(e=>{
+            e.unfreezeWorldMatrix()
+        }
+        ),
+        this._mesh.unfreezeWorldMatrix())
+    }
+    unallowMove() {
+        this._mesh != null && (this._mesh.getChildMeshes().forEach(e=>{
+            e.freezeWorldMatrix()
+        }
+        ),
+        this._mesh.freezeWorldMatrix())
+    }
+    getID() {
+        return this._id
+    }
+    setPosition(e) {
+        if (this._mesh) {
+            const t = ue4Position2Xverse(e);
+            this._mesh.position = t
+        } else
+            log$z.error("[Engine] no root for positioning")
+    }
+    setRotation(e) {
+        const t = ue4Rotation2Xverse_mesh(e);
+        this._mesh ? this._mesh.rotation = t : log$z.error("[Engine] no root for rotating")
+    }
+    setScale(e) {
+        this._mesh ? this._mesh.scaling = new Vector3(e,e,-e) : log$z.error("[Engine] no root for scaling")
+    }
+    disableAvatar() {
+        var e;
+        (e = this._mesh) == null || e.setEnabled(!1)
+    }
+    enableAvatar() {
+        var e;
+        (e = this._mesh) == null || e.setEnabled(!0)
+    }
+    togglePickable(e) {
+        var t;
+        (t = this.mesh) == null || t.getChildMeshes().forEach(r=>{
+            "instances"in r && "isPickable"in r && (r.isPickable = e)
+        }
+        ),
+        this.mesh != null && "isPickable"in this.mesh && (this.mesh.isPickable = e)
+    }
+    setMaterial(e) {
+        var t;
+        (t = this.mesh) == null || t.getChildMeshes().forEach(r=>{
+            "instances"in r && "material"in r && (r.material = e)
+        }
+        ),
+        this.mesh != null && "material"in this.mesh && (this.mesh.material = e)
+    }
+    dispose(e=!1, t=!1) {
+        !this.mesh.isDisposed() && this.mesh.dispose(e, t)
+    }
+}

+ 576 - 0
XStaticMeshComponent.js

@@ -0,0 +1,576 @@
+const log$u = new Logger$1("XStaticMeshComponent")
+class XStaticMeshComponent{
+    constructor(e) {
+        E(this, "scene");
+        E(this, "_staticmeshes");
+        E(this, "_lowModel_group");
+        E(this, "_CgPlane");
+        E(this, "_rootDir");
+        E(this, "_abosoluteUrl");
+        E(this, "_partMeshSkinInfo");
+        E(this, "_meshInfoJson");
+        E(this, "_orijson");
+        E(this, "_notUsedRegionLists");
+        E(this, "_meshInfoKeys");
+        E(this, "_currentUpdateRegionCount");
+        E(this, "_currentMeshUsedLod");
+        E(this, "_currentPartGroup");
+        E(this, "_allowRegionUpdate");
+        E(this, "_allowRegionForceLod");
+        E(this, "_forceLod");
+        E(this, "_scenemanager");
+        E(this, "regionIdInCamera");
+        E(this, "regionIdInCameraConst");
+        E(this, "_cameraInRegionId");
+        E(this, "_meshVis");
+        E(this, "_doMeshVisChangeNumber");
+        E(this, "_meshVisTypeName");
+        E(this, "_visCheckDurationFrameNumber");
+        E(this, "_regionLodRule");
+        E(this, "reg_staticmesh_partupdate", ()=>{
+            if (this._allowRegionUpdate && (this.scene.getFrameId(),
+            this._meshUpdateFrame()),
+            this._allowRegionForceLod) {
+                this.scene.getFrameId() % 2 == 0 && this.setOneRegionLod(this._meshInfoKeys[this._currentUpdateRegionCount % this._meshInfoKeys.length].toString(), this._forceLod);
+                let t = !0;
+                const r = Array.from(this._currentMeshUsedLod.keys());
+                if (r.length > 0) {
+                    for (let n = 0; n < r.length; ++n)
+                        this._currentMeshUsedLod.get(r[n]) != this._forceLod && (t = !1);
+                    t && (this._allowRegionForceLod = !1)
+                }
+            }
+        }
+        );
+        E(this, "setMeshInfo", (e,t="")=>{
+            this._abosoluteUrl != e && (this._abosoluteUrl.length > 0 && this.deleteLastRegionMesh(),
+            this._partMeshSkinInfo = t,
+            this._abosoluteUrl = e,
+            this._rootDir = this._abosoluteUrl.slice(0, -4) + "/",
+            this.parseJson(this._rootDir + "meshInfo.json").then(()=>{
+                this.startMeshUpdate()
+            }
+            ))
+        }
+        );
+        E(this, "_meshUpdateFrame", ()=>{
+            {
+                let e = this._meshInfoKeys[this._currentUpdateRegionCount % this._meshInfoKeys.length];
+                const t = !0;
+                let r = 3;
+                if (this._scenemanager != null && this._scenemanager.cameraComponent != null) {
+                    const n = this._getMainPlayerPosition();
+                    if (n != null) {
+                        if (this._cameraInRegionId >= 0) {
+                            const a = this.getRegionIdWhichIncludeCamera(n);
+                            (this._cameraInRegionId != a || this.regionIdInCamera.length == 0) && (this._cameraInRegionId = a,
+                            this.regionIdInCamera = this._getNeighborId(this._cameraInRegionId.toString()),
+                            this.regionIdInCameraConst = this.regionIdInCamera.slice());
+                            let s = this.regionIdInCamera.pop();
+                            for (; s != null; )
+                                if (this._notUsedRegionLists.indexOf(s) >= 0)
+                                    s = this.regionIdInCamera.pop();
+                                else
+                                    break;
+                            s != null && (e = s.toString())
+                        } else
+                            this._cameraInRegionId = this.getRegionIdWhichIncludeCamera(n);
+                        if (this._currentMeshUsedLod.size == 0 || this._notUsedRegionLists.indexOf(parseInt(e)) >= 0) {
+                            e = this._cameraInRegionId.toString();
+                            const a = this._getNeighborId(e);
+                            for (; a.length == 0 && (e = this.getNearestRegionIdWithCamera(n).toString()),
+                            this._notUsedRegionLists.indexOf(parseInt(e)) >= 0; )
+                                e = a.pop().toString()
+                        }
+                        const o = this._meshInfoJson[this._cameraInRegionId.toString()].lod;
+                        r = 3,
+                        this._cameraInRegionId.toString() == e ? r = this._regionLodRule[0] : o[0].indexOf(parseInt(e)) >= 0 ? r = this._regionLodRule[1] : o[1].indexOf(parseInt(e)) >= 0 ? r = this._regionLodRule[2] : o[2].indexOf(parseInt(e)) >= 0 ? r = this._regionLodRule[3] : r = this._regionLodRule[4]
+                    }
+                }
+                this.setOneRegionLod(e, r, t),
+                this.updateRegionNotInLocalNeighbor(),
+                this.cleanRootNodes()
+            }
+        }
+        );
+        E(this, "updateRegionNotInLocalNeighbor", ()=>{
+            Array.from(this._currentMeshUsedLod.keys()).forEach(t=>{
+                this.regionIdInCameraConst.indexOf(parseInt(t)) < 0 && this.setOneRegionLod(t, -1)
+            }
+            )
+        }
+        );
+        E(this, "cleanRootNodes", ()=>{
+            if (this.scene.getFrameId() % 3 == 0) {
+                const e = [];
+                this.scene.rootNodes.forEach(t=>{
+                    (t.getClassName() == "TransformNode" && t.getChildren().length == 0 || t.getClassName() == "Mesh" && t.name == "__root__" && t.getChildren().length == 0) && e.push(t)
+                }
+                ),
+                e.forEach(t=>{
+                    t.dispose()
+                }
+                )
+            }
+        }
+        );
+        E(this, "setOneRegionLod", (e,t,r=!0)=>{
+            this._currentUpdateRegionCount++;
+            const n = this._calHashCode(this._rootDir)
+              , o = "region_" + n + "_" + e;
+            if (t < 0) {
+                this._currentMeshUsedLod.has(e) && (this._currentMeshUsedLod.delete(e),
+                this._currentPartGroup.delete(o),
+                this.deleteMeshesByCustomProperty("group", "region_" + n + "_" + e));
+                return
+            }
+            const a = this._rootDir + e + "_lod" + t + "_xverse.glb"
+              , s = this._currentMeshUsedLod.get(e);
+            this._currentPartGroup.add(o),
+            s != null ? s != t && (this._currentMeshUsedLod.set(e, t),
+            this._scenemanager.addNewLowPolyMesh({
+                url: a,
+                group: "region_" + n + "_" + e,
+                pick: !0,
+                lod: t,
+                skinInfo: this._partMeshSkinInfo
+            }, [{
+                group: "region_" + n + "_" + e,
+                mode: 0
+            }])) : (this._currentMeshUsedLod.set(e, t),
+            this._scenemanager.addNewLowPolyMesh({
+                url: a,
+                group: "region_" + n + "_" + e,
+                pick: !0,
+                lod: t,
+                skinInfo: this._partMeshSkinInfo
+            }))
+        }
+        );
+        E(this, "checkPointInView", ({x: e, y: t, z: r})=>{
+            const n = ue4Position2Xverse({
+                x: e,
+                y: t,
+                z: r
+            });
+            if (!n)
+                return !1;
+            for (let o = 0; o < 6; o++)
+                if (this.scene.frustumPlanes[o].dotCoordinate(n) < 0)
+                    return !1;
+            return !0
+        }
+        );
+        E(this, "addNewLowPolyMesh", (e,t,r)=>{
+            if (!e.url.endsWith("glb") && !e.url.startsWith("blob:"))
+                return e.url.endsWith("zip") ? (this.setMeshInfo(e.url, e.skinInfo),
+                Promise.resolve(!0)) : (log$u.error("[Engine] input model path is error! ", e.url),
+                Promise.reject(new XLowpolyModelError("[Engine] input model path is error! " + e.url)));
+            {
+                const n = e.url;
+                return new Promise((o,a)=>this._scenemanager.urlTransformer(e.url).then(s=>{
+                    e.url = s;
+                    const l = new XStaticMeshFromOneGltf(this.scene,e)
+                      , u = Date.now();
+                    return new Promise((c,h)=>{
+                        l.loadMesh(r, !0).then(f=>{
+                            const d = Date.now();
+                            if (this._scenemanager.engineRunTimeStats.timeArray_loadStaticMesh.add(d - u),
+                            f == !0) {
+                                const _ = this.getLowModelType(e);
+                                let g = 0;
+                                if (this._lowModel_group.has(_) && (g = this._lowModel_group.get(_).length),
+                                r != null && this._scenemanager.currentShader != null && this._scenemanager.currentShader.name != r.name && l.setMaterial(this._scenemanager.currentShader),
+                                this._allowRegionUpdate == !1 && _.startsWith("region_"))
+                                    l.dispose();
+                                else if (this._staticmeshes.push(l),
+                                this.lowmodelGroupMapAddValue(_, l),
+                                t != null && t.length > 0) {
+                                    const m = [];
+                                    for (let v = 0; v < t.length; ++v)
+                                        m.push(t[v].group),
+                                        this.updateLowModelGroup(t[v], _, g)
+                                }
+                                this._scenemanager.engineRunTimeStats.timeArray_updateStaticMesh.add(Date.now() - d),
+                                c(!0)
+                            } else
+                                h(new XLowpolyModelError("[Engine] after lowmodel error!"))
+                        }
+                        ).catch(f=>{
+                            log$u.error("[Engine] load Mesh [" + n + "] error! " + f),
+                            h(new XLowpolyModelError(`[Engine] load Mesh [${n}] error! ${f}`))
+                        }
+                        )
+                    }
+                    )
+                }
+                ).then(s=>{
+                    s == !0 ? (log$u.info(`[Engine] load Mesh [${n}] successfully.`),
+                    o(!0)) : a(!1)
+                }
+                ).catch(s=>{
+                    log$u.error("[Engine] addNewLowPolyMesh [" + n + "] error! " + s),
+                    a(new XLowpolyModelError(`[Engine] addNewLowPolyMesh [${n}] error! ${s}`))
+                }
+                ))
+            }
+        }
+        );
+        E(this, "toggleLowModelVisibility", e=>{
+            const {vis: t, groupName: r="", skinInfo: n=""} = e;
+            this._meshVis = t,
+            this._meshVisTypeName = {
+                groupName: r,
+                skinInfo: n
+            },
+            this._doMeshVisChangeNumber = 0,
+            r == Te.ALL_MESHES || this._currentPartGroup.has(r) == !0 || this._partMeshSkinInfo == n ? t == !1 ? (this._visCheckDurationFrameNumber = 100,
+            this.stopMeshUpdate()) : (this._visCheckDurationFrameNumber = 1,
+            this.startMeshUpdate()) : this._visCheckDurationFrameNumber = 1
+        }
+        );
+        E(this, "reg_staticmesh_visibility", ()=>{
+            if (this._doMeshVisChangeNumber >= 0)
+                if (this._doMeshVisChangeNumber < this._visCheckDurationFrameNumber)
+                    if (this._doMeshVisChangeNumber = this._doMeshVisChangeNumber + 1,
+                    this._meshVisTypeName.groupName == Te.ALL_MESHES)
+                        this._lowModel_group.forEach((e,t)=>{
+                            for (let r = 0, n = e.length; r < n; ++r)
+                                e[r].toggleVisibility(this._meshVis)
+                        }
+                        );
+                    else {
+                        if (this._lowModel_group.has(this._meshVisTypeName.groupName))
+                            for (let e = 0; e < this._lowModel_group.get(this._meshVisTypeName.groupName).length; ++e)
+                                this._lowModel_group.get(this._meshVisTypeName.groupName)[e].toggleVisibility(this._meshVis);
+                        if (this._meshVisTypeName.skinInfo != "")
+                            for (let e = 0; e < this._staticmeshes.length; ++e)
+                                this._staticmeshes[e].skinInfo == this._meshVisTypeName.skinInfo && this._staticmeshes[e].toggleVisibility(this._meshVis)
+                    }
+                else
+                    this._meshVis = !0,
+                    this._meshVisTypeName = {
+                        groupName: "",
+                        skinInfo: ""
+                    },
+                    this._doMeshVisChangeNumber = -1
+        }
+        );
+        E(this, "_getMeshesByCustomProperty", (e,t)=>{
+            let r = [];
+            return this._staticmeshes.forEach(n=>{
+                n[e] != null && n[e] == t && (r = r.concat(n.meshes))
+            }
+            ),
+            r
+        }
+        );
+        this._lowModel_group = new Map,
+        this._staticmeshes = [],
+        this._meshInfoJson = null,
+        this._meshInfoKeys = [],
+        this._currentUpdateRegionCount = 0,
+        this._abosoluteUrl = "",
+        this._partMeshSkinInfo = "",
+        this._forceLod = 3,
+        this._allowRegionUpdate = !1,
+        this._allowRegionForceLod = !1,
+        this._currentMeshUsedLod = new Map,
+        this._currentPartGroup = new Set,
+        this._scenemanager = e,
+        this.scene = e.Scene,
+        this.regionIdInCamera = [],
+        this.regionIdInCameraConst = [],
+        this._cameraInRegionId = -1,
+        this._rootDir = "",
+        this._meshVis = !0,
+        this._meshVisTypeName = {
+            groupName: "",
+            skinInfo: ""
+        },
+        this._doMeshVisChangeNumber = -1,
+        this._visCheckDurationFrameNumber = -1,
+        this._regionLodRule = [0, 1, 3, 3, -1],
+        this.initCgLowModel(),
+        this._regionPartLoop()
+    }
+    get cameraInRegionId() {
+        return this._cameraInRegionId
+    }
+    setRegionLodRule(e) {
+        return e.length != 5 ? !1 : (e.forEach(t=>{}
+        ),
+        this._regionLodRule = e,
+        !0)
+    }
+    get lowModel_group() {
+        return this._lowModel_group
+    }
+    _regionPartLoop() {
+        this.scene.registerBeforeRender(this.reg_staticmesh_partupdate),
+        this.scene.registerAfterRender(this.reg_staticmesh_visibility)
+    }
+    _globalSearchCameraInWhichRegion(e, t) {
+        let r = -1;
+        for (let n = 0; n < t.length; ++n) {
+            const o = this._meshInfoJson[t[n].toString()].boundingbox
+              , a = o[0]
+              , s = o[1];
+            if (e.x >= a[0] && e.x <= s[0] && e.y >= a[1] && e.y <= s[1] && e.z >= a[2] && e.z <= s[2] || e.x >= s[0] && e.x <= a[0] && e.y >= s[1] && e.y <= a[1] && e.z >= s[2] && e.z <= a[2]) {
+                r = parseInt(t[n].toString());
+                break
+            }
+        }
+        return r
+    }
+    getRegionIdByPosition(e) {
+        return this.getRegionIdWhichIncludeCamera(e)
+    }
+    getRegionIdWhichIncludeCamera(e) {
+        let t = -1;
+        if (this._allowRegionUpdate == !1)
+            return t;
+        if (this._cameraInRegionId == -1 ? t = this._globalSearchCameraInWhichRegion(e, this._meshInfoKeys) : (t = this._globalSearchCameraInWhichRegion(e, this.regionIdInCameraConst),
+        t == -1 && (t = this._globalSearchCameraInWhichRegion(e, this._meshInfoKeys))),
+        t == -1) {
+            let r = 1e7;
+            for (let n = 0; n < this._meshInfoKeys.length; ++n) {
+                const o = this._meshInfoJson[this._meshInfoKeys[n]].center
+                  , a = Math.abs(e.x - o[0]) + Math.abs(e.y - o[1]);
+                r > a && (r = a,
+                t = parseInt(this._meshInfoKeys[n]))
+            }
+        }
+        return t
+    }
+    getNearestRegionIdWithCamera(e) {
+        let t = 1
+          , r = 1e7;
+        for (let n = 0; n < this._meshInfoKeys.length; ++n) {
+            if (this._notUsedRegionLists.indexOf(parseInt(this._meshInfoKeys[n])) >= 0)
+                continue;
+            const o = this._meshInfoJson[this._meshInfoKeys[n]].center
+              , a = Math.abs(e.x - o[0]) + Math.abs(e.y - o[1]);
+            r > a && (r = a,
+            t = parseInt(this._meshInfoKeys[n]))
+        }
+        return t
+    }
+    _getNeighborId(e) {
+        const t = this._meshInfoJson[e].lod;
+        let r = [];
+        const n = Object.keys(t);
+        for (let o = n.length - 1; o >= 0; --o)
+            r = r.concat(t[n[o]]);
+        return r.push(parseInt(e)),
+        r
+    }
+    _getMainPlayerPosition() {
+        const e = this._scenemanager.cameraComponent.getCameraPose().position
+          , t = this._scenemanager.avatarComponent.getMainAvatar();
+        if (t != null && t != null) {
+            const r = t.position;
+            if (r != null)
+                return r
+        }
+        return e
+    }
+    _calHashCode(e) {
+        return hashCode(e) + "_" + this._partMeshSkinInfo
+    }
+    forceAllRegionLod(e=3) {
+        e < 0 && (e = 0),
+        e > 3 && (e = 3),
+        this.stopMeshUpdate(),
+        this._allowRegionForceLod = !0,
+        this._forceLod = e
+    }
+    deleteLastRegionMesh() {
+        if (this._rootDir != "") {
+            const e = this._calHashCode(this._rootDir);
+            this._currentMeshUsedLod.clear(),
+            this._currentPartGroup.clear(),
+            this._meshInfoJson = null,
+            this._meshInfoKeys = [],
+            this._currentUpdateRegionCount = 0,
+            this._orijson = null,
+            this._notUsedRegionLists = [],
+            this._partMeshSkinInfo = "",
+            this._abosoluteUrl = "",
+            this.stopMeshUpdate(),
+            this.deleteMeshesByCustomProperty("group", "region_" + e, !0)
+        }
+    }
+    startMeshUpdate() {
+        this._allowRegionUpdate == !1 && this._meshInfoJson != null && this._abosoluteUrl != "" && this._meshInfoKeys.length > 0 && (this._allowRegionUpdate = !0)
+    }
+    stopMeshUpdate() {
+        this._allowRegionUpdate = !1
+    }
+    parseJson(e) {
+        return new Promise((t,r)=>this._scenemanager.urlTransformer(e).then(n=>{
+            const o = new XMLHttpRequest;
+            o.open("get", n),
+            o.send(null),
+            o.onload = ()=>{
+                if (o.status == 200) {
+                    const a = JSON.parse(o.responseText);
+                    this._orijson = a,
+                    this._meshInfoJson = this._orijson.usedRegion,
+                    this._notUsedRegionLists = this._orijson.notUsedRegion,
+                    this._meshInfoKeys = Object.keys(this._meshInfoJson),
+                    log$u.info("[Engine] parse zip mesh info successful"),
+                    t()
+                }
+            }
+            ,
+            o.onerror = ()=>{
+                log$u.error(`[Engine] load zip mesh info json error, (provided by blob): ${n}`),
+                r(new XLowpolyJsonError(`[Engine] load zip mesh info json error, (provided by blob): ${n}`))
+            }
+        }
+        ).catch(n=>{
+            log$u.error(`[Engine] load zip mesh info json error: ${n}, link:${e}`),
+            r(new XLowpolyJsonError(`[Engine] load zip mesh info json error: ${n}, link: ${e}`))
+        }
+        ))
+    }
+    initCgLowModel() {
+        const e = MeshBuilder.CreatePlane("CgPlane", {
+            size: 400
+        });
+        e.position = new Vector3(0,1010,0),
+        e.rotation = new Vector3(3 * Math.PI / 2,0,0),
+        this._CgPlane = new XStaticMesh({
+            id: "CgPlane",
+            mesh: e,
+            xtype: EMeshType.Cgplane
+        }),
+        this._CgPlane.hide()
+    }
+    getLowModelType(e) {
+        let t = "";
+        return e.group != null ? t = e.group : t = "default",
+        t
+    }
+    lowmodelGroupMapAddValue(e, t) {
+        const r = this._lowModel_group.get(e);
+        r != null ? (r.push(t),
+        this._lowModel_group.set(e, r)) : this._lowModel_group.set(e, [t])
+    }
+    updateLowModelGroup(e, t, r) {
+        let n = r;
+        e.group == t || (n = -1),
+        e.mode == 0 ? this.deleteLowModelGroup(e.group, n) : e.mode == 1 ? this.toggleVisibleLowModelGroup(!1, e.group, n) : e.mode == 2 && this.toggleVisibleLowModelGroup(!0, e.group, n)
+    }
+    toggleVisibleLowModelGroup(e, t, r=-1) {
+        if (this._lowModel_group.has(t)) {
+            const n = this._lowModel_group.get(t);
+            let o = n.length;
+            r >= 0 && o >= r && (o = r);
+            for (let a = 0; a < o; ++a)
+                n[a].toggleVisibility(e)
+        }
+    }
+    deleteLowModelGroup(e, t=-1) {
+        if (this._lowModel_group.has(e)) {
+            const o = this._lowModel_group.get(e);
+            let a = o.length;
+            t >= 0 && a >= t && (a = t);
+            for (let s = 0; s < a; ++s)
+                o[s].dispose();
+            t >= 0 ? this._lowModel_group.set(e, this._lowModel_group.get(e).slice(a)) : this._lowModel_group.delete(e)
+        }
+        const r = this._lowModel_group.get(e)
+          , n = [];
+        r != null && r.length > 0 ? this._staticmeshes.forEach(o=>{
+            if (o.group != e)
+                n.push(o);
+            else
+                for (let a = 0; a < r.length; ++a)
+                    o.groupUuid == r[a].groupUuid && n.push(o)
+        }
+        ) : this._staticmeshes.forEach(o=>{
+            o.group != e && n.push(o)
+        }
+        ),
+        this._staticmeshes = n
+    }
+    deleteMeshesByGroup(e) {
+        this.deleteLowModelGroup(e)
+    }
+    deleteMeshesById(e) {
+        this.deleteMeshesByCustomProperty("id", e)
+    }
+    deleteMeshesByLoD(e) {
+        this.deleteMeshesByCustomProperty("lod", e)
+    }
+    deleteMeshesBySkinInfo(e) {
+        this.deleteMeshesByCustomProperty("skinInfo", e)
+    }
+    removeMeshesFromSceneByGroup(e) {
+        this.removeMeshesFromSceneByCustomProperty("group", e)
+    }
+    removeMeshesFromSceneById(e) {
+        this.removeMeshesFromSceneByCustomProperty("id", e)
+    }
+    addMeshesToSceneByGroup(e) {
+        this.addMeshesToSceneByCustomProperty("group", e)
+    }
+    addMeshesToSceneById(e) {
+        this.addMeshesToSceneByCustomProperty("id", e)
+    }
+    removeMeshesFromSceneByCustomProperty(e, t, r=!1) {
+        this._staticmeshes.forEach(n=>{
+            n.isinscene && n[e] != null && (r ? n[e].indexOf(t) < 0 || n.removeFromScene() : n[e] != t || n.removeFromScene())
+        }
+        )
+    }
+    addMeshesToSceneByCustomProperty(e, t, r=!1) {
+        this._staticmeshes.forEach(n=>{
+            n.isinscene == !1 && n[e] != null && (r ? n[e].indexOf(t) < 0 || n.addToScene() : n[e] != t || n.addToScene())
+        }
+        )
+    }
+    deleteMeshesByCustomProperty(e, t, r=!1) {
+        const n = [];
+        this._staticmeshes.forEach(a=>{
+            a[e] != null && (r ? a[e].indexOf(t) < 0 ? n.push(a) : a.dispose() : a[e] != t ? n.push(a) : a.dispose())
+        }
+        ),
+        this._staticmeshes = n;
+        const o = Array.from(this._lowModel_group.keys());
+        for (let a = 0; a < o.length; ++a) {
+            const s = o[a]
+              , l = this._lowModel_group.get(s);
+            if (l != null) {
+                const u = [];
+                for (let c = 0; c < l.length; ++c)
+                    l[c][e] != null && (r ? l[c][e].indexOf(t) < 0 && u.push(l[c]) : l[c][e] != t && u.push(l[c]));
+                u.length > 0 ? this._lowModel_group.set(s, u) : this._lowModel_group.delete(s)
+            }
+        }
+    }
+    getMeshes() {
+        let e = [];
+        for (let t = 0; t < this._staticmeshes.length; ++t)
+            e = e.concat(this._staticmeshes[t].meshes);
+        return e
+    }
+    getCgMesh() {
+        return this._CgPlane
+    }
+    getMeshesByGroup(e="default") {
+        const t = this._lowModel_group.get(e);
+        if (t != null) {
+            let r = [];
+            for (let n = 0; n < t.length; ++n)
+                r = r.concat(t[n].meshes);
+            return r
+        } else
+            return null
+    }
+    getMeshesByGroup2(e="default") {
+        return this._getMeshesByCustomProperty("group", e)
+    }
+}

+ 125 - 0
XStaticMeshFromOneGltf.js

@@ -0,0 +1,125 @@
+class XStaticMeshFromOneGltf {
+    constructor(e, t) {
+        E(this, "_scene");
+        E(this, "_url");
+        E(this, "_group");
+        E(this, "_pickable");
+        E(this, "_meshes");
+        E(this, "_id");
+        E(this, "_lod");
+        E(this, "_groupUuid");
+        E(this, "_isInScene");
+        E(this, "_skinInfo");
+        E(this, "loadMesh", (e,t)=>{
+            const r = this._meshes.length
+              , n = t ? 1 : 0
+              , o = this._url;
+            return SceneLoader.LoadAssetContainerAsync("", o, this._scene, ()=>{
+                this._scene.blockMaterialDirtyMechanism = !0
+            }
+            , ".glb").then(a=>{
+                for (let s = a.materials.length - 1; s >= 0; --s)
+                    a.materials[s].dispose();
+                this._scene.blockMaterialDirtyMechanism = !0;
+                for (let s = 0; s < a.meshes.length; ++s) {
+                    const l = a.meshes[s];
+                    if ("instances"in l) {
+                        "visibility"in l && (l.visibility = 0),
+                        "isPickable"in l && (l.isPickable = this._pickable),
+                        e != null && (l.material = e),
+                        "hasVertexAlpha"in l && (l.hasVertexAlpha = !1);
+                        const u = new XStaticMesh({
+                            id: this._groupUuid + "-" + Math.random().toString(36).substr(2, 5),
+                            mesh: l,
+                            lod: this._lod,
+                            group: this._group,
+                            url: this._url,
+                            xtype: EMeshType.XStaticMesh,
+                            skinInfo: this._skinInfo
+                        });
+                        this._meshes.push(u)
+                    }
+                    this._scene.addMesh(l)
+                }
+                return !0
+            }
+            ).then(()=>{
+                this._isInScene = !0;
+                for (let a = r; a < this._meshes.length; ++a)
+                    this._meshes[a].mesh.visibility = n;
+                return Promise.resolve(!0)
+            }
+            ).catch(a=>{
+                log$u.error("[Engine] input gltf mesh uri error! " + a),
+                Promise.reject(new XLowpolyModelError("[Engine] input gltf mesh uri error! " + a))
+            }
+            )
+        }
+        );
+        this._meshes = [],
+        this._scene = e,
+        this._url = t.url,
+        t.group != null ? this._group = t.group : this._group = "default",
+        t.pick != null ? this._pickable = t.pick : this._pickable = !1,
+        t.id != null ? this._id = t.id : this._id = "default",
+        t.lod != null ? this._lod = t.lod : this._lod = -1,
+        t.skinInfo != null ? this._skinInfo = t.skinInfo : this._skinInfo = "default",
+        this._groupUuid = uuid$2(),
+        this._isInScene = !1
+    }
+    get isinscene() {
+        return this._isInScene
+    }
+    get groupUuid() {
+        return this._groupUuid
+    }
+    get skinInfo() {
+        return this._skinInfo
+    }
+    get group() {
+        return this._group
+    }
+    get meshes() {
+        return this._meshes
+    }
+    get url() {
+        return this._url
+    }
+    get id() {
+        return this._id
+    }
+    get lod() {
+        return this._lod
+    }
+    removeFromScene() {
+        if (this._isInScene) {
+            this._isInScene = !1;
+            for (let e = 0, t = this._meshes.length; e < t; ++e)
+                this._meshes[e].mesh != null && this._scene.removeMesh(this._meshes[e].mesh)
+        }
+    }
+    addToScene() {
+        if (this._isInScene == !1) {
+            this._isInScene = !0;
+            for (let e = 0, t = this._meshes.length; e < t; ++e)
+                this._meshes[e].mesh != null && this._scene.addMesh(this._meshes[e].mesh)
+        }
+    }
+    toggleVisibility(e) {
+        const t = e ? 1 : 0;
+        for (let r = 0, n = this._meshes.length; r < n; ++r)
+            "visibility"in this._meshes[r].mesh && (this._meshes[r].mesh.visibility = t)
+    }
+    togglePickable(e) {
+        for (let t = 0, r = this._meshes.length; t < r; ++t)
+            "isPickable"in this._meshes[t].mesh && (this._meshes[t].mesh.isPickable = e)
+    }
+    setMaterial(e) {
+        for (let t = 0, r = this._meshes.length; t < r; ++t)
+            "material"in this._meshes[t].mesh && (this._meshes[t].mesh.material = e)
+    }
+    dispose() {
+        for (let e = 0, t = this._meshes.length; e < t; ++e)
+            this._meshes[e].mesh.dispose(!1, !1)
+    }
+}

+ 188 - 0
XStats.js

@@ -0,0 +1,188 @@
+class XStats {
+    constructor(e) {
+        E(this, "scene");
+        E(this, "sceneInstrumentation");
+        E(this, "engineInstrumentation");
+        E(this, "caps");
+        E(this, "engine");
+        E(this, "_canvas");
+        E(this, "_osversion");
+        E(this, "_scenemanager");
+        this._scenemanager = e,
+        this.scene = e.Scene,
+        this._canvas = e.canvas,
+        this.initSceneInstrument()
+    }
+    initSceneInstrument() {
+        this.sceneInstrumentation = new SceneInstrumentation(this.scene),
+        this.sceneInstrumentation.captureCameraRenderTime = !0,
+        this.sceneInstrumentation.captureActiveMeshesEvaluationTime = !0,
+        this.sceneInstrumentation.captureRenderTargetsRenderTime = !0,
+        this.sceneInstrumentation.captureFrameTime = !0,
+        this.sceneInstrumentation.captureRenderTime = !0,
+        this.sceneInstrumentation.captureInterFrameTime = !0,
+        this.sceneInstrumentation.captureParticlesRenderTime = !0,
+        this.sceneInstrumentation.captureSpritesRenderTime = !0,
+        this.sceneInstrumentation.capturePhysicsTime = !0,
+        this.sceneInstrumentation.captureAnimationsTime = !0,
+        this.engineInstrumentation = new EngineInstrumentation(this.scene.getEngine()),
+        this.caps = this.scene.getEngine().getCaps(),
+        this.engine = this.scene.getEngine(),
+        this._osversion = this.osVersion()
+    }
+    getFrameTimeCounter() {
+        return this.sceneInstrumentation.frameTimeCounter.current
+    }
+    getInterFrameTimeCounter() {
+        return this.sceneInstrumentation.interFrameTimeCounter.current
+    }
+    getActiveMeshEvaluationTime() {
+        return this.sceneInstrumentation.activeMeshesEvaluationTimeCounter.current
+    }
+    getDrawCall() {
+        return this.sceneInstrumentation.drawCallsCounter.current
+    }
+    getDrawCallTime() {
+        return this.sceneInstrumentation.renderTimeCounter.current
+    }
+    getAnimationTime() {
+        return this.sceneInstrumentation.animationsTimeCounter.current
+    }
+    getActiveMesh() {
+        return this.scene.getActiveMeshes().length
+    }
+    getActiveFaces() {
+        return Math.round(this.scene.getActiveIndices() / 3)
+    }
+    getActiveBones() {
+        return this.scene.getActiveBones()
+    }
+    getActiveAnimation() {
+        return this.scene._activeAnimatables.length
+    }
+    getActiveParticles() {
+        return this.scene.getActiveParticles()
+    }
+    getTotalMaterials() {
+        return this.scene.materials.length
+    }
+    getTotalTextures() {
+        return this.scene.textures.length
+    }
+    getTotalGeometries() {
+        return this.scene.geometries.length
+    }
+    getTotalMeshes() {
+        return this.scene.meshes.length
+    }
+    getCameraRenderTime() {
+        return this.sceneInstrumentation.cameraRenderTimeCounter.current
+    }
+    getTotalRootNodes() {
+        return this.scene.rootNodes.length
+    }
+    getRenderTargetRenderTime() {
+        const e = this.getDrawCallTime()
+          , t = this.getActiveMeshEvaluationTime()
+          , r = this.getCameraRenderTime() - (t + e);
+        return this.getRTT1Time() + r
+    }
+    getRegisterBeforeRenderTime() {
+        return this.sceneInstrumentation.registerBeforeTimeCounter.current
+    }
+    getRegisterAfterRenderTime() {
+        return this.sceneInstrumentation.registerAfterTimeCounter.current
+    }
+    getRTT1Time() {
+        return this.sceneInstrumentation.getRTT1TimeCounter.current
+    }
+    getRegisterBeforeRenderObserverLength() {
+        return this.scene.onBeforeRenderObservable.observers.length
+    }
+    getRegisterAfterRenderObserverLength() {
+        return this.scene.onAfterRenderObservable.observers.length
+    }
+    getTotalMeshByType() {
+        const e = new Map;
+        return this.scene.meshes.forEach(t=>{
+            e.has(t.xtype) ? e.set(t.xtype, e.get(t.xtype) + 1) : e.set(t.xtype, 1)
+        }
+        ),
+        e
+    }
+    getHardwareRenderInfo() {
+        return {
+            maxTexturesUnits: this.caps.maxTexturesImageUnits,
+            maxVertexTextureImageUnits: this.caps.maxVertexTextureImageUnits,
+            maxCombinedTexturesImageUnits: this.caps.maxCombinedTexturesImageUnits,
+            maxTextureSize: this.caps.maxTextureSize,
+            maxSamples: this.caps.maxSamples,
+            maxCubemapTextureSize: this.caps.maxCubemapTextureSize,
+            maxRenderTextureSize: this.caps.maxRenderTextureSize,
+            maxVertexAttribs: this.caps.maxVertexAttribs,
+            maxVaryingVectors: this.caps.maxVaryingVectors,
+            maxVertexUniformVectors: this.caps.maxVertexUniformVectors,
+            maxFragmentUniformVectors: this.caps.maxFragmentUniformVectors,
+            standardDerivatives: this.caps.standardDerivatives,
+            supportTextureCompress: {
+                s3tc: this.caps.s3tc !== void 0,
+                s3tc_srgb: this.caps.s3tc_srgb !== void 0,
+                pvrtc: this.caps.pvrtc !== void 0,
+                etc1: this.caps.etc1 !== void 0,
+                etc2: this.caps.etc2 !== void 0,
+                astc: this.caps.astc !== void 0,
+                bptc: this.caps.bptc !== void 0
+            },
+            textureFloat: this.caps.textureFloat,
+            vertexArrayObject: this.caps.vertexArrayObject,
+            textureAnisotropicFilterExtension: this.caps.textureAnisotropicFilterExtension !== void 0,
+            maxAnisotropy: this.caps.maxAnisotropy,
+            instancedArrays: this.caps.instancedArrays,
+            uintIndices: this.caps.uintIndices,
+            highPrecisionShaders: this.caps.highPrecisionShaderSupported,
+            fragmentDepth: this.caps.fragmentDepthSupported,
+            textureFloatLinearFiltering: this.caps.textureFloatLinearFiltering,
+            renderToTextureFloat: this.caps.textureFloatRender,
+            textureHalfFloat: this.caps.textureHalfFloat,
+            textureHalfFloatLinearFiltering: this.caps.textureHalfFloatLinearFiltering,
+            textureHalfFloatRender: this.caps.textureHalfFloatRender,
+            textureLOD: this.caps.textureLOD,
+            drawBuffersExtension: this.caps.drawBuffersExtension,
+            depthTextureExtension: this.caps.depthTextureExtension,
+            colorBufferFloat: this.caps.colorBufferFloat,
+            supportTimerQuery: this.caps.timerQuery !== void 0,
+            canUseTimestampForTimerQuery: this.caps.canUseTimestampForTimerQuery,
+            supportOcclusionQuery: this.caps.supportOcclusionQuery,
+            multiview: this.caps.multiview,
+            oculusMultiview: this.caps.oculusMultiview,
+            maxMSAASamples: this.caps.maxMSAASamples,
+            blendMinMax: this.caps.blendMinMax,
+            canUseGLInstanceID: this.caps.canUseGLInstanceID,
+            canUseGLVertexID: this.caps.canUseGLVertexID,
+            supportComputeShaders: this.caps.supportComputeShaders,
+            supportSRGBBuffers: this.caps.supportSRGBBuffers,
+            supportStencil: this.engine.isStencilEnable
+        }
+    }
+    getSystemInfo() {
+        return {
+            resolution: "real: " + this.engine.getRenderWidth() + "x" + this.engine.getRenderHeight() + "	 cavs: " + this._canvas.clientWidth + "x" + this._canvas.clientHeight,
+            hardwareScalingLevel: this.engine.getHardwareScalingLevel().toFixed(2).toString() + "_" + this._scenemanager.initEngineScaleNumber.toFixed(2).toString(),
+            driver: this.engine.getGlInfo().renderer,
+            vender: this.engine.getGlInfo().vendor,
+            version: this.engine.getGlInfo().version,
+            os: this._osversion
+        }
+    }
+    getFps() {
+        const e = this.sceneInstrumentation.frameTimeCounter.lastSecAverage
+          , t = this.sceneInstrumentation.interFrameTimeCounter.lastSecAverage;
+        return 1e3 / (e + t)
+    }
+    osVersion() {
+        const e = window.navigator.userAgent;
+        let t;
+        return /iphone|ipad|ipod/gi.test(e) ? t = e.match(/OS (\d+)_(\d+)_?(\d+)?/) : /android/gi.test(e) && (t = e.match(/Android (\d+)/)),
+        t != null && t.length > 0 ? t[0] : null
+    }
+}

+ 319 - 0
XTelevision.js

@@ -0,0 +1,319 @@
+const log$y = new Logger$1("XTelevision");
+var EFitMode = (i=>(i.fill = "fill",
+i.contain = "contain",
+i.cover = "cover",
+i))(EFitMode || {});
+class XTelevision {
+    constructor(e, t, r, n) {
+        E(this, "videoElement");
+        E(this, "meshPath");
+        E(this, "scene");
+        E(this, "tvMeshs", []);
+        E(this, "vAng");
+        E(this, "videoMat");
+        E(this, "videoTexture");
+        E(this, "widthHeightScale");
+        E(this, "fitMode");
+        E(this, "_scenemanager");
+        if (this.scene = e,
+        this.meshPath = t,
+        this._scenemanager = r,
+        n != null) {
+            const {vAng: o=0, widthHeightScale: a=-1, fitMode: s="fill"} = n;
+            this.vAng = o,
+            this.widthHeightScale = a,
+            this.fitMode = s
+        }
+    }
+    set tvWidthHeightscale(e) {
+        this.widthHeightScale = e
+    }
+    get tvWidthHeightscale() {
+        return this.widthHeightScale
+    }
+    get tvFitMode() {
+        return this.fitMode
+    }
+    set tvFitMode(e) {
+        this.fitMode = e
+    }
+    setPlaySpeed(e) {
+        this.videoElement != null && (this.videoElement.playbackRate = e)
+    }
+    getMesh() {
+        return this.tvMeshs
+    }
+    createElement(e, t=!1) {
+        const n = new Stream$1().el;
+        return n.loop = t,
+        n.autoplay = !0,
+        n.src = e,
+        n
+    }
+    async setUrl(e) {
+        const {url: t, isLive: r=!1, poster: n=null, bLoop: o=!1, bMuted: a=!0} = e || {};
+        if (typeof t != "string")
+            return log$y.error("[Engine] Tv setUrl Error, url must be string: ", t),
+            Promise.reject(new XTvMediaUrlError("[Engine] url must be string"));
+        if (this.videoElement) {
+            this.videoElement.src = t,
+            n != null && n.length > 0 && (this.videoElement.poster = n);
+            const l = this.play();
+            return "bMuted"in e && l !== void 0 && l.then(()=>{
+                this.videoElement.muted = a
+            }
+            ),
+            this.videoElement.addEventListener("loadedmetadata", u=>{
+                this.videoElement.videoWidth > 0 ? this.videoMat.setFloat("mvWidthHeightScale", this.videoElement.videoWidth / this.videoElement.videoHeight) : this.videoMat.setFloat("mvWidthHeightScale", 16 / 9)
+            }
+            ),
+            Promise.resolve(this)
+        }
+        const s = this.createElement(t, o);
+        return n != null && n.length > 0 && (s.poster = n),
+        this.setVideo(s, r).then(()=>{
+            var u;
+            const l = (u = this.videoElement) == null ? void 0 : u.play();
+            "bMuted"in e && l !== void 0 && l.then(()=>{
+                this.videoElement.muted = a
+            }
+            )
+        }
+        ).catch(l=>{
+            log$y.error("[Engine] setUrl  error! " + l),
+            new XTvMediaUrlError("[Engine] setUrl  error! " + l)
+        }
+        )
+    }
+    setCurrentTime(e) {
+        if (!this.videoElement) {
+            log$y.warn("[Engine] The television is not been initialize succesfully");
+            return
+        }
+        const {currentTime: t} = e;
+        if (typeof t != "number") {
+            log$y.warn("[Engine] video currentTime must be number");
+            return
+        }
+        this.videoElement.currentTime = t / 1e3
+    }
+    getCurrentTime() {
+        return this.videoElement ? this.videoElement.currentTime * 1e3 : -1
+    }
+    play() {
+        return log$y.info("[Engine] Play television"),
+        this.toggle(!0),
+        this.videoElement ? this.videoElement.play() : Promise.resolve()
+    }
+    pause() {
+        var e;
+        return log$y.info("[Engine] Pause television"),
+        (e = this.videoElement) == null ? void 0 : e.pause()
+    }
+    stop() {
+        log$y.info("[Engine] Stop television"),
+        this.pause(),
+        setTimeout(()=>{
+            this.setCurrentTime({
+                currentTime: 0
+            })
+        }
+        ),
+        this.toggle(!1)
+    }
+    toggle(e) {
+        log$y.info(`[Engine] Set Tv visibility = ${e}`);
+        for (let t = 0; t < this.tvMeshs.length; ++t)
+            e == !0 ? this.tvMeshs[t].show() : this.tvMeshs[t].hide()
+    }
+    getVideoMat() {
+        return this.videoMat
+    }
+    changeTvFitMode() {
+        this.fitMode == "contain" ? (this.widthHeightScale < 0 && (this.widthHeightScale = 2.4),
+        this.videoMat.setFloat("tvWidthHeightScale", this.widthHeightScale),
+        this.videoMat.setFloat("bforceforceKeepContent", 1)) : this.fitMode == "cover" ? (this.widthHeightScale < 0 && (this.widthHeightScale = this.calWidthHeightScale()),
+        this.videoMat.setFloat("tvWidthHeightScale", this.widthHeightScale),
+        this.videoMat.setFloat("bforceforceKeepContent", -1)) : this.videoMat.setFloat("tvWidthHeightScale", -1)
+    }
+    async setVideo(e, t=!1, r=!0) {
+        return this.tvMeshs.length != 0 ? (log$y.warn(`[Engine] Set Video. length!=0, mesh: ${this.meshPath}, src: ${e.src}`),
+        new Promise((n,o)=>{
+            if (!(e instanceof HTMLVideoElement))
+                return log$y.error("[Engine] Error, param of setVideo must be a HTMLVideoElement"),
+                o(new XTvVideoElementError("[Engine] param of setVideo must be a HTMLVideoElement"));
+            this.videoElement = e,
+            r == !1 && (t == !1 || checkOS().isIOS) && e.crossOrigin !== "anonymous" && (e.crossOrigin = "anonymous",
+            e.load()),
+            this.videoElement.addEventListener("loadedmetadata", a=>{
+                this.videoElement.videoWidth > 0 ? this.videoMat.setFloat("mvWidthHeightScale", this.videoElement.videoWidth / this.videoElement.videoHeight) : this.videoMat.setFloat("mvWidthHeightScale", 16 / 9)
+            }
+            ),
+            this.videoTexture.updateURL(this.videoElement.src),
+            n(this)
+        }
+        )) : (log$y.warn(`[Engine] Set Video. length==0, mesh: ${this.meshPath}, src: ${e.src}`),
+        this.meshPath == "" ? (log$y.error("[Engine] Error, television meshPath is empty."),
+        Promise.reject(new XTvVideoElementError("[Engine] Error, television meshPath is empty."))) : this._scenemanager.urlTransformer(this.meshPath).then(n=>new Promise((o,a)=>e instanceof HTMLVideoElement ? (this.videoElement = e,
+        r == !1 && (t == !1 || checkOS().isIOS) && e.crossOrigin !== "anonymous" && (e.crossOrigin = "anonymous",
+        e.load()),
+        SceneLoader.LoadAssetContainerAsync("", n, this.scene, null, ".glb").then(s=>{
+            for (let u = s.materials.length - 1; u >= 0; --u)
+                s.materials[u].dispose();
+            const l = [];
+            this.videoTexture = new VideoTexture("videoTex_" + Date.now(),e,this.scene,!1,!0,void 0,{
+                autoPlay: !0,
+                autoUpdateTexture: !0,
+                muted: !0
+            }),
+            this.videoTexture.vAng = this.vAng,
+            this.videoMat = new ShaderMaterial("videoMat_" + Date.now(),this.scene,{
+                vertexSource: tvVertex,
+                fragmentSource: tvFragment
+            },{
+                attributes: ["uv", "position"],
+                uniforms: ["view", "projection", "worldViewProjection", "world"]
+            }),
+            this.videoMat.setTexture("texture_video", this.videoTexture),
+            this.videoMat.setFloat("tvWidthHeightScale", -1),
+            this.videoMat.setFloat("mvWidthHeightScale", 16 / 9),
+            this.videoMat.setFloat("bforceforceKeepContent", -1),
+            this.videoMat.backFaceCulling = !1,
+            this.videoMat.sideOrientation = Mesh.FRONTSIDE,
+            this.videoElement.addEventListener("loadedmetadata", u=>{
+                this.videoElement.videoWidth > 0 ? this.videoMat.setFloat("mvWidthHeightScale", this.videoElement.videoWidth / this.videoElement.videoHeight) : this.videoMat.setFloat("mvWidthHeightScale", 16 / 9)
+            }
+            );
+            for (let u = 0; u < s.meshes.length; ++u)
+                s.meshes[u].visibility = 1,
+                s.meshes[u].isPickable = !0,
+                s.meshes[u].checkCollisions = !1,
+                s.meshes[u].material = this.videoMat,
+                "hasVertexAlpha"in s.meshes[u] && (s.meshes[u].hasVertexAlpha = !1),
+                this.scene.addMesh(s.meshes[u]),
+                l.push(new XStaticMesh({
+                    id: s.meshes[u].id,
+                    mesh: s.meshes[u],
+                    xtype: EMeshType.Tv
+                }));
+            this.changeTvFitMode(),
+            this.tvMeshs = l,
+            this.toggle(!0),
+            o(this)
+        }
+        ).catch(s=>{
+            log$y.error("[Engine] setVideo: create Tv by input mesh error! " + s),
+            a(new XTvModelError("[Engine] setVideo: create Tv by input mesh error! " + s))
+        }
+        )) : a(new XTvVideoElementError("[Engine] param of setVideo must be a HTMLVideoElement")))))
+    }
+    async setSameVideo(e, t="") {
+        return e == null || e == null ? (log$y.error("[Engine] setSameVideo: input material is null or undefined "),
+        Promise.reject(new XTvModelError("[Engine] setSameVideo input material is null or undefined !"))) : this.tvMeshs.length != 0 && t == "" ? (log$y.warn(`[Engine] Set mirror video. length!=0, mesh: ${this.meshPath}`),
+        new Promise((r,n)=>{
+            try {
+                this.videoMat = e,
+                this.tvMeshs.forEach(o=>{
+                    o.setMaterial(e)
+                }
+                ),
+                this.changeTvFitMode(),
+                r(this)
+            } catch (o) {
+                log$y.error("[Engine] setSameVideo: create Tv by input mesh error! " + o),
+                n(new XTvModelError("[Engine] create Tv by input mesh error! " + o))
+            }
+        }
+        )) : (t != "" && (this.meshPath = t,
+        this.widthHeightScale = -1),
+        this.meshPath == "" ? (log$y.error("[Engine] Error, setSameVideo television meshPath is empty."),
+        Promise.reject(new XTvVideoElementError("[Engine] Error, setSameVideo television meshPath is empty."))) : (log$y.warn(`[Engine] Set mirror video. length==0, mesh: ${this.meshPath}`),
+        this._scenemanager.urlTransformer(this.meshPath).then(r=>new Promise((n,o)=>(this.videoMat = e,
+        e != null && e.getActiveTextures()[0] && (this.videoElement = e == null ? void 0 : e.getActiveTextures()[0].video),
+        SceneLoader.LoadAssetContainerAsync("", r, this.scene, null, ".glb").then(a=>{
+            for (let l = a.materials.length - 1; l >= 0; --l)
+                a.materials[l].dispose();
+            const s = [];
+            for (let l = 0; l < a.meshes.length; ++l)
+                a.meshes[l].visibility = 0,
+                a.meshes[l].isPickable = !0,
+                a.meshes[l].checkCollisions = !1,
+                a.meshes[l].material = this.videoMat,
+                "hasVertexAlpha"in a.meshes[l] && (a.meshes[l].hasVertexAlpha = !1),
+                this.scene.addMesh(a.meshes[l]),
+                s.push(new XStaticMesh({
+                    id: a.meshes[l].id,
+                    mesh: a.meshes[l],
+                    xtype: EMeshType.Tv
+                }));
+            t != "" && this.cleanTv(!1, !1),
+            this.tvMeshs = s,
+            this.changeTvFitMode(),
+            n(this)
+        }
+        ).catch(a=>{
+            log$y.error("[Engine] setSameVideo: create Tv by input mesh error! " + a),
+            o(new XTvModelError("[Engine] create Tv by input mesh error! " + a))
+        }
+        ))))))
+    }
+    async changeTvModel(e="") {
+        return e != "" && (this.meshPath = e,
+        this.widthHeightScale = -1),
+        this.meshPath == "" ? (log$y.error("[Engine] Error,changeTvModel television meshPath is empty."),
+        Promise.reject(new XTvVideoElementError("[Engine] Error, changeTvModel television meshPath is empty."))) : this.videoMat == null || this.videoMat == null ? (log$y.error("[Engine] changeTvModel: videoMat is null or undefined! "),
+        Promise.reject(new XTvModelError("[Engine] changeTvModel: videoMat is null or undefined!"))) : this._scenemanager.urlTransformer(this.meshPath).then(t=>new Promise((r,n)=>SceneLoader.LoadAssetContainerAsync("", t, this.scene, null, ".glb").then(o=>{
+            for (let s = o.materials.length - 1; s >= 0; --s)
+                o.materials[s].dispose();
+            const a = [];
+            for (let s = 0; s < o.meshes.length; ++s)
+                o.meshes[s].visibility = 0,
+                o.meshes[s].isPickable = !0,
+                o.meshes[s].checkCollisions = !1,
+                o.meshes[s].material = this.videoMat,
+                "hasVertexAlpha"in o.meshes[s] && (o.meshes[s].hasVertexAlpha = !1),
+                this.scene.addMesh(o.meshes[s]),
+                a.push(new XStaticMesh({
+                    id: o.meshes[s].id,
+                    mesh: o.meshes[s],
+                    xtype: EMeshType.Tv
+                }));
+            e != "" && this.cleanTv(!1, !1),
+            this.tvMeshs = a,
+            this.changeTvFitMode(),
+            r(this)
+        }
+        ).catch(o=>{
+            log$y.error("[Engine] changeTvModel: create Tv by input mesh error! " + o),
+            n(new XTvModelError("[Engine] changeTvModel: create Tv by input mesh error! " + o))
+        }
+        )))
+    }
+    calWidthHeightScale() {
+        const e = [1e5, 1e5, 1e5]
+          , t = [-1e5, -1e5, -1e5];
+        for (let a = 0; a < this.tvMeshs.length; ++a)
+            if (this.tvMeshs[a].mesh.name != "__root__") {
+                const s = this.tvMeshs[a].mesh.getBoundingInfo().boundingBox.vectorsWorld;
+                for (let l = 0; l < s.length; ++l)
+                    e[0] > s[l].x && (e[0] = s[l].x),
+                    e[1] > s[l].y && (e[1] = s[l].y),
+                    e[2] > s[l].z && (e[2] = s[l].z),
+                    t[0] < s[l].x && (t[0] = s[l].x),
+                    t[1] < s[l].y && (t[1] = s[l].y),
+                    t[2] < s[l].z && (t[2] = s[l].z);
+                break
+            }
+        const r = t[0] - e[0]
+          , n = t[1] - e[1]
+          , o = t[2] - e[2];
+        return Math.sqrt(r * r + o * o) / Math.abs(n)
+    }
+    cleanTv(e=!1, t=!0) {
+        log$y.warn("[Engine] cleanTV");
+        for (let r = 0; r < this.tvMeshs.length; ++r)
+            this.tvMeshs[r].dispose(e, t);
+        this.tvMeshs = [],
+        this.meshPath = ""
+    }
+}

+ 40 - 0
XVideoRawYUV.js

@@ -0,0 +1,40 @@
+class XVideoRawYUV {
+    constructor(e, t) {
+        E(this, "scene");
+        E(this, "_videoRawYUVTexture");
+        E(this, "videosResOriArray");
+        E(this, "_currentVideoId");
+        this.scene = e,
+        this._videoRawYUVTexture = [],
+        this.videosResOriArray = t,
+        this._currentVideoId = -1;
+        for (let r = 0; r < t.length; ++r)
+            (n=>{
+                const o = RawTexture.CreateLuminanceTexture(null, t[n].width, t[n].height * 1.5, this.scene, !1, !0);
+                o.name = "videoTex_" + t[n].width + "_" + t[n].height,
+                this._videoRawYUVTexture.push(o)
+            }
+            )(r)
+    }
+    inRange(e) {
+        return e >= 0 && e < this._videoRawYUVTexture.length
+    }
+    getVideoYUVTex(e) {
+        return this.inRange(e) ? this._videoRawYUVTexture[e] : null
+    }
+    findId(e, t) {
+        let r = 0;
+        for (let n = 0; n < this.videosResOriArray.length; ++n)
+            if (this.videosResOriArray[n].width == e && this.videosResOriArray[n].height == t) {
+                r = n;
+                break
+            }
+        return r
+    }
+    getCurrentVideoTexId() {
+        return this._currentVideoId
+    }
+    setCurrentVideoTexId(e) {
+        this._currentVideoId = e
+    }
+}

+ 88 - 0
Xverse.js

@@ -0,0 +1,88 @@
+class Xverse extends Xverse$1 {
+    async joinRoom(e) {
+        const t = e.pathName || "thirdwalk"
+          , r = e.rotationRenderType || RenderType.RotationVideo
+          , n = e.person || Person.Third
+          , o = new XverseRoom(le(oe({}, e), {
+            appId: e.appId || this.appId,
+            releaseId: e.releaseId || this.releaseId,
+            pageSession: this.pageSession,
+            isAllSync: !0,
+            rotationRenderType: r,
+            syncByEvent: !0,
+            pathName: t,
+            person: n,
+            role: e.role || "audience"
+        }));
+        return o.initRoom().then(()=>o)
+    }
+}
+
+const log$c = new Logger("xverse")
+class Xverse$1{
+    constructor(e) {
+        E(this, "debug", !1);
+        E(this, "pageSession");
+        E(this, "preload");
+        E(this, "appId");
+        E(this, "releaseId");
+        e || (e = {});
+        const {onLog: t, env: r, appId: n, releaseId: o, subPackageVersion: a} = e;
+        ve.NO_CACHE = !1,
+        ve.env = r || "PROD",
+        ve.SUB_PACKAGE_VERSION = a,
+        this.debug && Logger.setLevel(LoggerLevels.Debug);
+        const s = this.pageSession = uuid$1();
+        if (reporter.updateHeader({
+            pageSession: s
+        }),
+        reporter.updateReportUrl(REPORT_URL[ve.env]),
+        a && reporter.updateBody({
+            sdkVersion: a
+        }),
+        log$c.infoAndReportMeasurement({
+            metric: "sdkInit",
+            startTime: Date.now(),
+            extra: {
+                version: a,
+                enviroment: r,
+                pageSession: s
+            }
+        }),
+        log$c.debug("debug mode:", this.debug),
+        reporter.on("report", l=>{
+            t && t(l)
+        }
+        ),
+        n) {
+            this.appId = n,
+            this.releaseId = o;
+            const l = ModelManager.getInstance(n, o);
+            this.preload = new Preload(l)
+        }
+    }
+    get isSupported() {
+        return isSupported()
+    }
+    disableLogUpload() {
+        reporter.disable(),
+        log$c.debug("log upload has been disabled")
+    }
+    async getSkinList() {
+        return []
+    }
+    async getAvatarList() {
+        return []
+    }
+    async getGiftList() {
+        return [{
+            id: "hack "
+        }]
+    }
+}
+;
+
+E(Xverse$1, "NO_CACHE"),
+E(Xverse$1, "USE_TME_CDN"),
+E(Xverse$1, "env"),
+E(Xverse$1, "SUB_PACKAGE_VERSION");

+ 471 - 0
XverseAvatar.js

@@ -0,0 +1,471 @@
+const log$1 = new Logger("xverse-avatar");
+class XverseAvatar extends EventEmitter {
+    constructor({userId: e, isHost: t, room: r, avatarId: n, isSelf: o, group: a=AvatarGroup.Npc}) {
+        super();
+        E(this, "xAvatar");
+        E(this, "_isHost", !1);
+        E(this, "_room");
+        E(this, "_withModel", !1);
+        E(this, "_userId");
+        E(this, "group", AvatarGroup.User);
+        E(this, "state", "idle");
+        E(this, "isLoading", !0);
+        E(this, "_isMoving", !1);
+        E(this, "_isRotating", !1);
+        E(this, "_failed", !1);
+        E(this, "disconnected", !1);
+        E(this, "_avatarId");
+        E(this, "prioritySync", !1);
+        E(this, "priority", EAvatarRelationRank.Stranger);
+        E(this, "_avatarModel");
+        E(this, "_motionType", MotionType.Walk);
+        E(this, "isSelf", !1);
+        E(this, "_lastAnimTraceId", "");
+        E(this, "statusSyncQueue", new Queue);
+        E(this, "extraInfo", {});
+        E(this, "setPosition", e=>{
+            var t;
+            !this._room.signal.isUpdatedYUV || (t = this.xAvatar) == null || t.setPosition(positionPrecisionProtect(e))
+        }
+        );
+        E(this, "setRotation", e=>{
+            var t;
+            !this._room.signal.isUpdatedYUV || (t = this.xAvatar) == null || t.setRotation(rotationPrecisionProtect(e))
+        }
+        );
+        E(this, "stopAnimation", ()=>{
+            var e, t;
+            (t = (e = this.xAvatar) == null ? void 0 : e.controller) == null || t.stopAnimation()
+        }
+        );
+        E(this, "_playAnimation", async(e,t=!0,r=!1)=>{
+            var o;
+            if (!this._room.signal.isUpdatedYUV)
+                return;
+            if (this.state !== "idle" && !r)
+                return log$1.debug("_playAnimation", "state is not idle"),
+                Promise.resolve("_playAnimation, state is not idle");
+            const n = Date.now();
+            try {
+                if (!((o = this.xAvatar) != null && o.controller))
+                    return Promise.reject(new InternalError(`[avatar: ${this.userId}] Play animation failed: ${e}, no controller`));
+                this.isSelf && setTimeout(()=>{
+                    log$1.infoAndReportMeasurement({
+                        tag: e,
+                        startTime: n,
+                        value: 0,
+                        metric: "playAnimationStart"
+                    })
+                }
+                );
+                const a = uuid$1();
+                this._lastAnimTraceId = a,
+                await this.xAvatar.controller.playAnimation(e, t),
+                a === this._lastAnimTraceId && !this.isMoving && !t && e !== "Idle" && this.xAvatar.controller.playAnimation("Idle", t).catch(s=>{
+                    log$1.error(`[avatar: ${this.userId}] Play animation failed [force idle]`, s)
+                }
+                ),
+                this.isSelf && log$1.infoAndReportMeasurement({
+                    tag: e,
+                    startTime: n,
+                    extra: {
+                        loop: t
+                    },
+                    metric: "playAnimationEnd"
+                })
+            } catch (a) {
+                return log$1.error(`[avatar: ${this.userId}] Play animation failed: ${e}`, a),
+                this.isSelf && log$1.infoAndReportMeasurement({
+                    tag: e,
+                    startTime: n,
+                    metric: "playAnimationEnd",
+                    error: a,
+                    extra: {
+                        loop: t
+                    }
+                }),
+                Promise.reject(a)
+            }
+        }
+        );
+        E(this, "changeComponents", async e=>{
+            const {mode: t, endAnimation: r=""} = e || {}
+              , n = JSON.parse(JSON.stringify(e.avatarComponents));
+            let o = avatarComponentsValidate(n, this._avatarModel);
+            return !ChangeComponentsMode[t] && !o && (o = new ParamError(`changeComponents failed, mode: ${t} is invalid`)),
+            o ? (log$1.error(o),
+            Promise.reject(o)) : this._changeComponents({
+                avatarComponents: n,
+                mode: t,
+                endAnimation: r
+            }).then(()=>{
+                this.isSelf && t !== ChangeComponentsMode.Preview && this.avatarComponentsSync(this.avatarComponents)
+            }
+            )
+        }
+        );
+        E(this, "_changeComponents", async e=>{
+            var o;
+            const {avatarComponents: t=[], mode: r} = e || {}
+              , n = Date.now();
+            try {
+                if (!this.xAvatar)
+                    return Promise.reject(new InternalError("changeComponents failed, without instance: xAvatar"));
+                const a = await avatarComponentsModify(this._avatarModel, t)
+                  , s = []
+                  , l = await avatarComponentsParser(this._avatarModel, a, this.avatarComponents);
+                if (l.length === 0)
+                    return this.avatarComponents;
+                await this.beforeChangeComponentsHook(e);
+                for (const u of l) {
+                    const {id: c, type: h, url: f, suitComb: d} = u;
+                    s.push((o = this.xAvatar) == null ? void 0 : o.addComponent(c, h, f, d))
+                }
+                return await Promise.all(s),
+                this.emit("componentsChanged", {
+                    components: this.avatarComponents,
+                    mode: r
+                }),
+                this.isSelf && log$1.infoAndReportMeasurement({
+                    tag: "changeComponents",
+                    startTime: n,
+                    metric: "changeComponents",
+                    extra: {
+                        inputComponents: t,
+                        finalComponents: this.avatarComponents,
+                        mode: ChangeComponentsMode[r]
+                    }
+                }),
+                this.avatarComponents
+            } catch (a) {
+                return this.isSelf && log$1.infoAndReportMeasurement({
+                    tag: "changeComponents",
+                    startTime: n,
+                    metric: "changeComponents",
+                    error: a,
+                    extra: {
+                        inputComponents: t,
+                        finalComponents: this.avatarComponents,
+                        mode: ChangeComponentsMode[r]
+                    }
+                }),
+                Promise.reject(a)
+            }
+        }
+        );
+        E(this, "avatarComponentsSync", e=>{
+            e = e.map(t=>({
+                type: t.type,
+                id: t.id
+            })),
+            this._room.actionsHandler.avatarComponentsSync(e)
+        }
+        );
+        E(this, "hide", ()=>{
+            var e;
+            if ((e = this.xAvatar) != null && e.hide())
+                return Promise.resolve(`avatar: ${this.userId} hide success`);
+            {
+                const t = `avatar: ${this.userId} hide failed ${!this.xAvatar && "without instance: xAvatar"}`;
+                return log$1.warn(t),
+                Promise.reject(t)
+            }
+        }
+        );
+        E(this, "show", ()=>{
+            var e;
+            if ((e = this.xAvatar) != null && e.show())
+                return Promise.resolve(`avatar: ${this.userId} show success`);
+            {
+                const t = `avatar: ${this.userId} show failed ${!this.xAvatar && "without instance: xAvatar"}`;
+                return log$1.warn(t),
+                Promise.reject(t)
+            }
+        }
+        );
+        E(this, "sayTimer");
+        this._userId = e,
+        this._room = r,
+        this.isSelf = o || !1,
+        this._withModel = !!n,
+        this._isHost = t || !1,
+        this._avatarId = n,
+        this.group = a,
+        this._room.modelManager.getAvatarModelList().then(s=>{
+            const l = s.find(u=>u.id === n);
+            l && (this._avatarModel = l)
+        }
+        )
+    }
+    get avatarId() {
+        return this._avatarId
+    }
+    get isRender() {
+        var e;
+        return !!((e = this.xAvatar) != null && e.isRender)
+    }
+    get isHidden() {
+        var e;
+        return !!((e = this.xAvatar) != null && e.isHide)
+    }
+    get motionType() {
+        return this._motionType
+    }
+    set motionType(e) {
+        this._motionType = e
+    }
+    get nickname() {
+        var e;
+        return (e = this.xAvatar) == null ? void 0 : e.nickName
+    }
+    get words() {
+        var e;
+        return (e = this.xAvatar) == null ? void 0 : e.words
+    }
+    get isHost() {
+        return this._isHost
+    }
+    get failed() {
+        return this._failed
+    }
+    get scale() {
+        var e;
+        return (e = this.xAvatar) == null ? void 0 : e.scale
+    }
+    get animations() {
+        var e;
+        return !this.xAvatar || !this.xAvatar.controller ? [] : ((e = this.xAvatar) == null ? void 0 : e.getAvaliableAnimations()) || []
+    }
+    get position() {
+        var e;
+        return (e = this.xAvatar) == null ? void 0 : e.position
+    }
+    get rotation() {
+        var e;
+        return (e = this.xAvatar) == null ? void 0 : e.rotation
+    }
+    get pose() {
+        return {
+            position: this.position,
+            angle: this.rotation
+        }
+    }
+    get id() {
+        return this.userId
+    }
+    get isMoving() {
+        return this._isMoving
+    }
+    set isMoving(e) {
+        this._isMoving = e,
+        this.state = e ? "moving" : "idle"
+    }
+    get isRotating() {
+        return this._isRotating
+    }
+    set isRotating(e) {
+        this._isRotating = e,
+        this.state = e ? "rotating" : "idle"
+    }
+    get withModel() {
+        return this._withModel
+    }
+    get avatarComponents() {
+        var e;
+        return JSON.parse(JSON.stringify(((e = this.xAvatar) == null ? void 0 : e.clothesList) || []))
+    }
+    get userId() {
+        return this._userId
+    }
+    get removeWhenDisconnected() {
+        return this.extraInfo && this.extraInfo.removeWhenDisconnected !== void 0 ? this.extraInfo.removeWhenDisconnected : !0
+    }
+    setConnectionStatus(e) {
+        this.disconnected !== e && (this.disconnected = e,
+        e ? this.emit("disconnected") : this.emit("reconnected"),
+        log$1.warn(`avatar ${this.userId} status changed, disconnected:`, e))
+    }
+    setScale(e) {
+        var t;
+        (t = this.xAvatar) == null || t.setScale(e > 0 ? e : 1)
+    }
+    async playAnimation(e) {
+        const {animationName: t, loop: r, extra: n} = e || {};
+        if (this.isSelf) {
+            if (this.isMoving)
+                try {
+                    await this.stopMoving()
+                } catch (a) {
+                    return log$1.error(`stopMoving error before playAnimation ${t}`, a),
+                    Promise.reject(`stopMoving error before playAnimation ${t}`)
+                }
+            const o = {
+                info: {
+                    userId: this.userId,
+                    animation: t,
+                    loop: r,
+                    extra: encodeURIComponent(n || "")
+                },
+                broadcastType: CoreBroadcastType.PlayAnimation
+            };
+            this._room.avatarManager.broadcast.broadcast({
+                data: o
+            })
+        }
+        return this.emit("animationStart", {
+            animationName: t,
+            extra: safeDecodeURIComponent(n || "")
+        }),
+        this._playAnimation(t, r).then(()=>{
+            this.emit("animationEnd", {
+                animationName: t,
+                extra: safeDecodeURIComponent(n || "")
+            })
+        }
+        )
+    }
+    async beforeChangeComponentsHook(e) {}
+    turnTo(e) {
+        if (this._room.viewMode === "observer") {
+            this._room.sceneManager.cameraComponent.MainCamera.setTarget(ue4Position2Xverse(e.point));
+            return
+        }
+        return this._room.actionsHandler.turnTo(e).then(()=>{
+            this.emit("viewChanged", {
+                extra: (e == null ? void 0 : e.extra) || ""
+            })
+        }
+        )
+    }
+    async moveTo(e) {
+        const {point: t, extra: r=""} = e || {};
+        if (!this.position)
+            return Promise.reject(new ParamError("avatar position is empty"));
+        if (typeof r != "string" || typeof r == "string" && r.length > 64) {
+            const a = "extra shoud be string which length less than 64";
+            return log$1.warn(a),
+            Promise.reject(new ParamError(a))
+        }
+        const o = getDistance(this.position, t) / 100 > 100 ? MotionType.Run : MotionType.Walk;
+        return this._room.actionsHandler.moveTo({
+            point: t,
+            motionType: o,
+            extra: r
+        })
+    }
+    async stopMoving() {
+        return this._room.actionsHandler.stopMoving()
+    }
+    rotateTo(e) {
+        return this._room.actionsHandler.rotateTo(e)
+    }
+    setRayCast(e) {
+        this.xAvatar && (this.xAvatar.isRayCastEnable = e)
+    }
+    say(e, t) {
+        if (this.sayTimer && window.clearTimeout(this.sayTimer),
+        !this.xAvatar) {
+            log$1.error("say failed, without instance: xAvatar");
+            return
+        }
+        this.xAvatar.say(e, {
+            scale: this.xAvatar.scale,
+            isUser: this.group === AvatarGroup.User
+        }),
+        !(t === void 0 || t <= 0) && (this.sayTimer = window.setTimeout(()=>{
+            this.silent()
+        }
+        , t))
+    }
+    silent() {
+        var e;
+        if (!this.xAvatar) {
+            log$1.error("silent failed, without instance: xAvatar");
+            return
+        }
+        (e = this.xAvatar) == null || e.silent()
+    }
+    setMotionType({type: e=MotionType.Walk}) {
+        return this.motionType === e ? Promise.resolve() : this._room.actionsHandler.setMotionType(e).then(()=>{
+            this._motionType = e
+        }
+        )
+    }
+    setNickname(e) {
+        return this._room.actionsHandler.setNickName(encodeURIComponent(e))
+    }
+    _setNickname(e) {
+        var r, n;
+        if (!e)
+            return;
+        const t = safeDecodeURIComponent(e);
+        ((r = this.xAvatar) == null ? void 0 : r.nickName) !== t && (this.isSelf && (this._room.updateCurrentNetworkOptions({
+            nickname: t
+        }),
+        this._room.options.nickname = t),
+        (n = this.xAvatar) == null || n.setNickName(t, {
+            scale: this.xAvatar.scale
+        }))
+    }
+    _move(e) {
+        var s;
+        const {start: t, end: r, walkSpeed: n, moveAnimation: o="Walking", inter: a=[]} = e || {};
+        return (s = this.xAvatar) == null ? void 0 : s.move(t, r, n, o, a)
+    }
+    setPickBoxScale(e=1) {
+        return this.xAvatar ? (this.xAvatar.setPickBoxScale(e),
+        !0) : (log$1.error("setPickBoxScale failed, without instance: xAvatar"),
+        !1)
+    }
+    transfer(e) {
+        const {player: t, camera: r, areaName: n, attitude: o, pathName: a} = e;
+        return this._room.actionsHandler.transfer({
+            renderType: RenderType.RotationVideo,
+            player: t,
+            camera: r,
+            areaName: n,
+            attitude: o,
+            pathName: a,
+            tag: "transfer"
+        })
+    }
+    avatarLoadedHook() {}
+    avatarStartMovingHook() {}
+    avatarStopMovingHook() {}
+    async statusSync(e) {
+        var t, r, n;
+        try {
+            if ((t = e.event) != null && t.rotateEvent) {
+                const {angle: o, speed: a} = e.event.rotateEvent
+                  , s = this.motionType === MotionType.Run ? "Running" : "Walking";
+                this.rotation && (this.rotation.yaw = this.rotation.yaw % 360,
+                o.yaw - this.rotation.yaw > 180 && (o.yaw = 180 - o.yaw),
+                this.isRotating = !0,
+                await this.xAvatar.rotateTo(o, this.rotation, s).then(()=>{
+                    this._playAnimation("Idle", !0),
+                    this.isRotating = !1
+                }
+                ))
+            }
+            if (e.event && (((r = e.event) == null ? void 0 : r.points.length) || 0) > 1 && !this.isSelf) {
+                this.isMoving = !0,
+                e.playerState.attitude && (this._motionType = e.playerState.attitude);
+                const o = this.motionType === MotionType.Run ? "Running" : "Walking"
+                  , a = this._room.skin.routeList.find(l=>l.areaName === this._room.currentState.areaName)
+                  , s = ((a == null ? void 0 : a.step) || 7.5) * 30 * (25 / 30);
+                this.position && await this._move({
+                    start: this.position,
+                    end: e.event.points[e.event.points.length - 1],
+                    walkSpeed: s,
+                    moveAnimation: o,
+                    inter: (n = e.event) == null ? void 0 : n.points.slice(0, -1)
+                }).then(()=>{
+                    this.isMoving = !1
+                }
+                )
+            }
+        } catch {
+            return
+        }
+    }
+}

+ 363 - 0
XverseAvatarManager.js

@@ -0,0 +1,363 @@
+const log = new Logger("xverse-avatar-manager")
+class XverseAvatarManager extends EventEmitter {
+    constructor(e) {
+        super();
+        E(this, "xAvatarManager");
+        E(this, "_room");
+        E(this, "avatars", new Map);
+        E(this, "syncAvatarsLength", 0);
+        E(this, "broadcast");
+        this._room = e,
+        this._usersStatistics(),
+        this.broadcast = this.setupBroadcast(),
+        e.on("skinChanged", ()=>{
+            this.avatars.forEach(t=>{
+                t.disconnected && this.removeAvatar(t.userId, !0)
+            }
+            )
+        }
+        )
+    }
+    setupBroadcast() {
+        return new Broadcast(this._room,async e=>{
+            const {broadcastType: t, info: r} = e;
+            if (t !== CoreBroadcastType.PlayAnimation)
+                return;
+            const {userId: n, animation: o, extra: a, loop: s=!1} = r
+              , l = this.avatars.get(n);
+            l && !l.isSelf && (l.emit("animationStart", {
+                animationName: o,
+                extra: decodeURIComponent(a)
+            }),
+            await (l == null ? void 0 : l._playAnimation(o, s)),
+            l.emit("animationEnd", {
+                animationName: o,
+                extra: decodeURIComponent(a)
+            }))
+        }
+        )
+    }
+    hideAll(e=!0) {
+        this.xAvatarManager.hideAll(e)
+    }
+    showAll(e=!0) {
+        this.xAvatarManager.showAll(e)
+    }
+    async init() {
+        this.xAvatarManager = this._room.sceneManager.avatarComponent;
+        try {
+            const e = await this._room.modelManager.getApplicationConfig()
+              , {avatars: t} = e;
+            if (t) {
+                await avatarLoader.parse(this._room.sceneManager, t);
+                return
+            }
+            return Promise.reject("cannot find avatar config list")
+        } catch (e) {
+            return log.error(e),
+            Promise.reject("avatar mananger init error!")
+        }
+    }
+    async handleAvatar(e) {
+        var r;
+        if (this._room.viewMode === "simple" || !this._room.joined || !e.newUserStates)
+            return;
+        let t = e.newUserStates;
+        if (((r = this._room._userAvatar) == null ? void 0 : r.isMoving) && this._room._userAvatar.motionType === MotionType.Run) {
+            const n = t.filter(a=>a.userId === this._room.userId)
+              , o = t.filter(a=>a.userId !== this._room.userId).slice(0, 2);
+            t = n.concat(o)
+        }
+        if (e.getStateType === GetStateTypes.Event) {
+            this.syncAvatarsLength = (t || []).length;
+            const n = this._room.avatars.filter(s=>s.group == AvatarGroup.User);
+            n.filter(s=>!(t != null && t.find(l=>l.userId == s.userId))).forEach(s=>{
+                this.removeAvatar(s.userId)
+            }
+            );
+            const a = t.filter(s=>!n.find(l=>l.userId == s.userId));
+            this._handleAvatar(a)
+        }
+        this._handleAvatar(t)
+    }
+    async _handleAvatar(e) {
+        e == null || e.forEach(t=>{
+            var n, o, a, s, l, u, c, h, f;
+            const r = this._room.userId === t.userId;
+            if (((n = t.event) == null ? void 0 : n.type) === SyncEventType.ET_RemoveVisitor) {
+                const d = (a = (o = t.event) == null ? void 0 : o.removeVisitorEvent) == null ? void 0 : a.removeVisitorEvent
+                  , _ = JSON.parse(safeDecodeURIComponent(((l = (s = t.event) == null ? void 0 : s.removeVisitorEvent) == null ? void 0 : l.extraInfo) || ""))
+                  , {code: g, msg: m} = _;
+                d === RemoveVisitorType.RVT_ChangeToObserver ? this._room.audienceViewModeHook() : d === RemoveVisitorType.RVT_MoveOutOfTheRoom && this._room.leave(),
+                this._room.emit("visitorStatusChanged", {
+                    code: g,
+                    msg: m
+                })
+            }
+            if (t.event && [SyncEventType.Appear, SyncEventType.Reset].includes(t.event.type) || !t.event) {
+                let d = this.avatars.get(t.userId);
+                if (t.playerState.avatarId && (d == null ? void 0 : d.avatarId) !== t.playerState.avatarId && (d = void 0,
+                this.removeAvatar(t.userId)),
+                d) {
+                    if (d.disconnected && d.setConnectionStatus(!1),
+                    (u = t.event) != null && u.id && this._room.actionsHandler.confirmEvent(t.event.id),
+                    t.playerState.nickName && (d == null || d._setNickname(t.playerState.nickName)),
+                    t.playerState.avatarComponents && !d.isSelf && d.xAvatar) {
+                        const _ = safeParseComponents(t.playerState.avatarComponents);
+                        d._changeComponents({
+                            avatarComponents: _,
+                            mode: ChangeComponentsMode.Preview
+                        })
+                    }
+                } else {
+                    const {position: _, angle: g} = t.playerState.player
+                      , m = t.playerState.avatarId
+                      , v = t.playerState.prioritySync
+                      , y = safelyJsonParse(t.playerState.extra);
+                    if (!m)
+                        return;
+                    const b = safeParseComponents(t.playerState.avatarComponents)
+                      , T = safeDecodeURIComponent(t.playerState.nickName)
+                      , C = this.calculatePriority(t.userId, y);
+                    this.addAvatar({
+                        userId: t.userId,
+                        isHost: t.playerState.isHost,
+                        nickname: T,
+                        avatarPosition: _,
+                        avatarRotation: g,
+                        avatarScale: t.playerState.avatarSize,
+                        avatarId: m,
+                        avatarComponents: t.playerState.person === Person.First ? [] : b,
+                        priority: C,
+                        group: AvatarGroup.User,
+                        prioritySync: v,
+                        extraInfo: y
+                    }).then(()=>{
+                        var A;
+                        (A = t.event) != null && A.id && this._room.actionsHandler.confirmEvent(t.event.id),
+                        this.updateAvatarPositionAndRotation(t),
+                        r && (this.xAvatarManager.setMainAvatar(t.userId),
+                        this._room.emit("userAvatarLoaded"),
+                        log.info("userAvatarLoaded"))
+                    }
+                    ).catch(A=>{
+                        r && (this.xAvatarManager.setMainAvatar(t.userId),
+                        this._room.emit("userAvatarFailed", {
+                            error: A
+                        }),
+                        log.error("userAvatarFailed", A))
+                    }
+                    )
+                }
+            }
+            if (t.event && SyncEventType.Disappear === t.event.type && ((c = t == null ? void 0 : t.event) != null && c.id && this._room.actionsHandler.confirmEvent(t.event.id),
+            this.removeAvatar(t.userId)),
+            t.event && [SyncEventType.Move, SyncEventType.ChangeRenderInfo].includes(t.event.type) || !t.event) {
+                (h = t == null ? void 0 : t.event) != null && h.id && this._room.actionsHandler.confirmEvent(t.event.id);
+                const d = this.avatars.get(t.userId);
+                d && d.withModel && !d.isLoading && this.updateAvatarPositionAndRotation(t)
+            }
+            if (!r && ((f = t.event) == null ? void 0 : f.type) === SyncEventType.Rotate) {
+                const d = this.avatars.get(t.userId);
+                d.statusSyncQueue.append({
+                    type: QueueType.Rotate,
+                    action: ()=>d.statusSync(t)
+                })
+            }
+        }
+        )
+    }
+    calculatePriority(e, t) {
+        var n;
+        return e === this._room.userId ? EAvatarRelationRank.Self : (n = this._room.options.firends) != null && n.includes(e) ? EAvatarRelationRank.Friend : EAvatarRelationRank.Stranger
+    }
+    updateAvatarPositionAndRotation(e) {
+        var t, r;
+        if ((t = e == null ? void 0 : e.playerState) != null && t.player) {
+            let {position: n, angle: o} = e.playerState.player;
+            const a = this.avatars.get(e.userId);
+            if (!a)
+                return;
+            if (n = positionPrecisionProtect(n),
+            o = rotationPrecisionProtect(o),
+            a.isSelf && !this._room.networkController.rtcp.workers.inPanoMode && (a.setPosition(n),
+            a.setRotation(o)),
+            e.event && (((r = e.event) == null ? void 0 : r.points.length) || 0) > 1 && !a.isSelf && a.statusSyncQueue.append({
+                type: QueueType.Move,
+                action: ()=>a.statusSync(e)
+            }),
+            e.renderInfo && a.isSelf) {
+                const {isMoving: s, isRotating: l} = e.renderInfo;
+                this._updateAvatarMovingStatus({
+                    id: e.userId,
+                    isMoving: !!s,
+                    isRotating: !!l
+                })
+            }
+        }
+    }
+    async addAvatar(e) {
+        const {userId: t, isHost: r, avatarPosition: n, avatarId: o, avatarRotation: a, nickname: s, avatarComponents: l=[], priority: u, group: c=AvatarGroup.Npc, avatarScale: h=DEFAULT_AVATAR_SCALE, extraInfo: f, prioritySync: d} = e
+          , _ = t === this._room.userId;
+        let g = this.avatars.get(t);
+        if (g)
+            return Promise.resolve(g);
+        if (g = new xe.subAvatar({
+            userId: t,
+            isHost: r,
+            isSelf: _,
+            room: this._room,
+            avatarComponents: l,
+            avatarId: o,
+            nickname: s,
+            group: c
+        }),
+        this.avatars.set(t, g),
+        !g.withModel)
+            return g.isLoading = !1,
+            g.avatarLoadedHook(),
+            this._room.emit("avatarChanged", {
+                avatars: this._room.avatars
+            }),
+            g;
+        const v = (await this._room.modelManager.getAvatarModelList()).find(b=>b.id === o)
+          , y = Date.now();
+        if (!v)
+            return this._room.emit("avatarChanged", {
+                avatars: this._room.avatars
+            }),
+            this.avatars.delete(t),
+            Promise.reject(`no such avatar model with id: ${o}`);
+        try {
+            let b = await avatarComponentsModify(v, l);
+            b = b.filter(A=>A.type != "pendant");
+            const T = await avatarComponentsParser(v, b)
+              , C = await this.xAvatarManager.loadAvatar({
+                id: t,
+                avatarType: o,
+                priority: u,
+                avatarManager: this.xAvatarManager,
+                assets: T,
+                status: {
+                    avatarPosition: n,
+                    avatarRotation: a,
+                    avatarScale: h
+                }
+            })._timeout(8e3, new TimeoutError$1("loadAvatar timeout(8s)"));
+            return C.setPickBoxScale(t === this._room.userId ? 0 : 1),
+            g.xAvatar = C,
+            g.setScale(h),
+            g.extraInfo = f,
+            g.priority = u,
+            g.isLoading = !1,
+            g.prioritySync = !!d,
+            g._playAnimation("Idle", !0, !0),
+            g.avatarLoadedHook(),
+            this._room.emit("avatarChanged", {
+                avatars: this._room.avatars
+            }),
+            s && g._setNickname(s),
+            t === this._room.userId && (log.infoAndReportMeasurement({
+                metric: "avatarLoadDuration",
+                startTime: y,
+                group: "costs"
+            }),
+            log.infoAndReportMeasurement({
+                metric: "avatarLoadAt",
+                startTime: this._room._startTime,
+                group: "costs"
+            })),
+            g
+        } catch (b) {
+            return g.isLoading = !1,
+            this._room.emit("avatarChanged", {
+                avatars: this._room.avatars
+            }),
+            log.error(b),
+            Promise.reject(b)
+        }
+    }
+    removeAvatar(e, t=!1) {
+        const r = this.avatars.get(e);
+        if (!!r) {
+            if (r.removeWhenDisconnected || t) {
+                r.xAvatar && this.xAvatarManager.deleteAvatar(r.xAvatar),
+                this.avatars.delete(e),
+                this._room.emit("avatarChanged", {
+                    avatars: this._room.avatars
+                });
+                return
+            }
+            r.setConnectionStatus(!0)
+        }
+    }
+    clearOtherUsers() {
+        this.avatars.forEach(e=>{
+            !e.isSelf && e.group === AvatarGroup.User && this.removeAvatar(e.userId)
+        }
+        )
+    }
+    async _updateAvatarMovingStatus(e) {
+        const {id: t, isMoving: r, isRotating: n} = e
+          , o = this.avatars.get(t);
+        if (!!o) {
+            if (o.isRotating !== n) {
+                o.isRotating = n;
+                let a = "Idle";
+                n && (a = "Walking",
+                o.motionType === MotionType.Run && (a = "Running")),
+                o._playAnimation(a, !0, !0),
+                log.infoAndReportMeasurement({
+                    startTime: Date.now(),
+                    value: 0,
+                    metric: n ? "userAvatarStartRotating" : "userAvatarStopRotating",
+                    extra: {
+                        motionType: o.motionType,
+                        moveToExtra: this._room.moveToExtra
+                    }
+                })
+            }
+            if (o.isMoving !== r) {
+                o.isMoving = r;
+                let a = "Idle";
+                r && (a = "Walking",
+                o.motionType === MotionType.Run && (a = "Running")),
+                r ? (o.avatarStartMovingHook(),
+                o.emit("startMoving", {
+                    target: o,
+                    extra: this._room.moveToExtra
+                })) : (o.avatarStopMovingHook(),
+                o.emit("stopMoving", {
+                    target: o,
+                    extra: this._room.moveToExtra
+                })),
+                o._playAnimation(a, !0, !0),
+                log.infoAndReportMeasurement({
+                    startTime: Date.now(),
+                    value: 0,
+                    metric: r ? "userAvatarStartMoving" : "userAvatarStopMoving",
+                    extra: {
+                        motionType: o.motionType,
+                        moveToExtra: this._room.moveToExtra
+                    }
+                })
+            }
+        }
+    }
+    _usersStatistics() {
+        this.on("userAvatarLoaded", ()=>{
+            window.setInterval(()=>{
+                const e = this._room.avatars.filter(r=>r.group === AvatarGroup.User).length || 0
+                  , t = this._room.avatars.filter(r=>r.group === AvatarGroup.User && r.isRender).length || 0;
+                this._room.stats.assign({
+                    userNum: e,
+                    syncUserNum: this.syncAvatarsLength,
+                    renderedUserNum: t
+                })
+            }
+            , 3e3)
+        }
+        )
+    }
+}
+;

+ 10 - 0
XverseDatabase.js

@@ -0,0 +1,10 @@
+class XverseDatabase extends Dexie$1 {
+    constructor() {
+        super("XverseDatabase1");
+        E(this, "models");
+        this.version(1).stores({
+            models: "++id,name"
+        }),
+        this.models = this.table("models")
+    }
+}

+ 89 - 0
XverseEffect.js

@@ -0,0 +1,89 @@
+const log$8 = new Logger("effectManager");
+class XverseEffect extends EventEmitter {
+    constructor({id: e, jsonPath: t, type: r, room: n, scale: o=1}) {
+        super();
+        E(this, "_id");
+        E(this, "type");
+        E(this, "effect");
+        E(this, "avatarEffect");
+        E(this, "_room");
+        E(this, "_isLoading", !0);
+        E(this, "_failed", !1);
+        E(this, "_scale", 1);
+        this._room = n,
+        this._id = e,
+        this.type = r,
+        this._scale = o,
+        this.effect = e === "Rain" || e === "Boom" ? new XRain(this._room.scene,t,urlTransformer) : r === IEffectType.Sequence ? new XSequence(this._room.scene,t,"",urlTransformer) : new XSubSequence(this._room.scene,t,urlTransformer),
+        this.avatarEffect = new XSubSequence(this._room.scene,t,urlTransformer)
+    }
+    get failed() {
+        return this._failed
+    }
+    get position() {
+        if (this.type !== IEffectType.Sequence)
+            return this.effect.position
+    }
+    get rotation() {
+        if (this.type !== IEffectType.Sequence)
+            return this.effect.rotation
+    }
+    get isLoading() {
+        return this._isLoading
+    }
+    get id() {
+        return this._id
+    }
+    get name() {
+        return this.effect.name
+    }
+    get isPlaying() {
+        var e;
+        return !!((e = this.effect) != null && e.isPlaying)
+    }
+    async init() {
+        try {
+            await this.effect.init()._timeout(1e4, new TimeoutError("effect init timeout(10s)")),
+            this._isLoading = !1,
+            this._failed = !1
+        } catch (e) {
+            throw this._isLoading = !1,
+            this._failed = !0,
+            log$8.error(`effect: ${this.id} init error`, e),
+            e
+        }
+    }
+    play(e=!1) {
+        return new Promise((t,r)=>{
+            this.effect.play(e),
+            this.isPlaying ? t() : r(`play effect: ${this.name} failed`)
+        }
+        )
+    }
+    hide() {
+        return this.effect.hide()
+    }
+    show() {
+        return this.effect.show()
+    }
+    setRotation(e) {
+        var t;
+        return this.type === IEffectType.Sequence ? Promise.reject("setRotation failed, sequence unSuported") : (t = this.effect) == null ? void 0 : t.setRotation(e)
+    }
+    setPosition(e) {
+        var t;
+        return this.type === IEffectType.Sequence ? Promise.reject("setPosition failed, sequence unSuported") : (t = this.effect) == null ? void 0 : t.setPosition(e)
+    }
+    setScaling(e) {
+        var t;
+        return this.type === IEffectType.Sequence ? Promise.reject("setScaling failed, sequence unSuported") : (this._scale = e,
+        (t = this.effect) == null ? void 0 : t.setScaling({
+            x: e,
+            y: e,
+            z: e
+        }))
+    }
+    dispose() {
+        this.effect.dispose()
+    }
+}

+ 42 - 0
XverseEffectManager.js

@@ -0,0 +1,42 @@
+const log$7 = new Logger("xverse-effect-manager")
+class XverseEffectManager extends EventEmitter {
+    constructor(e) {
+        super();
+        E(this, "effects", new Map);
+        E(this, "room");
+        this.room = e
+    }
+    async addEffect(e) {
+        var o;
+        const {jsonPath: t, id: r, type: n=IEffectType.SubSequence} = e;
+        try {
+            this.effects.get(r) && ((o = this.effects.get(r)) == null || o.dispose());
+            const a = new Ae.subEffect({
+                id: r,
+                jsonPath: t,
+                type: n,
+                room: this.room
+            });
+            return this.effects.set(r, a),
+            await a.init(),
+            a
+        } catch (a) {
+            return this.effects.delete(r),
+            log$7.error(a),
+            Promise.reject(a)
+        }
+    }
+    clearEffects() {
+        this.effects.forEach(e=>{
+            e.dispose(),
+            this.effects.delete(e.id)
+        }
+        )
+    }
+    removeEffect(e) {
+        const t = this.effects.get(e);
+        t == null || t.dispose(),
+        t && this.effects.delete(t.id)
+    }
+}
+;

+ 270 - 0
XverseError.js

@@ -0,0 +1,270 @@
+const COMPONENT_LIST_PREFIX = "/component_list.json";
+class XverseError extends Error {
+    constructor(e, t) {
+        super(t);
+        E(this, "code");
+        this.code = e
+    }
+    toJSON() {
+        return {
+            code: this.code,
+            message: this.message
+        }
+    }
+    toString() {
+        if (Object(this) !== this)
+            throw new TypeError;
+        let t = this.name;
+        t = t === void 0 ? "Error" : String(t);
+        let r = this.message;
+        r = r === void 0 ? "" : String(r);
+        const n = this.code;
+        return r = n === void 0 ? r : n + "," + r,
+        t === "" ? r : r === "" ? t : t + ": " + r
+    }
+}
+class AvatarAssetLoadingError extends XverseError$1 {
+    constructor(e) {
+        super(5100, e || "[Engine] \u89D2\u8272\u8D44\u4EA7\u52A0\u8F7D\u5931\u8D25")
+    }
+}
+class AvatarAnimationError extends XverseError$1 {
+    constructor(e) {
+        super(5101, e || "[Engine] \u89D2\u8272\u52A8\u753B\u64AD\u653E\u5931\u8D25")
+    }
+}
+class TimeoutError$1 extends XverseError$1 {
+    constructor(e) {
+        super(Codes$2.Timeout, e || "[Engine] \u8D85\u65F6\u9519\u8BEF")
+    }
+}
+class DuplicateAvatarIDError extends XverseError$1 {
+    constructor(e) {
+        super(5103, e || "[Engine] \u89D2\u8272id\u91CD\u590D")
+    }
+}
+class ContainerLoadingFailedError extends XverseError$1 {
+    constructor(e) {
+        super(5104, e || "[Engine] \u89D2\u8272\u8D44\u4EA7\u62C9\u53D6\u9519\u8BEF")
+    }
+}
+class XTvMediaUrlError extends XverseError$1 {
+    constructor(e) {
+        super(5201, e || "[Engine] \u4F20\u5165Url\u9519\u8BEF")
+    }
+}
+class XTvVideoElementError extends XverseError$1 {
+    constructor(e) {
+        super(5202, e || "[Engine] \u4F20\u5165video DOM\u9519\u8BEF")
+    }
+}
+class XTvModelError extends XverseError$1 {
+    constructor(e) {
+        super(5203, e || "[Engine] \u4F20\u5165TV\u6A21\u578Burl\u9519\u8BEF")
+    }
+}
+class XLowpolyModelError extends XverseError$1 {
+    constructor(e) {
+        super(5204, e || "[Engine] \u4F20\u5165\u6A21\u578Burl\u9519\u8BEF")
+    }
+}
+class XLowpolyJsonError extends XverseError$1 {
+    constructor(e) {
+        super(5205, e || "[Engine] \u4F20\u5165\u6A21\u578Bjson\u9519\u8BEF")
+    }
+}
+class XDecalError extends XverseError$1 {
+    constructor(e) {
+        super(5206, e || "[Engine] Decal\u6A21\u578B\u9519\u8BEF")
+    }
+}
+class XDecalTextureError extends XverseError$1 {
+    constructor(e) {
+        super(5207, e || "[Engine] decal\u7EB9\u7406\u9519\u8BEF")
+    }
+}
+class XBreathPointError extends XverseError$1 {
+    constructor(e) {
+        super(5208, e || "[Engine] \u547C\u5438\u70B9\u9519\u8BEF")
+    }
+}
+class XMaterialError extends XverseError$1 {
+    constructor(e) {
+        super(5210, e || "[Engine] Material\u9519\u8BEF")
+    }
+}
+class ExceedMaxAvatarNumError extends XverseError$1 {
+    constructor(e) {
+        super(5211, e || "[Engine] \u89D2\u8272\u4E2A\u6570\u8D85\u51FA\u4E0A\u9650")
+    }
+}
+class ParamError extends XverseError {
+    constructor(e) {
+        super(1001, e || "\u53C2\u6570\u9519\u8BEF")
+    }
+}
+class InternalError extends XverseError {
+    constructor(e) {
+        super(1002, e || "\u5185\u90E8\u9519\u8BEF")
+    }
+}
+class TimeoutError extends XverseError {
+    constructor(e) {
+        super(1003, e || "\u8D85\u65F6")
+    }
+}
+class AuthenticationError extends XverseError {
+    constructor(e) {
+        super(1004, e || "\u9274\u6743\u5931\u8D25")
+    }
+}
+class TokenExpiredError extends XverseError {
+    constructor(e) {
+        super(1005, e || "Token \u5DF2\u8FC7\u671F")
+    }
+}
+class UnsupportedError extends XverseError {
+    constructor(e) {
+        super(1006, e || "\u624B\u673A\u7CFB\u7EDF\u4E0D\u652F\u6301XVerse")
+    }
+}
+class InitNetworkTimeoutError extends XverseError {
+    constructor(e) {
+        super(1007, e || "\u7F51\u7EDC\u521D\u59CB\u5316\u8D85\u65F6")
+    }
+}
+class InitDecoderTimeoutError extends XverseError {
+    constructor(e) {
+        super(1008, e || "Decoder \u521D\u59CB\u5316\u8D85\u65F6")
+    }
+}
+class InitConfigTimeoutError extends XverseError {
+    constructor(e) {
+        super(1009, e || "\u914D\u7F6E\u521D\u59CB\u5316\u8D85\u65F6")
+    }
+}
+class InitEngineTimeoutError extends XverseError {
+    constructor(e) {
+        super(1010, e || "\u5F15\u64CE\u521D\u59CB\u5316\u8D85\u65F6")
+    }
+}
+class InitEngineError extends XverseError {
+    constructor(e) {
+        super(1011, e || "\u5F15\u64CE\u521D\u59CB\u5316\u9519\u8BEF")
+    }
+}
+class ActionBlockedError extends XverseError {
+    constructor(e) {
+        super(1012, e || "\u52A8\u4F5C\u88AB\u5C4F\u853D")
+    }
+}
+class PreloadCanceledError extends XverseError {
+    constructor(e) {
+        super(1013, e || "\u9884\u52A0\u8F7D\u88AB\u53D6\u6D88")
+    }
+}
+class FrequencyLimitError extends XverseError {
+    constructor(e) {
+        super(1014, e || "\u9891\u7387\u9650\u5236")
+    }
+}
+class UsersUpperLimitError extends XverseError {
+    constructor(e) {
+        super(2e3, e || "\u76F4\u64AD\u95F4\u4EBA\u6570\u5DF2\u6EE1")
+    }
+}
+class RoomsUpperLimitError extends XverseError {
+    constructor(e) {
+        super(2001, e || "\u623F\u95F4\u5230\u8FBE\u4E0A\u9650")
+    }
+}
+class ServerParamError extends XverseError {
+    constructor(e) {
+        super(2002, e || "\u670D\u52A1\u5668\u53C2\u6570\u9519\u8BEF")
+    }
+}
+class LackOfTokenError extends XverseError {
+    constructor(e) {
+        super(2003, e || "\u7F3A\u5C11 Token")
+    }
+}
+class LoginFailedError extends XverseError {
+    constructor(e) {
+        super(2004, e || "\u8FDB\u5165\u623F\u95F4\u5931\u8D25")
+    }
+}
+class VerifyServiceDownError extends XverseError {
+    constructor(e) {
+        super(2005, e || "\u9274\u6743\u670D\u52A1\u5F02\u5E38")
+    }
+}
+class CreateSessionFailedError extends XverseError {
+    constructor(e) {
+        super(2006, e || "\u521B\u5EFA session \u5931\u8D25")
+    }
+}
+class RtcConnectionError extends XverseError {
+    constructor(e) {
+        super(2008, e || "RTC\u5EFA\u8054\u5931\u8D25")
+    }
+}
+class DoActionFailedError extends XverseError {
+    constructor(e) {
+        super(2009, e || "\u52A8\u4F5C\u6267\u884C\u5931\u8D25")
+    }
+}
+class StateSyncFailedError extends XverseError {
+    constructor(e) {
+        super(2010, e || "\u72B6\u6001\u540C\u6B65\u5931\u8D25")
+    }
+}
+class BroadcastFailedError extends XverseError {
+    constructor(e) {
+        super(2011, e || "\u5E7F\u64AD\u63A5\u53E3\u63A5\u53E3\u5F02\u5E38")
+    }
+}
+class DataAbnormalError extends XverseError {
+    constructor(e) {
+        super(2012, e || "\u6570\u636E\u5F02\u5E38")
+    }
+}
+class GetOnVehicleError extends XverseError {
+    constructor(e) {
+        super(2015, e || "\u4E0A\u8F7D\u5177\u5931\u8D25\u9700\u8981\u9884\u7EA6")
+    }
+}
+class RepeatLoginError extends XverseError {
+    constructor(e) {
+        super(2017, e || "\u5F02\u5730\u767B\u5F55")
+    }
+}
+class RoomDoseNotExistError extends XverseError {
+    constructor(e) {
+        super(2018, e || "\u6307\u5B9A\u623F\u95F4\u4E0D\u5B58\u5728")
+    }
+}
+class TicketExpireError extends XverseError {
+    constructor(e) {
+        super(2019, e || "\u7968\u636E\u8FC7\u671F")
+    }
+}
+class ServerRateLimitError extends XverseError {
+    constructor(e) {
+        super(2020, e || "\u670D\u52A1\u7AEF\u9891\u7387\u9650\u5236")
+    }
+}
+class DoActionBlockedError extends XverseError {
+    constructor(e) {
+        super(2333, e || "\u52A8\u4F5C\u88AB\u5C4F\u853D")
+    }
+}
+class ActionMaybeDelayError extends XverseError {
+    constructor(e) {
+        super(2334, e || "\u52A8\u4F5C\u53EF\u80FD\u5EF6\u8FDF\u6267\u884C")
+    }
+}
+class ActionResponseTimeoutError extends XverseError {
+    constructor(e) {
+        super(2999, e || "action\u56DE\u5305\u8D85\u65F6")
+    }
+}

+ 538 - 0
XverseRoom.js

@@ -0,0 +1,538 @@
+class XverseRoom extends XverseRoom$1 {
+    constructor() {
+        super(...arguments);
+        E(this, "joyStick", new JoyStick(this))
+    }
+    afterJoinRoomHook() {
+        this.joyStick.init({})
+    }
+}
+
+const log$3 = new Logger("xverse-room");
+class XverseRoom$1 extends EventEmitter {
+    constructor(e) {
+        super();
+        E(this, "disableAutoTurn", !1);
+        E(this, "options");
+        E(this, "_currentNetworkOptions");
+        E(this, "lastSkinId");
+        E(this, "debug");
+        E(this, "isFirstDataUsed", !1);
+        E(this, "userId", null);
+        E(this, "pathManager", new PathManager);
+        E(this, "networkController");
+        E(this, "_startTime", Date.now());
+        E(this, "canvas");
+        E(this, "modelManager");
+        E(this, "eventsController");
+        E(this, "panorama");
+        E(this, "engineProxy");
+        E(this, "_id");
+        E(this, "skinList", []);
+        E(this, "isHost", !1);
+        E(this, "avatarManager", new XverseAvatarManager(this));
+        E(this, "effectManager", new XverseEffectManager(this));
+        E(this, "sceneManager");
+        E(this, "scene");
+        E(this, "breathPointManager");
+        E(this, "_currentState");
+        E(this, "joined", !1);
+        E(this, "disableRotate", !1);
+        E(this, "isPano", !1);
+        E(this, "movingByClick", !0);
+        E(this, "camera", new Camera(this));
+        E(this, "stats", new Stats(this));
+        E(this, "isUpdatedRawYUVData", !1);
+        E(this, "actionsHandler", new ActionsHandler(this));
+        E(this, "_currentClickingState", null);
+        E(this, "signal", new Signal(this));
+        E(this, "firstFrameTimestamp");
+        E(this, "receiveRtcData", async()=>{
+            log$3.info("Invoke receiveRtcData");
+            let e = !1
+              , t = !1
+              , r = !1
+              , n = !1;
+            return this.viewMode === "serverless" ? (log$3.warn("set view mode to serverless"),
+            this.setViewMode("observer").then(()=>this, ()=>this)) : new Promise(o=>{
+                const a = this.networkController.rtcp.workers;
+                a.registerFunction("signal", s=>{
+                    this.signal.handleSignal(s)
+                }
+                ),
+                a.registerFunction("stream", s=>{
+                    var l;
+                    if (this.emit("streamTimestamp", {
+                        timestamp: Date.now()
+                    }),
+                    t || (t = !0,
+                    log$3.info("Invoke stream event")),
+                    s.stream) {
+                        r || (r = !0,
+                        log$3.info("Invoke updateRawYUVData")),
+                        this.isUpdatedRawYUVData = !1;
+                        const u = (l = this._currentState.skin) == null ? void 0 : l.fov;
+                        this.sceneManager.materialComponent.updateRawYUVData(s.stream, s.width, s.height, u),
+                        this.isUpdatedRawYUVData = !0
+                    }
+                    e || (log$3.info("Invoke isAfterRenderRegistered"),
+                    e = !0,
+                    this.scene.registerAfterRender(()=>{
+                        this.engineProxy.frameRenderNumber >= 2 && (n || (n = !0,
+                        log$3.info("Invoke registerAfterRender")),
+                        this.isFirstDataUsed || (log$3.info("Invoke isStreamAvailable"),
+                        this.isFirstDataUsed = !0,
+                        this.firstFrameTimestamp = Date.now(),
+                        o(this),
+                        this.afterJoinRoom()))
+                    }
+                    ))
+                }
+                ),
+                this.panorama.bindListener(()=>{
+                    o(this),
+                    this.afterJoinRoom()
+                }
+                ),
+                a.registerFunction("reconnectedFrame", ()=>{}
+                ),
+                log$3.info("Invoke decoderWorker.postMessage"),
+                a.decoderWorker.postMessage({
+                    t: 5
+                })
+            }
+            )
+        }
+        );
+        E(this, "moveToExtra", "");
+        this.options = e,
+        this.options.wsServerUrl || (this.options.wsServerUrl = SERVER_URLS.DEV),
+        this.modelManager = ModelManager.getInstance(e.appId, e.releaseId),
+        this.updateReporter();
+        const n = e
+          , {canvas: t} = n
+          , r = Oe(n, ["canvas"]);
+        log$3.infoAndReportMeasurement({
+            metric: "startJoinRoomAt",
+            startTime: Date.now(),
+            group: "joinRoom",
+            extra: r,
+            value: 0
+        })
+    }
+    get currentNetworkOptions() {
+        return this._currentNetworkOptions
+    }
+    get viewMode() {
+        var e;
+        return ((e = this._currentState) == null ? void 0 : e.viewMode) || "full"
+    }
+    get id() {
+        return this._id
+    }
+    get skinId() {
+        return this._currentState.skinId
+    }
+    get skin() {
+        return this._currentState.skin
+    }
+    get sessionId() {
+        return this.currentNetworkOptions.sessionId
+    }
+    get pictureQualityLevel() {
+        return this.currentState.pictureQualityLevel
+    }
+    get avatars() {
+        return Array.from(this.avatarManager.avatars.values())
+    }
+    get currentState() {
+        var e;
+        return le(oe({}, this._currentState), {
+            state: (e = this.networkController) == null ? void 0 : e._state
+        })
+    }
+    get _userAvatar() {
+        return this.avatars.find(e=>e.userId === this.userId)
+    }
+    get tvs() {
+        return this.engineProxy._tvs
+    }
+    get tv() {
+        return this.tvs[0]
+    }
+    get currentClickingState() {
+        return this._currentClickingState
+    }
+    afterJoinRoomHook() {}
+    beforeJoinRoomResolveHook() {}
+    afterReconnectedHook() {}
+    handleSignalHook(e) {}
+    skinChangedHook() {}
+    async beforeStartGameHook(e) {}
+    loadAssetsHook() {}
+    afterUserAvatarLoadedHook() {}
+    audienceViewModeHook() {}
+    setViewModeToObserver() {}
+    handleVehicleHook(e) {}
+    updateReporter() {
+        const {avatarId: e, skinId: t, userId: r, roomId: n, role: o, appId: a, wsServerUrl: s} = this.options;
+        reporter.updateHeader({
+            userId: r
+        }),
+        reporter.updateBody({
+            roomId: n,
+            role: o,
+            skinId: t,
+            avatarId: e,
+            appId: a,
+            wsServerUrl: s
+        })
+    }
+    async initRoom() {
+        const {timeout: e=DEFAULT_JOINROOM_TIMEOUT} = this.options;
+        return isSupported() ? this._initRoom()._timeout(e, new TimeoutError("initRoom timeout")) : Promise.reject(new UnsupportedError)
+    }
+    async _initRoom() {
+        const e = this.validateOptions(this.options);
+        if (e)
+            return log$3.error("initRoom param error", e),
+            Promise.reject(e);
+        const {canvas: t, avatarId: r, skinId: n, userId: o, wsServerUrl: a, role: s, token: l, pageSession: u, rotationRenderType: c, isAllSync: h=!1, appId: f, camera: d, player: _, avatarComponents: g, nickname: m, avatarScale: v, firends: y=[], syncByEvent: b=!1, areaName: T, attitude: C=MotionType.Walk, pathName: A, viewMode: S="full", person: P, roomId: R, roomTypeId: M, hasAvatar: x=!1, syncToOthers: I=!1, prioritySync: w=!1, removeWhenDisconnected: O=!0, extra: D} = this.options;
+        this.setCurrentNetworkOptions({
+            avatarId: r,
+            skinId: n,
+            roomId: R,
+            userId: o,
+            wsServerUrl: a,
+            role: s,
+            token: l,
+            pageSession: u,
+            rotationRenderType: c,
+            isAllSync: h,
+            appId: f,
+            camera: d,
+            player: _,
+            avatarComponents: g,
+            nickname: m,
+            avatarScale: v,
+            firends: y,
+            syncByEvent: b,
+            areaName: T,
+            attitude: C,
+            pathName: A,
+            person: P,
+            roomTypeId: M,
+            hasAvatar: x,
+            syncToOthers: I,
+            prioritySync: w,
+            extra: D,
+            removeWhenDisconnected: O
+        }),
+        this.userId = o,
+        this.canvas = t,
+        T && (this.pathManager.currentArea = T),
+        this.networkController = new NetworkController(this),
+        this.setCurrentState({
+            areaName: T,
+            pathName: A,
+            attitude: C,
+            speed: 0,
+            viewMode: S,
+            state: this.networkController._state,
+            skinId: n
+        });
+        try {
+            await Promise.all([this.initNetwork(), this.initConfig(), this.initWasm()]),
+            log$3.info("network config wasm all ready, start to create game");
+            const F = await this.requestCreateRoom({
+                skinId: n
+            })
+              , V = F.routeList.find(L=>L.areaName === T)
+              , N = ((V == null ? void 0 : V.step) || 7.5) * 30;
+            this.updateCurrentState({
+                skin: F,
+                skinId: F.id,
+                versionId: F.versionId,
+                speed: N
+            }),
+            await this.initEngine(F)
+        } catch (F) {
+            return Promise.reject(F)
+        }
+        return this.beforeJoinRoomResolve(),
+        this.receiveRtcData()
+    }
+    beforeJoinRoomResolve() {
+        this.setupStats(),
+        this.eventsController = new EventsController(this),
+        this.eventsController.bindEvents(),
+        this.panorama = new Panorama(this),
+        this.beforeJoinRoomResolveHook()
+    }
+    afterJoinRoom() {
+        this.joined = !0,
+        this.viewMode === "observer" && this.setViewModeToObserver(),
+        log$3.infoAndReportMeasurement({
+            tag: this.viewMode,
+            value: this.firstFrameTimestamp - this._startTime,
+            startTime: Date.now(),
+            metric: "joinRoom"
+        }),
+        this.camera.initialFov = this.sceneManager.cameraComponent.getCameraFov(),
+        this.stats.on("stats", ({stats: e})=>{
+            reporter.report("stats", oe({}, e))
+        }
+        ),
+        this.debug = new Debug(this),
+        this.afterJoinRoomHook()
+    }
+    afterReconnected() {
+        this.avatarManager.clearOtherUsers(),
+        this.afterReconnectedHook()
+    }
+    leave() {
+        var e, t;
+        return log$3.info("Invoke room.leave"),
+        (e = this.eventsController) == null || e.clearEvents(),
+        (t = this.networkController) == null || t.quit(),
+        this
+    }
+    validateOptions(e) {
+        const {canvas: t, avatarId: r, skinId: n, userId: o, role: a, roomId: s, token: l, appId: u, avatarComponents: c} = e || {}
+          , h = [];
+        return t instanceof HTMLCanvasElement || h.push(new ParamError("`canvas` must be instanceof of HTMLCanvasElement")),
+        (!o || typeof o != "string") && h.push(new ParamError("`userId` must be string")),
+        (!l || typeof l != "string") && h.push(new ParamError("`token` must be string")),
+        (!u || typeof u != "string") && h.push(new ParamError("`appId` must be string")),
+        a == "audience" || (!r || !n) && h.push(new ParamError("`avatarId` and `skinId` is required when create room")),
+        h[0]
+    }
+    async initNetwork() {
+        if (this.viewMode === "serverless")
+            return Promise.resolve();
+        const e = Date.now();
+        try {
+            await this.networkController.connect()._timeout(8e3, new InitNetworkTimeoutError),
+            log$3.infoAndReportMeasurement({
+                metric: "networkInitAt",
+                startTime: this._startTime,
+                group: "joinRoom"
+            }),
+            log$3.infoAndReportMeasurement({
+                metric: "networkInitCost",
+                startTime: e,
+                group: "joinRoom"
+            })
+        } catch (t) {
+            throw log$3.infoAndReportMeasurement({
+                metric: "networkInitAt",
+                startTime: e,
+                group: "joinRoom",
+                error: t
+            }),
+            t
+        }
+    }
+    async initConfig() {
+        const e = Date.now();
+        try {
+            await this.modelManager.getApplicationConfig()._timeout(8e3, new InitConfigTimeoutError),
+            log$3.infoAndReportMeasurement({
+                metric: "configInitAt",
+                startTime: this._startTime,
+                group: "joinRoom"
+            }),
+            log$3.infoAndReportMeasurement({
+                metric: "configInitCost",
+                startTime: e,
+                group: "joinRoom"
+            })
+        } catch (t) {
+            throw log$3.infoAndReportMeasurement({
+                metric: "configInitAt",
+                startTime: e,
+                group: "joinRoom",
+                error: t
+            }),
+            t
+        }
+    }
+    async initEngine(e) {
+        const t = Date.now();
+        try {
+            this.engineProxy = new EngineProxy(this),
+            await this.engineProxy.initEngine(e),
+            log$3.infoAndReportMeasurement({
+                metric: "webglInitAt",
+                startTime: this._startTime,
+                group: "joinRoom"
+            }),
+            log$3.infoAndReportMeasurement({
+                metric: "webglInitCost",
+                startTime: t,
+                group: "joinRoom"
+            });
+            return
+        } catch (r) {
+            let n = r;
+            return r.code !== Codes$1.InitEngineTimeout && (n = new InitEngineError),
+            log$3.error(r),
+            log$3.infoAndReportMeasurement({
+                metric: "webglInitAt",
+                startTime: t,
+                group: "joinRoom",
+                error: n
+            }),
+            Promise.reject(n)
+        }
+    }
+    async initWasm() {
+        if (this.viewMode === "serverless")
+            return Promise.resolve();
+        const e = Date.now();
+        try {
+            await this.networkController.rtcp.workers.init(this.options.resolution)._timeout(8e3, new InitDecoderTimeoutError),
+            this.networkController.rtcp.workers.registerFunction("error", t=>{
+                log$3.error("decode error", t);
+                const {code: r, message: n} = t;
+                this.emit("error", {
+                    code: r,
+                    msg: n
+                })
+            }
+            ),
+            log$3.infoAndReportMeasurement({
+                metric: "wasmInitAt",
+                group: "joinRoom",
+                startTime: this._startTime
+            }),
+            log$3.infoAndReportMeasurement({
+                metric: "wasmInitCost",
+                group: "joinRoom",
+                startTime: e
+            }),
+            eventsManager.on("traceId", t=>{
+                this.networkController.rtcp.workers.onTraceId(t)
+            }
+            )
+        } catch (t) {
+            throw log$3.infoAndReportMeasurement({
+                metric: "wasmInitAt",
+                group: "joinRoom",
+                startTime: e,
+                error: t
+            }),
+            t
+        }
+    }
+    async requestCreateRoom({skinId: e}) {
+        let t;
+        if (e) {
+            t = await this.getSkin(e);
+            const r = await this.modelManager.findRoute(e, this.options.pathName);
+            this.updateCurrentNetworkOptions({
+                areaName: r.areaName,
+                attitude: r.attitude,
+                versionId: t.versionId
+            });
+            const {camera: n, player: o} = getRandomItem(r.birthPointList) || this.options;
+            this.options.camera || this.updateCurrentNetworkOptions({
+                camera: n
+            }),
+            this.options.player || this.updateCurrentNetworkOptions({
+                player: o
+            })
+        }
+        if (this.viewMode === "serverless")
+            return t;
+        try {
+            await this.beforeStartGameHook(this.options);
+            const {room_id: r, data: n, session_id: o} = await this.networkController.startGame();
+            this._id = r;
+            const a = JSON.parse(n);
+            this.isHost = a.IsHost,
+            e = a.SkinID || e;
+            const s = await this.getSkin(e);
+            return this.updateCurrentNetworkOptions({
+                roomId: r,
+                sessionId: o
+            }),
+            reporter.updateBody({
+                roomId: r,
+                skinId: e,
+                serverSession: o
+            }),
+            s
+        } catch (r) {
+            throw log$3.error("Request create room error", r),
+            r
+        }
+    }
+    pause() {
+        return this.engineProxy.pause()
+    }
+    resume() {
+        return this.engineProxy.resume()
+    }
+    reconnect() {
+        this.networkController.reconnect()
+    }
+    async setViewMode(e) {}
+    handleRepetLogin() {
+        log$3.warn("receive " + Codes$1.RepeatLogin + " for repeat login"),
+        this.emit("repeatLogin"),
+        reporter.disable(),
+        this.networkController.quit()
+    }
+    setPictureQualityLevel(e) {
+        const t = {
+            high: EImageQuality.high,
+            low: EImageQuality.low,
+            average: EImageQuality.mid
+        };
+        return this.updateCurrentState({
+            pictureQualityLevel: e
+        }),
+        this.sceneManager.setImageQuality(t[e])
+    }
+    async getSkin(e) {
+        let t = null;
+        if (t = (this.skinList = await this.modelManager.getSkinsList()).find(n=>n.id === e || n.id === e),
+        t)
+            return t;
+        {
+            const n = `skin is invalid: skinId: ${e}`;
+            return Promise.reject(new ParamError(n))
+        }
+    }
+    setupStats() {
+        this.stats.assign({
+            roomId: this.id,
+            userId: this.userId
+        }),
+        setInterval(this.engineProxy.updateStats, 1e3)
+    }
+    proxyEvents(e, t) {
+        this.emit(e, t)
+    }
+    setCurrentNetworkOptions(e) {
+        this._currentNetworkOptions = e
+    }
+    updateCurrentNetworkOptions(e) {
+        Object.assign(this._currentNetworkOptions, e),
+        Object.assign(this.options, e)
+    }
+    setCurrentState(e) {
+        this._currentState = e
+    }
+    updateCurrentState(e) {
+        e.skinId && (this.lastSkinId = this.currentState.skinId,
+        this.updateCurrentNetworkOptions({
+            skinId: e.skinId
+        })),
+        e.versionId && this.updateCurrentNetworkOptions({
+            versionId: e.versionId
+        }),
+        Object.assign(this._currentState, e)
+    }
+}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2 - 0
libs/Oimo.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 925 - 0
libs/ammo.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 3 - 0
libs/babylon.gui.min.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
libs/babylon.gui.min.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 3 - 0
libs/babylon.inspector.bundle.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
libs/babylon.inspector.bundle.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2 - 0
libs/babylon.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
libs/babylon.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 11466 - 0
libs/babylonjs.loaders.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
libs/babylonjs.loaders.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 3 - 0
libs/babylonjs.materials.min.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
libs/babylonjs.materials.min.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 3 - 0
libs/babylonjs.postProcess.min.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
libs/babylonjs.postProcess.min.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 3 - 0
libs/babylonjs.proceduralTextures.min.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
libs/babylonjs.proceduralTextures.min.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2 - 0
libs/babylonjs.serializers.min.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
libs/babylonjs.serializers.min.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 13687 - 0
libs/cannon.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 5983 - 0
libs/crypto-js.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2 - 0
libs/dat.gui.min.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
libs/earcut.min.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 6 - 0
libs/jquery-1.10.2.min.js


+ 683 - 0
libs/md5.js

@@ -0,0 +1,683 @@
+/**
+ * [js-md5]{@link https://github.com/emn178/js-md5}
+ *
+ * @namespace md5
+ * @version 0.7.3
+ * @author Chen, Yi-Cyuan [emn178@gmail.com]
+ * @copyright Chen, Yi-Cyuan 2014-2017
+ * @license MIT
+ */
+(function () {
+  'use strict';
+
+  var ERROR = 'input is invalid type';
+  var WINDOW = typeof window === 'object';
+  var root = WINDOW ? window : {};
+  if (root.JS_MD5_NO_WINDOW) {
+    WINDOW = false;
+  }
+  var WEB_WORKER = !WINDOW && typeof self === 'object';
+  var NODE_JS = !root.JS_MD5_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
+  if (NODE_JS) {
+    root = global;
+  } else if (WEB_WORKER) {
+    root = self;
+  }
+  var COMMON_JS = !root.JS_MD5_NO_COMMON_JS && typeof module === 'object' && module.exports;
+  var AMD = typeof define === 'function' && define.amd;
+  var ARRAY_BUFFER = !root.JS_MD5_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
+  var HEX_CHARS = '0123456789abcdef'.split('');
+  var EXTRA = [128, 32768, 8388608, -2147483648];
+  var SHIFT = [0, 8, 16, 24];
+  var OUTPUT_TYPES = ['hex', 'array', 'digest', 'buffer', 'arrayBuffer', 'base64'];
+  var BASE64_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');
+
+  var blocks = [], buffer8;
+  if (ARRAY_BUFFER) {
+    var buffer = new ArrayBuffer(68);
+    buffer8 = new Uint8Array(buffer);
+    blocks = new Uint32Array(buffer);
+  }
+
+  if (root.JS_MD5_NO_NODE_JS || !Array.isArray) {
+    Array.isArray = function (obj) {
+      return Object.prototype.toString.call(obj) === '[object Array]';
+    };
+  }
+
+  if (ARRAY_BUFFER && (root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
+    ArrayBuffer.isView = function (obj) {
+      return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
+    };
+  }
+
+  /**
+   * @method hex
+   * @memberof md5
+   * @description Output hash as hex string
+   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+   * @returns {String} Hex string
+   * @example
+   * md5.hex('The quick brown fox jumps over the lazy dog');
+   * // equal to
+   * md5('The quick brown fox jumps over the lazy dog');
+   */
+  /**
+   * @method digest
+   * @memberof md5
+   * @description Output hash as bytes array
+   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+   * @returns {Array} Bytes array
+   * @example
+   * md5.digest('The quick brown fox jumps over the lazy dog');
+   */
+  /**
+   * @method array
+   * @memberof md5
+   * @description Output hash as bytes array
+   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+   * @returns {Array} Bytes array
+   * @example
+   * md5.array('The quick brown fox jumps over the lazy dog');
+   */
+  /**
+   * @method arrayBuffer
+   * @memberof md5
+   * @description Output hash as ArrayBuffer
+   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+   * @returns {ArrayBuffer} ArrayBuffer
+   * @example
+   * md5.arrayBuffer('The quick brown fox jumps over the lazy dog');
+   */
+  /**
+   * @method buffer
+   * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
+   * @memberof md5
+   * @description Output hash as ArrayBuffer
+   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+   * @returns {ArrayBuffer} ArrayBuffer
+   * @example
+   * md5.buffer('The quick brown fox jumps over the lazy dog');
+   */
+  /**
+   * @method base64
+   * @memberof md5
+   * @description Output hash as base64 string
+   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+   * @returns {String} base64 string
+   * @example
+   * md5.base64('The quick brown fox jumps over the lazy dog');
+   */
+  var createOutputMethod = function (outputType) {
+    return function (message) {
+      return new Md5(true).update(message)[outputType]();
+    };
+  };
+
+  /**
+   * @method create
+   * @memberof md5
+   * @description Create Md5 object
+   * @returns {Md5} Md5 object.
+   * @example
+   * var hash = md5.create();
+   */
+  /**
+   * @method update
+   * @memberof md5
+   * @description Create and update Md5 object
+   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+   * @returns {Md5} Md5 object.
+   * @example
+   * var hash = md5.update('The quick brown fox jumps over the lazy dog');
+   * // equal to
+   * var hash = md5.create();
+   * hash.update('The quick brown fox jumps over the lazy dog');
+   */
+  var createMethod = function () {
+    var method = createOutputMethod('hex');
+    if (NODE_JS) {
+      method = nodeWrap(method);
+    }
+    method.create = function () {
+      return new Md5();
+    };
+    method.update = function (message) {
+      return method.create().update(message);
+    };
+    for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
+      var type = OUTPUT_TYPES[i];
+      method[type] = createOutputMethod(type);
+    }
+    return method;
+  };
+
+  var nodeWrap = function (method) {
+    var crypto = eval("require('crypto')");
+    var Buffer = eval("require('buffer').Buffer");
+    var nodeMethod = function (message) {
+      if (typeof message === 'string') {
+        return crypto.createHash('md5').update(message, 'utf8').digest('hex');
+      } else {
+        if (message === null || message === undefined) {
+          throw ERROR;
+        } else if (message.constructor === ArrayBuffer) {
+          message = new Uint8Array(message);
+        }
+      }
+      if (Array.isArray(message) || ArrayBuffer.isView(message) ||
+        message.constructor === Buffer) {
+        return crypto.createHash('md5').update(new Buffer(message)).digest('hex');
+      } else {
+        return method(message);
+      }
+    };
+    return nodeMethod;
+  };
+
+  /**
+   * Md5 class
+   * @class Md5
+   * @description This is internal class.
+   * @see {@link md5.create}
+   */
+  function Md5(sharedMemory) {
+    if (sharedMemory) {
+      blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] =
+      blocks[4] = blocks[5] = blocks[6] = blocks[7] =
+      blocks[8] = blocks[9] = blocks[10] = blocks[11] =
+      blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+      this.blocks = blocks;
+      this.buffer8 = buffer8;
+    } else {
+      if (ARRAY_BUFFER) {
+        var buffer = new ArrayBuffer(68);
+        this.buffer8 = new Uint8Array(buffer);
+        this.blocks = new Uint32Array(buffer);
+      } else {
+        this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+      }
+    }
+    this.h0 = this.h1 = this.h2 = this.h3 = this.start = this.bytes = this.hBytes = 0;
+    this.finalized = this.hashed = false;
+    this.first = true;
+  }
+
+  /**
+   * @method update
+   * @memberof Md5
+   * @instance
+   * @description Update hash
+   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+   * @returns {Md5} Md5 object.
+   * @see {@link md5.update}
+   */
+  Md5.prototype.update = function (message) {
+    if (this.finalized) {
+      return;
+    }
+
+    var notString, type = typeof message;
+    if (type !== 'string') {
+      if (type === 'object') {
+        if (message === null) {
+          throw ERROR;
+        } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
+          message = new Uint8Array(message);
+        } else if (!Array.isArray(message)) {
+          if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {
+            throw ERROR;
+          }
+        }
+      } else {
+        throw ERROR;
+      }
+      notString = true;
+    }
+    var code, index = 0, i, length = message.length, blocks = this.blocks;
+    var buffer8 = this.buffer8;
+
+    while (index < length) {
+      if (this.hashed) {
+        this.hashed = false;
+        blocks[0] = blocks[16];
+        blocks[16] = blocks[1] = blocks[2] = blocks[3] =
+        blocks[4] = blocks[5] = blocks[6] = blocks[7] =
+        blocks[8] = blocks[9] = blocks[10] = blocks[11] =
+        blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+      }
+
+      if (notString) {
+        if (ARRAY_BUFFER) {
+          for (i = this.start; index < length && i < 64; ++index) {
+            buffer8[i++] = message[index];
+          }
+        } else {
+          for (i = this.start; index < length && i < 64; ++index) {
+            blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
+          }
+        }
+      } else {
+        if (ARRAY_BUFFER) {
+          for (i = this.start; index < length && i < 64; ++index) {
+            code = message.charCodeAt(index);
+            if (code < 0x80) {
+              buffer8[i++] = code;
+            } else if (code < 0x800) {
+              buffer8[i++] = 0xc0 | (code >> 6);
+              buffer8[i++] = 0x80 | (code & 0x3f);
+            } else if (code < 0xd800 || code >= 0xe000) {
+              buffer8[i++] = 0xe0 | (code >> 12);
+              buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);
+              buffer8[i++] = 0x80 | (code & 0x3f);
+            } else {
+              code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
+              buffer8[i++] = 0xf0 | (code >> 18);
+              buffer8[i++] = 0x80 | ((code >> 12) & 0x3f);
+              buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);
+              buffer8[i++] = 0x80 | (code & 0x3f);
+            }
+          }
+        } else {
+          for (i = this.start; index < length && i < 64; ++index) {
+            code = message.charCodeAt(index);
+            if (code < 0x80) {
+              blocks[i >> 2] |= code << SHIFT[i++ & 3];
+            } else if (code < 0x800) {
+              blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
+              blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
+            } else if (code < 0xd800 || code >= 0xe000) {
+              blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
+              blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
+              blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
+            } else {
+              code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
+              blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
+              blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
+              blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
+              blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
+            }
+          }
+        }
+      }
+      this.lastByteIndex = i;
+      this.bytes += i - this.start;
+      if (i >= 64) {
+        this.start = i - 64;
+        this.hash();
+        this.hashed = true;
+      } else {
+        this.start = i;
+      }
+    }
+    if (this.bytes > 4294967295) {
+      this.hBytes += this.bytes / 4294967296 << 0;
+      this.bytes = this.bytes % 4294967296;
+    }
+    return this;
+  };
+
+  Md5.prototype.finalize = function () {
+    if (this.finalized) {
+      return;
+    }
+    this.finalized = true;
+    var blocks = this.blocks, i = this.lastByteIndex;
+    blocks[i >> 2] |= EXTRA[i & 3];
+    if (i >= 56) {
+      if (!this.hashed) {
+        this.hash();
+      }
+      blocks[0] = blocks[16];
+      blocks[16] = blocks[1] = blocks[2] = blocks[3] =
+      blocks[4] = blocks[5] = blocks[6] = blocks[7] =
+      blocks[8] = blocks[9] = blocks[10] = blocks[11] =
+      blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+    }
+    blocks[14] = this.bytes << 3;
+    blocks[15] = this.hBytes << 3 | this.bytes >>> 29;
+    this.hash();
+  };
+
+  Md5.prototype.hash = function () {
+    var a, b, c, d, bc, da, blocks = this.blocks;
+
+    if (this.first) {
+      a = blocks[0] - 680876937;
+      a = (a << 7 | a >>> 25) - 271733879 << 0;
+      d = (-1732584194 ^ a & 2004318071) + blocks[1] - 117830708;
+      d = (d << 12 | d >>> 20) + a << 0;
+      c = (-271733879 ^ (d & (a ^ -271733879))) + blocks[2] - 1126478375;
+      c = (c << 17 | c >>> 15) + d << 0;
+      b = (a ^ (c & (d ^ a))) + blocks[3] - 1316259209;
+      b = (b << 22 | b >>> 10) + c << 0;
+    } else {
+      a = this.h0;
+      b = this.h1;
+      c = this.h2;
+      d = this.h3;
+      a += (d ^ (b & (c ^ d))) + blocks[0] - 680876936;
+      a = (a << 7 | a >>> 25) + b << 0;
+      d += (c ^ (a & (b ^ c))) + blocks[1] - 389564586;
+      d = (d << 12 | d >>> 20) + a << 0;
+      c += (b ^ (d & (a ^ b))) + blocks[2] + 606105819;
+      c = (c << 17 | c >>> 15) + d << 0;
+      b += (a ^ (c & (d ^ a))) + blocks[3] - 1044525330;
+      b = (b << 22 | b >>> 10) + c << 0;
+    }
+
+    a += (d ^ (b & (c ^ d))) + blocks[4] - 176418897;
+    a = (a << 7 | a >>> 25) + b << 0;
+    d += (c ^ (a & (b ^ c))) + blocks[5] + 1200080426;
+    d = (d << 12 | d >>> 20) + a << 0;
+    c += (b ^ (d & (a ^ b))) + blocks[6] - 1473231341;
+    c = (c << 17 | c >>> 15) + d << 0;
+    b += (a ^ (c & (d ^ a))) + blocks[7] - 45705983;
+    b = (b << 22 | b >>> 10) + c << 0;
+    a += (d ^ (b & (c ^ d))) + blocks[8] + 1770035416;
+    a = (a << 7 | a >>> 25) + b << 0;
+    d += (c ^ (a & (b ^ c))) + blocks[9] - 1958414417;
+    d = (d << 12 | d >>> 20) + a << 0;
+    c += (b ^ (d & (a ^ b))) + blocks[10] - 42063;
+    c = (c << 17 | c >>> 15) + d << 0;
+    b += (a ^ (c & (d ^ a))) + blocks[11] - 1990404162;
+    b = (b << 22 | b >>> 10) + c << 0;
+    a += (d ^ (b & (c ^ d))) + blocks[12] + 1804603682;
+    a = (a << 7 | a >>> 25) + b << 0;
+    d += (c ^ (a & (b ^ c))) + blocks[13] - 40341101;
+    d = (d << 12 | d >>> 20) + a << 0;
+    c += (b ^ (d & (a ^ b))) + blocks[14] - 1502002290;
+    c = (c << 17 | c >>> 15) + d << 0;
+    b += (a ^ (c & (d ^ a))) + blocks[15] + 1236535329;
+    b = (b << 22 | b >>> 10) + c << 0;
+    a += (c ^ (d & (b ^ c))) + blocks[1] - 165796510;
+    a = (a << 5 | a >>> 27) + b << 0;
+    d += (b ^ (c & (a ^ b))) + blocks[6] - 1069501632;
+    d = (d << 9 | d >>> 23) + a << 0;
+    c += (a ^ (b & (d ^ a))) + blocks[11] + 643717713;
+    c = (c << 14 | c >>> 18) + d << 0;
+    b += (d ^ (a & (c ^ d))) + blocks[0] - 373897302;
+    b = (b << 20 | b >>> 12) + c << 0;
+    a += (c ^ (d & (b ^ c))) + blocks[5] - 701558691;
+    a = (a << 5 | a >>> 27) + b << 0;
+    d += (b ^ (c & (a ^ b))) + blocks[10] + 38016083;
+    d = (d << 9 | d >>> 23) + a << 0;
+    c += (a ^ (b & (d ^ a))) + blocks[15] - 660478335;
+    c = (c << 14 | c >>> 18) + d << 0;
+    b += (d ^ (a & (c ^ d))) + blocks[4] - 405537848;
+    b = (b << 20 | b >>> 12) + c << 0;
+    a += (c ^ (d & (b ^ c))) + blocks[9] + 568446438;
+    a = (a << 5 | a >>> 27) + b << 0;
+    d += (b ^ (c & (a ^ b))) + blocks[14] - 1019803690;
+    d = (d << 9 | d >>> 23) + a << 0;
+    c += (a ^ (b & (d ^ a))) + blocks[3] - 187363961;
+    c = (c << 14 | c >>> 18) + d << 0;
+    b += (d ^ (a & (c ^ d))) + blocks[8] + 1163531501;
+    b = (b << 20 | b >>> 12) + c << 0;
+    a += (c ^ (d & (b ^ c))) + blocks[13] - 1444681467;
+    a = (a << 5 | a >>> 27) + b << 0;
+    d += (b ^ (c & (a ^ b))) + blocks[2] - 51403784;
+    d = (d << 9 | d >>> 23) + a << 0;
+    c += (a ^ (b & (d ^ a))) + blocks[7] + 1735328473;
+    c = (c << 14 | c >>> 18) + d << 0;
+    b += (d ^ (a & (c ^ d))) + blocks[12] - 1926607734;
+    b = (b << 20 | b >>> 12) + c << 0;
+    bc = b ^ c;
+    a += (bc ^ d) + blocks[5] - 378558;
+    a = (a << 4 | a >>> 28) + b << 0;
+    d += (bc ^ a) + blocks[8] - 2022574463;
+    d = (d << 11 | d >>> 21) + a << 0;
+    da = d ^ a;
+    c += (da ^ b) + blocks[11] + 1839030562;
+    c = (c << 16 | c >>> 16) + d << 0;
+    b += (da ^ c) + blocks[14] - 35309556;
+    b = (b << 23 | b >>> 9) + c << 0;
+    bc = b ^ c;
+    a += (bc ^ d) + blocks[1] - 1530992060;
+    a = (a << 4 | a >>> 28) + b << 0;
+    d += (bc ^ a) + blocks[4] + 1272893353;
+    d = (d << 11 | d >>> 21) + a << 0;
+    da = d ^ a;
+    c += (da ^ b) + blocks[7] - 155497632;
+    c = (c << 16 | c >>> 16) + d << 0;
+    b += (da ^ c) + blocks[10] - 1094730640;
+    b = (b << 23 | b >>> 9) + c << 0;
+    bc = b ^ c;
+    a += (bc ^ d) + blocks[13] + 681279174;
+    a = (a << 4 | a >>> 28) + b << 0;
+    d += (bc ^ a) + blocks[0] - 358537222;
+    d = (d << 11 | d >>> 21) + a << 0;
+    da = d ^ a;
+    c += (da ^ b) + blocks[3] - 722521979;
+    c = (c << 16 | c >>> 16) + d << 0;
+    b += (da ^ c) + blocks[6] + 76029189;
+    b = (b << 23 | b >>> 9) + c << 0;
+    bc = b ^ c;
+    a += (bc ^ d) + blocks[9] - 640364487;
+    a = (a << 4 | a >>> 28) + b << 0;
+    d += (bc ^ a) + blocks[12] - 421815835;
+    d = (d << 11 | d >>> 21) + a << 0;
+    da = d ^ a;
+    c += (da ^ b) + blocks[15] + 530742520;
+    c = (c << 16 | c >>> 16) + d << 0;
+    b += (da ^ c) + blocks[2] - 995338651;
+    b = (b << 23 | b >>> 9) + c << 0;
+    a += (c ^ (b | ~d)) + blocks[0] - 198630844;
+    a = (a << 6 | a >>> 26) + b << 0;
+    d += (b ^ (a | ~c)) + blocks[7] + 1126891415;
+    d = (d << 10 | d >>> 22) + a << 0;
+    c += (a ^ (d | ~b)) + blocks[14] - 1416354905;
+    c = (c << 15 | c >>> 17) + d << 0;
+    b += (d ^ (c | ~a)) + blocks[5] - 57434055;
+    b = (b << 21 | b >>> 11) + c << 0;
+    a += (c ^ (b | ~d)) + blocks[12] + 1700485571;
+    a = (a << 6 | a >>> 26) + b << 0;
+    d += (b ^ (a | ~c)) + blocks[3] - 1894986606;
+    d = (d << 10 | d >>> 22) + a << 0;
+    c += (a ^ (d | ~b)) + blocks[10] - 1051523;
+    c = (c << 15 | c >>> 17) + d << 0;
+    b += (d ^ (c | ~a)) + blocks[1] - 2054922799;
+    b = (b << 21 | b >>> 11) + c << 0;
+    a += (c ^ (b | ~d)) + blocks[8] + 1873313359;
+    a = (a << 6 | a >>> 26) + b << 0;
+    d += (b ^ (a | ~c)) + blocks[15] - 30611744;
+    d = (d << 10 | d >>> 22) + a << 0;
+    c += (a ^ (d | ~b)) + blocks[6] - 1560198380;
+    c = (c << 15 | c >>> 17) + d << 0;
+    b += (d ^ (c | ~a)) + blocks[13] + 1309151649;
+    b = (b << 21 | b >>> 11) + c << 0;
+    a += (c ^ (b | ~d)) + blocks[4] - 145523070;
+    a = (a << 6 | a >>> 26) + b << 0;
+    d += (b ^ (a | ~c)) + blocks[11] - 1120210379;
+    d = (d << 10 | d >>> 22) + a << 0;
+    c += (a ^ (d | ~b)) + blocks[2] + 718787259;
+    c = (c << 15 | c >>> 17) + d << 0;
+    b += (d ^ (c | ~a)) + blocks[9] - 343485551;
+    b = (b << 21 | b >>> 11) + c << 0;
+
+    if (this.first) {
+      this.h0 = a + 1732584193 << 0;
+      this.h1 = b - 271733879 << 0;
+      this.h2 = c - 1732584194 << 0;
+      this.h3 = d + 271733878 << 0;
+      this.first = false;
+    } else {
+      this.h0 = this.h0 + a << 0;
+      this.h1 = this.h1 + b << 0;
+      this.h2 = this.h2 + c << 0;
+      this.h3 = this.h3 + d << 0;
+    }
+  };
+
+  /**
+   * @method hex
+   * @memberof Md5
+   * @instance
+   * @description Output hash as hex string
+   * @returns {String} Hex string
+   * @see {@link md5.hex}
+   * @example
+   * hash.hex();
+   */
+  Md5.prototype.hex = function () {
+    this.finalize();
+
+    var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;
+
+    return HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] +
+      HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] +
+      HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] +
+      HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] +
+      HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] +
+      HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] +
+      HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] +
+      HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] +
+      HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] +
+      HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] +
+      HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] +
+      HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] +
+      HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] +
+      HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] +
+      HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] +
+      HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F];
+  };
+
+  /**
+   * @method toString
+   * @memberof Md5
+   * @instance
+   * @description Output hash as hex string
+   * @returns {String} Hex string
+   * @see {@link md5.hex}
+   * @example
+   * hash.toString();
+   */
+  Md5.prototype.toString = Md5.prototype.hex;
+
+  /**
+   * @method digest
+   * @memberof Md5
+   * @instance
+   * @description Output hash as bytes array
+   * @returns {Array} Bytes array
+   * @see {@link md5.digest}
+   * @example
+   * hash.digest();
+   */
+  Md5.prototype.digest = function () {
+    this.finalize();
+
+    var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;
+    return [
+      h0 & 0xFF, (h0 >> 8) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 24) & 0xFF,
+      h1 & 0xFF, (h1 >> 8) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 24) & 0xFF,
+      h2 & 0xFF, (h2 >> 8) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 24) & 0xFF,
+      h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 24) & 0xFF
+    ];
+  };
+
+  /**
+   * @method array
+   * @memberof Md5
+   * @instance
+   * @description Output hash as bytes array
+   * @returns {Array} Bytes array
+   * @see {@link md5.array}
+   * @example
+   * hash.array();
+   */
+  Md5.prototype.array = Md5.prototype.digest;
+
+  /**
+   * @method arrayBuffer
+   * @memberof Md5
+   * @instance
+   * @description Output hash as ArrayBuffer
+   * @returns {ArrayBuffer} ArrayBuffer
+   * @see {@link md5.arrayBuffer}
+   * @example
+   * hash.arrayBuffer();
+   */
+  Md5.prototype.arrayBuffer = function () {
+    this.finalize();
+
+    var buffer = new ArrayBuffer(16);
+    var blocks = new Uint32Array(buffer);
+    blocks[0] = this.h0;
+    blocks[1] = this.h1;
+    blocks[2] = this.h2;
+    blocks[3] = this.h3;
+    return buffer;
+  };
+
+  /**
+   * @method buffer
+   * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
+   * @memberof Md5
+   * @instance
+   * @description Output hash as ArrayBuffer
+   * @returns {ArrayBuffer} ArrayBuffer
+   * @see {@link md5.buffer}
+   * @example
+   * hash.buffer();
+   */
+  Md5.prototype.buffer = Md5.prototype.arrayBuffer;
+
+  /**
+   * @method base64
+   * @memberof Md5
+   * @instance
+   * @description Output hash as base64 string
+   * @returns {String} base64 string
+   * @see {@link md5.base64}
+   * @example
+   * hash.base64();
+   */
+  Md5.prototype.base64 = function () {
+    var v1, v2, v3, base64Str = '', bytes = this.array();
+    for (var i = 0; i < 15;) {
+      v1 = bytes[i++];
+      v2 = bytes[i++];
+      v3 = bytes[i++];
+      base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +
+        BASE64_ENCODE_CHAR[(v1 << 4 | v2 >>> 4) & 63] +
+        BASE64_ENCODE_CHAR[(v2 << 2 | v3 >>> 6) & 63] +
+        BASE64_ENCODE_CHAR[v3 & 63];
+    }
+    v1 = bytes[i];
+    base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +
+      BASE64_ENCODE_CHAR[(v1 << 4) & 63] +
+      '==';
+    return base64Str;
+  };
+
+  var exports = createMethod();
+
+  if (COMMON_JS) {
+    module.exports = exports;
+  } else {
+    /**
+     * @method md5
+     * @description Md5 hash function, export to global in browsers.
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {String} md5 hashes
+     * @example
+     * md5(''); // d41d8cd98f00b204e9800998ecf8427e
+     * md5('The quick brown fox jumps over the lazy dog'); // 9e107d9d372bb6826bd81d3542a419d6
+     * md5('The quick brown fox jumps over the lazy dog.'); // e4d909c290d0fb1ca068ffaddf22cbd0
+     *
+     * // It also supports UTF-8 encoding
+     * md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07
+     *
+     * // It also supports byte `Array`, `Uint8Array`, `ArrayBuffer`
+     * md5([]); // d41d8cd98f00b204e9800998ecf8427e
+     * md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e
+     */
+    root.md5 = exports;
+    if (AMD) {
+      define(function () {
+        return exports;
+      });
+    }
+  }
+})();

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 21 - 0
libs/recast.js


+ 0 - 0
libs/socket.2.3.js


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů