diff --git a/dist/mr.js b/dist/mr.js index c89a6233..3ed75494 100644 --- a/dist/mr.js +++ b/dist/mr.js @@ -498,7 +498,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ AnimationSystem: () => (/* binding */ AnimationSystem)\n/* harmony export */ });\n/* harmony import */ var three__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! three */ \"./node_modules/three/build/three.module.js\");\n/* harmony import */ var mrjs_core_MRSystem__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! mrjs/core/MRSystem */ \"./src/core/MRSystem.js\");\n/* harmony import */ var mrjs_core_MREntity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! mrjs/core/MREntity */ \"./src/core/MREntity.js\");\n/* harmony import */ var mrjs_core_entities_MRDivEntity__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! mrjs/core/entities/MRDivEntity */ \"./src/core/entities/MRDivEntity.js\");\n/* harmony import */ var mrjs_core_entities_MRModelEntity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! mrjs/core/entities/MRModelEntity */ \"./src/core/entities/MRModelEntity.js\");\n/* harmony import */ var mrjs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! mrjs */ \"./src/index.js\");\n\n\n\n\n\n\n\n\n\n/**\n * @class AnimationSystem\n * @classdesc Handles specific needs for setting up the masking for all necessary items.\n * @augments MRSystem\n */\nclass AnimationSystem extends mrjs_core_MRSystem__WEBPACK_IMPORTED_MODULE_0__.MRSystem {\n /**\n * @class\n * @description AnimationSystem's default constructor.\n */\n constructor() {\n super();\n }\n\n /**\n * @function\n * @description Updates each animation mixer in the registry. This function should be called\n * within the main animation loop of the application. It iterates through all the\n * animation mixers stored in the registry and updates them with the given deltaTime.\n * The deltaTime parameter is typically the time elapsed since the last frame\n * which is used to ensure smooth animation playback.\n * @param {number} deltaTime - The time elapsed since the last update call, used to update the animation mixers.\n * @param {object} frame - Additional frame information, not used in the current implementation but can be utilized for future enhancements.\n */\n update(deltaTime, frame) {\n for (const entity of this.registry) {\n if (entity.mixer) {\n entity.mixer.update(deltaTime);\n }\n }\n }\n\n /**\n * @function\n * @description (async) Called when the entity component is initialized\n * @param {object} entity - the entity being attached/initialized.\n */\n async attachedComponent(entity) {\n let comp = entity.components.get('animation');\n // the animations list does not load immediately, so we set a promise to wait for the model to load.\n await new Promise((resolve) => {\n const interval = setInterval(() => {\n if (entity.loaded) {\n clearInterval(interval);\n resolve();\n }\n }, 100); // Checks every 100ms\n });\n\n if (entity instanceof mrjs_core_entities_MRModelEntity__WEBPACK_IMPORTED_MODULE_3__.MRModelEntity && entity.animations.length > 0) {\n // Create a mixer for each Model instance with animations\n entity.mixer = new three__WEBPACK_IMPORTED_MODULE_5__.AnimationMixer(entity.object3D);\n this.setAnimation(entity, comp);\n }\n }\n\n /**\n * @function\n * @description Called when the entity component is updated\n * @param {object} entity - the entity being updated based on the component.\n */\n updatedComponent(entity) {\n let comp = entity.components.get('animation');\n if (entity instanceof mrjs_core_entities_MRModelEntity__WEBPACK_IMPORTED_MODULE_3__.MRModelEntity && entity.animations.length > 0) {\n this.setAnimation(entity, comp);\n }\n }\n\n /**\n * @function\n * @description Called when the entity component is detached\n * @param {object} entity - the entity being updated based on the component being detached.\n */\n detachedComponent(entity) {\n entity.mixer.stopAllActions();\n }\n\n /**\n * @function\n * @description When the system swaps to a new entity, this handles setting up the animations for the system runs.\n * @param {object} entity - given entity that might be handled by the system.\n */\n onNewEntity(entity) {\n if (entity instanceof mrjs_core_entities_MRModelEntity__WEBPACK_IMPORTED_MODULE_3__.MRModelEntity && entity.animations.length > 0) {\n let comp = entity.components.get('animation');\n if (!comp) {\n return;\n }\n this.registry.add(entity);\n entity.mixer = new three__WEBPACK_IMPORTED_MODULE_5__.AnimationMixer(entity.object3D);\n this.setAnimation(entity, comp);\n }\n }\n\n /**\n * @function\n * @description Sets the Animation of the entity object based on the component value associated with it. Otherwise lets those\n * be handled by the threejs default setup. (Always looping, always playing based on browser type, etc).\n * @param {object} entity - the entity being updated based on the component being detached.\n * @param {object} comp - component that contains the values of 'action', 'loop', and/or 'loopMode'\n */\n setAnimation(entity, comp) {\n let clip = entity.animations[comp.clip];\n let action = entity.mixer.clipAction(clip);\n\n if (comp.hasOwnProperty('action')) {\n switch (comp.action) {\n case 'play':\n action.play();\n break;\n case 'pause':\n action.pause();\n break;\n case 'stop':\n action.stop();\n break;\n default:\n mrjs__WEBPACK_IMPORTED_MODULE_4__.mrjsUtils.error.err('Unknown case hit for action in the AnimationSystem from entity:', entity, '. Comp is:', comp);\n return;\n }\n }\n\n let hasLoop = comp.hasOwnProperty('loop');\n let hasLoopMode = comp.hasOwnProperty('loopMode');\n if (hasLoop || hasLoopMode) {\n if (hasLoop && !hasLoopMode) {\n switch (comp.loop) {\n case true:\n case 'true':\n action.setLoop(three__WEBPACK_IMPORTED_MODULE_5__.LoopRepeat, Infinity);\n break;\n case false:\n case 'false':\n action.setLoop(three__WEBPACK_IMPORTED_MODULE_5__.LoopOnce, 1);\n break;\n default:\n // loopMode doesnt exist so we hit an unexpected value\n mrjs__WEBPACK_IMPORTED_MODULE_4__.mrjsUtils.error.err(\n 'Bad configuration for loop. It isnt set to true/false (did you mean to pair it with loopMode?) specified in the AnimationSystem from entity:',\n entity,\n ' Comp:',\n comp\n );\n return;\n }\n } else if (hasLoopMode && !hasLoop) {\n // The only time where loop doesnt need to exist but loopMode does is for 'once'. Additionally, note that we also\n // handle this version of loopMode even in the case where loop count exists.\n if (comp.loopMode === 'once') {\n action.setLoop(three__WEBPACK_IMPORTED_MODULE_5__.LoopOnce, 1);\n } else {\n mrjs__WEBPACK_IMPORTED_MODULE_4__.mrjsUtils.error.err(\n 'Bad configuration, loopMode isnt `once`, but loop isnt set (did you mean to pair it with loop?) specified in the AnimationSystem from entity:',\n entity,\n ' Comp:',\n comp\n );\n return;\n }\n } else if (hasLoop && hasLoopMode) {\n // Convert comp.loop to a number, and check if it's a valid normal number or Infinity\n let loopCount = Number(comp.loop);\n if (!((Number.isInteger(loopCount) && loopCount >= 0) || loopCount === Infinity)) {\n mrjs__WEBPACK_IMPORTED_MODULE_4__.mrjsUtils.error.err('loop must be a non-negative integer or Infinity when using loop as count. Entity:', entity, ' Comp:', comp);\n return;\n }\n // Use the appropriate looping based on loopMode\n switch (comp.loopMode) {\n case 'repeat':\n action.setLoop(three__WEBPACK_IMPORTED_MODULE_5__.LoopRepeat, loopCount);\n break;\n case 'pingpong':\n action.setLoop(three__WEBPACK_IMPORTED_MODULE_5__.LoopPingPong, loopCount);\n break;\n case 'once':\n action.setLoop(three__WEBPACK_IMPORTED_MODULE_5__.LoopOnce, 1);\n break;\n default:\n mrjs__WEBPACK_IMPORTED_MODULE_4__.mrjsUtils.error.err('Unknown loopMode specified in the AnimationSystem from entity:', entity, ' Comp:', comp);\n break;\n }\n } else {\n // hasLoop and both hasLoopMode are false, should never reach this case so error as failsafe\n mrjs__WEBPACK_IMPORTED_MODULE_4__.mrjsUtils.error.err('Hit unreachable code - major error in AnimationSystem loop handling');\n }\n }\n }\n}\n\n\n//# sourceURL=webpack://mrjs/./src/core/componentSystems/AnimationSystem.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ AnimationSystem: () => (/* binding */ AnimationSystem)\n/* harmony export */ });\n/* harmony import */ var three__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! three */ \"./node_modules/three/build/three.module.js\");\n/* harmony import */ var mrjs_core_MRSystem__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! mrjs/core/MRSystem */ \"./src/core/MRSystem.js\");\n/* harmony import */ var mrjs_core_MREntity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! mrjs/core/MREntity */ \"./src/core/MREntity.js\");\n/* harmony import */ var mrjs_core_entities_MRDivEntity__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! mrjs/core/entities/MRDivEntity */ \"./src/core/entities/MRDivEntity.js\");\n/* harmony import */ var mrjs_core_entities_MRModelEntity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! mrjs/core/entities/MRModelEntity */ \"./src/core/entities/MRModelEntity.js\");\n/* harmony import */ var mrjs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! mrjs */ \"./src/index.js\");\n\n\n\n\n\n\n\n\n\n/**\n * @class AnimationSystem\n * @classdesc Handles specific needs for setting up the masking for all necessary items.\n * @augments MRSystem\n */\nclass AnimationSystem extends mrjs_core_MRSystem__WEBPACK_IMPORTED_MODULE_0__.MRSystem {\n /**\n * @class\n * @description AnimationSystem's default constructor.\n */\n constructor() {\n super();\n }\n\n /**\n * @function\n * @description Updates each animation mixer in the registry. This function should be called\n * within the main animation loop of the application. It iterates through all the\n * animation mixers stored in the registry and updates them with the given deltaTime.\n * The deltaTime parameter is typically the time elapsed since the last frame\n * which is used to ensure smooth animation playback.\n * @param {number} deltaTime - The time elapsed since the last update call, used to update the animation mixers.\n * @param {object} frame - Additional frame information, not used in the current implementation but can be utilized for future enhancements.\n */\n update(deltaTime, frame) {\n for (const entity of this.registry) {\n if (entity.mixer) {\n entity.mixer.update(deltaTime);\n }\n }\n }\n\n /**\n * @function\n * @description (async) Called when the entity component is initialized\n * @param {object} entity - the entity being attached/initialized.\n */\n async attachedComponent(entity) {\n let comp = entity.components.get('animation');\n // the animations list does not load immediately, so we set a promise to wait for the model to load.\n await new Promise((resolve) => {\n const interval = setInterval(() => {\n if (entity.loaded) {\n clearInterval(interval);\n resolve();\n }\n }, 100); // Checks every 100ms\n });\n\n if (entity instanceof mrjs_core_entities_MRModelEntity__WEBPACK_IMPORTED_MODULE_3__.MRModelEntity && entity.animations.length > 0) {\n // Create a mixer for each Model instance with animations\n entity.mixer = new three__WEBPACK_IMPORTED_MODULE_5__.AnimationMixer(entity.object3D);\n this.setAnimation(entity, comp);\n }\n }\n\n /**\n * @function\n * @description Called when the entity component is updated\n * @param {object} entity - the entity being updated based on the component.\n */\n updatedComponent(entity) {\n let comp = entity.components.get('animation');\n if (entity instanceof mrjs_core_entities_MRModelEntity__WEBPACK_IMPORTED_MODULE_3__.MRModelEntity && entity.animations.length > 0) {\n this.setAnimation(entity, comp);\n }\n }\n\n /**\n * @function\n * @description Called when the entity component is detached\n * @param {object} entity - the entity being updated based on the component being detached.\n */\n detachedComponent(entity) {\n entity.mixer.stopAllActions();\n }\n\n /**\n * @function\n * @description When the system swaps to a new entity, this handles setting up the animations for the system runs.\n * @param {object} entity - given entity that might be handled by the system.\n */\n onNewEntity(entity) {\n if (entity instanceof mrjs_core_entities_MRModelEntity__WEBPACK_IMPORTED_MODULE_3__.MRModelEntity && entity.animations.length > 0) {\n let comp = entity.components.get('animation');\n if (!comp) {\n return;\n }\n this.registry.add(entity);\n entity.mixer = new three__WEBPACK_IMPORTED_MODULE_5__.AnimationMixer(entity.object3D);\n this.setAnimation(entity, comp);\n }\n }\n\n /**\n * @function\n * @description Sets the Animation of the entity object based on the component value associated with it. Otherwise lets those\n * be handled by the threejs default setup. (Always looping, always playing based on browser type, etc).\n * @param {object} entity - the entity being updated based on the component being detached.\n * @param {object} comp - component that contains the values of 'action', 'loop', and/or 'loopMode'\n */\n setAnimation(entity, comp) {\n console.log(entity.animations);\n let clip = entity.animations[comp.clip];\n let action = entity.mixer.clipAction(clip);\n\n // Handle ending position.\n // Threejs defaults to the starting position. `clampWhenFinished` being true, makes it default to the ending position.\n // action.clampWhenFinished = comp.clampWhenFinished ?? false;\n\n if (comp.hasOwnProperty('action')) {\n switch (comp.action) {\n case 'play':\n action.play();\n break;\n case 'pause':\n action.pause();\n break;\n case 'stop':\n action.stop();\n break;\n default:\n mrjs__WEBPACK_IMPORTED_MODULE_4__.mrjsUtils.error.err('Unknown case hit for action in the AnimationSystem from entity:', entity, '. Comp is:', comp);\n return;\n }\n }\n\n let hasLoop = comp.hasOwnProperty('loop');\n let hasLoopMode = comp.hasOwnProperty('loopMode');\n if (hasLoop || hasLoopMode) {\n if (hasLoop && !hasLoopMode) {\n switch (comp.loop) {\n case true:\n case 'true':\n action.setLoop(three__WEBPACK_IMPORTED_MODULE_5__.LoopRepeat, Infinity);\n break;\n case false:\n case 'false':\n action.setLoop(three__WEBPACK_IMPORTED_MODULE_5__.LoopOnce, 1);\n break;\n default:\n // loopMode doesnt exist so we hit an unexpected value\n mrjs__WEBPACK_IMPORTED_MODULE_4__.mrjsUtils.error.err(\n 'Bad configuration for loop. It isnt set to true/false (did you mean to pair it with loopMode?) specified in the AnimationSystem from entity:',\n entity,\n ' Comp:',\n comp\n );\n return;\n }\n } else if (hasLoopMode && !hasLoop) {\n // The only time where loop doesnt need to exist but loopMode does is for 'once'. Additionally, note that we also\n // handle this version of loopMode even in the case where loop count exists.\n if (comp.loopMode === 'once') {\n action.setLoop(three__WEBPACK_IMPORTED_MODULE_5__.LoopOnce, 1);\n } else {\n mrjs__WEBPACK_IMPORTED_MODULE_4__.mrjsUtils.error.err(\n 'Bad configuration, loopMode isnt `once`, but loop isnt set (did you mean to pair it with loop?) specified in the AnimationSystem from entity:',\n entity,\n ' Comp:',\n comp\n );\n return;\n }\n } else if (hasLoop && hasLoopMode) {\n // Convert comp.loop to a number, and check if it's a valid normal number or Infinity\n let loopCount = Number(comp.loop);\n if (!((Number.isInteger(loopCount) && loopCount >= 0) || loopCount === Infinity)) {\n mrjs__WEBPACK_IMPORTED_MODULE_4__.mrjsUtils.error.err('loop must be a non-negative integer or Infinity when using loop as count. Entity:', entity, ' Comp:', comp);\n return;\n }\n // Use the appropriate looping based on loopMode\n switch (comp.loopMode) {\n case 'repeat':\n action.setLoop(three__WEBPACK_IMPORTED_MODULE_5__.LoopRepeat, loopCount);\n break;\n case 'pingpong':\n action.setLoop(three__WEBPACK_IMPORTED_MODULE_5__.LoopPingPong, loopCount);\n break;\n case 'once':\n action.setLoop(three__WEBPACK_IMPORTED_MODULE_5__.LoopOnce, 1);\n break;\n default:\n mrjs__WEBPACK_IMPORTED_MODULE_4__.mrjsUtils.error.err('Unknown loopMode specified in the AnimationSystem from entity:', entity, ' Comp:', comp);\n break;\n }\n } else {\n // hasLoop and both hasLoopMode are false, should never reach this case so error as failsafe\n mrjs__WEBPACK_IMPORTED_MODULE_4__.mrjsUtils.error.err('Hit unreachable code - major error in AnimationSystem loop handling');\n }\n }\n }\n}\n\n\n//# sourceURL=webpack://mrjs/./src/core/componentSystems/AnimationSystem.js?"); /***/ }), diff --git a/samples/examples-assets/models/door1-UI.glb b/samples/examples-assets/models/door1-UI.glb new file mode 100644 index 00000000..e8752ebe Binary files /dev/null and b/samples/examples-assets/models/door1-UI.glb differ diff --git a/samples/examples-assets/models/door1.glb b/samples/examples-assets/models/door1.glb new file mode 100644 index 00000000..63243f14 Binary files /dev/null and b/samples/examples-assets/models/door1.glb differ diff --git a/samples/examples/models.html b/samples/examples/models.html index 6b1366d3..5b9845cd 100644 --- a/samples/examples/models.html +++ b/samples/examples/models.html @@ -267,6 +267,34 @@ + + + + loopMode = once, end at start position + + + + + + + + + + loopMode = once, freeze at final position + + + + + + model diff --git a/src/core/componentSystems/AnimationSystem.js b/src/core/componentSystems/AnimationSystem.js index 37403d55..3cdb4da3 100644 --- a/src/core/componentSystems/AnimationSystem.js +++ b/src/core/componentSystems/AnimationSystem.js @@ -109,89 +109,98 @@ export class AnimationSystem extends MRSystem { * @param {object} comp - component that contains the values of 'action', 'loop', and/or 'loopMode' */ setAnimation(entity, comp) { - let clip = entity.animations[comp.clip]; - let action = entity.mixer.clipAction(clip); + // XXX in future - add conditions to play specific animations based on names + // or other properties. + // For now, just looping through all existing ones to update as needed. + entity.animations.forEach((clip, index) => { + let action = entity.mixer.clipAction(clip); - if (comp.hasOwnProperty('action')) { - switch (comp.action) { - case 'play': - action.play(); - break; - case 'pause': - action.pause(); - break; - case 'stop': - action.stop(); - break; - default: - mrjsUtils.error.err('Unknown case hit for action in the AnimationSystem from entity:', entity, '. Comp is:', comp); - return; - } - } + // Handle ending position. Threejs defaults to the starting position; when + // `clampWhenFinished` is true, it defaults the stop position as the ending + // clip of the animation. + action.clampWhenFinished = comp.clampWhenFinished ?? false; - let hasLoop = comp.hasOwnProperty('loop'); - let hasLoopMode = comp.hasOwnProperty('loopMode'); - if (hasLoop || hasLoopMode) { - if (hasLoop && !hasLoopMode) { - switch (comp.loop) { - case true: - case 'true': - action.setLoop(THREE.LoopRepeat, Infinity); + if (comp.hasOwnProperty('action')) { + switch (comp.action) { + case 'play': + action.play(); break; - case false: - case 'false': - action.setLoop(THREE.LoopOnce, 1); + case 'pause': + action.pause(); + break; + case 'stop': + action.stop(); break; default: - // loopMode doesnt exist so we hit an unexpected value + mrjsUtils.error.err('Unknown case hit for action in the AnimationSystem from entity:', entity, '. Comp is:', comp); + return; + } + } + + let hasLoop = comp.hasOwnProperty('loop'); + let hasLoopMode = comp.hasOwnProperty('loopMode'); + if (hasLoop || hasLoopMode) { + if (hasLoop && !hasLoopMode) { + switch (comp.loop) { + case true: + case 'true': + action.setLoop(THREE.LoopRepeat, Infinity); + break; + case false: + case 'false': + action.setLoop(THREE.LoopOnce, 1); + break; + default: + // loopMode doesnt exist so we hit an unexpected value + mrjsUtils.error.err( + 'Bad configuration for loop. It isnt set to true/false (did you mean to pair it with loopMode?) specified in the AnimationSystem from entity:', + entity, + ' Comp:', + comp + ); + return; + } + } else if (hasLoopMode && !hasLoop) { + // The only time where loop doesnt need to exist but loopMode does is for 'once'. Additionally, note that we also + // handle this version of loopMode even in the case where loop count exists. + if (comp.loopMode === 'once') { + action.setLoop(THREE.LoopOnce, 1); + } else { mrjsUtils.error.err( - 'Bad configuration for loop. It isnt set to true/false (did you mean to pair it with loopMode?) specified in the AnimationSystem from entity:', + 'Bad configuration, loopMode isnt `once`, but loop isnt set (did you mean to pair it with loop?) specified in the AnimationSystem from entity:', entity, ' Comp:', comp ); return; - } - } else if (hasLoopMode && !hasLoop) { - // The only time where loop doesnt need to exist but loopMode does is for 'once'. Additionally, note that we also - // handle this version of loopMode even in the case where loop count exists. - if (comp.loopMode === 'once') { - action.setLoop(THREE.LoopOnce, 1); + } + } else if (hasLoop && hasLoopMode) { + // Convert comp.loop to a number, and check if it's a valid normal number or Infinity + let loopCount = Number(comp.loop); + if (!((Number.isInteger(loopCount) && loopCount >= 0) || loopCount === Infinity)) { + mrjsUtils.error.err('loop must be a non-negative integer or Infinity when using loop as count. Entity:', entity, ' Comp:', comp); + return; + } + // Use the appropriate looping based on loopMode + switch (comp.loopMode) { + case 'repeat': + action.setLoop(THREE.LoopRepeat, loopCount); + break; + case 'pingpong': + action.setLoop(THREE.LoopPingPong, loopCount); + break; + case 'once': + action.setLoop(THREE.LoopOnce, 1); + break; + default: + mrjsUtils.error.err('Unknown loopMode specified in the AnimationSystem from entity:', entity, ' Comp:', comp); + break; + } } else { - mrjsUtils.error.err( - 'Bad configuration, loopMode isnt `once`, but loop isnt set (did you mean to pair it with loop?) specified in the AnimationSystem from entity:', - entity, - ' Comp:', - comp - ); - return; + // hasLoop and both hasLoopMode are false, should never reach this case so error as failsafe + mrjsUtils.error.err('Hit unreachable code - major error in AnimationSystem loop handling'); } - } else if (hasLoop && hasLoopMode) { - // Convert comp.loop to a number, and check if it's a valid normal number or Infinity - let loopCount = Number(comp.loop); - if (!((Number.isInteger(loopCount) && loopCount >= 0) || loopCount === Infinity)) { - mrjsUtils.error.err('loop must be a non-negative integer or Infinity when using loop as count. Entity:', entity, ' Comp:', comp); - return; - } - // Use the appropriate looping based on loopMode - switch (comp.loopMode) { - case 'repeat': - action.setLoop(THREE.LoopRepeat, loopCount); - break; - case 'pingpong': - action.setLoop(THREE.LoopPingPong, loopCount); - break; - case 'once': - action.setLoop(THREE.LoopOnce, 1); - break; - default: - mrjsUtils.error.err('Unknown loopMode specified in the AnimationSystem from entity:', entity, ' Comp:', comp); - break; - } - } else { - // hasLoop and both hasLoopMode are false, should never reach this case so error as failsafe - mrjsUtils.error.err('Hit unreachable code - major error in AnimationSystem loop handling'); } - } + }); } }