diff --git a/.gitignore b/.gitignore
index 249ab8c2d5..7b036d513c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,7 +38,8 @@ playwright-report/
# Claude Code files
.claude/
CLAUDE.md
-
+docs/superpowers
+notes/
.husky/post-checkout
.husky/post-commit
.husky/pre-push
diff --git a/docs/en/graphics/material/examples/shaderlab-05-advance.mdx b/docs/en/graphics/material/examples/shaderlab-05-advance.mdx
index 4a21ce1c25..4e8c257ea1 100644
--- a/docs/en/graphics/material/examples/shaderlab-05-advance.mdx
+++ b/docs/en/graphics/material/examples/shaderlab-05-advance.mdx
@@ -190,7 +190,6 @@ gl_FragColor = vec4(lighting, 1.0);
The engine provides a rich set of built-in variables that can be used directly, such as transformation matrix variables:
```glsl
-mat4 renderer_LocalMat; // Local transformation matrix
mat4 renderer_ModelMat; // Model matrix
mat4 renderer_MVMat; // Model-view matrix
mat4 renderer_MVPMat; // MVP matrix
diff --git a/docs/en/graphics/material/shaderAPI.mdx b/docs/en/graphics/material/shaderAPI.mdx
index f546447e19..63e646c37e 100644
--- a/docs/en/graphics/material/shaderAPI.mdx
+++ b/docs/en/graphics/material/shaderAPI.mdx
@@ -27,7 +27,6 @@ vec4 fog(vec4 color, vec3 positionVS);
Provides system variables for model space, view space, world space, and camera coordinates:
```glsl
-mat4 renderer_LocalMat;
mat4 renderer_ModelMat;
mat4 camera_ViewMat;
mat4 camera_ProjMat;
diff --git a/docs/en/graphics/material/variables.mdx b/docs/en/graphics/material/variables.mdx
index f64f02b3a3..779f86130b 100644
--- a/docs/en/graphics/material/variables.mdx
+++ b/docs/en/graphics/material/variables.mdx
@@ -25,7 +25,6 @@ Below are the engine's built-in variables for reference when writing Shaders:
| Name | Type | Description |
| :---------------- | :--- | :--------------------------- |
-| renderer_LocalMat | mat4 | Model local coordinate matrix |
| renderer_ModelMat | mat4 | Model world coordinate matrix |
| renderer_MVMat | mat4 | Model view matrix |
| renderer_MVPMat | mat4 | Model view projection matrix |
diff --git a/docs/en/script/edit.mdx b/docs/en/script/edit.mdx
index 60feec35e5..95c8561a11 100644
--- a/docs/en/script/edit.mdx
+++ b/docs/en/script/edit.mdx
@@ -24,14 +24,14 @@ For more information about the code editor, please check [Code Editor](/en/docs/
After creating a script asset in the scene editor, double-click the script to open the code editor. Scripts in Galacean need to be written in [Typescript](https://www.typescriptlang.org/), and new scripts are created based on built-in templates by default. Additionally, the Galacean code editor is based on Monaco, with shortcuts similar to VSCode. After modifying the script, press `Ctrl/⌘ + S` to save, and the real-time preview area on the right will show the latest scene effects.
-> Tip: The Galacean code editor currently supports `.ts`, `.gs`, and `.glsl` file editing.
+> Tip: The Galacean code editor currently supports `.ts`, `.shader`, and `.glsl` file editing.
## File Preview
1. **File Search** Quickly search for files in the project
-2. **Code Filter** Whether to display only code files ( `.ts`, `.gs`, `.glsl` ) in the file tree
+2. **Code Filter** Whether to display only code files ( `.ts`, `.shader`, `.glsl` ) in the file tree
3. **Built-in Files** Used to display which files are non-editable internal files
4. **Expand/Hide** Toggle the expansion or hiding of folders
5. **Code Files** Editable code files will display corresponding file type thumbnails
diff --git a/docs/zh/graphics/material/examples/shaderlab-05-advance.mdx b/docs/zh/graphics/material/examples/shaderlab-05-advance.mdx
index c2d4d779dc..a9490581ac 100644
--- a/docs/zh/graphics/material/examples/shaderlab-05-advance.mdx
+++ b/docs/zh/graphics/material/examples/shaderlab-05-advance.mdx
@@ -193,7 +193,6 @@ void frag() {
引擎提供了丰富的内置变量,直接声明使用即可,比如变换矩阵相关变量:
```glsl
-mat4 renderer_LocalMat; // 本地变换矩阵
mat4 renderer_ModelMat; // 模型矩阵
mat4 renderer_MVMat; // 模型视图矩阵
mat4 renderer_MVPMat; // MVP矩阵
diff --git a/docs/zh/graphics/material/shaderAPI.mdx b/docs/zh/graphics/material/shaderAPI.mdx
index d6fcd4ac25..0c0879054b 100644
--- a/docs/zh/graphics/material/shaderAPI.mdx
+++ b/docs/zh/graphics/material/shaderAPI.mdx
@@ -29,7 +29,6 @@ vec4 fog(vec4 color, vec3 positionVS);
提供了模型空间、视图空间、世界空间、相机坐标等[系统变量](/docs/graphics/material/variables/):
```glsl
-mat4 renderer_LocalMat;
mat4 renderer_ModelMat;
mat4 camera_ViewMat;
mat4 camera_ProjMat;
diff --git a/docs/zh/graphics/material/variables.mdx b/docs/zh/graphics/material/variables.mdx
index 6059e0d0e1..48deab814b 100644
--- a/docs/zh/graphics/material/variables.mdx
+++ b/docs/zh/graphics/material/variables.mdx
@@ -25,7 +25,6 @@ Shader 代码中会经常用到内置变量,一种是**逐顶点**的 `attribu
| 名字 | 类型 | 解释 |
| :----------------- | :--- | ------------------ |
-| renderer_LocalMat | mat4 | 模型本地坐标系矩阵 |
| renderer_ModelMat | mat4 | 模型世界坐标系矩阵 |
| renderer_MVMat | mat4 | 模型视口矩阵 |
| renderer_MVPMat | mat4 | 模型视口投影矩阵 |
diff --git a/docs/zh/script/edit.mdx b/docs/zh/script/edit.mdx
index 28cdd108a5..4cb0255eff 100644
--- a/docs/zh/script/edit.mdx
+++ b/docs/zh/script/edit.mdx
@@ -24,14 +24,14 @@ Galacean Editor 提供了一个功能强大的代码编辑器,提供了代码
在场景编辑器中创建脚本资产后,双击该脚本即可打开代码编辑器。Galacean 中的脚本需使用 [Typescript](https://www.typescriptlang.org/) 语言编写,同时新脚本默认基于内置模板创建。另外,Galacean 的代码编辑器基于 Monaco,快捷键与 VSCode 类似。修改脚本后,按 `Ctrl/⌘ + S` 保存,右侧实时预览区展现最新场景效果。
-> 提示:Galacean 代码编辑器目前支持 `.ts` `.gs` 和 `.glsl` 的文件编辑
+> 提示:Galacean 代码编辑器目前支持 `.ts` `.shader` 和 `.glsl` 的文件编辑
## 文件预览
1. **文件搜索** 可快速搜索项目中的文件
-2. **代码筛选** 文件树是否仅显示代码文件 ( `.ts` `.gs` `.glsl` )
+2. **代码筛选** 文件树是否仅显示代码文件 ( `.ts` `.shader` `.glsl` )
3. **内置文件** 用来显示哪些文件是不可编辑的内部文件
4. **展开/隐藏** 可切换文件夹的展开或隐藏
5. **代码文件** 可编辑的代码文件会显示对应的文件类型的缩略图标
diff --git a/e2e/.dev/physx.release.js b/e2e/.dev/physx.release.js
index 426e4ca185..f816e9dce3 100644
--- a/e2e/.dev/physx.release.js
+++ b/e2e/.dev/physx.release.js
@@ -1,2 +1,2 @@
-var PHYSX=(()=>{var _scriptName=globalThis.document?.currentScript?.src;return async function(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};if(typeof __filename!="undefined"){_scriptName=__filename}else if(ENVIRONMENT_IS_WORKER){_scriptName=self.location.href}var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var isFileURI=filename=>filename.startsWith("file://");var readyPromiseResolve,readyPromiseReject;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var HEAP64,HEAPU64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["P"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){return "https://mdn.alipayobjects.com/rms/afts/file/A*RkDQSaUOhxsAAAAAgCAAAAgAehQnAQ/physx.release.wasm"}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;var __abort_js=()=>abort("");var createNamedFunction=(name,func)=>Object.defineProperty(func,"name",{value:name});var emval_freelist=[];var emval_handles=[0,1,,1,null,1,true,1,false,1];var BindingError=class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}};var throwBindingError=message=>{throw new BindingError(message)};var Emval={toValue:handle=>{if(!handle){throwBindingError(`Cannot use deleted val. handle = ${handle}`)}return emval_handles[handle]},toHandle:value=>{switch(value){case undefined:return 2;case null:return 4;case true:return 6;case false:return 8;default:{const handle=emval_freelist.pop()||emval_handles.length;emval_handles[handle]=value;emval_handles[handle+1]=1;return handle}}}};class PureVirtualError extends Error{}var AsciiToString=ptr=>{var str="";while(1){var ch=HEAPU8[ptr++];if(!ch)return str;str+=String.fromCharCode(ch)}};var registeredInstances={};var getBasestPointer=(class_,ptr)=>{if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr};var registerInheritedInstance=(class_,ptr,instance)=>{ptr=getBasestPointer(class_,ptr);if(registeredInstances.hasOwnProperty(ptr)){throwBindingError(`Tried to register registered instance: ${ptr}`)}else{registeredInstances[ptr]=instance}};var registeredTypes={};var getTypeName=type=>{var ptr=___getTypeName(type);var rv=AsciiToString(ptr);_free(ptr);return rv};var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`)}return impl};var unregisterInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);if(registeredInstances.hasOwnProperty(ptr)){delete registeredInstances[ptr]}else{throwBindingError(`Tried to unregister unregistered instance: ${ptr}`)}};var detachFinalizer=handle=>{};var finalizationRegistry=false;var runDestructor=$$=>{if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}};var releaseClassHandle=$$=>{$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}};var attachFinalizer=handle=>{if(!globalThis.FinalizationRegistry){attachFinalizer=handle=>handle;return handle}finalizationRegistry=new FinalizationRegistry(info=>{releaseClassHandle(info.$$)});attachFinalizer=handle=>{var $$=handle.$$;var hasSmartPtr=!!$$.smartPtr;if(hasSmartPtr){var info={$$};finalizationRegistry.register(handle,info,handle)}return handle};detachFinalizer=handle=>finalizationRegistry.unregister(handle);return attachFinalizer(handle)};var __embind_create_inheriting_constructor=(constructorName,wrapperType,properties)=>{constructorName=AsciiToString(constructorName);wrapperType=requireRegisteredType(wrapperType,"wrapper");properties=Emval.toValue(properties);var registeredClass=wrapperType.registeredClass;var wrapperPrototype=registeredClass.instancePrototype;var baseClass=registeredClass.baseClass;var baseClassPrototype=baseClass.instancePrototype;var baseConstructor=registeredClass.baseClass.constructor;var ctor=createNamedFunction(constructorName,function(...args){for(var name of registeredClass.baseClass.pureVirtualFunctions){if(this[name]===baseClassPrototype[name]){throw new PureVirtualError(`Pure virtual function ${name} must be implemented in JavaScript`)}}Object.defineProperty(this,"__parent",{value:wrapperPrototype});this["__construct"](...args)});wrapperPrototype["__construct"]=function __construct(...args){if(this===wrapperPrototype){throwBindingError("Pass correct 'this' to __construct")}var inner=baseConstructor["implement"](this,...args);detachFinalizer(inner);var $$=inner.$$;inner["notifyOnDestruction"]();$$.preservePointerOnDelete=true;Object.defineProperties(this,{$$:{value:$$}});attachFinalizer(this);registerInheritedInstance(registeredClass,$$.ptr,this)};wrapperPrototype["__destruct"]=function __destruct(){if(this===wrapperPrototype){throwBindingError("Pass correct 'this' to __destruct")}detachFinalizer(this);unregisterInheritedInstance(registeredClass,this.$$.ptr)};ctor.prototype=Object.create(wrapperPrototype);Object.assign(ctor.prototype,properties);return Emval.toHandle(ctor)};var structRegistrations={};var runDestructors=destructors=>{while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}};function readPointer(pointer){return this.fromWireType(HEAPU32[pointer>>2])}var awaitingDependencies={};var typeDependencies={};var InternalError=class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}};var throwInternalError=message=>{throw new InternalError(message)};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(type=>typeDependencies[type]=dependentTypes);function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i{typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}}if(0===unregisteredTypes.length){onComplete(typeConverters)}};var __embind_finalize_value_object=structType=>{var reg=structRegistrations[structType];delete structRegistrations[structType];var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;var fieldRecords=reg.fields;var fieldTypes=fieldRecords.map(field=>field.getterReturnType).concat(fieldRecords.map(field=>field.setterArgumentType));whenDependentTypesAreResolved([structType],fieldTypes,fieldTypes=>{var fields={};for(var[i,field]of fieldRecords.entries()){const getterReturnType=fieldTypes[i];const getter=field.getter;const getterContext=field.getterContext;const setterArgumentType=fieldTypes[i+fieldRecords.length];const setter=field.setter;const setterContext=field.setterContext;fields[field.fieldName]={read:ptr=>getterReturnType.fromWireType(getter(getterContext,ptr)),write:(ptr,o)=>{var destructors=[];setter(setterContext,ptr,setterArgumentType.toWireType(destructors,o));runDestructors(destructors)},optional:getterReturnType.optional}}return[{name:reg.name,fromWireType:ptr=>{var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},toWireType:(destructors,o)=>{for(var fieldName in fields){if(!(fieldName in o)&&!fields[fieldName].optional){throw new TypeError(`Missing field: "${fieldName}"`)}}var ptr=rawConstructor();for(fieldName in fields){fields[fieldName].write(ptr,o[fieldName])}if(destructors!==null){destructors.push(rawDestructor,ptr)}return ptr},readValueFromPointer:readPointer,destructorFunction:rawDestructor}]})};function sharedRegisterType(rawType,registeredInstance,options={}){var name=registeredInstance.name;if(!rawType){throwBindingError(`type "${name}" must have a positive integer typeid pointer`)}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError(`Cannot register type '${name}' twice`)}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(cb=>cb())}}function registerType(rawType,registeredInstance,options={}){return sharedRegisterType(rawType,registeredInstance,options)}var integerReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?pointer=>HEAP8[pointer]:pointer=>HEAPU8[pointer];case 2:return signed?pointer=>HEAP16[pointer>>1]:pointer=>HEAPU16[pointer>>1];case 4:return signed?pointer=>HEAP32[pointer>>2]:pointer=>HEAPU32[pointer>>2];case 8:return signed?pointer=>HEAP64[pointer>>3]:pointer=>HEAPU64[pointer>>3];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_bigint=(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0n;let fromWireType=value=>value;if(isUnsignedType){const bitSize=size*8;fromWireType=value=>BigInt.asUintN(bitSize,value);maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>{if(typeof value=="number"){value=BigInt(value)}return value},readValueFromPointer:integerReadValueFromPointer(name,size,!isUnsignedType),destructorFunction:null})};var __embind_register_bool=(rawType,name,trueValue,falseValue)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:function(wt){return!!wt},toWireType:function(destructors,o){return o?trueValue:falseValue},readValueFromPointer:function(pointer){return this.fromWireType(HEAPU8[pointer])},destructorFunction:null})};var shallowCopyInternalPointer=o=>({count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType});var throwInstanceAlreadyDeleted=obj=>{function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")};var deletionQueue=[];var flushPendingDeletes=()=>{while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}};var delayFunction;var init_ClassHandle=()=>{let proto=ClassHandle.prototype;Object.assign(proto,{isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;other.$$=other.$$;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right},clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}},delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}},isDeleted(){return!this.$$.ptr},deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}});const symbolDispose=Symbol.dispose;if(symbolDispose){proto[symbolDispose]=proto["delete"]}};function ClassHandle(){}var registeredPointers={};var ensureOverloadTable=(proto,methodName,humanName)=>{if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(...args){if(!proto[methodName].overloadTable.hasOwnProperty(args.length)){throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`)}return proto[methodName].overloadTable[args.length].apply(this,args)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}};var exposePublicSymbol=(name,value,numArguments)=>{if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError(`Cannot register public name '${name}' twice`)}ensureOverloadTable(Module,name,name);if(Module[name].overloadTable.hasOwnProperty(numArguments)){throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`)}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var char_0=48;var char_9=57;var makeLegalFunctionName=name=>{name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return`_${name}`}return name};function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}var upcastPointer=(ptr,ptrClass,desiredClass)=>{while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError(`Expected null or instance of ${desiredClass.name}, got an instance of ${ptrClass.name}`)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr};var embindRepr=v=>{if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}};function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle||!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,Emval.toHandle(()=>clonedHandle["delete"]()));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupported sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}var downcastPointer=(ptr,ptrClass,desiredClass)=>{if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)};var getInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]};var makeClassHandle=(prototype,record)=>{if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record,writable:true}}))};function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}var init_RegisteredPointer=()=>{Object.assign(RegisteredPointer.prototype,{getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr},destructor(ptr){this.rawDestructor?.(ptr)},readValueFromPointer:readPointer,fromWireType:RegisteredPointer_fromWireType})};function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this.toWireType=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this.toWireType=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this.toWireType=genericPointerToWireType}}var replacePublicSymbol=(name,value,numArguments)=>{if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistent public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var embind__requireFunction=(signature,rawFunction,isAsync=false)=>{signature=AsciiToString(signature);function makeDynCaller(){var rtn=getWasmTableEntry(rawFunction);return rtn}var fp=makeDynCaller();if(typeof fp!="function"){throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`)}return fp};class UnboundTypeError extends Error{}var throwUnboundTypeError=(message,types)=>{var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(`${message}: `+unboundTypes.map(getTypeName).join([", "]))};var __embind_register_class=(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor)=>{name=AsciiToString(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);upcast&&=embind__requireFunction(upcastSignature,upcast);downcast&&=embind__requireFunction(downcastSignature,downcast);rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError(`Cannot construct ${name} due to unbound types`,[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],base=>{base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(name,function(...args){if(Object.getPrototypeOf(this)!==instancePrototype){throw new BindingError(`Use 'new' to construct ${name}`)}if(undefined===registeredClass.constructor_body){throw new BindingError(`${name} has no accessible constructor`)}var body=registeredClass.constructor_body[args.length];if(undefined===body){throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`)}return body.apply(this,args)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);if(registeredClass.baseClass){registeredClass.baseClass.__derivedClasses??=[];registeredClass.baseClass.__derivedClasses.push(registeredClass)}var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})};function usesDestructorStack(argTypes){for(var i=1;i{var array=[];for(var i=0;i>2])}return array};var getFunctionName=signature=>{signature=signature.trim();const argsIndex=signature.indexOf("(");if(argsIndex===-1)return signature;return signature.slice(0,argsIndex)};var __embind_register_class_class_function=(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,fn,isAsync,isNonnullReturn)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=AsciiToString(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker,isAsync);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`${classType.name}.${methodName}`;function unboundTypesHandler(){throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`,rawArgTypes)}if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}var proto=classType.registeredClass.constructor;if(undefined===proto[methodName]){unboundTypesHandler.argCount=argCount-1;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-1]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));var func=craftInvokerFunction(humanName,invokerArgsArray,null,rawInvoker,fn,isAsync);if(undefined===proto[methodName].overloadTable){func.argCount=argCount-1;proto[methodName]=func}else{proto[methodName].overloadTable[argCount-1]=func}if(classType.registeredClass.__derivedClasses){for(const derivedClass of classType.registeredClass.__derivedClasses){if(!derivedClass.constructor.hasOwnProperty(methodName)){derivedClass.constructor[methodName]=func}}}return[]});return[]})};var __embind_register_class_constructor=(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`constructor ${classType.name}`;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError(`Cannot register multiple constructors with identical number of parameters (${argCount-1}) for class '${classType.name}'! Overload resolution is currently only performed using the parameter count, not actual type info!`)}classType.registeredClass.constructor_body[argCount-1]=()=>{throwUnboundTypeError(`Cannot construct ${classType.name} due to unbound types`,rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{argTypes.splice(1,0,null);classType.registeredClass.constructor_body[argCount-1]=craftInvokerFunction(humanName,argTypes,null,invoker,rawConstructor);return[]});return[]})};var __embind_register_class_function=(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual,isAsync,isNonnullReturn)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=AsciiToString(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker,isAsync);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`${classType.name}.${methodName}`;if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}if(isPureVirtual){classType.registeredClass.pureVirtualFunctions.push(methodName)}function unboundTypesHandler(){throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`,rawArgTypes)}var proto=classType.registeredClass.instancePrototype;var method=proto[methodName];if(undefined===method||undefined===method.overloadTable&&method.className!==classType.name&&method.argCount===argCount-2){unboundTypesHandler.argCount=argCount-2;unboundTypesHandler.className=classType.name;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-2]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context,isAsync);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})};var validateThis=(this_,classType,humanName)=>{if(!(this_ instanceof Object)){throwBindingError(`${humanName} with invalid "this": ${this_}`)}if(!(this_ instanceof classType.registeredClass.constructor)){throwBindingError(`${humanName} incompatible with "this" of type ${this_.constructor.name}`)}if(!this_.$$.ptr){throwBindingError(`cannot call emscripten binding method ${humanName} on deleted object`)}return upcastPointer(this_.$$.ptr,this_.$$.ptrType.registeredClass,classType.registeredClass)};var __embind_register_class_property=(classType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext)=>{fieldName=AsciiToString(fieldName);getter=embind__requireFunction(getterSignature,getter);whenDependentTypesAreResolved([],[classType],classType=>{classType=classType[0];var humanName=`${classType.name}.${fieldName}`;var desc={get(){throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`,[getterReturnType,setterArgumentType])},enumerable:true,configurable:true};if(setter){desc.set=()=>throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`,[getterReturnType,setterArgumentType])}else{desc.set=v=>throwBindingError(humanName+" is a read-only property")}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);whenDependentTypesAreResolved([],setter?[getterReturnType,setterArgumentType]:[getterReturnType],types=>{var getterReturnType=types[0];var desc={get(){var ptr=validateThis(this,classType,humanName+" getter");return getterReturnType.fromWireType(getter(getterContext,ptr))},enumerable:true};if(setter){setter=embind__requireFunction(setterSignature,setter);var setterArgumentType=types[1];desc.set=function(v){var ptr=validateThis(this,classType,humanName+" setter");var destructors=[];setter(setterContext,ptr,setterArgumentType.toWireType(destructors,v));runDestructors(destructors)}}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);return[]});return[]})};var __embind_register_constant=(name,type,value)=>{name=AsciiToString(name);whenDependentTypesAreResolved([],[type],type=>{type=type[0];Module[name]=type.fromWireType(value);return[]})};var __emval_decref=handle=>{if(handle>9&&0===--emval_handles[handle+1]){emval_handles[handle]=undefined;emval_freelist.push(handle)}};var EmValType={name:"emscripten::val",fromWireType:handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},toWireType:(destructors,value)=>Emval.toHandle(value),readValueFromPointer:readPointer,destructorFunction:null};var __embind_register_emval=rawType=>registerType(rawType,EmValType);var enumReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?function(pointer){return this.fromWireType(HEAP8[pointer])}:function(pointer){return this.fromWireType(HEAPU8[pointer])};case 2:return signed?function(pointer){return this.fromWireType(HEAP16[pointer>>1])}:function(pointer){return this.fromWireType(HEAPU16[pointer>>1])};case 4:return signed?function(pointer){return this.fromWireType(HEAP32[pointer>>2])}:function(pointer){return this.fromWireType(HEAPU32[pointer>>2])};default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};function getEnumValueType(rawValueType){return rawValueType===0?"object":rawValueType===1?"number":"string"}var __embind_register_enum=(rawType,name,size,isSigned,rawValueType)=>{name=AsciiToString(name);const valueType=getEnumValueType(rawValueType);switch(valueType){case"object":{function ctor(){}ctor.values={};registerType(rawType,{name,constructor:ctor,valueType,fromWireType:function(c){return this.constructor.values[c]},toWireType:(destructors,c)=>c.value,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor);break}case"number":{var keysMap={};registerType(rawType,{name,keysMap,valueType,fromWireType:c=>c,toWireType:(destructors,c)=>c,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,keysMap);delete Module[name].argCount;break}case"string":{var valuesMap={};var reverseMap={};var keysMap={};registerType(rawType,{name,valuesMap,reverseMap,keysMap,valueType,fromWireType:function(c){return this.reverseMap[c]},toWireType:function(destructors,c){return this.valuesMap[c]},readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,keysMap);delete Module[name].argCount;break}}};var __embind_register_enum_value=(rawEnumType,name,enumValue)=>{var enumType=requireRegisteredType(rawEnumType,"enum");name=AsciiToString(name);switch(enumType.valueType){case"object":{var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(`${enumType.name}_${name}`,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value;break}case"number":{enumType.keysMap[name]=enumValue;break}case"string":{enumType.valuesMap[name]=enumValue;enumType.reverseMap[enumValue]=name;enumType.keysMap[name]=name;break}}};var floatReadValueFromPointer=(name,width)=>{switch(width){case 4:return function(pointer){return this.fromWireType(HEAPF32[pointer>>2])};case 8:return function(pointer){return this.fromWireType(HEAPF64[pointer>>3])};default:throw new TypeError(`invalid float width (${width}): ${name}`)}};var __embind_register_float=(rawType,name,size)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:value=>value,toWireType:(destructors,value)=>value,readValueFromPointer:floatReadValueFromPointer(name,size),destructorFunction:null})};var __embind_register_function=(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn,isAsync,isNonnullReturn)=>{var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=AsciiToString(name);name=getFunctionName(name);rawInvoker=embind__requireFunction(signature,rawInvoker,isAsync);exposePublicSymbol(name,function(){throwUnboundTypeError(`Cannot call ${name} due to unbound types`,argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn,isAsync),argCount-1);return[]})};var __embind_register_integer=(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0;let fromWireType=value=>value;if(isUnsignedType){var bitshift=32-8*size;fromWireType=value=>value<>>bitshift;maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>value,readValueFromPointer:integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})};var installIndexedIterator=(proto,sizeMethodName,getMethodName)=>{const makeIterator=(size,getValue)=>{let index=0;return{next(){if(index>=size){return{done:true}}const current=index;index++;const value=getValue(current);return{value,done:false}},[Symbol.iterator](){return this}}};if(!proto[Symbol.iterator]){proto[Symbol.iterator]=function(){const size=this[sizeMethodName]();return makeIterator(size,i=>this[getMethodName](i))}}};var __embind_register_iterable=(rawClassType,rawElementType,sizeMethodName,getMethodName)=>{sizeMethodName=AsciiToString(sizeMethodName);getMethodName=AsciiToString(getMethodName);whenDependentTypesAreResolved([],[rawClassType,rawElementType],types=>{const classType=types[0];installIndexedIterator(classType.registeredClass.instancePrototype,sizeMethodName,getMethodName);return[]})};var __embind_register_memory_view=(rawType,dataTypeIndex,name)=>{var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,BigInt64Array,BigUint64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){var size=HEAPU32[handle>>2];var data=HEAPU32[handle+4>>2];return new TA(HEAP8.buffer,data,size)}name=AsciiToString(name);registerType(rawType,{name,fromWireType:decodeMemoryView,readValueFromPointer:decodeMemoryView},{ignoreDuplicateRegistrations:true})};var EmValOptionalType=Object.assign({optional:true},EmValType);var __embind_register_optional=(rawOptionalType,rawType)=>{registerType(rawOptionalType,EmValOptionalType)};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var __embind_register_std_string=(rawType,name)=>{name=AsciiToString(name);var stdStringIsUTF8=true;registerType(rawType,{name,fromWireType(value){var length=HEAPU32[value>>2];var payload=value+4;var str;if(stdStringIsUTF8){str=UTF8ToString(payload,length,true)}else{str="";for(var i=0;i>2]=length;if(valueIsOfTypeString){if(stdStringIsUTF8){stringToUTF8(value,ptr,length+1)}else{for(var i=0;i255){_free(base);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+i]=charCode}}}else{HEAPU8.set(value,ptr)}if(destructors!==null){destructors.push(_free,base)}return base},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var UTF16Decoder=globalThis.TextDecoder?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead,ignoreNul)=>{var idx=ptr>>1;var endIdx=findStringEnd(HEAPU16,idx,maxBytesToRead/2,ignoreNul);if(endIdx-idx>16&&UTF16Decoder)return UTF16Decoder.decode(HEAPU16.subarray(idx,endIdx));var str="";for(var i=idx;i{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead,ignoreNul)=>{var str="";var startIdx=ptr>>2;for(var i=0;!(i>=maxBytesToRead/4);i++){var utf32=HEAPU32[startIdx+i];if(!utf32&&!ignoreNul)break;str+=String.fromCodePoint(utf32)}return str};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i65535){i++}HEAP32[outPtr>>2]=codePoint;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i65535){i++}len+=4}return len};var __embind_register_std_wstring=(rawType,charSize,name)=>{name=AsciiToString(name);var decodeString,encodeString,lengthBytesUTF;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16}else{decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32}registerType(rawType,{name,fromWireType:value=>{var length=HEAPU32[value>>2];var str=decodeString(value+4,length*charSize,true);_free(value);return str},toWireType:(destructors,value)=>{if(!(typeof value=="string")){throwBindingError(`Cannot pass non-string to C++ string type ${name}`)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length/charSize;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var __embind_register_value_object=(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor)=>{structRegistrations[rawType]={name:AsciiToString(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}};var __embind_register_value_object_field=(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext)=>{structRegistrations[structType].fields.push({fieldName:AsciiToString(fieldName),getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext,setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext})};var __embind_register_void=(rawType,name)=>{name=AsciiToString(name);registerType(rawType,{isVoid:true,name,fromWireType:()=>undefined,toWireType:(destructors,o)=>undefined})};var runtimeKeepaliveCounter=0;var __emscripten_runtime_keepalive_clear=()=>{noExitRuntime=false;runtimeKeepaliveCounter=0};var emval_methodCallers=[];var emval_addMethodCaller=caller=>{var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id};var emval_lookupTypes=(argCount,argTypes)=>{var a=new Array(argCount);for(var i=0;i>2],`parameter ${i}`)}return a};var emval_returnValue=(toReturnWire,destructorsRef,handle)=>{var destructors=[];var result=toReturnWire(destructors,handle);if(destructors.length){HEAPU32[destructorsRef>>2]=Emval.toHandle(destructors)}return result};var emval_symbols={};var getStringOrSymbol=address=>{var symbol=emval_symbols[address];if(symbol===undefined){return AsciiToString(address)}return symbol};var __emval_create_invoker=(argCount,argTypesPtr,kind)=>{var GenericWireTypeSize=8;var[retType,...argTypes]=emval_lookupTypes(argCount,argTypesPtr);var toReturnWire=retType.toWireType.bind(retType);var argFromPtr=argTypes.map(type=>type.readValueFromPointer.bind(type));argCount--;var argN=new Array(argCount);var invokerFunction=(handle,methodName,destructorsRef,args)=>{var offset=0;for(var i=0;it.name)}) => ${retType.name}>`;return emval_addMethodCaller(createNamedFunction(functionName,invokerFunction))};var __emval_invoke=(caller,handle,methodName,destructorsRef,args)=>emval_methodCallers[caller](handle,methodName,destructorsRef,args);var __emval_new_cstring=v=>Emval.toHandle(getStringOrSymbol(v));var __emval_new_object=()=>Emval.toHandle({});var __emval_run_destructors=handle=>{var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)};var __emval_set_property=(handle,key,value)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);value=Emval.toValue(value);handle[key]=value};var timers={};var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var maybeExit=()=>{if(!keepRuntimeAlive()){try{_exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{return func()}catch(e){handleException(e)}finally{maybeExit()}};var _emscripten_get_now=()=>performance.now();var __setitimer_js=(which,timeout_ms)=>{if(timers[which]){clearTimeout(timers[which].id);delete timers[which]}if(!timeout_ms)return 0;var id=setTimeout(()=>{delete timers[which];callUserCallback(()=>__emscripten_timeout(which,_emscripten_get_now()))},timeout_ms);timers[which]={id,timeout_ms};return 0};var _emscripten_date_now=()=>Date.now();var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var printCharBuffers=[null,[],[]];var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j>2]=num;return 0};init_ClassHandle();init_RegisteredPointer();{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}var ___getTypeName,_free,_malloc,__emscripten_timeout,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){___getTypeName=wasmExports["Q"];_free=Module["_free"]=wasmExports["R"];_malloc=Module["_malloc"]=wasmExports["S"];__emscripten_timeout=wasmExports["U"];memory=wasmMemory=wasmExports["O"];__indirect_function_table=wasmTable=wasmExports["T"]}var wasmImports={J:__abort_js,x:__embind_create_inheriting_constructor,s:__embind_finalize_value_object,z:__embind_register_bigint,A:__embind_register_bool,b:__embind_register_class,p:__embind_register_class_class_function,e:__embind_register_class_constructor,a:__embind_register_class_function,d:__embind_register_class_property,K:__embind_register_constant,M:__embind_register_emval,l:__embind_register_enum,f:__embind_register_enum_value,y:__embind_register_float,g:__embind_register_function,o:__embind_register_integer,q:__embind_register_iterable,h:__embind_register_memory_view,r:__embind_register_optional,N:__embind_register_std_string,u:__embind_register_std_wstring,t:__embind_register_value_object,m:__embind_register_value_object_field,B:__embind_register_void,D:__emscripten_runtime_keepalive_clear,k:__emval_create_invoker,n:__emval_decref,j:__emval_invoke,v:__emval_new_cstring,L:__emval_new_object,i:__emval_run_destructors,w:__emval_set_property,E:__setitimer_js,I:_emscripten_date_now,c:_emscripten_get_now,F:_emscripten_resize_heap,G:_exit,H:_fd_write,C:_proc_exit};function run(){preRun();function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})}
+var PHYSX=(()=>{var _scriptName=globalThis.document?.currentScript?.src;return async function(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};if(typeof __filename!="undefined"){_scriptName=__filename}else if(ENVIRONMENT_IS_WORKER){_scriptName=self.location.href}var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var isFileURI=filename=>filename.startsWith("file://");var readyPromiseResolve,readyPromiseReject;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var HEAP64,HEAPU64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["P"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){return locateFile("physx.release.wasm")}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;var __abort_js=()=>abort("");var createNamedFunction=(name,func)=>Object.defineProperty(func,"name",{value:name});var emval_freelist=[];var emval_handles=[0,1,,1,null,1,true,1,false,1];var BindingError=class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}};var throwBindingError=message=>{throw new BindingError(message)};var Emval={toValue:handle=>{if(!handle){throwBindingError(`Cannot use deleted val. handle = ${handle}`)}return emval_handles[handle]},toHandle:value=>{switch(value){case undefined:return 2;case null:return 4;case true:return 6;case false:return 8;default:{const handle=emval_freelist.pop()||emval_handles.length;emval_handles[handle]=value;emval_handles[handle+1]=1;return handle}}}};class PureVirtualError extends Error{}var AsciiToString=ptr=>{var str="";while(1){var ch=HEAPU8[ptr++];if(!ch)return str;str+=String.fromCharCode(ch)}};var registeredInstances={};var getBasestPointer=(class_,ptr)=>{if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr};var registerInheritedInstance=(class_,ptr,instance)=>{ptr=getBasestPointer(class_,ptr);if(registeredInstances.hasOwnProperty(ptr)){throwBindingError(`Tried to register registered instance: ${ptr}`)}else{registeredInstances[ptr]=instance}};var registeredTypes={};var getTypeName=type=>{var ptr=___getTypeName(type);var rv=AsciiToString(ptr);_free(ptr);return rv};var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`)}return impl};var unregisterInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);if(registeredInstances.hasOwnProperty(ptr)){delete registeredInstances[ptr]}else{throwBindingError(`Tried to unregister unregistered instance: ${ptr}`)}};var detachFinalizer=handle=>{};var finalizationRegistry=false;var runDestructor=$$=>{if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}};var releaseClassHandle=$$=>{$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}};var attachFinalizer=handle=>{if(!globalThis.FinalizationRegistry){attachFinalizer=handle=>handle;return handle}finalizationRegistry=new FinalizationRegistry(info=>{releaseClassHandle(info.$$)});attachFinalizer=handle=>{var $$=handle.$$;var hasSmartPtr=!!$$.smartPtr;if(hasSmartPtr){var info={$$};finalizationRegistry.register(handle,info,handle)}return handle};detachFinalizer=handle=>finalizationRegistry.unregister(handle);return attachFinalizer(handle)};var __embind_create_inheriting_constructor=(constructorName,wrapperType,properties)=>{constructorName=AsciiToString(constructorName);wrapperType=requireRegisteredType(wrapperType,"wrapper");properties=Emval.toValue(properties);var registeredClass=wrapperType.registeredClass;var wrapperPrototype=registeredClass.instancePrototype;var baseClass=registeredClass.baseClass;var baseClassPrototype=baseClass.instancePrototype;var baseConstructor=registeredClass.baseClass.constructor;var ctor=createNamedFunction(constructorName,function(...args){for(var name of registeredClass.baseClass.pureVirtualFunctions){if(this[name]===baseClassPrototype[name]){throw new PureVirtualError(`Pure virtual function ${name} must be implemented in JavaScript`)}}Object.defineProperty(this,"__parent",{value:wrapperPrototype});this["__construct"](...args)});wrapperPrototype["__construct"]=function __construct(...args){if(this===wrapperPrototype){throwBindingError("Pass correct 'this' to __construct")}var inner=baseConstructor["implement"](this,...args);detachFinalizer(inner);var $$=inner.$$;inner["notifyOnDestruction"]();$$.preservePointerOnDelete=true;Object.defineProperties(this,{$$:{value:$$}});attachFinalizer(this);registerInheritedInstance(registeredClass,$$.ptr,this)};wrapperPrototype["__destruct"]=function __destruct(){if(this===wrapperPrototype){throwBindingError("Pass correct 'this' to __destruct")}detachFinalizer(this);unregisterInheritedInstance(registeredClass,this.$$.ptr)};ctor.prototype=Object.create(wrapperPrototype);Object.assign(ctor.prototype,properties);return Emval.toHandle(ctor)};var structRegistrations={};var runDestructors=destructors=>{while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}};function readPointer(pointer){return this.fromWireType(HEAPU32[pointer>>2])}var awaitingDependencies={};var typeDependencies={};var InternalError=class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}};var throwInternalError=message=>{throw new InternalError(message)};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(type=>typeDependencies[type]=dependentTypes);function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i{typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}}if(0===unregisteredTypes.length){onComplete(typeConverters)}};var __embind_finalize_value_object=structType=>{var reg=structRegistrations[structType];delete structRegistrations[structType];var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;var fieldRecords=reg.fields;var fieldTypes=fieldRecords.map(field=>field.getterReturnType).concat(fieldRecords.map(field=>field.setterArgumentType));whenDependentTypesAreResolved([structType],fieldTypes,fieldTypes=>{var fields={};for(var[i,field]of fieldRecords.entries()){const getterReturnType=fieldTypes[i];const getter=field.getter;const getterContext=field.getterContext;const setterArgumentType=fieldTypes[i+fieldRecords.length];const setter=field.setter;const setterContext=field.setterContext;fields[field.fieldName]={read:ptr=>getterReturnType.fromWireType(getter(getterContext,ptr)),write:(ptr,o)=>{var destructors=[];setter(setterContext,ptr,setterArgumentType.toWireType(destructors,o));runDestructors(destructors)},optional:getterReturnType.optional}}return[{name:reg.name,fromWireType:ptr=>{var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},toWireType:(destructors,o)=>{for(var fieldName in fields){if(!(fieldName in o)&&!fields[fieldName].optional){throw new TypeError(`Missing field: "${fieldName}"`)}}var ptr=rawConstructor();for(fieldName in fields){fields[fieldName].write(ptr,o[fieldName])}if(destructors!==null){destructors.push(rawDestructor,ptr)}return ptr},readValueFromPointer:readPointer,destructorFunction:rawDestructor}]})};function sharedRegisterType(rawType,registeredInstance,options={}){var name=registeredInstance.name;if(!rawType){throwBindingError(`type "${name}" must have a positive integer typeid pointer`)}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError(`Cannot register type '${name}' twice`)}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(cb=>cb())}}function registerType(rawType,registeredInstance,options={}){return sharedRegisterType(rawType,registeredInstance,options)}var integerReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?pointer=>HEAP8[pointer]:pointer=>HEAPU8[pointer];case 2:return signed?pointer=>HEAP16[pointer>>1]:pointer=>HEAPU16[pointer>>1];case 4:return signed?pointer=>HEAP32[pointer>>2]:pointer=>HEAPU32[pointer>>2];case 8:return signed?pointer=>HEAP64[pointer>>3]:pointer=>HEAPU64[pointer>>3];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_bigint=(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0n;let fromWireType=value=>value;if(isUnsignedType){const bitSize=size*8;fromWireType=value=>BigInt.asUintN(bitSize,value);maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>{if(typeof value=="number"){value=BigInt(value)}return value},readValueFromPointer:integerReadValueFromPointer(name,size,!isUnsignedType),destructorFunction:null})};var __embind_register_bool=(rawType,name,trueValue,falseValue)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:function(wt){return!!wt},toWireType:function(destructors,o){return o?trueValue:falseValue},readValueFromPointer:function(pointer){return this.fromWireType(HEAPU8[pointer])},destructorFunction:null})};var shallowCopyInternalPointer=o=>({count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType});var throwInstanceAlreadyDeleted=obj=>{function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")};var deletionQueue=[];var flushPendingDeletes=()=>{while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}};var delayFunction;var init_ClassHandle=()=>{let proto=ClassHandle.prototype;Object.assign(proto,{isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;other.$$=other.$$;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right},clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}},delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}},isDeleted(){return!this.$$.ptr},deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}});const symbolDispose=Symbol.dispose;if(symbolDispose){proto[symbolDispose]=proto["delete"]}};function ClassHandle(){}var registeredPointers={};var ensureOverloadTable=(proto,methodName,humanName)=>{if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(...args){if(!proto[methodName].overloadTable.hasOwnProperty(args.length)){throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`)}return proto[methodName].overloadTable[args.length].apply(this,args)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}};var exposePublicSymbol=(name,value,numArguments)=>{if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError(`Cannot register public name '${name}' twice`)}ensureOverloadTable(Module,name,name);if(Module[name].overloadTable.hasOwnProperty(numArguments)){throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`)}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var char_0=48;var char_9=57;var makeLegalFunctionName=name=>{name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return`_${name}`}return name};function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}var upcastPointer=(ptr,ptrClass,desiredClass)=>{while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError(`Expected null or instance of ${desiredClass.name}, got an instance of ${ptrClass.name}`)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr};var embindRepr=v=>{if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}};function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle||!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,Emval.toHandle(()=>clonedHandle["delete"]()));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupported sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}var downcastPointer=(ptr,ptrClass,desiredClass)=>{if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)};var getInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]};var makeClassHandle=(prototype,record)=>{if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record,writable:true}}))};function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}var init_RegisteredPointer=()=>{Object.assign(RegisteredPointer.prototype,{getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr},destructor(ptr){this.rawDestructor?.(ptr)},readValueFromPointer:readPointer,fromWireType:RegisteredPointer_fromWireType})};function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this.toWireType=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this.toWireType=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this.toWireType=genericPointerToWireType}}var replacePublicSymbol=(name,value,numArguments)=>{if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistent public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var embind__requireFunction=(signature,rawFunction,isAsync=false)=>{signature=AsciiToString(signature);function makeDynCaller(){var rtn=getWasmTableEntry(rawFunction);return rtn}var fp=makeDynCaller();if(typeof fp!="function"){throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`)}return fp};class UnboundTypeError extends Error{}var throwUnboundTypeError=(message,types)=>{var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(`${message}: `+unboundTypes.map(getTypeName).join([", "]))};var __embind_register_class=(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor)=>{name=AsciiToString(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);upcast&&=embind__requireFunction(upcastSignature,upcast);downcast&&=embind__requireFunction(downcastSignature,downcast);rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError(`Cannot construct ${name} due to unbound types`,[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],base=>{base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(name,function(...args){if(Object.getPrototypeOf(this)!==instancePrototype){throw new BindingError(`Use 'new' to construct ${name}`)}if(undefined===registeredClass.constructor_body){throw new BindingError(`${name} has no accessible constructor`)}var body=registeredClass.constructor_body[args.length];if(undefined===body){throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`)}return body.apply(this,args)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);if(registeredClass.baseClass){registeredClass.baseClass.__derivedClasses??=[];registeredClass.baseClass.__derivedClasses.push(registeredClass)}var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})};function usesDestructorStack(argTypes){for(var i=1;i{var array=[];for(var i=0;i>2])}return array};var getFunctionName=signature=>{signature=signature.trim();const argsIndex=signature.indexOf("(");if(argsIndex===-1)return signature;return signature.slice(0,argsIndex)};var __embind_register_class_class_function=(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,fn,isAsync,isNonnullReturn)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=AsciiToString(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker,isAsync);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`${classType.name}.${methodName}`;function unboundTypesHandler(){throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`,rawArgTypes)}if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}var proto=classType.registeredClass.constructor;if(undefined===proto[methodName]){unboundTypesHandler.argCount=argCount-1;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-1]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));var func=craftInvokerFunction(humanName,invokerArgsArray,null,rawInvoker,fn,isAsync);if(undefined===proto[methodName].overloadTable){func.argCount=argCount-1;proto[methodName]=func}else{proto[methodName].overloadTable[argCount-1]=func}if(classType.registeredClass.__derivedClasses){for(const derivedClass of classType.registeredClass.__derivedClasses){if(!derivedClass.constructor.hasOwnProperty(methodName)){derivedClass.constructor[methodName]=func}}}return[]});return[]})};var __embind_register_class_constructor=(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`constructor ${classType.name}`;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError(`Cannot register multiple constructors with identical number of parameters (${argCount-1}) for class '${classType.name}'! Overload resolution is currently only performed using the parameter count, not actual type info!`)}classType.registeredClass.constructor_body[argCount-1]=()=>{throwUnboundTypeError(`Cannot construct ${classType.name} due to unbound types`,rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{argTypes.splice(1,0,null);classType.registeredClass.constructor_body[argCount-1]=craftInvokerFunction(humanName,argTypes,null,invoker,rawConstructor);return[]});return[]})};var __embind_register_class_function=(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual,isAsync,isNonnullReturn)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=AsciiToString(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker,isAsync);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`${classType.name}.${methodName}`;if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}if(isPureVirtual){classType.registeredClass.pureVirtualFunctions.push(methodName)}function unboundTypesHandler(){throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`,rawArgTypes)}var proto=classType.registeredClass.instancePrototype;var method=proto[methodName];if(undefined===method||undefined===method.overloadTable&&method.className!==classType.name&&method.argCount===argCount-2){unboundTypesHandler.argCount=argCount-2;unboundTypesHandler.className=classType.name;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-2]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context,isAsync);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})};var validateThis=(this_,classType,humanName)=>{if(!(this_ instanceof Object)){throwBindingError(`${humanName} with invalid "this": ${this_}`)}if(!(this_ instanceof classType.registeredClass.constructor)){throwBindingError(`${humanName} incompatible with "this" of type ${this_.constructor.name}`)}if(!this_.$$.ptr){throwBindingError(`cannot call emscripten binding method ${humanName} on deleted object`)}return upcastPointer(this_.$$.ptr,this_.$$.ptrType.registeredClass,classType.registeredClass)};var __embind_register_class_property=(classType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext)=>{fieldName=AsciiToString(fieldName);getter=embind__requireFunction(getterSignature,getter);whenDependentTypesAreResolved([],[classType],classType=>{classType=classType[0];var humanName=`${classType.name}.${fieldName}`;var desc={get(){throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`,[getterReturnType,setterArgumentType])},enumerable:true,configurable:true};if(setter){desc.set=()=>throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`,[getterReturnType,setterArgumentType])}else{desc.set=v=>throwBindingError(humanName+" is a read-only property")}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);whenDependentTypesAreResolved([],setter?[getterReturnType,setterArgumentType]:[getterReturnType],types=>{var getterReturnType=types[0];var desc={get(){var ptr=validateThis(this,classType,humanName+" getter");return getterReturnType.fromWireType(getter(getterContext,ptr))},enumerable:true};if(setter){setter=embind__requireFunction(setterSignature,setter);var setterArgumentType=types[1];desc.set=function(v){var ptr=validateThis(this,classType,humanName+" setter");var destructors=[];setter(setterContext,ptr,setterArgumentType.toWireType(destructors,v));runDestructors(destructors)}}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);return[]});return[]})};var __embind_register_constant=(name,type,value)=>{name=AsciiToString(name);whenDependentTypesAreResolved([],[type],type=>{type=type[0];Module[name]=type.fromWireType(value);return[]})};var __emval_decref=handle=>{if(handle>9&&0===--emval_handles[handle+1]){emval_handles[handle]=undefined;emval_freelist.push(handle)}};var EmValType={name:"emscripten::val",fromWireType:handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},toWireType:(destructors,value)=>Emval.toHandle(value),readValueFromPointer:readPointer,destructorFunction:null};var __embind_register_emval=rawType=>registerType(rawType,EmValType);var enumReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?function(pointer){return this.fromWireType(HEAP8[pointer])}:function(pointer){return this.fromWireType(HEAPU8[pointer])};case 2:return signed?function(pointer){return this.fromWireType(HEAP16[pointer>>1])}:function(pointer){return this.fromWireType(HEAPU16[pointer>>1])};case 4:return signed?function(pointer){return this.fromWireType(HEAP32[pointer>>2])}:function(pointer){return this.fromWireType(HEAPU32[pointer>>2])};default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};function getEnumValueType(rawValueType){return rawValueType===0?"object":rawValueType===1?"number":"string"}var __embind_register_enum=(rawType,name,size,isSigned,rawValueType)=>{name=AsciiToString(name);const valueType=getEnumValueType(rawValueType);switch(valueType){case"object":{function ctor(){}ctor.values={};registerType(rawType,{name,constructor:ctor,valueType,fromWireType:function(c){return this.constructor.values[c]},toWireType:(destructors,c)=>c.value,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor);break}case"number":{var keysMap={};registerType(rawType,{name,keysMap,valueType,fromWireType:c=>c,toWireType:(destructors,c)=>c,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,keysMap);delete Module[name].argCount;break}case"string":{var valuesMap={};var reverseMap={};var keysMap={};registerType(rawType,{name,valuesMap,reverseMap,keysMap,valueType,fromWireType:function(c){return this.reverseMap[c]},toWireType:function(destructors,c){return this.valuesMap[c]},readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,keysMap);delete Module[name].argCount;break}}};var __embind_register_enum_value=(rawEnumType,name,enumValue)=>{var enumType=requireRegisteredType(rawEnumType,"enum");name=AsciiToString(name);switch(enumType.valueType){case"object":{var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(`${enumType.name}_${name}`,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value;break}case"number":{enumType.keysMap[name]=enumValue;break}case"string":{enumType.valuesMap[name]=enumValue;enumType.reverseMap[enumValue]=name;enumType.keysMap[name]=name;break}}};var floatReadValueFromPointer=(name,width)=>{switch(width){case 4:return function(pointer){return this.fromWireType(HEAPF32[pointer>>2])};case 8:return function(pointer){return this.fromWireType(HEAPF64[pointer>>3])};default:throw new TypeError(`invalid float width (${width}): ${name}`)}};var __embind_register_float=(rawType,name,size)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:value=>value,toWireType:(destructors,value)=>value,readValueFromPointer:floatReadValueFromPointer(name,size),destructorFunction:null})};var __embind_register_function=(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn,isAsync,isNonnullReturn)=>{var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=AsciiToString(name);name=getFunctionName(name);rawInvoker=embind__requireFunction(signature,rawInvoker,isAsync);exposePublicSymbol(name,function(){throwUnboundTypeError(`Cannot call ${name} due to unbound types`,argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn,isAsync),argCount-1);return[]})};var __embind_register_integer=(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0;let fromWireType=value=>value;if(isUnsignedType){var bitshift=32-8*size;fromWireType=value=>value<>>bitshift;maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>value,readValueFromPointer:integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})};var installIndexedIterator=(proto,sizeMethodName,getMethodName)=>{const makeIterator=(size,getValue)=>{let index=0;return{next(){if(index>=size){return{done:true}}const current=index;index++;const value=getValue(current);return{value,done:false}},[Symbol.iterator](){return this}}};if(!proto[Symbol.iterator]){proto[Symbol.iterator]=function(){const size=this[sizeMethodName]();return makeIterator(size,i=>this[getMethodName](i))}}};var __embind_register_iterable=(rawClassType,rawElementType,sizeMethodName,getMethodName)=>{sizeMethodName=AsciiToString(sizeMethodName);getMethodName=AsciiToString(getMethodName);whenDependentTypesAreResolved([],[rawClassType,rawElementType],types=>{const classType=types[0];installIndexedIterator(classType.registeredClass.instancePrototype,sizeMethodName,getMethodName);return[]})};var __embind_register_memory_view=(rawType,dataTypeIndex,name)=>{var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,BigInt64Array,BigUint64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){var size=HEAPU32[handle>>2];var data=HEAPU32[handle+4>>2];return new TA(HEAP8.buffer,data,size)}name=AsciiToString(name);registerType(rawType,{name,fromWireType:decodeMemoryView,readValueFromPointer:decodeMemoryView},{ignoreDuplicateRegistrations:true})};var EmValOptionalType=Object.assign({optional:true},EmValType);var __embind_register_optional=(rawOptionalType,rawType)=>{registerType(rawOptionalType,EmValOptionalType)};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var __embind_register_std_string=(rawType,name)=>{name=AsciiToString(name);var stdStringIsUTF8=true;registerType(rawType,{name,fromWireType(value){var length=HEAPU32[value>>2];var payload=value+4;var str;if(stdStringIsUTF8){str=UTF8ToString(payload,length,true)}else{str="";for(var i=0;i>2]=length;if(valueIsOfTypeString){if(stdStringIsUTF8){stringToUTF8(value,ptr,length+1)}else{for(var i=0;i255){_free(base);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+i]=charCode}}}else{HEAPU8.set(value,ptr)}if(destructors!==null){destructors.push(_free,base)}return base},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var UTF16Decoder=globalThis.TextDecoder?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead,ignoreNul)=>{var idx=ptr>>1;var endIdx=findStringEnd(HEAPU16,idx,maxBytesToRead/2,ignoreNul);if(endIdx-idx>16&&UTF16Decoder)return UTF16Decoder.decode(HEAPU16.subarray(idx,endIdx));var str="";for(var i=idx;i{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead,ignoreNul)=>{var str="";var startIdx=ptr>>2;for(var i=0;!(i>=maxBytesToRead/4);i++){var utf32=HEAPU32[startIdx+i];if(!utf32&&!ignoreNul)break;str+=String.fromCodePoint(utf32)}return str};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i65535){i++}HEAP32[outPtr>>2]=codePoint;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i65535){i++}len+=4}return len};var __embind_register_std_wstring=(rawType,charSize,name)=>{name=AsciiToString(name);var decodeString,encodeString,lengthBytesUTF;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16}else{decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32}registerType(rawType,{name,fromWireType:value=>{var length=HEAPU32[value>>2];var str=decodeString(value+4,length*charSize,true);_free(value);return str},toWireType:(destructors,value)=>{if(!(typeof value=="string")){throwBindingError(`Cannot pass non-string to C++ string type ${name}`)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length/charSize;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var __embind_register_value_object=(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor)=>{structRegistrations[rawType]={name:AsciiToString(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}};var __embind_register_value_object_field=(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext)=>{structRegistrations[structType].fields.push({fieldName:AsciiToString(fieldName),getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext,setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext})};var __embind_register_void=(rawType,name)=>{name=AsciiToString(name);registerType(rawType,{isVoid:true,name,fromWireType:()=>undefined,toWireType:(destructors,o)=>undefined})};var runtimeKeepaliveCounter=0;var __emscripten_runtime_keepalive_clear=()=>{noExitRuntime=false;runtimeKeepaliveCounter=0};var emval_methodCallers=[];var emval_addMethodCaller=caller=>{var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id};var emval_lookupTypes=(argCount,argTypes)=>{var a=new Array(argCount);for(var i=0;i>2],`parameter ${i}`)}return a};var emval_returnValue=(toReturnWire,destructorsRef,handle)=>{var destructors=[];var result=toReturnWire(destructors,handle);if(destructors.length){HEAPU32[destructorsRef>>2]=Emval.toHandle(destructors)}return result};var emval_symbols={};var getStringOrSymbol=address=>{var symbol=emval_symbols[address];if(symbol===undefined){return AsciiToString(address)}return symbol};var __emval_create_invoker=(argCount,argTypesPtr,kind)=>{var GenericWireTypeSize=8;var[retType,...argTypes]=emval_lookupTypes(argCount,argTypesPtr);var toReturnWire=retType.toWireType.bind(retType);var argFromPtr=argTypes.map(type=>type.readValueFromPointer.bind(type));argCount--;var argN=new Array(argCount);var invokerFunction=(handle,methodName,destructorsRef,args)=>{var offset=0;for(var i=0;it.name)}) => ${retType.name}>`;return emval_addMethodCaller(createNamedFunction(functionName,invokerFunction))};var __emval_invoke=(caller,handle,methodName,destructorsRef,args)=>emval_methodCallers[caller](handle,methodName,destructorsRef,args);var __emval_new_cstring=v=>Emval.toHandle(getStringOrSymbol(v));var __emval_new_object=()=>Emval.toHandle({});var __emval_run_destructors=handle=>{var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)};var __emval_set_property=(handle,key,value)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);value=Emval.toValue(value);handle[key]=value};var timers={};var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var maybeExit=()=>{if(!keepRuntimeAlive()){try{_exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{return func()}catch(e){handleException(e)}finally{maybeExit()}};var _emscripten_get_now=()=>performance.now();var __setitimer_js=(which,timeout_ms)=>{if(timers[which]){clearTimeout(timers[which].id);delete timers[which]}if(!timeout_ms)return 0;var id=setTimeout(()=>{delete timers[which];callUserCallback(()=>__emscripten_timeout(which,_emscripten_get_now()))},timeout_ms);timers[which]={id,timeout_ms};return 0};var _emscripten_date_now=()=>Date.now();var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var printCharBuffers=[null,[],[]];var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j>2]=num;return 0};init_ClassHandle();init_RegisteredPointer();{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}var ___getTypeName,_free,_malloc,__emscripten_timeout,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){___getTypeName=wasmExports["Q"];_free=Module["_free"]=wasmExports["R"];_malloc=Module["_malloc"]=wasmExports["S"];__emscripten_timeout=wasmExports["U"];memory=wasmMemory=wasmExports["O"];__indirect_function_table=wasmTable=wasmExports["T"]}var wasmImports={J:__abort_js,x:__embind_create_inheriting_constructor,s:__embind_finalize_value_object,z:__embind_register_bigint,A:__embind_register_bool,b:__embind_register_class,p:__embind_register_class_class_function,e:__embind_register_class_constructor,a:__embind_register_class_function,d:__embind_register_class_property,K:__embind_register_constant,M:__embind_register_emval,l:__embind_register_enum,f:__embind_register_enum_value,y:__embind_register_float,g:__embind_register_function,o:__embind_register_integer,q:__embind_register_iterable,h:__embind_register_memory_view,r:__embind_register_optional,N:__embind_register_std_string,u:__embind_register_std_wstring,t:__embind_register_value_object,m:__embind_register_value_object_field,B:__embind_register_void,D:__emscripten_runtime_keepalive_clear,k:__emval_create_invoker,n:__emval_decref,j:__emval_invoke,v:__emval_new_cstring,L:__emval_new_object,i:__emval_run_destructors,w:__emval_set_property,E:__setitimer_js,I:_emscripten_date_now,c:_emscripten_get_now,F:_emscripten_resize_heap,G:_exit,H:_fd_write,C:_proc_exit};function run(){preRun();function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})}
;return moduleRtn}})();if(typeof exports==="object"&&typeof module==="object"){module.exports=PHYSX;module.exports.default=PHYSX}else if(typeof define==="function"&&define["amd"])define([],()=>PHYSX);
diff --git a/e2e/.dev/physx.release.simd.js b/e2e/.dev/physx.release.simd.js
index bdb1d70990..45843d369b 100644
--- a/e2e/.dev/physx.release.simd.js
+++ b/e2e/.dev/physx.release.simd.js
@@ -1,2 +1,2 @@
-var PHYSX=(()=>{var _scriptName=globalThis.document?.currentScript?.src;return async function(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};if(typeof __filename!="undefined"){_scriptName=__filename}else if(ENVIRONMENT_IS_WORKER){_scriptName=self.location.href}var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var isFileURI=filename=>filename.startsWith("file://");var readyPromiseResolve,readyPromiseReject;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var HEAP64,HEAPU64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["P"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){return "https://mdn.alipayobjects.com/rms/afts/file/A*6vi1SJaSJ6IAAAAAgDAAAAgAehQnAQ/physx.release.simd.wasm"}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;var __abort_js=()=>abort("");var createNamedFunction=(name,func)=>Object.defineProperty(func,"name",{value:name});var emval_freelist=[];var emval_handles=[0,1,,1,null,1,true,1,false,1];var BindingError=class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}};var throwBindingError=message=>{throw new BindingError(message)};var Emval={toValue:handle=>{if(!handle){throwBindingError(`Cannot use deleted val. handle = ${handle}`)}return emval_handles[handle]},toHandle:value=>{switch(value){case undefined:return 2;case null:return 4;case true:return 6;case false:return 8;default:{const handle=emval_freelist.pop()||emval_handles.length;emval_handles[handle]=value;emval_handles[handle+1]=1;return handle}}}};class PureVirtualError extends Error{}var AsciiToString=ptr=>{var str="";while(1){var ch=HEAPU8[ptr++];if(!ch)return str;str+=String.fromCharCode(ch)}};var registeredInstances={};var getBasestPointer=(class_,ptr)=>{if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr};var registerInheritedInstance=(class_,ptr,instance)=>{ptr=getBasestPointer(class_,ptr);if(registeredInstances.hasOwnProperty(ptr)){throwBindingError(`Tried to register registered instance: ${ptr}`)}else{registeredInstances[ptr]=instance}};var registeredTypes={};var getTypeName=type=>{var ptr=___getTypeName(type);var rv=AsciiToString(ptr);_free(ptr);return rv};var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`)}return impl};var unregisterInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);if(registeredInstances.hasOwnProperty(ptr)){delete registeredInstances[ptr]}else{throwBindingError(`Tried to unregister unregistered instance: ${ptr}`)}};var detachFinalizer=handle=>{};var finalizationRegistry=false;var runDestructor=$$=>{if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}};var releaseClassHandle=$$=>{$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}};var attachFinalizer=handle=>{if(!globalThis.FinalizationRegistry){attachFinalizer=handle=>handle;return handle}finalizationRegistry=new FinalizationRegistry(info=>{releaseClassHandle(info.$$)});attachFinalizer=handle=>{var $$=handle.$$;var hasSmartPtr=!!$$.smartPtr;if(hasSmartPtr){var info={$$};finalizationRegistry.register(handle,info,handle)}return handle};detachFinalizer=handle=>finalizationRegistry.unregister(handle);return attachFinalizer(handle)};var __embind_create_inheriting_constructor=(constructorName,wrapperType,properties)=>{constructorName=AsciiToString(constructorName);wrapperType=requireRegisteredType(wrapperType,"wrapper");properties=Emval.toValue(properties);var registeredClass=wrapperType.registeredClass;var wrapperPrototype=registeredClass.instancePrototype;var baseClass=registeredClass.baseClass;var baseClassPrototype=baseClass.instancePrototype;var baseConstructor=registeredClass.baseClass.constructor;var ctor=createNamedFunction(constructorName,function(...args){for(var name of registeredClass.baseClass.pureVirtualFunctions){if(this[name]===baseClassPrototype[name]){throw new PureVirtualError(`Pure virtual function ${name} must be implemented in JavaScript`)}}Object.defineProperty(this,"__parent",{value:wrapperPrototype});this["__construct"](...args)});wrapperPrototype["__construct"]=function __construct(...args){if(this===wrapperPrototype){throwBindingError("Pass correct 'this' to __construct")}var inner=baseConstructor["implement"](this,...args);detachFinalizer(inner);var $$=inner.$$;inner["notifyOnDestruction"]();$$.preservePointerOnDelete=true;Object.defineProperties(this,{$$:{value:$$}});attachFinalizer(this);registerInheritedInstance(registeredClass,$$.ptr,this)};wrapperPrototype["__destruct"]=function __destruct(){if(this===wrapperPrototype){throwBindingError("Pass correct 'this' to __destruct")}detachFinalizer(this);unregisterInheritedInstance(registeredClass,this.$$.ptr)};ctor.prototype=Object.create(wrapperPrototype);Object.assign(ctor.prototype,properties);return Emval.toHandle(ctor)};var structRegistrations={};var runDestructors=destructors=>{while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}};function readPointer(pointer){return this.fromWireType(HEAPU32[pointer>>2])}var awaitingDependencies={};var typeDependencies={};var InternalError=class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}};var throwInternalError=message=>{throw new InternalError(message)};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(type=>typeDependencies[type]=dependentTypes);function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i{typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}}if(0===unregisteredTypes.length){onComplete(typeConverters)}};var __embind_finalize_value_object=structType=>{var reg=structRegistrations[structType];delete structRegistrations[structType];var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;var fieldRecords=reg.fields;var fieldTypes=fieldRecords.map(field=>field.getterReturnType).concat(fieldRecords.map(field=>field.setterArgumentType));whenDependentTypesAreResolved([structType],fieldTypes,fieldTypes=>{var fields={};for(var[i,field]of fieldRecords.entries()){const getterReturnType=fieldTypes[i];const getter=field.getter;const getterContext=field.getterContext;const setterArgumentType=fieldTypes[i+fieldRecords.length];const setter=field.setter;const setterContext=field.setterContext;fields[field.fieldName]={read:ptr=>getterReturnType.fromWireType(getter(getterContext,ptr)),write:(ptr,o)=>{var destructors=[];setter(setterContext,ptr,setterArgumentType.toWireType(destructors,o));runDestructors(destructors)},optional:getterReturnType.optional}}return[{name:reg.name,fromWireType:ptr=>{var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},toWireType:(destructors,o)=>{for(var fieldName in fields){if(!(fieldName in o)&&!fields[fieldName].optional){throw new TypeError(`Missing field: "${fieldName}"`)}}var ptr=rawConstructor();for(fieldName in fields){fields[fieldName].write(ptr,o[fieldName])}if(destructors!==null){destructors.push(rawDestructor,ptr)}return ptr},readValueFromPointer:readPointer,destructorFunction:rawDestructor}]})};function sharedRegisterType(rawType,registeredInstance,options={}){var name=registeredInstance.name;if(!rawType){throwBindingError(`type "${name}" must have a positive integer typeid pointer`)}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError(`Cannot register type '${name}' twice`)}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(cb=>cb())}}function registerType(rawType,registeredInstance,options={}){return sharedRegisterType(rawType,registeredInstance,options)}var integerReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?pointer=>HEAP8[pointer]:pointer=>HEAPU8[pointer];case 2:return signed?pointer=>HEAP16[pointer>>1]:pointer=>HEAPU16[pointer>>1];case 4:return signed?pointer=>HEAP32[pointer>>2]:pointer=>HEAPU32[pointer>>2];case 8:return signed?pointer=>HEAP64[pointer>>3]:pointer=>HEAPU64[pointer>>3];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_bigint=(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0n;let fromWireType=value=>value;if(isUnsignedType){const bitSize=size*8;fromWireType=value=>BigInt.asUintN(bitSize,value);maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>{if(typeof value=="number"){value=BigInt(value)}return value},readValueFromPointer:integerReadValueFromPointer(name,size,!isUnsignedType),destructorFunction:null})};var __embind_register_bool=(rawType,name,trueValue,falseValue)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:function(wt){return!!wt},toWireType:function(destructors,o){return o?trueValue:falseValue},readValueFromPointer:function(pointer){return this.fromWireType(HEAPU8[pointer])},destructorFunction:null})};var shallowCopyInternalPointer=o=>({count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType});var throwInstanceAlreadyDeleted=obj=>{function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")};var deletionQueue=[];var flushPendingDeletes=()=>{while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}};var delayFunction;var init_ClassHandle=()=>{let proto=ClassHandle.prototype;Object.assign(proto,{isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;other.$$=other.$$;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right},clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}},delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}},isDeleted(){return!this.$$.ptr},deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}});const symbolDispose=Symbol.dispose;if(symbolDispose){proto[symbolDispose]=proto["delete"]}};function ClassHandle(){}var registeredPointers={};var ensureOverloadTable=(proto,methodName,humanName)=>{if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(...args){if(!proto[methodName].overloadTable.hasOwnProperty(args.length)){throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`)}return proto[methodName].overloadTable[args.length].apply(this,args)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}};var exposePublicSymbol=(name,value,numArguments)=>{if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError(`Cannot register public name '${name}' twice`)}ensureOverloadTable(Module,name,name);if(Module[name].overloadTable.hasOwnProperty(numArguments)){throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`)}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var char_0=48;var char_9=57;var makeLegalFunctionName=name=>{name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return`_${name}`}return name};function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}var upcastPointer=(ptr,ptrClass,desiredClass)=>{while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError(`Expected null or instance of ${desiredClass.name}, got an instance of ${ptrClass.name}`)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr};var embindRepr=v=>{if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}};function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle||!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,Emval.toHandle(()=>clonedHandle["delete"]()));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupported sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}var downcastPointer=(ptr,ptrClass,desiredClass)=>{if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)};var getInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]};var makeClassHandle=(prototype,record)=>{if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record,writable:true}}))};function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}var init_RegisteredPointer=()=>{Object.assign(RegisteredPointer.prototype,{getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr},destructor(ptr){this.rawDestructor?.(ptr)},readValueFromPointer:readPointer,fromWireType:RegisteredPointer_fromWireType})};function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this.toWireType=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this.toWireType=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this.toWireType=genericPointerToWireType}}var replacePublicSymbol=(name,value,numArguments)=>{if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistent public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var embind__requireFunction=(signature,rawFunction,isAsync=false)=>{signature=AsciiToString(signature);function makeDynCaller(){var rtn=getWasmTableEntry(rawFunction);return rtn}var fp=makeDynCaller();if(typeof fp!="function"){throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`)}return fp};class UnboundTypeError extends Error{}var throwUnboundTypeError=(message,types)=>{var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(`${message}: `+unboundTypes.map(getTypeName).join([", "]))};var __embind_register_class=(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor)=>{name=AsciiToString(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);upcast&&=embind__requireFunction(upcastSignature,upcast);downcast&&=embind__requireFunction(downcastSignature,downcast);rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError(`Cannot construct ${name} due to unbound types`,[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],base=>{base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(name,function(...args){if(Object.getPrototypeOf(this)!==instancePrototype){throw new BindingError(`Use 'new' to construct ${name}`)}if(undefined===registeredClass.constructor_body){throw new BindingError(`${name} has no accessible constructor`)}var body=registeredClass.constructor_body[args.length];if(undefined===body){throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`)}return body.apply(this,args)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);if(registeredClass.baseClass){registeredClass.baseClass.__derivedClasses??=[];registeredClass.baseClass.__derivedClasses.push(registeredClass)}var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})};function usesDestructorStack(argTypes){for(var i=1;i{var array=[];for(var i=0;i>2])}return array};var getFunctionName=signature=>{signature=signature.trim();const argsIndex=signature.indexOf("(");if(argsIndex===-1)return signature;return signature.slice(0,argsIndex)};var __embind_register_class_class_function=(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,fn,isAsync,isNonnullReturn)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=AsciiToString(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker,isAsync);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`${classType.name}.${methodName}`;function unboundTypesHandler(){throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`,rawArgTypes)}if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}var proto=classType.registeredClass.constructor;if(undefined===proto[methodName]){unboundTypesHandler.argCount=argCount-1;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-1]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));var func=craftInvokerFunction(humanName,invokerArgsArray,null,rawInvoker,fn,isAsync);if(undefined===proto[methodName].overloadTable){func.argCount=argCount-1;proto[methodName]=func}else{proto[methodName].overloadTable[argCount-1]=func}if(classType.registeredClass.__derivedClasses){for(const derivedClass of classType.registeredClass.__derivedClasses){if(!derivedClass.constructor.hasOwnProperty(methodName)){derivedClass.constructor[methodName]=func}}}return[]});return[]})};var __embind_register_class_constructor=(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`constructor ${classType.name}`;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError(`Cannot register multiple constructors with identical number of parameters (${argCount-1}) for class '${classType.name}'! Overload resolution is currently only performed using the parameter count, not actual type info!`)}classType.registeredClass.constructor_body[argCount-1]=()=>{throwUnboundTypeError(`Cannot construct ${classType.name} due to unbound types`,rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{argTypes.splice(1,0,null);classType.registeredClass.constructor_body[argCount-1]=craftInvokerFunction(humanName,argTypes,null,invoker,rawConstructor);return[]});return[]})};var __embind_register_class_function=(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual,isAsync,isNonnullReturn)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=AsciiToString(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker,isAsync);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`${classType.name}.${methodName}`;if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}if(isPureVirtual){classType.registeredClass.pureVirtualFunctions.push(methodName)}function unboundTypesHandler(){throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`,rawArgTypes)}var proto=classType.registeredClass.instancePrototype;var method=proto[methodName];if(undefined===method||undefined===method.overloadTable&&method.className!==classType.name&&method.argCount===argCount-2){unboundTypesHandler.argCount=argCount-2;unboundTypesHandler.className=classType.name;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-2]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context,isAsync);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})};var validateThis=(this_,classType,humanName)=>{if(!(this_ instanceof Object)){throwBindingError(`${humanName} with invalid "this": ${this_}`)}if(!(this_ instanceof classType.registeredClass.constructor)){throwBindingError(`${humanName} incompatible with "this" of type ${this_.constructor.name}`)}if(!this_.$$.ptr){throwBindingError(`cannot call emscripten binding method ${humanName} on deleted object`)}return upcastPointer(this_.$$.ptr,this_.$$.ptrType.registeredClass,classType.registeredClass)};var __embind_register_class_property=(classType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext)=>{fieldName=AsciiToString(fieldName);getter=embind__requireFunction(getterSignature,getter);whenDependentTypesAreResolved([],[classType],classType=>{classType=classType[0];var humanName=`${classType.name}.${fieldName}`;var desc={get(){throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`,[getterReturnType,setterArgumentType])},enumerable:true,configurable:true};if(setter){desc.set=()=>throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`,[getterReturnType,setterArgumentType])}else{desc.set=v=>throwBindingError(humanName+" is a read-only property")}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);whenDependentTypesAreResolved([],setter?[getterReturnType,setterArgumentType]:[getterReturnType],types=>{var getterReturnType=types[0];var desc={get(){var ptr=validateThis(this,classType,humanName+" getter");return getterReturnType.fromWireType(getter(getterContext,ptr))},enumerable:true};if(setter){setter=embind__requireFunction(setterSignature,setter);var setterArgumentType=types[1];desc.set=function(v){var ptr=validateThis(this,classType,humanName+" setter");var destructors=[];setter(setterContext,ptr,setterArgumentType.toWireType(destructors,v));runDestructors(destructors)}}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);return[]});return[]})};var __embind_register_constant=(name,type,value)=>{name=AsciiToString(name);whenDependentTypesAreResolved([],[type],type=>{type=type[0];Module[name]=type.fromWireType(value);return[]})};var __emval_decref=handle=>{if(handle>9&&0===--emval_handles[handle+1]){emval_handles[handle]=undefined;emval_freelist.push(handle)}};var EmValType={name:"emscripten::val",fromWireType:handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},toWireType:(destructors,value)=>Emval.toHandle(value),readValueFromPointer:readPointer,destructorFunction:null};var __embind_register_emval=rawType=>registerType(rawType,EmValType);var enumReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?function(pointer){return this.fromWireType(HEAP8[pointer])}:function(pointer){return this.fromWireType(HEAPU8[pointer])};case 2:return signed?function(pointer){return this.fromWireType(HEAP16[pointer>>1])}:function(pointer){return this.fromWireType(HEAPU16[pointer>>1])};case 4:return signed?function(pointer){return this.fromWireType(HEAP32[pointer>>2])}:function(pointer){return this.fromWireType(HEAPU32[pointer>>2])};default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};function getEnumValueType(rawValueType){return rawValueType===0?"object":rawValueType===1?"number":"string"}var __embind_register_enum=(rawType,name,size,isSigned,rawValueType)=>{name=AsciiToString(name);const valueType=getEnumValueType(rawValueType);switch(valueType){case"object":{function ctor(){}ctor.values={};registerType(rawType,{name,constructor:ctor,valueType,fromWireType:function(c){return this.constructor.values[c]},toWireType:(destructors,c)=>c.value,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor);break}case"number":{var keysMap={};registerType(rawType,{name,keysMap,valueType,fromWireType:c=>c,toWireType:(destructors,c)=>c,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,keysMap);delete Module[name].argCount;break}case"string":{var valuesMap={};var reverseMap={};var keysMap={};registerType(rawType,{name,valuesMap,reverseMap,keysMap,valueType,fromWireType:function(c){return this.reverseMap[c]},toWireType:function(destructors,c){return this.valuesMap[c]},readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,keysMap);delete Module[name].argCount;break}}};var __embind_register_enum_value=(rawEnumType,name,enumValue)=>{var enumType=requireRegisteredType(rawEnumType,"enum");name=AsciiToString(name);switch(enumType.valueType){case"object":{var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(`${enumType.name}_${name}`,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value;break}case"number":{enumType.keysMap[name]=enumValue;break}case"string":{enumType.valuesMap[name]=enumValue;enumType.reverseMap[enumValue]=name;enumType.keysMap[name]=name;break}}};var floatReadValueFromPointer=(name,width)=>{switch(width){case 4:return function(pointer){return this.fromWireType(HEAPF32[pointer>>2])};case 8:return function(pointer){return this.fromWireType(HEAPF64[pointer>>3])};default:throw new TypeError(`invalid float width (${width}): ${name}`)}};var __embind_register_float=(rawType,name,size)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:value=>value,toWireType:(destructors,value)=>value,readValueFromPointer:floatReadValueFromPointer(name,size),destructorFunction:null})};var __embind_register_function=(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn,isAsync,isNonnullReturn)=>{var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=AsciiToString(name);name=getFunctionName(name);rawInvoker=embind__requireFunction(signature,rawInvoker,isAsync);exposePublicSymbol(name,function(){throwUnboundTypeError(`Cannot call ${name} due to unbound types`,argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn,isAsync),argCount-1);return[]})};var __embind_register_integer=(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0;let fromWireType=value=>value;if(isUnsignedType){var bitshift=32-8*size;fromWireType=value=>value<>>bitshift;maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>value,readValueFromPointer:integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})};var installIndexedIterator=(proto,sizeMethodName,getMethodName)=>{const makeIterator=(size,getValue)=>{let index=0;return{next(){if(index>=size){return{done:true}}const current=index;index++;const value=getValue(current);return{value,done:false}},[Symbol.iterator](){return this}}};if(!proto[Symbol.iterator]){proto[Symbol.iterator]=function(){const size=this[sizeMethodName]();return makeIterator(size,i=>this[getMethodName](i))}}};var __embind_register_iterable=(rawClassType,rawElementType,sizeMethodName,getMethodName)=>{sizeMethodName=AsciiToString(sizeMethodName);getMethodName=AsciiToString(getMethodName);whenDependentTypesAreResolved([],[rawClassType,rawElementType],types=>{const classType=types[0];installIndexedIterator(classType.registeredClass.instancePrototype,sizeMethodName,getMethodName);return[]})};var __embind_register_memory_view=(rawType,dataTypeIndex,name)=>{var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,BigInt64Array,BigUint64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){var size=HEAPU32[handle>>2];var data=HEAPU32[handle+4>>2];return new TA(HEAP8.buffer,data,size)}name=AsciiToString(name);registerType(rawType,{name,fromWireType:decodeMemoryView,readValueFromPointer:decodeMemoryView},{ignoreDuplicateRegistrations:true})};var EmValOptionalType=Object.assign({optional:true},EmValType);var __embind_register_optional=(rawOptionalType,rawType)=>{registerType(rawOptionalType,EmValOptionalType)};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var __embind_register_std_string=(rawType,name)=>{name=AsciiToString(name);var stdStringIsUTF8=true;registerType(rawType,{name,fromWireType(value){var length=HEAPU32[value>>2];var payload=value+4;var str;if(stdStringIsUTF8){str=UTF8ToString(payload,length,true)}else{str="";for(var i=0;i>2]=length;if(valueIsOfTypeString){if(stdStringIsUTF8){stringToUTF8(value,ptr,length+1)}else{for(var i=0;i255){_free(base);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+i]=charCode}}}else{HEAPU8.set(value,ptr)}if(destructors!==null){destructors.push(_free,base)}return base},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var UTF16Decoder=globalThis.TextDecoder?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead,ignoreNul)=>{var idx=ptr>>1;var endIdx=findStringEnd(HEAPU16,idx,maxBytesToRead/2,ignoreNul);if(endIdx-idx>16&&UTF16Decoder)return UTF16Decoder.decode(HEAPU16.subarray(idx,endIdx));var str="";for(var i=idx;i{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead,ignoreNul)=>{var str="";var startIdx=ptr>>2;for(var i=0;!(i>=maxBytesToRead/4);i++){var utf32=HEAPU32[startIdx+i];if(!utf32&&!ignoreNul)break;str+=String.fromCodePoint(utf32)}return str};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i65535){i++}HEAP32[outPtr>>2]=codePoint;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i65535){i++}len+=4}return len};var __embind_register_std_wstring=(rawType,charSize,name)=>{name=AsciiToString(name);var decodeString,encodeString,lengthBytesUTF;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16}else{decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32}registerType(rawType,{name,fromWireType:value=>{var length=HEAPU32[value>>2];var str=decodeString(value+4,length*charSize,true);_free(value);return str},toWireType:(destructors,value)=>{if(!(typeof value=="string")){throwBindingError(`Cannot pass non-string to C++ string type ${name}`)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length/charSize;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var __embind_register_value_object=(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor)=>{structRegistrations[rawType]={name:AsciiToString(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}};var __embind_register_value_object_field=(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext)=>{structRegistrations[structType].fields.push({fieldName:AsciiToString(fieldName),getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext,setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext})};var __embind_register_void=(rawType,name)=>{name=AsciiToString(name);registerType(rawType,{isVoid:true,name,fromWireType:()=>undefined,toWireType:(destructors,o)=>undefined})};var runtimeKeepaliveCounter=0;var __emscripten_runtime_keepalive_clear=()=>{noExitRuntime=false;runtimeKeepaliveCounter=0};var emval_methodCallers=[];var emval_addMethodCaller=caller=>{var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id};var emval_lookupTypes=(argCount,argTypes)=>{var a=new Array(argCount);for(var i=0;i>2],`parameter ${i}`)}return a};var emval_returnValue=(toReturnWire,destructorsRef,handle)=>{var destructors=[];var result=toReturnWire(destructors,handle);if(destructors.length){HEAPU32[destructorsRef>>2]=Emval.toHandle(destructors)}return result};var emval_symbols={};var getStringOrSymbol=address=>{var symbol=emval_symbols[address];if(symbol===undefined){return AsciiToString(address)}return symbol};var __emval_create_invoker=(argCount,argTypesPtr,kind)=>{var GenericWireTypeSize=8;var[retType,...argTypes]=emval_lookupTypes(argCount,argTypesPtr);var toReturnWire=retType.toWireType.bind(retType);var argFromPtr=argTypes.map(type=>type.readValueFromPointer.bind(type));argCount--;var argN=new Array(argCount);var invokerFunction=(handle,methodName,destructorsRef,args)=>{var offset=0;for(var i=0;it.name)}) => ${retType.name}>`;return emval_addMethodCaller(createNamedFunction(functionName,invokerFunction))};var __emval_invoke=(caller,handle,methodName,destructorsRef,args)=>emval_methodCallers[caller](handle,methodName,destructorsRef,args);var __emval_new_cstring=v=>Emval.toHandle(getStringOrSymbol(v));var __emval_new_object=()=>Emval.toHandle({});var __emval_run_destructors=handle=>{var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)};var __emval_set_property=(handle,key,value)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);value=Emval.toValue(value);handle[key]=value};var timers={};var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var maybeExit=()=>{if(!keepRuntimeAlive()){try{_exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{return func()}catch(e){handleException(e)}finally{maybeExit()}};var _emscripten_get_now=()=>performance.now();var __setitimer_js=(which,timeout_ms)=>{if(timers[which]){clearTimeout(timers[which].id);delete timers[which]}if(!timeout_ms)return 0;var id=setTimeout(()=>{delete timers[which];callUserCallback(()=>__emscripten_timeout(which,_emscripten_get_now()))},timeout_ms);timers[which]={id,timeout_ms};return 0};var _emscripten_date_now=()=>Date.now();var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var printCharBuffers=[null,[],[]];var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j>2]=num;return 0};init_ClassHandle();init_RegisteredPointer();{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}var ___getTypeName,_free,_malloc,__emscripten_timeout,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){___getTypeName=wasmExports["Q"];_free=Module["_free"]=wasmExports["R"];_malloc=Module["_malloc"]=wasmExports["S"];__emscripten_timeout=wasmExports["U"];memory=wasmMemory=wasmExports["O"];__indirect_function_table=wasmTable=wasmExports["T"]}var wasmImports={J:__abort_js,x:__embind_create_inheriting_constructor,s:__embind_finalize_value_object,z:__embind_register_bigint,A:__embind_register_bool,b:__embind_register_class,p:__embind_register_class_class_function,e:__embind_register_class_constructor,a:__embind_register_class_function,d:__embind_register_class_property,K:__embind_register_constant,M:__embind_register_emval,l:__embind_register_enum,f:__embind_register_enum_value,y:__embind_register_float,g:__embind_register_function,o:__embind_register_integer,q:__embind_register_iterable,h:__embind_register_memory_view,r:__embind_register_optional,N:__embind_register_std_string,u:__embind_register_std_wstring,t:__embind_register_value_object,m:__embind_register_value_object_field,B:__embind_register_void,D:__emscripten_runtime_keepalive_clear,k:__emval_create_invoker,n:__emval_decref,j:__emval_invoke,v:__emval_new_cstring,L:__emval_new_object,i:__emval_run_destructors,w:__emval_set_property,E:__setitimer_js,I:_emscripten_date_now,c:_emscripten_get_now,F:_emscripten_resize_heap,G:_exit,H:_fd_write,C:_proc_exit};function run(){preRun();function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})}
+var PHYSX=(()=>{var _scriptName=globalThis.document?.currentScript?.src;return async function(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};if(typeof __filename!="undefined"){_scriptName=__filename}else if(ENVIRONMENT_IS_WORKER){_scriptName=self.location.href}var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var isFileURI=filename=>filename.startsWith("file://");var readyPromiseResolve,readyPromiseReject;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var HEAP64,HEAPU64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["P"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){return locateFile("physx.release.simd.wasm")}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;var __abort_js=()=>abort("");var createNamedFunction=(name,func)=>Object.defineProperty(func,"name",{value:name});var emval_freelist=[];var emval_handles=[0,1,,1,null,1,true,1,false,1];var BindingError=class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}};var throwBindingError=message=>{throw new BindingError(message)};var Emval={toValue:handle=>{if(!handle){throwBindingError(`Cannot use deleted val. handle = ${handle}`)}return emval_handles[handle]},toHandle:value=>{switch(value){case undefined:return 2;case null:return 4;case true:return 6;case false:return 8;default:{const handle=emval_freelist.pop()||emval_handles.length;emval_handles[handle]=value;emval_handles[handle+1]=1;return handle}}}};class PureVirtualError extends Error{}var AsciiToString=ptr=>{var str="";while(1){var ch=HEAPU8[ptr++];if(!ch)return str;str+=String.fromCharCode(ch)}};var registeredInstances={};var getBasestPointer=(class_,ptr)=>{if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr};var registerInheritedInstance=(class_,ptr,instance)=>{ptr=getBasestPointer(class_,ptr);if(registeredInstances.hasOwnProperty(ptr)){throwBindingError(`Tried to register registered instance: ${ptr}`)}else{registeredInstances[ptr]=instance}};var registeredTypes={};var getTypeName=type=>{var ptr=___getTypeName(type);var rv=AsciiToString(ptr);_free(ptr);return rv};var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`)}return impl};var unregisterInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);if(registeredInstances.hasOwnProperty(ptr)){delete registeredInstances[ptr]}else{throwBindingError(`Tried to unregister unregistered instance: ${ptr}`)}};var detachFinalizer=handle=>{};var finalizationRegistry=false;var runDestructor=$$=>{if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}};var releaseClassHandle=$$=>{$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}};var attachFinalizer=handle=>{if(!globalThis.FinalizationRegistry){attachFinalizer=handle=>handle;return handle}finalizationRegistry=new FinalizationRegistry(info=>{releaseClassHandle(info.$$)});attachFinalizer=handle=>{var $$=handle.$$;var hasSmartPtr=!!$$.smartPtr;if(hasSmartPtr){var info={$$};finalizationRegistry.register(handle,info,handle)}return handle};detachFinalizer=handle=>finalizationRegistry.unregister(handle);return attachFinalizer(handle)};var __embind_create_inheriting_constructor=(constructorName,wrapperType,properties)=>{constructorName=AsciiToString(constructorName);wrapperType=requireRegisteredType(wrapperType,"wrapper");properties=Emval.toValue(properties);var registeredClass=wrapperType.registeredClass;var wrapperPrototype=registeredClass.instancePrototype;var baseClass=registeredClass.baseClass;var baseClassPrototype=baseClass.instancePrototype;var baseConstructor=registeredClass.baseClass.constructor;var ctor=createNamedFunction(constructorName,function(...args){for(var name of registeredClass.baseClass.pureVirtualFunctions){if(this[name]===baseClassPrototype[name]){throw new PureVirtualError(`Pure virtual function ${name} must be implemented in JavaScript`)}}Object.defineProperty(this,"__parent",{value:wrapperPrototype});this["__construct"](...args)});wrapperPrototype["__construct"]=function __construct(...args){if(this===wrapperPrototype){throwBindingError("Pass correct 'this' to __construct")}var inner=baseConstructor["implement"](this,...args);detachFinalizer(inner);var $$=inner.$$;inner["notifyOnDestruction"]();$$.preservePointerOnDelete=true;Object.defineProperties(this,{$$:{value:$$}});attachFinalizer(this);registerInheritedInstance(registeredClass,$$.ptr,this)};wrapperPrototype["__destruct"]=function __destruct(){if(this===wrapperPrototype){throwBindingError("Pass correct 'this' to __destruct")}detachFinalizer(this);unregisterInheritedInstance(registeredClass,this.$$.ptr)};ctor.prototype=Object.create(wrapperPrototype);Object.assign(ctor.prototype,properties);return Emval.toHandle(ctor)};var structRegistrations={};var runDestructors=destructors=>{while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}};function readPointer(pointer){return this.fromWireType(HEAPU32[pointer>>2])}var awaitingDependencies={};var typeDependencies={};var InternalError=class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}};var throwInternalError=message=>{throw new InternalError(message)};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(type=>typeDependencies[type]=dependentTypes);function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i{typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}}if(0===unregisteredTypes.length){onComplete(typeConverters)}};var __embind_finalize_value_object=structType=>{var reg=structRegistrations[structType];delete structRegistrations[structType];var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;var fieldRecords=reg.fields;var fieldTypes=fieldRecords.map(field=>field.getterReturnType).concat(fieldRecords.map(field=>field.setterArgumentType));whenDependentTypesAreResolved([structType],fieldTypes,fieldTypes=>{var fields={};for(var[i,field]of fieldRecords.entries()){const getterReturnType=fieldTypes[i];const getter=field.getter;const getterContext=field.getterContext;const setterArgumentType=fieldTypes[i+fieldRecords.length];const setter=field.setter;const setterContext=field.setterContext;fields[field.fieldName]={read:ptr=>getterReturnType.fromWireType(getter(getterContext,ptr)),write:(ptr,o)=>{var destructors=[];setter(setterContext,ptr,setterArgumentType.toWireType(destructors,o));runDestructors(destructors)},optional:getterReturnType.optional}}return[{name:reg.name,fromWireType:ptr=>{var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},toWireType:(destructors,o)=>{for(var fieldName in fields){if(!(fieldName in o)&&!fields[fieldName].optional){throw new TypeError(`Missing field: "${fieldName}"`)}}var ptr=rawConstructor();for(fieldName in fields){fields[fieldName].write(ptr,o[fieldName])}if(destructors!==null){destructors.push(rawDestructor,ptr)}return ptr},readValueFromPointer:readPointer,destructorFunction:rawDestructor}]})};function sharedRegisterType(rawType,registeredInstance,options={}){var name=registeredInstance.name;if(!rawType){throwBindingError(`type "${name}" must have a positive integer typeid pointer`)}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError(`Cannot register type '${name}' twice`)}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(cb=>cb())}}function registerType(rawType,registeredInstance,options={}){return sharedRegisterType(rawType,registeredInstance,options)}var integerReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?pointer=>HEAP8[pointer]:pointer=>HEAPU8[pointer];case 2:return signed?pointer=>HEAP16[pointer>>1]:pointer=>HEAPU16[pointer>>1];case 4:return signed?pointer=>HEAP32[pointer>>2]:pointer=>HEAPU32[pointer>>2];case 8:return signed?pointer=>HEAP64[pointer>>3]:pointer=>HEAPU64[pointer>>3];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_bigint=(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0n;let fromWireType=value=>value;if(isUnsignedType){const bitSize=size*8;fromWireType=value=>BigInt.asUintN(bitSize,value);maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>{if(typeof value=="number"){value=BigInt(value)}return value},readValueFromPointer:integerReadValueFromPointer(name,size,!isUnsignedType),destructorFunction:null})};var __embind_register_bool=(rawType,name,trueValue,falseValue)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:function(wt){return!!wt},toWireType:function(destructors,o){return o?trueValue:falseValue},readValueFromPointer:function(pointer){return this.fromWireType(HEAPU8[pointer])},destructorFunction:null})};var shallowCopyInternalPointer=o=>({count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType});var throwInstanceAlreadyDeleted=obj=>{function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")};var deletionQueue=[];var flushPendingDeletes=()=>{while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}};var delayFunction;var init_ClassHandle=()=>{let proto=ClassHandle.prototype;Object.assign(proto,{isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;other.$$=other.$$;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right},clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}},delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}},isDeleted(){return!this.$$.ptr},deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}});const symbolDispose=Symbol.dispose;if(symbolDispose){proto[symbolDispose]=proto["delete"]}};function ClassHandle(){}var registeredPointers={};var ensureOverloadTable=(proto,methodName,humanName)=>{if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(...args){if(!proto[methodName].overloadTable.hasOwnProperty(args.length)){throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`)}return proto[methodName].overloadTable[args.length].apply(this,args)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}};var exposePublicSymbol=(name,value,numArguments)=>{if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError(`Cannot register public name '${name}' twice`)}ensureOverloadTable(Module,name,name);if(Module[name].overloadTable.hasOwnProperty(numArguments)){throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`)}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var char_0=48;var char_9=57;var makeLegalFunctionName=name=>{name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return`_${name}`}return name};function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}var upcastPointer=(ptr,ptrClass,desiredClass)=>{while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError(`Expected null or instance of ${desiredClass.name}, got an instance of ${ptrClass.name}`)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr};var embindRepr=v=>{if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}};function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle||!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,Emval.toHandle(()=>clonedHandle["delete"]()));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupported sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}var downcastPointer=(ptr,ptrClass,desiredClass)=>{if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)};var getInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]};var makeClassHandle=(prototype,record)=>{if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record,writable:true}}))};function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}var init_RegisteredPointer=()=>{Object.assign(RegisteredPointer.prototype,{getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr},destructor(ptr){this.rawDestructor?.(ptr)},readValueFromPointer:readPointer,fromWireType:RegisteredPointer_fromWireType})};function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this.toWireType=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this.toWireType=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this.toWireType=genericPointerToWireType}}var replacePublicSymbol=(name,value,numArguments)=>{if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistent public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var embind__requireFunction=(signature,rawFunction,isAsync=false)=>{signature=AsciiToString(signature);function makeDynCaller(){var rtn=getWasmTableEntry(rawFunction);return rtn}var fp=makeDynCaller();if(typeof fp!="function"){throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`)}return fp};class UnboundTypeError extends Error{}var throwUnboundTypeError=(message,types)=>{var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(`${message}: `+unboundTypes.map(getTypeName).join([", "]))};var __embind_register_class=(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor)=>{name=AsciiToString(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);upcast&&=embind__requireFunction(upcastSignature,upcast);downcast&&=embind__requireFunction(downcastSignature,downcast);rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError(`Cannot construct ${name} due to unbound types`,[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],base=>{base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(name,function(...args){if(Object.getPrototypeOf(this)!==instancePrototype){throw new BindingError(`Use 'new' to construct ${name}`)}if(undefined===registeredClass.constructor_body){throw new BindingError(`${name} has no accessible constructor`)}var body=registeredClass.constructor_body[args.length];if(undefined===body){throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`)}return body.apply(this,args)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);if(registeredClass.baseClass){registeredClass.baseClass.__derivedClasses??=[];registeredClass.baseClass.__derivedClasses.push(registeredClass)}var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})};function usesDestructorStack(argTypes){for(var i=1;i{var array=[];for(var i=0;i>2])}return array};var getFunctionName=signature=>{signature=signature.trim();const argsIndex=signature.indexOf("(");if(argsIndex===-1)return signature;return signature.slice(0,argsIndex)};var __embind_register_class_class_function=(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,fn,isAsync,isNonnullReturn)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=AsciiToString(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker,isAsync);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`${classType.name}.${methodName}`;function unboundTypesHandler(){throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`,rawArgTypes)}if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}var proto=classType.registeredClass.constructor;if(undefined===proto[methodName]){unboundTypesHandler.argCount=argCount-1;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-1]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));var func=craftInvokerFunction(humanName,invokerArgsArray,null,rawInvoker,fn,isAsync);if(undefined===proto[methodName].overloadTable){func.argCount=argCount-1;proto[methodName]=func}else{proto[methodName].overloadTable[argCount-1]=func}if(classType.registeredClass.__derivedClasses){for(const derivedClass of classType.registeredClass.__derivedClasses){if(!derivedClass.constructor.hasOwnProperty(methodName)){derivedClass.constructor[methodName]=func}}}return[]});return[]})};var __embind_register_class_constructor=(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`constructor ${classType.name}`;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError(`Cannot register multiple constructors with identical number of parameters (${argCount-1}) for class '${classType.name}'! Overload resolution is currently only performed using the parameter count, not actual type info!`)}classType.registeredClass.constructor_body[argCount-1]=()=>{throwUnboundTypeError(`Cannot construct ${classType.name} due to unbound types`,rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{argTypes.splice(1,0,null);classType.registeredClass.constructor_body[argCount-1]=craftInvokerFunction(humanName,argTypes,null,invoker,rawConstructor);return[]});return[]})};var __embind_register_class_function=(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual,isAsync,isNonnullReturn)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=AsciiToString(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker,isAsync);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`${classType.name}.${methodName}`;if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}if(isPureVirtual){classType.registeredClass.pureVirtualFunctions.push(methodName)}function unboundTypesHandler(){throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`,rawArgTypes)}var proto=classType.registeredClass.instancePrototype;var method=proto[methodName];if(undefined===method||undefined===method.overloadTable&&method.className!==classType.name&&method.argCount===argCount-2){unboundTypesHandler.argCount=argCount-2;unboundTypesHandler.className=classType.name;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-2]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context,isAsync);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})};var validateThis=(this_,classType,humanName)=>{if(!(this_ instanceof Object)){throwBindingError(`${humanName} with invalid "this": ${this_}`)}if(!(this_ instanceof classType.registeredClass.constructor)){throwBindingError(`${humanName} incompatible with "this" of type ${this_.constructor.name}`)}if(!this_.$$.ptr){throwBindingError(`cannot call emscripten binding method ${humanName} on deleted object`)}return upcastPointer(this_.$$.ptr,this_.$$.ptrType.registeredClass,classType.registeredClass)};var __embind_register_class_property=(classType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext)=>{fieldName=AsciiToString(fieldName);getter=embind__requireFunction(getterSignature,getter);whenDependentTypesAreResolved([],[classType],classType=>{classType=classType[0];var humanName=`${classType.name}.${fieldName}`;var desc={get(){throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`,[getterReturnType,setterArgumentType])},enumerable:true,configurable:true};if(setter){desc.set=()=>throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`,[getterReturnType,setterArgumentType])}else{desc.set=v=>throwBindingError(humanName+" is a read-only property")}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);whenDependentTypesAreResolved([],setter?[getterReturnType,setterArgumentType]:[getterReturnType],types=>{var getterReturnType=types[0];var desc={get(){var ptr=validateThis(this,classType,humanName+" getter");return getterReturnType.fromWireType(getter(getterContext,ptr))},enumerable:true};if(setter){setter=embind__requireFunction(setterSignature,setter);var setterArgumentType=types[1];desc.set=function(v){var ptr=validateThis(this,classType,humanName+" setter");var destructors=[];setter(setterContext,ptr,setterArgumentType.toWireType(destructors,v));runDestructors(destructors)}}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);return[]});return[]})};var __embind_register_constant=(name,type,value)=>{name=AsciiToString(name);whenDependentTypesAreResolved([],[type],type=>{type=type[0];Module[name]=type.fromWireType(value);return[]})};var __emval_decref=handle=>{if(handle>9&&0===--emval_handles[handle+1]){emval_handles[handle]=undefined;emval_freelist.push(handle)}};var EmValType={name:"emscripten::val",fromWireType:handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},toWireType:(destructors,value)=>Emval.toHandle(value),readValueFromPointer:readPointer,destructorFunction:null};var __embind_register_emval=rawType=>registerType(rawType,EmValType);var enumReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?function(pointer){return this.fromWireType(HEAP8[pointer])}:function(pointer){return this.fromWireType(HEAPU8[pointer])};case 2:return signed?function(pointer){return this.fromWireType(HEAP16[pointer>>1])}:function(pointer){return this.fromWireType(HEAPU16[pointer>>1])};case 4:return signed?function(pointer){return this.fromWireType(HEAP32[pointer>>2])}:function(pointer){return this.fromWireType(HEAPU32[pointer>>2])};default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};function getEnumValueType(rawValueType){return rawValueType===0?"object":rawValueType===1?"number":"string"}var __embind_register_enum=(rawType,name,size,isSigned,rawValueType)=>{name=AsciiToString(name);const valueType=getEnumValueType(rawValueType);switch(valueType){case"object":{function ctor(){}ctor.values={};registerType(rawType,{name,constructor:ctor,valueType,fromWireType:function(c){return this.constructor.values[c]},toWireType:(destructors,c)=>c.value,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor);break}case"number":{var keysMap={};registerType(rawType,{name,keysMap,valueType,fromWireType:c=>c,toWireType:(destructors,c)=>c,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,keysMap);delete Module[name].argCount;break}case"string":{var valuesMap={};var reverseMap={};var keysMap={};registerType(rawType,{name,valuesMap,reverseMap,keysMap,valueType,fromWireType:function(c){return this.reverseMap[c]},toWireType:function(destructors,c){return this.valuesMap[c]},readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,keysMap);delete Module[name].argCount;break}}};var __embind_register_enum_value=(rawEnumType,name,enumValue)=>{var enumType=requireRegisteredType(rawEnumType,"enum");name=AsciiToString(name);switch(enumType.valueType){case"object":{var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(`${enumType.name}_${name}`,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value;break}case"number":{enumType.keysMap[name]=enumValue;break}case"string":{enumType.valuesMap[name]=enumValue;enumType.reverseMap[enumValue]=name;enumType.keysMap[name]=name;break}}};var floatReadValueFromPointer=(name,width)=>{switch(width){case 4:return function(pointer){return this.fromWireType(HEAPF32[pointer>>2])};case 8:return function(pointer){return this.fromWireType(HEAPF64[pointer>>3])};default:throw new TypeError(`invalid float width (${width}): ${name}`)}};var __embind_register_float=(rawType,name,size)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:value=>value,toWireType:(destructors,value)=>value,readValueFromPointer:floatReadValueFromPointer(name,size),destructorFunction:null})};var __embind_register_function=(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn,isAsync,isNonnullReturn)=>{var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=AsciiToString(name);name=getFunctionName(name);rawInvoker=embind__requireFunction(signature,rawInvoker,isAsync);exposePublicSymbol(name,function(){throwUnboundTypeError(`Cannot call ${name} due to unbound types`,argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn,isAsync),argCount-1);return[]})};var __embind_register_integer=(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0;let fromWireType=value=>value;if(isUnsignedType){var bitshift=32-8*size;fromWireType=value=>value<>>bitshift;maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>value,readValueFromPointer:integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})};var installIndexedIterator=(proto,sizeMethodName,getMethodName)=>{const makeIterator=(size,getValue)=>{let index=0;return{next(){if(index>=size){return{done:true}}const current=index;index++;const value=getValue(current);return{value,done:false}},[Symbol.iterator](){return this}}};if(!proto[Symbol.iterator]){proto[Symbol.iterator]=function(){const size=this[sizeMethodName]();return makeIterator(size,i=>this[getMethodName](i))}}};var __embind_register_iterable=(rawClassType,rawElementType,sizeMethodName,getMethodName)=>{sizeMethodName=AsciiToString(sizeMethodName);getMethodName=AsciiToString(getMethodName);whenDependentTypesAreResolved([],[rawClassType,rawElementType],types=>{const classType=types[0];installIndexedIterator(classType.registeredClass.instancePrototype,sizeMethodName,getMethodName);return[]})};var __embind_register_memory_view=(rawType,dataTypeIndex,name)=>{var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,BigInt64Array,BigUint64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){var size=HEAPU32[handle>>2];var data=HEAPU32[handle+4>>2];return new TA(HEAP8.buffer,data,size)}name=AsciiToString(name);registerType(rawType,{name,fromWireType:decodeMemoryView,readValueFromPointer:decodeMemoryView},{ignoreDuplicateRegistrations:true})};var EmValOptionalType=Object.assign({optional:true},EmValType);var __embind_register_optional=(rawOptionalType,rawType)=>{registerType(rawOptionalType,EmValOptionalType)};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var __embind_register_std_string=(rawType,name)=>{name=AsciiToString(name);var stdStringIsUTF8=true;registerType(rawType,{name,fromWireType(value){var length=HEAPU32[value>>2];var payload=value+4;var str;if(stdStringIsUTF8){str=UTF8ToString(payload,length,true)}else{str="";for(var i=0;i>2]=length;if(valueIsOfTypeString){if(stdStringIsUTF8){stringToUTF8(value,ptr,length+1)}else{for(var i=0;i255){_free(base);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+i]=charCode}}}else{HEAPU8.set(value,ptr)}if(destructors!==null){destructors.push(_free,base)}return base},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var UTF16Decoder=globalThis.TextDecoder?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead,ignoreNul)=>{var idx=ptr>>1;var endIdx=findStringEnd(HEAPU16,idx,maxBytesToRead/2,ignoreNul);if(endIdx-idx>16&&UTF16Decoder)return UTF16Decoder.decode(HEAPU16.subarray(idx,endIdx));var str="";for(var i=idx;i{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead,ignoreNul)=>{var str="";var startIdx=ptr>>2;for(var i=0;!(i>=maxBytesToRead/4);i++){var utf32=HEAPU32[startIdx+i];if(!utf32&&!ignoreNul)break;str+=String.fromCodePoint(utf32)}return str};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i65535){i++}HEAP32[outPtr>>2]=codePoint;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i65535){i++}len+=4}return len};var __embind_register_std_wstring=(rawType,charSize,name)=>{name=AsciiToString(name);var decodeString,encodeString,lengthBytesUTF;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16}else{decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32}registerType(rawType,{name,fromWireType:value=>{var length=HEAPU32[value>>2];var str=decodeString(value+4,length*charSize,true);_free(value);return str},toWireType:(destructors,value)=>{if(!(typeof value=="string")){throwBindingError(`Cannot pass non-string to C++ string type ${name}`)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length/charSize;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var __embind_register_value_object=(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor)=>{structRegistrations[rawType]={name:AsciiToString(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}};var __embind_register_value_object_field=(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext)=>{structRegistrations[structType].fields.push({fieldName:AsciiToString(fieldName),getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext,setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext})};var __embind_register_void=(rawType,name)=>{name=AsciiToString(name);registerType(rawType,{isVoid:true,name,fromWireType:()=>undefined,toWireType:(destructors,o)=>undefined})};var runtimeKeepaliveCounter=0;var __emscripten_runtime_keepalive_clear=()=>{noExitRuntime=false;runtimeKeepaliveCounter=0};var emval_methodCallers=[];var emval_addMethodCaller=caller=>{var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id};var emval_lookupTypes=(argCount,argTypes)=>{var a=new Array(argCount);for(var i=0;i>2],`parameter ${i}`)}return a};var emval_returnValue=(toReturnWire,destructorsRef,handle)=>{var destructors=[];var result=toReturnWire(destructors,handle);if(destructors.length){HEAPU32[destructorsRef>>2]=Emval.toHandle(destructors)}return result};var emval_symbols={};var getStringOrSymbol=address=>{var symbol=emval_symbols[address];if(symbol===undefined){return AsciiToString(address)}return symbol};var __emval_create_invoker=(argCount,argTypesPtr,kind)=>{var GenericWireTypeSize=8;var[retType,...argTypes]=emval_lookupTypes(argCount,argTypesPtr);var toReturnWire=retType.toWireType.bind(retType);var argFromPtr=argTypes.map(type=>type.readValueFromPointer.bind(type));argCount--;var argN=new Array(argCount);var invokerFunction=(handle,methodName,destructorsRef,args)=>{var offset=0;for(var i=0;it.name)}) => ${retType.name}>`;return emval_addMethodCaller(createNamedFunction(functionName,invokerFunction))};var __emval_invoke=(caller,handle,methodName,destructorsRef,args)=>emval_methodCallers[caller](handle,methodName,destructorsRef,args);var __emval_new_cstring=v=>Emval.toHandle(getStringOrSymbol(v));var __emval_new_object=()=>Emval.toHandle({});var __emval_run_destructors=handle=>{var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)};var __emval_set_property=(handle,key,value)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);value=Emval.toValue(value);handle[key]=value};var timers={};var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var maybeExit=()=>{if(!keepRuntimeAlive()){try{_exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{return func()}catch(e){handleException(e)}finally{maybeExit()}};var _emscripten_get_now=()=>performance.now();var __setitimer_js=(which,timeout_ms)=>{if(timers[which]){clearTimeout(timers[which].id);delete timers[which]}if(!timeout_ms)return 0;var id=setTimeout(()=>{delete timers[which];callUserCallback(()=>__emscripten_timeout(which,_emscripten_get_now()))},timeout_ms);timers[which]={id,timeout_ms};return 0};var _emscripten_date_now=()=>Date.now();var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var printCharBuffers=[null,[],[]];var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j>2]=num;return 0};init_ClassHandle();init_RegisteredPointer();{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}var ___getTypeName,_free,_malloc,__emscripten_timeout,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){___getTypeName=wasmExports["Q"];_free=Module["_free"]=wasmExports["R"];_malloc=Module["_malloc"]=wasmExports["S"];__emscripten_timeout=wasmExports["U"];memory=wasmMemory=wasmExports["O"];__indirect_function_table=wasmTable=wasmExports["T"]}var wasmImports={J:__abort_js,x:__embind_create_inheriting_constructor,s:__embind_finalize_value_object,z:__embind_register_bigint,A:__embind_register_bool,b:__embind_register_class,p:__embind_register_class_class_function,e:__embind_register_class_constructor,a:__embind_register_class_function,d:__embind_register_class_property,K:__embind_register_constant,M:__embind_register_emval,l:__embind_register_enum,f:__embind_register_enum_value,y:__embind_register_float,g:__embind_register_function,o:__embind_register_integer,q:__embind_register_iterable,h:__embind_register_memory_view,r:__embind_register_optional,N:__embind_register_std_string,u:__embind_register_std_wstring,t:__embind_register_value_object,m:__embind_register_value_object_field,B:__embind_register_void,D:__emscripten_runtime_keepalive_clear,k:__emval_create_invoker,n:__emval_decref,j:__emval_invoke,v:__emval_new_cstring,L:__emval_new_object,i:__emval_run_destructors,w:__emval_set_property,E:__setitimer_js,I:_emscripten_date_now,c:_emscripten_get_now,F:_emscripten_resize_heap,G:_exit,H:_fd_write,C:_proc_exit};function run(){preRun();function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})}
;return moduleRtn}})();if(typeof exports==="object"&&typeof module==="object"){module.exports=PHYSX;module.exports.default=PHYSX}else if(typeof define==="function"&&define["amd"])define([],()=>PHYSX);
diff --git a/e2e/.dev/physx.release.simd.wasm b/e2e/.dev/physx.release.simd.wasm
index 91e5bab34b..f52b48110a 100755
Binary files a/e2e/.dev/physx.release.simd.wasm and b/e2e/.dev/physx.release.simd.wasm differ
diff --git a/e2e/.dev/physx.release.wasm b/e2e/.dev/physx.release.wasm
index d9e03eee6a..3efb26a288 100755
Binary files a/e2e/.dev/physx.release.wasm and b/e2e/.dev/physx.release.wasm differ
diff --git a/e2e/.dev/public/spineboy.atlas b/e2e/.dev/public/spineboy.atlas
new file mode 100644
index 0000000000..eca542b711
--- /dev/null
+++ b/e2e/.dev/public/spineboy.atlas
@@ -0,0 +1,94 @@
+spineboy.png
+ size: 1024, 256
+ filter: Linear, Linear
+ scale: 0.5
+crosshair
+ bounds: 352, 7, 45, 45
+eye-indifferent
+ bounds: 862, 105, 47, 45
+eye-surprised
+ bounds: 505, 79, 47, 45
+front-bracer
+ bounds: 826, 66, 29, 40
+front-fist-closed
+ bounds: 786, 65, 38, 41
+front-fist-open
+ bounds: 710, 51, 43, 44
+ rotate: 90
+front-foot
+ bounds: 210, 6, 63, 35
+front-shin
+ bounds: 665, 128, 41, 92
+ rotate: 90
+front-thigh
+ bounds: 2, 2, 23, 56
+ rotate: 90
+front-upper-arm
+ bounds: 250, 205, 23, 49
+goggles
+ bounds: 665, 171, 131, 83
+gun
+ bounds: 798, 152, 105, 102
+head
+ bounds: 2, 27, 136, 149
+hoverboard-board
+ bounds: 2, 178, 246, 76
+hoverboard-thruster
+ bounds: 722, 96, 30, 32
+ rotate: 90
+hoverglow-small
+ bounds: 275, 81, 137, 38
+mouth-grind
+ bounds: 614, 97, 47, 30
+mouth-oooo
+ bounds: 612, 65, 47, 30
+mouth-smile
+ bounds: 661, 64, 47, 30
+muzzle-glow
+ bounds: 382, 54, 25, 25
+muzzle-ring
+ bounds: 275, 54, 25, 105
+ rotate: 90
+muzzle01
+ bounds: 911, 95, 67, 40
+ rotate: 90
+muzzle02
+ bounds: 792, 108, 68, 42
+muzzle03
+ bounds: 956, 171, 83, 53
+ rotate: 90
+muzzle04
+ bounds: 275, 7, 75, 45
+muzzle05
+ bounds: 140, 3, 68, 38
+neck
+ bounds: 250, 182, 18, 21
+portal-bg
+ bounds: 140, 43, 133, 133
+portal-flare1
+ bounds: 554, 65, 56, 30
+portal-flare2
+ bounds: 759, 112, 57, 31
+ rotate: 90
+portal-flare3
+ bounds: 554, 97, 58, 30
+portal-shade
+ bounds: 275, 121, 133, 133
+portal-streaks1
+ bounds: 410, 126, 126, 128
+portal-streaks2
+ bounds: 538, 129, 125, 125
+rear-bracer
+ bounds: 857, 67, 28, 36
+rear-foot
+ bounds: 663, 96, 57, 30
+rear-shin
+ bounds: 414, 86, 38, 89
+ rotate: 90
+rear-thigh
+ bounds: 756, 63, 28, 47
+rear-upper-arm
+ bounds: 60, 5, 20, 44
+ rotate: 90
+torso
+ bounds: 905, 164, 49, 90
diff --git a/e2e/.dev/public/spineboy.json b/e2e/.dev/public/spineboy.json
new file mode 100644
index 0000000000..c0eb0ae927
--- /dev/null
+++ b/e2e/.dev/public/spineboy.json
@@ -0,0 +1,8723 @@
+{
+ "skeleton": {
+ "hash": "dr3Kr/vMgPA",
+ "spine": "4.2.22",
+ "x": -188.63,
+ "y": -7.94,
+ "width": 418.45,
+ "height": 686.2,
+ "images": "./images/",
+ "audio": ""
+ },
+ "bones": [
+ { "name": "root", "rotation": 0.05 },
+ { "name": "hip", "parent": "root", "y": 247.27 },
+ { "name": "crosshair", "parent": "root", "x": 302.83, "y": 569.45, "color": "ff3f00ff", "icon": "circle" },
+ {
+ "name": "aim-constraint-target",
+ "parent": "hip",
+ "length": 26.24,
+ "rotation": 19.61,
+ "x": 1.02,
+ "y": 5.62,
+ "color": "abe323ff"
+ },
+ { "name": "rear-foot-target", "parent": "root", "x": 61.91, "y": 0.42, "color": "ff3f00ff", "icon": "ik" },
+ { "name": "rear-leg-target", "parent": "rear-foot-target", "x": -33.91, "y": 37.34, "color": "ff3f00ff", "icon": "ik" },
+ {
+ "name": "rear-thigh",
+ "parent": "hip",
+ "length": 85.72,
+ "rotation": -72.54,
+ "x": 8.91,
+ "y": -5.63,
+ "color": "ff000dff"
+ },
+ {
+ "name": "rear-shin",
+ "parent": "rear-thigh",
+ "length": 121.88,
+ "rotation": -19.83,
+ "x": 86.1,
+ "y": -1.33,
+ "color": "ff000dff"
+ },
+ {
+ "name": "rear-foot",
+ "parent": "rear-shin",
+ "length": 51.58,
+ "rotation": 45.78,
+ "x": 121.46,
+ "y": -0.76,
+ "color": "ff000dff"
+ },
+ {
+ "name": "back-foot-tip",
+ "parent": "rear-foot",
+ "length": 50.3,
+ "rotation": -0.85,
+ "x": 51.17,
+ "y": 0.24,
+ "inherit": "noRotationOrReflection",
+ "color": "ff000dff"
+ },
+ { "name": "board-ik", "parent": "root", "x": -131.78, "y": 69.09, "color": "4c56ffff", "icon": "arrows" },
+ { "name": "clipping", "parent": "root" },
+ {
+ "name": "hoverboard-controller",
+ "parent": "root",
+ "rotation": -0.28,
+ "x": -329.69,
+ "y": 69.82,
+ "color": "ff0004ff",
+ "icon": "arrowsB"
+ },
+ { "name": "exhaust1", "parent": "hoverboard-controller", "rotation": 3.02, "x": -249.68, "y": 53.39 },
+ { "name": "exhaust2", "parent": "hoverboard-controller", "rotation": 26.34, "x": -191.6, "y": -22.92 },
+ {
+ "name": "exhaust3",
+ "parent": "hoverboard-controller",
+ "rotation": -12.34,
+ "x": -236.03,
+ "y": 80.54,
+ "scaleX": 0.7847,
+ "scaleY": 0.7847
+ },
+ { "name": "portal-root", "parent": "root", "x": 12.9, "y": 328.54, "scaleX": 2.0334, "scaleY": 2.0334 },
+ { "name": "flare1", "parent": "portal-root", "x": -6.34, "y": -161.57, "icon": "particles" },
+ { "name": "flare10", "parent": "portal-root", "x": -6.34, "y": -161.57, "icon": "particles" },
+ { "name": "flare2", "parent": "portal-root", "x": -6.34, "y": -161.57, "icon": "particles" },
+ { "name": "flare3", "parent": "portal-root", "x": -6.34, "y": -161.57, "icon": "particles" },
+ { "name": "flare4", "parent": "portal-root", "x": -6.34, "y": -161.57, "icon": "particles" },
+ { "name": "flare5", "parent": "portal-root", "x": -6.34, "y": -161.57, "icon": "particles" },
+ { "name": "flare6", "parent": "portal-root", "x": -6.34, "y": -161.57, "icon": "particles" },
+ { "name": "flare7", "parent": "portal-root", "x": -6.34, "y": -161.57, "icon": "particles" },
+ { "name": "flare8", "parent": "portal-root", "x": -6.34, "y": -161.57, "icon": "particles" },
+ { "name": "flare9", "parent": "portal-root", "x": -6.34, "y": -161.57, "icon": "particles" },
+ {
+ "name": "torso",
+ "parent": "hip",
+ "length": 42.52,
+ "rotation": 103.82,
+ "x": -1.62,
+ "y": 4.9,
+ "color": "e0da19ff"
+ },
+ { "name": "torso2", "parent": "torso", "length": 42.52, "x": 42.52, "color": "e0da19ff" },
+ { "name": "torso3", "parent": "torso2", "length": 42.52, "x": 42.52, "color": "e0da19ff" },
+ { "name": "front-shoulder", "parent": "torso3", "rotation": 255.89, "x": 18.72, "y": 19.33, "color": "00ff04ff" },
+ { "name": "front-upper-arm", "parent": "front-shoulder", "length": 69.45, "rotation": -87.51, "color": "00ff04ff" },
+ {
+ "name": "front-bracer",
+ "parent": "front-upper-arm",
+ "length": 40.57,
+ "rotation": 18.3,
+ "x": 68.8,
+ "y": -0.68,
+ "color": "00ff04ff"
+ },
+ {
+ "name": "front-fist",
+ "parent": "front-bracer",
+ "length": 65.39,
+ "rotation": 12.43,
+ "x": 40.57,
+ "y": 0.2,
+ "color": "00ff04ff"
+ },
+ { "name": "front-foot-target", "parent": "root", "x": -13.53, "y": 0.04, "color": "ff3f00ff", "icon": "ik" },
+ { "name": "front-leg-target", "parent": "front-foot-target", "x": -28.4, "y": 29.06, "color": "ff3f00ff", "icon": "ik" },
+ {
+ "name": "front-thigh",
+ "parent": "hip",
+ "length": 74.81,
+ "rotation": -95.51,
+ "x": -17.46,
+ "y": -11.64,
+ "color": "00ff04ff"
+ },
+ {
+ "name": "front-shin",
+ "parent": "front-thigh",
+ "length": 128.77,
+ "rotation": -2.21,
+ "x": 78.69,
+ "y": 1.6,
+ "color": "00ff04ff"
+ },
+ {
+ "name": "front-foot",
+ "parent": "front-shin",
+ "length": 41.01,
+ "rotation": 51.27,
+ "x": 128.76,
+ "y": -0.34,
+ "color": "00ff04ff"
+ },
+ {
+ "name": "front-foot-tip",
+ "parent": "front-foot",
+ "length": 56.03,
+ "rotation": -1.68,
+ "x": 41.42,
+ "y": -0.09,
+ "inherit": "noRotationOrReflection",
+ "color": "00ff04ff"
+ },
+ { "name": "back-shoulder", "parent": "torso3", "rotation": -104.11, "x": 7.32, "y": -19.22, "color": "ff000dff" },
+ { "name": "rear-upper-arm", "parent": "back-shoulder", "length": 51.94, "rotation": -65.45, "color": "ff000dff" },
+ { "name": "rear-bracer", "parent": "rear-upper-arm", "length": 34.56, "rotation": 23.15, "x": 51.36, "color": "ff000dff" },
+ {
+ "name": "gun",
+ "parent": "rear-bracer",
+ "length": 43.11,
+ "rotation": -5.43,
+ "x": 34.42,
+ "y": -0.45,
+ "color": "ff000dff"
+ },
+ { "name": "gun-tip", "parent": "gun", "rotation": 7.1, "x": 200.78, "y": 52.5, "color": "ff0000ff" },
+ {
+ "name": "neck",
+ "parent": "torso3",
+ "length": 25.45,
+ "rotation": -31.54,
+ "x": 42.46,
+ "y": -0.31,
+ "color": "e0da19ff"
+ },
+ {
+ "name": "head",
+ "parent": "neck",
+ "length": 131.79,
+ "rotation": 26.1,
+ "x": 27.66,
+ "y": -0.26,
+ "color": "e0da19ff"
+ },
+ {
+ "name": "hair1",
+ "parent": "head",
+ "length": 47.23,
+ "rotation": -49.1,
+ "x": 149.83,
+ "y": -59.77,
+ "color": "e0da19ff"
+ },
+ {
+ "name": "hair2",
+ "parent": "hair1",
+ "length": 55.57,
+ "rotation": 50.42,
+ "x": 47.23,
+ "y": 0.19,
+ "color": "e0da19ff"
+ },
+ {
+ "name": "hair3",
+ "parent": "head",
+ "length": 62.22,
+ "rotation": -32.17,
+ "x": 164.14,
+ "y": 3.68,
+ "color": "e0da19ff"
+ },
+ {
+ "name": "hair4",
+ "parent": "hair3",
+ "length": 80.28,
+ "rotation": 83.71,
+ "x": 62.22,
+ "y": -0.04,
+ "color": "e0da19ff"
+ },
+ { "name": "hoverboard-thruster-front", "parent": "hoverboard-controller", "rotation": -29.2, "x": 95.77, "y": -2.99, "inherit": "noRotationOrReflection" },
+ { "name": "hoverboard-thruster-rear", "parent": "hoverboard-controller", "rotation": -29.2, "x": -76.47, "y": -4.88, "inherit": "noRotationOrReflection" },
+ { "name": "hoverglow-front", "parent": "hoverboard-thruster-front", "rotation": 0.17, "x": -1.78, "y": -37.79 },
+ { "name": "hoverglow-rear", "parent": "hoverboard-thruster-rear", "rotation": 0.17, "x": 1.06, "y": -35.66 },
+ {
+ "name": "muzzle",
+ "parent": "rear-bracer",
+ "rotation": 3.06,
+ "x": 242.34,
+ "y": 34.26,
+ "color": "ffb900ff",
+ "icon": "muzzleFlash"
+ },
+ { "name": "muzzle-ring", "parent": "muzzle", "color": "ffb900ff" },
+ { "name": "muzzle-ring2", "parent": "muzzle", "color": "ffb900ff" },
+ { "name": "muzzle-ring3", "parent": "muzzle", "color": "ffb900ff" },
+ { "name": "muzzle-ring4", "parent": "muzzle", "color": "ffb900ff" },
+ { "name": "portal", "parent": "portal-root" },
+ { "name": "portal-shade", "parent": "portal-root" },
+ { "name": "portal-streaks1", "parent": "portal-root" },
+ { "name": "portal-streaks2", "parent": "portal-root" },
+ { "name": "side-glow1", "parent": "hoverboard-controller", "x": -110.56, "y": 2.62, "color": "000effff" },
+ {
+ "name": "side-glow2",
+ "parent": "hoverboard-controller",
+ "x": -110.56,
+ "y": 2.62,
+ "scaleX": 0.738,
+ "scaleY": 0.738,
+ "color": "000effff"
+ },
+ { "name": "head-control", "parent": "head", "x": 110.21, "color": "00a220ff", "icon": "arrows" }
+ ],
+ "slots": [
+ { "name": "portal-bg", "bone": "portal" },
+ { "name": "portal-shade", "bone": "portal-shade" },
+ { "name": "portal-streaks2", "bone": "portal-streaks2", "blend": "additive" },
+ { "name": "portal-streaks1", "bone": "portal-streaks1", "blend": "additive" },
+ { "name": "portal-flare8", "bone": "flare8", "color": "c3cbffff", "blend": "additive" },
+ { "name": "portal-flare9", "bone": "flare9", "color": "c3cbffff", "blend": "additive" },
+ { "name": "portal-flare10", "bone": "flare10", "color": "c3cbffff", "blend": "additive" },
+ { "name": "clipping", "bone": "clipping" },
+ { "name": "exhaust3", "bone": "exhaust3", "color": "5eb4ffff", "blend": "additive" },
+ { "name": "hoverboard-thruster-rear", "bone": "hoverboard-thruster-rear" },
+ { "name": "hoverboard-thruster-front", "bone": "hoverboard-thruster-front" },
+ { "name": "hoverboard-board", "bone": "hoverboard-controller" },
+ { "name": "side-glow1", "bone": "side-glow1", "color": "ff8686ff", "blend": "additive" },
+ { "name": "side-glow3", "bone": "side-glow1", "color": "ff8686ff", "blend": "additive" },
+ { "name": "side-glow2", "bone": "side-glow2", "color": "ff8686ff", "blend": "additive" },
+ { "name": "hoverglow-front", "bone": "hoverglow-front", "color": "5eb4ffff", "blend": "additive" },
+ { "name": "hoverglow-rear", "bone": "hoverglow-rear", "color": "5eb4ffff", "blend": "additive" },
+ { "name": "exhaust1", "bone": "exhaust2", "color": "5eb4ffff", "blend": "additive" },
+ { "name": "exhaust2", "bone": "exhaust1", "color": "5eb4ffff", "blend": "additive" },
+ { "name": "rear-upper-arm", "bone": "rear-upper-arm", "attachment": "rear-upper-arm" },
+ { "name": "rear-bracer", "bone": "rear-bracer", "attachment": "rear-bracer" },
+ { "name": "gun", "bone": "gun", "attachment": "gun" },
+ { "name": "rear-foot", "bone": "rear-foot", "attachment": "rear-foot" },
+ { "name": "rear-thigh", "bone": "rear-thigh", "attachment": "rear-thigh" },
+ { "name": "rear-shin", "bone": "rear-shin", "attachment": "rear-shin" },
+ { "name": "neck", "bone": "neck", "attachment": "neck" },
+ { "name": "torso", "bone": "torso", "attachment": "torso" },
+ { "name": "front-upper-arm", "bone": "front-upper-arm", "attachment": "front-upper-arm" },
+ { "name": "head", "bone": "head", "attachment": "head" },
+ { "name": "eye", "bone": "head", "attachment": "eye-indifferent" },
+ { "name": "front-thigh", "bone": "front-thigh", "attachment": "front-thigh" },
+ { "name": "front-foot", "bone": "front-foot", "attachment": "front-foot" },
+ { "name": "front-shin", "bone": "front-shin", "attachment": "front-shin" },
+ { "name": "mouth", "bone": "head", "attachment": "mouth-smile" },
+ { "name": "goggles", "bone": "head", "attachment": "goggles" },
+ { "name": "front-bracer", "bone": "front-bracer", "attachment": "front-bracer" },
+ { "name": "front-fist", "bone": "front-fist", "attachment": "front-fist-closed" },
+ { "name": "muzzle", "bone": "muzzle" },
+ { "name": "head-bb", "bone": "head" },
+ { "name": "portal-flare1", "bone": "flare1", "color": "c3cbffff", "blend": "additive" },
+ { "name": "portal-flare2", "bone": "flare2", "color": "c3cbffff", "blend": "additive" },
+ { "name": "portal-flare3", "bone": "flare3", "color": "c3cbffff", "blend": "additive" },
+ { "name": "portal-flare4", "bone": "flare4", "color": "c3cbffff", "blend": "additive" },
+ { "name": "portal-flare5", "bone": "flare5", "color": "c3cbffff", "blend": "additive" },
+ { "name": "portal-flare6", "bone": "flare6", "color": "c3cbffff", "blend": "additive" },
+ { "name": "portal-flare7", "bone": "flare7", "color": "c3cbffff", "blend": "additive" },
+ { "name": "crosshair", "bone": "crosshair" },
+ { "name": "muzzle-glow", "bone": "gun-tip", "color": "ffffff00", "blend": "additive" },
+ { "name": "muzzle-ring", "bone": "muzzle-ring", "color": "d8baffff", "blend": "additive" },
+ { "name": "muzzle-ring2", "bone": "muzzle-ring2", "color": "d8baffff", "blend": "additive" },
+ { "name": "muzzle-ring3", "bone": "muzzle-ring3", "color": "d8baffff", "blend": "additive" },
+ { "name": "muzzle-ring4", "bone": "muzzle-ring4", "color": "d8baffff", "blend": "additive" }
+ ],
+ "ik": [
+ {
+ "name": "aim-ik",
+ "order": 13,
+ "bones": [ "rear-upper-arm" ],
+ "target": "crosshair",
+ "mix": 0
+ },
+ {
+ "name": "aim-torso-ik",
+ "order": 8,
+ "bones": [ "aim-constraint-target" ],
+ "target": "crosshair"
+ },
+ {
+ "name": "board-ik",
+ "order": 1,
+ "bones": [ "hoverboard-controller" ],
+ "target": "board-ik"
+ },
+ {
+ "name": "front-foot-ik",
+ "order": 6,
+ "bones": [ "front-foot" ],
+ "target": "front-foot-target"
+ },
+ {
+ "name": "front-leg-ik",
+ "order": 4,
+ "bones": [ "front-thigh", "front-shin" ],
+ "target": "front-leg-target",
+ "bendPositive": false
+ },
+ {
+ "name": "rear-foot-ik",
+ "order": 7,
+ "bones": [ "rear-foot" ],
+ "target": "rear-foot-target"
+ },
+ {
+ "name": "rear-leg-ik",
+ "order": 5,
+ "bones": [ "rear-thigh", "rear-shin" ],
+ "target": "rear-leg-target",
+ "bendPositive": false
+ }
+ ],
+ "transform": [
+ {
+ "name": "aim-front-arm-transform",
+ "order": 11,
+ "bones": [ "front-upper-arm" ],
+ "target": "aim-constraint-target",
+ "rotation": -180,
+ "mixRotate": 0,
+ "mixX": 0,
+ "mixScaleX": 0,
+ "mixShearY": 0
+ },
+ {
+ "name": "aim-head-transform",
+ "order": 10,
+ "bones": [ "head" ],
+ "target": "aim-constraint-target",
+ "rotation": 84.3,
+ "mixRotate": 0,
+ "mixX": 0,
+ "mixScaleX": 0,
+ "mixShearY": 0
+ },
+ {
+ "name": "aim-rear-arm-transform",
+ "order": 12,
+ "bones": [ "rear-upper-arm" ],
+ "target": "aim-constraint-target",
+ "x": 57.7,
+ "y": 56.4,
+ "mixRotate": 0,
+ "mixX": 0,
+ "mixScaleX": 0,
+ "mixShearY": 0
+ },
+ {
+ "name": "aim-torso-transform",
+ "order": 9,
+ "bones": [ "torso" ],
+ "target": "aim-constraint-target",
+ "rotation": 69.5,
+ "shearY": -36,
+ "mixRotate": 0,
+ "mixX": 0,
+ "mixScaleX": 0,
+ "mixShearY": 0
+ },
+ {
+ "name": "front-foot-board-transform",
+ "order": 2,
+ "bones": [ "front-foot-target" ],
+ "target": "hoverboard-controller",
+ "x": -69.8,
+ "y": 20.7,
+ "mixRotate": 0,
+ "mixX": 0,
+ "mixScaleX": 0,
+ "mixShearY": 0
+ },
+ {
+ "name": "rear-foot-board-transform",
+ "order": 3,
+ "bones": [ "rear-foot-target" ],
+ "target": "hoverboard-controller",
+ "x": 86.6,
+ "y": 21.3,
+ "mixRotate": 0,
+ "mixX": 0,
+ "mixScaleX": 0,
+ "mixShearY": 0
+ },
+ {
+ "name": "shoulder",
+ "bones": [ "back-shoulder" ],
+ "target": "front-shoulder",
+ "x": 40.17,
+ "y": -1.66,
+ "mixRotate": 0,
+ "mixX": -1,
+ "mixScaleX": 0,
+ "mixShearY": 0
+ },
+ {
+ "name": "toes-board",
+ "order": 14,
+ "bones": [ "front-foot-tip", "back-foot-tip" ],
+ "target": "hoverboard-controller",
+ "mixRotate": 0,
+ "mixX": 0,
+ "mixScaleX": 0,
+ "mixShearY": 0
+ }
+ ],
+ "skins": [
+ {
+ "name": "default",
+ "attachments": {
+ "clipping": {
+ "clipping": {
+ "type": "clipping",
+ "end": "head-bb",
+ "vertexCount": 9,
+ "vertices": [ 66.76, 509.48, 19.98, 434.54, 5.34, 336.28, 22.19, 247.93, 77.98, 159.54, 182.21, -97.56, 1452.26, -99.8, 1454.33, 843.61, 166.57, 841.02 ],
+ "color": "ce3a3aff"
+ }
+ },
+ "crosshair": {
+ "crosshair": { "width": 89, "height": 89 }
+ },
+ "exhaust1": {
+ "hoverglow-small": { "scaleX": 0.4629, "scaleY": 0.8129, "rotation": -83.07, "width": 274, "height": 75 }
+ },
+ "exhaust2": {
+ "hoverglow-small": {
+ "x": 0.01,
+ "y": -0.76,
+ "scaleX": 0.4208,
+ "scaleY": 0.8403,
+ "rotation": -89.25,
+ "width": 274,
+ "height": 75
+ }
+ },
+ "exhaust3": {
+ "hoverglow-small": { "scaleX": 0.4629, "scaleY": 0.8129, "rotation": -83.07, "width": 274, "height": 75 }
+ },
+ "eye": {
+ "eye-indifferent": {
+ "type": "mesh",
+ "uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ],
+ "triangles": [ 1, 3, 0, 1, 2, 3 ],
+ "vertices": [ 2, 66, -36.8, -91.35, 0.3, 46, 73.41, -91.35, 0.7, 2, 66, -87.05, -13.11, 0.70968, 46, 23.16, -13.11, 0.29032, 2, 66, -12.18, 34.99, 0.82818, 46, 98.03, 34.99, 0.17182, 2, 66, 38.07, -43.25, 0.59781, 46, 148.28, -43.25, 0.40219 ],
+ "hull": 4,
+ "edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ],
+ "width": 93,
+ "height": 89
+ },
+ "eye-surprised": {
+ "type": "mesh",
+ "uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ],
+ "triangles": [ 1, 2, 3, 1, 3, 0 ],
+ "vertices": [ 2, 66, -46.74, -89.7, 0.3, 46, 63.47, -89.7, 0.7, 2, 66, -77.58, -1.97, 0.71, 46, 32.63, -1.97, 0.29, 2, 66, 6.38, 27.55, 0.83, 46, 116.59, 27.55, 0.17, 2, 66, 37.22, -60.19, 0.6, 46, 147.44, -60.19, 0.4 ],
+ "hull": 4,
+ "edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ],
+ "width": 93,
+ "height": 89
+ }
+ },
+ "front-bracer": {
+ "front-bracer": { "x": 12.03, "y": -1.68, "rotation": 79.6, "width": 58, "height": 80 }
+ },
+ "front-fist": {
+ "front-fist-closed": { "x": 35.5, "y": 6, "rotation": 67.16, "width": 75, "height": 82 },
+ "front-fist-open": { "x": 39.57, "y": 7.76, "rotation": 67.16, "width": 86, "height": 87 }
+ },
+ "front-foot": {
+ "front-foot": {
+ "type": "mesh",
+ "uvs": [ 0.59417, 0.23422, 0.62257, 0.30336, 0.6501, 0.37036, 0.67637, 0.38404, 0.72068, 0.4071, 0.76264, 0.42894, 1, 0.70375, 1, 1, 0.65517, 1, 0.46923, 0.99999, 0, 1, 0, 0.39197, 0.17846, 0, 0.49796, 0 ],
+ "triangles": [ 8, 9, 3, 4, 8, 3, 5, 8, 4, 6, 8, 5, 8, 6, 7, 11, 1, 10, 0, 12, 13, 0, 11, 12, 0, 1, 11, 9, 2, 3, 1, 2, 10, 9, 10, 2 ],
+ "vertices": [ 2, 38, 18.17, 41.57, 0.7896, 39, 12.46, 46.05, 0.2104, 2, 38, 24.08, 40.76, 0.71228, 39, 16.12, 41.34, 0.28772, 2, 38, 29.81, 39.98, 0.55344, 39, 19.67, 36.78, 0.44656, 2, 38, 32.81, 41.67, 0.38554, 39, 23, 35.89, 0.61446, 2, 38, 37.86, 44.52, 0.25567, 39, 28.61, 34.4, 0.74433, 2, 38, 42.65, 47.22, 0.17384, 39, 33.92, 32.99, 0.82616, 1, 39, 64.15, 14.56, 1, 1, 39, 64.51, -5.87, 1, 1, 39, 21.08, -6.64, 1, 2, 38, 44.67, -6.77, 0.5684, 39, -2.34, -6.97, 0.4316, 1, 38, 3.1, -48.81, 1, 1, 38, -26.73, -19.31, 1, 1, 38, -30.15, 15.69, 1, 1, 38, -1.84, 44.32, 1 ],
+ "hull": 14,
+ "edges": [ 14, 16, 16, 18, 18, 20, 4, 18, 20, 22, 24, 26, 22, 24, 12, 14, 10, 12, 2, 4, 2, 20, 4, 6, 6, 16, 2, 0, 0, 26, 6, 8, 8, 10 ],
+ "width": 126,
+ "height": 69
+ }
+ },
+ "front-shin": {
+ "front-shin": {
+ "type": "mesh",
+ "uvs": [ 0.90031, 0.05785, 1, 0.12828, 1, 0.21619, 0.9025, 0.31002, 0.78736, 0.35684, 0.78081, 0.39874, 0.77215, 0.45415, 0.77098, 0.51572, 0.84094, 0.63751, 0.93095, 0.7491, 0.95531, 0.7793, 0.78126, 0.87679, 0.5613, 1, 0.2687, 1, 0, 1, 0.00279, 0.96112, 0.01358, 0.81038, 0.02822, 0.60605, 0.08324, 0.45142, 0.18908, 0.31882, 0.29577, 0.2398, 0.30236, 0.14941, 0.37875, 0.05902, 0.53284, 0, 0.70538, 0, 0.41094, 0.71968, 0.40743, 0.54751, 0.41094, 0.4536, 0.4724, 0.35186, 0.33367, 0.27829, 0.50226, 0.31664, 0.65328, 0.67507, 0.60762, 0.52716, 0.6006, 0.45125, 0.62747, 0.37543, 0.6573, 0.3385, 0.27843, 0.32924, 0.18967, 0.45203, 0.16509, 0.58586, 0.18265, 0.7682, 0.50532, 0.24634, 0.59473, 0.17967, 0.60161, 0.10611, 0.51392, 0.04327, 0.72198, 0.28849, 0.82343, 0.20266, 0.86814, 0.11377, 0.79592, 0.04634, 0.44858, 0.15515, 0.25466, 0.96219, 0.53169, 0.9448, 0.7531, 0.8324 ],
+ "triangles": [ 24, 0, 47, 43, 23, 24, 47, 43, 24, 43, 22, 23, 42, 43, 47, 46, 47, 0, 42, 47, 46, 46, 0, 1, 48, 22, 43, 48, 43, 42, 21, 22, 48, 41, 48, 42, 45, 42, 46, 41, 42, 45, 46, 1, 2, 45, 46, 2, 40, 48, 41, 48, 20, 21, 29, 48, 40, 29, 20, 48, 44, 41, 45, 40, 41, 44, 3, 45, 2, 44, 45, 3, 30, 29, 40, 35, 30, 40, 36, 19, 20, 36, 20, 29, 44, 35, 40, 28, 29, 30, 4, 44, 3, 35, 44, 4, 34, 30, 35, 5, 35, 4, 34, 28, 30, 33, 28, 34, 37, 19, 36, 18, 19, 37, 27, 29, 28, 27, 28, 33, 36, 29, 27, 37, 36, 27, 5, 34, 35, 6, 34, 5, 33, 34, 6, 6, 32, 33, 7, 32, 6, 26, 37, 27, 38, 18, 37, 38, 37, 26, 17, 18, 38, 31, 32, 7, 31, 7, 8, 32, 25, 26, 38, 26, 25, 27, 33, 32, 32, 26, 27, 39, 38, 25, 17, 38, 39, 16, 17, 39, 51, 31, 8, 51, 8, 9, 11, 51, 9, 11, 9, 10, 31, 50, 25, 31, 25, 32, 50, 31, 51, 49, 39, 25, 49, 25, 50, 15, 16, 39, 49, 15, 39, 13, 49, 50, 14, 15, 49, 13, 14, 49, 12, 50, 51, 12, 51, 11, 13, 50, 12 ],
+ "vertices": [ -23.66, 19.37, -11.73, 28.98, 4.34, 30.83, 22.41, 24.87, 32.05, 16.48, 39.77, 16.83, 49.98, 17.3, 61.25, 18.5, 82.85, 26.78, 102.4, 36.46, 107.69, 39.09, 127.15, 26.97, 151.74, 11.65, 154.49, -12.18, 157.02, -34.07, 149.89, -34.66, 122.23, -36.97, 84.75, -40.09, 55.97, -38.88, 30.73, -33.05, 15.29, -26.03, -1.3, -27.41, -18.54, -23.09, -30.78, -11.79, -32.4, 2.27, 101.92, -6.52, 70.48, -10.44, 53.28, -12.14, 34.11, -9.28, 21.96, -22.13, 27.39, -7.59, 91.48, 12.28, 64.88, 5.44, 51.07, 3.26, 36.95, 3.85, 29.92, 5.5, 31.8, -25.56, 55.08, -30.19, 79.77, -29.37, 112.93, -24.09, 14.51, -8.83, 1.48, -2.95, -12.03, -3.94, -22.69, -12.41, 20.17, 9.71, 3.53, 16.16, -13.14, 17.93, -24.78, 10.62, -1.62, -15.37, 147.71, -14.13, 141.93, 8.07, 119.3, 23.74 ],
+ "hull": 25,
+ "edges": [ 8, 6, 6, 4, 4, 2, 2, 0, 0, 48, 46, 48, 46, 44, 44, 42, 42, 40, 40, 38, 38, 36, 36, 34, 32, 34, 50, 52, 52, 54, 54, 56, 40, 58, 58, 60, 8, 10, 20, 22, 22, 24, 62, 64, 64, 66, 66, 68, 8, 70, 70, 60, 68, 70, 58, 72, 72, 74, 74, 76, 76, 78, 24, 26, 26, 28, 58, 80, 80, 82, 82, 84, 84, 86, 86, 44, 70, 88, 88, 90, 90, 92, 92, 94, 94, 48, 80, 88, 88, 6, 82, 90, 90, 4, 84, 92, 92, 2, 86, 94, 94, 0, 56, 60, 10, 12, 12, 14, 14, 16, 28, 30, 30, 32, 26, 98, 98, 78, 30, 98, 24, 100, 100, 50, 98, 100, 22, 102, 102, 62, 100, 102, 16, 18, 18, 20, 102, 18 ],
+ "width": 82,
+ "height": 184
+ }
+ },
+ "front-thigh": {
+ "front-thigh": { "x": 42.48, "y": 4.45, "rotation": 84.87, "width": 45, "height": 112 }
+ },
+ "front-upper-arm": {
+ "front-upper-arm": { "x": 28.31, "y": 7.37, "rotation": 97.9, "width": 46, "height": 97 }
+ },
+ "goggles": {
+ "goggles": {
+ "type": "mesh",
+ "uvs": [ 0.53653, 0.04114, 0.72922, 0.16036, 0.91667, 0.33223, 0.97046, 0.31329, 1, 0.48053, 0.95756, 0.5733, 0.88825, 0.6328, 0.86878, 0.78962, 0.77404, 0.8675, 0.72628, 1, 0.60714, 0.93863, 0.49601, 0.88138, 0.41558, 0.75027, 0.32547, 0.70084, 0.2782, 0.58257, 0.1721, 0.63281, 0.17229, 0.75071, 0.10781, 0.79898, 0, 0.32304, 0, 0.12476, 0.07373, 0.07344, 0.15423, 0.10734, 0.23165, 0.13994, 0.30313, 0.02256, 0.34802, 0, 0.42979, 0.69183, 0.39476, 0.51042, 0.39488, 0.31512, 0.45878, 0.23198, 0.56501, 0.28109, 0.69961, 0.39216, 0.82039, 0.54204, 0.85738, 0.62343, 0.91107, 0.51407, 0.72639, 0.32147, 0.58764, 0.19609, 0.48075, 0.11269, 0.37823, 0.05501, 0.3287, 0.17866, 0.319, 0.305, 0.36036, 0.53799, 0.40327, 0.70072, 0.30059, 0.55838, 0.21957, 0.2815, 0.09963, 0.28943, 0.56863, 0.4368, 0.4911, 0.37156, 0.51185, 0.52093, 0.67018, 0.59304, 0.7619, 0.68575, 0.73296, 0.43355 ],
+ "triangles": [ 18, 44, 15, 21, 19, 20, 17, 18, 15, 44, 19, 21, 2, 3, 4, 18, 19, 44, 2, 33, 34, 33, 2, 4, 5, 33, 4, 5, 6, 33, 7, 32, 6, 31, 50, 33, 32, 31, 33, 6, 32, 33, 31, 49, 50, 49, 31, 32, 49, 32, 7, 8, 49, 7, 33, 50, 34, 17, 15, 16, 9, 48, 8, 49, 48, 50, 50, 48, 45, 47, 45, 48, 50, 45, 30, 45, 47, 46, 45, 46, 29, 30, 45, 29, 30, 29, 34, 30, 34, 50, 47, 26, 46, 25, 10, 11, 12, 25, 11, 41, 12, 42, 42, 44, 43, 43, 21, 22, 41, 40, 25, 41, 42, 40, 29, 35, 34, 40, 26, 25, 25, 26, 47, 37, 24, 0, 36, 37, 0, 42, 43, 39, 42, 39, 40, 28, 38, 36, 40, 39, 26, 28, 27, 38, 26, 39, 27, 37, 38, 23, 39, 43, 38, 38, 37, 36, 27, 39, 38, 43, 22, 38, 37, 23, 24, 22, 23, 38, 36, 0, 35, 28, 36, 35, 29, 28, 35, 27, 28, 46, 26, 27, 46, 35, 0, 1, 34, 35, 1, 12, 41, 25, 47, 10, 25, 44, 21, 43, 42, 14, 44, 14, 15, 44, 13, 14, 42, 12, 13, 42, 46, 28, 29, 47, 48, 10, 48, 9, 10, 49, 8, 48, 2, 34, 1 ],
+ "vertices": [ 2, 66, 61.88, 22.81, 0.832, 46, 172.09, 22.81, 0.168, 2, 66, 59.89, -31.19, 0.6855, 46, 170.1, -31.19, 0.3145, 2, 66, 49.2, -86.8, 0.32635, 46, 159.41, -86.8, 0.67365, 2, 66, 56.82, -99.01, 0.01217, 46, 167.03, -99.01, 0.98783, 1, 46, 143.4, -115.48, 1, 2, 66, 15, -110.14, 0.0041, 46, 125.21, -110.14, 0.9959, 2, 66, -0.32, -96.36, 0.07948, 46, 109.89, -96.36, 0.92052, 2, 66, -26.56, -100.19, 0.01905, 46, 83.65, -100.19, 0.98095, 2, 66, -46.96, -81.16, 0.4921, 46, 63.26, -81.16, 0.50791, 2, 66, -71.84, -76.69, 0.56923, 46, 38.37, -76.69, 0.43077, 2, 66, -72.54, -43.98, 0.74145, 46, 37.67, -43.98, 0.25855, 2, 66, -73.2, -13.47, 0.87929, 46, 37.01, -13.47, 0.12071, 2, 66, -59.63, 13.55, 0.864, 46, 50.58, 13.55, 0.136, 2, 66, -59.69, 38.45, 0.85289, 46, 50.52, 38.45, 0.14711, 2, 66, -45.26, 56.6, 0.74392, 46, 64.95, 56.6, 0.25608, 2, 66, -62.31, 79.96, 0.624, 46, 47.9, 79.96, 0.376, 2, 66, -80.76, 73.42, 0.616, 46, 29.45, 73.42, 0.384, 2, 66, -93.9, 86.64, 0.288, 46, 16.31, 86.64, 0.712, 1, 46, 81.51, 139.38, 1, 1, 46, 112.56, 150.3, 1, 2, 66, 16.76, 134.97, 0.02942, 46, 126.97, 134.97, 0.97058, 2, 66, 18.42, 113.28, 0.36147, 46, 128.63, 113.28, 0.63853, 2, 66, 20.02, 92.43, 0.7135, 46, 130.23, 92.43, 0.2865, 2, 66, 44.58, 81.29, 0.69603, 46, 154.79, 81.29, 0.30397, 2, 66, 52, 71.48, 0.848, 46, 162.21, 71.48, 0.152, 2, 66, -49.25, 13.27, 0.8, 46, 60.96, 13.27, 0.2, 2, 66, -23.88, 31.88, 0.896, 46, 86.33, 31.88, 0.104, 2, 66, 6.72, 42.6, 0.928, 46, 116.93, 42.6, 0.072, 2, 66, 25.26, 31.44, 0.8, 46, 135.47, 31.44, 0.2, 2, 66, 26.77, 2.59, 0.75, 46, 136.98, 2.59, 0.25, 2, 66, 21.02, -36.66, 0.54887, 46, 131.23, -36.66, 0.45113, 2, 66, 8.01, -74.65, 0.36029, 46, 118.22, -74.65, 0.63971, 2, 66, -1.52, -88.24, 0.1253, 46, 108.69, -88.24, 0.8747, 2, 66, 20.25, -95.44, 0.08687, 46, 130.46, -95.44, 0.91313, 2, 66, 34.42, -39.36, 0.72613, 46, 144.63, -39.36, 0.27387, 2, 66, 42.03, 1.7, 0.824, 46, 152.25, 1.7, 0.176, 2, 66, 45.85, 32.6, 0.856, 46, 156.06, 32.6, 0.144, 1, 66, 46.01, 61.02, 1, 1, 66, 22.35, 66.41, 1, 1, 66, 1.73, 61.84, 1, 2, 66, -31.17, 38.83, 0.928, 46, 79.04, 38.83, 0.072, 2, 66, -52.94, 19.31, 0.79073, 46, 57.27, 19.31, 0.20927, 2, 66, -39.54, 52.42, 0.912, 46, 70.67, 52.42, 0.088, 2, 66, -3.2, 87.61, 0.744, 46, 107.02, 87.61, 0.256, 2, 66, -14.82, 116.7, 0.6368, 46, 95.4, 116.7, 0.3632, 2, 66, 2.7, -6.87, 0.856, 46, 112.91, -6.87, 0.144, 2, 66, 6.21, 15.8, 0.744, 46, 116.42, 15.8, 0.256, 2, 66, -15.39, 2.47, 0.856, 46, 94.82, 2.47, 0.144, 2, 66, -12.98, -40.48, 0.72102, 46, 97.24, -40.48, 0.27898, 2, 66, -19.55, -68.16, 0.59162, 46, 90.66, -68.16, 0.40838, 2, 66, 17.44, -47.15, 0.53452, 46, 127.65, -47.15, 0.46548 ],
+ "hull": 25,
+ "edges": [ 36, 34, 34, 32, 32, 30, 30, 28, 28, 26, 26, 24, 24, 22, 18, 16, 16, 14, 14, 12, 12, 10, 10, 8, 8, 6, 6, 4, 4, 2, 2, 0, 0, 48, 48, 46, 46, 44, 36, 38, 40, 38, 24, 50, 50, 52, 52, 54, 54, 56, 56, 58, 58, 60, 62, 64, 64, 12, 8, 66, 66, 68, 68, 70, 70, 72, 72, 74, 74, 76, 76, 78, 78, 80, 80, 82, 82, 24, 24, 84, 84, 86, 86, 44, 40, 42, 42, 44, 42, 88, 88, 30, 58, 90, 90, 92, 92, 94, 18, 20, 20, 22, 94, 20, 18, 96, 96, 98, 60, 100, 100, 62, 98, 100 ],
+ "width": 261,
+ "height": 166
+ }
+ },
+ "gun": {
+ "gun": { "x": 77.3, "y": 16.4, "rotation": 60.83, "width": 210, "height": 203 }
+ },
+ "head": {
+ "head": {
+ "type": "mesh",
+ "uvs": [ 0.75919, 0.06107, 0.88392, 0.17893, 0.90174, 0.30856, 0.94224, 0.1966, 1, 0.26584, 1, 0.422, 0.95864, 0.46993, 0.92118, 0.51333, 0.85957, 0.5347, 0.78388, 0.65605, 0.74384, 0.74838, 0.85116, 0.75151, 0.84828, 0.82564, 0.81781, 0.85367, 0.75599, 0.85906, 0.76237, 0.90468, 0.65875, 1, 0.38337, 1, 0.1858, 0.85404, 0.12742, 0.81091, 0.06025, 0.69209, 0, 0.58552, 0, 0.41021, 0.0853, 0.20692, 0.24243, 0.14504, 0.5, 0.1421, 0.50324, 0.07433, 0.41738, 0, 0.57614, 0, 0.85059, 0.36087, 0.73431, 0.43206, 0.68481, 0.31271, 0.72165, 0.16718, 0.55931, 0.04154, 0.44764, 0.22895, 0.23926, 0.26559, 0.71272, 0.44036, 0.56993, 0.383, 0.41678, 0.33511, 0.293, 0.31497, 0.70802, 0.44502, 0.56676, 0.38976, 0.41521, 0.34416, 0.28754, 0.33017, 0.88988, 0.50177, 0.30389, 0.73463, 0.2646, 0.65675, 0.21414, 0.61584, 0.14613, 0.62194, 0.10316, 0.66636, 0.10358, 0.72557, 0.14505, 0.79164, 0.20263, 0.81355, 0.27873, 0.80159, 0.34947, 0.7376, 0.23073, 0.57073, 0.08878, 0.60707, 0.29461, 0.8129, 0.73006, 0.87883, 0.69805, 0.87348, 0.66166, 0.79681, 0.22468, 0.69824, 0.14552, 0.67405 ],
+ "triangles": [ 50, 49, 62, 34, 25, 31, 39, 35, 34, 38, 39, 34, 37, 38, 34, 42, 39, 38, 43, 39, 42, 32, 2, 31, 31, 37, 34, 42, 38, 37, 41, 42, 37, 43, 22, 39, 30, 31, 29, 36, 37, 31, 30, 36, 31, 40, 41, 37, 36, 40, 37, 36, 30, 44, 55, 22, 43, 55, 48, 56, 47, 48, 55, 46, 55, 54, 42, 55, 43, 47, 55, 46, 62, 49, 48, 61, 47, 46, 62, 48, 47, 61, 62, 47, 46, 54, 45, 42, 41, 55, 61, 46, 45, 55, 41, 54, 61, 51, 50, 61, 50, 62, 60, 41, 40, 54, 41, 60, 53, 61, 45, 52, 51, 61, 57, 53, 45, 57, 45, 54, 53, 52, 61, 52, 19, 51, 57, 18, 52, 57, 52, 53, 17, 54, 60, 57, 54, 17, 18, 57, 17, 19, 50, 51, 33, 27, 28, 26, 27, 33, 0, 33, 28, 32, 33, 0, 32, 0, 1, 33, 25, 26, 33, 32, 25, 31, 25, 32, 2, 32, 1, 2, 3, 4, 29, 31, 2, 2, 4, 5, 29, 2, 5, 6, 29, 5, 30, 29, 6, 44, 30, 6, 18, 19, 52, 49, 56, 48, 34, 24, 25, 35, 23, 24, 35, 24, 34, 39, 22, 35, 22, 23, 35, 7, 44, 6, 8, 36, 44, 40, 36, 8, 8, 44, 7, 56, 21, 22, 55, 56, 22, 9, 40, 8, 20, 21, 56, 20, 56, 49, 9, 60, 40, 10, 60, 9, 20, 50, 19, 12, 10, 11, 13, 10, 12, 14, 60, 10, 13, 14, 10, 59, 60, 14, 58, 59, 14, 58, 14, 15, 16, 17, 60, 59, 16, 60, 15, 16, 59, 15, 59, 58, 20, 49, 50 ],
+ "vertices": [ 2, 50, 41.97, -41.8, 0.94074, 66, 165.41, -22.6, 0.05926, 4, 48, 73.47, 27.54, 0.26795, 50, -5.75, -51.71, 0.4738, 49, 112.99, -11.41, 0.12255, 66, 143.5, -66.13, 0.1357, 4, 48, 38.23, 10.99, 0.6831, 50, -41.01, -35.22, 0.07866, 49, 92.73, -44.66, 0.04872, 66, 108.65, -83.49, 0.18952, 2, 48, 73.35, 10.89, 0.8455, 66, 143.77, -82.78, 0.1545, 2, 48, 58.59, -10.38, 0.91607, 66, 129.5, -104.39, 0.08393, 3, 46, 195.82, -119.82, 0.104, 47, 75.49, -4.55, 0.09191, 48, 14.36, -24.8, 0.80409, 4, 46, 178.62, -113.98, 0.19022, 47, 59.82, -13.72, 0.33409, 48, -2.7, -18.57, 0.46643, 66, 68.41, -113.98, 0.00926, 4, 46, 163.06, -108.69, 0.18724, 47, 45.64, -22.03, 0.3133, 48, -18.14, -12.93, 0.47469, 66, 52.84, -108.69, 0.02477, 2, 46, 151.52, -95.05, 0.91122, 66, 41.31, -95.05, 0.08878, 2, 46, 110.61, -87.69, 0.70564, 66, 0.4, -87.69, 0.29436, 2, 46, 81.05, -86.58, 0.63951, 66, -29.16, -86.58, 0.36049, 2, 46, 89.82, -114.32, 0.57, 66, -20.39, -114.32, 0.43, 2, 46, 68.72, -120.91, 0.57, 66, -41.49, -120.91, 0.43, 2, 46, 58.1, -115.9, 0.57, 66, -52.11, -115.9, 0.43, 2, 46, 51.03, -100.63, 0.64242, 66, -59.18, -100.63, 0.35758, 2, 46, 38.79, -106.76, 0.81659, 66, -71.43, -106.76, 0.18341, 2, 46, 2.68, -89.7, 0.77801, 66, -107.53, -89.7, 0.22199, 2, 46, -22.07, -19.3, 0.823, 66, -132.28, -19.3, 0.177, 2, 46, 1.2, 45.63, 0.51204, 66, -109.01, 45.63, 0.48796, 2, 46, 8.07, 64.81, 0.60869, 66, -102.14, 64.81, 0.39131, 2, 46, 35.44, 93.73, 0.80009, 66, -74.77, 93.73, 0.19991, 2, 46, 59.98, 119.66, 0.93554, 66, -50.23, 119.66, 0.06446, 2, 46, 109.26, 136.99, 0.99895, 66, -0.95, 136.99, 0.00105, 1, 46, 174.07, 135.27, 1, 3, 46, 205.59, 101.22, 0.80778, 49, -16.84, 104.63, 0.15658, 66, 95.38, 101.22, 0.03564, 3, 50, 58.94, 30.5, 0.43491, 49, 38.36, 61.89, 0.28116, 66, 119.35, 35.65, 0.28393, 2, 50, 75.56, 19.01, 0.92164, 66, 138.68, 41.52, 0.07836, 1, 50, 106.7, 26.9, 1, 1, 50, 83.79, -9.51, 1, 5, 47, 44.51, 27.24, 0.15139, 48, 19.12, 19.33, 0.44847, 50, -46.82, -15.19, 0.05757, 49, 72.19, -48.24, 0.1149, 66, 89.35, -75.58, 0.22767, 3, 47, 7.42, 19.08, 0.37772, 49, 34.32, -45.24, 0.09918, 66, 58.9, -52.89, 0.52311, 2, 49, 45.94, -9.07, 0.4826, 66, 87.99, -28.45, 0.5174, 2, 50, 20.62, -16.35, 0.7435, 66, 132.21, -23.49, 0.2565, 2, 50, 75.74, 0.94, 0.97172, 66, 152.95, 30.42, 0.02828, 4, 46, 200.45, 40.46, 0.18809, 50, 44.6, 56.29, 0.05831, 49, 11.15, 50.46, 0.14366, 66, 90.24, 40.46, 0.60994, 2, 46, 171.41, 90.12, 0.48644, 66, 61.2, 90.12, 0.51356, 2, 46, 164.84, -48.18, 0.43217, 66, 54.62, -48.18, 0.56783, 4, 46, 168.13, -6.02, 0.01949, 47, -28.65, 49.02, 0.02229, 49, 8.54, -6.09, 0.12791, 66, 57.92, -6.02, 0.83031, 2, 46, 167.84, 37.87, 0.15, 66, 57.63, 37.87, 0.85, 2, 46, 162.36, 71.5, 0.24107, 66, 52.15, 71.5, 0.75893, 2, 46, 163.11, -47.44, 0.41951, 66, 52.9, -47.44, 0.58049, 2, 46, 165.94, -5.87, 0.16355, 66, 55.73, -5.87, 0.83645, 2, 46, 165.14, 37.38, 0.15, 66, 54.93, 37.38, 0.85, 2, 46, 157.6, 71.4, 0.21735, 66, 47.39, 71.4, 0.78265, 3, 46, 163.5, -99.54, 0.61812, 47, 39.01, -15.71, 0.30445, 66, 53.29, -99.54, 0.07744, 2, 46, 45.38, 27.24, 0.16741, 66, -64.83, 27.24, 0.83259, 2, 46, 63.74, 44.98, 0.15, 66, -46.47, 44.98, 0.85, 2, 46, 70.7, 61.92, 0.22175, 66, -39.51, 61.92, 0.77825, 2, 46, 62.88, 78.71, 0.38, 66, -47.34, 78.71, 0.62, 2, 46, 46.53, 85.3, 0.51, 66, -63.68, 85.3, 0.49, 2, 46, 29.92, 79.34, 0.388, 66, -80.29, 79.34, 0.612, 2, 46, 15.08, 62.21, 0.38, 66, -95.13, 62.21, 0.62, 2, 46, 14.09, 45.32, 0.41, 66, -96.12, 45.32, 0.59, 2, 46, 24.3, 27.06, 0.192, 66, -85.91, 27.06, 0.808, 1, 66, -61.57, 15.3, 1, 2, 46, 84.87, 62.14, 0.16757, 66, -25.34, 62.14, 0.83243, 2, 46, 61.9, 94.84, 0.68145, 66, -48.31, 94.84, 0.31855, 2, 46, 22.54, 21.88, 0.16, 66, -87.67, 21.88, 0.84, 2, 46, 43.15, -95.95, 0.73445, 66, -67.06, -95.95, 0.26555, 2, 46, 41.77, -87.24, 0.67858, 66, -68.44, -87.24, 0.32142, 2, 46, 60.05, -70.36, 0.50195, 66, -50.16, -70.36, 0.49805, 2, 46, 48.49, 51.09, 0.25, 66, -61.72, 51.09, 0.75, 2, 46, 48.17, 73.71, 0.15634, 66, -62.04, 73.71, 0.84366 ],
+ "hull": 29,
+ "edges": [ 10, 8, 8, 6, 6, 4, 4, 2, 2, 0, 0, 56, 54, 56, 54, 52, 52, 50, 50, 48, 48, 46, 46, 44, 42, 44, 32, 34, 4, 58, 58, 60, 62, 64, 64, 66, 66, 54, 50, 68, 68, 70, 70, 44, 60, 72, 62, 74, 72, 74, 74, 76, 76, 78, 78, 44, 16, 80, 80, 82, 82, 84, 84, 86, 86, 44, 14, 88, 88, 72, 14, 16, 10, 12, 12, 14, 12, 60, 90, 92, 92, 94, 94, 96, 96, 98, 98, 100, 100, 102, 102, 104, 104, 106, 106, 90, 108, 110, 110, 112, 38, 40, 40, 42, 112, 40, 34, 36, 36, 38, 36, 114, 114, 108, 30, 32, 30, 28, 24, 26, 28, 26, 22, 24, 22, 20, 20, 18, 18, 16, 28, 116, 116, 118, 118, 120, 120, 20 ],
+ "width": 271,
+ "height": 298
+ }
+ },
+ "head-bb": {
+ "head": {
+ "type": "boundingbox",
+ "vertexCount": 6,
+ "vertices": [ -19.14, -70.3, 40.8, -118.08, 257.78, -115.62, 285.17, 57.18, 120.77, 164.95, -5.07, 76.95 ]
+ }
+ },
+ "hoverboard-board": {
+ "hoverboard-board": {
+ "type": "mesh",
+ "uvs": [ 0.13865, 0.56624, 0.11428, 0.51461, 0.07619, 0.52107, 0.02364, 0.52998, 0.01281, 0.53182, 0, 0.37979, 0, 0.2206, 0.00519, 0.10825, 0.01038, 0.10726, 0.03834, 0.10194, 0.05091, 0, 0.08326, 0, 0.10933, 0.04206, 0.1382, 0.08865, 0.18916, 0.24067, 0.22234, 0.4063, 0.23886, 0.44063, 0.83412, 0.44034, 0.88444, 0.38296, 0.92591, 0.32639, 0.95996, 0.28841, 0.98612, 0.28542, 1, 0.38675, 0.99494, 0.47104, 0.97883, 0.53251, 0.94409, 0.62135, 0.90206, 0.69492, 0.86569, 0.71094, 0.82822, 0.70791, 0.81286, 0.77127, 0.62931, 0.77266, 0.61364, 0.70645, 0.47166, 0.70664, 0.45901, 0.77827, 0.27747, 0.76986, 0.2658, 0.70372, 0.24976, 0.71381, 0.24601, 0.77827, 0.23042, 0.84931, 0.20926, 0.90956, 0.17299, 1, 0.15077, 0.99967, 0.12906, 0.90192, 0.10369, 0.73693, 0.10198, 0.62482, 0.09131, 0.47272, 0.09133, 0.41325, 0.15082, 0.41868, 0.21991, 0.51856, 0.06331, 0.10816, 0.08383, 0.21696, 0.08905, 0.37532, 0.15903, 0.58726, 0.17538, 0.65706, 0.20118, 0.8029, 0.17918, 0.55644, 0.22166, 0.5802, 0.86259, 0.57962, 0.92346, 0.48534, 0.96691, 0.36881, 0.0945, 0.13259, 0.12688, 0.17831, 0.15986, 0.24682, 0.18036, 0.31268, 0.20607, 0.4235, 0.16074, 0.85403, 0.13624, 0.70122, 0.12096, 0.64049, 0.02396, 0.21811, 0.02732, 0.37839, 0.02557, 0.4972, 0.14476, 0.45736, 0.18019, 0.51689, 0.19692, 0.56636 ],
+ "triangles": [ 10, 11, 12, 9, 10, 12, 49, 9, 12, 60, 49, 12, 13, 60, 12, 61, 60, 13, 50, 49, 60, 50, 60, 61, 68, 8, 9, 68, 9, 49, 68, 49, 50, 7, 8, 68, 6, 7, 68, 61, 13, 14, 62, 61, 14, 50, 61, 62, 63, 62, 14, 59, 20, 21, 19, 20, 59, 51, 50, 62, 51, 62, 63, 51, 69, 68, 51, 68, 50, 6, 68, 69, 5, 6, 69, 18, 19, 59, 15, 63, 14, 59, 21, 22, 47, 51, 63, 47, 46, 51, 47, 63, 64, 15, 64, 63, 64, 15, 16, 71, 46, 47, 23, 59, 22, 69, 51, 70, 45, 46, 71, 70, 51, 2, 58, 18, 59, 58, 59, 23, 17, 18, 58, 70, 5, 69, 2, 51, 46, 1, 45, 71, 47, 48, 71, 47, 64, 48, 48, 72, 71, 1, 71, 72, 16, 48, 64, 45, 2, 46, 2, 45, 1, 70, 4, 5, 3, 70, 2, 3, 4, 70, 24, 58, 23, 72, 0, 1, 73, 55, 72, 55, 0, 72, 48, 73, 72, 57, 17, 58, 25, 57, 58, 56, 48, 16, 73, 48, 56, 56, 16, 17, 56, 17, 57, 52, 0, 55, 24, 25, 58, 44, 0, 52, 67, 44, 52, 52, 56, 53, 73, 52, 55, 56, 52, 73, 67, 52, 53, 26, 57, 25, 66, 67, 53, 56, 32, 35, 53, 56, 35, 56, 57, 32, 28, 31, 57, 57, 31, 32, 57, 27, 28, 26, 27, 57, 36, 53, 35, 43, 44, 67, 43, 67, 66, 34, 35, 32, 29, 31, 28, 30, 31, 29, 53, 54, 66, 53, 36, 54, 33, 34, 32, 37, 54, 36, 65, 43, 66, 38, 54, 37, 54, 65, 66, 39, 65, 54, 42, 43, 65, 38, 39, 54, 40, 42, 65, 40, 41, 42, 65, 39, 40 ],
+ "vertices": [ -189.36, 15.62, -201.35, 23.47, -220.09, 22.49, -245.95, 21.13, -251.28, 20.86, -257.58, 43.96, -257.57, 68.16, -255.02, 85.24, -252.47, 85.39, -238.71, 86.2, -232.52, 101.69, -216.61, 101.69, -203.78, 95.3, -189.58, 88.21, -164.51, 65.1, -148.19, 39.93, -140.06, 34.71, 152.82, 34.73, 177.57, 43.45, 197.97, 52.05, 214.72, 57.82, 227.6, 58.27, 234.42, 42.87, 231.94, 30.06, 224.01, 20.72, 206.91, 7.21, 186.23, -3.97, 168.34, -6.4, 149.9, -5.94, 142.35, -15.57, 52.04, -15.77, 44.33, -5.71, -25.52, -5.73, -31.75, -16.62, -121.07, -15.34, -126.81, -5.28, -134.7, -6.81, -136.54, -16.61, -144.22, -27.41, -154.63, -36.57, -172.47, -50.31, -183.41, -50.26, -194.09, -35.4, -206.56, -10.32, -207.4, 6.72, -212.65, 29.84, -212.64, 38.88, -183.37, 38.05, -149.38, 22.86, -226.43, 85.25, -216.33, 68.71, -213.76, 44.64, -179.34, 12.42, -171.29, 1.81, -158.6, -20.36, -169.42, 17.11, -148.52, 13.49, 166.82, 13.56, 196.76, 27.89, 218.14, 45.6, -211.08, 81.54, -195.15, 74.59, -178.93, 64.17, -168.84, 54.16, -156.19, 37.31, -178.5, -28.13, -190.55, -4.9, -198.07, 4.33, -245.79, 68.54, -244.14, 44.18, -245, 26.12, -186.36, 32.17, -168.92, 23.12, -160.69, 15.6 ],
+ "hull": 45,
+ "edges": [ 0, 2, 8, 10, 10, 12, 12, 14, 18, 20, 20, 22, 26, 28, 28, 30, 30, 32, 32, 34, 34, 36, 36, 38, 38, 40, 40, 42, 42, 44, 44, 46, 46, 48, 48, 50, 50, 52, 52, 54, 54, 56, 56, 58, 58, 60, 60, 62, 62, 64, 64, 66, 66, 68, 68, 70, 70, 72, 72, 74, 80, 82, 82, 84, 84, 86, 86, 88, 0, 88, 2, 90, 90, 92, 92, 94, 94, 96, 96, 32, 18, 98, 98, 100, 100, 102, 2, 4, 102, 4, 92, 102, 0, 104, 104, 106, 106, 108, 78, 80, 108, 78, 74, 76, 76, 78, 62, 56, 64, 70, 0, 110, 112, 114, 114, 116, 116, 118, 118, 42, 50, 116, 114, 34, 98, 120, 120, 122, 22, 24, 24, 26, 120, 24, 122, 124, 124, 126, 126, 128, 128, 96, 80, 130, 130, 132, 132, 134, 134, 88, 14, 16, 16, 18, 136, 16, 136, 138, 138, 140, 4, 6, 6, 8, 140, 6, 96, 112, 92, 142, 142, 144, 110, 146, 146, 112, 144, 146 ],
+ "width": 492,
+ "height": 152
+ }
+ },
+ "hoverboard-thruster-front": {
+ "hoverboard-thruster": { "x": 0.02, "y": -7.08, "rotation": 0.17, "width": 60, "height": 64 }
+ },
+ "hoverboard-thruster-rear": {
+ "hoverboard-thruster": { "x": 1.1, "y": -6.29, "rotation": 0.17, "width": 60, "height": 64 }
+ },
+ "hoverglow-front": {
+ "hoverglow-small": {
+ "x": 2.13,
+ "y": -2,
+ "scaleX": 0.303,
+ "scaleY": 0.495,
+ "rotation": 0.15,
+ "width": 274,
+ "height": 75
+ }
+ },
+ "hoverglow-rear": {
+ "hoverglow-small": {
+ "x": 1.39,
+ "y": -2.09,
+ "scaleX": 0.303,
+ "scaleY": 0.495,
+ "rotation": 0.61,
+ "width": 274,
+ "height": 75
+ }
+ },
+ "mouth": {
+ "mouth-grind": {
+ "type": "mesh",
+ "uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ],
+ "triangles": [ 1, 3, 0, 1, 2, 3 ],
+ "vertices": [ 2, 66, -98.93, -85.88, 0.22, 46, 11.28, -85.88, 0.78, 2, 66, -129.77, 1.84, 0.6, 46, -19.56, 1.84, 0.4, 2, 66, -74.12, 21.41, 0.6, 46, 36.09, 21.41, 0.4, 2, 66, -43.28, -66.32, 0.4, 46, 66.93, -66.32, 0.6 ],
+ "hull": 4,
+ "edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ],
+ "width": 93,
+ "height": 59
+ },
+ "mouth-oooo": {
+ "type": "mesh",
+ "uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ],
+ "triangles": [ 1, 3, 0, 1, 2, 3 ],
+ "vertices": [ 2, 46, 11.28, -85.89, 0.22, 66, -98.93, -85.89, 0.78, 2, 46, -19.56, 1.85, 0.6, 66, -129.78, 1.85, 0.4, 2, 46, 36.1, 21.42, 0.6, 66, -74.12, 21.42, 0.4, 2, 46, 66.94, -66.32, 0.4, 66, -43.27, -66.32, 0.6 ],
+ "hull": 4,
+ "edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ],
+ "width": 93,
+ "height": 59
+ },
+ "mouth-smile": {
+ "type": "mesh",
+ "uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ],
+ "triangles": [ 1, 3, 0, 1, 2, 3 ],
+ "vertices": [ 2, 66, -98.93, -85.89, 0.21075, 46, 11.28, -85.89, 0.78925, 2, 66, -129.77, 1.85, 0.6, 46, -19.56, 1.85, 0.4, 2, 66, -74.11, 21.42, 0.6, 46, 36.1, 21.42, 0.4, 2, 66, -43.27, -66.32, 0.40772, 46, 66.94, -66.32, 0.59228 ],
+ "hull": 4,
+ "edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ],
+ "width": 93,
+ "height": 59
+ }
+ },
+ "muzzle": {
+ "muzzle01": {
+ "x": 151.97,
+ "y": 5.81,
+ "scaleX": 3.7361,
+ "scaleY": 3.7361,
+ "rotation": 0.15,
+ "width": 133,
+ "height": 79
+ },
+ "muzzle02": {
+ "x": 187.25,
+ "y": 5.9,
+ "scaleX": 4.0623,
+ "scaleY": 4.0623,
+ "rotation": 0.15,
+ "width": 135,
+ "height": 84
+ },
+ "muzzle03": {
+ "x": 231.96,
+ "y": 6.02,
+ "scaleX": 4.1325,
+ "scaleY": 4.1325,
+ "rotation": 0.15,
+ "width": 166,
+ "height": 106
+ },
+ "muzzle04": {
+ "x": 231.96,
+ "y": 6.02,
+ "scaleX": 4.0046,
+ "scaleY": 4.0046,
+ "rotation": 0.15,
+ "width": 149,
+ "height": 90
+ },
+ "muzzle05": {
+ "x": 293.8,
+ "y": 6.19,
+ "scaleX": 4.4673,
+ "scaleY": 4.4673,
+ "rotation": 0.15,
+ "width": 135,
+ "height": 75
+ }
+ },
+ "muzzle-glow": {
+ "muzzle-glow": { "width": 50, "height": 50 }
+ },
+ "muzzle-ring": {
+ "muzzle-ring": { "x": -1.3, "y": 0.32, "scaleX": 0.3147, "scaleY": 0.3147, "width": 49, "height": 209 }
+ },
+ "muzzle-ring2": {
+ "muzzle-ring": { "x": -1.3, "y": 0.32, "scaleX": 0.3147, "scaleY": 0.3147, "width": 49, "height": 209 }
+ },
+ "muzzle-ring3": {
+ "muzzle-ring": { "x": -1.3, "y": 0.32, "scaleX": 0.3147, "scaleY": 0.3147, "width": 49, "height": 209 }
+ },
+ "muzzle-ring4": {
+ "muzzle-ring": { "x": -1.3, "y": 0.32, "scaleX": 0.3147, "scaleY": 0.3147, "width": 49, "height": 209 }
+ },
+ "neck": {
+ "neck": { "x": 9.77, "y": -3.01, "rotation": -55.22, "width": 36, "height": 41 }
+ },
+ "portal-bg": {
+ "portal-bg": { "x": -3.1, "y": 7.25, "scaleX": 1.0492, "scaleY": 1.0492, "width": 266, "height": 266 }
+ },
+ "portal-flare1": {
+ "portal-flare1": { "width": 111, "height": 60 },
+ "portal-flare2": { "width": 114, "height": 61 },
+ "portal-flare3": { "width": 115, "height": 59 }
+ },
+ "portal-flare2": {
+ "portal-flare1": { "width": 111, "height": 60 },
+ "portal-flare2": { "width": 114, "height": 61 },
+ "portal-flare3": { "width": 115, "height": 59 }
+ },
+ "portal-flare3": {
+ "portal-flare1": { "width": 111, "height": 60 },
+ "portal-flare2": { "width": 114, "height": 61 },
+ "portal-flare3": { "width": 115, "height": 59 }
+ },
+ "portal-flare4": {
+ "portal-flare1": { "width": 111, "height": 60 },
+ "portal-flare2": { "width": 114, "height": 61 },
+ "portal-flare3": { "width": 115, "height": 59 }
+ },
+ "portal-flare5": {
+ "portal-flare1": { "width": 111, "height": 60 },
+ "portal-flare2": { "width": 114, "height": 61 },
+ "portal-flare3": { "width": 115, "height": 59 }
+ },
+ "portal-flare6": {
+ "portal-flare1": { "width": 111, "height": 60 },
+ "portal-flare2": { "width": 114, "height": 61 },
+ "portal-flare3": { "width": 115, "height": 59 }
+ },
+ "portal-flare7": {
+ "portal-flare1": { "width": 111, "height": 60 },
+ "portal-flare2": { "width": 114, "height": 61 },
+ "portal-flare3": { "width": 115, "height": 59 }
+ },
+ "portal-flare8": {
+ "portal-flare1": { "width": 111, "height": 60 },
+ "portal-flare2": { "width": 114, "height": 61 },
+ "portal-flare3": { "width": 115, "height": 59 }
+ },
+ "portal-flare9": {
+ "portal-flare1": { "width": 111, "height": 60 },
+ "portal-flare2": { "width": 114, "height": 61 },
+ "portal-flare3": { "width": 115, "height": 59 }
+ },
+ "portal-flare10": {
+ "portal-flare1": { "width": 111, "height": 60 },
+ "portal-flare2": { "width": 114, "height": 61 },
+ "portal-flare3": { "width": 115, "height": 59 }
+ },
+ "portal-shade": {
+ "portal-shade": { "width": 266, "height": 266 }
+ },
+ "portal-streaks1": {
+ "portal-streaks1": { "scaleX": 0.9774, "scaleY": 0.9774, "width": 252, "height": 256 }
+ },
+ "portal-streaks2": {
+ "portal-streaks2": { "x": -1.64, "y": 2.79, "width": 250, "height": 249 }
+ },
+ "rear-bracer": {
+ "rear-bracer": { "x": 11.15, "y": -2.2, "rotation": 66.17, "width": 56, "height": 72 }
+ },
+ "rear-foot": {
+ "rear-foot": {
+ "type": "mesh",
+ "uvs": [ 0.48368, 0.1387, 0.51991, 0.21424, 0.551, 0.27907, 0.58838, 0.29816, 0.63489, 0.32191, 0.77342, 0.39267, 1, 0.73347, 1, 1, 0.54831, 0.99883, 0.31161, 1, 0, 1, 0, 0.41397, 0.13631, 0, 0.41717, 0 ],
+ "triangles": [ 8, 3, 4, 8, 4, 5, 8, 5, 6, 8, 6, 7, 11, 1, 10, 3, 9, 2, 2, 10, 1, 12, 13, 0, 0, 11, 12, 1, 11, 0, 2, 9, 10, 3, 8, 9 ],
+ "vertices": [ 2, 8, 10.45, 29.41, 0.90802, 9, -6.74, 49.62, 0.09198, 2, 8, 16.56, 29.27, 0.84259, 9, -2.65, 45.09, 0.15741, 2, 8, 21.8, 29.15, 0.69807, 9, 0.85, 41.2, 0.30193, 2, 8, 25.53, 31.43, 0.52955, 9, 5.08, 40.05, 0.47045, 2, 8, 30.18, 34.27, 0.39303, 9, 10.33, 38.62, 0.60697, 2, 8, 44.02, 42.73, 0.27525, 9, 25.98, 34.36, 0.72475, 2, 8, 76.47, 47.28, 0.21597, 9, 51.56, 13.9, 0.78403, 2, 8, 88.09, 36.29, 0.28719, 9, 51.55, -2.09, 0.71281, 2, 8, 52.94, -0.73, 0.47576, 9, 0.52, -1.98, 0.52424, 2, 8, 34.63, -20.23, 0.68757, 9, -26.23, -2.03, 0.31243, 2, 8, 10.44, -45.81, 0.84141, 9, -61.43, -2, 0.15859, 2, 8, -15.11, -21.64, 0.93283, 9, -61.4, 33.15, 0.06717, 1, 8, -22.57, 6.61, 1, 1, 8, -0.76, 29.67, 1 ],
+ "hull": 14,
+ "edges": [ 14, 12, 10, 12, 14, 16, 16, 18, 18, 20, 4, 18, 20, 22, 24, 26, 22, 24, 4, 2, 2, 20, 4, 6, 6, 16, 6, 8, 8, 10, 2, 0, 0, 26 ],
+ "width": 113,
+ "height": 60
+ }
+ },
+ "rear-shin": {
+ "rear-shin": { "x": 58.29, "y": -2.75, "rotation": 92.37, "width": 75, "height": 178 }
+ },
+ "rear-thigh": {
+ "rear-thigh": { "x": 33.11, "y": -4.11, "rotation": 72.54, "width": 55, "height": 94 }
+ },
+ "rear-upper-arm": {
+ "rear-upper-arm": { "x": 21.13, "y": 4.09, "rotation": 89.33, "width": 40, "height": 87 }
+ },
+ "side-glow1": {
+ "hoverglow-small": { "x": 2.09, "scaleX": 0.2353, "scaleY": 0.4132, "width": 274, "height": 75 }
+ },
+ "side-glow2": {
+ "hoverglow-small": { "x": 2.09, "scaleX": 0.2353, "scaleY": 0.4132, "width": 274, "height": 75 }
+ },
+ "side-glow3": {
+ "hoverglow-small": { "x": 2.09, "scaleX": 0.3586, "scaleY": 0.6297, "width": 274, "height": 75 }
+ },
+ "torso": {
+ "torso": {
+ "type": "mesh",
+ "uvs": [ 0.6251, 0.12672, 1, 0.26361, 1, 0.28871, 1, 0.66021, 1, 0.68245, 0.92324, 0.69259, 0.95116, 0.84965, 0.77124, 1, 0.49655, 1, 0.27181, 1, 0.13842, 0.77196, 0.09886, 0.6817, 0.05635, 0.58471, 0, 0.45614, 0, 0.33778, 0, 0.19436, 0.14463, 0, 0.27802, 0, 0.72525, 0.27835, 0.76091, 0.46216, 0.84888, 0.67963, 0.68257, 0.63249, 0.53986, 0.3847, 0.25443, 0.3217, 0.30063, 0.55174, 0.39553, 0.79507, 0.26389, 0.17007, 0.5241, 0.18674, 0.71492, 0.76655, 0.82151, 0.72956, 0.27626, 0.4304, 0.62327, 0.52952, 0.3455, 0.66679, 0.53243, 0.2914 ],
+ "triangles": [ 18, 1, 2, 19, 2, 3, 18, 0, 1, 23, 15, 26, 27, 26, 16, 14, 15, 23, 15, 16, 26, 17, 27, 16, 13, 14, 23, 0, 27, 17, 13, 23, 30, 11, 12, 24, 21, 31, 19, 12, 13, 30, 24, 22, 31, 31, 22, 19, 12, 30, 24, 32, 24, 31, 24, 30, 22, 3, 20, 19, 32, 31, 21, 11, 24, 32, 4, 5, 3, 8, 28, 7, 7, 29, 6, 7, 28, 29, 9, 25, 8, 8, 25, 28, 9, 10, 25, 29, 5, 6, 10, 32, 25, 25, 21, 28, 25, 32, 21, 10, 11, 32, 28, 21, 29, 29, 20, 5, 29, 21, 20, 5, 20, 3, 20, 21, 19, 33, 26, 27, 22, 18, 19, 19, 18, 2, 33, 27, 18, 30, 23, 22, 22, 33, 18, 23, 33, 22, 33, 23, 26, 27, 0, 18 ],
+ "vertices": [ 2, 29, 44.59, -10.39, 0.88, 40, -17.65, 33.99, 0.12, 3, 28, 59.65, -45.08, 0.12189, 29, 17.13, -45.08, 0.26811, 40, 22.68, 15.82, 0.61, 3, 28, 55.15, -44.72, 0.1345, 29, 12.63, -44.72, 0.2555, 40, 23.43, 11.37, 0.61, 3, 27, 31.01, -39.45, 0.51133, 28, -11.51, -39.45, 0.30867, 40, 34.58, -54.57, 0.18, 3, 27, 27.01, -39.14, 0.53492, 28, -15.5, -39.14, 0.28508, 40, 35.25, -58.52, 0.18, 2, 27, 25.79, -31.5, 0.75532, 28, -16.73, -31.5, 0.24468, 1, 27, -2.61, -32, 1, 1, 27, -28.2, -12.29, 1, 1, 27, -26.08, 14.55, 1, 1, 27, -24.35, 36.5, 1, 2, 27, 17.6, 46.3, 0.8332, 28, -24.92, 46.3, 0.1668, 2, 27, 34.1, 48.89, 0.59943, 28, -8.42, 48.89, 0.40058, 3, 27, 51.83, 51.67, 0.29262, 28, 9.32, 51.67, 0.63181, 29, -33.2, 51.67, 0.07557, 3, 27, 75.34, 55.35, 0.06656, 28, 32.82, 55.35, 0.62298, 29, -9.7, 55.35, 0.31046, 2, 28, 54.06, 53.67, 0.37296, 29, 11.54, 53.67, 0.62704, 2, 28, 79.79, 51.64, 0.10373, 29, 37.27, 51.64, 0.89627, 1, 29, 71.04, 34.76, 1, 1, 29, 70.01, 21.72, 1, 1, 30, 36.74, 7.06, 1, 3, 30, 45.7, -24.98, 0.67, 28, 25.87, -18.9, 0.3012, 29, -16.65, -18.9, 0.0288, 2, 27, 28.69, -24.42, 0.77602, 28, -13.83, -24.42, 0.22398, 3, 30, 43.24, -56.49, 0.064, 27, 38.43, -8.84, 0.67897, 28, -4.09, -8.84, 0.25703, 3, 30, 22.02, -14.85, 0.29, 28, 41.48, 1.59, 0.53368, 29, -1.04, 1.59, 0.17632, 3, 30, -7.45, -8.33, 0.76, 28, 54.98, 28.59, 0.06693, 29, 12.46, 28.59, 0.17307, 3, 30, 3.91, -48.4, 0.25, 27, 55.87, 27.33, 0.15843, 28, 13.35, 27.33, 0.59157, 1, 27, 11.47, 21.51, 1, 2, 30, -11.09, 18.74, 0.416, 29, 39.6, 25.51, 0.584, 2, 30, 14.56, 20.03, 0.53, 29, 34.6, 0.33, 0.47, 1, 27, 14.12, -10.1, 1, 2, 27, 19.94, -21.03, 0.92029, 28, -22.58, -21.03, 0.07971, 3, 30, -2.08, -27.26, 0.29, 28, 35.31, 27.99, 0.49582, 29, -7.21, 27.99, 0.21418, 2, 30, 34.42, -39.19, 0.25, 28, 14.84, -4.5, 0.75, 2, 27, 34.87, 24.58, 0.67349, 28, -7.64, 24.58, 0.32651, 2, 30, 18.5, 1.59, 0.76, 29, 15.76, 1, 0.24 ],
+ "hull": 18,
+ "edges": [ 14, 12, 12, 10, 10, 8, 18, 20, 32, 34, 30, 32, 2, 4, 36, 4, 36, 38, 38, 40, 4, 6, 6, 8, 40, 6, 40, 42, 14, 16, 16, 18, 50, 16, 46, 52, 54, 36, 2, 0, 0, 34, 54, 0, 54, 32, 20, 50, 14, 56, 56, 42, 50, 56, 56, 58, 58, 40, 58, 10, 46, 60, 60, 48, 26, 60, 60, 44, 24, 26, 24, 48, 42, 62, 62, 44, 48, 62, 48, 64, 64, 50, 42, 64, 20, 22, 22, 24, 64, 22, 26, 28, 28, 30, 28, 46, 44, 66, 66, 54, 46, 66, 66, 36, 62, 38 ],
+ "width": 98,
+ "height": 180
+ }
+ }
+ }
+ }
+ ],
+ "events": {
+ "footstep": {}
+ },
+ "animations": {
+ "aim": {
+ "slots": {
+ "crosshair": {
+ "attachment": [
+ { "name": "crosshair" }
+ ]
+ }
+ },
+ "bones": {
+ "front-fist": {
+ "rotate": [
+ { "value": 36.08 }
+ ]
+ },
+ "rear-bracer": {
+ "rotate": [
+ { "value": -26.55 }
+ ]
+ },
+ "rear-upper-arm": {
+ "rotate": [
+ { "value": 62.31 }
+ ]
+ },
+ "front-bracer": {
+ "rotate": [
+ { "value": 9.11 }
+ ]
+ },
+ "gun": {
+ "rotate": [
+ { "value": -0.31 }
+ ]
+ }
+ },
+ "ik": {
+ "aim-ik": [
+ { "mix": 0.995 }
+ ]
+ },
+ "transform": {
+ "aim-front-arm-transform": [
+ { "mixRotate": 0.784, "mixX": 0, "mixScaleX": 0, "mixShearY": 0 }
+ ],
+ "aim-head-transform": [
+ { "mixRotate": 0.659, "mixX": 0, "mixScaleX": 0, "mixShearY": 0 }
+ ],
+ "aim-torso-transform": [
+ { "mixRotate": 0.423, "mixX": 0, "mixScaleX": 0, "mixShearY": 0 }
+ ]
+ }
+ },
+ "death": {
+ "slots": {
+ "eye": {
+ "attachment": [
+ { "name": "eye-surprised" },
+ { "time": 0.5333, "name": "eye-indifferent" },
+ { "time": 2.2, "name": "eye-surprised" },
+ { "time": 4.6, "name": "eye-indifferent" }
+ ]
+ },
+ "front-fist": {
+ "attachment": [
+ { "name": "front-fist-open" }
+ ]
+ },
+ "mouth": {
+ "attachment": [
+ { "name": "mouth-oooo" },
+ { "time": 0.5333, "name": "mouth-grind" },
+ { "time": 1.4, "name": "mouth-oooo" },
+ { "time": 2.1667, "name": "mouth-grind" },
+ { "time": 4.5333, "name": "mouth-oooo" }
+ ]
+ }
+ },
+ "bones": {
+ "head": {
+ "rotate": [
+ {
+ "value": -2.83,
+ "curve": [ 0.015, -2.83, 0.036, 12.72 ]
+ },
+ {
+ "time": 0.0667,
+ "value": 12.19,
+ "curve": [ 0.096, 11.68, 0.119, -1.14 ]
+ },
+ {
+ "time": 0.1333,
+ "value": -6.86,
+ "curve": [ 0.149, -13.27, 0.21, -37.28 ]
+ },
+ {
+ "time": 0.3,
+ "value": -36.86,
+ "curve": [ 0.354, -36.61, 0.412, -32.35 ]
+ },
+ {
+ "time": 0.4667,
+ "value": -23.49,
+ "curve": [ 0.49, -19.87, 0.512, -3.29 ]
+ },
+ {
+ "time": 0.5333,
+ "value": -3.24,
+ "curve": [ 0.56, -3.39, 0.614, -67.25 ]
+ },
+ {
+ "time": 0.6333,
+ "value": -74.4,
+ "curve": [ 0.652, -81.58, 0.702, -88.94 ]
+ },
+ {
+ "time": 0.7333,
+ "value": -88.93,
+ "curve": [ 0.805, -88.91, 0.838, -80.87 ]
+ },
+ {
+ "time": 0.8667,
+ "value": -81.03,
+ "curve": [ 0.922, -81.32, 0.976, -85.29 ]
+ },
+ { "time": 1, "value": -85.29, "curve": "stepped" },
+ {
+ "time": 2.2333,
+ "value": -85.29,
+ "curve": [ 2.314, -85.29, 2.382, -68.06 ]
+ },
+ {
+ "time": 2.4667,
+ "value": -63.48,
+ "curve": [ 2.57, -57.87, 2.916, -55.24 ]
+ },
+ {
+ "time": 3.2,
+ "value": -55.1,
+ "curve": [ 3.447, -54.98, 4.135, -56.61 ]
+ },
+ {
+ "time": 4.2667,
+ "value": -58.23,
+ "curve": [ 4.672, -63.24, 4.646, -82.69 ]
+ },
+ { "time": 4.9333, "value": -85.29 }
+ ],
+ "scale": [
+ {
+ "time": 0.4667,
+ "curve": [ 0.469, 1.005, 0.492, 1.065, 0.475, 1.018, 0.492, 0.94 ]
+ },
+ {
+ "time": 0.5,
+ "x": 1.065,
+ "y": 0.94,
+ "curve": [ 0.517, 1.065, 0.541, 0.991, 0.517, 0.94, 0.542, 1.026 ]
+ },
+ {
+ "time": 0.5667,
+ "x": 0.99,
+ "y": 1.025,
+ "curve": [ 0.593, 0.988, 0.609, 1.002, 0.595, 1.024, 0.607, 1.001 ]
+ },
+ { "time": 0.6333 }
+ ]
+ },
+ "neck": {
+ "rotate": [
+ {
+ "value": -2.83,
+ "curve": [ 0.114, 1.33, 0.195, 4.13 ]
+ },
+ {
+ "time": 0.2667,
+ "value": 4.13,
+ "curve": [ 0.351, 4.14, 0.444, -24.5 ]
+ },
+ {
+ "time": 0.5,
+ "value": -24.69,
+ "curve": [ 0.571, -23.89, 0.55, 34.22 ]
+ },
+ {
+ "time": 0.6667,
+ "value": 35.13,
+ "curve": [ 0.713, 34.81, 0.756, 22.76 ]
+ },
+ {
+ "time": 0.8333,
+ "value": 22.82,
+ "curve": [ 0.868, 22.84, 0.916, 47.95 ]
+ },
+ { "time": 0.9667, "value": 47.95, "curve": "stepped" },
+ {
+ "time": 2.2333,
+ "value": 47.95,
+ "curve": [ 2.3, 47.95, 2.617, 18.72 ]
+ },
+ {
+ "time": 2.6667,
+ "value": 18.51,
+ "curve": [ 3.172, 16.58, 4.06, 16.79 ]
+ },
+ {
+ "time": 4.5333,
+ "value": 18.51,
+ "curve": [ 4.707, 19.13, 4.776, 41.11 ]
+ },
+ { "time": 4.8, "value": 47.95 }
+ ]
+ },
+ "torso": {
+ "rotate": [
+ {
+ "value": -8.62,
+ "curve": [ 0.01, -16.71, 0.032, -33.6 ]
+ },
+ {
+ "time": 0.0667,
+ "value": -33.37,
+ "curve": [ 0.182, -32.61, 0.298, 123.07 ]
+ },
+ {
+ "time": 0.4667,
+ "value": 122.77,
+ "curve": [ 0.511, 122.69, 0.52, 100.2 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 88.96,
+ "curve": [ 0.588, 83.89, 0.667, 75.34 ]
+ },
+ {
+ "time": 0.7,
+ "value": 75.34,
+ "curve": [ 0.767, 75.34, 0.9, 76.03 ]
+ },
+ { "time": 0.9667, "value": 76.03 }
+ ]
+ },
+ "front-upper-arm": {
+ "rotate": [
+ {
+ "value": -38.86,
+ "curve": [ 0.022, -40.38, 0.096, -41.92 ]
+ },
+ {
+ "time": 0.1333,
+ "value": -41.92,
+ "curve": [ 0.176, -41.92, 0.216, -16.92 ]
+ },
+ {
+ "time": 0.2333,
+ "value": -4.35,
+ "curve": [ 0.258, 13.69, 0.308, 60.35 ]
+ },
+ {
+ "time": 0.4,
+ "value": 60.17,
+ "curve": [ 0.496, 59.98, 0.539, 33.63 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 23.06,
+ "curve": [ 0.595, 32.71, 0.675, 53.71 ]
+ },
+ {
+ "time": 0.7333,
+ "value": 53.61,
+ "curve": [ 0.797, 53.51, 0.926, 30.98 ]
+ },
+ { "time": 0.9333, "value": 19.57, "curve": "stepped" },
+ {
+ "time": 1.9667,
+ "value": 19.57,
+ "curve": [ 2.245, 19.57, 2.702, 77.03 ]
+ },
+ {
+ "time": 3.0667,
+ "value": 77.06,
+ "curve": [ 3.209, 77.33, 3.291, 67.99 ]
+ },
+ {
+ "time": 3.4333,
+ "value": 67.96,
+ "curve": [ 3.608, 68.34, 3.729, 73.88 ]
+ },
+ {
+ "time": 3.8333,
+ "value": 73.42,
+ "curve": [ 4.152, 73.91, 4.46, 71.98 ]
+ },
+ {
+ "time": 4.6333,
+ "value": 64.77,
+ "curve": [ 4.688, 62.5, 4.847, 26.42 ]
+ },
+ { "time": 4.8667, "value": 10.94 }
+ ]
+ },
+ "rear-upper-arm": {
+ "rotate": [
+ {
+ "value": -44.7,
+ "curve": [ 0.033, -44.7, 0.12, 54.89 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 64.62,
+ "curve": [ 0.154, 79.18, 0.214, 79.42 ]
+ },
+ {
+ "time": 0.2667,
+ "value": 63.4,
+ "curve": [ 0.293, 55.19, 0.332, 30.13 ]
+ },
+ {
+ "time": 0.3667,
+ "value": 30.13,
+ "curve": [ 0.4, 30.13, 0.441, 39.87 ]
+ },
+ {
+ "time": 0.4667,
+ "value": 55.13,
+ "curve": [ 0.488, 68.18, 0.52, 100.72 ]
+ },
+ {
+ "time": 0.5333,
+ "value": 111.96,
+ "curve": [ 0.551, 126.88, 0.627, 185.97 ]
+ },
+ {
+ "time": 0.6667,
+ "value": 185.97,
+ "curve": [ 0.692, 185.97, 0.736, 162.43 ]
+ },
+ {
+ "time": 0.8,
+ "value": 158.01,
+ "curve": [ 0.9, 151.12, 1.017, 144.01 ]
+ },
+ { "time": 1.1, "value": 144.01, "curve": "stepped" },
+ {
+ "time": 2.3667,
+ "value": 144.01,
+ "curve": [ 2.492, 144.01, 2.742, 138.63 ]
+ },
+ {
+ "time": 2.8667,
+ "value": 138.63,
+ "curve": [ 3.067, 138.63, 3.467, 138.63 ]
+ },
+ {
+ "time": 3.6667,
+ "value": 138.63,
+ "curve": [ 3.883, 138.63, 4.317, 135.18 ]
+ },
+ {
+ "time": 4.5333,
+ "value": 135.18,
+ "curve": [ 4.575, 135.18, 4.692, 131.59 ]
+ },
+ {
+ "time": 4.7333,
+ "value": 131.59,
+ "curve": [ 4.758, 131.59, 4.517, 144.01 ]
+ },
+ { "time": 4.8333, "value": 144.01 }
+ ],
+ "translate": [
+ {
+ "time": 0.4667,
+ "curve": [ 0.517, 0, 0.617, -34.96, 0.517, 0, 0.617, -16.59 ]
+ },
+ { "time": 0.6667, "x": -35.02, "y": -16.62 }
+ ]
+ },
+ "front-bracer": {
+ "rotate": [
+ {
+ "value": 21.88,
+ "curve": [ 0.033, 21.88, 0.099, 20.44 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 9.43,
+ "curve": [ 0.164, -0.29, 0.162, -38.26 ]
+ },
+ {
+ "time": 0.2,
+ "value": -38.05,
+ "curve": [ 0.24, -37.96, 0.228, -17.82 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -9.73,
+ "curve": [ 0.372, -6.76, 0.431, -0.74 ]
+ },
+ {
+ "time": 0.4667,
+ "value": 6.47,
+ "curve": [ 0.489, 11.05, 0.503, 19.09 ]
+ },
+ {
+ "time": 0.5333,
+ "value": 19.09,
+ "curve": [ 0.571, 19.09, 0.554, -42.67 ]
+ },
+ {
+ "time": 0.6,
+ "value": -42.67,
+ "curve": [ 0.653, -42.67, 0.691, -13.8 ]
+ },
+ {
+ "time": 0.7,
+ "value": -3.54,
+ "curve": [ 0.707, 3.8, 0.719, 24.94 ]
+ },
+ {
+ "time": 0.8,
+ "value": 25.31,
+ "curve": [ 0.902, 24.75, 0.992, -0.34 ]
+ },
+ { "time": 1, "value": -32.16, "curve": "stepped" },
+ {
+ "time": 2.2333,
+ "value": -32.16,
+ "curve": [ 2.6, -32.16, 2.638, -5.3 ]
+ },
+ {
+ "time": 2.7,
+ "value": -1.96,
+ "curve": [ 2.707, -1.56, 2.775, 1.67 ]
+ },
+ {
+ "time": 2.8,
+ "value": 1.67,
+ "curve": [ 2.825, 1.67, 2.875, -0.39 ]
+ },
+ {
+ "time": 2.9,
+ "value": -0.39,
+ "curve": [ 2.925, -0.39, 2.975, 0.26 ]
+ },
+ {
+ "time": 3,
+ "value": 0.26,
+ "curve": [ 3.025, 0.26, 3.075, -1.81 ]
+ },
+ {
+ "time": 3.1,
+ "value": -1.81,
+ "curve": [ 3.125, -1.81, 3.175, -0.52 ]
+ },
+ {
+ "time": 3.2,
+ "value": -0.52,
+ "curve": [ 3.225, -0.52, 3.275, -2.41 ]
+ },
+ {
+ "time": 3.3,
+ "value": -2.41,
+ "curve": [ 3.333, -2.41, 3.4, -0.38 ]
+ },
+ {
+ "time": 3.4333,
+ "value": -0.38,
+ "curve": [ 3.467, -0.38, 3.533, -2.25 ]
+ },
+ {
+ "time": 3.5667,
+ "value": -2.25,
+ "curve": [ 3.592, -2.25, 3.642, -0.33 ]
+ },
+ {
+ "time": 3.6667,
+ "value": -0.33,
+ "curve": [ 3.7, -0.33, 3.767, -1.34 ]
+ },
+ {
+ "time": 3.8,
+ "value": -1.34,
+ "curve": [ 3.825, -1.34, 3.862, -0.77 ]
+ },
+ {
+ "time": 3.9,
+ "value": -0.77,
+ "curve": [ 3.942, -0.77, 3.991, -1.48 ]
+ },
+ {
+ "time": 4,
+ "value": -1.87,
+ "curve": [ 4.167, -1.87, 4.5, -1.96 ]
+ },
+ {
+ "time": 4.6667,
+ "value": -1.96,
+ "curve": [ 4.709, 18.05, 4.767, 34.55 ]
+ },
+ {
+ "time": 4.8,
+ "value": 34.55,
+ "curve": [ 4.84, 34.24, 4.902, 12.03 ]
+ },
+ { "time": 4.9333, "value": -18.75 }
+ ]
+ },
+ "front-fist": {
+ "rotate": [
+ {
+ "value": -2.33,
+ "curve": [ 0.019, 4.43, 0.069, 10.82 ]
+ },
+ {
+ "time": 0.1,
+ "value": 10.6,
+ "curve": [ 0.148, 10.6, 0.123, -15.24 ]
+ },
+ {
+ "time": 0.2,
+ "value": -15.35,
+ "curve": [ 0.266, -15.44, 0.316, -6.48 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -3.9,
+ "curve": [ 0.362, 0.43, 0.479, 22.36 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 22.01,
+ "curve": [ 0.61, 21.84, 0.627, 12.85 ]
+ },
+ {
+ "time": 0.6333,
+ "value": 9.05,
+ "curve": [ 0.643, 2.77, 0.622, -39.43 ]
+ },
+ {
+ "time": 0.7,
+ "value": -39.5,
+ "curve": [ 0.773, -39.57, 0.814, 14.77 ]
+ },
+ {
+ "time": 0.8667,
+ "value": 14.81,
+ "curve": [ 0.965, 14.88, 1.1, 5.64 ]
+ },
+ { "time": 1.1, "value": -6.08, "curve": "stepped" },
+ {
+ "time": 2.2333,
+ "value": -6.08,
+ "curve": [ 2.307, -6.08, 2.427, -25.89 ]
+ },
+ {
+ "time": 2.5333,
+ "value": -22.42,
+ "curve": [ 2.598, -20.38, 2.657, 5.73 ]
+ },
+ {
+ "time": 2.7,
+ "value": 5.73,
+ "curve": [ 2.77, 5.73, 2.851, -5.38 ]
+ },
+ {
+ "time": 2.9333,
+ "value": -5.38,
+ "curve": [ 3.008, -5.38, 3.087, -4.54 ]
+ },
+ {
+ "time": 3.1667,
+ "value": -4.17,
+ "curve": [ 3.223, -3.91, 4.486, 5.73 ]
+ },
+ {
+ "time": 4.6667,
+ "value": 5.73,
+ "curve": [ 4.733, 5.73, 4.886, -2.47 ]
+ },
+ { "time": 4.9333, "value": -6.52 }
+ ]
+ },
+ "rear-bracer": {
+ "rotate": [
+ {
+ "value": 10.36,
+ "curve": [ 0.033, 10.36, 0.1, -32.89 ]
+ },
+ {
+ "time": 0.1333,
+ "value": -32.89,
+ "curve": [ 0.183, -32.89, 0.283, -4.45 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -4.45,
+ "curve": [ 0.367, -4.45, 0.438, -6.86 ]
+ },
+ {
+ "time": 0.4667,
+ "value": -8.99,
+ "curve": [ 0.529, -13.62, 0.605, -20.58 ]
+ },
+ {
+ "time": 0.6333,
+ "value": -23.2,
+ "curve": [ 0.708, -30.18, 0.758, -35.56 ]
+ },
+ {
+ "time": 0.8,
+ "value": -35.56,
+ "curve": [ 0.875, -35.56, 1.025, -23.2 ]
+ },
+ { "time": 1.1, "value": -23.2 }
+ ]
+ },
+ "gun": {
+ "rotate": [
+ {
+ "value": -2.79,
+ "curve": [ 0.033, -2.79, 0.12, -7.22 ]
+ },
+ {
+ "time": 0.1333,
+ "value": -8.52,
+ "curve": [ 0.168, -11.87, 0.29, -23.71 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -26.24,
+ "curve": [ 0.369, -28.31, 0.436, -29.75 ]
+ },
+ {
+ "time": 0.5,
+ "value": -29.66,
+ "curve": [ 0.552, -29.58, 0.611, -25.47 ]
+ },
+ {
+ "time": 0.6333,
+ "value": -22.68,
+ "curve": [ 0.656, -19.76, 0.68, -10.02 ]
+ },
+ {
+ "time": 0.7,
+ "value": -6.49,
+ "curve": [ 0.722, -2.6, 0.75, -1.22 ]
+ },
+ {
+ "time": 0.7667,
+ "value": -1.35,
+ "curve": [ 0.792, -1.55, 0.842, -19.74 ]
+ },
+ { "time": 0.8667, "value": -19.8 }
+ ]
+ },
+ "hip": {
+ "translate": [
+ {
+ "curve": [ 0.098, -42.62, 0.166, -79.85, 0.029, 84.97, 0.109, 155.93 ]
+ },
+ {
+ "time": 0.2667,
+ "x": -133.79,
+ "y": 152.44,
+ "curve": [ 0.361, -184.63, 0.392, -203.69, 0.42, 149.12, 0.467, -15.7 ]
+ },
+ {
+ "time": 0.4667,
+ "x": -230.02,
+ "y": -113.87,
+ "curve": [ 0.523, -249.86, 0.565, -261.7, 0.473, -133.1, 0.583, -203.43 ]
+ },
+ {
+ "time": 0.6,
+ "x": -268.57,
+ "y": -203.43,
+ "curve": [ 0.663, -280.98, 0.816, -290.05, 0.708, -203.43, 0.892, -203.5 ]
+ },
+ { "time": 1, "x": -290.42, "y": -203.5 }
+ ]
+ },
+ "front-thigh": {
+ "rotate": [
+ {
+ "curve": [ 0.06, 1.02, 0.151, 45.23 ]
+ },
+ {
+ "time": 0.1667,
+ "value": 54.01,
+ "curve": [ 0.19, 66.85, 0.358, 169.85 ]
+ },
+ {
+ "time": 0.5,
+ "value": 169.51,
+ "curve": [ 0.628, 169.85, 0.692, 108.85 ]
+ },
+ {
+ "time": 0.7,
+ "value": 97.74,
+ "curve": [ 0.723, 102.6, 0.805, 111.6 ]
+ },
+ {
+ "time": 0.8667,
+ "value": 111.69,
+ "curve": [ 0.899, 111.83, 1.015, 109.15 ]
+ },
+ { "time": 1.0667, "value": 95.8 }
+ ]
+ },
+ "front-shin": {
+ "rotate": [
+ {
+ "curve": [ 0.086, -0.02, 0.191, -24.25 ]
+ },
+ {
+ "time": 0.2,
+ "value": -26.5,
+ "curve": [ 0.214, -29.92, 0.249, -40.51 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -40.57,
+ "curve": [ 0.431, -40.7, 0.459, -11.34 ]
+ },
+ {
+ "time": 0.4667,
+ "value": -8.71,
+ "curve": [ 0.477, -5.16, 0.524, 17.13 ]
+ },
+ {
+ "time": 0.6,
+ "value": 16.98,
+ "curve": [ 0.632, 17.09, 0.625, 2.76 ]
+ },
+ {
+ "time": 0.6333,
+ "value": 2.76,
+ "curve": [ 0.648, 2.76, 0.653, 2.75 ]
+ },
+ {
+ "time": 0.6667,
+ "value": 2.59,
+ "curve": [ 0.678, 2.39, 0.733, 2.53 ]
+ },
+ {
+ "time": 0.7333,
+ "value": -9.43,
+ "curve": [ 0.745, -2.48, 0.782, 3.12 ]
+ },
+ {
+ "time": 0.8,
+ "value": 4.28,
+ "curve": [ 0.832, 6.32, 0.895, 8.46 ]
+ },
+ {
+ "time": 0.9333,
+ "value": 8.49,
+ "curve": [ 0.986, 8.53, 1.051, 6.38 ]
+ },
+ {
+ "time": 1.0667,
+ "value": 2.28,
+ "curve": [ 1.078, 4.17, 1.103, 5.86 ]
+ },
+ {
+ "time": 1.1333,
+ "value": 5.88,
+ "curve": [ 1.191, 5.93, 1.209, 4.56 ]
+ },
+ { "time": 1.2333, "value": 2.52 }
+ ]
+ },
+ "rear-thigh": {
+ "rotate": [
+ {
+ "curve": [ 0.033, 0, 0.12, 50.26 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 57.3,
+ "curve": [ 0.164, 73.34, 0.274, 147.18 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 147.1,
+ "curve": [ 0.475, 146.45, 0.583, 95.72 ]
+ },
+ {
+ "time": 0.6,
+ "value": 79.66,
+ "curve": [ 0.62, 94.74, 0.732, 103.15 ]
+ },
+ {
+ "time": 0.7667,
+ "value": 103.02,
+ "curve": [ 0.812, 102.85, 0.897, 95.75 ]
+ },
+ { "time": 0.9333, "value": 83.01 }
+ ]
+ },
+ "rear-shin": {
+ "rotate": [
+ {
+ "curve": [ 0.021, -16.65, 0.091, -54.82 ]
+ },
+ {
+ "time": 0.1667,
+ "value": -55.29,
+ "curve": [ 0.187, -55.42, 0.213, -52.52 ]
+ },
+ {
+ "time": 0.2333,
+ "value": -45.98,
+ "curve": [ 0.242, -43.1, 0.311, -12.73 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -6.32,
+ "curve": [ 0.356, 0.13, 0.467, 24.5 ]
+ },
+ {
+ "time": 0.5,
+ "value": 24.5,
+ "curve": [ 0.543, 24.5, 0.56, 3.78 ]
+ },
+ {
+ "time": 0.5667,
+ "value": -3.53,
+ "curve": [ 0.585, 3.86, 0.659, 16.63 ]
+ },
+ {
+ "time": 0.7,
+ "value": 16.56,
+ "curve": [ 0.782, 16.43, 0.896, 8.44 ]
+ },
+ {
+ "time": 0.9333,
+ "value": 4.04,
+ "curve": [ 0.956, 6.84, 1.008, 8.41 ]
+ },
+ {
+ "time": 1.0333,
+ "value": 8.41,
+ "curve": [ 1.067, 8.41, 1.122, 8.14 ]
+ },
+ { "time": 1.1667, "value": 5.8 }
+ ]
+ },
+ "rear-foot": {
+ "rotate": [
+ {
+ "value": -0.28,
+ "curve": [ 0.033, -0.28, 0.256, -66.71 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -66.84,
+ "curve": [ 0.418, -66.91, 0.499, -21.79 ]
+ },
+ {
+ "time": 0.6,
+ "value": -21.52,
+ "curve": [ 0.652, -21.38, 0.665, -53.96 ]
+ },
+ {
+ "time": 0.7,
+ "value": -54.26,
+ "curve": [ 0.757, -53.96, 0.843, -2.07 ]
+ },
+ {
+ "time": 0.9333,
+ "value": -1.47,
+ "curve": [ 0.968, -2.07, 0.975, -19.96 ]
+ },
+ {
+ "time": 1,
+ "value": -19.96,
+ "curve": [ 1.025, -19.96, 1.075, -12.42 ]
+ },
+ {
+ "time": 1.1,
+ "value": -12.42,
+ "curve": [ 1.133, -12.42, 1.2, -18.34 ]
+ },
+ { "time": 1.2333, "value": -18.34 }
+ ]
+ },
+ "front-foot": {
+ "rotate": [
+ {
+ "curve": [ 0.008, -11.33, 0.108, -57.71 ]
+ },
+ {
+ "time": 0.1333,
+ "value": -57.71,
+ "curve": [ 0.175, -57.71, 0.229, 19.73 ]
+ },
+ {
+ "time": 0.3,
+ "value": 19.34,
+ "curve": [ 0.354, 19.34, 0.4, -57.76 ]
+ },
+ {
+ "time": 0.4333,
+ "value": -57.76,
+ "curve": [ 0.458, -57.76, 0.511, -3.56 ]
+ },
+ {
+ "time": 0.5333,
+ "value": 3.7,
+ "curve": [ 0.563, 13.29, 0.633, 15.79 ]
+ },
+ {
+ "time": 0.6667,
+ "value": 15.79,
+ "curve": [ 0.7, 15.79, 0.767, -48.75 ]
+ },
+ {
+ "time": 0.8,
+ "value": -48.75,
+ "curve": [ 0.842, -48.75, 0.925, 4.7 ]
+ },
+ {
+ "time": 0.9667,
+ "value": 4.7,
+ "curve": [ 1, 4.7, 1.067, -22.9 ]
+ },
+ {
+ "time": 1.1,
+ "value": -22.9,
+ "curve": [ 1.142, -22.9, 1.225, -13.28 ]
+ },
+ { "time": 1.2667, "value": -13.28 }
+ ]
+ },
+ "rear-foot-target": {
+ "rotate": [
+ { "value": -0.28 }
+ ]
+ },
+ "front-foot-tip": {
+ "rotate": [
+ {
+ "value": -0.28,
+ "curve": [ 0.008, -0.28, 0.003, -66.62 ]
+ },
+ {
+ "time": 0.0667,
+ "value": -65.75,
+ "curve": [ 0.166, -64.42, 0.234, 14.35 ]
+ },
+ {
+ "time": 0.2667,
+ "value": 38.25,
+ "curve": [ 0.294, 57.91, 0.392, 89.79 ]
+ },
+ {
+ "time": 0.4667,
+ "value": 90.73,
+ "curve": [ 0.483, 90.73, 0.55, 177.66 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 177.66,
+ "curve": [ 0.733, 176.24, 0.75, 11.35 ]
+ },
+ {
+ "time": 0.8,
+ "value": 11.35,
+ "curve": [ 0.886, 12.29, 0.911, 47.88 ]
+ },
+ {
+ "time": 0.9333,
+ "value": 56.77,
+ "curve": [ 0.967, 70.59, 1.05, 86.46 ]
+ },
+ {
+ "time": 1.1,
+ "value": 86.46,
+ "curve": [ 1.187, 86.46, 1.214, 66.44 ]
+ },
+ { "time": 1.3333, "value": 64.55 }
+ ]
+ },
+ "back-foot-tip": {
+ "rotate": [
+ {
+ "value": -0.28,
+ "curve": [ 0, -7.97, 0.027, -18.69 ]
+ },
+ {
+ "time": 0.0667,
+ "value": -19,
+ "curve": [ 0.166, -19.3, 0.208, 15.58 ]
+ },
+ {
+ "time": 0.2667,
+ "value": 45.95,
+ "curve": [ 0.306, 66.24, 0.378, 99.08 ]
+ },
+ {
+ "time": 0.4333,
+ "value": 99.08,
+ "curve": [ 0.497, 98.62, 0.488, -1.2 ]
+ },
+ {
+ "time": 0.5667,
+ "value": -1.32,
+ "curve": [ 0.637, -0.84, 0.687, 94.41 ]
+ },
+ {
+ "time": 0.7333,
+ "value": 94.33,
+ "curve": [ 0.832, 94.16, 0.895, 29.6 ]
+ },
+ {
+ "time": 0.9667,
+ "value": 28.67,
+ "curve": [ 1.026, 28.67, 1.045, 53.14 ]
+ },
+ { "time": 1.1, "value": 53.38 }
+ ]
+ },
+ "hair4": {
+ "rotate": [
+ {
+ "curve": [ 0.011, 4.5, 0.05, 11.42 ]
+ },
+ {
+ "time": 0.0667,
+ "value": 11.42,
+ "curve": [ 0.1, 11.42, 0.136, -5.92 ]
+ },
+ {
+ "time": 0.1667,
+ "value": -10.54,
+ "curve": [ 0.206, -16.51, 0.327, -22 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -24.47,
+ "curve": [ 0.413, -27.37, 0.467, -43.99 ]
+ },
+ {
+ "time": 0.5,
+ "value": -43.99,
+ "curve": [ 0.533, -43.99, 0.552, 12.12 ]
+ },
+ {
+ "time": 0.6333,
+ "value": 11.85,
+ "curve": [ 0.714, 11.59, 0.758, -34.13 ]
+ },
+ {
+ "time": 0.8,
+ "value": -34.13,
+ "curve": [ 0.858, -34.13, 1.015, -12.47 ]
+ },
+ {
+ "time": 1.0667,
+ "value": -8.85,
+ "curve": [ 1.121, -5.07, 1.219, -0.02 ]
+ },
+ {
+ "time": 1.3333,
+ "value": 1.29,
+ "curve": [ 1.509, 3.3, 1.763, 2.75 ]
+ },
+ {
+ "time": 1.8667,
+ "value": 2.78,
+ "curve": [ 1.974, 2.81, 2.108, 2.81 ]
+ },
+ {
+ "time": 2.2,
+ "value": 2.78,
+ "curve": [ 2.315, 2.74, 2.374, 1.22 ]
+ },
+ {
+ "time": 2.4667,
+ "value": 1.18,
+ "curve": [ 2.525, 1.18, 2.608, 10.79 ]
+ },
+ {
+ "time": 2.6667,
+ "value": 10.79,
+ "curve": [ 2.725, 10.79, 2.893, 4.72 ]
+ },
+ {
+ "time": 3.0333,
+ "value": 4.72,
+ "curve": [ 3.117, 4.72, 3.283, 7.93 ]
+ },
+ {
+ "time": 3.3667,
+ "value": 7.93,
+ "curve": [ 3.492, 7.93, 3.775, 6.93 ]
+ },
+ {
+ "time": 3.9,
+ "value": 6.93,
+ "curve": [ 3.981, 6.93, 4.094, 6.9 ]
+ },
+ {
+ "time": 4.2,
+ "value": 8.44,
+ "curve": [ 4.267, 9.42, 4.401, 16.61 ]
+ },
+ {
+ "time": 4.5,
+ "value": 16.33,
+ "curve": [ 4.582, 16.12, 4.709, 9.94 ]
+ },
+ {
+ "time": 4.7333,
+ "value": 6.51,
+ "curve": [ 4.747, 4.57, 4.779, -1.76 ]
+ },
+ {
+ "time": 4.8,
+ "value": -1.75,
+ "curve": [ 4.823, -1.73, 4.82, 4.47 ]
+ },
+ {
+ "time": 4.8667,
+ "value": 6.04,
+ "curve": [ 4.899, 7.14, 4.913, 6.93 ]
+ },
+ { "time": 4.9333, "value": 6.93 }
+ ]
+ },
+ "hair2": {
+ "rotate": [
+ {
+ "value": 10.61,
+ "curve": [ 0.075, 10.61, 0.05, 12.67 ]
+ },
+ {
+ "time": 0.0667,
+ "value": 12.67,
+ "curve": [ 0.123, 12.67, 0.194, -16.51 ]
+ },
+ {
+ "time": 0.2,
+ "value": -19.87,
+ "curve": [ 0.207, -23.48, 0.236, -31.68 ]
+ },
+ {
+ "time": 0.3,
+ "value": -31.8,
+ "curve": [ 0.356, -31.9, 0.437, -25.61 ]
+ },
+ {
+ "time": 0.4667,
+ "value": -19.29,
+ "curve": [ 0.485, -15.33, 0.529, 6.48 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 6.67,
+ "curve": [ 0.628, 6.97, 0.65, -46.39 ]
+ },
+ {
+ "time": 0.7333,
+ "value": -46.3,
+ "curve": [ 0.843, -46.17, 0.941, -33.37 ]
+ },
+ {
+ "time": 0.9667,
+ "value": -23.17,
+ "curve": [ 0.972, -20.98, 1.047, 15.21 ]
+ },
+ {
+ "time": 1.1,
+ "value": 15.21,
+ "curve": [ 1.142, 15.21, 1.183, 10.73 ]
+ },
+ {
+ "time": 1.2667,
+ "value": 10.61,
+ "curve": [ 1.45, 10.34, 1.817, 10.61 ]
+ },
+ {
+ "time": 2,
+ "value": 10.61,
+ "curve": [ 2.075, 10.61, 2.225, 16.9 ]
+ },
+ {
+ "time": 2.3,
+ "value": 16.9,
+ "curve": [ 2.327, 16.9, 2.347, 6.81 ]
+ },
+ {
+ "time": 2.4,
+ "value": 6.83,
+ "curve": [ 2.492, 6.87, 2.602, 17.39 ]
+ },
+ {
+ "time": 2.6667,
+ "value": 17.39,
+ "curve": [ 2.742, 17.39, 2.892, 10.67 ]
+ },
+ {
+ "time": 2.9667,
+ "value": 10.64,
+ "curve": [ 3.187, 10.57, 3.344, 10.73 ]
+ },
+ {
+ "time": 3.6,
+ "value": 11.4,
+ "curve": [ 3.766, 11.83, 3.874, 14.87 ]
+ },
+ {
+ "time": 3.9333,
+ "value": 14.83,
+ "curve": [ 4.022, 14.76, 4.208, 9.49 ]
+ },
+ {
+ "time": 4.3,
+ "value": 9.54,
+ "curve": [ 4.391, 9.58, 4.441, 14.82 ]
+ },
+ {
+ "time": 4.5333,
+ "value": 14.84,
+ "curve": [ 4.642, 14.88, 4.692, 1.17 ]
+ },
+ {
+ "time": 4.7667,
+ "value": 1.24,
+ "curve": [ 4.823, 1.3, 4.818, 18.35 ]
+ },
+ {
+ "time": 4.8667,
+ "value": 18.38,
+ "curve": [ 4.905, 18.41, 4.901, 10.61 ]
+ },
+ { "time": 4.9333, "value": 10.61 }
+ ]
+ },
+ "torso2": {
+ "rotate": [
+ {
+ "curve": [ 0.048, 0, 0.129, -12.73 ]
+ },
+ {
+ "time": 0.1667,
+ "value": -15.95,
+ "curve": [ 0.221, -20.66, 0.254, -21.62 ]
+ },
+ {
+ "time": 0.3,
+ "value": -21.59,
+ "curve": [ 0.458, -21.46, 0.46, -1.67 ]
+ },
+ {
+ "time": 0.6333,
+ "value": -1.71,
+ "curve": [ 0.71, -1.73, 0.715, -4 ]
+ },
+ {
+ "time": 0.7667,
+ "value": -3.97,
+ "curve": [ 0.866, -3.92, 0.84, 0.02 ]
+ },
+ { "time": 1, "curve": "stepped" },
+ {
+ "time": 2,
+ "curve": [ 2.275, 0, 2.867, -5.8 ]
+ },
+ {
+ "time": 3.1,
+ "value": -6.44,
+ "curve": [ 3.327, -7.06, 3.71, -6.23 ]
+ },
+ {
+ "time": 3.9333,
+ "value": -5.41,
+ "curve": [ 4.168, -4.53, 4.488, -2.83 ]
+ },
+ { "time": 4.8 }
+ ]
+ },
+ "torso3": {
+ "rotate": [
+ {
+ "curve": [ 0.025, 0, 0.09, -3.66 ]
+ },
+ {
+ "time": 0.1,
+ "value": -4.55,
+ "curve": [ 0.143, -8.4, 0.223, -17.07 ]
+ },
+ {
+ "time": 0.2333,
+ "value": -18.31,
+ "curve": [ 0.282, -24.44, 0.35, -29 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -30.07,
+ "curve": [ 0.405, -32.58, 0.442, -33.03 ]
+ },
+ {
+ "time": 0.4667,
+ "value": -32.99,
+ "curve": [ 0.491, -33.04, 0.505, -23.56 ]
+ },
+ {
+ "time": 0.5333,
+ "value": -23.55,
+ "curve": [ 0.571, -23.67, 0.599, -27.21 ]
+ },
+ {
+ "time": 0.6333,
+ "value": -27.21,
+ "curve": [ 0.669, -27.2, 0.742, -10.43 ]
+ },
+ {
+ "time": 0.7667,
+ "value": -7.79,
+ "curve": [ 0.788, -5.53, 0.796, -4.42 ]
+ },
+ {
+ "time": 0.8333,
+ "value": -2.9,
+ "curve": [ 0.875, -1.21, 0.933, 0 ]
+ },
+ { "time": 0.9667, "curve": "stepped" },
+ {
+ "time": 2.4333,
+ "curve": [ 2.517, 0, 2.683, 4.63 ]
+ },
+ {
+ "time": 2.7667,
+ "value": 4.66,
+ "curve": [ 3.084, 4.76, 3.248, 4.37 ]
+ },
+ {
+ "time": 3.4,
+ "value": 3.74,
+ "curve": [ 3.596, 2.92, 3.755, 2.18 ]
+ },
+ {
+ "time": 3.8667,
+ "value": 1.72,
+ "curve": [ 4.136, 0.59, 4.471, 0 ]
+ },
+ { "time": 4.8 }
+ ]
+ },
+ "hair3": {
+ "rotate": [
+ {
+ "curve": [ 0, 0, 0.041, 10.74 ]
+ },
+ {
+ "time": 0.0667,
+ "value": 14.16,
+ "curve": [ 0.075, 15.22, 0.148, 18.04 ]
+ },
+ {
+ "time": 0.2,
+ "value": 18.13,
+ "curve": [ 0.251, 18.23, 0.307, -4.75 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -5.06,
+ "curve": [ 0.412, -5.3, 0.47, -0.96 ]
+ },
+ {
+ "time": 0.5,
+ "value": 2.21,
+ "curve": [ 0.512, 3.48, 0.595, 20.31 ]
+ },
+ {
+ "time": 0.6333,
+ "value": 24.87,
+ "curve": [ 0.647, 26.53, 0.719, 29.33 ]
+ },
+ {
+ "time": 0.8,
+ "value": 29.22,
+ "curve": [ 0.859, 29.14, 0.9, 28.48 ]
+ },
+ {
+ "time": 0.9333,
+ "value": 26.11,
+ "curve": [ 0.981, 22.72, 0.998, 2.06 ]
+ },
+ { "time": 1.1, "value": 2.21 }
+ ]
+ },
+ "hair1": {
+ "rotate": [
+ {
+ "curve": [ 0.047, -0.21, 0.048, 7.86 ]
+ },
+ {
+ "time": 0.0667,
+ "value": 13.27,
+ "curve": [ 0.083, 18.05, 0.135, 24.44 ]
+ },
+ {
+ "time": 0.2,
+ "value": 24.02,
+ "curve": [ 0.225, 24.02, 0.28, 6.32 ]
+ },
+ {
+ "time": 0.3,
+ "value": 3.1,
+ "curve": [ 0.323, -0.58, 0.382, -7.12 ]
+ },
+ {
+ "time": 0.4667,
+ "value": -7.45,
+ "curve": [ 0.512, -7.66, 0.538, 12.13 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 16.46,
+ "curve": [ 0.609, 22.72, 0.672, 27.4 ]
+ },
+ {
+ "time": 0.7333,
+ "value": 27.55,
+ "curve": [ 0.827, 27.4, 0.933, 23.23 ]
+ },
+ {
+ "time": 0.9667,
+ "value": 19.11,
+ "curve": [ 0.998, 15.27, 1.092, -2.53 ]
+ },
+ {
+ "time": 1.1333,
+ "value": -2.53,
+ "curve": [ 1.158, -2.53, 1.208, 0 ]
+ },
+ { "time": 1.2333, "curve": "stepped" },
+ {
+ "time": 2,
+ "curve": [ 2.075, 0, 2.248, 0.35 ]
+ },
+ {
+ "time": 2.3333,
+ "value": 0.78,
+ "curve": [ 2.585, 2.06, 2.805, 3.46 ]
+ },
+ {
+ "time": 3.2,
+ "value": 3.5,
+ "curve": [ 3.593, 3.54, 3.979, 2.36 ]
+ },
+ {
+ "time": 4.1667,
+ "value": 1.55,
+ "curve": [ 4.391, 0.59, 4.447, 0.04 ]
+ },
+ {
+ "time": 4.6,
+ "value": 0.04,
+ "curve": [ 4.642, 0.04, 4.742, 0 ]
+ },
+ { "time": 4.9333 }
+ ]
+ },
+ "head-control": {
+ "translate": [
+ {
+ "curve": [ 0.025, 0, 0.09, 1.43, 0.025, 0, 0.075, -34.76 ]
+ },
+ {
+ "time": 0.1,
+ "x": 1.59,
+ "y": -34.76,
+ "curve": [ 0.214, 3.33, 0.375, 5.34, 0.192, -34.76, 0.441, -21.17 ]
+ },
+ {
+ "time": 0.4667,
+ "x": 5.34,
+ "y": -12.57,
+ "curve": [ 0.492, 5.34, 0.55, 5.24, 0.482, -7.36, 0.504, 4.03 ]
+ },
+ {
+ "time": 0.5667,
+ "x": 5.11,
+ "y": 4.01,
+ "curve": [ 0.658, 4.45, 0.679, 3.19, 0.649, 3.98, 0.642, -16.84 ]
+ },
+ {
+ "time": 0.7,
+ "x": 2.8,
+ "y": -16.74,
+ "curve": [ 0.787, 1.15, 0.881, -1.29, 0.772, -16.62, 0.82, 8.95 ]
+ },
+ {
+ "time": 0.9,
+ "x": -1.72,
+ "y": 8.91,
+ "curve": [ 0.961, -3.06, 1.025, -3.58, 0.975, 8.87, 0.951, -1.37 ]
+ },
+ {
+ "time": 1.1,
+ "x": -3.58,
+ "y": -1.45,
+ "curve": [ 1.292, -3.58, 2.002, -2.4, 1.292, -1.56, 1.975, -1.45 ]
+ },
+ {
+ "time": 2.1667,
+ "x": -1.39,
+ "y": -1.45,
+ "curve": [ 2.25, -0.88, 2.503, 1.38, 2.283, -1.45, 2.603, -12.44 ]
+ },
+ {
+ "time": 2.6667,
+ "x": 2.13,
+ "y": -14.45,
+ "curve": [ 2.766, 2.59, 2.999, 2.81, 2.835, -19.73, 3.003, -25.2 ]
+ },
+ {
+ "time": 3.1333,
+ "x": 2.91,
+ "y": -26.08,
+ "curve": [ 3.392, 3.1, 4.199, 4.05, 3.483, -28.44, 4.129, -27.23 ]
+ },
+ {
+ "time": 4.3667,
+ "x": 4.81,
+ "y": -19.59,
+ "curve": [ 4.429, 5.1, 4.594, 8.54, 4.538, -14.08, 4.583, -7.88 ]
+ },
+ {
+ "time": 4.6667,
+ "x": 8.65,
+ "y": -4.56,
+ "curve": [ 4.794, 8.86, 4.806, 5.93, 4.691, -3.59, 4.8, -1.61 ]
+ },
+ { "time": 4.9333, "x": 5.8, "y": -1.99 }
+ ]
+ }
+ },
+ "ik": {
+ "front-foot-ik": [
+ { "mix": 0 }
+ ],
+ "front-leg-ik": [
+ { "mix": 0, "bendPositive": false }
+ ],
+ "rear-foot-ik": [
+ { "mix": 0.005 }
+ ],
+ "rear-leg-ik": [
+ { "mix": 0.005, "bendPositive": false }
+ ]
+ }
+ },
+ "hoverboard": {
+ "slots": {
+ "exhaust1": {
+ "attachment": [
+ { "name": "hoverglow-small" }
+ ]
+ },
+ "exhaust2": {
+ "attachment": [
+ { "name": "hoverglow-small" }
+ ]
+ },
+ "exhaust3": {
+ "attachment": [
+ { "name": "hoverglow-small" }
+ ]
+ },
+ "front-fist": {
+ "attachment": [
+ { "name": "front-fist-open" }
+ ]
+ },
+ "hoverboard-board": {
+ "attachment": [
+ { "name": "hoverboard-board" }
+ ]
+ },
+ "hoverboard-thruster-front": {
+ "attachment": [
+ { "name": "hoverboard-thruster" }
+ ]
+ },
+ "hoverboard-thruster-rear": {
+ "attachment": [
+ { "name": "hoverboard-thruster" }
+ ]
+ },
+ "hoverglow-front": {
+ "attachment": [
+ { "name": "hoverglow-small" }
+ ]
+ },
+ "hoverglow-rear": {
+ "attachment": [
+ { "name": "hoverglow-small" }
+ ]
+ },
+ "side-glow1": {
+ "attachment": [
+ { "name": "hoverglow-small" },
+ { "time": 0.9667 }
+ ]
+ },
+ "side-glow2": {
+ "attachment": [
+ { "time": 0.0667, "name": "hoverglow-small" },
+ { "time": 1 }
+ ]
+ },
+ "side-glow3": {
+ "attachment": [
+ { "name": "hoverglow-small" },
+ { "time": 0.9667 }
+ ]
+ }
+ },
+ "bones": {
+ "hoverboard-controller": {
+ "translate": [
+ {
+ "x": 319.55,
+ "y": -1.59,
+ "curve": [ 0.064, 319.55, 0.2, 347.85, 0.058, -1.2, 0.2, 23.11 ]
+ },
+ {
+ "time": 0.2667,
+ "x": 347.66,
+ "y": 39.62,
+ "curve": [ 0.35, 347.41, 0.476, 341.47, 0.323, 53.58, 0.44, 85.82 ]
+ },
+ {
+ "time": 0.5333,
+ "x": 338.47,
+ "y": 85.72,
+ "curve": [ 0.603, 334.83, 0.913, 319.65, 0.621, 85.62, 0.88, -1.53 ]
+ },
+ { "time": 1, "x": 319.55, "y": -1.59 }
+ ]
+ },
+ "hip": {
+ "translate": [
+ {
+ "x": -53.49,
+ "y": 32.14,
+ "curve": [ 0.061, -53.77, 0.093, -51.81, 0.044, 16.34, 0.063, 9.67 ]
+ },
+ {
+ "time": 0.1333,
+ "x": -49.31,
+ "y": 7.01,
+ "curve": [ 0.3, -35.27, 0.461, -20.06, 0.314, 9.52, 0.408, 121.09 ]
+ },
+ {
+ "time": 0.5667,
+ "x": -20.06,
+ "y": 122.72,
+ "curve": [ 0.716, -20.09, 0.912, -53.29, 0.753, 121.8, 0.946, 51.85 ]
+ },
+ { "time": 1, "x": -53.49, "y": 32.14 }
+ ]
+ },
+ "exhaust1": {
+ "scale": [
+ {
+ "x": 1.593,
+ "y": 0.964,
+ "curve": [ 0.033, 1.593, 0.1, 1, 0.033, 0.964, 0.1, 0.713 ]
+ },
+ {
+ "time": 0.1333,
+ "y": 0.713,
+ "curve": [ 0.15, 1, 0.183, 1.774, 0.15, 0.713, 0.183, 0.883 ]
+ },
+ {
+ "time": 0.2,
+ "x": 1.774,
+ "y": 0.883,
+ "curve": [ 0.242, 1.774, 0.325, 1.181, 0.242, 0.883, 0.325, 0.649 ]
+ },
+ {
+ "time": 0.3667,
+ "x": 1.181,
+ "y": 0.649,
+ "curve": [ 0.408, 1.181, 0.492, 1.893, 0.408, 0.649, 0.492, 0.819 ]
+ },
+ {
+ "time": 0.5333,
+ "x": 1.893,
+ "y": 0.819,
+ "curve": [ 0.558, 1.893, 0.608, 1.18, 0.558, 0.819, 0.608, 0.686 ]
+ },
+ {
+ "time": 0.6333,
+ "x": 1.18,
+ "y": 0.686,
+ "curve": [ 0.658, 1.18, 0.708, 1.903, 0.658, 0.686, 0.708, 0.855 ]
+ },
+ {
+ "time": 0.7333,
+ "x": 1.903,
+ "y": 0.855,
+ "curve": [ 0.767, 1.903, 0.833, 1.311, 0.767, 0.855, 0.833, 0.622 ]
+ },
+ {
+ "time": 0.8667,
+ "x": 1.311,
+ "y": 0.622,
+ "curve": [ 0.9, 1.311, 0.967, 1.593, 0.9, 0.622, 0.967, 0.964 ]
+ },
+ { "time": 1, "x": 1.593, "y": 0.964 }
+ ]
+ },
+ "exhaust2": {
+ "scale": [
+ {
+ "x": 1.88,
+ "y": 0.832,
+ "curve": [ 0.025, 1.88, 0.075, 1.311, 0.025, 0.832, 0.075, 0.686 ]
+ },
+ {
+ "time": 0.1,
+ "x": 1.311,
+ "y": 0.686,
+ "curve": [ 0.133, 1.311, 0.2, 2.01, 0.133, 0.686, 0.208, 0.736 ]
+ },
+ {
+ "time": 0.2333,
+ "x": 2.01,
+ "y": 0.769,
+ "curve": [ 0.267, 2.01, 0.333, 1, 0.282, 0.831, 0.333, 0.91 ]
+ },
+ {
+ "time": 0.3667,
+ "y": 0.91,
+ "curve": [ 0.4, 1, 0.467, 1.699, 0.4, 0.91, 0.474, 0.891 ]
+ },
+ {
+ "time": 0.5,
+ "x": 1.699,
+ "y": 0.86,
+ "curve": [ 0.517, 1.699, 0.55, 1.181, 0.54, 0.813, 0.55, 0.713 ]
+ },
+ {
+ "time": 0.5667,
+ "x": 1.181,
+ "y": 0.713,
+ "curve": [ 0.617, 1.181, 0.717, 1.881, 0.617, 0.713, 0.717, 0.796 ]
+ },
+ {
+ "time": 0.7667,
+ "x": 1.881,
+ "y": 0.796,
+ "curve": [ 0.8, 1.881, 0.867, 1.3, 0.8, 0.796, 0.867, 0.649 ]
+ },
+ {
+ "time": 0.9,
+ "x": 1.3,
+ "y": 0.649,
+ "curve": [ 0.925, 1.3, 0.975, 1.88, 0.925, 0.649, 0.975, 0.832 ]
+ },
+ { "time": 1, "x": 1.88, "y": 0.832 }
+ ]
+ },
+ "hoverboard-thruster-front": {
+ "rotate": [
+ {
+ "curve": [ 0.125, 0, 0.375, 24.06 ]
+ },
+ {
+ "time": 0.5,
+ "value": 24.06,
+ "curve": [ 0.625, 24.06, 0.875, 0 ]
+ },
+ { "time": 1 }
+ ]
+ },
+ "hoverglow-front": {
+ "scale": [
+ {
+ "x": 0.849,
+ "y": 1.764,
+ "curve": [ 0.017, 0.849, 0.05, 0.835, 0.017, 1.764, 0.05, 2.033 ]
+ },
+ {
+ "time": 0.0667,
+ "x": 0.835,
+ "y": 2.033,
+ "curve": [ 0.092, 0.835, 0.142, 0.752, 0.092, 2.033, 0.142, 1.584 ]
+ },
+ {
+ "time": 0.1667,
+ "x": 0.752,
+ "y": 1.584,
+ "curve": [ 0.183, 0.752, 0.217, 0.809, 0.183, 1.584, 0.217, 1.71 ]
+ },
+ {
+ "time": 0.2333,
+ "x": 0.809,
+ "y": 1.71,
+ "curve": [ 0.25, 0.809, 0.283, 0.717, 0.25, 1.71, 0.283, 1.45 ]
+ },
+ {
+ "time": 0.3,
+ "x": 0.717,
+ "y": 1.45,
+ "curve": [ 0.317, 0.717, 0.35, 0.777, 0.317, 1.45, 0.35, 1.698 ]
+ },
+ {
+ "time": 0.3667,
+ "x": 0.777,
+ "y": 1.698,
+ "curve": [ 0.4, 0.781, 0.45, 0.685, 0.375, 1.698, 0.45, 1.173 ]
+ },
+ {
+ "time": 0.4667,
+ "x": 0.685,
+ "y": 1.173,
+ "curve": [ 0.492, 0.685, 0.542, 0.825, 0.492, 1.173, 0.542, 1.572 ]
+ },
+ {
+ "time": 0.5667,
+ "x": 0.825,
+ "y": 1.572,
+ "curve": [ 0.611, 0.816, 0.63, 0.727, 0.611, 1.577, 0.606, 1.255 ]
+ },
+ {
+ "time": 0.6667,
+ "x": 0.725,
+ "y": 1.241,
+ "curve": [ 0.692, 0.725, 0.742, 0.895, 0.692, 1.241, 0.749, 1.799 ]
+ },
+ {
+ "time": 0.7667,
+ "x": 0.895,
+ "y": 1.857,
+ "curve": [ 0.783, 0.895, 0.796, 0.892, 0.796, 1.955, 0.817, 1.962 ]
+ },
+ {
+ "time": 0.8333,
+ "x": 0.845,
+ "y": 1.962,
+ "curve": [ 0.845, 0.831, 0.883, 0.802, 0.85, 1.962, 0.872, 1.704 ]
+ },
+ {
+ "time": 0.9,
+ "x": 0.802,
+ "y": 1.491,
+ "curve": [ 0.917, 0.802, 0.95, 0.845, 0.907, 1.441, 0.936, 1.508 ]
+ },
+ {
+ "time": 0.9667,
+ "x": 0.845,
+ "y": 1.627,
+ "curve": [ 0.975, 0.845, 0.992, 0.849, 0.973, 1.652, 0.992, 1.764 ]
+ },
+ { "time": 1, "x": 0.849, "y": 1.764 }
+ ]
+ },
+ "hoverboard-thruster-rear": {
+ "rotate": [
+ {
+ "curve": [ 0.125, 0, 0.375, 24.06 ]
+ },
+ {
+ "time": 0.5,
+ "value": 24.06,
+ "curve": [ 0.625, 24.06, 0.875, 0 ]
+ },
+ { "time": 1 }
+ ]
+ },
+ "hoverglow-rear": {
+ "scale": [
+ {
+ "x": 0.845,
+ "y": 1.31,
+ "curve": [ 0.017, 0.845, 0.117, 0.899, 0.017, 1.31, 0.117, 2.033 ]
+ },
+ {
+ "time": 0.1333,
+ "x": 0.899,
+ "y": 2.033,
+ "curve": [ 0.15, 0.899, 0.183, 0.752, 0.15, 2.033, 0.183, 1.574 ]
+ },
+ {
+ "time": 0.2,
+ "x": 0.752,
+ "y": 1.574,
+ "curve": [ 0.225, 0.752, 0.275, 0.809, 0.225, 1.574, 0.275, 1.71 ]
+ },
+ {
+ "time": 0.3,
+ "x": 0.809,
+ "y": 1.71,
+ "curve": [ 0.317, 0.809, 0.35, 0.717, 0.317, 1.71, 0.35, 1.397 ]
+ },
+ {
+ "time": 0.3667,
+ "x": 0.717,
+ "y": 1.397,
+ "curve": [ 0.383, 0.717, 0.417, 0.777, 0.383, 1.397, 0.417, 1.45 ]
+ },
+ {
+ "time": 0.4333,
+ "x": 0.777,
+ "y": 1.45,
+ "curve": [ 0.45, 0.777, 0.496, 0.689, 0.45, 1.45, 0.481, 1.168 ]
+ },
+ {
+ "time": 0.5333,
+ "x": 0.685,
+ "y": 1.173,
+ "curve": [ 0.565, 0.682, 0.617, 0.758, 0.575, 1.177, 0.617, 1.297 ]
+ },
+ {
+ "time": 0.6333,
+ "x": 0.758,
+ "y": 1.297,
+ "curve": [ 0.658, 0.758, 0.708, 0.725, 0.658, 1.297, 0.708, 1.241 ]
+ },
+ {
+ "time": 0.7333,
+ "x": 0.725,
+ "y": 1.241,
+ "curve": [ 0.772, 0.732, 0.796, 0.893, 0.782, 1.238, 0.778, 1.854 ]
+ },
+ {
+ "time": 0.8333,
+ "x": 0.895,
+ "y": 1.857,
+ "curve": [ 0.878, 0.9, 0.992, 0.845, 0.88, 1.86, 0.992, 1.31 ]
+ },
+ { "time": 1, "x": 0.845, "y": 1.31 }
+ ]
+ },
+ "front-upper-arm": {
+ "rotate": [
+ {
+ "value": -85.92,
+ "curve": [ 0.08, -85.59, 0.284, -62.7 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -55.14,
+ "curve": [ 0.438, -48.65, 0.551, -43.21 ]
+ },
+ {
+ "time": 0.6333,
+ "value": -43.21,
+ "curve": [ 0.716, -43.22, 0.908, -85.92 ]
+ },
+ { "time": 1, "value": -85.92 }
+ ],
+ "translate": [
+ {
+ "x": -0.59,
+ "y": -2.94,
+ "curve": [ 0.1, -1.21, 0.275, -1.74, 0.092, -2.94, 0.275, -6.39 ]
+ },
+ {
+ "time": 0.3667,
+ "x": -1.74,
+ "y": -6.39,
+ "curve": [ 0.433, -1.74, 0.567, 0.72, 0.433, -6.39, 0.587, -4.48 ]
+ },
+ {
+ "time": 0.6333,
+ "x": 0.72,
+ "y": -4.21,
+ "curve": [ 0.725, 0.72, 0.908, -0.08, 0.743, -3.57, 0.908, -2.94 ]
+ },
+ { "time": 1, "x": -0.59, "y": -2.94 }
+ ]
+ },
+ "front-fist": {
+ "rotate": [
+ {
+ "value": 7.61,
+ "curve": [ 0.143, 7.62, 0.247, -23.17 ]
+ },
+ {
+ "time": 0.2667,
+ "value": -26.56,
+ "curve": [ 0.281, -29.08, 0.351, -37.36 ]
+ },
+ {
+ "time": 0.4333,
+ "value": -37.2,
+ "curve": [ 0.513, -37.05, 0.562, -29.88 ]
+ },
+ {
+ "time": 0.6,
+ "value": -25.18,
+ "curve": [ 0.621, -22.58, 0.694, -3.98 ]
+ },
+ {
+ "time": 0.8,
+ "value": 3.63,
+ "curve": [ 0.861, 8.03, 0.946, 7.57 ]
+ },
+ { "time": 1, "value": 7.61 }
+ ],
+ "translate": [
+ {
+ "curve": [ 0.117, 0, 0.35, 0.52, 0.117, 0, 0.35, -3.27 ]
+ },
+ {
+ "time": 0.4667,
+ "x": 0.52,
+ "y": -3.27,
+ "curve": [ 0.6, 0.52, 0.867, 0, 0.6, -3.27, 0.867, 0 ]
+ },
+ { "time": 1 }
+ ],
+ "shear": [
+ {
+ "y": 19.83,
+ "curve": [ 0.117, 0, 0.35, 15.28, 0.117, 19.83, 0.35, 28.31 ]
+ },
+ {
+ "time": 0.4667,
+ "x": 15.28,
+ "y": 28.31,
+ "curve": [ 0.6, 15.28, 0.867, 0, 0.6, 28.31, 0.867, 19.83 ]
+ },
+ { "time": 1, "y": 19.83 }
+ ]
+ },
+ "board-ik": {
+ "translate": [
+ {
+ "x": 393.62,
+ "curve": [ 0.083, 393.62, 0.25, 393.48, 0.083, 0, 0.25, 117.69 ]
+ },
+ {
+ "time": 0.3333,
+ "x": 393.48,
+ "y": 117.69,
+ "curve": [ 0.375, 393.48, 0.458, 393.62, 0.375, 117.69, 0.458, 83.82 ]
+ },
+ { "time": 0.5, "x": 393.62, "y": 83.82 },
+ { "time": 0.6667, "x": 393.62, "y": 30.15 },
+ { "time": 1, "x": 393.62 }
+ ]
+ },
+ "front-thigh": {
+ "translate": [
+ { "x": -7.49, "y": 8.51 }
+ ]
+ },
+ "front-leg-target": {
+ "translate": [
+ {
+ "time": 0.3667,
+ "curve": [ 0.428, 10.83, 0.567, 12.78, 0.414, 7.29, 0.567, 8.79 ]
+ },
+ {
+ "time": 0.6,
+ "x": 12.78,
+ "y": 8.79,
+ "curve": [ 0.692, 12.78, 0.772, 11.27, 0.692, 8.79, 0.766, 8.62 ]
+ },
+ { "time": 0.8667 }
+ ]
+ },
+ "rear-leg-target": {
+ "translate": [
+ {
+ "time": 0.4667,
+ "curve": [ 0.492, 0, 0.534, 4.47, 0.492, 0, 0.542, 1.63 ]
+ },
+ {
+ "time": 0.5667,
+ "x": 4.53,
+ "y": 1.77,
+ "curve": [ 0.622, 4.64, 0.717, 3.31, 0.615, 2.06, 0.71, 2.1 ]
+ },
+ { "time": 0.8 }
+ ]
+ },
+ "exhaust3": {
+ "scale": [
+ {
+ "x": 1.882,
+ "y": 0.81,
+ "curve": [ 0.017, 1.882, 0.167, 1.3, 0.017, 0.81, 0.167, 0.649 ]
+ },
+ {
+ "time": 0.2,
+ "x": 1.3,
+ "y": 0.649,
+ "curve": [ 0.225, 1.3, 0.275, 2.051, 0.225, 0.649, 0.275, 0.984 ]
+ },
+ {
+ "time": 0.3,
+ "x": 2.051,
+ "y": 0.984,
+ "curve": [ 0.325, 2.051, 0.375, 1.311, 0.325, 0.984, 0.384, 0.715 ]
+ },
+ {
+ "time": 0.4,
+ "x": 1.311,
+ "y": 0.686,
+ "curve": [ 0.433, 1.311, 0.5, 1.86, 0.426, 0.638, 0.5, 0.537 ]
+ },
+ {
+ "time": 0.5333,
+ "x": 1.86,
+ "y": 0.537,
+ "curve": [ 0.567, 1.86, 0.633, 1.187, 0.567, 0.537, 0.604, 0.854 ]
+ },
+ {
+ "time": 0.6667,
+ "x": 1.187,
+ "y": 0.854,
+ "curve": [ 0.7, 1.187, 0.767, 1.549, 0.707, 0.854, 0.774, 0.775 ]
+ },
+ {
+ "time": 0.8,
+ "x": 1.549,
+ "y": 0.746,
+ "curve": [ 0.817, 1.549, 0.85, 1.181, 0.815, 0.729, 0.85, 0.713 ]
+ },
+ {
+ "time": 0.8667,
+ "x": 1.181,
+ "y": 0.713,
+ "curve": [ 0.9, 1.181, 0.967, 1.882, 0.9, 0.713, 0.967, 0.81 ]
+ },
+ { "time": 1, "x": 1.882, "y": 0.81 }
+ ]
+ },
+ "side-glow1": {
+ "rotate": [
+ { "value": 51.12, "curve": "stepped" },
+ { "time": 0.0667, "value": 43.82, "curve": "stepped" },
+ { "time": 0.1, "value": 40.95, "curve": "stepped" },
+ { "time": 0.1667, "value": 27.78, "curve": "stepped" },
+ { "time": 0.2, "value": 10.24, "curve": "stepped" },
+ { "time": 0.2667, "curve": "stepped" },
+ { "time": 0.8, "value": -25.81 }
+ ],
+ "translate": [
+ { "x": 338.28, "y": 40.22, "curve": "stepped" },
+ { "time": 0.0667, "x": 331.2, "y": 30.39, "curve": "stepped" },
+ { "time": 0.1, "x": 318.63, "y": 20.59, "curve": "stepped" },
+ { "time": 0.1667, "x": 302.45, "y": 9.64, "curve": "stepped" },
+ { "time": 0.2, "x": 276.87, "y": 1.13, "curve": "stepped" },
+ { "time": 0.2667, "x": 248.16, "curve": "stepped" },
+ { "time": 0.3, "x": 221.36, "curve": "stepped" },
+ { "time": 0.3667, "x": 195.69, "curve": "stepped" },
+ { "time": 0.4, "x": 171.08, "curve": "stepped" },
+ { "time": 0.4667, "x": 144.84, "curve": "stepped" },
+ { "time": 0.5, "x": 121.22, "curve": "stepped" },
+ { "time": 0.5667, "x": 91.98, "curve": "stepped" },
+ { "time": 0.6, "x": 62.63, "curve": "stepped" },
+ { "time": 0.6667, "x": 30.78, "curve": "stepped" },
+ { "time": 0.7, "curve": "stepped" },
+ { "time": 0.7667, "x": -28.45, "curve": "stepped" },
+ { "time": 0.8, "x": -67.49, "y": 16.82, "curve": "stepped" },
+ { "time": 0.8667, "x": -83.07, "y": 24.36, "curve": "stepped" },
+ { "time": 0.9, "x": -93.81, "y": 29.55 }
+ ],
+ "scale": [
+ { "x": 0.535, "curve": "stepped" },
+ { "time": 0.0667, "x": 0.594, "curve": "stepped" },
+ { "time": 0.1, "x": 0.844, "curve": "stepped" },
+ { "time": 0.1667, "curve": "stepped" },
+ { "time": 0.8, "x": 0.534, "curve": "stepped" },
+ { "time": 0.8667, "x": 0.428, "y": 0.801, "curve": "stepped" },
+ { "time": 0.9, "x": 0.349, "y": 0.654 }
+ ]
+ },
+ "side-glow2": {
+ "rotate": [
+ { "time": 0.0667, "value": 51.12, "curve": "stepped" },
+ { "time": 0.1, "value": 43.82, "curve": "stepped" },
+ { "time": 0.1667, "value": 40.95, "curve": "stepped" },
+ { "time": 0.2, "value": 27.78, "curve": "stepped" },
+ { "time": 0.2667, "value": 10.24, "curve": "stepped" },
+ { "time": 0.3, "curve": "stepped" },
+ { "time": 0.8667, "value": -25.81 }
+ ],
+ "translate": [
+ { "time": 0.0667, "x": 338.28, "y": 40.22, "curve": "stepped" },
+ { "time": 0.1, "x": 331.2, "y": 30.39, "curve": "stepped" },
+ { "time": 0.1667, "x": 318.63, "y": 20.59, "curve": "stepped" },
+ { "time": 0.2, "x": 302.45, "y": 9.64, "curve": "stepped" },
+ { "time": 0.2667, "x": 276.87, "y": 1.13, "curve": "stepped" },
+ { "time": 0.3, "x": 248.16, "curve": "stepped" },
+ { "time": 0.3667, "x": 221.36, "curve": "stepped" },
+ { "time": 0.4, "x": 195.69, "curve": "stepped" },
+ { "time": 0.4667, "x": 171.08, "curve": "stepped" },
+ { "time": 0.5, "x": 144.84, "curve": "stepped" },
+ { "time": 0.5667, "x": 121.22, "curve": "stepped" },
+ { "time": 0.6, "x": 91.98, "curve": "stepped" },
+ { "time": 0.6667, "x": 62.63, "curve": "stepped" },
+ { "time": 0.7, "x": 30.78, "curve": "stepped" },
+ { "time": 0.7667, "curve": "stepped" },
+ { "time": 0.8, "x": -28.45, "curve": "stepped" },
+ { "time": 0.8667, "x": -67.49, "y": 16.82, "curve": "stepped" },
+ { "time": 0.9, "x": -83.07, "y": 24.36, "curve": "stepped" },
+ { "time": 0.9667, "x": -93.81, "y": 29.55 }
+ ],
+ "scale": [
+ { "time": 0.0667, "x": 0.535, "curve": "stepped" },
+ { "time": 0.1, "x": 0.594, "curve": "stepped" },
+ { "time": 0.1667, "x": 0.844, "curve": "stepped" },
+ { "time": 0.2, "curve": "stepped" },
+ { "time": 0.8667, "x": 0.534, "curve": "stepped" },
+ { "time": 0.9, "x": 0.428, "y": 0.801, "curve": "stepped" },
+ { "time": 0.9667, "x": 0.349, "y": 0.654 }
+ ]
+ },
+ "torso": {
+ "rotate": [
+ {
+ "value": -34.73,
+ "curve": [ 0.034, -36.31, 0.162, -39.33 ]
+ },
+ {
+ "time": 0.2667,
+ "value": -39.37,
+ "curve": [ 0.384, -39.37, 0.491, -29.52 ]
+ },
+ {
+ "time": 0.5,
+ "value": -28.86,
+ "curve": [ 0.525, -26.95, 0.571, -21.01 ]
+ },
+ {
+ "time": 0.6333,
+ "value": -21.01,
+ "curve": [ 0.725, -21.01, 0.969, -33.35 ]
+ },
+ { "time": 1, "value": -34.73 }
+ ]
+ },
+ "neck": {
+ "rotate": [
+ {
+ "value": 10.2,
+ "curve": [ 0.07, 12.09, 0.189, 16.03 ]
+ },
+ {
+ "time": 0.2667,
+ "value": 16.14,
+ "curve": [ 0.333, 16.14, 0.449, 8.03 ]
+ },
+ {
+ "time": 0.5,
+ "value": 5.83,
+ "curve": [ 0.542, 4.02, 0.6, 2.68 ]
+ },
+ {
+ "time": 0.6333,
+ "value": 2.68,
+ "curve": [ 0.725, 2.68, 0.943, 8.57 ]
+ },
+ { "time": 1, "value": 10.2 }
+ ]
+ },
+ "head": {
+ "rotate": [
+ {
+ "value": 10.2,
+ "curve": [ 0.044, 11.52, 0.2, 16.12 ]
+ },
+ {
+ "time": 0.2667,
+ "value": 16.14,
+ "curve": [ 0.375, 16.17, 0.492, 2.65 ]
+ },
+ {
+ "time": 0.6333,
+ "value": 2.68,
+ "curve": [ 0.725, 2.7, 0.963, 9.26 ]
+ },
+ { "time": 1, "value": 10.2 }
+ ],
+ "translate": [
+ {
+ "curve": [ 0.03, -0.24, 0.2, -4.22, 0.051, -1.06, 0.2, -3.62 ]
+ },
+ {
+ "time": 0.2667,
+ "x": -4.22,
+ "y": -3.62,
+ "curve": [ 0.358, -4.22, 0.542, 0.84, 0.358, -3.62, 0.542, 6.01 ]
+ },
+ {
+ "time": 0.6333,
+ "x": 0.84,
+ "y": 6.01,
+ "curve": [ 0.725, 0.84, 0.939, 0.32, 0.725, 6.01, 0.945, 1.14 ]
+ },
+ { "time": 1 }
+ ]
+ },
+ "front-bracer": {
+ "rotate": [
+ {
+ "value": -11.18,
+ "curve": [ 0.064, -14.82, 0.25, -20.01 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -20.01,
+ "curve": [ 0.429, -20.12, 0.58, 5.12 ]
+ },
+ {
+ "time": 0.6,
+ "value": 8.67,
+ "curve": [ 0.617, 11.72, 0.687, 20.52 ]
+ },
+ {
+ "time": 0.7667,
+ "value": 20.55,
+ "curve": [ 0.848, 20.7, 0.963, -9.43 ]
+ },
+ { "time": 1, "value": -11.18 }
+ ]
+ },
+ "hair3": {
+ "rotate": [
+ {
+ "value": 9.61,
+ "curve": [ 0.014, 8.51, 0.075, 2.63 ]
+ },
+ {
+ "time": 0.1,
+ "value": 2.63,
+ "curve": [ 0.15, 2.63, 0.25, 13.52 ]
+ },
+ {
+ "time": 0.3,
+ "value": 13.52,
+ "curve": [ 0.35, 13.52, 0.45, 11.28 ]
+ },
+ {
+ "time": 0.5,
+ "value": 11.28,
+ "curve": [ 0.575, 11.28, 0.725, 18.13 ]
+ },
+ {
+ "time": 0.8,
+ "value": 18.13,
+ "curve": [ 0.85, 18.13, 0.978, 11.07 ]
+ },
+ { "time": 1, "value": 9.61 }
+ ]
+ },
+ "hair4": {
+ "rotate": [
+ {
+ "value": -17.7,
+ "curve": [ 0.008, -17.7, 0.025, -23.73 ]
+ },
+ {
+ "time": 0.0333,
+ "value": -23.73,
+ "curve": [ 0.067, -23.73, 0.154, -4.4 ]
+ },
+ {
+ "time": 0.1667,
+ "value": -1.92,
+ "curve": [ 0.197, 4.09, 0.236, 12.91 ]
+ },
+ {
+ "time": 0.2667,
+ "value": 17.56,
+ "curve": [ 0.301, 22.68, 0.342, 27.97 ]
+ },
+ {
+ "time": 0.3667,
+ "value": 27.97,
+ "curve": [ 0.4, 27.97, 0.467, -1.45 ]
+ },
+ {
+ "time": 0.5,
+ "value": -1.45,
+ "curve": [ 0.517, -1.45, 0.55, 3.16 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 3.16,
+ "curve": [ 0.583, 3.16, 0.617, -8.9 ]
+ },
+ {
+ "time": 0.6333,
+ "value": -8.9,
+ "curve": [ 0.642, -8.9, 0.658, -5.4 ]
+ },
+ {
+ "time": 0.6667,
+ "value": -5.4,
+ "curve": [ 0.683, -5.4, 0.717, -15.32 ]
+ },
+ {
+ "time": 0.7333,
+ "value": -15.32,
+ "curve": [ 0.75, -15.32, 0.783, -9.19 ]
+ },
+ {
+ "time": 0.8,
+ "value": -9.19,
+ "curve": [ 0.817, -9.19, 0.85, -23.6 ]
+ },
+ {
+ "time": 0.8667,
+ "value": -23.6,
+ "curve": [ 0.883, -23.6, 0.917, -17.38 ]
+ },
+ {
+ "time": 0.9333,
+ "value": -17.38,
+ "curve": [ 0.942, -17.38, 0.958, -20.46 ]
+ },
+ {
+ "time": 0.9667,
+ "value": -20.46,
+ "curve": [ 0.975, -20.46, 0.992, -17.7 ]
+ },
+ { "time": 1, "value": -17.7 }
+ ]
+ },
+ "hair1": {
+ "rotate": [
+ {
+ "value": 9.61,
+ "curve": [ 0.06, 9.04, 0.25, 8.9 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 8.9,
+ "curve": [ 0.392, 8.9, 0.508, 14.58 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 14.58,
+ "curve": [ 0.675, 14.58, 0.956, 10.28 ]
+ },
+ { "time": 1, "value": 9.61 }
+ ]
+ },
+ "hair2": {
+ "rotate": [
+ {
+ "value": -3.82,
+ "curve": [ 0.017, -3.82, 0.064, -9.16 ]
+ },
+ {
+ "time": 0.1333,
+ "value": -9.09,
+ "curve": [ 0.178, -9.04, 0.234, 1.29 ]
+ },
+ {
+ "time": 0.2667,
+ "value": 5.98,
+ "curve": [ 0.276, 7.27, 0.336, 17.1 ]
+ },
+ {
+ "time": 0.3667,
+ "value": 17.1,
+ "curve": [ 0.413, 17.1, 0.467, 1.59 ]
+ },
+ {
+ "time": 0.5,
+ "value": 1.59,
+ "curve": [ 0.533, 1.59, 0.567, 13.63 ]
+ },
+ {
+ "time": 0.6,
+ "value": 13.63,
+ "curve": [ 0.617, 13.63, 0.683, 0.78 ]
+ },
+ {
+ "time": 0.7,
+ "value": 0.78,
+ "curve": [ 0.717, 0.78, 0.75, 12.01 ]
+ },
+ {
+ "time": 0.7667,
+ "value": 11.9,
+ "curve": [ 0.792, 11.73, 0.817, -0.85 ]
+ },
+ {
+ "time": 0.8333,
+ "value": -0.85,
+ "curve": [ 0.85, -0.85, 0.88, 1.99 ]
+ },
+ {
+ "time": 0.9,
+ "value": 1.82,
+ "curve": [ 0.916, 1.68, 0.95, -6.9 ]
+ },
+ {
+ "time": 0.9667,
+ "value": -6.9,
+ "curve": [ 0.975, -6.9, 0.992, -3.82 ]
+ },
+ { "time": 1, "value": -3.82 }
+ ]
+ },
+ "rear-upper-arm": {
+ "rotate": [
+ {
+ "value": 31.65,
+ "curve": [ 0.108, 31.65, 0.325, 13.01 ]
+ },
+ {
+ "time": 0.4333,
+ "value": 13.01,
+ "curve": [ 0.71, 13.01, 0.917, 31.65 ]
+ },
+ { "time": 1, "value": 31.65 }
+ ]
+ },
+ "rear-bracer": {
+ "rotate": [
+ {
+ "value": 31,
+ "curve": [ 0.108, 31, 0.325, 12.76 ]
+ },
+ {
+ "time": 0.4333,
+ "value": 12.79,
+ "curve": [ 0.587, 12.82, 0.917, 31 ]
+ },
+ { "time": 1, "value": 31 }
+ ]
+ },
+ "gun": {
+ "rotate": [
+ {
+ "value": 1.95,
+ "curve": [ 0.083, 1.95, 0.245, 36.73 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 36.71,
+ "curve": [ 0.439, 36.69, 0.589, 10.68 ]
+ },
+ {
+ "time": 0.6333,
+ "value": 8.75,
+ "curve": [ 0.701, 5.81, 0.917, 1.95 ]
+ },
+ { "time": 1, "value": 1.95 }
+ ]
+ },
+ "torso2": {
+ "rotate": [
+ {
+ "curve": [ 0.033, 0, 0.1, 2.35 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 2.35,
+ "curve": [ 0.225, 2.35, 0.408, -2.4 ]
+ },
+ {
+ "time": 0.5,
+ "value": -2.4,
+ "curve": [ 0.567, -2.4, 0.7, 1.44 ]
+ },
+ {
+ "time": 0.7667,
+ "value": 1.44,
+ "curve": [ 0.825, 1.44, 0.942, 0 ]
+ },
+ { "time": 1 }
+ ]
+ },
+ "torso3": {
+ "rotate": [
+ {
+ "curve": [ 0.063, 0.77, 0.106, 1.42 ]
+ },
+ {
+ "time": 0.1667,
+ "value": 1.42,
+ "curve": [ 0.259, 1.42, 0.344, -1.25 ]
+ },
+ {
+ "time": 0.4667,
+ "value": -1.26,
+ "curve": [ 0.656, -1.26, 0.917, -0.78 ]
+ },
+ { "time": 1 }
+ ]
+ },
+ "head-control": {
+ "translate": [
+ {
+ "x": 0.37,
+ "y": -11.17,
+ "curve": [ 0.133, 0.37, 0.335, -10.23, 0.133, -11.17, 0.335, 3.15 ]
+ },
+ {
+ "time": 0.5333,
+ "x": -10.23,
+ "y": 3.15,
+ "curve": [ 0.71, -10.23, 0.883, 0.37, 0.71, 3.15, 0.883, -11.17 ]
+ },
+ { "time": 1, "x": 0.37, "y": -11.17 }
+ ]
+ },
+ "front-shoulder": {
+ "translate": [
+ {
+ "x": 1.46,
+ "y": 10.15,
+ "curve": [ 0.103, 1.46, 0.249, 1.36, 0.103, 10.15, 0.249, -4.39 ]
+ },
+ {
+ "time": 0.4,
+ "x": 1.36,
+ "y": -4.39,
+ "curve": [ 0.621, 1.36, 0.85, 1.46, 0.621, -4.39, 0.85, 10.15 ]
+ },
+ { "time": 1, "x": 1.46, "y": 10.15 }
+ ]
+ },
+ "back-shoulder": {
+ "translate": [
+ {
+ "x": 1.4,
+ "y": 0.44,
+ "curve": [ 0.088, 1.4, 0.208, -2.47, 0.088, 0.44, 0.208, 8.61 ]
+ },
+ {
+ "time": 0.3333,
+ "x": -2.47,
+ "y": 8.61,
+ "curve": [ 0.572, -2.47, 0.833, 1.4, 0.572, 8.61, 0.833, 0.44 ]
+ },
+ { "time": 1, "x": 1.4, "y": 0.44 }
+ ]
+ }
+ },
+ "transform": {
+ "front-foot-board-transform": [
+ { "mixRotate": 0.997 }
+ ],
+ "rear-foot-board-transform": [
+ {}
+ ],
+ "toes-board": [
+ { "mixX": 0, "mixScaleX": 0, "mixShearY": 0 }
+ ]
+ },
+ "attachments": {
+ "default": {
+ "front-foot": {
+ "front-foot": {
+ "deform": [
+ {
+ "offset": 26,
+ "vertices": [ -0.02832, -5.37024, -0.02832, -5.37024, 3.8188, -3.7757, -0.02832, -5.37024, -3.82159, 3.77847 ]
+ }
+ ]
+ }
+ },
+ "front-shin": {
+ "front-shin": {
+ "deform": [
+ {
+ "offset": 14,
+ "vertices": [ 0.5298, -1.12677, -0.85507, -4.20587, -11.35158, -10.19225, -10.79865, -8.43765, -6.06447, -6.89757, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.54892, -3.06021, 1.48463, -2.29663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4.80437, -7.01817 ]
+ },
+ {
+ "time": 0.3667,
+ "offset": 14,
+ "vertices": [ 0.5298, -1.12677, -11.66571, -9.07211, -25.65866, -17.53735, -25.53217, -16.50978, -11.78232, -11.26097, 0, 0, 0.60487, -1.63589, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.60487, -1.63589, 0, 0, -2.64522, -7.35739, 1.48463, -2.29663, 0, 0, 0, 0, 0, 0, 0.60487, -1.63589, 0.60487, -1.63589, 0.60487, -1.63589, 0.60487, -1.63589, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.60487, -1.63589, 0, 0, -10.06873, -12.0999 ]
+ },
+ {
+ "time": 0.5333,
+ "offset": 14,
+ "vertices": [ 0.5298, -1.12677, -0.85507, -4.20587, -7.00775, -8.24771, -6.45482, -6.49312, -6.06447, -6.89757, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.54892, -3.06021, 1.48463, -2.29663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4.80437, -7.01817 ]
+ },
+ {
+ "time": 1,
+ "offset": 14,
+ "vertices": [ 0.5298, -1.12677, -0.85507, -4.20587, -11.35158, -10.19225, -10.79865, -8.43765, -6.06447, -6.89757, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.54892, -3.06021, 1.48463, -2.29663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4.80437, -7.01817 ]
+ }
+ ]
+ }
+ },
+ "hoverboard-board": {
+ "hoverboard-board": {
+ "deform": [
+ {
+ "curve": [ 0.067, 0, 0.2, 1 ]
+ },
+ {
+ "time": 0.2667,
+ "offset": 1,
+ "vertices": [ 2.45856, 0, 0, 0, 0, 0, 0, 0, 0, 3.55673, -3.0E-4, 3.55673, -3.0E-4, 0, 0, 0, 0, 0, 0, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, 0, 0, 0, 0, 0, 0, 0, 0, -4.90558, 0.11214, -9.40706, 6.2E-4, -6.34871, 4.3E-4, -6.34925, -6.57018, -6.34925, -6.57018, -6.34871, 4.3E-4, -2.3308, 1.7E-4, -2.33133, -6.57045, -2.33133, -6.57045, -2.3308, 1.7E-4, 0, 0, 1.2E-4, 2.45856, 1.2E-4, 2.45856, 1.2E-4, 2.45856, 1.2E-4, 2.45856, 3.3297, 4.44005, 3.3297, 4.44005, 3.3297, 4.44005, 1.2E-4, 2.45856, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2.46227, 1.7E-4, -2.46227, 1.7E-4, -2.52316, 1.1313, -2.52316, 1.1313, -2.52316, 1.1313, 1.2E-4, 2.45856, 1.2E-4, 2.45856, -9.40694, 2.45918, 1.88063, 0.44197, -2.9E-4, -3.54808, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2.52316, 1.1313, -2.52316, 1.1313, -2.52316, 1.1313, -2.46227, 1.7E-4, -2.46227, 1.7E-4, -2.46227, 1.7E-4, 0, 0, 0, 0, 1.2E-4, 2.45856 ],
+ "curve": [ 0.45, 0, 0.817, 1 ]
+ },
+ { "time": 1 }
+ ]
+ }
+ },
+ "rear-foot": {
+ "rear-foot": {
+ "deform": [
+ {
+ "offset": 28,
+ "vertices": [ -1.93078, 1.34782, -0.31417, 2.33363, 3.05122, 0.33946, 2.31472, -2.01678, 2.17583, -2.05795, -0.04277, -2.99459, 1.15429, 0.26328, 0.97501, -0.67169 ]
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "idle": {
+ "slots": {
+ "front-fist": {
+ "attachment": [
+ { "name": "front-fist-open" }
+ ]
+ }
+ },
+ "bones": {
+ "front-foot-target": {
+ "translate": [
+ { "x": -69.06 }
+ ]
+ },
+ "hip": {
+ "rotate": [
+ {
+ "curve": [ 0.073, 0.35, 0.303, 1.27 ]
+ },
+ {
+ "time": 0.4,
+ "value": 1.28,
+ "curve": [ 0.615, 1.3, 0.847, -1.41 ]
+ },
+ {
+ "time": 1.2,
+ "value": -1.38,
+ "curve": [ 1.344, -1.37, 1.602, -0.28 ]
+ },
+ { "time": 1.6667 }
+ ],
+ "translate": [
+ {
+ "x": -11.97,
+ "y": -23.15,
+ "curve": [ 0.059, -12.96, 0.258, -15.19, 0.142, -23.15, 0.341, -24.89 ]
+ },
+ {
+ "time": 0.4667,
+ "x": -15.14,
+ "y": -26.74,
+ "curve": [ 0.62, -15.1, 0.788, -13.28, 0.597, -28.66, 0.75, -30.01 ]
+ },
+ {
+ "time": 0.9,
+ "x": -12.02,
+ "y": -30.01,
+ "curve": [ 0.978, -11.13, 1.175, -9.05, 1.036, -29.94, 1.234, -28.08 ]
+ },
+ {
+ "time": 1.3333,
+ "x": -9.06,
+ "y": -26.64,
+ "curve": [ 1.501, -9.06, 1.614, -10.95, 1.454, -24.89, 1.609, -23.15 ]
+ },
+ { "time": 1.6667, "x": -11.97, "y": -23.15 }
+ ]
+ },
+ "rear-foot-target": {
+ "translate": [
+ { "x": 48.87 }
+ ]
+ },
+ "front-upper-arm": {
+ "rotate": [
+ {
+ "value": -60.87,
+ "curve": [ 0.154, -60.85, 0.452, -68.65 ]
+ },
+ {
+ "time": 0.8333,
+ "value": -68.65,
+ "curve": [ 1.221, -68.65, 1.542, -60.87 ]
+ },
+ { "time": 1.6667, "value": -60.87 }
+ ]
+ },
+ "front-bracer": {
+ "rotate": [
+ {
+ "value": 42.46,
+ "curve": [ 0.029, 42.97, 0.134, 45.28 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 45.27,
+ "curve": [ 0.578, 45.26, 0.798, 40.07 ]
+ },
+ {
+ "time": 0.8333,
+ "value": 39.74,
+ "curve": [ 0.878, 39.32, 1.019, 38.23 ]
+ },
+ {
+ "time": 1.2,
+ "value": 38.22,
+ "curve": [ 1.377, 38.22, 1.619, 41.68 ]
+ },
+ { "time": 1.6667, "value": 42.46 }
+ ]
+ },
+ "rear-upper-arm": {
+ "rotate": [
+ {
+ "value": 39.2,
+ "curve": [ 0.185, 39.22, 0.5, 29.37 ]
+ },
+ {
+ "time": 0.6667,
+ "value": 29.37,
+ "curve": [ 0.917, 29.37, 1.417, 39.2 ]
+ },
+ { "time": 1.6667, "value": 39.2 }
+ ]
+ },
+ "head": {
+ "rotate": [
+ {
+ "value": -6.75,
+ "curve": [ 0.176, -7.88, 0.349, -8.95 ]
+ },
+ {
+ "time": 0.4667,
+ "value": -8.95,
+ "curve": [ 0.55, -8.95, 0.697, -6.77 ]
+ },
+ {
+ "time": 0.8333,
+ "value": -5.44,
+ "curve": [ 0.88, -4.98, 1.05, -4.12 ]
+ },
+ {
+ "time": 1.1333,
+ "value": -4.12,
+ "curve": [ 1.266, -4.12, 1.469, -5.48 ]
+ },
+ { "time": 1.6667, "value": -6.75 }
+ ]
+ },
+ "front-fist": {
+ "rotate": [
+ {
+ "curve": [ 0.086, 0, 0.233, 2.48 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 4.13,
+ "curve": [ 0.429, 5.7, 0.711, 10.06 ]
+ },
+ {
+ "time": 0.8333,
+ "value": 10.06,
+ "curve": [ 0.926, 10.06, 1.092, 4.21 ]
+ },
+ {
+ "time": 1.2,
+ "value": 2.78,
+ "curve": [ 1.349, 0.8, 1.551, 0 ]
+ },
+ { "time": 1.6667 }
+ ]
+ },
+ "rear-bracer": {
+ "rotate": [
+ {
+ "curve": [ 0.063, 0.54, 0.367, 3.39 ]
+ },
+ {
+ "time": 0.5333,
+ "value": 3.39,
+ "curve": [ 0.696, 3.39, 0.939, -1.63 ]
+ },
+ {
+ "time": 1.2,
+ "value": -1.61,
+ "curve": [ 1.42, -1.59, 1.574, -0.67 ]
+ },
+ { "time": 1.6667 }
+ ]
+ },
+ "gun": {
+ "rotate": [
+ {
+ "curve": [ 0.099, 0.27, 0.367, 1.23 ]
+ },
+ {
+ "time": 0.5333,
+ "value": 1.23,
+ "curve": [ 0.665, 1.23, 0.937, -0.56 ]
+ },
+ {
+ "time": 1.1333,
+ "value": -0.55,
+ "curve": [ 1.316, -0.55, 1.582, -0.21 ]
+ },
+ { "time": 1.6667 }
+ ]
+ },
+ "torso": {
+ "rotate": [
+ {
+ "value": -22.88,
+ "curve": [ 0.099, -23.45, 0.363, -24.74 ]
+ },
+ {
+ "time": 0.5333,
+ "value": -24.74,
+ "curve": [ 0.706, -24.74, 0.961, -20.97 ]
+ },
+ {
+ "time": 1.1333,
+ "value": -20.97,
+ "curve": [ 1.355, -20.97, 1.567, -22.28 ]
+ },
+ { "time": 1.6667, "value": -22.88 }
+ ]
+ },
+ "neck": {
+ "rotate": [
+ {
+ "value": 3.78,
+ "curve": [ 0.167, 3.78, 0.5, 5.45 ]
+ },
+ {
+ "time": 0.6667,
+ "value": 5.45,
+ "curve": [ 0.917, 5.45, 1.417, 3.78 ]
+ },
+ { "time": 1.6667, "value": 3.78 }
+ ]
+ },
+ "torso2": {
+ "rotate": [
+ {
+ "curve": [ 0.067, 0.33, 0.341, 2.54 ]
+ },
+ {
+ "time": 0.5333,
+ "value": 2.54,
+ "curve": [ 0.734, 2.55, 0.982, -0.94 ]
+ },
+ {
+ "time": 1.1333,
+ "value": -0.93,
+ "curve": [ 1.365, -0.91, 1.549, -0.56 ]
+ },
+ { "time": 1.6667 }
+ ]
+ },
+ "torso3": {
+ "rotate": [
+ {
+ "value": -2.15,
+ "curve": [ 0.052, -1.9, 0.384, -0.15 ]
+ },
+ {
+ "time": 0.5333,
+ "value": -0.14,
+ "curve": [ 0.762, -0.13, 0.895, -3.1 ]
+ },
+ {
+ "time": 1.1333,
+ "value": -3.1,
+ "curve": [ 1.348, -3.1, 1.592, -2.46 ]
+ },
+ { "time": 1.6667, "value": -2.15 }
+ ]
+ },
+ "hair1": {
+ "rotate": [
+ {
+ "curve": [ 0.067, 0, 0.213, 2.86 ]
+ },
+ {
+ "time": 0.2667,
+ "value": 3.65,
+ "curve": [ 0.358, 4.99, 0.535, 7.92 ]
+ },
+ {
+ "time": 0.6667,
+ "value": 7.92,
+ "curve": [ 0.809, 7.92, 1.067, 5.49 ]
+ },
+ {
+ "time": 1.1333,
+ "value": 4.7,
+ "curve": [ 1.245, 3.34, 1.525, 0 ]
+ },
+ { "time": 1.6667 }
+ ]
+ },
+ "hair2": {
+ "rotate": [
+ {
+ "curve": [ 0.067, 0, 0.225, -7.97 ]
+ },
+ {
+ "time": 0.2667,
+ "value": -9.75,
+ "curve": [ 0.316, -11.84, 0.519, -16.66 ]
+ },
+ {
+ "time": 0.6667,
+ "value": -16.66,
+ "curve": [ 0.817, -16.66, 1.029, -11.43 ]
+ },
+ {
+ "time": 1.1333,
+ "value": -9.14,
+ "curve": [ 1.25, -6.56, 1.525, 0 ]
+ },
+ { "time": 1.6667 }
+ ]
+ },
+ "hair3": {
+ "rotate": [
+ {
+ "curve": [ 0.1, 0, 0.3, 1.32 ]
+ },
+ {
+ "time": 0.4,
+ "value": 1.32,
+ "curve": [ 0.55, 1.32, 0.866, 0.93 ]
+ },
+ {
+ "time": 1,
+ "value": 0.73,
+ "curve": [ 1.189, 0.46, 1.5, 0 ]
+ },
+ { "time": 1.6667 }
+ ]
+ },
+ "hair4": {
+ "rotate": [
+ {
+ "curve": [ 0.118, -0.44, 0.3, -8.52 ]
+ },
+ {
+ "time": 0.4,
+ "value": -8.52,
+ "curve": [ 0.55, -8.52, 0.85, 1.96 ]
+ },
+ {
+ "time": 1,
+ "value": 1.96,
+ "curve": [ 1.167, 1.96, 1.577, 0.38 ]
+ },
+ { "time": 1.6667 }
+ ]
+ },
+ "head-control": {
+ "translate": [
+ {
+ "curve": [ 0.098, 1.46, 0.3, 4.49, 0.17, 0.13, 0.316, -3.28 ]
+ },
+ {
+ "time": 0.4,
+ "x": 4.55,
+ "y": -5.95,
+ "curve": [ 0.53, 4.64, 0.776, 2.59, 0.492, -8.89, 0.668, -14.21 ]
+ },
+ {
+ "time": 0.8667,
+ "x": 1.42,
+ "y": -14.26,
+ "curve": [ 0.966, 0.15, 1.109, -2.91, 0.994, -14.26, 1.144, -10.58 ]
+ },
+ {
+ "time": 1.2333,
+ "x": -3.02,
+ "y": -8.26,
+ "curve": [ 1.342, -3.02, 1.568, -1.48, 1.317, -6.1, 1.558, 0 ]
+ },
+ { "time": 1.6667 }
+ ]
+ },
+ "front-shoulder": {
+ "translate": [
+ {
+ "curve": [ 0.21, 0, 0.525, -1.72, 0.21, 0, 0.525, 4.08 ]
+ },
+ {
+ "time": 0.8333,
+ "x": -1.72,
+ "y": 4.08,
+ "curve": [ 1.15, -1.72, 1.46, 0, 1.15, 4.08, 1.46, 0 ]
+ },
+ { "time": 1.6667 }
+ ]
+ }
+ }
+ },
+ "idle-turn": {
+ "slots": {
+ "front-fist": {
+ "attachment": [
+ { "name": "front-fist-open" }
+ ]
+ }
+ },
+ "bones": {
+ "front-upper-arm": {
+ "rotate": [
+ {
+ "value": -302.77,
+ "curve": [ 0, -406.9, 0.125, -420.87 ]
+ },
+ { "time": 0.2667, "value": -420.87 }
+ ],
+ "translate": [
+ {
+ "x": 2.24,
+ "y": -4.98,
+ "curve": [ 0.067, 2.24, 0.111, 0, 0.067, -4.98, 0.111, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "rear-upper-arm": {
+ "rotate": [
+ {
+ "value": 248.56,
+ "curve": [ 0, 371.28, 0.062, 399.2 ]
+ },
+ { "time": 0.1333, "value": 399.2 }
+ ],
+ "translate": [
+ {
+ "x": -2.84,
+ "y": 37.28,
+ "curve": [ 0.033, -2.84, 0.069, 0, 0.033, 37.28, 0.069, 0 ]
+ },
+ { "time": 0.1333 }
+ ]
+ },
+ "gun": {
+ "rotate": [
+ {
+ "value": -3.95,
+ "curve": [ 0, -10.4, 0.019, -20.43 ]
+ },
+ {
+ "time": 0.0333,
+ "value": -20.45,
+ "curve": [ 0.044, -20.47, 0.125, 0 ]
+ },
+ { "time": 0.2 }
+ ]
+ },
+ "neck": {
+ "rotate": [
+ {
+ "value": 17.2,
+ "curve": [ 0, 6.27, 0.125, 3.78 ]
+ },
+ { "time": 0.2667, "value": 3.78 }
+ ]
+ },
+ "hip": {
+ "translate": [
+ {
+ "x": -2.69,
+ "y": -6.79,
+ "curve": [ 0.067, -2.69, 0.2, -11.97, 0.067, -6.79, 0.2, -23.15 ]
+ },
+ { "time": 0.2667, "x": -11.97, "y": -23.15 }
+ ]
+ },
+ "front-fist": {
+ "rotate": [
+ {
+ "value": -15.54,
+ "curve": [ 0, -3.08, 0.034, 18.44 ]
+ },
+ {
+ "time": 0.0667,
+ "value": 19.02,
+ "curve": [ 0.108, 19.75, 0.169, 0 ]
+ },
+ { "time": 0.2667 }
+ ],
+ "scale": [
+ {
+ "x": 0.94,
+ "curve": [ 0, 0.962, 0.024, 1.237, 0, 1, 0.026, 0.947 ]
+ },
+ {
+ "time": 0.0667,
+ "x": 1.236,
+ "y": 0.947,
+ "curve": [ 0.117, 1.235, 0.189, 1, 0.117, 0.947, 0.189, 1 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "rear-bracer": {
+ "rotate": [
+ {
+ "value": 11.75,
+ "curve": [ 0, -7.97, 0.017, -33.4 ]
+ },
+ {
+ "time": 0.0333,
+ "value": -33.39,
+ "curve": [ 0.049, -33.37, 0.131, 0 ]
+ },
+ { "time": 0.2 }
+ ]
+ },
+ "torso": {
+ "rotate": [
+ {
+ "value": -18.25,
+ "curve": [ 0, -10.59, 0.125, -22.88 ]
+ },
+ { "time": 0.2667, "value": -22.88 }
+ ],
+ "scale": [
+ {
+ "y": 1.03,
+ "curve": [ 0.067, 1, 0.132, 1, 0.067, 1.03, 0.132, 1 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "head": {
+ "rotate": [
+ {
+ "value": 5.12,
+ "curve": [ 0, -6.34, 0.125, -6.75 ]
+ },
+ { "time": 0.2667, "value": -6.75 }
+ ],
+ "scale": [
+ {
+ "y": 1.03,
+ "curve": [ 0.067, 1, 0.107, 1, 0.067, 1.03, 0.107, 1 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "rear-foot-target": {
+ "translate": [
+ {
+ "x": -58.39,
+ "y": 30.48,
+ "curve": [ 0, -7.15, 0.047, 16.62, 0, 12.71, 0.039, 0.22 ]
+ },
+ {
+ "time": 0.1,
+ "x": 34.14,
+ "y": -0.19,
+ "curve": [ 0.136, 45.79, 0.163, 48.87, 0.133, -0.41, 0.163, 0 ]
+ },
+ { "time": 0.2, "x": 48.87 }
+ ]
+ },
+ "front-bracer": {
+ "rotate": [
+ {
+ "value": 6.69,
+ "curve": [ 0, 19.76, 0.039, 56.53 ]
+ },
+ {
+ "time": 0.0667,
+ "value": 56.63,
+ "curve": [ 0.114, 56.79, 0.189, 42.46 ]
+ },
+ { "time": 0.2667, "value": 42.46 }
+ ]
+ },
+ "front-foot-target": {
+ "rotate": [
+ {
+ "value": -1.85,
+ "curve": [ 0.014, -8.91, 0.047, -28.4 ]
+ },
+ {
+ "time": 0.1,
+ "value": -28.89,
+ "curve": [ 0.144, -29.29, 0.262, -21.77 ]
+ },
+ { "time": 0.2667 }
+ ],
+ "translate": [
+ {
+ "x": 9.97,
+ "y": 0.82,
+ "curve": [ 0, -54.41, 0.078, -69.06, 0, 0.15, 0.078, 0 ]
+ },
+ { "time": 0.1667, "x": -69.06 }
+ ]
+ },
+ "hair3": {
+ "rotate": [
+ {
+ "value": -9.01,
+ "curve": [ 0.044, -9.01, 0.072, 7.41 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 10.08,
+ "curve": [ 0.166, 11.47, 0.208, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "hair4": {
+ "rotate": [
+ {
+ "value": -16.49,
+ "curve": [ 0.044, -16.49, 0.101, -5.98 ]
+ },
+ {
+ "time": 0.1333,
+ "value": -2.95,
+ "curve": [ 0.162, -0.34, 0.208, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "hair1": {
+ "rotate": [
+ {
+ "value": -3.85,
+ "curve": [ 0.044, -3.85, 0.072, 6.91 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 8.05,
+ "curve": [ 0.166, 8.65, 0.208, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "hair2": {
+ "rotate": [
+ {
+ "value": 1.25,
+ "curve": [ 0.044, 1.25, 0.072, 8.97 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 8.6,
+ "curve": [ 0.166, 8.4, 0.208, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "front-thigh": {
+ "translate": [
+ {
+ "x": 12.21,
+ "y": 1.89,
+ "curve": [ 0.033, 12.21, 0.1, 0, 0.033, 1.89, 0.1, 0 ]
+ },
+ { "time": 0.1333 }
+ ]
+ },
+ "rear-thigh": {
+ "translate": [
+ {
+ "x": -16.11,
+ "y": -1.38,
+ "curve": [ 0.033, -16.11, 0.1, 0, 0.033, -1.38, 0.1, 0 ]
+ },
+ { "time": 0.1333 }
+ ]
+ },
+ "torso3": {
+ "rotate": [
+ { "time": 0.2667, "value": -2.15 }
+ ]
+ },
+ "head-control": {
+ "translate": [
+ {
+ "x": -13.72,
+ "y": -34.7,
+ "curve": [ 0.067, -13.72, 0.2, 0, 0.067, -34.7, 0.2, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "front-shoulder": {
+ "translate": [
+ {
+ "x": 1.13,
+ "y": -14.31,
+ "curve": [ 0.067, 1.13, 0.2, 0, 0.067, -14.31, 0.2, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ }
+ }
+ },
+ "jump": {
+ "slots": {
+ "front-fist": {
+ "attachment": [
+ { "name": "front-fist-open" },
+ { "time": 0.1, "name": "front-fist-closed" },
+ { "time": 0.8333, "name": "front-fist-open" }
+ ]
+ }
+ },
+ "bones": {
+ "front-thigh": {
+ "rotate": [
+ {
+ "value": 55.08,
+ "curve": [ 0.007, 46.66, 0.043, 26.3 ]
+ },
+ {
+ "time": 0.0667,
+ "value": 22.84,
+ "curve": [ 0.1, 17.99, 0.165, 15.78 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 15.71,
+ "curve": [ 0.309, 15.63, 0.408, 46.67 ]
+ },
+ {
+ "time": 0.5,
+ "value": 63.6,
+ "curve": [ 0.56, 74.72, 0.762, 91.48 ]
+ },
+ {
+ "time": 0.9667,
+ "value": 91.81,
+ "curve": [ 1.068, 92.01, 1.096, 22.05 ]
+ },
+ {
+ "time": 1.1667,
+ "value": 22.25,
+ "curve": [ 1.18, 22.29, 1.176, 56.17 ]
+ },
+ {
+ "time": 1.2,
+ "value": 56.16,
+ "curve": [ 1.246, 56.15, 1.263, 54.94 ]
+ },
+ { "time": 1.3333, "value": 55.08 }
+ ],
+ "translate": [
+ { "x": -5.13, "y": 11.55 }
+ ]
+ },
+ "torso": {
+ "rotate": [
+ {
+ "value": -45.57,
+ "curve": [ 0.022, -44.61, 0.03, -39.06 ]
+ },
+ {
+ "time": 0.0667,
+ "value": -35.29,
+ "curve": [ 0.12, -29.77, 0.28, -19.95 ]
+ },
+ {
+ "time": 0.4333,
+ "value": -19.95,
+ "curve": [ 0.673, -19.95, 0.871, -22.38 ]
+ },
+ {
+ "time": 0.9667,
+ "value": -27.08,
+ "curve": [ 1.094, -33.33, 1.176, -44.93 ]
+ },
+ { "time": 1.3333, "value": -45.57 }
+ ],
+ "translate": [
+ { "x": -3.79, "y": -0.77 }
+ ]
+ },
+ "rear-thigh": {
+ "rotate": [
+ {
+ "value": 12.81,
+ "curve": [ 0.067, 12.81, 0.242, 67.88 ]
+ },
+ {
+ "time": 0.2667,
+ "value": 74.11,
+ "curve": [ 0.314, 86.02, 0.454, 92.23 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 92.24,
+ "curve": [ 0.753, 92.26, 0.966, 67.94 ]
+ },
+ {
+ "time": 1,
+ "value": 61.32,
+ "curve": [ 1.039, 53.75, 1.218, 12.68 ]
+ },
+ { "time": 1.3333, "value": 12.81 }
+ ]
+ },
+ "rear-shin": {
+ "rotate": [
+ {
+ "value": -115.64,
+ "curve": [ 0.067, -117.17, 0.125, -117.15 ]
+ },
+ {
+ "time": 0.1667,
+ "value": -117.15,
+ "curve": [ 0.225, -117.15, 0.332, -108.76 ]
+ },
+ {
+ "time": 0.4,
+ "value": -107.15,
+ "curve": [ 0.48, -105.26, 0.685, -103.49 ]
+ },
+ {
+ "time": 0.7667,
+ "value": -101.97,
+ "curve": [ 0.826, -100.87, 0.919, -92.3 ]
+ },
+ {
+ "time": 1,
+ "value": -92.28,
+ "curve": [ 1.113, -92.26, 1.297, -114.22 ]
+ },
+ { "time": 1.3333, "value": -115.64 }
+ ]
+ },
+ "front-upper-arm": {
+ "rotate": [
+ {
+ "value": -40.21,
+ "curve": [ 0.054, -35.46, 0.15, -31.12 ]
+ },
+ {
+ "time": 0.2,
+ "value": -31.12,
+ "curve": [ 0.308, -31.12, 0.547, -80.12 ]
+ },
+ {
+ "time": 0.6333,
+ "value": -96.56,
+ "curve": [ 0.697, -108.56, 0.797, -112.54 ]
+ },
+ {
+ "time": 0.8667,
+ "value": -112.6,
+ "curve": [ 1.137, -112.84, 1.274, -49.19 ]
+ },
+ { "time": 1.3333, "value": -40.21 }
+ ]
+ },
+ "front-bracer": {
+ "rotate": [
+ {
+ "value": 20.54,
+ "curve": [ 0.054, 32.23, 0.192, 55.84 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 62.58,
+ "curve": [ 0.29, 71.87, 0.375, 79.28 ]
+ },
+ {
+ "time": 0.4333,
+ "value": 79.18,
+ "curve": [ 0.555, 78.98, 0.684, 27.54 ]
+ },
+ {
+ "time": 0.7333,
+ "value": 13.28,
+ "curve": [ 0.786, -1.85, 0.874, -24.76 ]
+ },
+ {
+ "time": 1,
+ "value": -25.45,
+ "curve": [ 1.165, -26.36, 1.303, 9.1 ]
+ },
+ { "time": 1.3333, "value": 20.54 }
+ ]
+ },
+ "front-fist": {
+ "rotate": [
+ {
+ "value": -36.16,
+ "curve": [ 0.114, -39.59, 0.3, -45.61 ]
+ },
+ {
+ "time": 0.4,
+ "value": -45.61,
+ "curve": [ 0.442, -45.61, 0.537, -21.54 ]
+ },
+ {
+ "time": 0.5667,
+ "value": -15.4,
+ "curve": [ 0.592, -10.23, 0.692, 11.89 ]
+ },
+ {
+ "time": 0.7333,
+ "value": 11.73,
+ "curve": [ 0.783, 11.54, 0.831, 1.8 ]
+ },
+ {
+ "time": 0.8667,
+ "value": -5.78,
+ "curve": [ 0.897, -12.22, 0.901, -14.22 ]
+ },
+ {
+ "time": 0.9333,
+ "value": -14.51,
+ "curve": [ 0.974, -14.89, 0.976, 10.38 ]
+ },
+ {
+ "time": 1,
+ "value": 10.55,
+ "curve": [ 1.027, 10.74, 1.023, -8.44 ]
+ },
+ {
+ "time": 1.0333,
+ "value": -8.42,
+ "curve": [ 1.059, -8.36, 1.074, 10.12 ]
+ },
+ {
+ "time": 1.1,
+ "value": 10.22,
+ "curve": [ 1.168, 10.48, 1.27, -36.07 ]
+ },
+ { "time": 1.3333, "value": -36.16 }
+ ]
+ },
+ "rear-upper-arm": {
+ "rotate": [
+ {
+ "value": 40.5,
+ "curve": [ 0.048, 36.1, 0.168, 20.45 ]
+ },
+ {
+ "time": 0.3,
+ "value": 20.45,
+ "curve": [ 0.476, 20.45, 0.571, 33.76 ]
+ },
+ {
+ "time": 0.6,
+ "value": 38.67,
+ "curve": [ 0.642, 45.8, 0.681, 57.44 ]
+ },
+ {
+ "time": 0.7333,
+ "value": 62.91,
+ "curve": [ 0.829, 72.8, 0.996, 77.61 ]
+ },
+ {
+ "time": 1.0333,
+ "value": 80.37,
+ "curve": [ 1.082, 83.94, 1.148, 90.6 ]
+ },
+ {
+ "time": 1.2,
+ "value": 90.6,
+ "curve": [ 1.248, 90.46, 1.317, 53.07 ]
+ },
+ { "time": 1.3333, "value": 49.06 }
+ ]
+ },
+ "rear-bracer": {
+ "rotate": [
+ {
+ "value": 28.28,
+ "curve": [ 0.022, 25.12, 0.187, -0.89 ]
+ },
+ {
+ "time": 0.2,
+ "value": -2.52,
+ "curve": [ 0.257, -9.92, 0.372, -17.38 ]
+ },
+ {
+ "time": 0.4333,
+ "value": -17.41,
+ "curve": [ 0.54, -17.47, 0.659, -16.91 ]
+ },
+ {
+ "time": 0.7667,
+ "value": -12.1,
+ "curve": [ 0.907, -5.79, 1.025, 14.58 ]
+ },
+ {
+ "time": 1.1,
+ "value": 20.58,
+ "curve": [ 1.191, 27.85, 1.283, 29.67 ]
+ },
+ { "time": 1.3333, "value": 29.67 }
+ ]
+ },
+ "neck": {
+ "rotate": [
+ {
+ "value": 11.88,
+ "curve": [ 0.104, 11.82, 0.179, 11.15 ]
+ },
+ {
+ "time": 0.2,
+ "value": 10.08,
+ "curve": [ 0.255, 7.29, 0.405, -8.15 ]
+ },
+ {
+ "time": 0.4333,
+ "value": -9.35,
+ "curve": [ 0.508, -12.48, 0.595, -13.14 ]
+ },
+ {
+ "time": 0.6667,
+ "value": -12.61,
+ "curve": [ 0.714, -12.26, 0.815, -5.57 ]
+ },
+ {
+ "time": 0.8333,
+ "value": -4.08,
+ "curve": [ 0.883, -0.07, 1.045, 12.77 ]
+ },
+ {
+ "time": 1.1,
+ "value": 15.06,
+ "curve": [ 1.208, 19.6, 1.279, 20.64 ]
+ },
+ { "time": 1.3333, "value": 20.73 }
+ ]
+ },
+ "head": {
+ "rotate": [
+ {
+ "value": 13.14,
+ "curve": [ 0.008, 12.19, 0.197, -23.53 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -23.95,
+ "curve": [ 0.509, -23.95, 0.667, -2.66 ]
+ },
+ {
+ "time": 0.7333,
+ "value": -2.66,
+ "curve": [ 0.792, -2.66, 0.908, -13.32 ]
+ },
+ {
+ "time": 0.9667,
+ "value": -13.32,
+ "curve": [ 1.158, -13.11, 1.241, -1.58 ]
+ },
+ { "time": 1.3333, "value": -1.58 }
+ ],
+ "scale": [
+ {
+ "curve": [ 0.041, 1, 0.052, 0.962, 0.041, 1, 0.052, 1.137 ]
+ },
+ {
+ "time": 0.1,
+ "x": 0.954,
+ "y": 1.137,
+ "curve": [ 0.202, 0.962, 0.318, 1, 0.202, 1.137, 0.252, 1.002 ]
+ },
+ { "time": 0.4667 },
+ {
+ "time": 1.0667,
+ "x": 1.002,
+ "curve": [ 1.092, 1.002, 1.126, 1.143, 1.092, 1, 1.128, 0.975 ]
+ },
+ {
+ "time": 1.1667,
+ "x": 1.144,
+ "y": 0.973,
+ "curve": [ 1.204, 1.145, 1.233, 0.959, 1.206, 0.972, 1.227, 1.062 ]
+ },
+ {
+ "time": 1.2667,
+ "x": 0.958,
+ "y": 1.063,
+ "curve": [ 1.284, 0.958, 1.292, 1.001, 1.288, 1.063, 1.288, 1.001 ]
+ },
+ { "time": 1.3333 }
+ ]
+ },
+ "hip": {
+ "translate": [
+ {
+ "y": -45.46,
+ "curve": [ 0.042, -0.09, 0.15, 15.22, 0.031, 44.98, 0.123, 289.73 ]
+ },
+ {
+ "time": 0.2,
+ "x": 15.22,
+ "y": 415.85,
+ "curve": [ 0.332, 15.22, 0.539, -34.52, 0.271, 532.93, 0.483, 720.5 ]
+ },
+ {
+ "time": 0.7667,
+ "x": -34.52,
+ "y": 721.6,
+ "curve": [ 0.888, -34.52, 1.057, -21.95, 1.049, 721.17, 1.098, 379.84 ]
+ },
+ {
+ "time": 1.1333,
+ "x": -15.67,
+ "y": 266.77,
+ "curve": [ 1.144, -14.77, 1.188, -10.53, 1.15, 213.72, 1.172, -61.32 ]
+ },
+ {
+ "time": 1.2333,
+ "x": -6.53,
+ "y": -61.34,
+ "curve": [ 1.272, -3.22, 1.311, 0.05, 1.291, -61.36, 1.296, -44.8 ]
+ },
+ { "time": 1.3333, "y": -45.46 }
+ ]
+ },
+ "front-shin": {
+ "rotate": [
+ {
+ "value": -74.19,
+ "curve": [ 0, -51.14, 0.042, -12.54 ]
+ },
+ {
+ "time": 0.1667,
+ "value": -12.28,
+ "curve": [ 0.285, -12.32, 0.37, -74.44 ]
+ },
+ {
+ "time": 0.4333,
+ "value": -92.92,
+ "curve": [ 0.498, -111.86, 0.617, -140.28 ]
+ },
+ {
+ "time": 0.9,
+ "value": -140.84,
+ "curve": [ 1.004, -141.04, 1.09, -47.87 ]
+ },
+ {
+ "time": 1.1,
+ "value": -37.44,
+ "curve": [ 1.108, -29.83, 1.14, -21.18 ]
+ },
+ {
+ "time": 1.1667,
+ "value": -21.08,
+ "curve": [ 1.18, -21.03, 1.191, -50.65 ]
+ },
+ {
+ "time": 1.2,
+ "value": -53.17,
+ "curve": [ 1.22, -58.53, 1.271, -73.38 ]
+ },
+ { "time": 1.3333, "value": -74.19 }
+ ]
+ },
+ "front-foot": {
+ "rotate": [
+ {
+ "value": 7.35,
+ "curve": [ 0, 4.8, 0.05, -26.64 ]
+ },
+ {
+ "time": 0.0667,
+ "value": -26.64,
+ "curve": [ 0.192, -26.64, 0.442, -11.77 ]
+ },
+ {
+ "time": 0.5667,
+ "value": -11.77,
+ "curve": [ 0.692, -11.77, 0.942, -19.36 ]
+ },
+ {
+ "time": 1.0667,
+ "value": -19.36,
+ "curve": [ 1.133, -19.36, 1.32, 3.82 ]
+ },
+ { "time": 1.3333, "value": 7.35 }
+ ]
+ },
+ "rear-foot": {
+ "rotate": [
+ { "value": -7.14 }
+ ]
+ },
+ "gun": {
+ "rotate": [
+ {
+ "value": 12.36,
+ "curve": [ 0.022, 16.28, 0.15, 30.81 ]
+ },
+ {
+ "time": 0.2,
+ "value": 30.81,
+ "curve": [ 0.258, 30.81, 0.375, 13.26 ]
+ },
+ {
+ "time": 0.4333,
+ "value": 13.26,
+ "curve": [ 0.508, 13.26, 0.658, 15.05 ]
+ },
+ {
+ "time": 0.7333,
+ "value": 14.98,
+ "curve": [ 0.789, 14.94, 0.828, 13.62 ]
+ },
+ {
+ "time": 0.8667,
+ "value": 12.72,
+ "curve": [ 0.887, 12.25, 0.984, 9.83 ]
+ },
+ {
+ "time": 1.0333,
+ "value": 8.6,
+ "curve": [ 1.045, 8.31, 1.083, 7.55 ]
+ },
+ {
+ "time": 1.1333,
+ "value": 7.13,
+ "curve": [ 1.175, 6.78, 1.283, 6.18 ]
+ },
+ { "time": 1.3333, "value": 6.18 }
+ ]
+ },
+ "front-leg-target": {
+ "translate": [
+ { "x": -13.95, "y": -30.34 }
+ ]
+ },
+ "rear-leg-target": {
+ "rotate": [
+ { "value": -38.43 }
+ ],
+ "translate": [
+ { "x": 85, "y": -33.59 }
+ ]
+ },
+ "front-foot-target": {
+ "rotate": [
+ { "value": -62.54 }
+ ],
+ "translate": [
+ { "x": 16.34, "y": 0.18 }
+ ]
+ },
+ "rear-foot-target": {
+ "rotate": [
+ { "value": 18.55 }
+ ],
+ "translate": [
+ { "x": -176.39, "y": 134.12 }
+ ]
+ },
+ "back-foot-tip": {
+ "rotate": [
+ {
+ "value": -143.73,
+ "curve": [ 0.083, -144.24, 0.167, -74.26 ]
+ },
+ {
+ "time": 0.2667,
+ "value": -52.76,
+ "curve": [ 0.342, -36.57, 0.513, -36.57 ]
+ },
+ {
+ "time": 0.6333,
+ "value": -30.97,
+ "curve": [ 0.724, -26.78, 0.848, -17.06 ]
+ },
+ {
+ "time": 0.9667,
+ "value": -16.74,
+ "curve": [ 1.167, -16.2, 1.272, -144.17 ]
+ },
+ { "time": 1.3333, "value": -143.73 }
+ ]
+ },
+ "front-foot-tip": {
+ "rotate": [
+ {
+ "value": -1.57,
+ "curve": [ 0, -24.71, 0.162, -60.88 ]
+ },
+ {
+ "time": 0.2667,
+ "value": -60.83,
+ "curve": [ 0.342, -60.8, 0.582, -43.5 ]
+ },
+ {
+ "time": 0.7,
+ "value": -39.45,
+ "curve": [ 0.773, -36.94, 0.832, -36.78 ]
+ },
+ {
+ "time": 0.9667,
+ "value": -36.6,
+ "curve": [ 1.054, -36.49, 1.092, -37.37 ]
+ },
+ {
+ "time": 1.1667,
+ "value": -33.26,
+ "curve": [ 1.237, -29.37, 1.147, -1.41 ]
+ },
+ { "time": 1.2, "value": -1.57 }
+ ]
+ },
+ "hair3": {
+ "rotate": [
+ {
+ "value": -6.81,
+ "curve": [ 0, 13.59, 0.117, 18.21 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 18.21,
+ "curve": [ 0.167, 18.21, 0.26, 12.95 ]
+ },
+ {
+ "time": 0.3,
+ "value": 11.56,
+ "curve": [ 0.382, 8.7, 0.55, 9.43 ]
+ },
+ {
+ "time": 0.6667,
+ "value": 9.32,
+ "curve": [ 0.843, 9.15, 0.918, -7.34 ]
+ },
+ { "time": 1.3333, "value": -6.81 }
+ ],
+ "translate": [
+ {
+ "time": 0.6667,
+ "curve": [ 0.781, 0, 0.972, 16.03, 0.781, 0, 0.972, 0.92 ]
+ },
+ {
+ "time": 1.1333,
+ "x": 16.03,
+ "y": 0.92,
+ "curve": [ 1.211, 16.03, 1.281, 0, 1.211, 0.92, 1.281, 0 ]
+ },
+ { "time": 1.3333 }
+ ]
+ },
+ "hair4": {
+ "rotate": [
+ {
+ "value": -6.81,
+ "curve": [ 0.001, -3.88, 0.063, 16.18 ]
+ },
+ {
+ "time": 0.1667,
+ "value": 16.14,
+ "curve": [ 0.242, 16.1, 0.249, 16.07 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 13.46,
+ "curve": [ 0.442, 10.09, 0.573, -2.2 ]
+ },
+ {
+ "time": 0.6,
+ "value": -6.04,
+ "curve": [ 0.614, -8.05, 0.717, -33.44 ]
+ },
+ {
+ "time": 0.7667,
+ "value": -33.44,
+ "curve": [ 0.809, -33.44, 0.835, -31.32 ]
+ },
+ {
+ "time": 0.8667,
+ "value": -27.36,
+ "curve": [ 0.874, -26.47, 0.903, -14.28 ]
+ },
+ {
+ "time": 0.9333,
+ "value": -14.47,
+ "curve": [ 0.956, -14.62, 0.944, -25.91 ]
+ },
+ {
+ "time": 1,
+ "value": -25.96,
+ "curve": [ 1.062, -26.02, 1.051, -1.87 ]
+ },
+ {
+ "time": 1.0667,
+ "value": -1.87,
+ "curve": [ 1.096, -1.87, 1.096, -16.09 ]
+ },
+ {
+ "time": 1.1333,
+ "value": -16.08,
+ "curve": [ 1.169, -16.08, 1.153, -3.38 ]
+ },
+ {
+ "time": 1.2,
+ "value": -3.38,
+ "curve": [ 1.234, -3.38, 1.271, -6.07 ]
+ },
+ { "time": 1.3333, "value": -6.07 }
+ ]
+ },
+ "hair2": {
+ "rotate": [
+ {
+ "value": -6.81,
+ "curve": [ 0, -3.17, 0.042, 16.33 ]
+ },
+ {
+ "time": 0.0667,
+ "value": 16.33,
+ "curve": [ 0.21, 15.74, 0.208, -12.06 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -12.21,
+ "curve": [ 0.417, -12.3, 0.552, -3.98 ]
+ },
+ {
+ "time": 0.6667,
+ "value": 1.52,
+ "curve": [ 0.726, 4.35, 0.817, 4.99 ]
+ },
+ {
+ "time": 0.8667,
+ "value": 4.99,
+ "curve": [ 0.901, 4.99, 0.912, -29.05 ]
+ },
+ {
+ "time": 0.9667,
+ "value": -27.45,
+ "curve": [ 0.987, -26.83, 1.018, -5.42 ]
+ },
+ {
+ "time": 1.0667,
+ "value": -5.46,
+ "curve": [ 1.107, -5.22, 1.095, -33.51 ]
+ },
+ {
+ "time": 1.1333,
+ "value": -33.28,
+ "curve": [ 1.162, -33.57, 1.192, 8.04 ]
+ },
+ {
+ "time": 1.2667,
+ "value": 7.86,
+ "curve": [ 1.302, 7.77, 1.313, 2.7 ]
+ },
+ { "time": 1.3333, "value": 2.7 }
+ ]
+ },
+ "hair1": {
+ "rotate": [
+ {
+ "value": -6.81,
+ "curve": [ 0.001, -3.12, 0.074, 14.66 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 14.66,
+ "curve": [ 0.188, 14.8, 0.293, 9.56 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 5.99,
+ "curve": [ 0.381, 1.72, 0.55, -11.11 ]
+ },
+ {
+ "time": 0.6667,
+ "value": -11.11,
+ "curve": [ 0.833, -11.11, 0.933, 22.54 ]
+ },
+ {
+ "time": 1.1,
+ "value": 22.54,
+ "curve": [ 1.158, 22.54, 1.275, -6.81 ]
+ },
+ { "time": 1.3333, "value": -6.81 }
+ ]
+ },
+ "torso2": {
+ "rotate": [
+ {
+ "value": 4.52,
+ "curve": [ 0.013, 2.33, 0.092, -9.75 ]
+ },
+ {
+ "time": 0.1333,
+ "value": -9.75,
+ "curve": [ 0.175, -9.75, 0.291, -1.26 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 0.96,
+ "curve": [ 0.359, 2.3, 0.543, 4.25 ]
+ },
+ {
+ "time": 0.6,
+ "value": 4.68,
+ "curve": [ 0.683, 5.3, 0.771, 5.92 ]
+ },
+ {
+ "time": 0.8333,
+ "value": 6.48,
+ "curve": [ 0.871, 6.82, 1.083, 11.37 ]
+ },
+ {
+ "time": 1.1667,
+ "value": 11.37,
+ "curve": [ 1.208, 11.37, 1.317, 6.18 ]
+ },
+ { "time": 1.3333, "value": 4.52 }
+ ],
+ "translate": [
+ {
+ "curve": [ 0, 0, 0.082, -2.24, 0, 0, 0.082, -0.42 ]
+ },
+ {
+ "time": 0.1667,
+ "x": -2.98,
+ "y": -0.56,
+ "curve": [ 0.232, -2.24, 0.298, 0, 0.232, -0.42, 0.298, 0 ]
+ },
+ { "time": 0.3333, "curve": "stepped" },
+ {
+ "time": 0.8667,
+ "curve": [ 0.889, 0, 0.912, 0.26, 0.889, 0, 0.912, 0.06 ]
+ },
+ {
+ "time": 0.9333,
+ "x": 0.68,
+ "y": 0.23,
+ "curve": [ 1.016, 2.22, 1.095, 5.9, 1.023, 0.97, 1.095, 1.99 ]
+ },
+ {
+ "time": 1.1667,
+ "x": 6.47,
+ "y": 2.18,
+ "curve": [ 1.23, 5.75, 1.286, 0, 1.23, 1.94, 1.286, 0 ]
+ },
+ { "time": 1.3333 }
+ ]
+ },
+ "torso3": {
+ "rotate": [
+ {
+ "value": 4.52,
+ "curve": [ 0.025, 4.52, 0.075, -6.17 ]
+ },
+ {
+ "time": 0.1,
+ "value": -6.17,
+ "curve": [ 0.175, -6.17, 0.381, -0.71 ]
+ },
+ {
+ "time": 0.4,
+ "value": -0.25,
+ "curve": [ 0.447, 0.87, 0.775, 4.84 ]
+ },
+ {
+ "time": 0.9,
+ "value": 4.84,
+ "curve": [ 1.008, 4.84, 1.225, 4.52 ]
+ },
+ { "time": 1.3333, "value": 4.52 }
+ ]
+ },
+ "head-control": {
+ "translate": [
+ {
+ "curve": [ 0.138, -2.4, 0.227, -10.44, 0.123, 1.05, 0.227, 2.7 ]
+ },
+ {
+ "time": 0.3667,
+ "x": -10.44,
+ "y": 2.7,
+ "curve": [ 0.484, -10.44, 0.585, -5.63, 0.484, 2.7, 0.629, -23.62 ]
+ },
+ {
+ "time": 0.7333,
+ "x": -2.29,
+ "y": -26.61,
+ "curve": [ 0.818, -0.39, 0.962, 1.21, 0.858, -30.17, 0.972, -28.75 ]
+ },
+ {
+ "time": 1.1,
+ "x": 1.25,
+ "y": -28.75,
+ "curve": [ 1.192, 1.28, 1.234, 0.98, 1.224, -28.75, 1.235, -2.15 ]
+ },
+ { "time": 1.3333 }
+ ]
+ },
+ "front-shoulder": {
+ "translate": [
+ {
+ "curve": [ 0.031, -2.22, 0.065, -3.73, 0.02, -3.25, 0.065, -14.74 ]
+ },
+ {
+ "time": 0.1,
+ "x": -3.73,
+ "y": -14.74,
+ "curve": [ 0.216, -3.73, 0.384, -0.17, 0.216, -14.74, 0.402, -12.51 ]
+ },
+ {
+ "time": 0.5,
+ "x": 1.63,
+ "y": -9.51,
+ "curve": [ 0.632, 3.69, 0.935, 7.41, 0.585, -6.91, 0.909, 10.86 ]
+ },
+ {
+ "time": 1.1,
+ "x": 7.45,
+ "y": 10.99,
+ "curve": [ 1.18, 7.46, 1.265, 2.86, 1.193, 11.05, 1.294, 3.38 ]
+ },
+ { "time": 1.3333 }
+ ]
+ }
+ },
+ "ik": {
+ "front-foot-ik": [
+ {
+ "mix": 0,
+ "curve": [ 0.3, 0, 0.9, 1, 0.3, 0, 0.9, 0 ]
+ },
+ { "time": 1.2 }
+ ],
+ "front-leg-ik": [
+ {
+ "mix": 0,
+ "bendPositive": false,
+ "curve": [ 0.3, 0, 0.9, 1, 0.3, 0, 0.9, 0 ]
+ },
+ { "time": 1.2, "bendPositive": false }
+ ],
+ "rear-foot-ik": [
+ { "mix": 0 }
+ ],
+ "rear-leg-ik": [
+ { "mix": 0, "bendPositive": false }
+ ]
+ },
+ "events": [
+ { "time": 1.2, "name": "footstep" }
+ ]
+ },
+ "portal": {
+ "slots": {
+ "clipping": {
+ "attachment": [
+ { "name": "clipping" }
+ ]
+ },
+ "front-fist": {
+ "attachment": [
+ { "name": "front-fist-open" }
+ ]
+ },
+ "mouth": {
+ "attachment": [
+ { "time": 0.9, "name": "mouth-grind" },
+ { "time": 2.2667, "name": "mouth-smile" }
+ ]
+ },
+ "portal-bg": {
+ "attachment": [
+ { "name": "portal-bg" },
+ { "time": 3 }
+ ]
+ },
+ "portal-flare1": {
+ "attachment": [
+ { "time": 1.1, "name": "portal-flare1" },
+ { "time": 1.1333, "name": "portal-flare2" },
+ { "time": 1.1667, "name": "portal-flare3" },
+ { "time": 1.2, "name": "portal-flare1" },
+ { "time": 1.2333, "name": "portal-flare2" },
+ { "time": 1.2667, "name": "portal-flare1" },
+ { "time": 1.3333 }
+ ]
+ },
+ "portal-flare2": {
+ "attachment": [
+ { "time": 1.1, "name": "portal-flare2" },
+ { "time": 1.1333, "name": "portal-flare3" },
+ { "time": 1.1667, "name": "portal-flare1" },
+ { "time": 1.2, "name": "portal-flare2" },
+ { "time": 1.2333, "name": "portal-flare3" },
+ { "time": 1.2667 }
+ ]
+ },
+ "portal-flare3": {
+ "attachment": [
+ { "time": 1.2, "name": "portal-flare3" },
+ { "time": 1.2333, "name": "portal-flare2" },
+ { "time": 1.2667 }
+ ]
+ },
+ "portal-flare4": {
+ "attachment": [
+ { "time": 1.2, "name": "portal-flare2" },
+ { "time": 1.2333, "name": "portal-flare1" },
+ { "time": 1.2667, "name": "portal-flare2" },
+ { "time": 1.3333 }
+ ]
+ },
+ "portal-flare5": {
+ "attachment": [
+ { "time": 1.2333, "name": "portal-flare3" },
+ { "time": 1.2667, "name": "portal-flare1" },
+ { "time": 1.3333 }
+ ]
+ },
+ "portal-flare6": {
+ "attachment": [
+ { "time": 1.2667, "name": "portal-flare3" },
+ { "time": 1.3333 }
+ ]
+ },
+ "portal-flare7": {
+ "attachment": [
+ { "time": 1.1333, "name": "portal-flare2" },
+ { "time": 1.1667 }
+ ]
+ },
+ "portal-flare8": {
+ "attachment": [
+ { "time": 1.2, "name": "portal-flare3" },
+ { "time": 1.2333, "name": "portal-flare2" },
+ { "time": 1.2667 }
+ ]
+ },
+ "portal-flare9": {
+ "attachment": [
+ { "time": 1.2, "name": "portal-flare2" },
+ { "time": 1.2333, "name": "portal-flare3" },
+ { "time": 1.2667, "name": "portal-flare1" },
+ { "time": 1.3 }
+ ]
+ },
+ "portal-flare10": {
+ "attachment": [
+ { "time": 1.2, "name": "portal-flare2" },
+ { "time": 1.2333, "name": "portal-flare1" },
+ { "time": 1.2667, "name": "portal-flare3" },
+ { "time": 1.3 }
+ ]
+ },
+ "portal-shade": {
+ "attachment": [
+ { "name": "portal-shade" },
+ { "time": 3 }
+ ]
+ },
+ "portal-streaks1": {
+ "attachment": [
+ { "name": "portal-streaks1" },
+ { "time": 3 }
+ ]
+ },
+ "portal-streaks2": {
+ "attachment": [
+ { "name": "portal-streaks2" },
+ { "time": 3 }
+ ]
+ }
+ },
+ "bones": {
+ "portal-root": {
+ "translate": [
+ {
+ "x": -458.35,
+ "y": 105.19,
+ "curve": [ 0.333, -458.22, 0.669, -457.86, 0.934, 105.19, 0.671, 105.19 ]
+ },
+ {
+ "time": 1,
+ "x": -456.02,
+ "y": 105.19,
+ "curve": [ 1.339, -454.14, 2.208, -447.28, 1.35, 105.19, 2.05, 105.19 ]
+ },
+ {
+ "time": 2.4,
+ "x": -439.12,
+ "y": 105.19,
+ "curve": [ 2.463, -436.44, 2.502, -432.92, 2.487, 105.19, 2.512, 105.09 ]
+ },
+ {
+ "time": 2.6,
+ "x": -432.58,
+ "y": 105.09,
+ "curve": [ 2.784, -431.94, 2.978, -446.6, 2.772, 105.09, 2.933, 105.19 ]
+ },
+ { "time": 3.0333, "x": -457.42, "y": 105.19 }
+ ],
+ "scale": [
+ {
+ "x": 0.003,
+ "y": 0.006,
+ "curve": [ 0.329, 0.044, 0.347, 0.117, 0.329, 0.097, 0.37, 0.249 ]
+ },
+ {
+ "time": 0.4,
+ "x": 0.175,
+ "y": 0.387,
+ "curve": [ 0.63, 0.619, 0.663, 0.723, 0.609, 1.338, 0.645, 1.524 ]
+ },
+ {
+ "time": 0.7333,
+ "x": 0.724,
+ "y": 1.52,
+ "curve": [ 0.798, 0.725, 0.907, 0.647, 0.797, 1.517, 0.895, 1.424 ]
+ },
+ {
+ "time": 1,
+ "x": 0.645,
+ "y": 1.426,
+ "curve": [ 1.095, 0.643, 1.139, 0.688, 1.089, 1.428, 1.115, 1.513 ]
+ },
+ {
+ "time": 1.2333,
+ "x": 0.685,
+ "y": 1.516,
+ "curve": [ 1.325, 0.683, 1.508, 0.636, 1.343, 1.518, 1.467, 1.4 ]
+ },
+ {
+ "time": 1.6,
+ "x": 0.634,
+ "y": 1.401,
+ "curve": [ 1.728, 0.631, 1.946, 0.687, 1.722, 1.402, 1.924, 1.522 ]
+ },
+ {
+ "time": 2.0667,
+ "x": 0.688,
+ "y": 1.522,
+ "curve": [ 2.189, 0.69, 2.289, 0.649, 2.142, 1.522, 2.265, 1.417 ]
+ },
+ {
+ "time": 2.4,
+ "x": 0.65,
+ "y": 1.426,
+ "curve": [ 2.494, 0.651, 2.504, 0.766, 2.508, 1.434, 2.543, 1.566 ]
+ },
+ {
+ "time": 2.6,
+ "x": 0.766,
+ "y": 1.568,
+ "curve": [ 2.73, 0.765, 3.006, 0.098, 2.767, 1.564, 2.997, 0.1 ]
+ },
+ { "time": 3.0333, "x": 0.007, "y": 0.015 }
+ ]
+ },
+ "portal-streaks1": {
+ "rotate": [
+ {},
+ { "time": 3.1667, "value": 1200 }
+ ],
+ "translate": [
+ {
+ "x": 15.15,
+ "curve": [ 0.162, 15.15, 0.432, 12.6, 0.162, 0, 0.432, -3.86 ]
+ },
+ {
+ "time": 0.6667,
+ "x": 10.9,
+ "y": -6.44,
+ "curve": [ 0.794, 9.93, 0.912, 9.21, 0.794, -7.71, 0.912, -8.66 ]
+ },
+ {
+ "time": 1,
+ "x": 9.21,
+ "y": -8.66,
+ "curve": [ 1.083, 9.21, 1.25, 21.53, 1.083, -8.66, 1.265, -4.9 ]
+ },
+ {
+ "time": 1.3333,
+ "x": 21.53,
+ "y": -3.19,
+ "curve": [ 1.5, 21.53, 1.939, 12.3, 1.446, -0.37, 1.9, 6.26 ]
+ },
+ {
+ "time": 2.0667,
+ "x": 11.26,
+ "y": 6.26,
+ "curve": [ 2.239, 9.85, 2.389, 9.68, 2.208, 6.26, 2.523, 0.51 ]
+ },
+ {
+ "time": 2.5667,
+ "x": 9.39,
+ "y": -0.8,
+ "curve": [ 2.657, 9.24, 2.842, 9.21, 2.646, -3.2, 2.842, -8.91 ]
+ },
+ { "time": 2.9333, "x": 9.21, "y": -8.91 }
+ ],
+ "scale": [
+ {
+ "curve": [ 0.167, 1, 0.5, 1.053, 0.167, 1, 0.5, 1.053 ]
+ },
+ {
+ "time": 0.6667,
+ "x": 1.053,
+ "y": 1.053,
+ "curve": [ 0.833, 1.053, 1.167, 0.986, 0.833, 1.053, 1.167, 0.986 ]
+ },
+ {
+ "time": 1.3333,
+ "x": 0.986,
+ "y": 0.986,
+ "curve": [ 1.5, 0.986, 1.833, 1.053, 1.5, 0.986, 1.833, 1.053 ]
+ },
+ { "time": 2, "x": 1.053, "y": 1.053 }
+ ]
+ },
+ "portal-streaks2": {
+ "rotate": [
+ {},
+ { "time": 3.1667, "value": 600 }
+ ],
+ "translate": [
+ { "x": -2.11 },
+ { "time": 1, "x": -2.11, "y": 6.63 },
+ { "time": 1.9333, "x": -2.11 }
+ ],
+ "scale": [
+ {
+ "x": 1.014,
+ "y": 1.014,
+ "curve": [ 0.229, 0.909, 0.501, 0.755, 0.242, 0.892, 0.502, 0.768 ]
+ },
+ {
+ "time": 0.8667,
+ "x": 0.745,
+ "y": 0.745,
+ "curve": [ 1.282, 0.733, 2.021, 0.699, 1.27, 0.719, 2.071, 0.709 ]
+ },
+ {
+ "time": 2.2,
+ "x": 0.7,
+ "y": 0.704,
+ "curve": [ 2.315, 0.7, 2.421, 0.794, 2.311, 0.701, 2.485, 0.797 ]
+ },
+ {
+ "time": 2.5667,
+ "x": 0.794,
+ "y": 0.794,
+ "curve": [ 2.734, 0.794, 2.99, 0.323, 2.714, 0.789, 3.019, 0.341 ]
+ },
+ { "time": 3.1667, "x": 0, "y": 0 }
+ ]
+ },
+ "portal-shade": {
+ "translate": [
+ { "x": -29.68 }
+ ],
+ "scale": [
+ { "x": 0.714, "y": 0.714 }
+ ]
+ },
+ "portal": {
+ "rotate": [
+ {},
+ { "time": 3.1667, "value": 600 }
+ ]
+ },
+ "clipping": {
+ "translate": [
+ { "x": -476.55, "y": 2.27 }
+ ],
+ "scale": [
+ { "x": 0.983, "y": 1.197 }
+ ]
+ },
+ "hip": {
+ "rotate": [
+ {
+ "time": 1.0667,
+ "value": 22.74,
+ "curve": [ 1.163, 18.84, 1.77, 8.77 ]
+ },
+ {
+ "time": 1.9,
+ "value": 7.82,
+ "curve": [ 2.271, 5.1, 2.89, 0 ]
+ },
+ { "time": 3.1667 }
+ ],
+ "translate": [
+ { "x": -899.41, "y": 4.47, "curve": "stepped" },
+ {
+ "time": 1.0667,
+ "x": -694.16,
+ "y": 183.28,
+ "curve": [ 1.091, -602.08, 1.138, -427.59, 1.115, 185.6, 1.171, 133.18 ]
+ },
+ {
+ "time": 1.2333,
+ "x": -316.97,
+ "y": 55.29,
+ "curve": [ 1.317, -220.27, 1.512, -123.21, 1.271, 8.68, 1.461, -83.18 ]
+ },
+ {
+ "time": 1.6,
+ "x": -95.53,
+ "y": -112.23,
+ "curve": [ 1.718, -58.25, 2.037, -22.54, 1.858, -166.17, 2.109, -31.4 ]
+ },
+ {
+ "time": 2.1667,
+ "x": -14.82,
+ "y": -31.12,
+ "curve": [ 2.294, -7.28, 2.442, -7.2, 2.274, -30.6, 2.393, -36.76 ]
+ },
+ {
+ "time": 2.6,
+ "x": -7.2,
+ "y": -36.96,
+ "curve": [ 2.854, -7.2, 3.071, -11.87, 2.786, -36.27, 3.082, -22.98 ]
+ },
+ { "time": 3.1667, "x": -11.97, "y": -23.15 }
+ ]
+ },
+ "rear-foot-target": {
+ "rotate": [
+ { "time": 1.0667, "value": 41.6, "curve": "stepped" },
+ {
+ "time": 1.2333,
+ "value": 41.6,
+ "curve": [ 1.258, 41.6, 1.379, 35.46 ]
+ },
+ {
+ "time": 1.4,
+ "value": 30.09,
+ "curve": [ 1.412, 27.04, 1.433, 10.65 ]
+ },
+ { "time": 1.4333, "value": -0.28 },
+ { "time": 1.6, "value": 2.44 }
+ ],
+ "translate": [
+ { "x": -899.41, "y": 4.47, "curve": "stepped" },
+ {
+ "time": 1.0667,
+ "x": -591.13,
+ "y": 438.46,
+ "curve": [ 1.076, -539.77, 1.206, -268.1, 1.117, 418.44, 1.21, 333.18 ]
+ },
+ {
+ "time": 1.2333,
+ "x": -225.28,
+ "y": 304.53,
+ "curve": [ 1.265, -175.22, 1.393, -74.21, 1.296, 226.52, 1.401, 49.61 ]
+ },
+ {
+ "time": 1.4333,
+ "x": -52.32,
+ "y": 0.2,
+ "curve": [ 1.454, -40.85, 1.616, 40.87, 1.466, 0.17, 1.614, 0.04 ]
+ },
+ { "time": 1.6667, "x": 45.87, "y": 0.01 },
+ { "time": 1.9333, "x": 48.87 }
+ ]
+ },
+ "front-foot-target": {
+ "rotate": [
+ {
+ "time": 1.0667,
+ "value": 32.08,
+ "curve": [ 1.108, 32.08, 1.192, 35.16 ]
+ },
+ {
+ "time": 1.2333,
+ "value": 35.16,
+ "curve": [ 1.258, 35.16, 1.317, 2.23 ]
+ },
+ {
+ "time": 1.3333,
+ "value": -4.74,
+ "curve": [ 1.351, -12.14, 1.429, -34.96 ]
+ },
+ {
+ "time": 1.6,
+ "value": -34.77,
+ "curve": [ 1.765, -34.58, 1.897, -17.25 ]
+ },
+ { "time": 1.9333 }
+ ],
+ "translate": [
+ { "x": -899.41, "y": 4.47, "curve": "stepped" },
+ {
+ "time": 1.0667,
+ "x": -533.93,
+ "y": 363.75,
+ "curve": [ 1.074, -480.85, 1.18, -261.31, 1.094, 362.3, 1.195, 267.77 ]
+ },
+ {
+ "time": 1.2333,
+ "x": -201.23,
+ "y": 199.93,
+ "curve": [ 1.269, -161.38, 1.294, -140.32, 1.274, 126.67, 1.308, 77.12 ]
+ },
+ {
+ "time": 1.3333,
+ "x": -124.08,
+ "y": 0.2,
+ "curve": [ 1.426, -85.6, 1.633, -69.06, 1.45, 0.48, 1.633, 0 ]
+ },
+ { "time": 1.7333, "x": -69.06 }
+ ]
+ },
+ "torso": {
+ "rotate": [
+ {
+ "time": 1.0667,
+ "value": 27.02,
+ "curve": [ 1.187, 26.86, 1.291, 7.81 ]
+ },
+ {
+ "time": 1.3333,
+ "value": -2.62,
+ "curve": [ 1.402, -19.72, 1.429, -48.64 ]
+ },
+ {
+ "time": 1.4667,
+ "value": -56.31,
+ "curve": [ 1.509, -64.87, 1.62, -77.14 ]
+ },
+ {
+ "time": 1.7333,
+ "value": -77.34,
+ "curve": [ 1.837, -76.89, 1.895, -71.32 ]
+ },
+ {
+ "time": 2,
+ "value": -57.52,
+ "curve": [ 2.104, -43.83, 2.189, -28.59 ]
+ },
+ {
+ "time": 2.3,
+ "value": -29.03,
+ "curve": [ 2.413, -29.48, 2.513, -36.79 ]
+ },
+ {
+ "time": 2.6667,
+ "value": -36.79,
+ "curve": [ 2.814, -36.95, 2.947, -22.88 ]
+ },
+ { "time": 3.1667, "value": -22.88 }
+ ]
+ },
+ "neck": {
+ "rotate": [
+ {
+ "time": 1.0667,
+ "value": -3.57,
+ "curve": [ 1.146, -3.66, 1.15, -13.5 ]
+ },
+ {
+ "time": 1.2333,
+ "value": -13.5,
+ "curve": [ 1.428, -13.5, 1.443, 11.58 ]
+ },
+ {
+ "time": 1.5667,
+ "value": 11.42,
+ "curve": [ 1.658, 11.3, 1.775, 3.78 ]
+ },
+ {
+ "time": 1.8667,
+ "value": 3.78,
+ "curve": [ 1.92, 3.78, 2.036, 8.01 ]
+ },
+ {
+ "time": 2.1,
+ "value": 7.93,
+ "curve": [ 2.266, 7.72, 2.42, 3.86 ]
+ },
+ {
+ "time": 2.5333,
+ "value": 3.86,
+ "curve": [ 2.783, 3.86, 3.004, 3.78 ]
+ },
+ { "time": 3.1667, "value": 3.78 }
+ ]
+ },
+ "head": {
+ "rotate": [
+ {
+ "time": 1.0667,
+ "value": 16.4,
+ "curve": [ 1.133, 9.9, 1.207, 1.87 ]
+ },
+ {
+ "time": 1.3333,
+ "value": 1.67,
+ "curve": [ 1.46, 1.56, 1.547, 47.54 ]
+ },
+ {
+ "time": 1.7333,
+ "value": 47.55,
+ "curve": [ 1.897, 47.56, 2.042, 5.68 ]
+ },
+ {
+ "time": 2.0667,
+ "value": 0.86,
+ "curve": [ 2.074, -0.61, 2.086, -2.81 ]
+ },
+ {
+ "time": 2.1,
+ "value": -5.31,
+ "curve": [ 2.145, -13.07, 2.216, -23.65 ]
+ },
+ {
+ "time": 2.2667,
+ "value": -23.71,
+ "curve": [ 2.334, -23.79, 2.426, -13.43 ]
+ },
+ {
+ "time": 2.4667,
+ "value": -9.18,
+ "curve": [ 2.498, -5.91, 2.604, 2.53 ]
+ },
+ {
+ "time": 2.6667,
+ "value": 2.52,
+ "curve": [ 2.738, 2.24, 2.85, -8.76 ]
+ },
+ {
+ "time": 2.9333,
+ "value": -8.67,
+ "curve": [ 3.036, -8.55, 3.09, -7.09 ]
+ },
+ { "time": 3.1667, "value": -6.75 }
+ ],
+ "scale": [
+ {
+ "time": 1.3333,
+ "curve": [ 1.392, 1, 1.526, 1, 1.392, 1, 1.508, 1.043 ]
+ },
+ {
+ "time": 1.5667,
+ "x": 0.992,
+ "y": 1.043,
+ "curve": [ 1.598, 0.985, 1.676, 0.955, 1.584, 1.043, 1.672, 1.04 ]
+ },
+ {
+ "time": 1.7333,
+ "x": 0.954,
+ "y": 1.029,
+ "curve": [ 1.843, 0.954, 1.933, 1, 1.825, 1.013, 1.933, 1 ]
+ },
+ { "time": 2 }
+ ]
+ },
+ "rear-upper-arm": {
+ "rotate": [
+ {
+ "time": 0.9,
+ "value": 39.24,
+ "curve": [ 0.968, 39.93, 1.267, 85.31 ]
+ },
+ {
+ "time": 1.4667,
+ "value": 112.27,
+ "curve": [ 1.555, 124.24, 1.576, 126.44 ]
+ },
+ {
+ "time": 1.6333,
+ "value": 126.44,
+ "curve": [ 1.782, 126.44, 1.992, 94.55 ]
+ },
+ {
+ "time": 2.1,
+ "value": 79.96,
+ "curve": [ 2.216, 64.26, 2.407, 34.36 ]
+ },
+ {
+ "time": 2.5667,
+ "value": 33.38,
+ "curve": [ 2.815, 31.87, 3.1, 39.2 ]
+ },
+ { "time": 3.1667, "value": 39.2 }
+ ]
+ },
+ "back-foot-tip": {
+ "rotate": [
+ {
+ "time": 1.0667,
+ "value": 56.07,
+ "curve": [ 1.138, 59.21, 1.192, 59.65 ]
+ },
+ {
+ "time": 1.2333,
+ "value": 59.46,
+ "curve": [ 1.295, 59.17, 1.45, 22.54 ]
+ },
+ { "time": 1.4667, "value": -0.84 }
+ ]
+ },
+ "front-upper-arm": {
+ "rotate": [
+ {
+ "time": 1.0667,
+ "value": 118.03,
+ "curve": [ 1.075, 93.64, 1.358, -34.03 ]
+ },
+ {
+ "time": 1.6667,
+ "value": -33.94,
+ "curve": [ 1.808, -33.89, 1.879, -25 ]
+ },
+ {
+ "time": 1.9667,
+ "value": -25.19,
+ "curve": [ 2.09, -25.46, 2.312, -34.58 ]
+ },
+ {
+ "time": 2.3667,
+ "value": -38.36,
+ "curve": [ 2.465, -45.18, 2.557, -60.1 ]
+ },
+ {
+ "time": 2.8333,
+ "value": -61.1,
+ "curve": [ 2.843, -61.06, 3.16, -60.87 ]
+ },
+ { "time": 3.1667, "value": -60.87 }
+ ]
+ },
+ "front-bracer": {
+ "rotate": [
+ {
+ "time": 1.0667,
+ "value": 0.66,
+ "curve": [ 1.108, 0.66, 1.221, 44.95 ]
+ },
+ {
+ "time": 1.2333,
+ "value": 49.25,
+ "curve": [ 1.263, 59.42, 1.342, 68.06 ]
+ },
+ {
+ "time": 1.3667,
+ "value": 68.34,
+ "curve": [ 1.409, 68.8, 1.476, 4.9 ]
+ },
+ {
+ "time": 1.5,
+ "value": -2.05,
+ "curve": [ 1.529, -10.3, 1.695, -15.95 ]
+ },
+ {
+ "time": 1.7333,
+ "value": -17.38,
+ "curve": [ 1.807, -20.1, 1.878, -21.19 ]
+ },
+ {
+ "time": 1.9333,
+ "value": -21.08,
+ "curve": [ 2.073, -20.8, 2.146, -7.63 ]
+ },
+ {
+ "time": 2.1667,
+ "value": -3.64,
+ "curve": [ 2.186, 0.12, 2.275, 15.28 ]
+ },
+ {
+ "time": 2.3333,
+ "value": 21.78,
+ "curve": [ 2.392, 28.31, 2.575, 37.66 ]
+ },
+ {
+ "time": 2.7,
+ "value": 39.43,
+ "curve": [ 2.947, 42.93, 3.02, 42.46 ]
+ },
+ { "time": 3.1667, "value": 42.46 }
+ ]
+ },
+ "front-thigh": {
+ "translate": [
+ { "time": 1.1, "x": -6.41, "y": 18.23, "curve": "stepped" },
+ { "time": 1.1333, "x": -6.41, "y": 18.23 },
+ { "time": 1.2, "x": 1.61, "y": 3.66 },
+ { "time": 1.2333, "x": 4.5, "y": -3.15 },
+ { "time": 1.3667, "x": -3.79, "y": 2.94 },
+ { "time": 1.4, "x": -8.37, "y": 8.72 },
+ { "time": 1.4333, "x": -11.26, "y": 16.99 },
+ { "time": 1.4667, "x": -9.89, "y": 24.73, "curve": "stepped" },
+ { "time": 1.8667, "x": -9.89, "y": 24.73 },
+ { "time": 2.1 }
+ ]
+ },
+ "front-foot-tip": {
+ "rotate": [
+ { "time": 1.0667, "value": 42.55, "curve": "stepped" },
+ { "time": 1.1333, "value": 42.55 },
+ { "time": 1.2333, "value": 17.71 },
+ { "time": 1.3667, "value": 3.63 },
+ { "time": 1.4333 }
+ ]
+ },
+ "rear-bracer": {
+ "rotate": [
+ {
+ "time": 1.0667,
+ "value": 108.71,
+ "curve": [ 1.082, 108.29, 1.437, 50.73 ]
+ },
+ {
+ "time": 1.5667,
+ "value": 24.87,
+ "curve": [ 1.62, 14.2, 1.66, -11.74 ]
+ },
+ {
+ "time": 1.7333,
+ "value": -11.74,
+ "curve": [ 1.961, -11.73, 2.172, 1.66 ]
+ },
+ {
+ "time": 2.2667,
+ "value": 7.88,
+ "curve": [ 2.331, 12.13, 2.439, 18.65 ]
+ },
+ {
+ "time": 2.5333,
+ "value": 18.72,
+ "curve": [ 2.788, 18.91, 3.145, -0.3 ]
+ },
+ { "time": 3.1667 }
+ ]
+ },
+ "front-fist": {
+ "rotate": [
+ {
+ "time": 1.1,
+ "value": 6.32,
+ "curve": [ 1.11, 3.31, 1.153, -5.07 ]
+ },
+ {
+ "time": 1.2333,
+ "value": -5.13,
+ "curve": [ 1.311, -5.19, 1.364, 34.65 ]
+ },
+ {
+ "time": 1.4667,
+ "value": 34.53,
+ "curve": [ 1.574, 34.41, 1.547, -55.78 ]
+ },
+ {
+ "time": 1.8667,
+ "value": -54.7,
+ "curve": [ 1.947, -54.7, 2.03, -53.94 ]
+ },
+ {
+ "time": 2.1333,
+ "value": -42.44,
+ "curve": [ 2.215, -33.42, 2.358, -4.43 ]
+ },
+ {
+ "time": 2.4,
+ "value": 0.03,
+ "curve": [ 2.444, 4.66, 2.536, 8.2 ]
+ },
+ {
+ "time": 2.6333,
+ "value": 8.2,
+ "curve": [ 2.733, 8.19, 2.804, -0.67 ]
+ },
+ {
+ "time": 2.9,
+ "value": -0.82,
+ "curve": [ 3.127, -1.16, 3.093, 0 ]
+ },
+ { "time": 3.1667 }
+ ]
+ },
+ "gun": {
+ "rotate": [
+ {
+ "time": 1.2667,
+ "curve": [ 1.35, 0, 1.549, 7.49 ]
+ },
+ {
+ "time": 1.6,
+ "value": 9.5,
+ "curve": [ 1.663, 12.02, 1.846, 19.58 ]
+ },
+ {
+ "time": 1.9333,
+ "value": 19.43,
+ "curve": [ 1.985, 19.4, 2.057, 2.98 ]
+ },
+ {
+ "time": 2.2,
+ "value": 2.95,
+ "curve": [ 2.304, 3.55, 2.458, 10.8 ]
+ },
+ {
+ "time": 2.5,
+ "value": 10.8,
+ "curve": [ 2.642, 10.8, 2.873, -2.54 ]
+ },
+ {
+ "time": 2.9333,
+ "value": -2.55,
+ "curve": [ 3.09, -2.57, 3.08, 0 ]
+ },
+ { "time": 3.1667 }
+ ]
+ },
+ "hair2": {
+ "rotate": [
+ {
+ "time": 1.0667,
+ "value": 26.19,
+ "curve": [ 1.158, 26.19, 1.368, 26 ]
+ },
+ {
+ "time": 1.4333,
+ "value": 24.43,
+ "curve": [ 1.534, 22.03, 2, -29.14 ]
+ },
+ {
+ "time": 2.2,
+ "value": -29.14,
+ "curve": [ 2.292, -29.14, 2.475, 6.71 ]
+ },
+ {
+ "time": 2.5667,
+ "value": 6.71,
+ "curve": [ 2.675, 6.71, 2.814, -5.06 ]
+ },
+ {
+ "time": 2.9,
+ "value": -5.06,
+ "curve": [ 2.973, -5.06, 3.123, 0 ]
+ },
+ { "time": 3.1667 }
+ ]
+ },
+ "hair4": {
+ "rotate": [
+ {
+ "time": 1.0667,
+ "value": 5.21,
+ "curve": [ 1.108, 5.21, 1.192, 26.19 ]
+ },
+ {
+ "time": 1.2333,
+ "value": 26.19,
+ "curve": [ 1.317, 26.19, 1.483, 10.63 ]
+ },
+ {
+ "time": 1.5667,
+ "value": 10.63,
+ "curve": [ 1.627, 10.63, 1.642, 17.91 ]
+ },
+ {
+ "time": 1.7,
+ "value": 17.94,
+ "curve": [ 1.761, 17.97, 1.774, 8.22 ]
+ },
+ {
+ "time": 1.8,
+ "value": 3.33,
+ "curve": [ 1.839, -4.21, 1.95, -22.67 ]
+ },
+ {
+ "time": 2,
+ "value": -22.67,
+ "curve": [ 2.025, -22.67, 2.123, -21.86 ]
+ },
+ {
+ "time": 2.1667,
+ "value": -18.71,
+ "curve": [ 2.228, -14.31, 2.294, -0.3 ]
+ },
+ {
+ "time": 2.3667,
+ "value": 6.36,
+ "curve": [ 2.433, 12.45, 2.494, 19.21 ]
+ },
+ {
+ "time": 2.6,
+ "value": 19.21,
+ "curve": [ 2.729, 19.21, 2.854, 6.75 ]
+ },
+ {
+ "time": 2.9333,
+ "value": 4.62,
+ "curve": [ 3.09, 0.45, 3.062, 0 ]
+ },
+ { "time": 3.1667 }
+ ]
+ },
+ "hair3": {
+ "rotate": [
+ {
+ "time": 1.4333,
+ "curve": [ 1.45, 0, 1.452, 11.29 ]
+ },
+ {
+ "time": 1.5,
+ "value": 11.21,
+ "curve": [ 1.596, 11.06, 1.573, -14.17 ]
+ },
+ {
+ "time": 1.7333,
+ "value": -20.4,
+ "curve": [ 1.851, -24.98, 1.943, -28.45 ]
+ },
+ {
+ "time": 2.2,
+ "value": -28.75,
+ "curve": [ 2.317, -28.75, 2.55, 7.04 ]
+ },
+ {
+ "time": 2.6667,
+ "value": 7.04,
+ "curve": [ 2.792, 7.04, 2.885, -5.19 ]
+ },
+ {
+ "time": 2.9667,
+ "value": -5.19,
+ "curve": [ 3.037, -5.19, 3.096, 0 ]
+ },
+ { "time": 3.1667 }
+ ]
+ },
+ "hair1": {
+ "rotate": [
+ {
+ "time": 1.2333,
+ "curve": [ 1.283, 0, 1.349, 3.99 ]
+ },
+ {
+ "time": 1.4333,
+ "value": 6.58,
+ "curve": [ 1.497, 8.54, 1.683, 9.35 ]
+ },
+ {
+ "time": 1.7667,
+ "value": 9.35,
+ "curve": [ 1.825, 9.35, 1.945, -8.71 ]
+ },
+ {
+ "time": 2,
+ "value": -11.15,
+ "curve": [ 2.058, -13.71, 2.2, -14.97 ]
+ },
+ {
+ "time": 2.2667,
+ "value": -14.97,
+ "curve": [ 2.367, -14.97, 2.567, 18.77 ]
+ },
+ {
+ "time": 2.6667,
+ "value": 18.77,
+ "curve": [ 2.733, 18.77, 2.817, 8.29 ]
+ },
+ {
+ "time": 2.8667,
+ "value": 6.51,
+ "curve": [ 2.988, 2.17, 3.058, 0 ]
+ },
+ { "time": 3.1667 }
+ ]
+ },
+ "flare1": {
+ "rotate": [
+ { "time": 1.1, "value": 8.2 }
+ ],
+ "translate": [
+ { "time": 1.1, "x": -19.97, "y": 149.68 },
+ { "time": 1.2, "x": 3.85, "y": 152.43 },
+ { "time": 1.2333, "x": -15.42, "y": 152.29 }
+ ],
+ "scale": [
+ {
+ "time": 1.1,
+ "x": 0.805,
+ "y": 0.805,
+ "curve": [ 1.119, 0.763, 1.16, 1.162, 1.117, 0.805, 1.15, 0.605 ]
+ },
+ {
+ "time": 1.1667,
+ "x": 1.279,
+ "y": 0.605,
+ "curve": [ 1.177, 1.47, 1.192, 2.151, 1.175, 0.605, 1.192, 0.911 ]
+ },
+ {
+ "time": 1.2,
+ "x": 2.151,
+ "y": 0.911,
+ "curve": [ 1.208, 2.151, 1.231, 1.668, 1.208, 0.911, 1.227, 0.844 ]
+ },
+ {
+ "time": 1.2333,
+ "x": 1.608,
+ "y": 0.805,
+ "curve": [ 1.249, 1.205, 1.283, 0.547, 1.254, 0.685, 1.283, 0.416 ]
+ },
+ { "time": 1.3, "x": 0.547, "y": 0.416 }
+ ],
+ "shear": [
+ { "time": 1.1, "y": 4.63 },
+ { "time": 1.2333, "x": -5.74, "y": 4.63 }
+ ]
+ },
+ "flare2": {
+ "rotate": [
+ { "time": 1.1, "value": 12.29 }
+ ],
+ "translate": [
+ { "time": 1.1, "x": -8.63, "y": 132.96 },
+ { "time": 1.2, "x": 4.35, "y": 132.93 }
+ ],
+ "scale": [
+ { "time": 1.1, "x": 0.864, "y": 0.864 },
+ { "time": 1.1667, "x": 0.945, "y": 0.945 },
+ { "time": 1.2, "x": 1.511, "y": 1.081 }
+ ],
+ "shear": [
+ { "time": 1.1, "y": 24.03 }
+ ]
+ },
+ "flare3": {
+ "rotate": [
+ { "time": 1.1667, "value": 2.88 }
+ ],
+ "translate": [
+ { "time": 1.1667, "x": 3.24, "y": 114.81 }
+ ],
+ "scale": [
+ { "time": 1.1667, "x": 0.668, "y": 0.668 }
+ ],
+ "shear": [
+ { "time": 1.1667, "y": 38.59 }
+ ]
+ },
+ "flare4": {
+ "rotate": [
+ { "time": 1.1667, "value": -8.64 }
+ ],
+ "translate": [
+ { "time": 1.1667, "x": -3.82, "y": 194.06 },
+ { "time": 1.2667, "x": -1.82, "y": 198.47, "curve": "stepped" },
+ { "time": 1.3, "x": -1.94, "y": 187.81 }
+ ],
+ "scale": [
+ { "time": 1.1667, "x": 0.545, "y": 0.545 },
+ { "time": 1.2667, "x": 0.757, "y": 0.757 }
+ ],
+ "shear": [
+ { "time": 1.1667, "x": 7.42, "y": -22.04 }
+ ]
+ },
+ "flare5": {
+ "translate": [
+ { "time": 1.2, "x": -11.17, "y": 176.42 },
+ { "time": 1.2333, "x": -8.56, "y": 179.04, "curve": "stepped" },
+ { "time": 1.3, "x": -14.57, "y": 168.69 }
+ ],
+ "scale": [
+ { "time": 1.2333, "x": 1.146 },
+ { "time": 1.3, "x": 0.703, "y": 0.61 }
+ ],
+ "shear": [
+ { "time": 1.2, "x": 6.9 }
+ ]
+ },
+ "flare6": {
+ "rotate": [
+ { "time": 1.2333, "value": -5.36 },
+ { "time": 1.2667, "value": -0.54 }
+ ],
+ "translate": [
+ { "time": 1.2333, "x": 14.52, "y": 204.67 },
+ { "time": 1.2667, "x": 19.16, "y": 212.9, "curve": "stepped" },
+ { "time": 1.3, "x": 9.23, "y": 202.85 }
+ ],
+ "scale": [
+ { "time": 1.2333, "x": 0.777, "y": 0.49 },
+ { "time": 1.2667, "x": 0.777, "y": 0.657 },
+ { "time": 1.3, "x": 0.475, "y": 0.401 }
+ ]
+ },
+ "flare7": {
+ "rotate": [
+ { "time": 1.1, "value": 5.98 },
+ { "time": 1.1333, "value": 32.82 }
+ ],
+ "translate": [
+ { "time": 1.1, "x": -6.34, "y": 112.98 },
+ { "time": 1.1333, "x": 2.66, "y": 111.6 }
+ ],
+ "scale": [
+ { "time": 1.1, "x": 0.588, "y": 0.588 }
+ ],
+ "shear": [
+ { "time": 1.1333, "x": -19.93 }
+ ]
+ },
+ "flare8": {
+ "rotate": [
+ { "time": 1.2333, "value": -6.85 }
+ ],
+ "translate": [
+ { "time": 1.1667, "x": 66.67, "y": 125.52, "curve": "stepped" },
+ { "time": 1.2, "x": 58.24, "y": 113.53, "curve": "stepped" },
+ { "time": 1.2333, "x": 40.15, "y": 114.69 }
+ ],
+ "scale": [
+ { "time": 1.1667, "x": 1.313, "y": 1.203 },
+ { "time": 1.2333, "x": 1.038, "y": 0.95 }
+ ],
+ "shear": [
+ { "time": 1.2, "y": -13.01 }
+ ]
+ },
+ "flare9": {
+ "rotate": [
+ { "time": 1.1667, "value": 2.9 }
+ ],
+ "translate": [
+ { "time": 1.1667, "x": 28.45, "y": 151.35, "curve": "stepped" },
+ { "time": 1.2, "x": 48.8, "y": 191.09, "curve": "stepped" },
+ { "time": 1.2333, "x": 52, "y": 182.52, "curve": "stepped" },
+ { "time": 1.2667, "x": 77.01, "y": 195.96 }
+ ],
+ "scale": [
+ { "time": 1.1667, "x": 0.871, "y": 1.073 },
+ { "time": 1.2, "x": 0.927, "y": 0.944 },
+ { "time": 1.2333, "x": 1.165, "y": 1.336 }
+ ],
+ "shear": [
+ { "time": 1.1667, "x": 7.95, "y": 25.48 }
+ ]
+ },
+ "flare10": {
+ "rotate": [
+ { "time": 1.1667, "value": 2.18 }
+ ],
+ "translate": [
+ { "time": 1.1667, "x": 55.64, "y": 137.64, "curve": "stepped" },
+ { "time": 1.2, "x": 90.49, "y": 151.07, "curve": "stepped" },
+ { "time": 1.2333, "x": 114.06, "y": 153.05, "curve": "stepped" },
+ { "time": 1.2667, "x": 90.44, "y": 164.61 }
+ ],
+ "scale": [
+ { "time": 1.1667, "x": 2.657, "y": 0.891 },
+ { "time": 1.2, "x": 3.314, "y": 1.425 },
+ { "time": 1.2333, "x": 2.871, "y": 0.924 },
+ { "time": 1.2667, "x": 2.317, "y": 0.775 }
+ ],
+ "shear": [
+ { "time": 1.1667, "x": -1.35 }
+ ]
+ },
+ "torso2": {
+ "rotate": [
+ {
+ "time": 1,
+ "curve": [ 1.117, 0, 1.255, 24.94 ]
+ },
+ {
+ "time": 1.4,
+ "value": 24.94,
+ "curve": [ 1.477, 24.94, 1.59, -17.62 ]
+ },
+ {
+ "time": 1.6333,
+ "value": -19.48,
+ "curve": [ 1.717, -23.1, 1.784, -26.12 ]
+ },
+ {
+ "time": 1.9333,
+ "value": -26.14,
+ "curve": [ 2.067, -26.15, 2.158, 4.3 ]
+ },
+ {
+ "time": 2.3,
+ "value": 4.22,
+ "curve": [ 2.45, 4.13, 2.579, -1.76 ]
+ },
+ {
+ "time": 2.7333,
+ "value": -1.8,
+ "curve": [ 2.816, -1.82, 2.857, -2.94 ]
+ },
+ {
+ "time": 2.9333,
+ "value": -2.99,
+ "curve": [ 3.056, -3.08, 3.09, 0 ]
+ },
+ { "time": 3.1667 }
+ ]
+ },
+ "torso3": {
+ "rotate": [
+ {
+ "time": 1.3,
+ "curve": [ 1.352, 0, 1.408, 6.47 ]
+ },
+ {
+ "time": 1.4667,
+ "value": 6.43,
+ "curve": [ 1.55, 6.39, 1.723, -5.05 ]
+ },
+ {
+ "time": 1.7333,
+ "value": -5.53,
+ "curve": [ 1.782, -7.72, 1.843, -16.94 ]
+ },
+ {
+ "time": 1.9667,
+ "value": -16.86,
+ "curve": [ 2.111, -16.78, 2.259, -3.97 ]
+ },
+ {
+ "time": 2.4,
+ "value": -2.43,
+ "curve": [ 2.525, -1.12, 2.639, -0.5 ]
+ },
+ {
+ "time": 2.7333,
+ "value": -0.49,
+ "curve": [ 2.931, -0.47, 2.999, -2.15 ]
+ },
+ { "time": 3.1667, "value": -2.15 }
+ ]
+ },
+ "head-control": {
+ "translate": [
+ {
+ "time": 1.2333,
+ "curve": [ 1.25, 0, 1.474, 6.89, 1.25, 0, 1.496, 0.98 ]
+ },
+ {
+ "time": 1.6667,
+ "x": 11.99,
+ "y": -6.42,
+ "curve": [ 1.743, 14.01, 1.86, 14.33, 1.785, -11.55, 1.86, -27.1 ]
+ },
+ {
+ "time": 1.9667,
+ "x": 13.91,
+ "y": -26.88,
+ "curve": [ 2.074, 13.49, 2.244, 8.13, 2.074, -26.65, 2.215, -21.78 ]
+ },
+ {
+ "time": 2.3,
+ "x": 6.07,
+ "y": -16.64,
+ "curve": [ 2.416, 1.84, 2.497, -1.41, 2.417, -9.57, 2.526, -1.72 ]
+ },
+ {
+ "time": 2.5667,
+ "x": -3.78,
+ "y": -1.71,
+ "curve": [ 2.661, -6.98, 2.76, -8.76, 2.692, -1.68, 2.821, -15.75 ]
+ },
+ {
+ "time": 2.9,
+ "x": -8.32,
+ "y": -16.7,
+ "curve": [ 2.962, -8.12, 3.082, -0.04, 2.958, -17.39, 3.089, 0 ]
+ },
+ { "time": 3.1667 }
+ ]
+ },
+ "front-shoulder": {
+ "translate": [
+ {
+ "time": 1.3333,
+ "curve": [ 1.488, 0, 1.717, 0.21, 1.488, 0, 1.688, -30.29 ]
+ },
+ {
+ "time": 1.9,
+ "x": 0.83,
+ "y": -30.29,
+ "curve": [ 2.078, 1.43, 2.274, 2.88, 2.071, -30.29, 2.289, 4.48 ]
+ },
+ {
+ "time": 2.4333,
+ "x": 2.89,
+ "y": 4.59,
+ "curve": [ 2.604, 2.89, 2.677, -0.68, 2.57, 4.7, 2.694, -2.43 ]
+ },
+ {
+ "time": 2.7667,
+ "x": -0.67,
+ "y": -2.47,
+ "curve": [ 2.866, -0.67, 2.986, -0.07, 2.882, -2.47, 3.036, -0.06 ]
+ },
+ { "time": 3.1667 }
+ ]
+ }
+ },
+ "ik": {
+ "rear-leg-ik": [
+ { "time": 3.1667, "softness": 10, "bendPositive": false }
+ ]
+ }
+ },
+ "run": {
+ "slots": {
+ "mouth": {
+ "attachment": [
+ { "name": "mouth-grind" }
+ ]
+ }
+ },
+ "bones": {
+ "front-thigh": {
+ "translate": [
+ {
+ "x": -5.14,
+ "y": 11.13,
+ "curve": [ 0.033, -7.77, 0.112, -9.03, 0.034, 11.13, 0.108, 9.74 ]
+ },
+ {
+ "time": 0.1667,
+ "x": -9.03,
+ "y": 7.99,
+ "curve": [ 0.23, -9.05, 0.314, -1.34, 0.236, 5.93, 0.28, 3.22 ]
+ },
+ {
+ "time": 0.3333,
+ "x": 0.41,
+ "y": 3.19,
+ "curve": [ 0.352, 2.09, 0.449, 11.16, 0.384, 3.16, 0.449, 4.98 ]
+ },
+ {
+ "time": 0.5,
+ "x": 11.17,
+ "y": 6.76,
+ "curve": [ 0.571, 10.79, 0.621, -1.83, 0.542, 8.21, 0.625, 11.13 ]
+ },
+ { "time": 0.6667, "x": -5.14, "y": 11.13 }
+ ]
+ },
+ "torso": {
+ "rotate": [
+ {
+ "value": -37.66,
+ "curve": [ 0.034, -37.14, 0.107, -36.21 ]
+ },
+ {
+ "time": 0.1333,
+ "value": -36.21,
+ "curve": [ 0.158, -36.21, 0.209, -38.8 ]
+ },
+ {
+ "time": 0.2333,
+ "value": -38.79,
+ "curve": [ 0.259, -38.78, 0.313, -38.03 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -37.66,
+ "curve": [ 0.357, -37.21, 0.4, -36.21 ]
+ },
+ {
+ "time": 0.4333,
+ "value": -36.21,
+ "curve": [ 0.458, -36.21, 0.539, -38.8 ]
+ },
+ {
+ "time": 0.5667,
+ "value": -38.8,
+ "curve": [ 0.592, -38.8, 0.645, -38 ]
+ },
+ { "time": 0.6667, "value": -37.66 }
+ ]
+ },
+ "rear-thigh": {
+ "translate": [
+ {
+ "x": -16.41,
+ "y": 1.55,
+ "curve": [ 0.013, -15.67, 0.183, -8.55, 0.03, 2.39, 0.183, 6.17 ]
+ },
+ {
+ "time": 0.2333,
+ "x": -8.55,
+ "y": 6.17,
+ "curve": [ 0.308, -8.55, 0.492, -19.75, 0.308, 6.17, 0.492, 0.61 ]
+ },
+ {
+ "time": 0.5667,
+ "x": -19.75,
+ "y": 0.61,
+ "curve": [ 0.592, -19.75, 0.641, -18.06, 0.592, 0.61, 0.632, 0.78 ]
+ },
+ { "time": 0.6667, "x": -16.41, "y": 1.55 }
+ ]
+ },
+ "front-upper-arm": {
+ "rotate": [
+ {
+ "value": -39.03,
+ "curve": [ 0.051, -0.1, 0.145, 88.36 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 88.36,
+ "curve": [ 0.28, 88.76, 0.324, 59.52 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 51.13,
+ "curve": [ 0.358, 30.2, 0.445, -74.91 ]
+ },
+ {
+ "time": 0.5667,
+ "value": -75.82,
+ "curve": [ 0.599, -76.06, 0.642, -55.72 ]
+ },
+ { "time": 0.6667, "value": -39.03 }
+ ]
+ },
+ "front-bracer": {
+ "rotate": [
+ {
+ "value": 20.54,
+ "curve": [ 0.052, 11.42, 0.089, 0.13 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 0.15,
+ "curve": [ 0.186, 0.17, 0.221, 26.29 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 32.37,
+ "curve": [ 0.247, 39.19, 0.286, 61.45 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 61.58,
+ "curve": [ 0.371, 61.69, 0.42, 55.79 ]
+ },
+ { "time": 0.4667, "value": 49.68 },
+ { "time": 0.6667, "value": 20.54 }
+ ]
+ },
+ "front-fist": {
+ "rotate": [
+ {
+ "value": -36.16,
+ "curve": [ 0.014, -38.8, 0.036, -43.27 ]
+ },
+ {
+ "time": 0.0667,
+ "value": -43.37,
+ "curve": [ 0.102, -43.49, 0.182, -28.46 ]
+ },
+ {
+ "time": 0.2,
+ "value": -23.04,
+ "curve": [ 0.23, -13.87, 0.264, 3.86 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 3.7,
+ "curve": [ 0.38, 3.64, 0.535, -16.22 ]
+ },
+ { "time": 0.5667, "value": -21.29 },
+ { "time": 0.6667, "value": -36.16 }
+ ]
+ },
+ "rear-upper-arm": {
+ "rotate": [
+ {
+ "value": 40.5,
+ "curve": [ 0.028, 23.74, 0.128, -79.86 ]
+ },
+ {
+ "time": 0.2333,
+ "value": -79.87,
+ "curve": [ 0.38, -79.88, 0.403, 63.25 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 64.13,
+ "curve": [ 0.607, 64.35, 0.644, 53.1 ]
+ },
+ { "time": 0.6667, "value": 40.5 }
+ ],
+ "translate": [
+ {
+ "x": -3.79,
+ "y": -0.77,
+ "curve": [ 0.044, -4.58, 0.169, -5.48, 0.044, 0.93, 0.169, 2.85 ]
+ },
+ {
+ "time": 0.2333,
+ "x": -5.48,
+ "y": 2.85,
+ "curve": [ 0.346, -5.48, 0.475, -2.68, 0.346, 2.85, 0.475, -3.13 ]
+ },
+ {
+ "time": 0.5667,
+ "x": -2.68,
+ "y": -3.13,
+ "curve": [ 0.611, -2.68, 0.642, -3.34, 0.611, -3.13, 0.642, -1.73 ]
+ },
+ { "time": 0.6667, "x": -3.79, "y": -0.77 }
+ ]
+ },
+ "rear-bracer": {
+ "rotate": [
+ { "value": 28.28 },
+ {
+ "time": 0.2333,
+ "value": -11.12,
+ "curve": [ 0.252, -14.12, 0.297, -19.37 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -19.38,
+ "curve": [ 0.435, -19.41, 0.522, 38.96 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 38.87,
+ "curve": [ 0.619, 38.76, 0.644, 32.01 ]
+ },
+ { "time": 0.6667, "value": 28.28 }
+ ]
+ },
+ "neck": {
+ "rotate": [
+ {
+ "value": 11.88,
+ "curve": [ 0.024, 11.4, 0.075, 9.74 ]
+ },
+ {
+ "time": 0.1,
+ "value": 9.74,
+ "curve": [ 0.125, 9.74, 0.208, 13.36 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 13.36,
+ "curve": [ 0.258, 13.36, 0.321, 12.2 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 11.88,
+ "curve": [ 0.365, 11.06, 0.408, 9.72 ]
+ },
+ {
+ "time": 0.4333,
+ "value": 9.72,
+ "curve": [ 0.458, 9.72, 0.542, 13.36 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 13.36,
+ "curve": [ 0.592, 13.36, 0.636, 12.48 ]
+ },
+ { "time": 0.6667, "value": 11.88 }
+ ]
+ },
+ "head": {
+ "rotate": [
+ {
+ "value": 13.14,
+ "curve": [ 0.02, 11.99, 0.039, 8.94 ]
+ },
+ {
+ "time": 0.0667,
+ "value": 8.93,
+ "curve": [ 0.122, 8.9, 0.232, 15.8 ]
+ },
+ {
+ "time": 0.2667,
+ "value": 15.81,
+ "curve": [ 0.325, 15.82, 0.357, 8.95 ]
+ },
+ {
+ "time": 0.4,
+ "value": 8.93,
+ "curve": [ 0.444, 8.91, 0.568, 15.8 ]
+ },
+ {
+ "time": 0.6,
+ "value": 15.77,
+ "curve": [ 0.632, 15.74, 0.649, 14.05 ]
+ },
+ { "time": 0.6667, "value": 13.14 }
+ ],
+ "scale": [
+ {
+ "curve": [ 0.014, 0.996, 0.068, 0.991, 0.027, 1.005, 0.083, 1.012 ]
+ },
+ {
+ "time": 0.1,
+ "x": 0.991,
+ "y": 1.012,
+ "curve": [ 0.128, 0.991, 0.205, 1.018, 0.128, 1.012, 0.197, 0.988 ]
+ },
+ {
+ "time": 0.2333,
+ "x": 1.018,
+ "y": 0.988,
+ "curve": [ 0.272, 1.018, 0.305, 1.008, 0.262, 0.988, 0.311, 0.995 ]
+ },
+ {
+ "time": 0.3333,
+ "curve": [ 0.351, 0.995, 0.417, 0.987, 0.359, 1.006, 0.417, 1.013 ]
+ },
+ {
+ "time": 0.4333,
+ "x": 0.987,
+ "y": 1.013,
+ "curve": [ 0.467, 0.987, 0.533, 1.02, 0.467, 1.013, 0.533, 0.989 ]
+ },
+ {
+ "time": 0.5667,
+ "x": 1.02,
+ "y": 0.989,
+ "curve": [ 0.592, 1.02, 0.652, 1.004, 0.592, 0.989, 0.644, 0.996 ]
+ },
+ { "time": 0.6667 }
+ ]
+ },
+ "gun": {
+ "rotate": [
+ {
+ "value": 12.36,
+ "curve": [ 0.022, 16.28, 0.087, 20.25 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 20.19,
+ "curve": [ 0.168, 20.32, 0.254, -8.82 ]
+ },
+ {
+ "time": 0.2667,
+ "value": -11.88,
+ "curve": [ 0.291, -17.91, 0.344, -24.11 ]
+ },
+ {
+ "time": 0.4,
+ "value": -23.88,
+ "curve": [ 0.448, -23.69, 0.533, -15.47 ]
+ },
+ { "time": 0.5667, "value": -8.69 },
+ { "time": 0.6667, "value": 12.36 }
+ ]
+ },
+ "hip": {
+ "rotate": [
+ { "value": -8.24 }
+ ],
+ "translate": [
+ {
+ "x": -3.6,
+ "y": -34.1,
+ "curve": [ 0.042, -3.84, 0.118, 7.62, 0.042, -33.74, 0.112, 20.55 ]
+ },
+ {
+ "time": 0.1667,
+ "x": 7.61,
+ "y": 20.36,
+ "curve": [ 0.194, 7.6, 0.21, 5.06, 0.204, 20.65, 0.217, -8.69 ]
+ },
+ {
+ "time": 0.2333,
+ "x": 1.68,
+ "y": -18.48,
+ "curve": [ 0.279, -4.99, 0.297, -5.64, 0.254, -31.08, 0.292, -34.55 ]
+ },
+ {
+ "time": 0.3333,
+ "x": -5.76,
+ "y": -35,
+ "curve": [ 0.379, -5.9, 0.451, 6.8, 0.384, -35.56, 0.428, 17.6 ]
+ },
+ {
+ "time": 0.5,
+ "x": 6.61,
+ "y": 17.01,
+ "curve": [ 0.536, 6.47, 0.545, 3.56, 0.533, 16.75, 0.548, -8.71 ]
+ },
+ {
+ "time": 0.5667,
+ "x": 0.35,
+ "y": -18.81,
+ "curve": [ 0.597, -4.07, 0.642, -3.45, 0.584, -28.58, 0.642, -34.32 ]
+ },
+ { "time": 0.6667, "x": -3.6, "y": -34.1 }
+ ]
+ },
+ "front-foot-target": {
+ "rotate": [
+ {
+ "value": -62.54,
+ "curve": [ 0.015, -74.19, 0.056, -103.19 ]
+ },
+ {
+ "time": 0.0667,
+ "value": -111.08,
+ "curve": [ 0.092, -129.44, 0.189, -146.55 ]
+ },
+ {
+ "time": 0.2333,
+ "value": -146.32,
+ "curve": [ 0.285, -146.06, 0.32, -125.1 ]
+ },
+ { "time": 0.3333, "value": -117.24 },
+ {
+ "time": 0.5,
+ "value": -35.07,
+ "curve": [ 0.522, -28.64, 0.546, -24.84 ]
+ },
+ {
+ "time": 0.5667,
+ "value": -24.9,
+ "curve": [ 0.595, -25, 0.623, -40.82 ]
+ },
+ { "time": 0.6667, "value": -62.54 }
+ ],
+ "translate": [
+ { "x": 16.34, "y": 0.18 },
+ {
+ "time": 0.0667,
+ "x": -101.43,
+ "y": 8.04,
+ "curve": [ 0.085, -131.35, 0.129, -207.69, 0.08, 14.9, 0.124, 113.28 ]
+ },
+ {
+ "time": 0.1667,
+ "x": -207.92,
+ "y": 145.81,
+ "curve": [ 0.196, -208.13, 0.21, -202.91, 0.186, 160.26, 0.206, 163.48 ]
+ },
+ {
+ "time": 0.2333,
+ "x": -189.94,
+ "y": 163.85,
+ "curve": [ 0.27, -169.94, 0.31, -126.19, 0.269, 164.35, 0.316, 85.97 ]
+ },
+ {
+ "time": 0.3333,
+ "x": -90.56,
+ "y": 78.57,
+ "curve": [ 0.355, -57.99, 0.376, -29.14, 0.35, 71.55, 0.376, 66.4 ]
+ },
+ {
+ "time": 0.4,
+ "x": 2.87,
+ "y": 66.38,
+ "curve": [ 0.412, 19.24, 0.469, 90.73, 0.429, 66.37, 0.469, 70.66 ]
+ },
+ {
+ "time": 0.5,
+ "x": 117.18,
+ "y": 70.46,
+ "curve": [ 0.522, 136.24, 0.542, 151.33, 0.539, 70.2, 0.555, 38.25 ]
+ },
+ {
+ "time": 0.5667,
+ "x": 151.49,
+ "y": 25.29,
+ "curve": [ 0.578, 146.76, 0.586, 133.13, 0.572, 19.7, 0.582, 12.23 ]
+ },
+ { "time": 0.6, "x": 115.02, "y": 0.1 },
+ { "time": 0.6667, "x": 16.34, "y": 0.18 }
+ ]
+ },
+ "front-leg-target": {
+ "translate": [
+ { "x": -13.95, "y": -30.34 }
+ ]
+ },
+ "rear-foot-target": {
+ "rotate": [
+ { "value": 18.55 },
+ {
+ "time": 0.2333,
+ "value": 167.84,
+ "curve": [ 0.246, 153.66, 0.256, 129.74 ]
+ },
+ {
+ "time": 0.2667,
+ "value": 124.32,
+ "curve": [ 0.296, 124.43, 0.313, 129.93 ]
+ },
+ {
+ "time": 0.3667,
+ "value": 129.87,
+ "curve": [ 0.421, 128.32, 0.519, 0.98 ]
+ },
+ {
+ "time": 0.5667,
+ "curve": [ 0.6, 0.27, 0.642, 4.73 ]
+ },
+ { "time": 0.6667, "value": 18.55 }
+ ],
+ "translate": [
+ {
+ "x": -176.39,
+ "y": 134.12,
+ "curve": [ 0.018, -142.26, 0.054, -94.41, 0.01, 120.96, 0.044, 84.08 ]
+ },
+ {
+ "time": 0.0667,
+ "x": -73.56,
+ "y": 76.68,
+ "curve": [ 0.086, -42.82, 0.194, 101.2, 0.098, 66.73, 0.198, 60.88 ]
+ },
+ { "time": 0.2333, "x": 98.32, "y": 32.17 },
+ { "time": 0.2667, "x": 49.13, "y": -0.63 },
+ {
+ "time": 0.4,
+ "x": -147.9,
+ "y": 0.32,
+ "curve": [ 0.414, -168.78, 0.478, -284.76, 0.43, 30.09, 0.478, 129.14 ]
+ },
+ {
+ "time": 0.5,
+ "x": -283.37,
+ "y": 167.12,
+ "curve": [ 0.526, -285.66, 0.548, -280.54, 0.516, 194.84, 0.55, 216.53 ]
+ },
+ {
+ "time": 0.5667,
+ "x": -266.98,
+ "y": 216.12,
+ "curve": [ 0.581, -256.27, 0.643, -206.54, 0.61, 214.82, 0.65, 145.33 ]
+ },
+ { "time": 0.6667, "x": -176.39, "y": 134.12 }
+ ]
+ },
+ "rear-leg-target": {
+ "translate": [
+ { "x": 85, "y": -33.59 }
+ ]
+ },
+ "back-foot-tip": {
+ "rotate": [
+ {
+ "value": -147.04,
+ "curve": [ 0.033, -113.4, 0.161, 44.34 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 43.48,
+ "curve": [ 0.24, 43.41, 0.282, 35.72 ]
+ },
+ {
+ "time": 0.3,
+ "value": 0.29,
+ "curve": [ 0.347, 0.28, 0.396, 4.27 ]
+ },
+ {
+ "time": 0.4,
+ "curve": [ 0.424, -23.8, 0.525, -181.39 ]
+ },
+ {
+ "time": 0.5667,
+ "value": -181.39,
+ "curve": [ 0.592, -181.39, 0.642, -169.09 ]
+ },
+ { "time": 0.6667, "value": -147.04 }
+ ]
+ },
+ "front-foot-tip": {
+ "rotate": [
+ {
+ "value": -0.25,
+ "curve": [ 0.008, -0.25, 0.056, 1.73 ]
+ },
+ {
+ "time": 0.0667,
+ "value": -7.68,
+ "curve": [ 0.075, -43.13, 0.15, -130.44 ]
+ },
+ {
+ "time": 0.2,
+ "value": -130.08,
+ "curve": [ 0.239, -129.79, 0.272, -126.8 ]
+ },
+ {
+ "time": 0.3,
+ "value": -116.24,
+ "curve": [ 0.333, -103.91, 0.348, -86.1 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -71.08,
+ "curve": [ 0.386, -55.25, 0.415, -32.44 ]
+ },
+ {
+ "time": 0.4333,
+ "value": -21.63,
+ "curve": [ 0.47, -0.01, 0.542, 33.42 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 33.2,
+ "curve": [ 0.622, 32.7, 0.569, 0.64 ]
+ },
+ { "time": 0.6667, "value": -0.25 }
+ ]
+ },
+ "hair1": {
+ "rotate": [
+ {
+ "value": -6.81,
+ "curve": [ 0.087, -6.81, 0.143, -5.75 ]
+ },
+ {
+ "time": 0.1667,
+ "value": -4.3,
+ "curve": [ 0.183, -3.28, 0.209, 2.79 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 2.78,
+ "curve": [ 0.262, 2.77, 0.305, -6.63 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -6.64,
+ "curve": [ 0.419, -6.68, 0.49, -4.84 ]
+ },
+ {
+ "time": 0.5,
+ "value": -4.38,
+ "curve": [ 0.518, -3.56, 0.574, 2.32 ]
+ },
+ {
+ "time": 0.6,
+ "value": 2.33,
+ "curve": [ 0.643, 2.35, 0.633, -6.81 ]
+ },
+ { "time": 0.6667, "value": -6.81 }
+ ]
+ },
+ "hair2": {
+ "rotate": [
+ {
+ "value": -6.81,
+ "curve": [ 0.014, -3.17, 0.109, 43.93 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 43.95,
+ "curve": [ 0.177, 43.97, 0.192, -13.76 ]
+ },
+ {
+ "time": 0.2667,
+ "value": -13.83,
+ "curve": [ 0.302, -13.72, 0.322, -8.86 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -6.6,
+ "curve": [ 0.349, -3.5, 0.436, 41.1 ]
+ },
+ {
+ "time": 0.4667,
+ "value": 41.05,
+ "curve": [ 0.51, 40.99, 0.549, -14.06 ]
+ },
+ {
+ "time": 0.6,
+ "value": -14.18,
+ "curve": [ 0.63, -14.26, 0.656, -9.04 ]
+ },
+ { "time": 0.6667, "value": -6.81 }
+ ]
+ },
+ "hair3": {
+ "rotate": [
+ {
+ "value": -6.81,
+ "curve": [ 0.079, -6.83, 0.108, 0.3 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 1.96,
+ "curve": [ 0.177, 4.89, 0.208, 6.28 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 6.29,
+ "curve": [ 0.313, 6.31, 0.383, 3.49 ]
+ },
+ {
+ "time": 0.4,
+ "value": 2.58,
+ "curve": [ 0.442, 0.28, 0.523, -6.81 ]
+ },
+ { "time": 0.6, "value": -6.81 }
+ ]
+ },
+ "hair4": {
+ "rotate": [
+ {
+ "value": -6.81,
+ "curve": [ 0.011, -4.06, 0.108, 24.92 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 24.92,
+ "curve": [ 0.158, 24.92, 0.208, -10.62 ]
+ },
+ {
+ "time": 0.2333,
+ "value": -10.62,
+ "curve": [ 0.254, -10.62, 0.312, -9.73 ]
+ },
+ {
+ "time": 0.3333,
+ "value": -6.4,
+ "curve": [ 0.356, -2.95, 0.438, 24.93 ]
+ },
+ {
+ "time": 0.4667,
+ "value": 24.93,
+ "curve": [ 0.492, 24.93, 0.575, -9.78 ]
+ },
+ {
+ "time": 0.6,
+ "value": -9.78,
+ "curve": [ 0.617, -9.78, 0.655, -8.63 ]
+ },
+ { "time": 0.6667, "value": -6.81 }
+ ]
+ },
+ "torso2": {
+ "rotate": [
+ {
+ "value": 3.5,
+ "curve": [ 0.07, 3.51, 0.075, 8.69 ]
+ },
+ {
+ "time": 0.1,
+ "value": 8.69,
+ "curve": [ 0.139, 8.69, 0.214, 6.9 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 6.33,
+ "curve": [ 0.266, 5.34, 0.285, 3.48 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 3.48,
+ "curve": [ 0.398, 3.48, 0.408, 8.68 ]
+ },
+ {
+ "time": 0.4333,
+ "value": 8.68,
+ "curve": [ 0.458, 8.68, 0.551, 6.8 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 6.26,
+ "curve": [ 0.598, 5.17, 0.642, 3.49 ]
+ },
+ { "time": 0.6667, "value": 3.5 }
+ ]
+ },
+ "torso3": {
+ "rotate": [
+ {
+ "value": 4.52,
+ "curve": [ 0.067, 4.54, 0.075, -7.27 ]
+ },
+ {
+ "time": 0.1,
+ "value": -7.27,
+ "curve": [ 0.125, -7.27, 0.227, 0.84 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 1.24,
+ "curve": [ 0.254, 2.5, 0.301, 4.51 ]
+ },
+ {
+ "time": 0.3333,
+ "value": 4.52,
+ "curve": [ 0.386, 4.54, 0.408, -7.35 ]
+ },
+ {
+ "time": 0.4333,
+ "value": -7.35,
+ "curve": [ 0.458, -7.35, 0.549, -0.14 ]
+ },
+ {
+ "time": 0.5667,
+ "value": 0.95,
+ "curve": [ 0.586, 2.18, 0.632, 4.54 ]
+ },
+ { "time": 0.6667, "value": 4.52 }
+ ]
+ },
+ "aim-constraint-target": {
+ "rotate": [
+ { "value": 30.57 }
+ ]
+ },
+ "rear-foot": {
+ "rotate": [
+ { "value": -6.5 }
+ ]
+ },
+ "front-foot": {
+ "rotate": [
+ { "value": 4.5 }
+ ]
+ },
+ "head-control": {
+ "translate": [
+ {
+ "y": -9.94,
+ "curve": [ 0.058, 0, 0.175, -15.32, 0.044, -4.19, 0.175, 5 ]
+ },
+ {
+ "time": 0.2333,
+ "x": -15.32,
+ "y": 5,
+ "curve": [ 0.317, -15.32, 0.429, -9.74, 0.317, 5, 0.382, -31.71 ]
+ },
+ {
+ "time": 0.4667,
+ "x": -7.81,
+ "y": -31.59,
+ "curve": [ 0.507, -5.76, 0.617, 0, 0.549, -31.47, 0.628, -13.33 ]
+ },
+ { "time": 0.6667, "y": -9.94 }
+ ]
+ },
+ "front-shoulder": {
+ "translate": [
+ {
+ "x": -0.74,
+ "y": 11.22,
+ "curve": [ 0.061, -0.74, 0.144, 1.17, 0.061, 11.22, 0.143, -17.93 ]
+ },
+ {
+ "time": 0.2333,
+ "x": 1.19,
+ "y": -17.9,
+ "curve": [ 0.54, 1.25, 0.558, -0.74, 0.545, -17.8, 0.558, 11.22 ]
+ },
+ { "time": 0.6667, "x": -0.74, "y": 11.22 }
+ ]
+ },
+ "back-shoulder": {
+ "translate": [
+ {
+ "curve": [ 0.083, 0, 0.25, 0, 0.083, 0, 0.25, 8.93 ]
+ },
+ {
+ "time": 0.3333,
+ "y": 8.93,
+ "curve": [ 0.417, 0, 0.583, 0, 0.417, 8.93, 0.583, 0 ]
+ },
+ { "time": 0.6667 }
+ ]
+ }
+ },
+ "ik": {
+ "front-leg-ik": [
+ { "softness": 10, "bendPositive": false },
+ { "time": 0.5667, "softness": 14.8, "bendPositive": false },
+ { "time": 0.6, "softness": 48.2, "bendPositive": false },
+ { "time": 0.6667, "softness": 10, "bendPositive": false }
+ ],
+ "rear-leg-ik": [
+ { "bendPositive": false },
+ { "time": 0.1667, "softness": 22.5, "bendPositive": false },
+ { "time": 0.3, "softness": 61.4, "bendPositive": false },
+ { "time": 0.6667, "bendPositive": false }
+ ]
+ },
+ "events": [
+ { "time": 0.2333, "name": "footstep" },
+ { "time": 0.5667, "name": "footstep" }
+ ]
+ },
+ "run-to-idle": {
+ "slots": {
+ "front-fist": {
+ "attachment": [
+ { "name": "front-fist-open" }
+ ]
+ }
+ },
+ "bones": {
+ "front-foot-target": {
+ "translate": [
+ {
+ "x": -16.5,
+ "y": 3.41,
+ "curve": [ 0.033, -16.5, 0.1, -69.06, 0.033, 3.41, 0.1, 0 ]
+ },
+ { "time": 0.1333, "x": -69.06 }
+ ]
+ },
+ "hip": {
+ "translate": [
+ {
+ "x": -28.78,
+ "y": -72.96,
+ "curve": [ 0.036, -28.63, 0.2, -10.85, 0.135, -62.35, 0.2, -23.15 ]
+ },
+ { "time": 0.2667, "x": -11.97, "y": -23.15 }
+ ]
+ },
+ "rear-foot-target": {
+ "translate": [
+ {
+ "x": 33.15,
+ "y": 31.61,
+ "curve": [ 0.017, 33.15, 0.05, 24.41, 0.017, 31.61, 0.041, 20.73 ]
+ },
+ {
+ "time": 0.0667,
+ "x": 24.41,
+ "y": 0.19,
+ "curve": [ 0.117, 24.41, 0.217, 48.87, 0.117, 0.19, 0.217, 0 ]
+ },
+ { "time": 0.2667, "x": 48.87 }
+ ]
+ },
+ "front-upper-arm": {
+ "rotate": [
+ {
+ "value": -80.61,
+ "curve": [ 0.067, -80.61, 0.2, -60.87 ]
+ },
+ { "time": 0.2667, "value": -60.87 }
+ ]
+ },
+ "front-bracer": {
+ "rotate": [
+ {
+ "value": 8.79,
+ "curve": [ 0.041, 8.79, 0.115, 6.3 ]
+ },
+ {
+ "time": 0.1667,
+ "value": 6.41,
+ "curve": [ 0.201, 6.48, 0.241, 42.46 ]
+ },
+ { "time": 0.2667, "value": 42.46 }
+ ]
+ },
+ "rear-upper-arm": {
+ "rotate": [
+ {
+ "value": 55.3,
+ "curve": [ 0.067, 55.3, 0.2, 39.2 ]
+ },
+ { "time": 0.2667, "value": 39.2 }
+ ]
+ },
+ "head": {
+ "rotate": [
+ {
+ "curve": [ 0.05, 0, 0.083, 2.67 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 2.67,
+ "curve": [ 0.15, 2.67, 0.25, -6.75 ]
+ },
+ { "time": 0.2667, "value": -6.75 }
+ ]
+ },
+ "front-fist": {
+ "rotate": [
+ {
+ "value": 38.26,
+ "curve": [ 0.041, 38.26, 0.127, -2.19 ]
+ },
+ {
+ "time": 0.1667,
+ "value": -3,
+ "curve": [ 0.209, -3.84, 0.241, 0 ]
+ },
+ { "time": 0.2667 }
+ ],
+ "scale": [
+ {
+ "x": 0.844,
+ "curve": [ 0.067, 0.844, 0.2, 1, 0.067, 1, 0.2, 1 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "rear-bracer": {
+ "rotate": [
+ {
+ "value": 57.24,
+ "curve": [ 0.067, 57.24, 0.2, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "gun": {
+ "rotate": [
+ {
+ "value": 2.28,
+ "curve": [ 0.041, 2.28, 0.105, 15.34 ]
+ },
+ {
+ "time": 0.1667,
+ "value": 15.32,
+ "curve": [ 0.205, 15.31, 0.241, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "torso": {
+ "rotate": [
+ {
+ "value": -12.98,
+ "curve": [ 0.033, -12.98, 0.103, -14.81 ]
+ },
+ {
+ "time": 0.1333,
+ "value": -16.63,
+ "curve": [ 0.168, -18.69, 0.233, -22.88 ]
+ },
+ { "time": 0.2667, "value": -22.88 }
+ ],
+ "scale": [
+ {
+ "x": 0.963,
+ "y": 1.074,
+ "curve": [ 0.067, 0.963, 0.132, 1, 0.067, 1.074, 0.132, 1 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "neck": {
+ "rotate": [
+ {},
+ { "time": 0.2667, "value": 3.78 }
+ ]
+ },
+ "hair3": {
+ "rotate": [
+ {
+ "curve": [ 0.033, 0, 0.1, 0.88 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 0.88,
+ "curve": [ 0.167, 0.88, 0.233, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "hair4": {
+ "rotate": [
+ {
+ "curve": [ 0.033, 0, 0.1, 15.97 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 15.97,
+ "curve": [ 0.167, 15.97, 0.233, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "hair1": {
+ "rotate": [
+ {
+ "curve": [ 0.033, 0, 0.1, 10.76 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 10.76,
+ "curve": [ 0.167, 10.76, 0.233, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "hair2": {
+ "rotate": [
+ {
+ "curve": [ 0.014, -2.28, 0.042, -7.84 ]
+ },
+ {
+ "time": 0.0667,
+ "value": -7.82,
+ "curve": [ 0.108, -7.79, 0.166, 6.57 ]
+ },
+ {
+ "time": 0.2,
+ "value": 6.67,
+ "curve": [ 0.222, 6.73, 0.255, 1.98 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "torso2": {
+ "rotate": [
+ {
+ "curve": [ 0.041, 0, 0.107, 3.03 ]
+ },
+ {
+ "time": 0.1667,
+ "value": 3.03,
+ "curve": [ 0.205, 3.03, 0.241, 0 ]
+ },
+ { "time": 0.2667 }
+ ]
+ },
+ "torso3": {
+ "rotate": [
+ {
+ "curve": [ 0.049, 0, 0.166, 0.66 ]
+ },
+ {
+ "time": 0.2,
+ "value": 0.66,
+ "curve": [ 0.232, 0.65, 0.249, -2.15 ]
+ },
+ { "time": 0.2667, "value": -2.15 }
+ ]
+ },
+ "head-control": {
+ "translate": [
+ { "x": -10.12, "y": 8.71 },
+ { "time": 0.2667 }
+ ]
+ },
+ "front-shoulder": {
+ "translate": [
+ { "x": 4.91, "y": 11.54 },
+ { "time": 0.2667 }
+ ]
+ }
+ }
+ },
+ "shoot": {
+ "slots": {
+ "muzzle": {
+ "rgba": [
+ { "time": 0.1333, "color": "ffffffff" },
+ { "time": 0.2, "color": "ffffff62" }
+ ],
+ "attachment": [
+ { "time": 0.0333, "name": "muzzle01" },
+ { "time": 0.0667, "name": "muzzle02" },
+ { "time": 0.1, "name": "muzzle03" },
+ { "time": 0.1333, "name": "muzzle04" },
+ { "time": 0.1667, "name": "muzzle05" },
+ { "time": 0.2 }
+ ]
+ },
+ "muzzle-glow": {
+ "rgba": [
+ { "color": "ff0c0c00" },
+ {
+ "time": 0.0333,
+ "color": "ffc9adff",
+ "curve": [ 0.255, 1, 0.273, 1, 0.255, 0.76, 0.273, 0.4, 0.255, 0.65, 0.273, 0.22, 0.255, 1, 0.273, 1 ]
+ },
+ { "time": 0.3, "color": "ff400cff" },
+ { "time": 0.6333, "color": "ff0c0c00" }
+ ],
+ "attachment": [
+ { "name": "muzzle-glow" }
+ ]
+ },
+ "muzzle-ring": {
+ "rgba": [
+ {
+ "time": 0.0333,
+ "color": "d8baffff",
+ "curve": [ 0.202, 0.85, 0.214, 0.84, 0.202, 0.73, 0.214, 0.73, 0.202, 1, 0.214, 1, 0.202, 1, 0.214, 0.21 ]
+ },
+ { "time": 0.2333, "color": "d7baff00" }
+ ],
+ "attachment": [
+ { "time": 0.0333, "name": "muzzle-ring" },
+ { "time": 0.2333 }
+ ]
+ },
+ "muzzle-ring2": {
+ "rgba": [
+ {
+ "time": 0.0333,
+ "color": "d8baffff",
+ "curve": [ 0.174, 0.85, 0.184, 0.84, 0.174, 0.73, 0.184, 0.73, 0.174, 1, 0.184, 1, 0.174, 1, 0.184, 0.21 ]
+ },
+ { "time": 0.2, "color": "d7baff00" }
+ ],
+ "attachment": [
+ { "time": 0.0333, "name": "muzzle-ring" },
+ { "time": 0.2 }
+ ]
+ },
+ "muzzle-ring3": {
+ "rgba": [
+ {
+ "time": 0.0333,
+ "color": "d8baffff",
+ "curve": [ 0.174, 0.85, 0.184, 0.84, 0.174, 0.73, 0.184, 0.73, 0.174, 1, 0.184, 1, 0.174, 1, 0.184, 0.21 ]
+ },
+ { "time": 0.2, "color": "d7baff00" }
+ ],
+ "attachment": [
+ { "time": 0.0333, "name": "muzzle-ring" },
+ { "time": 0.2 }
+ ]
+ },
+ "muzzle-ring4": {
+ "rgba": [
+ {
+ "time": 0.0333,
+ "color": "d8baffff",
+ "curve": [ 0.174, 0.85, 0.184, 0.84, 0.174, 0.73, 0.184, 0.73, 0.174, 1, 0.184, 1, 0.174, 1, 0.184, 0.21 ]
+ },
+ { "time": 0.2, "color": "d7baff00" }
+ ],
+ "attachment": [
+ { "time": 0.0333, "name": "muzzle-ring" },
+ { "time": 0.2 }
+ ]
+ }
+ },
+ "bones": {
+ "gun": {
+ "rotate": [
+ {
+ "time": 0.0667,
+ "curve": [ 0.094, 25.89, 0.112, 45.27 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 45.35,
+ "curve": [ 0.192, 45.28, 0.18, -0.09 ]
+ },
+ { "time": 0.6333 }
+ ]
+ },
+ "muzzle": {
+ "translate": [
+ { "x": -11.02, "y": 25.16 }
+ ]
+ },
+ "rear-upper-arm": {
+ "translate": [
+ {
+ "time": 0.0333,
+ "curve": [ 0.045, 0.91, 0.083, 3.46, 0.044, 0.86, 0.083, 3.32 ]
+ },
+ {
+ "time": 0.1,
+ "x": 3.46,
+ "y": 3.32,
+ "curve": [ 0.133, 3.46, 0.176, -0.1, 0.133, 3.32, 0.169, 0 ]
+ },
+ { "time": 0.2333 }
+ ]
+ },
+ "rear-bracer": {
+ "translate": [
+ {
+ "time": 0.0333,
+ "curve": [ 0.075, -3.78, 0.083, -4.36, 0.08, -2.7, 0.083, -2.88 ]
+ },
+ {
+ "time": 0.1,
+ "x": -4.36,
+ "y": -2.88,
+ "curve": [ 0.133, -4.36, 0.168, 0.18, 0.133, -2.88, 0.167, 0 ]
+ },
+ { "time": 0.2333 }
+ ]
+ },
+ "gun-tip": {
+ "translate": [
+ {},
+ { "time": 0.3, "x": 3.15, "y": 0.39 }
+ ],
+ "scale": [
+ { "x": 0.366, "y": 0.366 },
+ { "time": 0.0333, "x": 1.453, "y": 1.453 },
+ { "time": 0.3, "x": 0.366, "y": 0.366 }
+ ]
+ },
+ "muzzle-ring": {
+ "translate": [
+ { "time": 0.0333 },
+ { "time": 0.2333, "x": 64.47 }
+ ],
+ "scale": [
+ { "time": 0.0333 },
+ { "time": 0.2333, "x": 5.951, "y": 5.951 }
+ ]
+ },
+ "muzzle-ring2": {
+ "translate": [
+ { "time": 0.0333 },
+ { "time": 0.2, "x": 172.57 }
+ ],
+ "scale": [
+ { "time": 0.0333 },
+ { "time": 0.2, "x": 4, "y": 4 }
+ ]
+ },
+ "muzzle-ring3": {
+ "translate": [
+ { "time": 0.0333 },
+ { "time": 0.2, "x": 277.17 }
+ ],
+ "scale": [
+ { "time": 0.0333 },
+ { "time": 0.2, "x": 2, "y": 2 }
+ ]
+ },
+ "muzzle-ring4": {
+ "translate": [
+ { "time": 0.0333 },
+ { "time": 0.2, "x": 392.06 }
+ ]
+ }
+ }
+ },
+ "walk": {
+ "bones": {
+ "rear-foot-target": {
+ "rotate": [
+ {
+ "value": -32.82,
+ "curve": [ 0.035, -42.69, 0.057, -70.49 ]
+ },
+ {
+ "time": 0.1,
+ "value": -70.59,
+ "curve": [ 0.236, -70.78, 0.335, -9.87 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -1.56,
+ "curve": [ 0.393, 5.5, 0.477, 13.96 ]
+ },
+ {
+ "time": 0.5,
+ "value": 13.96,
+ "curve": [ 0.519, 13.96, 0.508, 0.13 ]
+ },
+ { "time": 0.5667, "value": -0.28 },
+ {
+ "time": 0.7333,
+ "value": -0.28,
+ "curve": [ 0.827, -0.06, 0.958, -21.07 ]
+ },
+ { "time": 1, "value": -32.82 }
+ ],
+ "translate": [
+ {
+ "x": -167.32,
+ "y": 0.58,
+ "curve": [ 0.022, -180.55, 0.075, -235.51, 0.045, 0.58, 0.075, 30.12 ]
+ },
+ {
+ "time": 0.1,
+ "x": -235.51,
+ "y": 39.92,
+ "curve": [ 0.142, -235.51, 0.208, -201.73, 0.138, 54.94, 0.18, 60.78 ]
+ },
+ {
+ "time": 0.2333,
+ "x": -176.33,
+ "y": 61.48,
+ "curve": [ 0.272, -136.61, 0.321, -45.18, 0.275, 62.02, 0.321, 56.6 ]
+ },
+ {
+ "time": 0.3667,
+ "x": 8.44,
+ "y": 49.67,
+ "curve": [ 0.403, 51.03, 0.486, 66.86, 0.401, 44.37, 0.48, 23.11 ]
+ },
+ { "time": 0.5, "x": 66.57, "y": 14.22 },
+ { "time": 0.5333, "x": 52.58, "y": 0.6 },
+ { "time": 1, "x": -167.32, "y": 0.58 }
+ ]
+ },
+ "front-foot-target": {
+ "rotate": [
+ {
+ "value": 18.19,
+ "curve": [ 0.01, 11.17, 0.043, 1.37 ]
+ },
+ { "time": 0.1, "value": 0.47 },
+ {
+ "time": 0.2333,
+ "value": 0.55,
+ "curve": [ 0.364, 0.3, 0.515, -80.48 ]
+ },
+ {
+ "time": 0.7333,
+ "value": -80.78,
+ "curve": [ 0.788, -80.38, 0.921, 17.42 ]
+ },
+ { "time": 1, "value": 18.19 }
+ ],
+ "translate": [
+ {
+ "x": 139.21,
+ "y": 22.94,
+ "curve": [ 0.025, 139.21, 0.069, 111.46, 0.031, 3.25, 0.075, 0.06 ]
+ },
+ { "time": 0.1, "x": 96.69, "y": 0.06 },
+ {
+ "time": 0.5,
+ "x": -94.87,
+ "y": -0.03,
+ "curve": [ 0.518, -106.82, 0.575, -152.56, 0.534, 5.42, 0.557, 38.46 ]
+ },
+ {
+ "time": 0.6,
+ "x": -152.56,
+ "y": 57.05,
+ "curve": [ 0.633, -152.56, 0.688, -128.05, 0.643, 75.61, 0.7, 84.14 ]
+ },
+ {
+ "time": 0.7333,
+ "x": -109.42,
+ "y": 84.14,
+ "curve": [ 0.771, -93.91, 0.832, -30.64, 0.787, 84.14, 0.799, 89.65 ]
+ },
+ {
+ "time": 0.8667,
+ "x": 17,
+ "y": 75.25,
+ "curve": [ 0.903, 66.18, 0.967, 139.21, 0.932, 61.53, 0.967, 44.02 ]
+ },
+ { "time": 1, "x": 139.21, "y": 22.94 }
+ ]
+ },
+ "hip": {
+ "rotate": [
+ { "value": -4.35 }
+ ],
+ "translate": [
+ {
+ "x": -2.86,
+ "y": -13.86,
+ "curve": [ 0.025, -2.84, 0.067, -2.82, 0.028, -19.14, 0.054, -24.02 ]
+ },
+ {
+ "time": 0.1,
+ "x": -2.61,
+ "y": -24.19,
+ "curve": [ 0.143, -2.34, 0.202, -1.79, 0.152, -23.98, 0.213, -14.81 ]
+ },
+ {
+ "time": 0.2667,
+ "x": -1.21,
+ "y": -7.12,
+ "curve": [ 0.308, -0.86, 0.345, -0.51, 0.306, -1.63, 0.341, 3.15 ]
+ },
+ {
+ "time": 0.3667,
+ "x": -0.33,
+ "y": 3.15,
+ "curve": [ 0.41, 0.02, 0.458, 0.26, 0.427, 3.3, 0.481, -6.75 ]
+ },
+ {
+ "time": 0.5,
+ "x": 0.26,
+ "y": -10.59,
+ "curve": [ 0.553, 0.26, 0.559, 0.2, 0.519, -14.41, 0.548, -23.88 ]
+ },
+ {
+ "time": 0.6,
+ "x": -0.17,
+ "y": -23.71,
+ "curve": [ 0.663, -0.72, 0.798, -2.09, 0.702, -23.36, 0.802, 3.53 ]
+ },
+ {
+ "time": 0.8667,
+ "x": -2.46,
+ "y": 3.48,
+ "curve": [ 0.901, -2.63, 0.967, -2.87, 0.913, 3.45, 0.967, -7.64 ]
+ },
+ { "time": 1, "x": -2.86, "y": -13.86 }
+ ]
+ },
+ "front-foot-tip": {
+ "rotate": [
+ {
+ "value": 28.96,
+ "curve": [ 0.056, 28.74, 0.049, 19.6 ]
+ },
+ { "time": 0.0667, "value": 1.68 },
+ {
+ "time": 0.5,
+ "value": -10,
+ "curve": [ 0.525, -10, 0.592, -54.69 ]
+ },
+ {
+ "time": 0.6,
+ "value": -59.66,
+ "curve": [ 0.623, -74.54, 0.674, -101.78 ]
+ },
+ {
+ "time": 0.7333,
+ "value": -101.78,
+ "curve": [ 0.812, -101.78, 0.855, -84.67 ]
+ },
+ {
+ "time": 0.8667,
+ "value": -63.53,
+ "curve": [ 0.869, -58.38, 0.975, 28.96 ]
+ },
+ { "time": 1, "value": 28.96 }
+ ]
+ },
+ "torso": {
+ "rotate": [
+ {
+ "value": -20.72,
+ "curve": [ 0.025, -20.57, 0.071, -20.04 ]
+ },
+ {
+ "time": 0.1333,
+ "value": -20.04,
+ "curve": [ 0.187, -20.04, 0.285, -21.16 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -21.16,
+ "curve": [ 0.405, -21.16, 0.47, -20.9 ]
+ },
+ {
+ "time": 0.5,
+ "value": -20.71,
+ "curve": [ 0.518, -20.6, 0.582, -20.03 ]
+ },
+ {
+ "time": 0.6333,
+ "value": -20.04,
+ "curve": [ 0.709, -20.05, 0.815, -21.18 ]
+ },
+ {
+ "time": 0.8667,
+ "value": -21.18,
+ "curve": [ 0.908, -21.18, 0.971, -20.93 ]
+ },
+ { "time": 1, "value": -20.72 }
+ ]
+ },
+ "neck": {
+ "rotate": [
+ {
+ "value": 17.78,
+ "curve": [ 0.025, 17.93, 0.071, 18.46 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 18.46,
+ "curve": [ 0.187, 18.46, 0.285, 17.34 ]
+ },
+ {
+ "time": 0.3667,
+ "value": 17.34,
+ "curve": [ 0.405, 17.34, 0.47, 17.6 ]
+ },
+ {
+ "time": 0.5,
+ "value": 17.79,
+ "curve": [ 0.518, 17.9, 0.582, 18.47 ]
+ },
+ {
+ "time": 0.6333,
+ "value": 18.46,
+ "curve": [ 0.709, 18.45, 0.815, 17.32 ]
+ },
+ {
+ "time": 0.8667,
+ "value": 17.32,
+ "curve": [ 0.908, 17.32, 0.971, 17.57 ]
+ },
+ { "time": 1, "value": 17.78 }
+ ]
+ },
+ "head": {
+ "rotate": [
+ {
+ "value": -12.23,
+ "curve": [ 0.061, -12.23, 0.191, -7.45 ]
+ },
+ {
+ "time": 0.2667,
+ "value": -7.43,
+ "curve": [ 0.341, -7.42, 0.421, -12.23 ]
+ },
+ {
+ "time": 0.5,
+ "value": -12.23,
+ "curve": [ 0.567, -12.26, 0.694, -7.46 ]
+ },
+ {
+ "time": 0.7667,
+ "value": -7.47,
+ "curve": [ 0.853, -7.49, 0.943, -12.23 ]
+ },
+ { "time": 1, "value": -12.23 }
+ ],
+ "scale": [
+ {
+ "curve": [ 0.039, 1, 0.084, 0.991, 0.039, 1, 0.084, 1.019 ]
+ },
+ {
+ "time": 0.1333,
+ "x": 0.991,
+ "y": 1.019,
+ "curve": [ 0.205, 0.991, 0.318, 1.019, 0.205, 1.019, 0.337, 0.992 ]
+ },
+ {
+ "time": 0.4,
+ "x": 1.019,
+ "y": 0.992,
+ "curve": [ 0.456, 1.019, 0.494, 1.001, 0.483, 0.991, 0.493, 0.999 ]
+ },
+ {
+ "time": 0.5,
+ "curve": [ 0.508, 0.998, 0.584, 0.991, 0.51, 1.002, 0.584, 1.019 ]
+ },
+ {
+ "time": 0.6333,
+ "x": 0.991,
+ "y": 1.019,
+ "curve": [ 0.705, 0.991, 0.818, 1.019, 0.705, 1.019, 0.837, 0.992 ]
+ },
+ {
+ "time": 0.9,
+ "x": 1.019,
+ "y": 0.992,
+ "curve": [ 0.956, 1.019, 0.955, 1, 0.983, 0.991, 0.955, 1 ]
+ },
+ { "time": 1 }
+ ]
+ },
+ "back-foot-tip": {
+ "rotate": [
+ { "value": 4.09 },
+ { "time": 0.0333, "value": 3.05 },
+ {
+ "time": 0.1,
+ "value": -59.01,
+ "curve": [ 0.124, -72.97, 0.169, -100.05 ]
+ },
+ {
+ "time": 0.2333,
+ "value": -99.71,
+ "curve": [ 0.326, -99.21, 0.349, -37.4 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -17.85,
+ "curve": [ 0.388, 4.74, 0.451, 32.35 ]
+ },
+ {
+ "time": 0.5,
+ "value": 32.4,
+ "curve": [ 0.537, 32.44, 0.566, 6.43 ]
+ },
+ { "time": 0.5667, "value": 2 },
+ { "time": 1, "value": 4.09 }
+ ]
+ },
+ "front-thigh": {
+ "translate": [
+ {
+ "x": 17.15,
+ "y": -0.09,
+ "curve": [ 0.178, 17.14, 0.295, -4.26, 0.009, -0.09, 0.475, 0.02 ]
+ },
+ {
+ "time": 0.5,
+ "x": -4.26,
+ "y": 0.02,
+ "curve": [ 0.705, -4.27, 0.848, 17.15, 0.525, 0.02, 0.975, -0.09 ]
+ },
+ { "time": 1, "x": 17.15, "y": -0.09 }
+ ]
+ },
+ "rear-thigh": {
+ "translate": [
+ {
+ "x": -17.71,
+ "y": -4.63,
+ "curve": [ 0.036, -19.81, 0.043, -20.86, 0.036, -4.63, 0.05, -7.03 ]
+ },
+ {
+ "time": 0.1,
+ "x": -20.95,
+ "y": -7.06,
+ "curve": [ 0.162, -21.05, 0.4, 7.79, 0.2, -7.13, 0.4, -1.9 ]
+ },
+ {
+ "time": 0.5,
+ "x": 7.79,
+ "y": -1.94,
+ "curve": [ 0.612, 7.69, 0.875, -10.49, 0.592, -1.97, 0.917, -3.25 ]
+ },
+ { "time": 1, "x": -17.71, "y": -4.63 }
+ ]
+ },
+ "torso2": {
+ "rotate": [
+ {
+ "value": 1,
+ "curve": [ 0.006, 1.2, 0.084, 2.88 ]
+ },
+ {
+ "time": 0.1333,
+ "value": 2.88,
+ "curve": [ 0.205, 2.88, 0.284, -1.17 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -1.17,
+ "curve": [ 0.411, -1.17, 0.481, 0.57 ]
+ },
+ {
+ "time": 0.5,
+ "value": 1,
+ "curve": [ 0.515, 1.33, 0.59, 2.83 ]
+ },
+ {
+ "time": 0.6333,
+ "value": 2.85,
+ "curve": [ 0.683, 2.86, 0.796, -1.2 ]
+ },
+ {
+ "time": 0.8667,
+ "value": -1.2,
+ "curve": [ 0.916, -1.2, 0.984, 0.62 ]
+ },
+ { "time": 1, "value": 1 }
+ ]
+ },
+ "torso3": {
+ "rotate": [
+ { "value": -1.81 }
+ ]
+ },
+ "front-upper-arm": {
+ "rotate": [
+ {
+ "value": -9.51,
+ "curve": [ 0.021, -13.32, 0.058, -19.4 ]
+ },
+ {
+ "time": 0.1,
+ "value": -19.4,
+ "curve": [ 0.238, -19.69, 0.337, 7.78 ]
+ },
+ {
+ "time": 0.3667,
+ "value": 16.2,
+ "curve": [ 0.399, 25.42, 0.497, 60.19 ]
+ },
+ {
+ "time": 0.6,
+ "value": 60.26,
+ "curve": [ 0.719, 60.13, 0.845, 27.61 ]
+ },
+ {
+ "time": 0.8667,
+ "value": 22.45,
+ "curve": [ 0.892, 16.38, 0.979, -3.27 ]
+ },
+ { "time": 1, "value": -9.51 }
+ ]
+ },
+ "front-bracer": {
+ "rotate": [
+ {
+ "value": 13.57,
+ "curve": [ 0.022, 9.71, 0.147, -3.78 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -3.69,
+ "curve": [ 0.457, -3.66, 0.479, 0.83 ]
+ },
+ {
+ "time": 0.5,
+ "value": 4.05,
+ "curve": [ 0.513, 6.08, 0.635, 30.8 ]
+ },
+ {
+ "time": 0.8,
+ "value": 30.92,
+ "curve": [ 0.974, 31, 0.98, 18.35 ]
+ },
+ { "time": 1, "value": 13.57 }
+ ]
+ },
+ "front-fist": {
+ "rotate": [
+ {
+ "value": -28.72,
+ "curve": [ 0.024, -31.74, 0.176, -43.4 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -43.6,
+ "curve": [ 0.403, -43.65, 0.47, -40.15 ]
+ },
+ {
+ "time": 0.5,
+ "value": -35.63,
+ "curve": [ 0.547, -28.59, 0.624, -4.57 ]
+ },
+ {
+ "time": 0.7333,
+ "value": -4.59,
+ "curve": [ 0.891, -4.62, 0.954, -24.28 ]
+ },
+ { "time": 1, "value": -28.48 }
+ ]
+ },
+ "rear-upper-arm": {
+ "rotate": [
+ {
+ "value": 28.28,
+ "curve": [ 0.034, 30.94, 0.068, 32.05 ]
+ },
+ {
+ "time": 0.1,
+ "value": 31.88,
+ "curve": [ 0.194, 31.01, 0.336, -0.11 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -7.11,
+ "curve": [ 0.421, -19.73, 0.53, -46.21 ]
+ },
+ {
+ "time": 0.6,
+ "value": -45.75,
+ "curve": [ 0.708, -45.03, 0.844, -13.56 ]
+ },
+ {
+ "time": 0.8667,
+ "value": -6.48,
+ "curve": [ 0.909, 6.59, 0.958, 24.21 ]
+ },
+ { "time": 1, "value": 28.28 }
+ ]
+ },
+ "hair2": {
+ "rotate": [
+ {
+ "value": -2.79,
+ "curve": [ 0.074, -2.84, 0.121, 25.08 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 24.99,
+ "curve": [ 0.35, 24.89, 0.427, -2.86 ]
+ },
+ {
+ "time": 0.5,
+ "value": -2.8,
+ "curve": [ 0.575, -2.73, 0.652, 24.5 ]
+ },
+ {
+ "time": 0.7333,
+ "value": 24.55,
+ "curve": [ 0.828, 24.6, 0.932, -2.69 ]
+ },
+ { "time": 1, "value": -2.79 }
+ ]
+ },
+ "hair4": {
+ "rotate": [
+ {
+ "value": -6.01,
+ "curve": [ 0.106, -5.97, 0.151, 18.62 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 18.72,
+ "curve": [ 0.336, 18.7, 0.405, -11.37 ]
+ },
+ {
+ "time": 0.5,
+ "value": -11.45,
+ "curve": [ 0.626, -11.46, 0.629, 18.94 ]
+ },
+ {
+ "time": 0.7333,
+ "value": 18.92,
+ "curve": [ 0.833, 18.92, 0.913, -6.06 ]
+ },
+ { "time": 1, "value": -6.01 }
+ ],
+ "translate": [
+ { "x": 0.03, "y": 1.35 }
+ ]
+ },
+ "rear-bracer": {
+ "rotate": [
+ {
+ "value": 10.06,
+ "curve": [ 0.044, 11.16, 0.063, 11.49 ]
+ },
+ {
+ "time": 0.1,
+ "value": 11.49,
+ "curve": [ 0.215, 11.49, 0.336, 2.92 ]
+ },
+ {
+ "time": 0.3667,
+ "value": 0.84,
+ "curve": [ 0.416, -2.52, 0.498, -10.84 ]
+ },
+ {
+ "time": 0.6,
+ "value": -10.83,
+ "curve": [ 0.762, -10.71, 0.845, -3.05 ]
+ },
+ {
+ "time": 0.8667,
+ "value": -1.34,
+ "curve": [ 0.917, 2.54, 0.977, 8.81 ]
+ },
+ { "time": 1, "value": 10.06 }
+ ]
+ },
+ "gun": {
+ "rotate": [
+ {
+ "value": -14.67,
+ "curve": [ 0.086, -14.67, 0.202, 8.31 ]
+ },
+ {
+ "time": 0.2333,
+ "value": 12.14,
+ "curve": [ 0.279, 17.71, 0.391, 25.79 ]
+ },
+ {
+ "time": 0.5,
+ "value": 25.77,
+ "curve": [ 0.631, 25.74, 0.694, 4.53 ]
+ },
+ {
+ "time": 0.7333,
+ "value": -0.65,
+ "curve": [ 0.768, -5.21, 0.902, -14.4 ]
+ },
+ { "time": 1, "value": -14.67 }
+ ]
+ },
+ "front-leg-target": {
+ "translate": [
+ {
+ "x": -2.83,
+ "y": -8.48,
+ "curve": [ 0.008, -2.83, 0.058, 0.09, 0.001, 4.97, 0.058, 6.68 ]
+ },
+ {
+ "time": 0.0667,
+ "x": 0.09,
+ "y": 6.68,
+ "curve": [ 0.3, 0.09, 0.767, -2.83, 0.3, 6.68, 0.767, -8.48 ]
+ },
+ { "time": 1, "x": -2.83, "y": -8.48 }
+ ]
+ },
+ "hair1": {
+ "rotate": [
+ {
+ "curve": [ 0.028, 1.24, 0.016, 3.46 ]
+ },
+ {
+ "time": 0.1,
+ "value": 3.45,
+ "curve": [ 0.159, 3.45, 0.189, 0.23 ]
+ },
+ {
+ "time": 0.2333,
+ "value": -2.29,
+ "curve": [ 0.265, -4.32, 0.305, -5.92 ]
+ },
+ {
+ "time": 0.3667,
+ "value": -5.94,
+ "curve": [ 0.446, -5.96, 0.52, 3.41 ]
+ },
+ {
+ "time": 0.6,
+ "value": 3.42,
+ "curve": [ 0.717, 3.42, 0.772, -5.93 ]
+ },
+ {
+ "time": 0.8667,
+ "value": -5.97,
+ "curve": [ 0.933, -5.99, 0.982, -0.94 ]
+ },
+ { "time": 1 }
+ ]
+ },
+ "hair3": {
+ "rotate": [
+ {
+ "curve": [ 0.067, 0, 0.159, -10.48 ]
+ },
+ {
+ "time": 0.2333,
+ "value": -10.49,
+ "curve": [ 0.334, -10.5, 0.439, -0.09 ]
+ },
+ {
+ "time": 0.5,
+ "value": -0.09,
+ "curve": [ 0.569, -0.09, 0.658, -10.75 ]
+ },
+ {
+ "time": 0.7333,
+ "value": -10.7,
+ "curve": [ 0.833, -10.63, 0.947, 0 ]
+ },
+ { "time": 1 }
+ ]
+ },
+ "gun-tip": {
+ "rotate": [
+ { "time": 0.2333, "value": 0.11 }
+ ]
+ },
+ "muzzle-ring": {
+ "rotate": [
+ { "time": 0.2333, "value": 0.11 }
+ ]
+ },
+ "muzzle-ring2": {
+ "rotate": [
+ { "time": 0.2667, "value": 0.11 }
+ ]
+ },
+ "muzzle-ring3": {
+ "rotate": [
+ { "time": 0.2667, "value": 0.11 }
+ ]
+ },
+ "muzzle-ring4": {
+ "rotate": [
+ { "time": 0.2667, "value": 0.11 }
+ ]
+ },
+ "back-shoulder": {
+ "translate": [
+ {
+ "x": -0.18,
+ "y": -4.49,
+ "curve": [ 0.133, -0.18, 0.333, 7.69, 0.133, -4.49, 0.333, 2.77 ]
+ },
+ {
+ "time": 0.4667,
+ "x": 7.69,
+ "y": 2.77,
+ "curve": [ 0.6, 7.69, 0.858, -0.18, 0.6, 2.77, 0.858, -4.49 ]
+ },
+ { "time": 1, "x": -0.18, "y": -4.49 }
+ ]
+ },
+ "front-shoulder": {
+ "translate": [
+ {
+ "x": 1.46,
+ "y": 9.37,
+ "curve": [ 0.162, 1.41, 0.333, -1.66, 0.162, 9.37, 0.301, -7.23 ]
+ },
+ {
+ "time": 0.5,
+ "x": -1.6,
+ "y": -7.27,
+ "curve": [ 0.735, -1.5, 0.847, 1.46, 0.723, -7.31, 0.838, 9.32 ]
+ },
+ { "time": 1, "x": 1.46, "y": 9.37 }
+ ]
+ },
+ "head-control": {
+ "translate": [
+ {
+ "x": -6.46,
+ "y": -8.4,
+ "curve": [ 0.053, -5.31, 0.167, -3.64, 0.093, -8.4, 0.196, -3.81 ]
+ },
+ {
+ "time": 0.2333,
+ "x": -3.64,
+ "y": -1.32,
+ "curve": [ 0.309, -3.64, 0.436, -5.84, 0.275, 1.43, 0.38, 10.3 ]
+ },
+ {
+ "time": 0.5,
+ "x": -7.03,
+ "y": 10.29,
+ "curve": [ 0.538, -7.75, 0.66, -10.54, 0.598, 10.27, 0.694, 1.56 ]
+ },
+ {
+ "time": 0.7333,
+ "x": -10.54,
+ "y": -1.26,
+ "curve": [ 0.797, -10.54, 0.933, -7.91, 0.768, -3.79, 0.875, -8.4 ]
+ },
+ { "time": 1, "x": -6.46, "y": -8.4 }
+ ]
+ }
+ },
+ "ik": {
+ "front-leg-ik": [
+ {
+ "softness": 25.7,
+ "bendPositive": false,
+ "curve": [ 0.008, 1, 0.025, 1, 0.008, 25.7, 0.025, 9.9 ]
+ },
+ {
+ "time": 0.0333,
+ "softness": 9.9,
+ "bendPositive": false,
+ "curve": [ 0.15, 1, 0.383, 1, 0.15, 9.9, 0.383, 43.2 ]
+ },
+ {
+ "time": 0.5,
+ "softness": 43.2,
+ "bendPositive": false,
+ "curve": [ 0.625, 1, 0.875, 1, 0.625, 43.2, 0.846, 45.57 ]
+ },
+ { "time": 1, "softness": 25.7, "bendPositive": false }
+ ],
+ "rear-leg-ik": [
+ { "softness": 5, "bendPositive": false },
+ { "time": 0.4333, "softness": 4.9, "bendPositive": false },
+ { "time": 0.5, "softness": 28.81, "bendPositive": false },
+ { "time": 0.6, "softness": 43.8, "bendPositive": false },
+ { "time": 1, "softness": 5, "bendPositive": false }
+ ]
+ },
+ "events": [
+ { "name": "footstep" },
+ { "time": 0.5, "name": "footstep" }
+ ]
+ }
+ }
+ }
\ No newline at end of file
diff --git a/e2e/.dev/public/spineboy.png b/e2e/.dev/public/spineboy.png
new file mode 100644
index 0000000000..0ea9737f30
Binary files /dev/null and b/e2e/.dev/public/spineboy.png differ
diff --git a/e2e/.dev/public/tank-pro.atlas b/e2e/.dev/public/tank-pro.atlas
new file mode 100644
index 0000000000..0c1a46580e
--- /dev/null
+++ b/e2e/.dev/public/tank-pro.atlas
@@ -0,0 +1,65 @@
+tank-pro.png
+size:870,638
+filter:Linear,Linear
+scale:0.5
+antenna
+bounds:647,376,11,152
+cannon
+bounds:2,359,466,29
+cannon-connector
+bounds:812,448,56,68
+ground
+bounds:2,16,512,177
+guntower
+bounds:641,4,365,145
+rotate:90
+machinegun
+bounds:470,359,166,29
+machinegun-mount
+bounds:823,518,36,48
+rock
+bounds:690,371,265,61
+offsets:7,1,290,64
+rotate:90
+smoke-glow
+bounds:817,371,50,50
+smoke-puff01-bg
+bounds:753,371,92,62
+rotate:90
+smoke-puff01-fg
+bounds:753,465,86,57
+offsets:1,1,88,59
+rotate:90
+smoke-puff02-fg
+bounds:788,127,85,53
+offsets:4,6,92,62
+rotate:90
+smoke-puff03-fg
+bounds:788,214,85,53
+offsets:4,6,92,62
+rotate:90
+smoke-puff04-fg
+bounds:788,47,78,48
+rotate:90
+tank-bottom
+bounds:2,390,643,138
+tank-bottom-shadow
+bounds:2,195,638,162
+offsets:1,8,646,171
+tank-top
+bounds:2,530,687,106
+offsets:13,5,704,111
+tread
+bounds:817,423,48,15
+tread-inside
+bounds:611,81,13,14
+wheel-big
+bounds:516,97,96,96
+wheel-big-overlay
+bounds:516,2,93,93
+wheel-mid
+bounds:753,553,68,68
+wheel-mid-overlay
+bounds:788,301,68,68
+wheel-small
+bounds:788,9,36,36
diff --git a/e2e/.dev/public/tank-pro.json b/e2e/.dev/public/tank-pro.json
new file mode 100644
index 0000000000..324d313149
--- /dev/null
+++ b/e2e/.dev/public/tank-pro.json
@@ -0,0 +1 @@
+{"skeleton":{"hash":"nW5DL2uoEfA","spine":"4.2.40","x":-5852.65,"y":-348.5,"width":7202.61,"height":1298.88,"images":"./images/","audio":""},"bones":[{"name":"root"},{"name":"tank-root","parent":"root","y":146.79},{"name":"tank-treads","parent":"tank-root","y":48.35},{"name":"tank-body","parent":"tank-treads","y":10},{"name":"guntower","parent":"tank-body","x":-21.72,"y":245.48},{"name":"antenna-root","parent":"guntower","x":164.61,"y":202.53},{"name":"antenna1","parent":"antenna-root","length":40,"rotation":90,"y":40,"color":"ffee00ff"},{"name":"antenna2","parent":"antenna1","length":42,"x":42,"color":"ffee00ff"},{"name":"antenna3","parent":"antenna2","length":42,"x":42,"color":"ffee00ff"},{"name":"antenna4","parent":"antenna3","length":42,"x":42,"color":"ffee00ff"},{"name":"antenna5","parent":"antenna4","length":42,"x":42,"color":"ffee00ff"},{"name":"antenna6","parent":"antenna5","length":42,"x":42,"color":"ffee00ff"},{"name":"cannon-connector","parent":"guntower","x":-235.05,"y":96.07},{"name":"cannon-target","parent":"tank-root","x":-2276.67,"y":400.17,"color":"0096ffff","icon":"arrows"},{"name":"cannon","parent":"cannon-connector","length":946.68,"rotation":180,"color":"ff4000ff"},{"name":"machinegun-mount","parent":"guntower","length":90.98,"rotation":90,"x":-123.73,"y":218.33,"color":"15ff00ff"},{"name":"machinegun-target","parent":"tank-root","x":-2272.76,"y":607.77,"color":"0096ffff","icon":"ik"},{"name":"machinegun","parent":"machinegun-mount","length":208.95,"rotation":90,"x":91.52,"y":-1.03,"color":"15ff00ff"},{"name":"machinegun-tip","parent":"machinegun","x":210.43,"y":-12.21},{"name":"rock","parent":"root","x":-1925.2,"y":33.17},{"name":"smoke-root","parent":"tank-root","x":-1200.38,"y":405.76,"scaleX":-6.5,"scaleY":6.5,"color":"ff4000ff","icon":"particles"},{"name":"smoke-glow","parent":"smoke-root","x":62.92,"y":-0.71,"color":"ff4000ff","icon":"particles"},{"name":"smoke1","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke10","parent":"smoke-root","rotation":-103.52,"x":25.45,"y":2.48,"scaleX":3.9011,"scaleY":2.8523,"color":"ff4000ff","icon":"particles"},{"name":"smoke11","parent":"smoke-root","rotation":-103.52,"x":25.45,"y":2.48,"scaleX":3.9011,"scaleY":2.8523,"color":"ff4000ff","icon":"particles"},{"name":"smoke12","parent":"smoke-root","rotation":-103.52,"x":25.45,"y":2.48,"scaleX":3.9011,"scaleY":2.8523,"color":"ff4000ff","icon":"particles"},{"name":"smoke13","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke14","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke15","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke16","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke17","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke18","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke2","parent":"smoke-root","rotation":-84.14,"x":45.06,"y":29.7,"scaleX":3.3345,"scaleY":3.3345,"color":"ff4000ff","icon":"particles"},{"name":"smoke20","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke21","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke22","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke23","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke24","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke25","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke26","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke27","parent":"smoke-root","rotation":-179.99,"x":14.75,"y":-1.55,"scaleX":1.6484,"scaleY":1.6484,"color":"ff4000ff","icon":"particles"},{"name":"smoke3","parent":"smoke-root","rotation":-87.91,"x":55.15,"y":-17.5,"scaleX":3.0415,"scaleY":4.157,"color":"ff4000ff","icon":"particles"},{"name":"smoke4","parent":"smoke-root","rotation":-87.91,"x":69.25,"y":8.01,"scaleX":2.1808,"scaleY":2.9807,"color":"ff4000ff","icon":"particles"},{"name":"smoke5","parent":"smoke-root","rotation":-87.91,"x":80.63,"y":59.88,"scaleX":4.5119,"scaleY":2.9725,"color":"ff4000ff","icon":"particles"},{"name":"smoke6","parent":"smoke-root","rotation":-87.91,"x":96.19,"y":25.65,"scaleX":3.7912,"scaleY":3.0552,"color":"ff4000ff","icon":"particles"},{"name":"smoke7","parent":"smoke-root","rotation":153.68,"x":85.65,"y":-50.47,"scaleX":4.8523,"scaleY":3.6528,"color":"ff4000ff","icon":"particles"},{"name":"smoke8","parent":"smoke-root","rotation":67.58,"x":47.85,"y":-42.55,"scaleX":4.0006,"scaleY":3.4796,"color":"ff4000ff","icon":"particles"},{"name":"smoke9","parent":"smoke-root","rotation":150.05,"x":104.02,"y":-8.73,"scaleX":4.2074,"scaleY":3.0762,"color":"ff4000ff","icon":"particles"},{"name":"tank-glow","parent":"tank-root","x":-247.72,"y":404.37,"scaleX":1.0582,"scaleY":0.6785},{"name":"tread","parent":"tank-root","length":82,"rotation":180,"x":-22.9,"y":213.86,"scaleX":0.9933,"color":"e64344ff"},{"name":"wheel-mid-center","parent":"tank-root","y":-66.21},{"name":"tread-collider1","parent":"wheel-mid-center","x":-329.58,"y":-85.44,"color":"ff00fbff"},{"name":"tread-collider2","parent":"wheel-mid-center","x":-165.95,"y":-85.44,"color":"ff00fbff"},{"name":"tread-collider3","parent":"wheel-mid-center","y":-85.44,"color":"ff00fbff"},{"name":"tread-collider4","parent":"wheel-mid-center","x":163.56,"y":-85.44,"color":"ff00fbff"},{"name":"tread-collider5","parent":"wheel-mid-center","x":329.12,"y":-85.44,"color":"ff00fbff"},{"name":"tread-gravity1","parent":"tank-root","rotation":180,"x":-175.35,"y":149.31,"color":"ff00fbff"},{"name":"tread-gravity2","parent":"tank-root","rotation":180,"x":177.89,"y":144.78,"color":"ff00fbff"},{"name":"tread10","parent":"tread","length":82,"rotation":48.85,"x":662.9,"y":-120.35,"color":"e64344ff"},{"name":"tread11","parent":"tread","length":82,"rotation":97.99,"x":651.5,"y":-39.69,"color":"e64344ff"},{"name":"tread12","parent":"tread","length":82,"rotation":113.79,"x":618.43,"y":34.83,"color":"e64344ff"},{"name":"tread13","parent":"tread","length":82,"rotation":122.96,"x":573.82,"y":103.18,"color":"e64344ff"},{"name":"tread14","parent":"tread","length":82,"rotation":142.01,"x":509.19,"y":153.3,"color":"e64344ff"},{"name":"tread15","parent":"tread","length":82,"rotation":157.84,"x":433.25,"y":184.02,"color":"e64344ff"},{"name":"tread16","parent":"tread","length":82,"rotation":157.37,"x":357.56,"y":215.37,"color":"e64344ff"},{"name":"tread17","parent":"tread","length":82,"rotation":157.29,"x":281.92,"y":246.8,"color":"e64344ff"},{"name":"tread18","parent":"tread","length":82,"rotation":157.19,"x":206.33,"y":278.38,"color":"e64344ff"},{"name":"tread19","parent":"tread","length":82,"rotation":157.14,"x":130.77,"y":310.02,"color":"e64344ff"},{"name":"tread2","parent":"tread","length":82,"x":82,"color":"e64344ff"},{"name":"tread20","parent":"tread","length":82,"rotation":157.34,"x":55.1,"y":341.41,"color":"e64344ff"},{"name":"tread21","parent":"tread","length":82,"rotation":158.11,"x":-20.99,"y":371.77,"color":"e64344ff"},{"name":"tread22","parent":"tread","length":82,"rotation":157.99,"x":-97.02,"y":402.28,"color":"e64344ff"},{"name":"tread23","parent":"tread","length":82,"rotation":157.59,"x":-172.83,"y":433.33,"color":"e64344ff"},{"name":"tread24","parent":"tread","length":82,"rotation":156.86,"x":-248.23,"y":465.34,"color":"e64344ff"},{"name":"tread25","parent":"tread","length":82,"rotation":177.94,"x":-330.17,"y":468.27,"color":"e64344ff"},{"name":"tread26","parent":"tread","length":82,"rotation":-169.55,"x":-410.81,"y":453.5,"color":"e64344ff"},{"name":"tread27","parent":"tread","length":82,"rotation":-163.86,"x":-489.58,"y":430.86,"color":"e64344ff"},{"name":"tread28","parent":"tread","length":82,"rotation":-139.13,"x":-551.59,"y":377.57,"color":"e64344ff"},{"name":"tread29","parent":"tread","length":82,"rotation":-89.04,"x":-550.21,"y":296.14,"color":"e64344ff"},{"name":"tread3","parent":"tread","length":82,"rotation":-8.91,"x":163.01,"y":-12.61,"color":"e64344ff"},{"name":"tread30","parent":"tread","length":82,"rotation":-38.99,"x":-486.48,"y":244.89,"color":"e64344ff"},{"name":"tread31","parent":"tread","length":82,"rotation":-20.04,"x":-409.45,"y":216.98,"color":"e64344ff"},{"name":"tread32","parent":"tread","length":82,"rotation":-46.24,"x":-352.74,"y":158.15,"color":"e64344ff"},{"name":"tread33","parent":"tread","length":82,"rotation":-27.95,"x":-280.3,"y":119.98,"color":"e64344ff"},{"name":"tread34","parent":"tread","length":82,"rotation":10.46,"x":-199.66,"y":134.77,"color":"e64344ff"},{"name":"tread35","parent":"tread","length":82,"rotation":-17.9,"x":-121.63,"y":109.73,"color":"e64344ff"},{"name":"tread36","parent":"tread","length":82,"rotation":-36.82,"x":-55.99,"y":60.92,"color":"fbff00ff"},{"name":"tread4","parent":"tread","length":82,"rotation":-29.27,"x":234.55,"y":-52.43,"color":"e64344ff"},{"name":"tread5","parent":"tread","length":82,"rotation":-45.26,"x":292.26,"y":-110.28,"color":"e64344ff"},{"name":"tread6","parent":"tread","length":82,"rotation":-15.29,"x":371.36,"y":-131.76,"color":"e64344ff"},{"name":"tread7","parent":"tread","length":82,"rotation":-5.49,"x":452.98,"y":-139.55,"color":"e64344ff"},{"name":"tread8","parent":"tread","length":82,"rotation":-24.99,"x":527.31,"y":-173.95,"color":"e64344ff"},{"name":"tread9","parent":"tread","length":82,"rotation":-5.44,"x":608.94,"y":-181.68,"color":"e64344ff"},{"name":"wheel-big-root1","parent":"tank-treads","x":-549.6,"y":14.4,"color":"abe323ff"},{"name":"wheel-big-root2","parent":"tank-treads","x":547.34,"y":14.4},{"name":"wheel-big1","parent":"wheel-big-root1","x":-0.02,"color":"abe323ff"},{"name":"wheel-big2","parent":"wheel-big-root2"},{"name":"wheel-mid-root1","parent":"wheel-mid-center","x":-410.57,"color":"abe323ff"},{"name":"wheel-mid-root2","parent":"wheel-mid-center","x":-246.95},{"name":"wheel-mid-root3","parent":"wheel-mid-center","x":-82.73},{"name":"wheel-mid-root4","parent":"wheel-mid-center","x":80.89},{"name":"wheel-mid-root5","parent":"wheel-mid-center","x":244.51},{"name":"wheel-mid-root6","parent":"wheel-mid-center","x":408.74},{"name":"wheel-mid1","parent":"wheel-mid-root1","color":"abe323ff"},{"name":"wheel-mid2","parent":"wheel-mid-root2"},{"name":"wheel-mid3","parent":"wheel-mid-root3"},{"name":"wheel-mid4","parent":"wheel-mid-root4"},{"name":"wheel-mid5","parent":"wheel-mid-root5"},{"name":"wheel-mid6","parent":"wheel-mid-root6"},{"name":"wheel-small-root1","parent":"tank-treads","x":-337.39,"y":109.43},{"name":"wheel-small-root2","parent":"tank-treads","x":0.09,"y":109.43},{"name":"wheel-small-root3","parent":"tank-treads","x":334.69,"y":109.43},{"name":"wheel-small1","parent":"wheel-small-root1","color":"abe323ff"},{"name":"wheel-small2","parent":"wheel-small-root2"},{"name":"wheel-small3","parent":"wheel-small-root3"}],"slots":[{"name":"rock","bone":"rock","attachment":"rock"},{"name":"ground","bone":"root","attachment":"ground"},{"name":"ground2","bone":"root","attachment":"ground"},{"name":"ground3","bone":"root","attachment":"ground"},{"name":"ground4","bone":"root","attachment":"ground"},{"name":"ground5","bone":"root","attachment":"ground"},{"name":"ground6","bone":"root","attachment":"ground"},{"name":"ground7","bone":"root","attachment":"ground"},{"name":"tank-body-shadow","bone":"tank-body","color":"ffffffb9","attachment":"tank-bottom-shadow"},{"name":"bottom","bone":"tank-body","attachment":"tank-bottom"},{"name":"tread-inside1","bone":"tread","attachment":"tread-inside"},{"name":"tread-inside53","bone":"tread27","attachment":"tread-inside"},{"name":"tread-inside27","bone":"tread14","attachment":"tread-inside"},{"name":"tread-inside3","bone":"tread2","attachment":"tread-inside"},{"name":"tread-inside55","bone":"tread28","attachment":"tread-inside"},{"name":"tread-inside29","bone":"tread15","attachment":"tread-inside"},{"name":"tread-inside5","bone":"tread3","attachment":"tread-inside"},{"name":"tread-inside57","bone":"tread29","attachment":"tread-inside"},{"name":"tread-inside31","bone":"tread16","attachment":"tread-inside"},{"name":"tread-inside7","bone":"tread4","attachment":"tread-inside"},{"name":"tread-inside59","bone":"tread30","attachment":"tread-inside"},{"name":"tread-inside33","bone":"tread17","attachment":"tread-inside"},{"name":"tread-inside9","bone":"tread5","attachment":"tread-inside"},{"name":"tread-inside61","bone":"tread31","attachment":"tread-inside"},{"name":"tread-inside35","bone":"tread18","attachment":"tread-inside"},{"name":"tread-inside11","bone":"tread6","attachment":"tread-inside"},{"name":"tread-inside63","bone":"tread32","attachment":"tread-inside"},{"name":"tread-inside37","bone":"tread19","attachment":"tread-inside"},{"name":"tread-inside13","bone":"tread7","attachment":"tread-inside"},{"name":"tread-inside65","bone":"tread33","attachment":"tread-inside"},{"name":"tread-inside39","bone":"tread20","attachment":"tread-inside"},{"name":"tread-inside15","bone":"tread8","attachment":"tread-inside"},{"name":"tread-inside67","bone":"tread34","attachment":"tread-inside"},{"name":"tread-inside69","bone":"tread35","attachment":"tread-inside"},{"name":"tread-inside71","bone":"tread36","attachment":"tread-inside"},{"name":"tread-inside41","bone":"tread21","attachment":"tread-inside"},{"name":"tread-inside17","bone":"tread9","attachment":"tread-inside"},{"name":"tread-inside43","bone":"tread22","attachment":"tread-inside"},{"name":"tread-inside19","bone":"tread10","attachment":"tread-inside"},{"name":"tread-inside45","bone":"tread23","attachment":"tread-inside"},{"name":"tread-inside21","bone":"tread11","attachment":"tread-inside"},{"name":"tread-inside47","bone":"tread24","attachment":"tread-inside"},{"name":"tread-inside23","bone":"tread12","attachment":"tread-inside"},{"name":"tread-inside49","bone":"tread25","attachment":"tread-inside"},{"name":"tread-inside25","bone":"tread13","attachment":"tread-inside"},{"name":"tread-inside51","bone":"tread26","attachment":"tread-inside"},{"name":"tread-inside2","bone":"tread","attachment":"tread-inside"},{"name":"tread-inside54","bone":"tread27","attachment":"tread-inside"},{"name":"tread-inside28","bone":"tread14","attachment":"tread-inside"},{"name":"tread-inside4","bone":"tread2","attachment":"tread-inside"},{"name":"tread-inside56","bone":"tread28","attachment":"tread-inside"},{"name":"tread-inside30","bone":"tread15","attachment":"tread-inside"},{"name":"tread-inside6","bone":"tread3","attachment":"tread-inside"},{"name":"tread-inside58","bone":"tread29","attachment":"tread-inside"},{"name":"tread-inside32","bone":"tread16","attachment":"tread-inside"},{"name":"tread-inside8","bone":"tread4","attachment":"tread-inside"},{"name":"tread-inside60","bone":"tread30","attachment":"tread-inside"},{"name":"tread-inside34","bone":"tread17","attachment":"tread-inside"},{"name":"tread-inside10","bone":"tread5","attachment":"tread-inside"},{"name":"tread-inside62","bone":"tread31","attachment":"tread-inside"},{"name":"tread-inside36","bone":"tread18","attachment":"tread-inside"},{"name":"tread-inside12","bone":"tread6","attachment":"tread-inside"},{"name":"tread-inside64","bone":"tread32","attachment":"tread-inside"},{"name":"tread-inside38","bone":"tread19","attachment":"tread-inside"},{"name":"tread-inside14","bone":"tread7","attachment":"tread-inside"},{"name":"tread-inside66","bone":"tread33","attachment":"tread-inside"},{"name":"tread-inside40","bone":"tread20","attachment":"tread-inside"},{"name":"tread-inside16","bone":"tread8","attachment":"tread-inside"},{"name":"tread-inside68","bone":"tread34","attachment":"tread-inside"},{"name":"tread-inside70","bone":"tread35","attachment":"tread-inside"},{"name":"tread-inside72","bone":"tread36","attachment":"tread-inside"},{"name":"tread-inside42","bone":"tread21","attachment":"tread-inside"},{"name":"tread-inside18","bone":"tread9","attachment":"tread-inside"},{"name":"tread-inside44","bone":"tread22","attachment":"tread-inside"},{"name":"tread-inside20","bone":"tread10","attachment":"tread-inside"},{"name":"tread-inside46","bone":"tread23","attachment":"tread-inside"},{"name":"tread-inside22","bone":"tread11","attachment":"tread-inside"},{"name":"tread-inside48","bone":"tread24","attachment":"tread-inside"},{"name":"tread-inside24","bone":"tread12","attachment":"tread-inside"},{"name":"tread-inside50","bone":"tread25","attachment":"tread-inside"},{"name":"tread-inside26","bone":"tread13","attachment":"tread-inside"},{"name":"tread-inside52","bone":"tread26","attachment":"tread-inside"},{"name":"wheel-big","bone":"wheel-big1","color":"dbdbdbff","attachment":"wheel-big"},{"name":"wheel-big2","bone":"wheel-big2","color":"dbdbdbff","attachment":"wheel-big"},{"name":"wheel-mid","bone":"wheel-mid1","attachment":"wheel-mid"},{"name":"wheel-mid2","bone":"wheel-mid2","attachment":"wheel-mid"},{"name":"wheel-mid3","bone":"wheel-mid3","attachment":"wheel-mid"},{"name":"wheel-mid4","bone":"wheel-mid4","attachment":"wheel-mid"},{"name":"wheel-mid5","bone":"wheel-mid5","attachment":"wheel-mid"},{"name":"wheel-mid6","bone":"wheel-mid6","attachment":"wheel-mid"},{"name":"wheel-small","bone":"wheel-small1","attachment":"wheel-small"},{"name":"wheel-small2","bone":"wheel-small2","attachment":"wheel-small"},{"name":"wheel-small3","bone":"wheel-small3","attachment":"wheel-small"},{"name":"wheel-mid-overlay","bone":"wheel-mid-root1","color":"ffffffec","attachment":"wheel-mid-overlay","blend":"multiply"},{"name":"wheel-mid-overlay2","bone":"wheel-mid-root2","color":"ffffffec","attachment":"wheel-mid-overlay","blend":"multiply"},{"name":"wheel-mid-overlay3","bone":"wheel-mid-root3","color":"ffffffec","attachment":"wheel-mid-overlay","blend":"multiply"},{"name":"wheel-mid-overlay4","bone":"wheel-mid-root4","color":"ffffffec","attachment":"wheel-mid-overlay","blend":"multiply"},{"name":"wheel-mid-overlay5","bone":"wheel-mid-root5","color":"ffffffec","attachment":"wheel-mid-overlay","blend":"multiply"},{"name":"wheel-mid-overlay6","bone":"wheel-mid-root6","color":"ffffffec","attachment":"wheel-mid-overlay","blend":"multiply"},{"name":"wheel-big-overlay1","bone":"wheel-big-root1","color":"ffffffe9","attachment":"wheel-big-overlay","blend":"multiply"},{"name":"wheel-big-overlay2","bone":"wheel-big-root2","color":"ffffffe9","attachment":"wheel-big-overlay","blend":"multiply"},{"name":"treads-path","bone":"tank-root","attachment":"treads-path"},{"name":"tread","bone":"tread","attachment":"tread"},{"name":"tread27","bone":"tread27","color":"adc9b8ff","attachment":"tread"},{"name":"tread14","bone":"tread14","attachment":"tread"},{"name":"tread2","bone":"tread2","attachment":"tread"},{"name":"tread28","bone":"tread28","attachment":"tread"},{"name":"tread15","bone":"tread15","color":"adc9b8ff","attachment":"tread"},{"name":"tread3","bone":"tread3","color":"adc9b8ff","attachment":"tread"},{"name":"tread29","bone":"tread29","color":"adc9b8ff","attachment":"tread"},{"name":"tread16","bone":"tread16","attachment":"tread"},{"name":"tread4","bone":"tread4","attachment":"tread"},{"name":"tread30","bone":"tread30","attachment":"tread"},{"name":"tread17","bone":"tread17","color":"adc9b8ff","attachment":"tread"},{"name":"tread5","bone":"tread5","color":"adc9b8ff","attachment":"tread"},{"name":"tread31","bone":"tread31","color":"adc9b8ff","attachment":"tread"},{"name":"tread18","bone":"tread18","attachment":"tread"},{"name":"tread6","bone":"tread6","attachment":"tread"},{"name":"tread32","bone":"tread32","attachment":"tread"},{"name":"tread19","bone":"tread19","color":"adc9b8ff","attachment":"tread"},{"name":"tread7","bone":"tread7","color":"adc9b8ff","attachment":"tread"},{"name":"tread33","bone":"tread33","color":"adc9b8ff","attachment":"tread"},{"name":"tread20","bone":"tread20","attachment":"tread"},{"name":"tread8","bone":"tread8","attachment":"tread"},{"name":"tread34","bone":"tread34","attachment":"tread"},{"name":"tread35","bone":"tread35","color":"adc9b8ff","attachment":"tread"},{"name":"tread36","bone":"tread36","color":"adc9b8ff","attachment":"tread"},{"name":"tread21","bone":"tread21","color":"adc9b8ff","attachment":"tread"},{"name":"tread9","bone":"tread9","color":"adc9b8ff","attachment":"tread"},{"name":"tread22","bone":"tread22","attachment":"tread"},{"name":"tread10","bone":"tread10","attachment":"tread"},{"name":"tread23","bone":"tread23","color":"adc9b8ff","attachment":"tread"},{"name":"tread11","bone":"tread11","color":"adc9b8ff","attachment":"tread"},{"name":"tread24","bone":"tread24","attachment":"tread"},{"name":"tread12","bone":"tread12","attachment":"tread"},{"name":"tread25","bone":"tread25","color":"adc9b8ff","attachment":"tread"},{"name":"tread13","bone":"tread13","color":"adc9b8ff","attachment":"tread"},{"name":"tread26","bone":"tread26","attachment":"tread"},{"name":"machinegun","bone":"machinegun","attachment":"machinegun"},{"name":"machinegun-mount","bone":"machinegun-mount","attachment":"machinegun-mount"},{"name":"tank-top","bone":"tank-body","attachment":"tank-top"},{"name":"guntower","bone":"guntower","attachment":"guntower"},{"name":"cannon","bone":"cannon","attachment":"cannon"},{"name":"cannon-connector","bone":"cannon-connector","attachment":"cannon-connector"},{"name":"antenna","bone":"antenna-root","attachment":"antenna"},{"name":"smoke-puff1-bg","bone":"smoke1","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg13","bone":"smoke13","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg14","bone":"smoke14","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg15","bone":"smoke15","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg16","bone":"smoke16","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg17","bone":"smoke17","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg18","bone":"smoke18","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg20","bone":"smoke20","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg21","bone":"smoke21","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg22","bone":"smoke22","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg23","bone":"smoke23","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg24","bone":"smoke24","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg25","bone":"smoke25","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg26","bone":"smoke26","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg27","bone":"smoke27","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg2","bone":"smoke2","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg5","bone":"smoke5","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg6","bone":"smoke6","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg7","bone":"smoke7","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg10","bone":"smoke10","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg11","bone":"smoke11","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg12","bone":"smoke12","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg8","bone":"smoke8","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg9","bone":"smoke9","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg4","bone":"smoke4","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-bg3","bone":"smoke3","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg","bone":"smoke1","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg13","bone":"smoke13","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg14","bone":"smoke14","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg15","bone":"smoke15","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg16","bone":"smoke16","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg17","bone":"smoke17","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg18","bone":"smoke18","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg20","bone":"smoke20","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg21","bone":"smoke21","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg22","bone":"smoke22","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg23","bone":"smoke23","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg24","bone":"smoke24","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg25","bone":"smoke25","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg26","bone":"smoke26","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg27","bone":"smoke27","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg2","bone":"smoke2","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg5","bone":"smoke5","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg6","bone":"smoke6","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg7","bone":"smoke7","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg10","bone":"smoke10","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg11","bone":"smoke11","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg12","bone":"smoke12","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg8","bone":"smoke8","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg9","bone":"smoke9","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg4","bone":"smoke4","color":"ecececff","dark":"000000"},{"name":"smoke-puff1-fg3","bone":"smoke3","color":"ecececff","dark":"000000"},{"name":"smoke-glow","bone":"smoke-glow","blend":"additive"},{"name":"clipping","bone":"tank-body","attachment":"clipping"},{"name":"tank-glow","bone":"tank-glow","color":"fcdc6da7","blend":"additive"}],"ik":[{"name":"cannon-ik","bones":["cannon"],"target":"cannon-target"},{"name":"machinegun-ik","order":1,"bones":["machinegun"],"target":"machinegun-target","mix":0}],"transform":[{"name":"wheel-big-transform","order":8,"bones":["wheel-big2"],"target":"wheel-big1","rotation":65.6,"mixX":0,"mixScaleX":0,"mixShearY":0},{"name":"wheel-mid1-transform","order":3,"bones":["wheel-mid2","wheel-mid4"],"target":"wheel-mid1","rotation":93,"mixX":0,"mixScaleX":0,"mixShearY":0},{"name":"wheel-mid2-transform","order":4,"bones":["wheel-mid3","wheel-mid5"],"target":"wheel-mid1","rotation":-89,"mixX":0,"mixScaleX":0,"mixShearY":0},{"name":"wheel-mid3-transform","order":5,"bones":["wheel-mid6"],"target":"wheel-mid1","rotation":-152.6,"mixX":0,"mixScaleX":0,"mixShearY":0},{"name":"wheel-small1-transform","order":6,"bones":["wheel-small2"],"target":"wheel-small1","rotation":87,"mixX":0,"mixScaleX":0,"mixShearY":0},{"name":"wheel-small2-transform","order":7,"bones":["wheel-small3"],"target":"wheel-small1","rotation":54.9,"mixX":0,"mixScaleX":0,"mixShearY":0}],"path":[{"name":"treads-path","order":2,"bones":["tread","tread2","tread3","tread4","tread5","tread6","tread7","tread8","tread9","tread10","tread11","tread12","tread13","tread14","tread15","tread16","tread17","tread18","tread19","tread20","tread21","tread22","tread23","tread24","tread25","tread26","tread27","tread28","tread29","tread30","tread31","tread32","tread33","tread34","tread35","tread36"],"target":"treads-path","rotateMode":"chain"}],"skins":[{"name":"default","attachments":{"antenna":{"antenna":{"type":"mesh","uvs":[0.64286,0.07876,0.65354,0.1535,0.66325,0.22138,0.67367,0.29433,0.68383,0.36543,0.6936,0.43374,0.70311,0.5003,0.71311,0.57031,0.72327,0.64139,0.73406,0.71689,0.74441,0.7893,0.75614,0.87141,0.76905,0.94311,1,0.94311,1,1,0,1,0,0.94311,0.20106,0.94311,0.20106,0.87094,0.21461,0.78847,0.22651,0.71607,0.23886,0.64099,0.25036,0.57105,0.26206,0.49983,0.27306,0.43291,0.2843,0.36454,0.29593,0.29382,0.308,0.22038,0.319,0.15345,0.33142,0.07796,0.34423,0,0.63161,0],"triangles":[30,31,0,29,30,0,29,0,1,28,29,1,28,1,2,27,28,2,27,2,3,26,3,4,25,26,4,25,4,5,26,27,3,24,5,6,23,24,6,7,23,6,24,25,5,22,7,8,21,22,8,21,8,9,7,22,23,20,9,10,19,20,10,20,21,9,19,10,11,18,19,11,17,18,11,17,11,12,15,16,17,12,13,14,15,17,12,14,15,12],"vertices":[2,10,65.38,-3.14,0.3125,11,23.38,-3.14,0.6875,2,10,42.73,-3.38,0.66667,11,0.73,-3.38,0.33333,2,9,64.17,-3.59,0.33333,10,22.17,-3.59,0.66667,2,9,42.06,-3.82,0.66667,10,0.06,-3.82,0.33333,2,8,62.52,-4.04,0.33333,9,20.52,-4.04,0.66667,2,8,41.82,-4.26,0.66667,9,-0.18,-4.26,0.33333,2,7,63.65,-4.47,0.33333,8,21.65,-4.47,0.66667,2,7,42.44,-4.69,0.66667,8,0.44,-4.69,0.33333,2,6,62.9,-4.91,0.33333,7,20.9,-4.91,0.66667,2,6,40.03,-5.15,0.66667,7,-1.97,-5.15,0.33333,2,5,5.38,58.09,0.4,6,18.09,-5.38,0.6,1,5,5.64,33.21,1,1,5,5.92,11.48,1,1,5,11,11.48,1,1,5,11,-5.76,1,1,5,-11,-5.76,1,1,5,-11,11.48,1,1,5,-6.58,11.48,1,1,5,-6.58,33.35,1,2,5,-6.28,58.34,0.4,6,18.34,6.28,0.6,2,6,40.27,6.02,0.66667,7,-1.73,6.02,0.33333,2,6,63.03,5.75,0.33333,7,21.03,5.75,0.66667,2,7,42.22,5.49,0.66667,8,0.22,5.49,0.33333,2,7,63.8,5.23,0.33333,8,21.8,5.23,0.66667,2,8,42.07,4.99,0.66667,9,0.07,4.99,0.33333,2,8,62.79,4.75,0.33333,9,20.79,4.75,0.66667,2,9,42.22,4.49,0.66667,10,0.22,4.49,0.33333,2,9,64.47,4.22,0.33333,10,22.47,4.22,0.66667,2,10,42.75,3.98,0.66667,11,0.75,3.98,0.33333,2,10,65.62,3.71,0.3125,11,23.62,3.71,0.6875,1,11,47.24,3.43,1,1,11,47.24,-2.9,1],"hull":32,"edges":[28,30,28,26,30,32,26,24,24,22,32,34,34,24,34,36,36,22,60,62,38,36,20,22,38,20,40,38,18,20,40,18,42,40,16,18,42,16,44,42,14,16,44,14,46,44,12,14,46,12,48,46,10,12,48,10,50,48,8,10,50,8,52,50,6,8,52,6,54,52,4,6,54,4,56,54,2,4,56,2,60,58,58,56,62,0,0,2,58,0],"width":22,"height":303}},"bottom":{"tank-bottom":{"x":-16.67,"y":9.89,"width":1285,"height":276}},"cannon":{"cannon":{"x":481.95,"y":-0.03,"rotation":180,"width":931,"height":58}},"cannon-connector":{"cannon-connector":{"type":"mesh","uvs":[1,0.03237,1,0.10603,0.90988,0.32859,0.81975,0.55116,0.72963,0.77373,0.6395,0.9963,0.42157,0.9963,0.20364,0.9963,0,0.85434,0,0.69902,0.02268,0.52884,0,0.31444,0.21602,0.12998,0.43368,0,0.63547,0.0037,0.48408,0.77059,0.31496,0.52497,0.64133,0.19648,0.21516,0.76766,0.58346,0.56471,0.68444,0.40146,0.46758,0.36649,0.28935,0.34604],"triangles":[7,18,6,6,18,15,7,8,18,8,9,18,18,16,15,15,16,19,9,10,18,18,10,16,16,21,19,19,21,20,10,22,16,10,11,22,16,22,21,21,17,20,21,12,13,17,13,14,17,21,13,11,12,22,21,22,12,6,15,5,5,15,4,15,19,4,4,19,3,19,20,3,3,20,2,20,17,2,2,17,1,17,14,1,14,0,1],"vertices":[1,12,35.91,69.08,1,1,12,35.91,59.14,1,1,12,25.82,29.09,1,1,12,15.72,-0.95,1,1,12,5.63,-31,1,1,12,-4.46,-61.05,1,2,12,-28.87,-61.05,0.33333,14,28.87,61.03,0.66667,1,14,53.28,61.02,1,1,14,76.09,41.84,1,1,14,71.17,21.63,1,1,14,72.83,-1.62,1,1,14,70.38,-29.12,1,1,14,50.67,-56.14,1,2,12,-28.43,74.38,0.41,14,28.43,-74.4,0.59,2,12,-4.92,72.95,0.52,14,4.92,-72.95,0.48,2,12,-21.87,-30.58,0.49,14,21.87,30.57,0.51,1,14,40.81,-2.6,1,2,12,-4.26,46.93,0.49,14,4.26,-46.93,0.51,1,14,51.99,30.15,1,2,12,-10.74,-2.78,0.49,14,10.74,2.78,0.51,2,12,0.57,19.25,0.49,14,-0.57,-19.25,0.51,1,14,23.72,-23.99,1,1,14,43.68,-26.76,1],"hull":15,"edges":[0,2,2,4,4,6,6,8,8,10,10,12,12,14,14,16,16,18,18,20,20,22,22,24,24,26,26,28,28,0],"width":112,"height":135}},"clipping":{"clipping":{"type":"clipping","end":"tank-glow","vertexCount":32,"vertices":[1,3,165.84,455.67,1,1,3,114.21,493.01,1,1,3,-38.53,492.23,1,1,3,-193.4,464.18,1,2,3,-280.85,415.48,0.752,14,24.09,-73.93,0.248,1,14,70.34,-27.32,1,1,14,412.56,-22.02,1,1,14,412.82,-29.21,1,1,14,539.26,-29.34,1,1,14,539.52,-17.09,1,1,14,894.02,-16.8,1,1,14,902.99,-28.89,1,1,14,942.06,-28.58,1,1,14,948.14,-16.64,1,1,14,947.9,14.29,1,1,14,539.3,14.55,1,1,14,539,29.22,1,1,14,412.51,29.88,1,1,14,412.51,21.73,1,1,14,74.24,27.28,1,1,3,-296.64,281.2,1,1,3,-316.06,225.71,1,1,3,-521.69,190.74,1,1,3,-610.03,141.02,1,1,3,-671.84,87.13,1,1,3,-652.23,-11.24,1,1,3,-618.53,-71.36,1,1,3,-478.77,-114.21,1,1,3,-274.11,-116.26,1,1,3,1.38,-45.75,1,1,3,189.67,148.78,1,1,3,215.75,276.59,1],"color":"ce3a3aff"}},"ground":{"ground":{"x":837.96,"y":-172,"width":1024,"height":353}},"ground2":{"ground":{"x":-179.89,"y":-172,"width":1024,"height":353}},"ground3":{"ground":{"x":-1213.48,"y":-172,"scaleX":1.035,"width":1024,"height":353}},"ground4":{"ground":{"x":-2268.51,"y":-172,"scaleX":1.04,"width":1024,"height":353}},"ground5":{"ground":{"x":-3306.54,"y":-172,"width":1024,"height":353}},"ground6":{"ground":{"x":-4322.71,"y":-172,"width":1024,"height":353}},"ground7":{"ground":{"x":-5340.65,"y":-172,"width":1024,"height":353}},"guntower":{"guntower":{"x":77.22,"y":122.59,"width":730,"height":289}},"machinegun":{"machinegun":{"x":44.85,"y":-5.72,"rotation":-180,"width":331,"height":57}},"machinegun-mount":{"machinegun-mount":{"x":47.42,"y":-1.54,"rotation":-90,"width":72,"height":96}},"rock":{"rock":{"x":25.24,"y":27.35,"width":580,"height":127}},"smoke-glow":{"smoke-glow":{"type":"mesh","uvs":[1,0.24906,1,0.51991,1,0.73165,0.70776,1,0.49012,1,0.24373,1,0,0.71158,0,0.50308,0,0.26235,0.28107,0,0.47435,0,0.73345,0,0.48858,0.51759],"triangles":[12,7,8,12,10,11,12,11,0,9,10,12,12,8,9,12,0,1,6,7,12,12,1,2,5,6,12,3,4,12,5,12,4,2,3,12],"vertices":[49.99,25.1,50,-1.98,50.01,-23.15,20.79,-50,-0.98,-50,-25.62,-50.01,-50,-21.17,-50,-0.32,-50.01,23.75,-21.9,50,-2.58,50,23.33,50.01,-1.14,-1.76],"hull":12,"edges":[2,24,24,14,20,24,24,8,2,0,20,22,0,22,18,20,14,16,18,16,12,14,8,10,12,10,6,8,2,4,6,4],"width":100,"height":100}},"smoke-puff1-bg":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg2":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg3":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg4":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg5":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg6":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg7":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg8":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg9":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg10":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg11":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg12":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg13":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg14":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg15":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg16":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg17":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg18":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg20":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg21":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg22":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg23":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg24":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg25":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg26":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-bg27":{"smoke-puff01-bg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123}},"smoke-puff1-fg":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg2":{"smoke-puff01-fg":{"x":-1.01,"y":-0.07,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.82,"y":-0.39,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.82,"y":-0.39,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.82,"y":-0.39,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg3":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.02,"y":-0.25,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1145,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.03,"y":-0.43,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg4":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.63,"y":-0.09,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.63,"y":-0.09,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.63,"y":-0.09,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg5":{"smoke-puff01-fg":{"x":-1.21,"y":-0.08,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.89,"y":-0.04,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.89,"y":-0.04,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.89,"y":-0.04,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg6":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.48,"y":-0.07,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.48,"y":-0.07,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.48,"y":-0.07,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg7":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-0.7,"y":-0.36,"scaleX":0.1216,"scaleY":0.1214,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-0.56,"y":-0.15,"scaleX":0.1224,"scaleY":0.1224,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-0.56,"y":-0.15,"scaleX":0.1224,"scaleY":0.1224,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg8":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-0.65,"y":0.01,"scaleX":0.1226,"scaleY":0.1226,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-0.75,"y":-0.15,"scaleX":0.1211,"scaleY":0.1211,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-0.75,"y":-0.15,"scaleX":0.1211,"scaleY":0.1211,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg9":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.99,"y":-0.09,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.99,"y":-0.09,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-0.95,"y":-0.48,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg10":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.27,"y":-0.37,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.27,"y":-0.37,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.27,"y":-0.37,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg11":{"smoke-puff04-fg":{"x":-1.27,"y":-0.37,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg12":{"smoke-puff04-fg":{"x":-1.27,"y":-0.37,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg13":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg14":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg15":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg16":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg17":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg18":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg20":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg21":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg22":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg23":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg24":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg25":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg26":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"smoke-puff1-fg27":{"smoke-puff01-fg":{"x":-0.5,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":175,"height":118},"smoke-puff02-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff03-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":184,"height":123},"smoke-puff04-fg":{"x":-1.38,"y":-0.12,"scaleX":0.1106,"scaleY":0.1106,"rotation":88.58,"width":155,"height":96}},"tank-body-shadow":{"tank-bottom-shadow":{"x":-11.44,"y":-42.89,"width":1291,"height":341}},"tank-glow":{"smoke-glow":{"type":"mesh","uvs":[1,1,0,1,1,0],"triangles":[1,2,0],"vertices":[469.64,-738.08,-1660.32,-738.08,469.64,1391.88],"hull":3,"edges":[0,2,0,4,2,4],"width":100,"height":100}},"tank-top":{"tank-top":{"x":6.8,"y":168.71,"width":1407,"height":222}},"tread":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread-inside1":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside2":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside3":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside4":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside5":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside6":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside7":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside8":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside9":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside10":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside11":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside12":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside13":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside14":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside15":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside16":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside17":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside18":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside19":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside20":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside21":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside22":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside23":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside24":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside25":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside26":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside27":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside28":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside29":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside30":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside31":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside32":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside33":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside34":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside35":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside36":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside37":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside38":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside39":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside40":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside41":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside42":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside43":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside44":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside45":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside46":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside47":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside48":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside49":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside50":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside51":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside52":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside53":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside54":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside55":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside56":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside57":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside58":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside59":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside60":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside61":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside62":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside63":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside64":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside65":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside66":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside67":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside68":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside69":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside70":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside71":{"tread-inside":{"x":60.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread-inside72":{"tread-inside":{"x":20.1,"y":12.56,"rotation":-180,"width":25,"height":28}},"tread2":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread3":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread4":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread5":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread6":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread7":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread8":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread9":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread10":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread11":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread12":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread13":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread14":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread15":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread16":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread17":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread18":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread19":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread20":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread21":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread22":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread23":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread24":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread25":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread26":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread27":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread28":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread29":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread30":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread31":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread32":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread33":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread34":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread35":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"tread36":{"tread":{"x":45.47,"y":-8.28,"rotation":-180,"width":96,"height":30}},"treads-path":{"treads-path":{"type":"path","closed":true,"lengths":[185.21,354.53,478.3,608.52,786,1058.49,1138.97,1223.96,1303.87,1388.23,1471.11,1551.64,1633.55,1713.27,1799.89,1882.28,2164.2,2326.85,2444.07,2584.91,2754.15,2940.62],"vertexCount":66,"vertices":[1,110,11.23,41.87,1,1,110,0.79,41.95,1,1,110,-34.72,42.24,1,1,56,-104.22,0.41,1,1,56,0.07,0.55,1,1,56,68.8,0.65,1,1,109,20.5,43.47,1,1,109,1.14,40.82,1,1,109,-27.38,36.85,1,1,93,147.07,105.01,1,1,93,96.21,96.63,1,1,93,43.87,87.72,1,1,93,16.18,103.35,1,1,93,-33.67,94.21,1,1,93,-99.36,81.25,1,1,93,-122.05,-1.7,1,1,93,-83.58,-47.93,1,1,93,-33.53,-109.37,1,1,97,-83.57,-66.1,1,1,97,-2.17,-67.9,1,2,97,56.68,-41.49,0.68,51,-24.31,-41.49,0.32,1,51,-26.59,16.7,1,1,51,-2.69,16.7,1,1,51,13.52,16.7,1,2,98,-52.42,-46.51,0.744,51,30.21,-46.52,0.256,1,98,-0.32,-68.92,1,2,98,52.09,-44.73,0.712,52,-28.91,-44.73,0.288,1,52,-22.81,16.24,1,1,52,-1.42,16.24,1,1,52,20.48,16.24,1,2,99,-47.21,-47.46,0.744,52,36.01,-47.46,0.256,1,99,-0.29,-69.66,1,2,99,45.24,-47.26,0.736,53,-37.49,-47.26,0.264,1,53,-23.76,15.28,1,1,53,-0.14,15.28,1,1,53,24.45,15.28,1,2,100,-47.37,-48.7,0.744,53,33.53,-48.7,0.256,1,100,-0.5,-70.4,1,2,100,49.09,-48.34,0.744,54,-33.58,-48.34,0.256,1,54,-20.89,15.84,1,1,54,-1.26,15.84,1,1,54,15.78,15.84,1,2,101,-52.5,-48.21,0.76,54,28.45,-48.22,0.24,1,101,-2.5,-68.92,1,2,101,55.72,-47.82,0.752,55,-28.88,-47.83,0.248,1,55,-21.64,16.7,1,1,55,-0.48,16.7,1,1,55,20.74,16.7,1,2,102,-53.65,-48.9,0.76,55,25.97,-48.9,0.24,1,102,2.28,-69.66,1,1,102,44.95,-69.74,1,1,94,76.03,-85.61,1,1,94,93.58,-42.24,1,1,94,118.67,19.75,1,1,94,78.59,76.62,1,1,94,37.27,95.07,1,1,94,31.45,97.67,1,1,94,-15.16,87.48,1,1,94,-79.8,92.52,1,1,94,-119.06,95.58,1,1,111,47.07,42.29,1,1,111,0.25,42.75,1,1,111,-29.64,43.29,1,1,57,-86.65,1.35,1,1,57,0.49,0.26,1,1,57,92.42,-0.9,1],"color":"ff8819ff"}},"wheel-big":{"wheel-big":{"width":191,"height":191}},"wheel-big-overlay1":{"wheel-big-overlay":{"width":186,"height":186}},"wheel-big-overlay2":{"wheel-big-overlay":{"width":186,"height":186}},"wheel-big2":{"wheel-big":{"width":191,"height":191}},"wheel-mid":{"wheel-mid":{"width":136,"height":136}},"wheel-mid-overlay":{"wheel-mid-overlay":{"width":136,"height":136}},"wheel-mid-overlay2":{"wheel-mid-overlay":{"width":136,"height":136}},"wheel-mid-overlay3":{"wheel-mid-overlay":{"width":136,"height":136}},"wheel-mid-overlay4":{"wheel-mid-overlay":{"width":136,"height":136}},"wheel-mid-overlay5":{"wheel-mid-overlay":{"width":136,"height":136}},"wheel-mid-overlay6":{"wheel-mid-overlay":{"width":136,"height":136}},"wheel-mid2":{"wheel-mid":{"width":136,"height":136}},"wheel-mid3":{"wheel-mid":{"width":136,"height":136}},"wheel-mid4":{"wheel-mid":{"width":136,"height":136}},"wheel-mid5":{"wheel-mid":{"width":136,"height":136}},"wheel-mid6":{"wheel-mid":{"width":136,"height":136}},"wheel-small":{"wheel-small":{"width":71,"height":71}},"wheel-small2":{"wheel-small":{"width":71,"height":71}},"wheel-small3":{"wheel-small":{"width":71,"height":71}}}}],"animations":{"drive":{"bones":{"tank-root":{"rotate":[{"time":2},{"time":2.0667,"value":1.99},{"time":2.5,"value":-15.63},{"time":2.6667,"value":-10.37,"curve":[2.718,-10.37,2.78,-8.34]},{"time":2.8333,"value":-6.13,"curve":[2.909,-2.8,2.974,0.8]},{"time":3,"value":1.84},{"time":3.0667,"value":5.32},{"time":3.1667,"value":10.99},{"time":3.2333,"value":9.73},{"time":3.4333,"value":-4.52,"curve":[3.474,-3.99,3.608,0.01]},{"time":3.6667,"value":0.01}],"translate":[{"curve":[1.019,0,1.608,-582.83,1.019,0,1.608,0]},{"time":2,"x":-1209.75},{"time":2.3333,"x":-1652.84,"y":26.05},{"time":2.5,"x":-1877.69,"y":71.5},{"time":2.6667,"x":-2053.37,"y":100.44},{"time":2.8333,"x":-2183.86,"y":97.42},{"time":3,"x":-2312.32,"y":74.12},{"time":3.0667,"x":-2340.68,"y":45.94},{"time":3.1333,"x":-2403.04,"y":17.04},{"time":3.1667,"x":-2439.84,"y":5.45},{"time":3.2333,"x":-2523.34,"y":-3.31},{"time":3.4333,"x":-2728.27,"y":-12.73},{"time":3.5,"x":-2795.65,"y":-6.14,"curve":[3.538,-2829.89,3.583,-2878.59,3.538,-4.93,3.583,-3.21]},{"time":3.6333,"x":-2938.53,"y":-1.09,"curve":[3.89,-3218.84,4.404,-3972.02,3.89,-0.79,4.404,0]},{"time":4.8333,"x":-3972.02},{"time":5,"x":-3991.31},{"time":5.3667,"x":-3973.94}]},"tread-collider1":{"translate":[{"time":2},{"time":2.0667,"y":9.99},{"time":2.1667,"y":37.69},{"time":2.3333,"y":53.45},{"time":2.5,"y":30.97},{"time":2.6667,"y":-2.89},{"time":2.8333,"y":-0.71},{"time":3.0667,"y":-13.64},{"time":3.1667,"y":59.3},{"time":3.2333,"y":48.2},{"time":3.4333,"y":-11.27},{"time":3.6333,"y":4.15}]},"tread-collider2":{"translate":[{"time":2},{"time":2.0667,"y":-2.83},{"time":2.1667,"y":-17.44},{"time":2.3333,"y":46.07},{"time":2.5,"y":19.45},{"time":2.6667,"y":13.46},{"time":2.8333,"y":-1.92,"curve":"stepped"},{"time":2.9667,"y":-1.92},{"time":3,"y":-13.17},{"time":3.0667,"y":-23.25},{"time":3.1667,"y":28.13},{"time":3.2333,"y":25.63},{"time":3.4333,"y":-1.52},{"time":3.6333,"y":1.15}]},"tread-collider3":{"translate":[{"time":2},{"time":2.0667,"y":-7.76},{"time":2.1667,"y":-16.61},{"time":2.5,"y":29.05},{"time":2.6667,"y":30.12},{"time":2.8333,"y":5.3},{"time":3,"y":-0.38},{"time":3.1667,"y":2.6},{"time":3.4333,"y":15.41},{"time":3.6333,"y":1.44}]},"tread-collider4":{"translate":[{"time":2},{"time":2.1667,"y":-6.72},{"time":2.3333,"y":-0.92},{"time":2.5,"y":18.37},{"time":2.6667,"y":38.77},{"time":2.8333,"y":30.6},{"time":3.1667,"y":12.61},{"time":3.2333,"y":-16},{"time":3.4333,"y":25.62},{"time":3.6333,"y":-0.68}]},"tread-collider5":{"translate":[{"time":2},{"time":2.1667,"y":3.35},{"time":2.3333,"y":22.17},{"time":2.6667,"y":13.35},{"time":2.8333,"y":39},{"time":3,"y":39.88},{"time":3.1667,"y":26.57},{"time":3.2333,"y":-10.15},{"time":3.4333,"y":35.98},{"time":3.6333,"y":-1.36}]},"wheel-mid-root6":{"translate":[{"time":2},{"time":2.1667,"y":5.61},{"time":2.3333,"y":27.21},{"time":2.5,"y":30.28},{"time":2.6667,"y":-2.81},{"time":2.8333,"y":19.59},{"time":3,"y":29.11},{"time":3.1667,"y":32.55},{"time":3.2333,"y":3.55},{"time":3.4333,"y":40.54},{"time":3.6333}]},"wheel-mid-root5":{"translate":[{"time":2},{"time":2.1667,"y":-7.46},{"time":2.3333,"y":9.53},{"time":2.6667,"y":36.78},{"time":2.8333,"y":46.11},{"time":3.1667,"y":7.55},{"time":3.2333,"y":-16.28},{"time":3.4333,"y":26.21},{"time":3.6333}]},"wheel-mid-root4":{"translate":[{"time":2},{"time":2.1667,"y":-13.98},{"time":2.3333,"y":-8.26},{"time":2.5,"y":24.27},{"time":2.6667,"y":34.42},{"time":2.8333,"y":8.88},{"time":3.1667,"y":10.32},{"time":3.2333,"y":-7.63},{"time":3.4333,"y":19.69},{"time":3.6333}]},"wheel-mid-root3":{"translate":[{"time":2},{"time":2.1667,"y":-21.14},{"time":2.3333,"y":22.83},{"time":2.5,"y":23.34},{"time":2.6667,"y":18.07},{"time":2.8333,"y":1.2},{"time":3.0667,"y":-13.36},{"time":3.1667,"y":15.48},{"time":3.2333,"y":13.34},{"time":3.4333,"y":6.4},{"time":3.6333}]},"wheel-mid-root2":{"translate":[{"time":2},{"time":2.0667,"y":-4.39},{"time":2.1667,"y":3.13},{"time":2.3333,"y":53.56},{"time":2.5,"y":16.65},{"time":2.6667,"y":8.39},{"time":3.0667,"y":-19.16},{"time":3.1667,"y":43.25},{"time":3.2333,"y":39.04},{"time":3.4333,"y":-8.61},{"time":3.6333}]},"wheel-mid-root1":{"translate":[{"time":2},{"time":2.0333,"y":22.64},{"time":2.0667,"y":53.65},{"time":2.1667,"y":71.18},{"time":2.5,"y":46.83},{"time":2.6667,"y":8.38},{"time":3.0667,"y":-10.03},{"time":3.1667,"y":72.71},{"time":3.2333,"y":64.74},{"time":3.4333,"y":-17.65},{"time":3.6333}]},"tank-body":{"rotate":[{"curve":[0.208,0,0.625,-4.39]},{"time":0.8333,"value":-4.39},{"time":2},{"time":2.1667,"value":-1.34},{"time":2.3333,"value":-6.23},{"time":2.5,"value":-5.45},{"time":2.9667,"value":-5.07},{"time":3.0667,"value":-2.39},{"time":3.1667,"value":-0.98},{"time":3.2333,"value":-1.1},{"time":3.4,"value":0.43,"curve":[3.433,0.43,3.483,-1.56]},{"time":3.5333,"value":-3.55,"curve":[3.675,-3.47,3.754,1.49]},{"time":3.8333,"value":1.93},{"time":4,"value":0.48},{"time":4.3333,"curve":[4.476,0.59,4.833,3.8]},{"time":5,"value":3.8,"curve":[5.286,3.8,5.35,-2.17]},{"time":5.4667,"value":-2.17},{"time":5.6,"value":-0.61}]},"wheel-big-root1":{"translate":[{"time":2},{"time":2.0667,"y":20.07},{"time":2.3333,"y":67.24},{"time":2.6667,"y":21.04},{"time":3,"y":10.28},{"time":3.1,"y":11.28},{"time":3.1667,"y":29.43},{"time":3.2333,"y":35.31},{"time":3.4333,"y":18.38},{"time":3.5}]},"tank-treads":{"rotate":[{},{"time":0.8333,"value":-2.4},{"time":2},{"time":2.0667,"value":1.72},{"time":2.4333,"value":-0.37},{"time":2.8},{"time":3,"value":-1.41},{"time":3.1667,"value":0.54},{"time":3.2667,"value":2.22,"curve":[3.348,2.22,3.392,-1.31]},{"time":3.4333,"value":-1.31},{"time":3.7333,"value":-1.14},{"time":4.3333,"curve":[4.476,0.35,4.833,2.24]},{"time":5,"value":2.24,"curve":[5.286,2.24,5.35,0]},{"time":5.4667}]},"cannon-target":{"translate":[{},{"time":0.8333,"y":121.95},{"time":2,"y":45.73}]},"wheel-big-root2":{"translate":[{"time":3.4333,"y":13.01}]},"wheel-big1":{"rotate":[{"curve":[0.51,0,0.804,57.81]},{"time":1,"value":120},{"time":1.2667,"value":240},{"time":1.5333,"value":360},{"time":1.7667,"value":480},{"time":2.0333,"value":600},{"time":2.2,"value":720},{"time":2.4,"value":840},{"time":2.5667,"value":960},{"time":2.7333,"value":1080},{"time":2.9333,"value":1200},{"time":3.1333,"value":1320},{"time":3.3333,"value":1440},{"time":3.5,"value":1560},{"time":3.6667,"value":1680},{"time":3.8667,"value":1800},{"time":4.0667,"value":1920},{"time":4.2667,"value":2040},{"time":4.5,"value":2160,"curve":[4.563,2194.34,4.695,2225.3]},{"time":4.8333,"value":2247.67}]},"wheel-mid1":{"rotate":[{"curve":[0.459,0,0.724,57.81]},{"time":0.9,"value":120},{"time":1.1667,"value":240},{"time":1.4333,"value":360},{"time":1.6333,"value":480},{"time":1.8333,"value":600},{"time":2,"value":720},{"time":2.1333,"value":840},{"time":2.2667,"value":960},{"time":2.4,"value":1080},{"time":2.5333,"value":1200},{"time":2.6667,"value":1320},{"time":2.8333,"value":1440},{"time":2.9667,"value":1560},{"time":3.1,"value":1680},{"time":3.2333,"value":1800},{"time":3.3667,"value":1920},{"time":3.5,"value":2040},{"time":3.6333,"value":2160},{"time":3.7667,"value":2280},{"time":3.9,"value":2400},{"time":4.0333,"value":2520},{"time":4.1667,"value":2640},{"time":4.3,"value":2760},{"time":4.4667,"value":2880,"curve":[4.538,2949.2,4.742,3000]},{"time":4.8333,"value":3000}]},"wheel-small1":{"rotate":[{"curve":[0.34,0,0.536,57.81]},{"time":0.6667,"value":120},{"time":0.8667,"value":240},{"time":1.0333,"value":360},{"time":1.1667,"value":480},{"time":1.3,"value":600},{"time":1.4333,"value":720},{"time":1.5333,"value":840},{"time":1.6333,"value":960},{"time":1.7333,"value":1080},{"time":1.8333,"value":1200},{"time":1.9333,"value":1320},{"time":2.0333,"value":1440},{"time":2.1333,"value":1560},{"time":2.2333,"value":1680},{"time":2.3333,"value":1800},{"time":2.4333,"value":1920},{"time":2.5333,"value":2040},{"time":2.6333,"value":2160},{"time":2.7333,"value":2280},{"time":2.8333,"value":2400},{"time":2.9333,"value":2520},{"time":3.0333,"value":2640},{"time":3.1333,"value":2760},{"time":3.2333,"value":2880},{"time":3.3333,"value":3000},{"time":3.4333,"value":3120},{"time":3.5333,"value":3240},{"time":3.6333,"value":3360},{"time":3.7333,"value":3480},{"time":3.8333,"value":3600},{"time":3.9333,"value":3720},{"time":4.0333,"value":3840},{"time":4.1333,"value":3960},{"time":4.2333,"value":4080},{"time":4.3333,"value":4200},{"time":4.4333,"value":4320},{"time":4.6667,"value":4440},{"time":4.9,"value":4490}]},"wheel-small-root1":{"translate":[{"time":2},{"time":2.1333,"y":12.37},{"time":2.4667,"y":32.37},{"time":2.7333,"y":-5.27},{"time":2.9667,"y":14.31},{"time":3.1667,"y":19.54},{"time":3.4667,"y":7.5},{"time":4.3667,"y":-2.4}]},"wheel-small-root2":{"translate":[{"time":2},{"time":2.9,"y":5.26},{"time":3.1667,"y":10.67},{"time":3.4667,"y":-4.71}]},"wheel-small-root3":{"translate":[{"time":2},{"time":2.4667,"y":-10.56},{"time":2.9,"y":-16.08},{"time":3.1667,"y":10.12},{"time":3.4667,"y":4.1},{"time":4.3667,"y":-0.03}]},"antenna1":{"rotate":[{},{"time":0.3333,"value":1.05,"curve":[0.731,1.05,1.008,-4.55]},{"time":1.2333,"value":-4.55,"curve":[1.35,-4.55,1.583,0.53]},{"time":1.7,"value":0.53},{"time":2,"value":-2.13},{"time":2.2,"value":4.71},{"time":2.3667,"value":-7.26},{"time":2.5667,"value":2.26},{"time":2.7333,"value":-3.39},{"time":3.0667,"value":-5.53},{"time":3.2333,"value":7.78},{"time":3.4667,"value":-5.99},{"time":3.7,"value":3.11},{"time":3.9,"value":-3.05},{"time":4.1,"value":0.31},{"time":4.3,"value":-3.06},{"time":4.5333,"value":0.36},{"time":4.8667,"value":4.94,"curve":[4.925,4.94,5.042,-2.38]},{"time":5.1,"value":-2.38},{"time":5.2667,"value":3.65},{"time":5.4,"value":-3.04},{"time":5.5,"value":1.49},{"time":5.6,"value":-1.86},{"time":5.7,"value":0.42}]},"antenna2":{"rotate":[{},{"time":0.3333,"value":1.05,"curve":[0.731,1.05,1.008,-4.55]},{"time":1.2333,"value":-4.55,"curve":[1.35,-4.55,1.583,0.53]},{"time":1.7,"value":0.53},{"time":2,"value":-2.13},{"time":2.2,"value":4.71},{"time":2.3667,"value":-7.26},{"time":2.5667,"value":2.26},{"time":2.7333,"value":-3.39},{"time":3.0667,"value":-5.53},{"time":3.2333,"value":7.78},{"time":3.4667,"value":-5.99},{"time":3.7,"value":3.11},{"time":3.9,"value":-3.05},{"time":4.1,"value":0.31},{"time":4.3,"value":-3.06},{"time":4.5333,"value":0.36},{"time":4.8667,"value":4.94,"curve":[4.925,4.94,5.042,-2.38]},{"time":5.1,"value":-2.38},{"time":5.2667,"value":3.65},{"time":5.4,"value":-3.04},{"time":5.5,"value":1.49},{"time":5.6,"value":-1.86},{"time":5.7,"value":0.42}]},"antenna3":{"rotate":[{},{"time":0.3333,"value":1.05,"curve":[0.731,1.05,1.008,-4.55]},{"time":1.2333,"value":-4.55,"curve":[1.35,-4.55,1.583,0.53]},{"time":1.7,"value":0.53},{"time":2,"value":-2.13},{"time":2.2,"value":4.71},{"time":2.3667,"value":-7.26},{"time":2.5667,"value":2.26},{"time":2.7333,"value":-3.39},{"time":3.0667,"value":-5.53},{"time":3.2333,"value":7.78},{"time":3.4667,"value":-5.99},{"time":3.7,"value":3.11},{"time":3.9,"value":-3.05},{"time":4.1,"value":0.31},{"time":4.3,"value":-3.06},{"time":4.5333,"value":0.36},{"time":4.8667,"value":4.94,"curve":[4.925,4.94,5.042,-2.38]},{"time":5.1,"value":-2.38},{"time":5.2667,"value":3.65},{"time":5.4,"value":-3.04},{"time":5.5,"value":1.49},{"time":5.6,"value":-1.86},{"time":5.7,"value":0.42}]},"antenna4":{"rotate":[{},{"time":0.3333,"value":1.05,"curve":[0.731,1.05,1.008,-4.55]},{"time":1.2333,"value":-4.55,"curve":[1.35,-4.55,1.583,0.53]},{"time":1.7,"value":0.53},{"time":2,"value":-2.13},{"time":2.2,"value":4.71},{"time":2.3667,"value":-7.26},{"time":2.5667,"value":2.26},{"time":2.7333,"value":-3.39},{"time":3.0667,"value":-5.53},{"time":3.2333,"value":7.78},{"time":3.4667,"value":-5.99},{"time":3.7,"value":3.11},{"time":3.9,"value":-3.05},{"time":4.1,"value":0.31},{"time":4.3,"value":-3.06},{"time":4.5333,"value":0.36},{"time":4.8667,"value":4.94,"curve":[4.925,4.94,5.042,-2.38]},{"time":5.1,"value":-2.38},{"time":5.2667,"value":3.65},{"time":5.4,"value":-3.04},{"time":5.5,"value":1.49},{"time":5.6,"value":-1.86},{"time":5.7,"value":0.42}]},"antenna5":{"rotate":[{},{"time":0.3333,"value":1.05,"curve":[0.731,1.05,1.008,-4.55]},{"time":1.2333,"value":-4.55,"curve":[1.35,-4.55,1.583,0.53]},{"time":1.7,"value":0.53},{"time":2,"value":-2.13},{"time":2.2,"value":4.71},{"time":2.3667,"value":-7.26},{"time":2.5667,"value":2.26},{"time":2.7333,"value":-3.39},{"time":3.0667,"value":-5.53},{"time":3.2333,"value":7.78},{"time":3.4667,"value":-5.99},{"time":3.7,"value":3.11},{"time":3.9,"value":-3.05},{"time":4.1,"value":0.31},{"time":4.3,"value":-3.06},{"time":4.5333,"value":0.36},{"time":4.8667,"value":4.94,"curve":[4.925,4.94,5.042,-2.38]},{"time":5.1,"value":-2.38},{"time":5.2667,"value":3.65},{"time":5.4,"value":-3.04},{"time":5.5,"value":1.49},{"time":5.6,"value":-1.86},{"time":5.7,"value":0.42}]},"antenna6":{"rotate":[{},{"time":0.3333,"value":1.05,"curve":[0.731,1.05,1.008,-4.55]},{"time":1.2333,"value":-4.55,"curve":[1.35,-4.55,1.583,0.53]},{"time":1.7,"value":0.53},{"time":2,"value":-2.13},{"time":2.2,"value":4.71},{"time":2.3667,"value":-7.26},{"time":2.5667,"value":2.26},{"time":2.7333,"value":-3.39},{"time":3.0667,"value":-5.53},{"time":3.2333,"value":7.78},{"time":3.4667,"value":-5.99},{"time":3.7,"value":3.11},{"time":3.9,"value":-3.05},{"time":4.1,"value":0.31},{"time":4.3,"value":-3.06},{"time":4.5333,"value":0.36},{"time":4.8667,"value":4.94,"curve":[4.925,4.94,5.042,-2.38]},{"time":5.1,"value":-2.38},{"time":5.2667,"value":3.65},{"time":5.4,"value":-3.04},{"time":5.5,"value":1.49},{"time":5.6,"value":-1.86},{"time":5.7,"value":0.42}]},"machinegun":{"rotate":[{"value":8.07,"curve":"stepped"},{"time":2.0667,"value":8.07},{"time":2.1667,"value":3.11},{"time":2.5667,"value":-10.99,"curve":"stepped"},{"time":3.1333,"value":-10.99},{"time":3.2667,"value":18.18},{"time":3.4333,"value":2.75,"curve":"stepped"},{"time":4.7,"value":2.75},{"time":4.9,"value":8.07}]}},"path":{"treads-path":{"position":[{"curve":[0.984,0,1.588,0.1788]},{"time":2,"value":0.385,"curve":[2.023,0.3916,2.045,0.3983]},{"time":2.0667,"value":0.405},{"time":2.3333,"value":0.555},{"time":2.5,"value":0.605},{"time":2.6667,"value":0.685},{"time":2.8333,"value":0.745},{"time":3,"value":0.785},{"time":3.0667,"value":0.8},{"time":3.1333,"value":0.825},{"time":3.1667,"value":0.835},{"time":3.2333,"value":0.87},{"time":3.5,"value":0.98,"curve":[3.726,1.0474,4.335,1.4]},{"time":4.8333,"value":1.4}]}}},"shoot":{"slots":{"rock":{"attachment":[{}]},"smoke-glow":{"rgba":[{"time":0.1333,"color":"ffffffff"},{"time":0.1667,"color":"ffbc8af4"},{"time":0.2,"color":"fc8e8e90"},{"time":0.2667,"color":"fa3e3e1e"}],"attachment":[{"time":0.0667,"name":"smoke-glow"},{"time":0.3}]},"smoke-puff1-bg":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.6,"light":"ffd50cff","dark":"604b3f"},{"time":1.0333,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.0667,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg2":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":0.8333,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg3":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":0.8333,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg4":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.6,"light":"ffd50cff","dark":"604b3f"},{"time":0.9,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg5":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.6,"light":"ffd50cff","dark":"604b3f"},{"time":0.9,"light":"ffd50c00","dark":"604a3f"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg6":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":0.7,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg7":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3333,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":0.8333,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg8":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4333,"light":"ffd50cff","dark":"604b3f"},{"time":0.9333,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg9":{"rgba2":[{"time":0.1333,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.5333,"light":"ffd50cff","dark":"604b3f"},{"time":0.8333,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg10":{"rgba2":[{"time":0.1333,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.5333,"light":"ffd50cff","dark":"604b3f"},{"time":0.8333,"light":"ffd50c00","dark":"604a3f"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg11":{"rgba2":[{"time":0.1333,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":0.7667,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.3333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg12":{"rgba2":[{"time":0.3333,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.6,"light":"ffd50cff","dark":"604b3f"},{"time":0.8667,"light":"ffd50c00","dark":"604a3f"}],"attachment":[{"time":0.3667,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg13":{"rgba2":[{"time":0.3667,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":1,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.3667,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg14":{"rgba2":[{"time":0.4333,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.6,"light":"ffd50cff","dark":"604b3f"},{"time":1.0667,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.4333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg15":{"rgba2":[{"time":0.4,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":0.8333,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.4,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg16":{"rgba2":[{"time":0.4,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":0.7,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.4,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg17":{"rgba2":[{"time":0.2333,"light":"ffd50cff","dark":"534035"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4,"light":"ffd50cff","dark":"604b3f"},{"time":0.6667,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.2333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg18":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.5,"light":"ffd50cff","dark":"604b3f"},{"time":0.8,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.2333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg20":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":0.8,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.3333,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg21":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":0.7,"light":"ffd50c00","dark":"604b3f"}]},"smoke-puff1-bg22":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":0.7,"light":"ffd50c00","dark":"604b3f"}]},"smoke-puff1-bg23":{"rgba2":[{"time":0.0667,"light":"ffd50cff","dark":"3b2c23"},{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.5,"light":"ffd50cff","dark":"604b3f"},{"time":0.7667,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.3,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg24":{"rgba2":[{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":0.7,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.3,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg25":{"rgba2":[{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":1,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.3667,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg26":{"rgba2":[{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.6,"light":"ffd50cff","dark":"604b3f"},{"time":0.9333,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.3667,"name":"smoke-puff01-bg"}]},"smoke-puff1-bg27":{"rgba2":[{"time":0.3,"light":"ffd50cff","dark":"604b3f","curve":"stepped"},{"time":0.4667,"light":"ffd50cff","dark":"604b3f"},{"time":0.7333,"light":"ffd50c00","dark":"604b3f"}],"attachment":[{"time":0.3667,"name":"smoke-puff01-bg"}]},"smoke-puff1-fg":{"rgba2":[{"time":0.0667,"light":"ffdf31ff","dark":"ff0000"},{"time":0.1333,"light":"fde252ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.6,"light":"ab764cff","dark":"ac8d75"},{"time":1.0333,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.0667,"name":"smoke-puff01-fg"},{"time":0.1667,"name":"smoke-puff02-fg"},{"time":0.2,"name":"smoke-puff03-fg"},{"time":0.2333,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg2":{"rgba2":[{"time":0.1333,"light":"ffdf31ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":0.8333,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-fg"},{"time":0.1667,"name":"smoke-puff02-fg"},{"time":0.2,"name":"smoke-puff03-fg"},{"time":0.2333,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg3":{"rgba2":[{"time":0.1333,"light":"ffe457ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":0.8333,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-fg"},{"time":0.1667,"name":"smoke-puff02-fg"},{"time":0.2,"name":"smoke-puff03-fg"},{"time":0.2667,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg4":{"rgba2":[{"time":0.1333,"light":"fae781ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.6,"light":"ab764cff","dark":"ac8d75"},{"time":0.9,"light":"ac8c7500","dark":"604a3f"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-fg"},{"time":0.1667,"name":"smoke-puff02-fg"},{"time":0.2,"name":"smoke-puff03-fg"},{"time":0.2667,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg5":{"rgba2":[{"time":0.1333,"light":"ffdf31ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.6,"light":"ab764cff","dark":"ac8d75"},{"time":0.9,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-fg"},{"time":0.1667,"name":"smoke-puff02-fg"},{"time":0.2,"name":"smoke-puff03-fg"},{"time":0.2667,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg6":{"rgba2":[{"time":0.1333,"light":"ffdf31ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":0.7,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-fg"},{"time":0.1667,"name":"smoke-puff02-fg"},{"time":0.2,"name":"smoke-puff03-fg"},{"time":0.2667,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg7":{"rgba2":[{"time":0.1333,"light":"ffdf31ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3333,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":0.8333,"light":"ac8c7500","dark":"604a3f"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-fg"},{"time":0.1667,"name":"smoke-puff02-fg"},{"time":0.2,"name":"smoke-puff03-fg"},{"time":0.2333,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg8":{"rgba2":[{"time":0.1333,"light":"ffdf31ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4333,"light":"ab764cff","dark":"ac8d75"},{"time":0.9333,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-fg"},{"time":0.1667,"name":"smoke-puff02-fg"},{"time":0.2,"name":"smoke-puff03-fg"},{"time":0.2333,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg9":{"rgba2":[{"time":0.1333,"light":"ffdf31ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.5333,"light":"ab764cff","dark":"ac8d75"},{"time":0.8333,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-fg"},{"time":0.1667,"name":"smoke-puff02-fg"},{"time":0.2,"name":"smoke-puff03-fg"},{"time":0.2333,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg10":{"rgba2":[{"time":0.1333,"light":"fce35dff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.5333,"light":"ab764cff","dark":"ac8d75"},{"time":0.8333,"light":"ac8c7500","dark":"604a3f"}],"attachment":[{"time":0.1333,"name":"smoke-puff01-fg"},{"time":0.1667,"name":"smoke-puff02-fg"},{"time":0.2,"name":"smoke-puff03-fg"},{"time":0.2667,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg11":{"rgba2":[{"time":0.3333,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":0.7667,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.3333,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg12":{"rgba2":[{"time":0.3667,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.6,"light":"ab764cff","dark":"ac8d75"},{"time":0.8667,"light":"ac8c7500","dark":"604a3f"}],"attachment":[{"time":0.3667,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg13":{"rgba2":[{"time":0.3667,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":1,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.3667,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg14":{"rgba2":[{"time":0.4333,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.6,"light":"ab764cff","dark":"ac8d75"},{"time":1.0667,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.4333,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg15":{"rgba2":[{"time":0.4,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":0.8333,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.4,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg16":{"rgba2":[{"time":0.4,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":0.7,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.4,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg17":{"rgba2":[{"time":0.2333,"light":"e3c05eff","dark":"ab7e59"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4,"light":"ab764cff","dark":"ac8d75"},{"time":0.6667,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.2333,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg18":{"rgba2":[{"time":0.1333,"light":"ffdf31ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.5,"light":"ab764cff","dark":"ac8d75"},{"time":0.7667,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.2333,"name":"smoke-puff03-fg"},{"time":0.2667,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg20":{"rgba2":[{"time":0.1333,"light":"ffdf31ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":0.8,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.3333,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg21":{"rgba2":[{"time":0.1333,"light":"ffdf31ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":0.7,"light":"ab764c00","dark":"ac8d75"}]},"smoke-puff1-fg22":{"rgba2":[{"time":0.1333,"light":"ffdf31ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":0.7,"light":"ab764c00","dark":"ac8d75"}]},"smoke-puff1-fg23":{"rgba2":[{"time":0.1333,"light":"ffdf31ff","dark":"ff0000"},{"time":0.1667,"light":"ffe568ff","dark":"e26425"},{"time":0.2,"light":"ffe568ff","dark":"ab774c"},{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.5,"light":"ab764cff","dark":"ac8d75"},{"time":0.7667,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.3,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg24":{"rgba2":[{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":0.7,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.3,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg25":{"rgba2":[{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":1,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.3667,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg26":{"rgba2":[{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.6,"light":"ab764cff","dark":"ac8d75"},{"time":0.9333,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.3667,"name":"smoke-puff04-fg"}]},"smoke-puff1-fg27":{"rgba2":[{"time":0.3,"light":"ab764cff","dark":"ac8d75","curve":"stepped"},{"time":0.4667,"light":"ab764cff","dark":"ac8d75"},{"time":0.7333,"light":"ab764c00","dark":"ac8d75"}],"attachment":[{"time":0.3667,"name":"smoke-puff04-fg"}]},"tank-glow":{"rgba":[{"time":0.0667,"color":"fc994d84"},{"time":0.1333,"color":"f5b16bc8","curve":[0.221,0.96,0.252,0.98,0.221,0.69,0.252,0.62,0.221,0.42,0.252,0.33,0.221,0.78,0.252,0.32]},{"time":0.2667,"color":"fc994c30"}],"attachment":[{"time":0.0667,"name":"smoke-glow"},{"time":0.2667}]}},"bones":{"cannon":{"translate":[{"time":0.0667},{"time":0.1667,"x":34.77,"y":0.9},{"time":0.2667,"x":1.3}]},"tank-body":{"rotate":[{"time":0.0667},{"time":0.1667,"value":-4.29,"curve":[0.2,-4.29,0.267,2.37]},{"time":0.3,"value":2.37,"curve":[0.333,2.37,0.4,0]},{"time":0.4333}],"translate":[{"time":0.0667},{"time":0.1667,"x":31.04,"y":1.67,"curve":[0.2,31.04,0.267,-12.05,0.2,1.67,0.267,-0.23]},{"time":0.3,"x":-12.05,"y":-0.23},{"time":0.3667}]},"tank-treads":{"rotate":[{"time":0.0667},{"time":0.1667,"value":-3.08},{"time":0.3,"value":-0.42}]},"smoke1":{"rotate":[{"time":0.0667},{"time":0.1333,"value":2.88},{"time":0.1667,"value":2.34},{"time":0.2,"value":124.36},{"time":0.2667,"value":142.26},{"time":0.3333,"value":86.78},{"time":0.4667,"value":128.79},{"time":0.6333,"value":146.22},{"time":1.0333,"value":210.7}],"translate":[{"time":0.0667,"x":-9.69,"y":1.05},{"time":0.1333,"x":7.53,"y":1.21},{"time":0.1667,"x":3.26,"y":4.07},{"time":0.2,"x":29.64,"y":-17.46},{"time":0.2667,"x":86.97,"y":17.83},{"time":0.3333,"x":193.74,"y":-38.98},{"time":0.4,"x":341.67,"y":-39.52},{"time":0.6333,"x":393.24,"y":-4.01},{"time":1.0333,"x":410.76,"y":6.35}],"scale":[{"time":0.0667},{"time":0.1333,"x":3.171,"y":0.756},{"time":0.1667,"x":3.488,"y":1.279},{"time":0.2,"x":5.151,"y":2.369},{"time":0.2667,"x":4.735,"y":3.622},{"time":0.3,"x":4.735,"y":4.019},{"time":0.3333,"x":4.613,"y":3.339},{"time":0.3667,"x":4.918,"y":3.561},{"time":0.4,"x":4.6,"y":4.263},{"time":0.6333,"x":4.449,"y":2.62},{"time":1.0333,"x":3.09,"y":1.447}]},"smoke2":{"rotate":[{"time":0.1667,"value":31.55},{"time":0.3,"value":-22.63},{"time":0.4667,"value":142.89},{"time":0.6,"value":253.78},{"time":0.8333,"value":299.28}],"translate":[{"time":0.1667,"x":17.26,"y":4.86},{"time":0.2333,"x":141.22,"y":27.27},{"time":0.3,"x":178.86,"y":56.63},{"time":0.3667,"x":200.46,"y":71.05},{"time":0.4333,"x":213.12,"y":78.39},{"time":0.6333,"x":221.44,"y":73.1},{"time":0.8333,"x":223.32,"y":73.74}],"scale":[{"time":0.1667,"x":1.34,"y":1.34},{"time":0.2333,"x":2.81,"y":1.317},{"time":0.3,"x":2.932,"y":1.374},{"time":0.4667,"x":1.247,"y":0.639},{"time":0.8333,"x":0.778,"y":0.515}]},"smoke3":{"rotate":[{"time":0.1667,"value":-5.54},{"time":0.2333,"value":0.2},{"time":0.3333,"value":20.27},{"time":0.4,"value":31.36},{"time":0.4667,"value":68.52},{"time":0.5333,"value":99.74},{"time":0.6333,"value":145.8},{"time":0.8333,"value":193.28}],"translate":[{"time":0.1333,"x":1.17,"y":8.53},{"time":0.1667,"x":37.53,"y":4.84},{"time":0.2,"x":67.99,"y":9.85},{"time":0.2333,"x":134.14,"y":-13.5},{"time":0.2667,"x":181.31,"y":-19.93},{"time":0.3,"x":238.28,"y":-8.82},{"time":0.3333,"x":268.51,"y":-25.75},{"time":0.3667,"x":359.06,"y":-28.49},{"time":0.4,"x":432.96,"y":-24.11},{"time":0.4667,"x":452.16,"y":-16.73},{"time":0.6333,"x":456.28,"y":-0.41},{"time":0.8333,"x":454.14,"y":16.41}],"scale":[{"time":0.1333,"x":2.258,"y":1.366},{"time":0.1667,"x":2.656,"y":1.47},{"time":0.2,"x":3.202,"y":1.772},{"time":0.2333,"x":3.202,"y":1.93},{"time":0.2667,"x":3.124,"y":1.896},{"time":0.3,"x":3.593,"y":1.896},{"time":0.3333,"x":2.363,"y":1.247},{"time":0.3667,"x":1.845,"y":0.973},{"time":0.4,"x":1.754,"y":0.926},{"time":0.4333,"x":1.448,"y":0.695},{"time":0.4667,"x":1.441,"y":0.688},{"time":0.5333,"x":0.865,"y":0.456},{"time":0.7,"x":0.86,"y":0.454},{"time":0.8333,"x":0.211,"y":0.111}]},"smoke4":{"rotate":[{"time":0.1667,"value":-20.35},{"time":0.2333,"value":18.5},{"time":0.3,"value":57.77},{"time":0.4,"value":105.85},{"time":0.6,"value":161.28},{"time":0.9,"value":208.43}],"translate":[{"time":0.1667,"x":35.95,"y":25.54},{"time":0.2333,"x":34.17,"y":1.87},{"time":0.3,"x":136.7,"y":21.5},{"time":0.4,"x":138.61,"y":34.8},{"time":0.6,"x":160.38,"y":37.13},{"time":0.9,"x":196.41,"y":30.36}],"scale":[{"time":0.1667,"x":2.751,"y":1.754},{"time":0.2333,"x":3.486,"y":2.224},{"time":0.2667,"x":3.486,"y":2.586},{"time":0.3,"x":3.847,"y":2.109},{"time":0.4,"x":1.96,"y":1.074},{"time":0.9,"x":0.825,"y":0.452}]},"smoke5":{"rotate":[{"time":0.2,"value":23.09},{"time":0.2667,"value":12.24},{"time":0.3333,"value":36.92},{"time":0.4333,"value":-37.33},{"time":0.5333,"value":-0.66},{"time":0.9,"value":64.02}],"translate":[{"time":0.1333},{"time":0.2333,"x":123.76,"y":19.44},{"time":0.3,"x":239.08,"y":-49.72},{"time":0.3667,"x":280.23,"y":-51.46},{"time":0.7,"x":340.62,"y":-20.09},{"time":0.9,"x":349.18,"y":-5.25}],"scale":[{"time":0.1333},{"time":0.1667,"x":1.718,"y":1.718},{"time":0.2,"x":2.109,"y":2.109},{"time":0.2333,"x":1.781,"y":2.183},{"time":0.2667,"x":2.148,"y":2.633},{"time":0.3333,"x":2.234,"y":2.738},{"time":0.3667,"x":1.366,"y":2.148},{"time":0.4,"x":0.97,"y":1.524},{"time":0.4333,"x":1.078,"y":1.157},{"time":0.4667,"x":1.126,"y":1.005},{"time":0.7,"x":1.241,"y":1.301},{"time":0.9,"x":0.709,"y":0.893}]},"smoke6":{"rotate":[{"time":0.1667,"value":-37.43},{"time":0.2333,"value":-18.36},{"time":0.3333,"value":28.58},{"time":0.4,"value":150.54},{"time":0.7,"value":301.59}],"translate":[{"time":0.1333},{"time":0.2,"x":68.04,"y":16.15},{"time":0.2667,"x":214.52,"y":13.25},{"time":0.3333,"x":285.4,"y":17.95},{"time":0.4,"x":202.91,"y":101.43},{"time":0.4667,"x":189.25,"y":116.39},{"time":0.7,"x":182.77,"y":137.4}],"scale":[{"time":0.1333},{"time":0.1667,"x":1.152,"y":1.288},{"time":0.2,"x":1.939,"y":2.168},{"time":0.2333,"x":2.278,"y":2.223},{"time":0.2667,"x":2.023,"y":1.974},{"time":0.3,"x":2.644,"y":1.974},{"time":0.4,"x":1.539,"y":1.425},{"time":0.4667,"x":1.14,"y":0.939},{"time":0.7,"x":0.215,"y":0.161}]},"smoke7":{"rotate":[{"time":0.1667,"value":-243.11},{"time":0.4,"value":-182.02},{"time":0.8333,"value":-83.02}],"translate":[{"time":0.1333,"x":3.19,"y":-6.53},{"time":0.1667,"x":44.54,"y":1.12},{"time":0.2,"x":65.84,"y":6.02},{"time":0.2333,"x":173.84,"y":97.51},{"time":0.4,"x":167.39,"y":74.58},{"time":0.8333,"x":227.77,"y":84.64}],"scale":[{"time":0.1333,"x":0.878,"y":0.878},{"time":0.1667,"x":1.235,"y":1.235},{"time":0.2,"x":1.461,"y":1.461},{"time":0.2333,"x":1.114,"y":1.114},{"time":0.3333,"x":1.067,"y":1.067},{"time":0.4667,"x":0.81,"y":0.753},{"time":0.8333,"x":0.52,"y":0.484}]},"smoke8":{"rotate":[{"time":0.1667,"value":-156.52},{"time":0.2667,"value":-154.05},{"time":0.3333,"value":-108.35},{"time":0.6,"value":-93.14},{"time":0.9333,"value":-70.89}],"translate":[{"time":0.1667,"x":20.72,"y":0.25},{"time":0.2333,"x":46.1,"y":-10.06},{"time":0.3,"x":149.77,"y":0.92},{"time":0.3667,"x":241.21,"y":49.01},{"time":0.5333,"x":276,"y":58.76},{"time":0.7,"x":292.02,"y":65.91},{"time":0.9333,"x":308.7,"y":69.51}],"scale":[{"time":0.1333,"y":1.174},{"time":0.1667,"x":1.813,"y":1.438},{"time":0.2,"x":1.813,"y":1.878},{"time":0.2333,"x":1.211,"y":1.878},{"time":0.2667,"x":1.584,"y":1.596},{"time":0.3,"x":1.958,"y":1.878},{"time":0.4667,"x":1.139,"y":0.958},{"time":0.9333,"x":0.839,"y":0.591}]},"smoke9":{"rotate":[{"time":0.1333,"value":-44.34},{"time":0.1667,"value":14.73},{"time":0.2333,"value":116.07},{"time":0.2667,"value":118.29},{"time":0.3333,"value":148.13},{"time":0.3667,"value":172.74},{"time":0.4,"value":235.69},{"time":0.4333,"value":283.36},{"time":0.7667,"value":358.76}],"translate":[{"time":0.1333,"x":-3.49,"y":0.04},{"time":0.2,"x":87.4,"y":-7.97},{"time":0.2667,"x":233.69,"y":-33.86},{"time":0.3333,"x":296.44,"y":-30.87},{"time":0.4,"x":390.8,"y":4},{"time":0.4667,"x":391.42,"y":13.17},{"time":0.6333,"x":413.3,"y":36.13},{"time":0.7667,"x":408.59,"y":40.75}],"scale":[{"time":0.1333,"x":1.289,"y":1.501},{"time":0.2,"x":1.751,"y":2.039},{"time":0.2667,"x":2.064,"y":2.347},{"time":0.3333,"x":1.822,"y":2.072},{"time":0.4,"x":1.296,"y":1.045},{"time":0.4667,"x":1.872,"y":1.526},{"time":0.6333,"x":1.181,"y":1.037},{"time":0.7667,"x":0.716,"y":0.615}]},"smoke10":{"rotate":[{"time":0.1333,"value":12.16},{"time":0.2,"value":49.19},{"time":0.2667,"value":33.17},{"time":0.3333,"value":42.23},{"time":0.4,"value":11.69},{"time":0.4667,"value":41.83},{"time":0.5333,"value":54.86},{"time":0.6333,"value":75.25},{"time":0.8333,"value":126.4}],"translate":[{"time":0.1333,"x":7.74,"y":10.25},{"time":0.2,"x":42.9,"y":72.89},{"time":0.2667,"x":221.58,"y":82.27},{"time":0.3333,"x":297.31,"y":85.39},{"time":0.4,"x":322.91,"y":81.04},{"time":0.4667,"x":346.62,"y":76.68},{"time":0.6667,"x":377.46,"y":81.85},{"time":0.8333,"x":402.18,"y":101.03}],"scale":[{"time":0.1333,"x":0.537,"y":1.062},{"time":0.1667,"x":1.042,"y":0.841},{"time":0.2,"x":1.937,"y":1.563},{"time":0.2333,"x":1.937,"y":2.176},{"time":0.2667,"x":2.254,"y":2.532},{"time":0.3,"x":2.24,"y":2.516},{"time":0.5333,"x":1.731,"y":1.882},{"time":0.8333,"x":0.855,"y":0.867}]},"smoke-glow":{"translate":[{"time":0.0667,"x":-57.08,"y":0.01},{"time":0.1,"x":-49.68,"y":-1.46},{"time":0.1333,"x":6.3,"y":-2.92},{"time":0.1667,"x":31.57,"y":0.44},{"time":0.2,"x":34.04,"y":0.27},{"time":0.2333,"x":109.29,"y":1.02},{"time":0.4,"x":119.89,"y":1.01},{"time":0.4333,"x":135.2,"y":1.03},{"time":0.4667,"x":152.86,"y":1.06},{"time":0.5333,"x":164.64,"y":1.07},{"time":0.6,"x":179.94,"y":1.09},{"time":0.6333,"x":190.54,"y":1.1}],"scale":[{"time":0.0667,"x":0.233,"y":0.233},{"time":0.1,"x":0.42,"y":0.288},{"time":0.1333,"x":1.669,"y":1.072},{"time":0.1667,"x":1.669,"y":1.785,"curve":"stepped"},{"time":0.2,"x":1.669,"y":1.785},{"time":0.2333,"x":2.544,"y":1.785},{"time":0.4333,"x":3.48,"y":2.22},{"time":0.4667,"x":4.337,"y":2.655}]},"smoke11":{"rotate":[{"time":0.4,"value":47.07},{"time":0.4333,"value":109.71},{"time":0.4667,"value":164.62},{"time":0.8333,"value":276.93}],"translate":[{"time":0.3333,"x":280.31,"y":126.85},{"time":0.4,"x":296.27,"y":125.62},{"time":0.4667,"x":312.45,"y":131.57},{"time":0.6667,"x":310.5,"y":149.67},{"time":0.8333,"x":307.08,"y":153.94}],"scale":[{"time":0.3333,"x":1.491,"y":1.491},{"time":0.4667,"x":1.144,"y":0.948},{"time":0.5667,"x":0.491,"y":0.491},{"time":0.8333,"x":0.985,"y":0.91}]},"smoke12":{"rotate":[{"time":0.3667,"value":-37.96},{"time":0.4333,"value":28.55},{"time":0.5333,"value":108.53},{"time":0.8667,"value":191.85}],"translate":[{"time":0.3667,"x":390.22,"y":-1.06},{"time":0.4333,"x":411.78,"y":26.39},{"time":0.5333,"x":428.12,"y":56.28},{"time":0.8667,"x":444.34,"y":68.06}],"scale":[{"time":0.3667,"x":2.006,"y":1.821},{"time":0.5333,"x":1.719,"y":1.293},{"time":0.7333,"x":1.562,"y":1.304},{"time":0.8667,"x":0.727,"y":0.637}]},"smoke13":{"rotate":[{"time":0.3667,"value":305.8},{"time":0.4,"value":478.49},{"time":0.4333,"value":537.45},{"time":0.4667,"value":573.84},{"time":0.5333,"value":596.4},{"time":0.7,"value":622.3},{"time":1,"value":657.95}],"translate":[{"time":0.3667,"x":331.84,"y":-25.82},{"time":0.4,"x":417.88,"y":-42.62},{"time":0.4667,"x":451.61,"y":-42.21},{"time":0.5333,"x":453.81,"y":-37.03},{"time":0.6,"x":451.86,"y":-31.89},{"time":0.7,"x":453.37,"y":-27.28},{"time":1,"x":454.04,"y":-17.89}],"scale":[{"time":0.3667,"x":4.509,"y":3.114},{"time":0.4,"x":3.673,"y":2.537},{"time":0.4333,"x":4.201,"y":2.638},{"time":0.4667,"x":4.27,"y":2.399},{"time":0.6,"x":2.798,"y":1.932},{"time":0.8333,"x":2.316,"y":1.599},{"time":1,"x":1.081,"y":0.746}]},"smoke14":{"rotate":[{"time":0.4333,"value":271.03},{"time":0.7,"value":299.97},{"time":1.0667,"value":331.16}],"translate":[{"time":0.4333,"x":371.68,"y":-29.8},{"time":0.7667,"x":400.59,"y":-44.36},{"time":1.0667,"x":432.26,"y":-44.79}],"scale":[{"time":0.4333,"x":4.011,"y":3.366},{"time":0.7667,"x":2.071,"y":1.624},{"time":1.0667,"x":1.798,"y":1.111}]},"smoke15":{"rotate":[{"time":0.4,"value":111.75},{"time":0.4667,"value":171.93},{"time":0.6,"value":256.95},{"time":0.8333,"value":299.15}],"translate":[{"time":0.4,"x":266.71,"y":-53.04},{"time":0.4333,"x":290.84,"y":-51.43},{"time":0.5333,"x":305.65,"y":-44.32},{"time":0.6667,"x":318.96,"y":-38.95},{"time":0.8333,"x":342.65,"y":-27.33}],"scale":[{"time":0.4,"x":2.749,"y":2.095},{"time":0.4333,"x":3.302,"y":2.289},{"time":0.4667,"x":2.591,"y":1.895},{"time":0.5333,"x":1.777,"y":1.354},{"time":0.7,"x":1.932,"y":1.267},{"time":0.8333,"x":1.002,"y":1.546}]},"smoke16":{"rotate":[{"time":0.4,"value":89.78},{"time":0.4667,"value":137.83},{"time":0.5333,"value":193.49},{"time":0.6,"value":235.26},{"time":0.6333,"value":286.8}],"translate":[{"time":0.4,"x":217.23,"y":-21.45},{"time":0.4667,"x":249.95,"y":-13.73},{"time":0.5333,"x":264.96,"y":-9.87},{"time":0.6,"x":278.95,"y":6.37},{"time":0.6333,"x":245.65,"y":11.77}],"scale":[{"time":0.4,"x":2.265,"y":1.859},{"time":0.4333,"x":2.621,"y":1.955},{"time":0.4667,"x":1.953,"y":1.538},{"time":0.6,"x":1.005,"y":0.825},{"time":0.6333,"x":0.387,"y":0.318}]},"smoke17":{"rotate":[{"time":0.2333,"value":99.02},{"time":0.3,"value":58.06},{"time":0.3333,"value":34.05},{"time":0.3667,"value":-17.34},{"time":0.6667,"value":-62.36}],"translate":[{"time":0.2333,"x":18.91,"y":-62.91},{"time":0.3,"x":2.43,"y":-61.54},{"time":0.3333,"x":1.89,"y":-36.55},{"time":0.3667,"x":6.97,"y":-29.52},{"time":0.4333,"x":10.78,"y":-20.55},{"time":0.6667,"x":18.65,"y":-13.19}],"scale":[{"time":0.2333,"x":1.915,"y":1.915},{"time":0.3,"x":1.509,"y":1.509},{"time":0.3333,"x":1.01,"y":1.01},{"time":0.3667,"x":0.715,"y":0.715},{"time":0.4333,"x":0.949,"y":0.721},{"time":0.5667,"x":0.785,"y":0.74}]},"smoke18":{"rotate":[{"time":0.2333,"value":141.75},{"time":0.2667,"value":134.51},{"time":0.3333,"value":249.12},{"time":0.5,"value":363.82},{"time":0.7333,"value":450.54}],"translate":[{"time":0.2333,"x":60.81,"y":56.17},{"time":0.2667,"x":68.74,"y":69.4},{"time":0.3333,"x":76.85,"y":69.07},{"time":0.5,"x":101.49,"y":89.87},{"time":0.7333,"x":118.58,"y":101.16}],"scale":[{"time":0.2333,"x":2.288,"y":2.288},{"time":0.2667,"x":2.288,"y":1.628},{"time":0.3,"x":1.524,"y":1.308},{"time":0.5,"x":1.757,"y":1.385},{"time":0.5333,"x":2.08,"y":1.51},{"time":0.7333,"x":1.405,"y":0.896}]},"smoke20":{"rotate":[{"time":0.3333,"value":95.16},{"time":0.3667,"value":130.42},{"time":0.4,"value":170.7},{"time":0.4333,"value":266.75},{"time":0.4667,"value":299.82},{"time":0.5333,"value":326.88},{"time":0.6,"value":350.8},{"time":0.9,"value":403.14}],"translate":[{"time":0.3333,"x":124.61,"y":-46.55},{"time":0.5333,"x":173.8,"y":-36.62},{"time":0.7,"x":186.5,"y":-35.41},{"time":0.9,"x":188.56,"y":-37.75}],"scale":[{"time":0.3333,"x":3.346,"y":2.654},{"time":0.3667,"x":2.661,"y":2.111},{"time":0.4333,"x":2.751,"y":1.984},{"time":0.4667,"x":3.059,"y":2.21},{"time":0.5333,"x":2.159,"y":1.712},{"time":0.7,"x":1.601,"y":1.27},{"time":0.9,"x":1.679,"y":0.856}]},"smoke23":{"rotate":[{"time":0.3,"value":115.12},{"time":0.3667,"value":79.01},{"time":0.7667,"value":6.96}],"translate":[{"time":0.3,"x":75.15,"y":-50.92},{"time":0.3667,"x":59.33,"y":-53.52},{"time":0.7667,"x":39.68,"y":-48.64}],"scale":[{"time":0.3,"x":3.331,"y":2.096},{"time":0.4333,"x":2.4,"y":2.006},{"time":0.5,"x":2.555,"y":2.094},{"time":0.7667,"x":1.35,"y":1.241}]},"antenna1":{"rotate":[{"time":0.0667},{"time":0.2,"value":11.78},{"time":0.3,"value":-9.52},{"time":0.4,"value":8.07},{"time":0.5,"value":-4.45},{"time":0.6,"value":1.54},{"time":0.7,"value":-0.34}]},"antenna2":{"rotate":[{"time":0.0667},{"time":0.2,"value":11.78},{"time":0.3,"value":-9.52},{"time":0.4,"value":8.07},{"time":0.5,"value":-4.45},{"time":0.6,"value":1.54},{"time":0.7,"value":-0.34}]},"antenna3":{"rotate":[{"time":0.0667},{"time":0.2,"value":11.78},{"time":0.3,"value":-9.52},{"time":0.4,"value":8.07},{"time":0.5,"value":-4.45},{"time":0.6,"value":1.54},{"time":0.7,"value":-0.34}]},"antenna4":{"rotate":[{"time":0.0667},{"time":0.2,"value":11.78},{"time":0.3,"value":-9.52},{"time":0.4,"value":8.07},{"time":0.5,"value":-4.45},{"time":0.6,"value":1.54},{"time":0.7,"value":-0.34}]},"antenna5":{"rotate":[{"time":0.0667},{"time":0.2,"value":11.78},{"time":0.3,"value":-9.52},{"time":0.4,"value":8.07},{"time":0.5,"value":-4.45},{"time":0.6,"value":1.54},{"time":0.7,"value":-0.34}]},"antenna6":{"rotate":[{"time":0.0667},{"time":0.2,"value":11.78},{"time":0.3,"value":-9.52},{"time":0.4,"value":8.07},{"time":0.5,"value":-4.45},{"time":0.6,"value":1.54},{"time":0.7,"value":-0.34}]},"smoke24":{"rotate":[{"time":0.3,"value":71.32},{"time":0.3667,"value":112.39},{"time":0.4667,"value":159.56},{"time":0.7,"value":224.21}],"translate":[{"time":0.3,"x":90.72,"y":-18.79},{"time":0.3667,"x":149.69,"y":-7.78},{"time":0.4667,"x":176.26,"y":12.31},{"time":0.7,"x":184.07,"y":31.75}],"scale":[{"time":0.3,"x":2.906,"y":2.311},{"time":0.4333,"x":3.567,"y":2.58},{"time":0.4667,"x":3.157,"y":2.41},{"time":0.7,"x":1.705,"y":1.356}]},"smoke25":{"rotate":[{"time":0.3667,"value":91.25},{"time":0.4333,"value":117.56},{"time":0.6333,"value":150.9},{"time":1,"value":189.47}],"translate":[{"time":0.3667,"x":187.21,"y":-51.18},{"time":0.5333,"x":245.48,"y":-46.28},{"time":0.6667,"x":277.36,"y":-43.12},{"time":1,"x":313.27,"y":-38.14}],"scale":[{"time":0.3667,"x":3.606,"y":2.657},{"time":0.4333,"x":4.166,"y":2.792},{"time":0.5333,"x":3.09,"y":2.091},{"time":1,"x":3.062,"y":1.801}]},"smoke26":{"rotate":[{"time":0.3667,"value":10.64},{"time":0.4,"value":60.85},{"time":0.4667,"value":89.45},{"time":0.7,"value":125.01},{"time":0.9333,"value":155.24}],"translate":[{"time":0.3667,"x":442.07,"y":-13.19},{"time":0.4,"x":453.7,"y":0.81},{"time":0.4667,"x":443.57,"y":-6.95},{"time":0.7,"x":460.97,"y":15.79},{"time":0.9333,"x":465.22,"y":20.92}],"scale":[{"time":0.3667,"x":2.726,"y":2.726},{"time":0.4333,"x":3.729,"y":2.822},{"time":0.4667,"x":3.398,"y":2.441},{"time":0.7,"x":4.324,"y":3.159},{"time":0.9,"x":1.977,"y":1.48}]},"smoke27":{"rotate":[{"time":0.3667,"value":24.75},{"time":0.4333,"value":-5.43},{"time":0.5333,"value":-39.76},{"time":0.8333,"value":-56.25}],"translate":[{"time":0.3667,"x":92.98,"y":-49.06},{"time":0.5333,"x":129.81,"y":-33.09},{"time":0.8333,"x":143.68,"y":-25.27}],"scale":[{"time":0.3667,"x":3.633,"y":2.223},{"time":0.4333,"x":2.745,"y":2.283},{"time":0.4667,"x":2.962,"y":2.122},{"time":0.5333,"x":2.007,"y":1.266}]},"cannon-target":{"translate":[{"time":0.1333},{"time":0.2,"y":128.38,"curve":[0.4,0,0.8,0,0.4,128.38,0.8,0]},{"time":1}],"scale":[{"time":0.4333,"x":0.632,"y":1.244},{"time":0.4667,"x":0.477,"y":1.487}]},"machinegun-target":{"scale":[{"time":0.4333,"x":0.632,"y":1.244},{"time":0.4667,"x":0.477,"y":1.487}]},"machinegun":{"rotate":[{"value":8.07,"curve":"stepped"},{"time":0.0667,"value":8.07},{"time":0.2333,"value":-18.67,"curve":[0.894,-18.44,0.832,7.5]},{"time":0.9,"value":8.07}]},"tank-root":{"translate":[{"time":0.0667},{"time":0.1667,"x":46.59,"curve":[0.192,46.59,0.242,0,0.192,0,0.242,0]},{"time":0.2667}]},"tank-glow":{"translate":[{"time":0.1333,"x":198.14,"curve":[0.199,190.76,0.222,-255.89,0.199,0,0.222,0]},{"time":0.2333,"x":-390}],"scale":[{"time":0.0667},{"time":0.1333,"x":1.185,"y":0.945,"curve":[0.199,1.182,0.222,1.048,0.199,0.939,0.222,0.579]},{"time":0.2333,"x":1.008,"y":0.471}]}},"attachments":{"default":{"clipping":{"clipping":{"deform":[{"time":0.0667,"offset":54,"vertices":[4.59198,-4.59192]},{"time":0.1333,"offset":8,"vertices":[-8.97369,-1.88211,9.11177,1.02258,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-14.73321,-45.16878,-30.31448,-84.4631,-32.24969,-108.78421,70.26825,-36.90201]},{"time":0.1667,"offset":8,"vertices":[-11.32373,-1.65065,11.42179,0.53259,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-15.36503,-69.18713,-4.45626,-121.90839,5.46554,-115.23274,71.78526,-33.85687]},{"time":0.2,"offset":8,"vertices":[-8.70522,1.02196,8.65102,-1.4101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4.59198,-4.59192]},{"time":0.2333,"offset":8,"vertices":[-5.23146,0.85796,5.23882,-0.81519]},{"time":0.2667,"offset":54,"vertices":[4.59198,-4.59192]}]}},"smoke-glow":{"smoke-glow":{"deform":[{"time":0.1333,"vertices":[-14.17073,19.14352,0,0,-10.97961,-15.09065,-5.79558,-24.82121,0.68117,-17.78759,-1.1179,-5.4463,0,0,0,0,17.52957,6.89397,-0.33841,-2.21582,5.51004,18.88118,-6.80153,20.91101]},{"time":0.1667,"vertices":[-4.34264,39.78125,5.6649,-2.42686,-8.39346,-22.52338,-2.66431,5.08595,-19.28093,3.98568,-11.21397,10.2879,4.56749,4.1329,-19.50706,-2.28786,11.35747,4.55941,9.04341,-11.72194,2.15381,5.14344,-12.82158,16.08209,-23.19814,1.81836]},{"time":0.2,"vertices":[-3.95581,36.12203,37.20779,-0.87419,21.29579,-15.76854,-2.02438,6.16526,-5.92201,4.19709,-1.39027,9.92793,7.70584,-0.7169,-6.69733,-2.62048,17.91826,7.77333,-12.2858,3.25454,-12.75876,3.71516,9.67891,15.48546]},{"time":0.2333,"vertices":[-11.9371,26.01078,2.91821,-0.27533,7.69899,-17.45375,-2.02438,6.16526,-5.92201,4.19709,-1.39027,9.92793,7.70584,-0.7169,-6.69733,-2.62048,17.91826,7.77333,-4.30551,-6.01406,-12.75876,3.71516,-5.10017,17.59191]},{"time":0.2667,"vertices":[0.5959,23.58176,20.74303,0.93943,7.69899,-17.45375,-2.02438,6.16526,-5.92201,4.19709,-1.39027,9.92793,20.51733,2.52203,13.35544,2.64274,24.32408,-1.94308,8.50604,-20.99353,13.14276,5.73959,6.31876,19.2114,16.98909,0.80981]}]}}}},"drawOrder":[{"time":0.3,"offsets":[{"slot":"smoke-puff1-bg2","offset":24},{"slot":"smoke-puff1-bg8","offset":19},{"slot":"smoke-puff1-bg9","offset":22},{"slot":"smoke-puff1-bg3","offset":17},{"slot":"smoke-puff1-fg17","offset":13},{"slot":"smoke-puff1-fg2","offset":2},{"slot":"smoke-puff1-fg5","offset":8},{"slot":"smoke-puff1-fg6","offset":4},{"slot":"smoke-puff1-fg7","offset":-4},{"slot":"smoke-puff1-fg4","offset":-4}]},{"time":0.3333,"offsets":[{"slot":"smoke-puff1-bg2","offset":8},{"slot":"smoke-puff1-bg8","offset":5},{"slot":"smoke-puff1-bg9","offset":3},{"slot":"smoke-puff1-fg","offset":24},{"slot":"smoke-puff1-fg5","offset":-14},{"slot":"smoke-puff1-fg6","offset":-21},{"slot":"smoke-puff1-fg7","offset":-19},{"slot":"smoke-puff1-fg10","offset":-21}]},{"time":0.3667,"offsets":[{"slot":"smoke-puff1-bg2","offset":7},{"slot":"smoke-puff1-bg9","offset":4},{"slot":"smoke-puff1-fg","offset":24},{"slot":"smoke-puff1-fg2","offset":5},{"slot":"smoke-puff1-fg6","offset":-22},{"slot":"smoke-puff1-fg7","offset":-18},{"slot":"smoke-puff1-fg10","offset":-20}]},{"time":0.4,"offsets":[{"slot":"smoke-puff1-bg2","offset":5},{"slot":"smoke-puff1-bg4","offset":0},{"slot":"smoke-puff1-fg","offset":24},{"slot":"smoke-puff1-fg2","offset":5},{"slot":"smoke-puff1-fg6","offset":-21},{"slot":"smoke-puff1-fg7","offset":-18},{"slot":"smoke-puff1-fg10","offset":-22}]},{"time":0.4333,"offsets":[{"slot":"smoke-puff1-bg2","offset":4},{"slot":"smoke-puff1-bg9","offset":4},{"slot":"smoke-puff1-fg","offset":24},{"slot":"smoke-puff1-fg2","offset":5},{"slot":"smoke-puff1-fg6","offset":-17},{"slot":"smoke-puff1-fg7","offset":-19},{"slot":"smoke-puff1-fg10","offset":-23}]},{"time":0.5333,"offsets":[{"slot":"smoke-puff1-bg2","offset":9},{"slot":"smoke-puff1-bg12","offset":0},{"slot":"smoke-puff1-fg","offset":24},{"slot":"smoke-puff1-fg2","offset":6},{"slot":"smoke-puff1-fg6","offset":-20},{"slot":"smoke-puff1-fg7","offset":-19},{"slot":"smoke-puff1-fg10","offset":-23},{"slot":"smoke-puff1-fg4","offset":-5}]}]}}}
\ No newline at end of file
diff --git a/e2e/.dev/public/tank-pro.png b/e2e/.dev/public/tank-pro.png
new file mode 100644
index 0000000000..badc613ca2
Binary files /dev/null and b/e2e/.dev/public/tank-pro.png differ
diff --git a/e2e/.dev/vite.config.js b/e2e/.dev/vite.config.js
index 2cfc11d824..498a9a0244 100644
--- a/e2e/.dev/vite.config.js
+++ b/e2e/.dev/vite.config.js
@@ -42,6 +42,7 @@ demoList.forEach(({ file }) => {
fs.outputJSONSync(path.join(__dirname, OUT_PATH, ".demoList.json"), demoSorted);
module.exports = {
+ publicDir: path.resolve(__dirname, "public"),
server: {
open: true,
host: "0.0.0.0",
diff --git a/e2e/case/animator-blendShape.ts b/e2e/case/animator-blendShape.ts
index 28641ca91f..27307ec95d 100644
--- a/e2e/case/animator-blendShape.ts
+++ b/e2e/case/animator-blendShape.ts
@@ -38,7 +38,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
const { defaultSceneRoot } = asset;
rootEntity.addChild(defaultSceneRoot);
const animator = defaultSceneRoot.getComponent(Animator);
- const skinMeshRenderer = defaultSceneRoot.getComponent(SkinnedMeshRenderer);
+ const skinMeshRenderer = defaultSceneRoot.getComponentsIncludeChildren(SkinnedMeshRenderer, [])[0];
skinMeshRenderer.blendShapeWeights[0] = 1.0;
animator.play("TheWave");
diff --git a/e2e/case/animator-multiSubMeshBlendShape.ts b/e2e/case/animator-multiSubMeshBlendShape.ts
index 546d9b8f3d..5c1365a758 100644
--- a/e2e/case/animator-multiSubMeshBlendShape.ts
+++ b/e2e/case/animator-multiSubMeshBlendShape.ts
@@ -45,8 +45,8 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
.then((asset) => {
const { defaultSceneRoot } = asset;
rootEntity.addChild(defaultSceneRoot);
- const entity = defaultSceneRoot;
- defaultSceneRoot.transform.rotation = new Vector3(-90, -0, 0);
+ const entity = defaultSceneRoot.children[0];
+ entity.transform.rotation = new Vector3(-90, -0, 0);
const animator = entity.addComponent(Animator);
animator.animatorController = new AnimatorController(engine);
diff --git a/e2e/case/gpu-instancing-auto-batch.ts b/e2e/case/gpu-instancing-auto-batch.ts
new file mode 100644
index 0000000000..e9f80e35ab
--- /dev/null
+++ b/e2e/case/gpu-instancing-auto-batch.ts
@@ -0,0 +1,68 @@
+/**
+ * @title GPU Instancing Auto Batch
+ * @category Mesh
+ */
+import {
+ AmbientLight,
+ AssetType,
+ Camera,
+ Color,
+ DirectLight,
+ GLTFResource,
+ Logger,
+ Vector3,
+ WebGLEngine
+} from "@galacean/engine";
+import { initScreenshot, updateForE2E } from "./.mockForE2E";
+
+Logger.enable();
+WebGLEngine.create({ canvas: "canvas" }).then(async (engine) => {
+ engine.canvas.resizeByClientSize(2);
+
+ const scene = engine.sceneManager.activeScene;
+ const rootEntity = scene.createRootEntity("Root");
+
+ // Camera
+ const cameraEntity = rootEntity.createChild("Camera");
+ cameraEntity.transform.setPosition(0, 10, 80);
+ cameraEntity.transform.lookAt(new Vector3(0, 0, 0));
+ const camera = cameraEntity.addComponent(Camera);
+ camera.farClipPlane = 300;
+
+ // Light
+ const lightEntity = rootEntity.createChild("Light");
+ lightEntity.transform.setRotation(-45, -45, 0);
+ lightEntity.addComponent(DirectLight).color = new Color(1, 1, 1, 1);
+
+ // Load Duck model and ambient light
+ const [glTF, ambientLight] = await Promise.all([
+ engine.resourceManager.load({
+ url: "https://gw.alipayobjects.com/os/bmw-prod/6cb8f543-285c-491a-8cfd-57a1160dc9ab.glb",
+ type: AssetType.GLTF
+ }),
+ engine.resourceManager.load({
+ url: "https://mdn.alipayobjects.com/oasis_be/afts/file/A*eRJ8QKzf5zAAAAAAgBAAAAgAekp5AQ/ambient.ambLight",
+ type: AssetType.AmbientLight
+ })
+ ]);
+ scene.ambientLight = ambientLight;
+
+ // Clone ducks with fixed seed positions for deterministic output
+ const count = 500;
+ const spread = 50;
+ for (let i = 0; i < count; i++) {
+ const duck = glTF.instantiateSceneRoot();
+ // Use deterministic positions based on index
+ const t = i / count;
+ duck.transform.setPosition(
+ Math.sin(t * 137.5) * spread * 0.5,
+ Math.cos(t * 97.3) * spread * 0.5,
+ Math.sin(t * 59.1) * spread * 0.5
+ );
+ duck.transform.setRotation(t * 360, t * 720, t * 1080);
+ rootEntity.addChild(duck);
+ }
+
+ updateForE2E(engine);
+ initScreenshot(engine, camera);
+});
diff --git a/e2e/case/gpu-instancing-custom-data.ts b/e2e/case/gpu-instancing-custom-data.ts
new file mode 100644
index 0000000000..f64b01a8f7
--- /dev/null
+++ b/e2e/case/gpu-instancing-custom-data.ts
@@ -0,0 +1,100 @@
+/**
+ * @title GPU Instancing Custom Data
+ * @category Mesh
+ */
+import {
+ Camera,
+ Color,
+ DirectLight,
+ Logger,
+ Material,
+ MeshRenderer,
+ PrimitiveMesh,
+ Shader,
+ ShaderProperty,
+ Vector3,
+ Vector4,
+ WebGLEngine
+} from "@galacean/engine";
+import { initScreenshot, updateForE2E } from "./.mockForE2E";
+
+Logger.enable();
+
+// Custom shader: uses renderer_CustomColor (per-instance) for fragment output
+Shader.create(
+ "CustomInstanceShader",
+ `
+ #include
+ attribute vec3 POSITION;
+ attribute vec3 NORMAL;
+
+ varying vec3 v_normal;
+
+ void main() {
+ gl_Position = renderer_MVPMat * vec4(POSITION, 1.0);
+ v_normal = normalize((renderer_NormalMat * vec4(NORMAL, 0.0)).xyz);
+ }
+ `,
+ `
+ uniform vec4 renderer_CustomColor;
+
+ varying vec3 v_normal;
+
+ void main() {
+ vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0));
+ float NdotL = max(dot(v_normal, lightDir), 0.2);
+ gl_FragColor = vec4(renderer_CustomColor.rgb * NdotL, 1.0);
+ }
+ `
+);
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize(2);
+
+ const scene = engine.sceneManager.activeScene;
+ const rootEntity = scene.createRootEntity("Root");
+
+ // Camera
+ const cameraEntity = rootEntity.createChild("Camera");
+ cameraEntity.transform.setPosition(0, 10, 80);
+ cameraEntity.transform.lookAt(new Vector3(0, 0, 0));
+ const camera = cameraEntity.addComponent(Camera);
+ camera.farClipPlane = 300;
+
+ // Light
+ const lightEntity = rootEntity.createChild("Light");
+ lightEntity.transform.setRotation(-45, -45, 0);
+ lightEntity.addComponent(DirectLight).color = new Color(1, 1, 1, 1);
+
+ // Shared mesh and material
+ const mesh = PrimitiveMesh.createCuboid(engine, 1, 1, 1);
+ const material = new Material(engine, Shader.find("CustomInstanceShader"));
+ const customColorProperty = ShaderProperty.getByName("renderer_CustomColor");
+
+ // Create cubes with deterministic positions and colors
+ const count = 500;
+ const spread = 50;
+ for (let i = 0; i < count; i++) {
+ const entity = rootEntity.createChild("Cube" + i);
+ const t = i / count;
+ entity.transform.setPosition(
+ Math.sin(t * 137.5) * spread * 0.5,
+ Math.cos(t * 97.3) * spread * 0.5,
+ Math.sin(t * 59.1) * spread * 0.5
+ );
+ entity.transform.setRotation(t * 360, t * 720, t * 1080);
+
+ const renderer = entity.addComponent(MeshRenderer);
+ renderer.mesh = mesh;
+ renderer.setMaterial(material);
+
+ // Deterministic colors based on index
+ renderer.shaderData.setVector4(
+ customColorProperty,
+ new Vector4(Math.sin(t * 6.28) * 0.5 + 0.5, Math.cos(t * 4.71) * 0.5 + 0.5, Math.sin(t * 3.14) * 0.5 + 0.5, 1.0)
+ );
+ }
+
+ updateForE2E(engine);
+ initScreenshot(engine, camera);
+});
diff --git a/e2e/case/particleRenderer-dream.ts b/e2e/case/particleRenderer-dream.ts
index e515bd4fbf..6eb51637ed 100644
--- a/e2e/case/particleRenderer-dream.ts
+++ b/e2e/case/particleRenderer-dream.ts
@@ -68,8 +68,8 @@ WebGLEngine.create({
cameraEntity.addChild(fireEntity);
- updateForE2E(engine, 500);
- initScreenshot(engine, camera);
+ // updateForE2E(engine, 500);
+ // initScreenshot(engine, camera);
});
});
diff --git a/e2e/case/shaderLab-mrt.ts b/e2e/case/shaderLab-mrt.ts
index 54afaf6494..a8f3a2c7cd 100644
--- a/e2e/case/shaderLab-mrt.ts
+++ b/e2e/case/shaderLab-mrt.ts
@@ -9,7 +9,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E";
const shaderLab = new ShaderLab();
-const shaderSource = `Shader "/custom.gs" {
+const shaderSource = `Shader "/custom.shader" {
SubShader "Default" {
UsePass "pbr/Default/ShadowCaster"
diff --git a/e2e/case/spine-spineboy.ts b/e2e/case/spine-spineboy.ts
new file mode 100644
index 0000000000..4ccfdc21bf
--- /dev/null
+++ b/e2e/case/spine-spineboy.ts
@@ -0,0 +1,36 @@
+/**
+ * @title Spine Spineboy
+ * @category Spine
+ */
+import { Camera, Logger, Vector3, WebGLEngine } from "@galacean/engine";
+import { SpineAnimationRenderer } from "@galacean/engine-spine";
+import "@galacean/engine-spine-core-4.2";
+import { initScreenshot, updateForE2E } from "./.mockForE2E";
+
+Logger.enable();
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+ const scene = engine.sceneManager.activeScene;
+ const root = scene.createRootEntity();
+
+ const cameraEntity = root.createChild("camera");
+ const camera = cameraEntity.addComponent(Camera);
+ cameraEntity.transform.setPosition(0, 0, 20);
+ cameraEntity.transform.lookAt(new Vector3(0, 0, 0));
+
+ engine.resourceManager
+ .load({
+ urls: ["/spineboy.json", "/spineboy.atlas", "/spineboy.png"],
+ type: "Spine"
+ })
+ .then((resource: any) => {
+ const spineEntity = resource.instantiate();
+ spineEntity.transform.setPosition(-0.5, -3.2, 0);
+ root.addChild(spineEntity);
+ spineEntity.getComponent(SpineAnimationRenderer).state.setAnimation(0, "idle", true);
+
+ updateForE2E(engine);
+ initScreenshot(engine, camera);
+ });
+});
diff --git a/e2e/case/spine-tint-black.ts b/e2e/case/spine-tint-black.ts
new file mode 100644
index 0000000000..b2c6c91504
--- /dev/null
+++ b/e2e/case/spine-tint-black.ts
@@ -0,0 +1,41 @@
+/**
+ * @title Spine TintBlack
+ * @category Spine
+ */
+import { Camera, Logger, Vector3, WebGLEngine } from "@galacean/engine";
+import { SpineAnimationRenderer } from "@galacean/engine-spine";
+import "@galacean/engine-spine-core-4.2";
+import { initScreenshot, updateForE2E } from "./.mockForE2E";
+
+Logger.enable();
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+ const scene = engine.sceneManager.activeScene;
+ const root = scene.createRootEntity();
+
+ const cameraEntity = root.createChild("camera");
+ const camera = cameraEntity.addComponent(Camera);
+ cameraEntity.transform.setPosition(0, 0, 60);
+ cameraEntity.transform.lookAt(new Vector3(0, 0, 0));
+
+ engine.resourceManager
+ .load({
+ urls: ["/tank-pro.json", "/tank-pro.atlas", "/tank-pro.png"],
+ type: "Spine"
+ })
+ .then((resource: any) => {
+ const spineEntity = resource.instantiate();
+ const spine = spineEntity.getComponent(SpineAnimationRenderer);
+ // tank-pro ships with per-slot darkColor; tintBlack renders the two-color tint.
+ spine.tintBlack = true;
+ spineEntity.transform.setPosition(3, -5, 0);
+ root.addChild(spineEntity);
+ spine.state.setAnimation(0, "shoot", true);
+
+ // Sample ~0.4s into "shoot": the muzzle-smoke darkColor makes the two-color tint
+ // clearly visible (tintBlack on/off differs ~7% here, vs ~0.01% on a static frame).
+ updateForE2E(engine, 40, 10);
+ initScreenshot(engine, camera);
+ });
+});
diff --git a/e2e/config.ts b/e2e/config.ts
index e5a9b621cf..d0142dcc29 100644
--- a/e2e/config.ts
+++ b/e2e/config.ts
@@ -1,4 +1,18 @@
export const E2E_CONFIG = {
+ Spine: {
+ spineboy: {
+ category: "Spine",
+ caseFileName: "spine-spineboy",
+ threshold: 0,
+ diffPercentage: 0.05
+ },
+ tintBlack: {
+ category: "Spine",
+ caseFileName: "spine-tint-black",
+ threshold: 0,
+ diffPercentage: 0.05
+ }
+ },
Animator: {
additive: {
category: "Animator",
diff --git a/e2e/fixtures/originImage/Animator_animator-crossfade.jpg b/e2e/fixtures/originImage/Animator_animator-crossfade.jpg
index ab638de414..c71cd431c8 100644
--- a/e2e/fixtures/originImage/Animator_animator-crossfade.jpg
+++ b/e2e/fixtures/originImage/Animator_animator-crossfade.jpg
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e18021c1e0a3b682259046e812eba199c907ffd9a634dda12379f1f0c3b8b43a
-size 45710
+oid sha256:a1dfa2435f26222617b08ce2830ca28d8d84e083678dde1a9d945479c7b6d019
+size 45716
diff --git a/e2e/fixtures/originImage/Animator_animator-play.jpg b/e2e/fixtures/originImage/Animator_animator-play.jpg
index 79170b806b..ddfb02c4e7 100644
--- a/e2e/fixtures/originImage/Animator_animator-play.jpg
+++ b/e2e/fixtures/originImage/Animator_animator-play.jpg
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4910985a371b9022e73d2a7efbd71ca6754bcc7bf4c25edc2a41391f1bb1533b
-size 46499
+oid sha256:82d8ba4776b6b27b923d0f1f0094d48d0d4c59e61e8666275690fec70c39e854
+size 46527
diff --git a/e2e/fixtures/originImage/Camera_camera-opaque-texture.jpg b/e2e/fixtures/originImage/Camera_camera-opaque-texture.jpg
index bcc2d5f331..1d409ae544 100644
--- a/e2e/fixtures/originImage/Camera_camera-opaque-texture.jpg
+++ b/e2e/fixtures/originImage/Camera_camera-opaque-texture.jpg
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fdde03beb8ead802b61a14e8649102c18ccfe6b9e1e176d3b9f8bc0dfe7f9c76
-size 47500
+oid sha256:a2068be8d0b35c94ec1ddaed9da63494f1a93294ea6128e2036563df1947e854
+size 47482
diff --git a/e2e/fixtures/originImage/GPUInstancing_gpu-instancing-auto-batch.jpg b/e2e/fixtures/originImage/GPUInstancing_gpu-instancing-auto-batch.jpg
new file mode 100644
index 0000000000..ecec018d8c
--- /dev/null
+++ b/e2e/fixtures/originImage/GPUInstancing_gpu-instancing-auto-batch.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dc2a26d0ad06c2dfa722484b2e914b43c4c6671c76ec31d18715dc4a89ec1d88
+size 590047
diff --git a/e2e/fixtures/originImage/GPUInstancing_gpu-instancing-custom-data.jpg b/e2e/fixtures/originImage/GPUInstancing_gpu-instancing-custom-data.jpg
new file mode 100644
index 0000000000..79399c097c
--- /dev/null
+++ b/e2e/fixtures/originImage/GPUInstancing_gpu-instancing-custom-data.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:baece2c4437803e1474699eb4e99214b38caa05393f747fe0f7e47bd6469a693
+size 427005
diff --git a/e2e/fixtures/originImage/Physics_physx-collision.jpg b/e2e/fixtures/originImage/Physics_physx-collision.jpg
deleted file mode 100644
index b1e9becba9..0000000000
--- a/e2e/fixtures/originImage/Physics_physx-collision.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:55cd5d3035ef575743d7332317b571ea2403f9330c918a3c45e96dbbe853e164
-size 39366
diff --git a/e2e/fixtures/originImage/Physics_physx-customUrl.jpg b/e2e/fixtures/originImage/Physics_physx-customUrl.jpg
deleted file mode 100644
index b1e9becba9..0000000000
--- a/e2e/fixtures/originImage/Physics_physx-customUrl.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:55cd5d3035ef575743d7332317b571ea2403f9330c918a3c45e96dbbe853e164
-size 39366
diff --git a/e2e/fixtures/originImage/Physics_physx-mesh-collider-data.jpg b/e2e/fixtures/originImage/Physics_physx-mesh-collider-data.jpg
deleted file mode 100644
index 6bdbf7360d..0000000000
--- a/e2e/fixtures/originImage/Physics_physx-mesh-collider-data.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e62ca32193b97c4a47ce59666a4f7754f0f20486e055ac3998de2e07aa4d1272
-size 128943
diff --git a/e2e/fixtures/originImage/Spine_spine-spineboy.jpg b/e2e/fixtures/originImage/Spine_spine-spineboy.jpg
new file mode 100644
index 0000000000..76065431eb
--- /dev/null
+++ b/e2e/fixtures/originImage/Spine_spine-spineboy.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:58b5f1a156b82707ead037eb64d0b130534834c146d907b45be9b0cb16dbed49
+size 74591
diff --git a/e2e/fixtures/originImage/Spine_spine-tint-black.jpg b/e2e/fixtures/originImage/Spine_spine-tint-black.jpg
new file mode 100644
index 0000000000..410a970ccf
--- /dev/null
+++ b/e2e/fixtures/originImage/Spine_spine-tint-black.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:51fc0948e2ce8b819093526b5665b10e9975a810d4c1975fa661c091f2842e81
+size 122845
diff --git a/e2e/package.json b/e2e/package.json
index 34a2f56922..fb743e744a 100644
--- a/e2e/package.json
+++ b/e2e/package.json
@@ -1,7 +1,7 @@
{
"name": "@galacean/engine-e2e",
"private": true,
- "version": "2.0.0-alpha.24",
+ "version": "0.0.0-experimental-2.0-game.19",
"license": "MIT",
"scripts": {
"case": "vite serve .dev --config .dev/vite.config.js",
@@ -20,6 +20,8 @@
"@galacean/engine-rhi-webgl": "workspace:*",
"@galacean/engine-physics-lite": "workspace:*",
"@galacean/engine-physics-physx": "workspace:*",
+ "@galacean/engine-spine": "workspace:*",
+ "@galacean/engine-spine-core-4.2": "workspace:*",
"dat.gui": "^0.7.9",
"vite": "3.1.6",
"sass": "^1.55.0"
diff --git a/examples/package.json b/examples/package.json
index a57e6c4187..d0847d14a4 100644
--- a/examples/package.json
+++ b/examples/package.json
@@ -1,6 +1,6 @@
{
"name": "@galacean/engine-examples",
- "version": "2.0.0-alpha.24",
+ "version": "0.0.0-experimental-2.0-game.19",
"private": true,
"license": "MIT",
"main": "dist/main.js",
@@ -21,11 +21,15 @@
"@galacean/engine-rhi-webgl": "workspace:*",
"@galacean/engine-shader": "workspace:*",
"@galacean/engine-shaderlab": "workspace:*",
+ "@galacean/engine-spine": "workspace:*",
+ "@galacean/engine-spine-core-3.8": "workspace:*",
+ "@galacean/engine-spine-core-4.2": "workspace:*",
"@galacean/engine-toolkit": "latest",
+ "@galacean/engine-toolkit-stats": "latest",
"@galacean/engine-ui": "workspace:*"
},
"devDependencies": {
"dat.gui": "^0.7.9",
"vite": "^4.4.4"
}
-}
\ No newline at end of file
+}
diff --git a/examples/src/gpu-instancing-auto-batch.ts b/examples/src/gpu-instancing-auto-batch.ts
new file mode 100644
index 0000000000..d7aa1b9f80
--- /dev/null
+++ b/examples/src/gpu-instancing-auto-batch.ts
@@ -0,0 +1,194 @@
+/**
+ * @title GPU Instancing Auto Batch
+ * @category Mesh
+ * @thumbnail https://mdn.alipayobjects.com/merchant_appfe/afts/img/A*jjZMTrp-vU8AAAAAAAAAAAAADiR2AQ/original
+ */
+import { OrbitControl, Stats } from "@galacean/engine-toolkit";
+import {
+ AmbientLight,
+ AssetType,
+ Camera,
+ Color,
+ DirectLight,
+ GLTFResource,
+ Logger,
+ Material,
+ MeshRenderer,
+ PrimitiveMesh,
+ Script,
+ Shader,
+ ShaderProperty,
+ Vector3,
+ Vector4,
+ WebGLEngine,
+ WebGLMode
+} from "@galacean/engine";
+
+const _customColorProperty = ShaderProperty.getByName("renderer_CustomColor");
+
+class SpiralAnimate extends Script {
+ radius: number = 0;
+ radiusSpeed: number = 0;
+ theta: number = 0;
+ thetaSpeed: number = 0;
+ phi: number = 0;
+ phiSpeed: number = 0;
+ rotateSpeed: Vector3 = new Vector3();
+ scaleBase: number = 1;
+ scaleFreq: number = 0;
+ colorPhase: number = 0;
+ colorSpeed: number = 0;
+ private _time: number = 0;
+ private _color: Vector4 = new Vector4();
+ private _hasColor: boolean = false;
+
+ onUpdate(deltaTime: number): void {
+ this._time += deltaTime;
+ const t = this._time;
+ const transform = this.entity.transform;
+
+ // Spiral breathing motion
+ const r = this.radius * (0.6 + 0.4 * Math.sin(t * this.radiusSpeed));
+ const theta = this.theta + t * this.thetaSpeed;
+ const phi = this.phi + t * this.phiSpeed;
+
+ const sinTheta = Math.sin(theta);
+ transform.setPosition(r * sinTheta * Math.cos(phi), r * Math.cos(theta), r * sinTheta * Math.sin(phi));
+
+ // Rotation
+ const { rotateSpeed } = this;
+ transform.rotate(rotateSpeed.x * deltaTime, rotateSpeed.y * deltaTime, rotateSpeed.z * deltaTime);
+
+ // Scale pulse
+ const s = this.scaleBase * (0.7 + 0.3 * Math.sin(t * this.scaleFreq));
+ transform.setScale(s, s, s);
+
+ // Color animation (cubes only)
+ if (this._hasColor) {
+ const ct = t * this.colorSpeed + this.colorPhase;
+ this._color.set(
+ 0.5 + 0.5 * Math.sin(ct),
+ 0.5 + 0.5 * Math.sin(ct + 2.094),
+ 0.5 + 0.5 * Math.sin(ct + 4.189),
+ 1.0
+ );
+ this.entity.getComponent(MeshRenderer).shaderData.setVector4(_customColorProperty, this._color);
+ }
+ }
+
+ enableColor(): void {
+ this._hasColor = true;
+ }
+}
+
+// Custom shader for cubes
+Shader.create(
+ "CustomInstanceShader",
+ `
+ #include
+ attribute vec3 POSITION;
+ attribute vec3 NORMAL;
+
+ varying vec3 v_normal;
+
+ void main() {
+ gl_Position = renderer_MVPMat * vec4(POSITION, 1.0);
+ v_normal = normalize((renderer_NormalMat * vec4(NORMAL, 0.0)).xyz);
+ }
+ `,
+ `
+ uniform vec4 renderer_CustomColor;
+
+ varying vec3 v_normal;
+
+ void main() {
+ vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0));
+ float NdotL = max(dot(v_normal, lightDir), 0.2);
+ gl_FragColor = vec4(renderer_CustomColor.rgb * NdotL, 1.0);
+ }
+ `
+);
+
+WebGLEngine.create({ canvas: "canvas", graphicDeviceOptions: { webGLMode: WebGLMode.WebGL2 } }).then(async (engine) => {
+ engine.canvas.resizeByClientSize();
+
+ const scene = engine.sceneManager.activeScene;
+ const rootEntity = scene.createRootEntity("Root");
+
+ // Camera
+ const cameraEntity = rootEntity.createChild("Camera");
+ cameraEntity.transform.setPosition(0, 0, 100);
+ cameraEntity.transform.lookAt(new Vector3(0, 0, 0));
+ const camera = cameraEntity.addComponent(Camera);
+ camera.farClipPlane = 500;
+ cameraEntity.addComponent(OrbitControl);
+
+ // Stats
+ cameraEntity.addComponent(Stats);
+
+ // Light
+ const lightEntity = rootEntity.createChild("Light");
+ lightEntity.transform.setRotation(-45, -45, 0);
+ lightEntity.addComponent(DirectLight).color = new Color(1, 1, 1, 1);
+
+ // Load Duck model and ambient light
+ const [glTF, ambientLight] = await Promise.all([
+ engine.resourceManager.load({
+ url: "https://mdn.alipayobjects.com/rms/afts/file/A*9R-_TY9K_6oAAAAAgIAAAAgAehQnAQ/Avocado.glb",
+ type: AssetType.GLTF
+ }),
+ engine.resourceManager.load({
+ url: "https://mdn.alipayobjects.com/oasis_be/afts/file/A*eRJ8QKzf5zAAAAAAgBAAAAgAekp5AQ/ambient.ambLight",
+ type: AssetType.AmbientLight
+ })
+ ]);
+ scene.ambientLight = ambientLight;
+
+ // Cube resources
+ const cubeMesh = PrimitiveMesh.createCuboid(engine, 1, 1, 1);
+ const cubeMaterial = new Material(engine, Shader.find("CustomInstanceShader"));
+ const customColorProperty = ShaderProperty.getByName("renderer_CustomColor");
+
+ // Interleave ducks and cubes to break batching — instancing shines here
+ const duckCount = 2500;
+ const cubeCount = 2500;
+ const totalCount = duckCount + cubeCount;
+
+ for (let i = 0; i < totalCount; i++) {
+ const ti = i / totalCount;
+ const isDuck = i % 2 === 0 && i / 2 < duckCount;
+ const isCube = !isDuck;
+
+ let entity;
+ if (isDuck) {
+ entity = glTF.instantiateSceneRoot();
+ rootEntity.addChild(entity);
+ } else {
+ entity = rootEntity.createChild("Cube" + i);
+ const renderer = entity.addComponent(MeshRenderer);
+ renderer.mesh = cubeMesh;
+ renderer.setMaterial(cubeMaterial);
+ const initColor = new Vector4(Math.random(), Math.random(), Math.random(), 1.0);
+ renderer.shaderData.setVector4(customColorProperty, initColor);
+ }
+
+ const anim = entity.addComponent(SpiralAnimate);
+ anim.radius = 10 + Math.random() * 40;
+ anim.radiusSpeed = 0.3 + Math.random() * 0.6;
+ anim.theta = ti * Math.PI * 2 * 13.7;
+ anim.phi = ti * Math.PI * 2 * 7.3;
+ anim.thetaSpeed = (0.2 + Math.random() * 0.4) * (Math.random() > 0.5 ? 1 : -1);
+ anim.phiSpeed = (0.3 + Math.random() * 0.5) * (Math.random() > 0.5 ? 1 : -1);
+ anim.rotateSpeed = new Vector3((Math.random() - 0.5) * 60, (Math.random() - 0.5) * 60, (Math.random() - 0.5) * 60);
+ anim.scaleBase = (isDuck ? 20 : 1) * (0.6 + Math.random() * 0.8);
+ anim.scaleFreq = 0.5 + Math.random() * 2;
+
+ if (isCube) {
+ anim.colorPhase = Math.random() * Math.PI * 2;
+ anim.colorSpeed = 0.5 + Math.random() * 2;
+ anim.enableColor();
+ }
+ }
+
+ engine.run();
+});
diff --git a/examples/src/gpu-instancing-custom-data.ts b/examples/src/gpu-instancing-custom-data.ts
new file mode 100644
index 0000000000..27857d5730
--- /dev/null
+++ b/examples/src/gpu-instancing-custom-data.ts
@@ -0,0 +1,151 @@
+/**
+ * @title GPU Instancing Custom Data
+ * @category Mesh
+ * @thumbnail https://mdn.alipayobjects.com/merchant_appfe/afts/img/A*jjZMTrp-vU8AAAAAAAAAAAAADiR2AQ/original
+ */
+import { OrbitControl, Stats } from "@galacean/engine-toolkit";
+import {
+ Camera,
+ Color,
+ DirectLight,
+ Logger,
+ Material,
+ MeshRenderer,
+ PrimitiveMesh,
+ Script,
+ Shader,
+ ShaderProperty,
+ Vector3,
+ Vector4,
+ WebGLEngine,
+ WebGLMode
+} from "@galacean/engine";
+
+const _customColorProperty = ShaderProperty.getByName("renderer_CustomColor");
+
+class SpiralFlash extends Script {
+ radius: number = 0;
+ radiusSpeed: number = 0;
+ theta: number = 0;
+ thetaSpeed: number = 0;
+ phi: number = 0;
+ phiSpeed: number = 0;
+ rotateSpeed: Vector3 = new Vector3();
+ scaleBase: number = 1;
+ scaleFreq: number = 0;
+ colorPhase: number = 0;
+ colorSpeed: number = 1;
+ private _time: number = 0;
+ private _color: Vector4 = new Vector4();
+
+ onUpdate(deltaTime: number): void {
+ this._time += deltaTime;
+ const t = this._time;
+ const transform = this.entity.transform;
+
+ // Spiral breathing motion
+ const r = this.radius * (0.6 + 0.4 * Math.sin(t * this.radiusSpeed));
+ const theta = this.theta + t * this.thetaSpeed;
+ const phi = this.phi + t * this.phiSpeed;
+
+ const sinTheta = Math.sin(theta);
+ transform.setPosition(r * sinTheta * Math.cos(phi), r * Math.cos(theta), r * sinTheta * Math.sin(phi));
+
+ // Rotation
+ const { rotateSpeed } = this;
+ transform.rotate(rotateSpeed.x * deltaTime, rotateSpeed.y * deltaTime, rotateSpeed.z * deltaTime);
+
+ // Scale pulse
+ const s = this.scaleBase * (0.7 + 0.3 * Math.sin(t * this.scaleFreq));
+ transform.setScale(s, s, s);
+
+ // Color cycles through hue based on time + unique phase
+ const ct = t * this.colorSpeed + this.colorPhase;
+ this._color.set(0.5 + 0.5 * Math.sin(ct), 0.5 + 0.5 * Math.sin(ct + 2.094), 0.5 + 0.5 * Math.sin(ct + 4.189), 1.0);
+ this.entity.getComponent(MeshRenderer).shaderData.setVector4(_customColorProperty, this._color);
+ }
+}
+
+Logger.enable();
+
+Shader.create(
+ "CustomInstanceShader",
+ `
+ #include
+ attribute vec3 POSITION;
+ attribute vec3 NORMAL;
+
+ varying vec3 v_normal;
+
+ void main() {
+ gl_Position = renderer_MVPMat * vec4(POSITION, 1.0);
+ v_normal = normalize((renderer_NormalMat * vec4(NORMAL, 0.0)).xyz);
+ }
+ `,
+ `
+ uniform vec4 renderer_CustomColor;
+
+ varying vec3 v_normal;
+
+ void main() {
+ vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0));
+ float NdotL = max(dot(v_normal, lightDir), 0.2);
+ gl_FragColor = vec4(renderer_CustomColor.rgb * NdotL, 1.0);
+ }
+ `
+);
+
+WebGLEngine.create({ canvas: "canvas", graphicDeviceOptions: { webGLMode: WebGLMode.WebGL2 } }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+
+ const scene = engine.sceneManager.activeScene;
+ const rootEntity = scene.createRootEntity("Root");
+
+ // Camera
+ const cameraEntity = rootEntity.createChild("Camera");
+ cameraEntity.transform.setPosition(0, 0, 100);
+ cameraEntity.transform.lookAt(new Vector3(0, 0, 0));
+ const camera = cameraEntity.addComponent(Camera);
+ camera.farClipPlane = 500;
+ cameraEntity.addComponent(OrbitControl);
+
+ // Stats
+ cameraEntity.addComponent(Stats);
+
+ // Light
+ const lightEntity = rootEntity.createChild("Light");
+ lightEntity.transform.setRotation(-45, -45, 0);
+ lightEntity.addComponent(DirectLight).color = new Color(1, 1, 1, 1);
+
+ const mesh = PrimitiveMesh.createCuboid(engine, 1, 1, 1);
+ const material = new Material(engine, Shader.find("CustomInstanceShader"));
+ const customColorProperty = ShaderProperty.getByName("renderer_CustomColor");
+
+ const count = 5000;
+ for (let i = 0; i < count; i++) {
+ const entity = rootEntity.createChild("Cube" + i);
+ const ti = i / count;
+
+ const renderer = entity.addComponent(MeshRenderer);
+ renderer.mesh = mesh;
+ renderer.setMaterial(material);
+
+ const initColor = new Vector4(Math.random(), Math.random(), Math.random(), 1.0);
+ renderer.shaderData.setVector4(customColorProperty, initColor);
+
+ const anim = entity.addComponent(SpiralFlash);
+ anim.radius = 10 + Math.random() * 40;
+ anim.radiusSpeed = 0.3 + Math.random() * 0.6;
+ anim.theta = ti * Math.PI * 2 * 13.7;
+ anim.phi = ti * Math.PI * 2 * 7.3;
+ anim.thetaSpeed = (0.2 + Math.random() * 0.4) * (Math.random() > 0.5 ? 1 : -1);
+ anim.phiSpeed = (0.3 + Math.random() * 0.5) * (Math.random() > 0.5 ? 1 : -1);
+ anim.rotateSpeed = new Vector3((Math.random() - 0.5) * 60, (Math.random() - 0.5) * 60, (Math.random() - 0.5) * 60);
+ anim.scaleBase = 0.6 + Math.random() * 0.8;
+ anim.scaleFreq = 0.5 + Math.random() * 2;
+ anim.colorPhase = Math.random() * Math.PI * 2;
+ anim.colorSpeed = 0.5 + Math.random() * 2;
+ }
+
+ engine.run();
+});
diff --git a/examples/src/spine-keli-4.2.ts b/examples/src/spine-keli-4.2.ts
new file mode 100644
index 0000000000..8ad862a4a0
--- /dev/null
+++ b/examples/src/spine-keli-4.2.ts
@@ -0,0 +1,60 @@
+/**
+ * @title Spine Keli (4.2)
+ * @category Spine
+ */
+import { Camera, Entity, Logger, Vector3, WebGLEngine } from "@galacean/engine";
+import { SpineAnimationRenderer, SpineResource } from "@galacean/engine-spine";
+import "@galacean/engine-spine-core-4.2";
+import * as dat from "dat.gui";
+
+Logger.enable();
+
+const gui = new dat.GUI();
+const state = { animation: "" };
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+ const scene = engine.sceneManager.activeScene;
+ const root = scene.createRootEntity();
+
+ const cameraEntity = root.createChild("camera");
+ const camera = cameraEntity.addComponent(Camera);
+ cameraEntity.transform.setPosition(0, 2, 10);
+ cameraEntity.transform.lookAt(new Vector3(0, 2, 0));
+
+ engine.run();
+
+ engine.resourceManager
+ .load({
+ urls: [
+ "/spine/keli/keli.json",
+ "/spine/keli/keli.atlas",
+ "/spine/keli/images/arm_l.png",
+ "/spine/keli/images/arm_r.png",
+ "/spine/keli/images/head.png",
+ "/spine/keli/images/legs.png",
+ "/spine/keli/images/torso.png"
+ ],
+ type: "Spine"
+ })
+ .then((resource) => {
+ const spineEntity: Entity = resource.instantiate();
+ root.addChild(spineEntity);
+
+ const animator = spineEntity.getComponent(SpineAnimationRenderer);
+ const animationNames = resource.skeletonData.animations.map((animation) => animation.name);
+
+ state.animation = animationNames[0];
+ animator.state.setAnimation(0, state.animation, true);
+
+ gui
+ .add(state, "animation", animationNames)
+ .name("Animation")
+ .onChange((name: string) => {
+ animator.state.setAnimation(0, name, true);
+ });
+ })
+ .catch((error) => {
+ console.error("Failed to load keli (4.2):", error);
+ });
+});
diff --git a/examples/src/spine-otakugirl-3.8.ts b/examples/src/spine-otakugirl-3.8.ts
new file mode 100644
index 0000000000..410db286dd
--- /dev/null
+++ b/examples/src/spine-otakugirl-3.8.ts
@@ -0,0 +1,55 @@
+/**
+ * @title Spine Otakugirl (3.8)
+ * @category Spine
+ * @remarks
+ * A genuine spine 3.8.99 export (binary .skel), unlike the keli asset used in the 4.2 example —
+ * this verifies the 3.8 backend against data it's actually meant to parse.
+ */
+import { Camera, Entity, Logger, Vector3, WebGLEngine } from "@galacean/engine";
+import { SpineAnimationRenderer, SpineResource } from "@galacean/engine-spine";
+import "@galacean/engine-spine-core-3.8";
+import * as dat from "dat.gui";
+
+Logger.enable();
+
+const gui = new dat.GUI();
+const state = { animation: "" };
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+ const scene = engine.sceneManager.activeScene;
+ const root = scene.createRootEntity();
+
+ const cameraEntity = root.createChild("camera");
+ const camera = cameraEntity.addComponent(Camera);
+ cameraEntity.transform.setPosition(0, 2, 10);
+ cameraEntity.transform.lookAt(new Vector3(0, 2, 0));
+
+ engine.run();
+
+ engine.resourceManager
+ .load({
+ urls: ["/spine/otakugirl/otakugirl.skel", "/spine/otakugirl/otakugirl.atlas", "/spine/otakugirl/otakugirl.png"],
+ type: "Spine"
+ })
+ .then((resource) => {
+ const spineEntity: Entity = resource.instantiate();
+ root.addChild(spineEntity);
+
+ const animator = spineEntity.getComponent(SpineAnimationRenderer);
+ const animationNames = resource.skeletonData.animations.map((animation) => animation.name);
+
+ state.animation = animationNames[0];
+ animator.state.setAnimation(0, state.animation, true);
+
+ gui
+ .add(state, "animation", animationNames)
+ .name("Animation")
+ .onChange((name: string) => {
+ animator.state.setAnimation(0, name, true);
+ });
+ })
+ .catch((error) => {
+ console.error("Failed to load otakugirl (3.8):", error);
+ });
+});
diff --git a/examples/src/sprite-mask.ts b/examples/src/sprite-mask.ts
new file mode 100644
index 0000000000..f8dc719105
--- /dev/null
+++ b/examples/src/sprite-mask.ts
@@ -0,0 +1,87 @@
+/**
+ * @title Sprite Mask
+ * @category 2D
+ */
+import {
+ AssetType,
+ Camera,
+ Sprite,
+ SpriteMask,
+ SpriteMaskInteraction,
+ SpriteMaskLayer,
+ SpriteRenderer,
+ Texture2D,
+ WebGLEngine
+} from "@galacean/engine";
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+
+ const scene = engine.sceneManager.activeScene;
+ scene.background.solidColor.set(0.05, 0.05, 0.07, 1);
+ const root = scene.createRootEntity("Root");
+
+ const cameraEntity = root.createChild("Camera");
+ cameraEntity.transform.setPosition(0, 0, 50);
+ cameraEntity.addComponent(Camera);
+
+ engine.resourceManager
+ .load({
+ url: "https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*ApFPTZSqcMkAAAAAAAAAAAAAARQnAQ",
+ type: AssetType.Texture2D
+ })
+ .then((texture) => {
+ const sprite = new Sprite(engine, texture);
+ const maskSprite = new Sprite(engine, createSolidTexture(engine));
+
+ const spriteWidth = sprite.width;
+ const spriteHeight = sprite.height;
+ // Mask covers ~half of the sprite so the cut is obvious.
+ const maskWidth = spriteWidth * 0.6;
+ const maskHeight = spriteHeight * 0.6;
+ // Lay the two characters out side by side based on sprite size.
+ const groupOffsetX = spriteWidth * 0.6;
+
+ // --- Left: VisibleInsideMask -> only the part covered by the square mask is visible ---
+ const leftGroup = root.createChild("LeftGroup");
+ leftGroup.transform.setPosition(-groupOffsetX, 0, 0);
+
+ const leftMaskEntity = leftGroup.createChild("Mask");
+ const leftMask = leftMaskEntity.addComponent(SpriteMask);
+ leftMask.sprite = maskSprite;
+ leftMask.width = maskWidth;
+ leftMask.height = maskHeight;
+ leftMask.influenceLayers = SpriteMaskLayer.Layer0;
+
+ const leftSpriteEntity = leftGroup.createChild("Sprite");
+ const leftSprite = leftSpriteEntity.addComponent(SpriteRenderer);
+ leftSprite.sprite = sprite;
+ leftSprite.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
+ leftSprite.maskLayer = SpriteMaskLayer.Layer0;
+
+ // --- Right: VisibleOutsideMask -> character with a square hole punched out ---
+ const rightGroup = root.createChild("RightGroup");
+ rightGroup.transform.setPosition(groupOffsetX, 0, 0);
+
+ const rightMaskEntity = rightGroup.createChild("Mask");
+ const rightMask = rightMaskEntity.addComponent(SpriteMask);
+ rightMask.sprite = maskSprite;
+ rightMask.width = maskWidth;
+ rightMask.height = maskHeight;
+ rightMask.influenceLayers = SpriteMaskLayer.Layer1;
+
+ const rightSpriteEntity = rightGroup.createChild("Sprite");
+ const rightSprite = rightSpriteEntity.addComponent(SpriteRenderer);
+ rightSprite.sprite = sprite;
+ rightSprite.maskInteraction = SpriteMaskInteraction.VisibleOutsideMask;
+ rightSprite.maskLayer = SpriteMaskLayer.Layer1;
+ });
+
+ engine.run();
+});
+
+function createSolidTexture(engine: WebGLEngine): Texture2D {
+ const texture = new Texture2D(engine, 1, 1);
+ texture.setPixelBuffer(new Uint8Array([255, 255, 255, 255]));
+ return texture;
+}
diff --git a/examples/src/sprite-renderer-filled.ts b/examples/src/sprite-renderer-filled.ts
new file mode 100644
index 0000000000..e31504875f
--- /dev/null
+++ b/examples/src/sprite-renderer-filled.ts
@@ -0,0 +1,146 @@
+/**
+ * @title Sprite Filled
+ * @category 2D
+ */
+
+import * as dat from "dat.gui";
+import {
+ AssetType,
+ Camera,
+ Sprite,
+ SpriteDrawMode,
+ SpriteFilledMode,
+ SpriteFilledOrigin,
+ SpriteRenderer,
+ Texture2D,
+ Vector3,
+ WebGLEngine
+} from "@galacean/engine";
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+
+ const scene = engine.sceneManager.activeScene;
+ scene.background.solidColor.set(0.15, 0.15, 0.18, 1);
+ const rootEntity = scene.createRootEntity();
+
+ // Create camera
+ const cameraEntity = rootEntity.createChild("camera");
+ cameraEntity.transform.setPosition(0, 0, 50);
+ cameraEntity.addComponent(Camera);
+
+ // Load texture and create sprite
+ engine.resourceManager
+ .load({
+ url: "https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*ApFPTZSqcMkAAAAAAAAAAAAAARQnAQ",
+ type: AssetType.Texture2D
+ })
+ .then((texture) => {
+ const spriteEntity = rootEntity.createChild("sprite");
+ spriteEntity.transform.position = new Vector3(0, 0, 0);
+ spriteEntity.transform.setScale(2, 2, 2);
+ const renderer = spriteEntity.addComponent(SpriteRenderer);
+ renderer.sprite = new Sprite(engine, texture);
+
+ // Set filled mode
+ renderer.drawMode = SpriteDrawMode.Filled;
+ renderer.filledMode = SpriteFilledMode.Radial360;
+ renderer.filledOrigin = SpriteFilledOrigin.Bottom;
+ renderer.filledAmount = 0.75;
+ renderer.filledClockWise = true;
+
+ addGUI(renderer);
+ });
+
+ engine.run();
+
+ function addGUI(renderer: SpriteRenderer) {
+ const gui = new dat.GUI();
+
+ const filledModeMap: Record = {
+ Horizontal: SpriteFilledMode.Horizontal,
+ Vertical: SpriteFilledMode.Vertical,
+ Radial90: SpriteFilledMode.Radial90,
+ Radial180: SpriteFilledMode.Radial180,
+ Radial360: SpriteFilledMode.Radial360
+ };
+
+ const originForRadial360: string[] = ["Right", "Top", "Left", "Bottom"];
+ const originForRadial180: string[] = ["Right", "Top", "Left", "Bottom"];
+ const originForRadial90: string[] = ["BottomLeft", "BottomRight", "TopRight", "TopLeft"];
+ const originForHorizontal: string[] = ["Left", "Right"];
+ const originForVertical: string[] = ["Bottom", "Top"];
+
+ const originMap: Record = {
+ Right: SpriteFilledOrigin.Right,
+ TopRight: SpriteFilledOrigin.TopRight,
+ Top: SpriteFilledOrigin.Top,
+ TopLeft: SpriteFilledOrigin.TopLeft,
+ Left: SpriteFilledOrigin.Left,
+ BottomLeft: SpriteFilledOrigin.BottomLeft,
+ Bottom: SpriteFilledOrigin.Bottom,
+ BottomRight: SpriteFilledOrigin.BottomRight
+ };
+
+ const state = {
+ filledMode: "Radial360",
+ origin: "Bottom",
+ amount: 0.75,
+ clockWise: true
+ };
+
+ const folder = gui.addFolder("Filled Sprite");
+ folder.open();
+
+ // Filled mode
+ folder.add(state, "filledMode", Object.keys(filledModeMap)).onChange((value: string) => {
+ renderer.filledMode = filledModeMap[value];
+ updateOriginOptions(value);
+ });
+
+ // Origin
+ let originCtrl = folder.add(state, "origin", originForRadial360).onChange((value: string) => {
+ renderer.filledOrigin = originMap[value];
+ });
+
+ // Amount
+ folder.add(state, "amount", 0.0, 1.0, 0.01).onChange((value: number) => {
+ renderer.filledAmount = value;
+ });
+
+ // ClockWise
+ folder.add(state, "clockWise").onChange((value: boolean) => {
+ renderer.filledClockWise = value;
+ });
+
+ function updateOriginOptions(mode: string) {
+ folder.remove(originCtrl);
+
+ let options: string[];
+ switch (mode) {
+ case "Horizontal":
+ options = originForHorizontal;
+ break;
+ case "Vertical":
+ options = originForVertical;
+ break;
+ case "Radial90":
+ options = originForRadial90;
+ break;
+ case "Radial180":
+ options = originForRadial180;
+ break;
+ default:
+ options = originForRadial360;
+ break;
+ }
+
+ state.origin = options[0];
+ renderer.filledOrigin = originMap[state.origin];
+
+ originCtrl = folder.add(state, "origin", options).onChange((value: string) => {
+ renderer.filledOrigin = originMap[value];
+ });
+ }
+ }
+});
diff --git a/examples/src/ui-mask-alpha.ts b/examples/src/ui-mask-alpha.ts
new file mode 100644
index 0000000000..9b5f340fd0
--- /dev/null
+++ b/examples/src/ui-mask-alpha.ts
@@ -0,0 +1,97 @@
+/**
+ * @title UI Mask Alpha Cutoff
+ * @category UI
+ */
+import * as dat from "dat.gui";
+import { Camera, Color, Sprite, SpriteMaskInteraction, Texture2D, TextureFormat, WebGLEngine } from "@galacean/engine";
+import { CanvasRenderMode, Image, Mask, Text, UICanvas, UITransform } from "@galacean/engine-ui";
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+
+ const scene = engine.sceneManager.activeScene;
+ scene.background.solidColor = new Color(0.03, 0.04, 0.07, 1);
+ const root = scene.createRootEntity("Root");
+
+ const cameraEntity = root.createChild("Camera");
+ cameraEntity.transform.setPosition(0, 0, 10);
+ const camera = cameraEntity.addComponent(Camera);
+
+ const canvasEntity = root.createChild("UICanvas");
+ const uiCanvas = canvasEntity.addComponent(UICanvas);
+ uiCanvas.renderMode = CanvasRenderMode.ScreenSpaceCamera;
+ uiCanvas.camera = camera;
+ uiCanvas.referenceResolutionPerUnit = 100;
+ uiCanvas.referenceResolution.set(1200, 800);
+
+ const solidSprite = createSolidSprite(engine);
+ const circleSprite = createCircleSprite(engine, 256);
+
+ const groupEntity = canvasEntity.createChild("Group");
+ (groupEntity.transform).setPosition(0, 40, 0);
+
+ // Circular mask
+ const maskEntity = groupEntity.createChild("Mask");
+ (maskEntity.transform).size.set(300, 300);
+ const mask = maskEntity.addComponent(Mask);
+ mask.sprite = circleSprite;
+ mask.alphaCutoff = 0.5;
+
+ // Background visible inside circle
+ const insideEntity = groupEntity.createChild("Inside");
+ (insideEntity.transform).size.set(500, 500);
+ const inside = insideEntity.addComponent(Image);
+ inside.sprite = solidSprite;
+ inside.color.set(0.95, 0.61, 0.07, 1);
+ inside.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
+
+ const labelEntity = canvasEntity.createChild("Label");
+ (labelEntity.transform).size.set(800, 80);
+ (labelEntity.transform).setPosition(0, -260, 0);
+ const label = labelEntity.addComponent(Text);
+ label.text = "Drag the slider to change alphaCutoff";
+ label.fontSize = 28;
+ label.color.set(0.85, 0.9, 1, 1);
+
+ const gui = new dat.GUI();
+ const state = { alphaCutoff: 0.5 };
+ gui
+ .add(state, "alphaCutoff", 0.0, 1.0, 0.01)
+ .name("Mask Alpha Cutoff")
+ .onChange((value: number) => {
+ mask.alphaCutoff = value;
+ });
+
+ engine.run();
+});
+
+function createSolidSprite(engine: WebGLEngine): Sprite {
+ const texture = new Texture2D(engine, 1, 1);
+ texture.setPixelBuffer(new Uint8Array([255, 255, 255, 255]));
+ return new Sprite(engine, texture);
+}
+
+/** Soft circle: alpha falls off radially so alphaCutoff has visible effect. */
+function createCircleSprite(engine: WebGLEngine, size: number): Sprite {
+ const buffer = new Uint8Array(size * size * 4);
+ const cx = size * 0.5;
+ const cy = size * 0.5;
+ const radius = size * 0.5;
+ for (let y = 0; y < size; y++) {
+ for (let x = 0; x < size; x++) {
+ const dx = x - cx;
+ const dy = y - cy;
+ const dist = Math.sqrt(dx * dx + dy * dy);
+ const t = Math.max(0, 1 - dist / radius);
+ const alpha = Math.min(255, Math.floor(t * 255));
+ const i = (y * size + x) * 4;
+ buffer[i] = 255;
+ buffer[i + 1] = 255;
+ buffer[i + 2] = 255;
+ buffer[i + 3] = alpha;
+ }
+ }
+ const texture = new Texture2D(engine, size, size, TextureFormat.R8G8B8A8, false);
+ texture.setPixelBuffer(buffer);
+ return new Sprite(engine, texture);
+}
diff --git a/examples/src/ui-mask-overlay.ts b/examples/src/ui-mask-overlay.ts
new file mode 100644
index 0000000000..e7fa6b5096
--- /dev/null
+++ b/examples/src/ui-mask-overlay.ts
@@ -0,0 +1,120 @@
+/**
+ * @title UI Mask Overlay
+ * @category UI
+ */
+import { Camera, Color, Sprite, SpriteMaskInteraction, Texture2D, WebGLEngine } from "@galacean/engine";
+import { CanvasRenderMode, Image, Mask, RectMask2D, Text, UICanvas, UITransform } from "@galacean/engine-ui";
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+
+ const scene = engine.sceneManager.activeScene;
+ scene.background.solidColor = new Color(0.03, 0.04, 0.07, 1);
+ const root = scene.createRootEntity("Root");
+
+ // Camera is required for default scene rendering even though overlay UI doesn't use it for projection.
+ const cameraEntity = root.createChild("Camera");
+ cameraEntity.transform.setPosition(0, 0, 10);
+ cameraEntity.addComponent(Camera);
+
+ const canvasEntity = root.createChild("UICanvas");
+ const uiCanvas = canvasEntity.addComponent(UICanvas);
+ uiCanvas.renderMode = CanvasRenderMode.ScreenSpaceOverlay;
+ uiCanvas.referenceResolutionPerUnit = 100;
+ uiCanvas.referenceResolution.set(1200, 800);
+
+ const solidSprite = createSolidSprite(engine);
+
+ // ===== Left half: SpriteMask (Mask component) =====
+ const maskGroup = canvasEntity.createChild("MaskGroup");
+ (maskGroup.transform).setPosition(-300, 60, 0);
+
+ const maskEntity = maskGroup.createChild("Mask");
+ (maskEntity.transform).size.set(280, 280);
+ const mask = maskEntity.addComponent(Mask);
+ mask.sprite = solidSprite;
+
+ const insideEntity = maskGroup.createChild("InsideImage");
+ (insideEntity.transform).size.set(440, 440);
+ const inside = insideEntity.addComponent(Image);
+ inside.sprite = solidSprite;
+ inside.color.set(0.91, 0.3, 0.24, 1);
+ inside.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
+
+ const maskLabelEntity = maskGroup.createChild("Label");
+ (maskLabelEntity.transform).size.set(360, 60);
+ (maskLabelEntity.transform).setPosition(0, -260, 0);
+ const maskLabel = maskLabelEntity.addComponent(Text);
+ maskLabel.text = "Mask (Overlay)";
+ maskLabel.fontSize = 32;
+ maskLabel.color.set(1, 1, 1, 1);
+
+ // ===== Right half: RectMask2D =====
+ const rectGroup = canvasEntity.createChild("RectGroup");
+ (rectGroup.transform).setPosition(300, 60, 0);
+
+ const viewportEntity = rectGroup.createChild("Viewport");
+ (viewportEntity.transform).size.set(360, 280);
+ const viewport = viewportEntity.addComponent(Image);
+ viewport.sprite = solidSprite;
+ viewport.color.set(0.17, 0.18, 0.2, 1);
+ viewportEntity.addComponent(RectMask2D);
+
+ const contentEntity = viewportEntity.createChild("Content");
+ (contentEntity.transform).size.set(560, 480);
+ (contentEntity.transform).setPosition(60, -50, 0);
+
+ const tileColors = [
+ new Color(0.91, 0.3, 0.24, 1),
+ new Color(0.16, 0.5, 0.73, 1),
+ new Color(0.18, 0.8, 0.44, 1),
+ new Color(0.95, 0.61, 0.07, 1)
+ ];
+ const tileSize = 220;
+ const gap = 20;
+ for (let row = 0; row < 2; row++) {
+ for (let col = 0; col < 2; col++) {
+ const i = row * 2 + col;
+ const tileEntity = contentEntity.createChild(`Tile_${i}`);
+ const t = tileEntity.transform;
+ t.size.set(tileSize, tileSize);
+ t.setPosition(col * (tileSize + gap) - (tileSize + gap) / 2, (tileSize + gap) / 2 - row * (tileSize + gap), 0);
+
+ const tile = tileEntity.addComponent(Image);
+ tile.sprite = solidSprite;
+ tile.color = tileColors[i];
+
+ const labelEntity = tileEntity.createChild("Label");
+ (labelEntity.transform).size.set(tileSize, tileSize);
+ const label = labelEntity.addComponent(Text);
+ label.text = `${i + 1}`;
+ label.fontSize = 64;
+ label.color.set(1, 1, 1, 1);
+ }
+ }
+
+ const rectLabelEntity = rectGroup.createChild("Label");
+ (rectLabelEntity.transform).size.set(360, 60);
+ (rectLabelEntity.transform).setPosition(0, -260, 0);
+ const rectLabel = rectLabelEntity.addComponent(Text);
+ rectLabel.text = "RectMask2D (Overlay)";
+ rectLabel.fontSize = 32;
+ rectLabel.color.set(1, 1, 1, 1);
+
+ // Top header
+ const headerEntity = canvasEntity.createChild("Header");
+ (headerEntity.transform).size.set(900, 80);
+ (headerEntity.transform).setPosition(0, 320, 0);
+ const header = headerEntity.addComponent(Text);
+ header.text = "ScreenSpaceOverlay · Mask & RectMask2D";
+ header.fontSize = 36;
+ header.color.set(0.85, 0.92, 1, 1);
+
+ engine.run();
+});
+
+function createSolidSprite(engine: WebGLEngine): Sprite {
+ const texture = new Texture2D(engine, 1, 1);
+ texture.setPixelBuffer(new Uint8Array([255, 255, 255, 255]));
+ return new Sprite(engine, texture);
+}
diff --git a/examples/src/ui-mask.ts b/examples/src/ui-mask.ts
new file mode 100644
index 0000000000..260392e6e1
--- /dev/null
+++ b/examples/src/ui-mask.ts
@@ -0,0 +1,85 @@
+/**
+ * @title UI Mask
+ * @category UI
+ */
+import { Camera, Color, Sprite, SpriteMaskInteraction, Texture2D, WebGLEngine } from "@galacean/engine";
+import { CanvasRenderMode, Image, Mask, Text, UICanvas, UITransform } from "@galacean/engine-ui";
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+
+ const scene = engine.sceneManager.activeScene;
+ scene.background.solidColor = new Color(0.03, 0.04, 0.07, 1);
+ const root = scene.createRootEntity("Root");
+
+ const cameraEntity = root.createChild("Camera");
+ cameraEntity.transform.setPosition(0, 0, 10);
+ const camera = cameraEntity.addComponent(Camera);
+
+ const canvasEntity = root.createChild("UICanvas");
+ const uiCanvas = canvasEntity.addComponent(UICanvas);
+ uiCanvas.renderMode = CanvasRenderMode.ScreenSpaceCamera;
+ uiCanvas.camera = camera;
+ uiCanvas.referenceResolutionPerUnit = 100;
+ uiCanvas.referenceResolution.set(1200, 800);
+
+ const solidSprite = createSolidSprite(engine);
+
+ // --- Left group: VisibleInsideMask ---
+ const leftGroupEntity = canvasEntity.createChild("LeftGroup");
+ (leftGroupEntity.transform).setPosition(-300, 0, 0);
+
+ // Square mask
+ const leftMaskEntity = leftGroupEntity.createChild("Mask");
+ (leftMaskEntity.transform).size.set(300, 300);
+ const leftMask = leftMaskEntity.addComponent(Mask);
+ leftMask.sprite = solidSprite;
+
+ // Image clipped to inside the mask
+ const insideImageEntity = leftGroupEntity.createChild("InsideImage");
+ (insideImageEntity.transform).size.set(500, 500);
+ const insideImage = insideImageEntity.addComponent(Image);
+ insideImage.sprite = solidSprite;
+ insideImage.color.set(0.91, 0.3, 0.24, 1);
+ insideImage.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
+
+ const leftLabelEntity = leftGroupEntity.createChild("Label");
+ (leftLabelEntity.transform).size.set(300, 60);
+ (leftLabelEntity.transform).setPosition(0, -210, 0);
+ const leftLabel = leftLabelEntity.addComponent(Text);
+ leftLabel.text = "VisibleInsideMask";
+ leftLabel.fontSize = 30;
+ leftLabel.color.set(1, 1, 1, 1);
+
+ // --- Right group: VisibleOutsideMask ---
+ const rightGroupEntity = canvasEntity.createChild("RightGroup");
+ (rightGroupEntity.transform).setPosition(300, 0, 0);
+
+ const rightMaskEntity = rightGroupEntity.createChild("Mask");
+ (rightMaskEntity.transform).size.set(300, 300);
+ const rightMask = rightMaskEntity.addComponent(Mask);
+ rightMask.sprite = solidSprite;
+
+ const outsideImageEntity = rightGroupEntity.createChild("OutsideImage");
+ (outsideImageEntity.transform).size.set(500, 500);
+ const outsideImage = outsideImageEntity.addComponent(Image);
+ outsideImage.sprite = solidSprite;
+ outsideImage.color.set(0.16, 0.5, 0.73, 1);
+ outsideImage.maskInteraction = SpriteMaskInteraction.VisibleOutsideMask;
+
+ const rightLabelEntity = rightGroupEntity.createChild("Label");
+ (rightLabelEntity.transform).size.set(300, 60);
+ (rightLabelEntity.transform).setPosition(0, -210, 0);
+ const rightLabel = rightLabelEntity.addComponent(Text);
+ rightLabel.text = "VisibleOutsideMask";
+ rightLabel.fontSize = 30;
+ rightLabel.color.set(1, 1, 1, 1);
+
+ engine.run();
+});
+
+function createSolidSprite(engine: WebGLEngine): Sprite {
+ const texture = new Texture2D(engine, 1, 1);
+ texture.setPixelBuffer(new Uint8Array([255, 255, 255, 255]));
+ return new Sprite(engine, texture);
+}
diff --git a/examples/src/ui-rect-mask-nested.ts b/examples/src/ui-rect-mask-nested.ts
new file mode 100644
index 0000000000..0eae971bfb
--- /dev/null
+++ b/examples/src/ui-rect-mask-nested.ts
@@ -0,0 +1,76 @@
+/**
+ * @title UI RectMask2D Nested
+ * @category UI
+ */
+import { Camera, Color, Sprite, Texture2D, WebGLEngine } from "@galacean/engine";
+import { CanvasRenderMode, Image, RectMask2D, Text, UICanvas, UITransform } from "@galacean/engine-ui";
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+
+ const scene = engine.sceneManager.activeScene;
+ scene.background.solidColor = new Color(0.03, 0.04, 0.07, 1);
+ const root = scene.createRootEntity("Root");
+
+ const cameraEntity = root.createChild("Camera");
+ cameraEntity.transform.setPosition(0, 0, 10);
+ const camera = cameraEntity.addComponent(Camera);
+
+ const canvasEntity = root.createChild("UICanvas");
+ const uiCanvas = canvasEntity.addComponent(UICanvas);
+ uiCanvas.renderMode = CanvasRenderMode.ScreenSpaceCamera;
+ uiCanvas.camera = camera;
+ uiCanvas.referenceResolutionPerUnit = 100;
+ uiCanvas.referenceResolution.set(1200, 800);
+
+ const solidSprite = createSolidSprite(engine);
+
+ // Outer mask (wide, short)
+ const outerEntity = canvasEntity.createChild("OuterMask");
+ (outerEntity.transform).size.set(560, 240);
+ (outerEntity.transform).setPosition(0, 60, 0);
+ const outerImage = outerEntity.addComponent(Image);
+ outerImage.sprite = solidSprite;
+ outerImage.color.set(0.13, 0.18, 0.28, 1);
+ outerEntity.addComponent(RectMask2D);
+
+ // Inner mask (tall, narrow), child of outer
+ const innerEntity = outerEntity.createChild("InnerMask");
+ (innerEntity.transform).size.set(240, 480);
+ (innerEntity.transform).setPosition(0, 0, 0);
+ const innerImage = innerEntity.addComponent(Image);
+ innerImage.sprite = solidSprite;
+ innerImage.color.set(0.2, 0.3, 0.5, 1);
+ innerEntity.addComponent(RectMask2D);
+
+ // Big colored content under both masks — only the intersection of outer ∩ inner remains visible
+ const contentEntity = innerEntity.createChild("Content");
+ (contentEntity.transform).size.set(800, 800);
+ const content = contentEntity.addComponent(Image);
+ content.sprite = solidSprite;
+ content.color.set(0.95, 0.61, 0.07, 1);
+
+ const labelTopEntity = canvasEntity.createChild("LabelTop");
+ (labelTopEntity.transform).size.set(900, 60);
+ (labelTopEntity.transform).setPosition(0, 220, 0);
+ const labelTop = labelTopEntity.addComponent(Text);
+ labelTop.text = "Outer 560x240 ∩ Inner 240x480 → visible: 240x240";
+ labelTop.fontSize = 28;
+ labelTop.color.set(1, 1, 1, 1);
+
+ const labelBottomEntity = canvasEntity.createChild("LabelBottom");
+ (labelBottomEntity.transform).size.set(900, 60);
+ (labelBottomEntity.transform).setPosition(0, -260, 0);
+ const labelBottom = labelBottomEntity.addComponent(Text);
+ labelBottom.text = "Nested RectMask2D takes the rect intersection of all ancestor masks.";
+ labelBottom.fontSize = 24;
+ labelBottom.color.set(0.77, 0.82, 0.89, 1);
+
+ engine.run();
+});
+
+function createSolidSprite(engine: WebGLEngine): Sprite {
+ const texture = new Texture2D(engine, 1, 1);
+ texture.setPixelBuffer(new Uint8Array([255, 255, 255, 255]));
+ return new Sprite(engine, texture);
+}
diff --git a/examples/src/ui-rect-mask.ts b/examples/src/ui-rect-mask.ts
new file mode 100644
index 0000000000..92078d6c6a
--- /dev/null
+++ b/examples/src/ui-rect-mask.ts
@@ -0,0 +1,135 @@
+/**
+ * @title UI RectMask2D
+ * @category UI
+ */
+import * as dat from "dat.gui";
+import { Camera, Color, Sprite, Texture2D, Vector2, WebGLEngine } from "@galacean/engine";
+import { CanvasRenderMode, Image, RectMask2D, Text, UICanvas, UITransform } from "@galacean/engine-ui";
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+
+ const scene = engine.sceneManager.activeScene;
+ scene.background.solidColor = new Color(0.03, 0.04, 0.07, 1);
+ const root = scene.createRootEntity("Root");
+
+ const cameraEntity = root.createChild("Camera");
+ cameraEntity.transform.setPosition(0, 0, 10);
+ const camera = cameraEntity.addComponent(Camera);
+
+ const canvasEntity = root.createChild("UICanvas");
+ const uiCanvas = canvasEntity.addComponent(UICanvas);
+ uiCanvas.renderMode = CanvasRenderMode.ScreenSpaceCamera;
+ uiCanvas.camera = camera;
+ uiCanvas.referenceResolutionPerUnit = 100;
+ uiCanvas.referenceResolution.set(1200, 800);
+
+ const solidSprite = createSolidSprite(engine);
+
+ // Frame card
+ const frameEntity = canvasEntity.createChild("Frame");
+ (frameEntity.transform).size.set(560, 460);
+ (frameEntity.transform).setPosition(-180, 20, 0);
+ const frame = frameEntity.addComponent(Image);
+ frame.sprite = solidSprite;
+ frame.color.set(0.09, 0.11, 0.15, 1);
+
+ // Viewport with RectMask2D
+ const viewportEntity = frameEntity.createChild("Viewport");
+ (viewportEntity.transform).size.set(440, 320);
+ (viewportEntity.transform).setPosition(30, -10, 0);
+ const viewport = viewportEntity.addComponent(Image);
+ viewport.sprite = solidSprite;
+ viewport.color.set(0.17, 0.18, 0.2, 1);
+ const rectMask = viewportEntity.addComponent(RectMask2D);
+
+ // 3x3 colored tiles overflow the viewport
+ const contentEntity = viewportEntity.createChild("Content");
+ (contentEntity.transform).size.set(740, 560);
+ (contentEntity.transform).setPosition(80, -60, 0);
+
+ const colors = [
+ new Color(0.91, 0.3, 0.24, 1),
+ new Color(0.16, 0.5, 0.73, 1),
+ new Color(0.18, 0.8, 0.44, 1),
+ new Color(0.95, 0.61, 0.07, 1),
+ new Color(0.56, 0.27, 0.68, 1),
+ new Color(0.2, 0.6, 0.86, 1),
+ new Color(0.83, 0.33, 0.33, 1),
+ new Color(0.1, 0.74, 0.61, 1),
+ new Color(0.93, 0.78, 0.0, 1)
+ ];
+
+ const tileWidth = 180;
+ const tileHeight = 180;
+ const gap = 10;
+ for (let row = 0; row < 3; row++) {
+ for (let col = 0; col < 3; col++) {
+ const index = row * 3 + col;
+ const tileEntity = contentEntity.createChild(`Tile_${index}`);
+ const t = tileEntity.transform;
+ t.size.set(tileWidth, tileHeight);
+ t.setPosition(col * (tileWidth + gap) - 170, 170 - row * (tileHeight + gap), 0);
+
+ const tile = tileEntity.addComponent(Image);
+ tile.sprite = solidSprite;
+ tile.color = colors[index];
+
+ const labelEntity = tileEntity.createChild("Label");
+ (labelEntity.transform).size.set(tileWidth, tileHeight);
+ const label = labelEntity.addComponent(Text);
+ label.text = `${index + 1}`;
+ label.fontSize = 56;
+ label.color.set(1, 1, 1, 1);
+ }
+ }
+
+ // Right info card
+ const noteEntity = canvasEntity.createChild("Note");
+ (noteEntity.transform).size.set(360, 220);
+ (noteEntity.transform).setPosition(290, 20, 0);
+ const note = noteEntity.addComponent(Image);
+ note.sprite = solidSprite;
+ note.color.set(0.08, 0.09, 0.12, 1);
+
+ const noteTextEntity = noteEntity.createChild("Copy");
+ (noteTextEntity.transform).size.set(320, 180);
+ const noteText = noteTextEntity.addComponent(Text);
+ noteText.text =
+ "RectMask2D clips Image\nand Text by an axis-\naligned rectangle.\n\nUse the GUI to tweak\nsoftness / alphaClip.";
+ noteText.fontSize = 26;
+ noteText.color.set(0.77, 0.82, 0.89, 1);
+
+ const gui = new dat.GUI();
+ const state = {
+ softnessX: 0,
+ softnessY: 0,
+ alphaClip: false
+ };
+ gui
+ .add(state, "softnessX", 0, 80, 1)
+ .name("softness.x")
+ .onChange((v: number) => {
+ rectMask.softness = new Vector2(v, state.softnessY);
+ });
+ gui
+ .add(state, "softnessY", 0, 80, 1)
+ .name("softness.y")
+ .onChange((v: number) => {
+ rectMask.softness = new Vector2(state.softnessX, v);
+ });
+ gui
+ .add(state, "alphaClip")
+ .name("alphaClip (discard)")
+ .onChange((v: boolean) => {
+ rectMask.alphaClip = v;
+ });
+
+ engine.run();
+});
+
+function createSolidSprite(engine: WebGLEngine): Sprite {
+ const texture = new Texture2D(engine, 1, 1);
+ texture.setPixelBuffer(new Uint8Array([255, 255, 255, 255]));
+ return new Sprite(engine, texture);
+}
diff --git a/examples/src/ui-text-outline.ts b/examples/src/ui-text-outline.ts
new file mode 100644
index 0000000000..fae8865a9f
--- /dev/null
+++ b/examples/src/ui-text-outline.ts
@@ -0,0 +1,121 @@
+/**
+ * @title UI Text Outline
+ * @category UI
+ */
+import * as dat from "dat.gui";
+import { Camera, Color, WebGLEngine } from "@galacean/engine";
+import { CanvasRenderMode, Text, UICanvas, UITransform } from "@galacean/engine-ui";
+
+WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
+ engine.canvas.resizeByClientSize();
+
+ const scene = engine.sceneManager.activeScene;
+ scene.background.solidColor = new Color(0.05, 0.07, 0.1, 1);
+ const root = scene.createRootEntity("Root");
+
+ const cameraEntity = root.createChild("Camera");
+ cameraEntity.transform.setPosition(0, 0, 10);
+ const camera = cameraEntity.addComponent(Camera);
+
+ const canvasEntity = root.createChild("UICanvas");
+ const uiCanvas = canvasEntity.addComponent(UICanvas);
+ uiCanvas.renderMode = CanvasRenderMode.ScreenSpaceCamera;
+ uiCanvas.camera = camera;
+ uiCanvas.referenceResolutionPerUnit = 100;
+ uiCanvas.referenceResolution.set(1280, 800);
+
+ // ---------- Matrix grid ----------
+ // Rows = outlineWidth (0 / 1 / 2 / 4 / 8 px)
+ // Cols = (text color, outline color) presets
+ const widthCases = [0, 1, 2, 4, 8];
+ const colorCases: { name: string; fill: Color; outline: Color }[] = [
+ { name: "white / black", fill: new Color(1, 1, 1, 1), outline: new Color(0, 0, 0, 1) },
+ { name: "black / white", fill: new Color(0, 0, 0, 1), outline: new Color(1, 1, 1, 1) },
+ { name: "yellow / red", fill: new Color(1, 0.85, 0.1, 1), outline: new Color(0.85, 0.1, 0.1, 1) }
+ ];
+
+ const cellW = 380;
+ const cellH = 110;
+ const startX = -cellW * (colorCases.length - 1) * 0.5;
+ const startY = 220;
+
+ // Column headers
+ for (let c = 0; c < colorCases.length; c++) {
+ const headerEntity = canvasEntity.createChild(`header-${c}`);
+ const ht = headerEntity.transform;
+ ht.size.set(cellW, 32);
+ ht.setPosition(startX + c * cellW, startY + cellH * 0.5 + 20, 0);
+ const header = headerEntity.addComponent(Text);
+ header.text = `text/outline = ${colorCases[c].name}`;
+ header.fontSize = 18;
+ header.color.set(0.7, 0.78, 0.9, 1);
+ }
+
+ for (let r = 0; r < widthCases.length; r++) {
+ const w = widthCases[r];
+
+ // Row label
+ const labelEntity = canvasEntity.createChild(`row-label-${r}`);
+ const lt = labelEntity.transform;
+ lt.size.set(120, cellH);
+ lt.setPosition(startX - cellW * 0.5 - 60, startY - r * cellH, 0);
+ const label = labelEntity.addComponent(Text);
+ label.text = `width = ${w}px`;
+ label.fontSize = 18;
+ label.color.set(0.7, 0.78, 0.9, 1);
+
+ for (let c = 0; c < colorCases.length; c++) {
+ const { fill, outline } = colorCases[c];
+
+ const cell = canvasEntity.createChild(`cell-${r}-${c}`);
+ const ct = cell.transform;
+ ct.size.set(cellW, cellH);
+ ct.setPosition(startX + c * cellW, startY - r * cellH, 0);
+ const text = cell.addComponent(Text);
+ text.text = "Hello 描边 Outline";
+ text.fontSize = 36;
+ text.color = fill;
+ text.outlineWidth = w;
+ text.outlineColor = outline;
+ }
+ }
+
+ // ---------- Live preview controlled by dat.gui ----------
+ const previewEntity = canvasEntity.createChild("preview");
+ const previewTransform = previewEntity.transform;
+ previewTransform.size.set(900, 160);
+ previewTransform.setPosition(0, -300, 0);
+ const preview = previewEntity.addComponent(Text);
+ preview.text = "实时 Live 预览 Preview";
+ preview.fontSize = 72;
+ preview.color = new Color(1, 1, 1, 1);
+ preview.outlineWidth = 3;
+ preview.outlineColor = new Color(0, 0, 0, 1);
+
+ const state = {
+ fontSize: preview.fontSize,
+ outlineWidth: preview.outlineWidth,
+ fillColor: [255, 255, 255],
+ outlineColor: [0, 0, 0],
+ text: preview.text
+ };
+
+ const gui = new dat.GUI();
+ gui.add(state, "text").onChange((v: string) => {
+ preview.text = v;
+ });
+ gui.add(state, "fontSize", 12, 120, 1).onChange((v: number) => {
+ preview.fontSize = v;
+ });
+ gui.add(state, "outlineWidth", 0, 8, 0.1).onChange((v: number) => {
+ preview.outlineWidth = v;
+ });
+ gui.addColor(state, "fillColor").onChange((rgb: number[]) => {
+ preview.color.set(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, 1);
+ });
+ gui.addColor(state, "outlineColor").onChange((rgb: number[]) => {
+ preview.outlineColor.set(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, 1);
+ });
+
+ engine.run();
+});
diff --git a/package.json b/package.json
index 6be276f782..feef7fc6c0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@galacean/engine-root",
- "version": "2.0.0-alpha.24",
+ "version": "0.0.0-experimental-2.0-game.19",
"packageManager": "pnpm@9.3.0",
"private": true,
"scripts": {
@@ -41,18 +41,18 @@
"@types/dom-webcodecs": "^0.1.13",
"@types/node": "^18.7.16",
"@types/webxr": "latest",
- "@typescript-eslint/eslint-plugin": "^6.1.0",
- "@typescript-eslint/parser": "^6.1.0",
+ "@typescript-eslint/eslint-plugin": "^8.58.1",
+ "@typescript-eslint/parser": "^8.58.1",
"@vitest/coverage-v8": "2.1.3",
"bumpp": "^9.5.2",
"cross-env": "^5.2.0",
"electron": "^13",
- "eslint": "^8.44.0",
+ "eslint": "^8.57.1",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^5.0.0",
"fs-extra": "^10.1.0",
"husky": "^8.0.0",
- "lint-staged": "^10.5.3",
+ "lint-staged": "^16.4.0",
"nyc": "^15.1.0",
"odiff-bin": "^2.5.0",
"prettier": "^3.0.0",
@@ -65,9 +65,8 @@
"vitest": "2.1.3"
},
"lint-staged": {
- "*.{ts}": [
- "eslint --fix",
- "git add"
+ "**/*.ts": [
+ "eslint --fix"
]
},
"repository": "git@github.com:galacean/runtime.git"
diff --git a/packages/core/package.json b/packages/core/package.json
index be735da868..b6d08f767b 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@galacean/engine-core",
- "version": "2.0.0-alpha.24",
+ "version": "0.0.0-experimental-2.0-game.19",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
diff --git a/packages/core/src/2d/assembler/FilledSpriteAssembler.ts b/packages/core/src/2d/assembler/FilledSpriteAssembler.ts
new file mode 100644
index 0000000000..b5e9797119
--- /dev/null
+++ b/packages/core/src/2d/assembler/FilledSpriteAssembler.ts
@@ -0,0 +1,613 @@
+import { BoundingBox, Matrix, Vector2, Vector3 } from "@galacean/engine-math";
+import { SubPrimitiveChunk } from "../../RenderPipeline/SubPrimitiveChunk";
+import { StaticInterfaceImplement } from "../../base/StaticInterfaceImplement";
+import { SpriteFilledMode } from "../enums/SpriteFilledMode";
+import { SpriteFilledOrigin } from "../enums/SpriteFilledOrigin";
+import { ISpriteAssembler } from "./ISpriteAssembler";
+import { ISpriteRenderer } from "./ISpriteRenderer";
+
+/**
+ * Assemble vertex data for the sprite renderer in filled mode.
+ */
+@StaticInterfaceImplement()
+export class FilledSpriteAssembler {
+ private static _matrix = new Matrix();
+ private static _worldPositions = [
+ new Vector3(),
+ new Vector3(),
+ new Vector3(),
+ new Vector3(),
+ new Vector3(),
+ new Vector3(),
+ new Vector3(),
+ new Vector3(),
+ new Vector3()
+ ];
+ private static _uvs = [
+ new Vector2(),
+ new Vector2(),
+ new Vector2(),
+ new Vector2(),
+ new Vector2(),
+ new Vector2(),
+ new Vector2(),
+ new Vector2(),
+ new Vector2()
+ ];
+ private static _inPositions: Vector3[] = [];
+ private static _inUVs: Vector2[] = [];
+ private static _outPositions: Vector3[] = [new Vector3(), new Vector3(), new Vector3(), new Vector3()];
+ private static _outUVs: Vector2[] = [new Vector2(), new Vector2(), new Vector2(), new Vector2()];
+ private static _vertexOffset = 0;
+ private static _indicesOffset = 0;
+
+ static resetData(renderer: ISpriteRenderer): void {
+ const manager = renderer._getChunkManager();
+ const lastSubChunk = renderer._subChunk;
+ lastSubChunk && manager.freeSubChunk(lastSubChunk);
+ const subChunk = manager.allocateSubChunk(16);
+ subChunk.indices = [];
+ renderer._subChunk = subChunk;
+ }
+
+ static updatePositions(
+ renderer: ISpriteRenderer,
+ worldMatrix: Matrix,
+ width: number,
+ height: number,
+ pivot: Vector2,
+ flipX: boolean,
+ flipY: boolean
+ ): void {
+ const { x: pivotX, y: pivotY } = pivot;
+ const modelMatrix = FilledSpriteAssembler._matrix;
+ const { elements: wE } = modelMatrix;
+ const { elements: pWE } = worldMatrix;
+ const sx = flipX ? -width : width;
+ const sy = flipY ? -height : height;
+ (wE[0] = pWE[0] * sx), (wE[1] = pWE[1] * sx), (wE[2] = pWE[2] * sx);
+ (wE[4] = pWE[4] * sy), (wE[5] = pWE[5] * sy), (wE[6] = pWE[6] * sy);
+ (wE[8] = pWE[8]), (wE[9] = pWE[9]), (wE[10] = pWE[10]);
+ wE[12] = pWE[12] - pivotX * wE[0] - pivotY * wE[4];
+ wE[13] = pWE[13] - pivotX * wE[1] - pivotY * wE[5];
+ wE[14] = pWE[14] - pivotX * wE[2] - pivotY * wE[6];
+
+ switch (renderer.filledMode) {
+ case SpriteFilledMode.Horizontal:
+ this._filledLinear(renderer, modelMatrix, true);
+ break;
+ case SpriteFilledMode.Vertical:
+ this._filledLinear(renderer, modelMatrix, false);
+ break;
+ case SpriteFilledMode.Radial90:
+ this._filledRadial90(
+ renderer,
+ modelMatrix,
+ renderer.filledOrigin,
+ renderer.filledAmount,
+ renderer.filledClockWise
+ );
+ break;
+ case SpriteFilledMode.Radial180:
+ this._filledRadial180(
+ renderer,
+ modelMatrix,
+ renderer.filledOrigin,
+ renderer.filledAmount,
+ renderer.filledClockWise
+ );
+ break;
+ case SpriteFilledMode.Radial360:
+ this._filledRadial360(
+ renderer,
+ modelMatrix,
+ renderer.filledOrigin,
+ renderer.filledAmount,
+ renderer.filledClockWise
+ );
+ break;
+ default:
+ break;
+ }
+
+ // @ts-ignore
+ BoundingBox.transform(renderer.sprite._getBounds(), modelMatrix, renderer._bounds);
+ }
+
+ static updateUVs(renderer: ISpriteRenderer): void {
+ // UVs are computed in updatePositions.
+ }
+
+ static updateColor(renderer: ISpriteRenderer, alpha: number): void {
+ const subChunk = renderer._subChunk;
+ const { r, g, b, a } = renderer.color;
+ const finalAlpha = a * alpha;
+ const vertices = subChunk.chunk.vertices;
+ const vertexArea = subChunk.vertexArea;
+ for (let i = 0, o = vertexArea.start + 5, n = vertexArea.size / 9; i < n; ++i, o += 9) {
+ vertices[o] = r;
+ vertices[o + 1] = g;
+ vertices[o + 2] = b;
+ vertices[o + 3] = finalAlpha;
+ }
+ }
+
+ private static _filledLinear(renderer: ISpriteRenderer, matrix: Matrix, isHorizontal: boolean): void {
+ const amount = renderer.filledAmount;
+ if (amount <= 0.001) {
+ renderer._subChunk.indices.length = 0;
+ return;
+ }
+
+ const sprite = renderer.sprite;
+ const [lPosLB, lPosRB, lPosLT, lPosRT] = sprite._getPositions();
+ const spriteUVs = sprite._getUVs();
+ const { x: left, y: bottom } = spriteUVs[0];
+ const { x: right, y: top } = spriteUVs[15];
+
+ const subChunk = renderer._subChunk;
+ const vertices = subChunk.chunk.vertices;
+
+ let x0: number, y0: number, u0: number, v0: number;
+ let x1: number, y1: number, u1: number, v1: number;
+ let x2: number, y2: number, u2: number, v2: number;
+ let x3: number, y3: number, u3: number, v3: number;
+
+ if (isHorizontal) {
+ const originIsStart = renderer.filledOrigin === SpriteFilledOrigin.Left;
+ const startX = originIsStart ? lPosLB.x : lPosRB.x - (lPosRB.x - lPosLB.x) * amount;
+ const endX = originIsStart ? lPosLB.x + (lPosRB.x - lPosLB.x) * amount : lPosRB.x;
+ const startU = originIsStart ? left : right - (right - left) * amount;
+ const endU = originIsStart ? left + (right - left) * amount : right;
+ (x0 = startX), (y0 = lPosLB.y), (u0 = startU), (v0 = bottom);
+ (x1 = endX), (y1 = lPosRB.y), (u1 = endU), (v1 = bottom);
+ (x2 = startX), (y2 = lPosLT.y), (u2 = startU), (v2 = top);
+ (x3 = endX), (y3 = lPosRT.y), (u3 = endU), (v3 = top);
+ } else {
+ const originIsStart = renderer.filledOrigin === SpriteFilledOrigin.Bottom;
+ const startY = originIsStart ? lPosLB.y : lPosLT.y - (lPosLT.y - lPosLB.y) * amount;
+ const endY = originIsStart ? lPosLB.y + (lPosLT.y - lPosLB.y) * amount : lPosLT.y;
+ const startV = originIsStart ? bottom : top - (top - bottom) * amount;
+ const endV = originIsStart ? bottom + (top - bottom) * amount : top;
+ (x0 = lPosLB.x), (y0 = startY), (u0 = left), (v0 = startV);
+ (x1 = lPosRB.x), (y1 = startY), (u1 = right), (v1 = startV);
+ (x2 = lPosLT.x), (y2 = endY), (u2 = left), (v2 = endV);
+ (x3 = lPosRT.x), (y3 = endY), (u3 = right), (v3 = endV);
+ }
+
+ const { elements: wE } = matrix;
+ const start = subChunk.vertexArea.start;
+ // LB
+ vertices[start] = wE[0] * x0 + wE[4] * y0 + wE[12];
+ vertices[start + 1] = wE[1] * x0 + wE[5] * y0 + wE[13];
+ vertices[start + 2] = wE[2] * x0 + wE[6] * y0 + wE[14];
+ vertices[start + 3] = u0;
+ vertices[start + 4] = v0;
+ // RB
+ vertices[start + 9] = wE[0] * x1 + wE[4] * y1 + wE[12];
+ vertices[start + 10] = wE[1] * x1 + wE[5] * y1 + wE[13];
+ vertices[start + 11] = wE[2] * x1 + wE[6] * y1 + wE[14];
+ vertices[start + 12] = u1;
+ vertices[start + 13] = v1;
+ // LT
+ vertices[start + 18] = wE[0] * x2 + wE[4] * y2 + wE[12];
+ vertices[start + 19] = wE[1] * x2 + wE[5] * y2 + wE[13];
+ vertices[start + 20] = wE[2] * x2 + wE[6] * y2 + wE[14];
+ vertices[start + 21] = u2;
+ vertices[start + 22] = v2;
+ // RT
+ vertices[start + 27] = wE[0] * x3 + wE[4] * y3 + wE[12];
+ vertices[start + 28] = wE[1] * x3 + wE[5] * y3 + wE[13];
+ vertices[start + 29] = wE[2] * x3 + wE[6] * y3 + wE[14];
+ vertices[start + 30] = u3;
+ vertices[start + 31] = v3;
+
+ const indices = subChunk.indices;
+ indices[0] = 0;
+ indices[1] = 1;
+ indices[2] = 2;
+ indices[3] = 2;
+ indices[4] = 1;
+ indices[5] = 3;
+ indices.length = 6;
+ }
+
+ private static _filledRadial90(
+ renderer: ISpriteRenderer,
+ matrix: Matrix,
+ origin: SpriteFilledOrigin,
+ amount: number,
+ cw: boolean
+ ): void {
+ if (amount <= 0.001) {
+ renderer._subChunk.indices.length = 0;
+ return;
+ }
+
+ const sprite = renderer.sprite;
+ const [lPosLB, lPosRB, lPosLT, lPosRT] = sprite._getPositions();
+ const spriteUVs = sprite._getUVs();
+ const { x: left, y: bottom } = spriteUVs[0];
+ const { x: right, y: top } = spriteUVs[15];
+
+ // Transform 4 corners to world space
+ const [wLB, wRB, wLT, wRT] = this._worldPositions;
+ const [uvLB, uvRB, uvLT, uvRT] = this._uvs;
+ wLB.set(lPosLB.x, lPosLB.y, 0).transformToVec3(matrix), uvLB.set(left, bottom);
+ wRB.set(lPosRB.x, lPosRB.y, 0).transformToVec3(matrix), uvRB.set(right, bottom);
+ wLT.set(lPosLT.x, lPosLT.y, 0).transformToVec3(matrix), uvLT.set(left, top);
+ wRT.set(lPosRT.x, lPosRT.y, 0).transformToVec3(matrix), uvRT.set(right, top);
+
+ // Map vertices based on origin corner:
+ // [center, CW-adjacent, CCW-adjacent, opposite]
+ const { _inPositions: inPositions, _inUVs: inUVs, _outPositions: outPositions, _outUVs: outUVs } = this;
+ switch (origin) {
+ case SpriteFilledOrigin.BottomLeft:
+ (inPositions[0] = wLB), (inUVs[0] = uvLB);
+ (inPositions[1] = wRB), (inUVs[1] = uvRB);
+ (inPositions[2] = wLT), (inUVs[2] = uvLT);
+ (inPositions[3] = wRT), (inUVs[3] = uvRT);
+ break;
+ case SpriteFilledOrigin.BottomRight:
+ (inPositions[0] = wRB), (inUVs[0] = uvRB);
+ (inPositions[1] = wRT), (inUVs[1] = uvRT);
+ (inPositions[2] = wLB), (inUVs[2] = uvLB);
+ (inPositions[3] = wLT), (inUVs[3] = uvLT);
+ break;
+ case SpriteFilledOrigin.TopRight:
+ (inPositions[0] = wRT), (inUVs[0] = uvRT);
+ (inPositions[1] = wLT), (inUVs[1] = uvLT);
+ (inPositions[2] = wRB), (inUVs[2] = uvRB);
+ (inPositions[3] = wLB), (inUVs[3] = uvLB);
+ break;
+ case SpriteFilledOrigin.TopLeft:
+ (inPositions[0] = wLT), (inUVs[0] = uvLT);
+ (inPositions[1] = wLB), (inUVs[1] = uvLB);
+ (inPositions[2] = wRT), (inUVs[2] = uvRT);
+ (inPositions[3] = wRB), (inUVs[3] = uvRB);
+ break;
+ default:
+ break;
+ }
+
+ const startAngle = cw ? 90 - amount * 90 : 0;
+ const endAngle = cw ? 90 : amount * 90;
+
+ this._vertexOffset = this._indicesOffset = 0;
+ this._radialCut(renderer._subChunk, inPositions, inUVs, startAngle, endAngle, outPositions, outUVs);
+ renderer._subChunk.indices.length = this._indicesOffset;
+ }
+
+ private static _filledRadial180(
+ renderer: ISpriteRenderer,
+ matrix: Matrix,
+ origin: SpriteFilledOrigin,
+ amount: number,
+ cw: boolean
+ ): void {
+ if (amount <= 0.001) {
+ renderer._subChunk.indices.length = 0;
+ return;
+ }
+
+ const sprite = renderer.sprite;
+ const [lPosLB, lPosRB, lPosLT, lPosRT] = sprite._getPositions();
+ const spriteUVs = sprite._getUVs();
+ const { x: left, y: bottom } = spriteUVs[0];
+ const { x: right, y: top } = spriteUVs[15];
+
+ // Transform corners and compute edge midpoints
+ const [wLB, wMB, wRB, wLM, , wRM, wLT, wMT, wRT] = this._worldPositions;
+ const [uvLB, uvMB, uvRB, uvLM, , uvRM, uvLT, uvMT, uvRT] = this._uvs;
+ wLB.set(lPosLB.x, lPosLB.y, 0).transformToVec3(matrix), uvLB.set(left, bottom);
+ wRB.set(lPosRB.x, lPosRB.y, 0).transformToVec3(matrix), uvRB.set(right, bottom);
+ wLT.set(lPosLT.x, lPosLT.y, 0).transformToVec3(matrix), uvLT.set(left, top);
+ wRT.set(lPosRT.x, lPosRT.y, 0).transformToVec3(matrix), uvRT.set(right, top);
+ Vector3.lerp(wLB, wRB, 0.5, wMB), Vector2.lerp(uvLB, uvRB, 0.5, uvMB);
+ Vector3.lerp(wLB, wLT, 0.5, wLM), Vector2.lerp(uvLB, uvLT, 0.5, uvLM);
+ Vector3.lerp(wLT, wRT, 0.5, wMT), Vector2.lerp(uvLT, uvRT, 0.5, uvMT);
+ Vector3.lerp(wRB, wRT, 0.5, wRM), Vector2.lerp(uvRB, uvRT, 0.5, uvRM);
+
+ const startAngle = cw ? 180 - amount * 180 : 0;
+ const endAngle = cw ? 180 : amount * 180;
+
+ this._vertexOffset = this._indicesOffset = 0;
+ const { _inPositions: inPositions, _inUVs: inUVs, _outPositions: outPositions, _outUVs: outUVs } = this;
+ const { _subChunk: subChunk } = renderer;
+
+ // Center is at the origin edge midpoint; two quadrants cover the full sprite.
+ // Quadrant A (0°-90°), Quadrant B (90°-180°)
+ switch (origin) {
+ case SpriteFilledOrigin.Bottom:
+ // Center=MB, A: [MB,RB,MT,RT], B: [MB,MT,LB,LT]
+ (inPositions[0] = wMB), (inUVs[0] = uvMB);
+ (inPositions[1] = wRB), (inUVs[1] = uvRB);
+ (inPositions[2] = wMT), (inUVs[2] = uvMT);
+ (inPositions[3] = wRT), (inUVs[3] = uvRT);
+ this._radialCut(subChunk, inPositions, inUVs, startAngle, endAngle, outPositions, outUVs);
+ (inPositions[1] = wMT), (inUVs[1] = uvMT);
+ (inPositions[2] = wLB), (inUVs[2] = uvLB);
+ (inPositions[3] = wLT), (inUVs[3] = uvLT);
+ this._radialCut(subChunk, inPositions, inUVs, startAngle - 90, endAngle - 90, outPositions, outUVs);
+ break;
+ case SpriteFilledOrigin.Top:
+ // Center=MT, A: [MT,LT,MB,LB], B: [MT,MB,RT,RB]
+ (inPositions[0] = wMT), (inUVs[0] = uvMT);
+ (inPositions[1] = wLT), (inUVs[1] = uvLT);
+ (inPositions[2] = wMB), (inUVs[2] = uvMB);
+ (inPositions[3] = wLB), (inUVs[3] = uvLB);
+ this._radialCut(subChunk, inPositions, inUVs, startAngle, endAngle, outPositions, outUVs);
+ (inPositions[1] = wMB), (inUVs[1] = uvMB);
+ (inPositions[2] = wRT), (inUVs[2] = uvRT);
+ (inPositions[3] = wRB), (inUVs[3] = uvRB);
+ this._radialCut(subChunk, inPositions, inUVs, startAngle - 90, endAngle - 90, outPositions, outUVs);
+ break;
+ case SpriteFilledOrigin.Left:
+ // Center=LM, A: [LM,LB,RM,RB], B: [LM,RM,LT,RT]
+ (inPositions[0] = wLM), (inUVs[0] = uvLM);
+ (inPositions[1] = wLB), (inUVs[1] = uvLB);
+ (inPositions[2] = wRM), (inUVs[2] = uvRM);
+ (inPositions[3] = wRB), (inUVs[3] = uvRB);
+ this._radialCut(subChunk, inPositions, inUVs, startAngle, endAngle, outPositions, outUVs);
+ (inPositions[1] = wRM), (inUVs[1] = uvRM);
+ (inPositions[2] = wLT), (inUVs[2] = uvLT);
+ (inPositions[3] = wRT), (inUVs[3] = uvRT);
+ this._radialCut(subChunk, inPositions, inUVs, startAngle - 90, endAngle - 90, outPositions, outUVs);
+ break;
+ case SpriteFilledOrigin.Right:
+ // Center=RM, A: [RM,RT,LM,LT], B: [RM,LM,RB,LB]
+ (inPositions[0] = wRM), (inUVs[0] = uvRM);
+ (inPositions[1] = wRT), (inUVs[1] = uvRT);
+ (inPositions[2] = wLM), (inUVs[2] = uvLM);
+ (inPositions[3] = wLT), (inUVs[3] = uvLT);
+ this._radialCut(subChunk, inPositions, inUVs, startAngle, endAngle, outPositions, outUVs);
+ (inPositions[1] = wLM), (inUVs[1] = uvLM);
+ (inPositions[2] = wRB), (inUVs[2] = uvRB);
+ (inPositions[3] = wLB), (inUVs[3] = uvLB);
+ this._radialCut(subChunk, inPositions, inUVs, startAngle - 90, endAngle - 90, outPositions, outUVs);
+ break;
+ default:
+ break;
+ }
+
+ subChunk.indices.length = this._indicesOffset;
+ }
+
+ private static _filledRadial360(
+ renderer: ISpriteRenderer,
+ matrix: Matrix,
+ origin: SpriteFilledOrigin,
+ amount: number,
+ cw: boolean
+ ): void {
+ if (amount <= 0.001) {
+ renderer._subChunk.indices.length = 0;
+ return;
+ }
+
+ let startAngle = 0;
+ switch (origin) {
+ case SpriteFilledOrigin.Right:
+ startAngle = cw ? 360 - amount * 360 : 0;
+ break;
+ case SpriteFilledOrigin.Top:
+ startAngle = cw ? 450 - amount * 360 : 90;
+ break;
+ case SpriteFilledOrigin.Left:
+ startAngle = cw ? 540 - amount * 360 : 180;
+ break;
+ case SpriteFilledOrigin.Bottom:
+ startAngle = cw ? 630 - amount * 360 : 270;
+ break;
+ default:
+ break;
+ }
+ const endAngle = startAngle + amount * 360;
+
+ this._processRadialGrid(renderer, matrix, startAngle, endAngle);
+ }
+
+ /**
+ * Prepare the 3x3 grid and process 4 quadrants for radial fill.
+ */
+ private static _processRadialGrid(
+ renderer: ISpriteRenderer,
+ matrix: Matrix,
+ startAngle: number,
+ endAngle: number
+ ): void {
+ const sprite = renderer.sprite;
+ const [lPosLB, lPosRB, lPosLT, lPosRT] = sprite._getPositions();
+ const spriteUVs = sprite._getUVs();
+ const { x: left, y: bottom } = spriteUVs[0];
+ const { x: right, y: top } = spriteUVs[15];
+
+ // ---------------
+ // LT - MT - RT
+ // | | |
+ // LM - C - RM
+ // | | |
+ // LB - MB - RB
+ // ---------------
+ const [wPosLB, wPosMB, wPosRB, wPosLM, wPosC, wPosRM, wPosLT, wPosMT, wPosRT] = this._worldPositions;
+ const [uvLB, uvMB, uvRB, uvLM, uvC, uvRM, uvLT, uvMT, uvRT] = this._uvs;
+
+ wPosLB.set(lPosLB.x, lPosLB.y, 0).transformToVec3(matrix), uvLB.set(left, bottom);
+ wPosRB.set(lPosRB.x, lPosRB.y, 0).transformToVec3(matrix), uvRB.set(right, bottom);
+ wPosLT.set(lPosLT.x, lPosLT.y, 0).transformToVec3(matrix), uvLT.set(left, top);
+ wPosRT.set(lPosRT.x, lPosRT.y, 0).transformToVec3(matrix), uvRT.set(right, top);
+ Vector3.lerp(wPosLB, wPosRB, 0.5, wPosMB), Vector2.lerp(uvLB, uvRB, 0.5, uvMB);
+ Vector3.lerp(wPosLB, wPosLT, 0.5, wPosLM), Vector2.lerp(uvLB, uvLT, 0.5, uvLM);
+ Vector3.lerp(wPosLT, wPosRT, 0.5, wPosMT), Vector2.lerp(uvLT, uvRT, 0.5, uvMT);
+ Vector3.lerp(wPosRB, wPosRT, 0.5, wPosRM), Vector2.lerp(uvRB, uvRT, 0.5, uvRM);
+ Vector3.lerp(wPosLB, wPosRT, 0.5, wPosC), Vector2.lerp(uvLB, uvRT, 0.5, uvC);
+
+ this._vertexOffset = this._indicesOffset = 0;
+ const { _inPositions: inPositions, _inUVs: inUVs, _outPositions: outPositions, _outUVs: outUVs } = this;
+ const { _subChunk: subChunk } = renderer;
+ let quadrantStart = 0;
+ let quadrantEnd = 0;
+ (inPositions[0] = wPosC), (inUVs[0] = uvC);
+
+ {
+ // First quadrant (0°-90°)
+ if (startAngle >= 90) {
+ quadrantStart = startAngle - 360;
+ quadrantEnd = endAngle - 360;
+ } else {
+ quadrantStart = startAngle;
+ quadrantEnd = endAngle;
+ }
+ (inPositions[1] = wPosRM), (inUVs[1] = uvRM);
+ (inPositions[2] = wPosMT), (inUVs[2] = uvMT);
+ (inPositions[3] = wPosRT), (inUVs[3] = uvRT);
+ this._radialCut(subChunk, inPositions, inUVs, quadrantStart, quadrantEnd, outPositions, outUVs);
+ }
+
+ {
+ // Second quadrant (90°-180°)
+ if (startAngle >= 180) {
+ quadrantStart = startAngle - 360 - 90;
+ quadrantEnd = endAngle - 360 - 90;
+ } else {
+ quadrantStart = startAngle - 90;
+ quadrantEnd = endAngle - 90;
+ }
+ (inPositions[1] = wPosMT), (inUVs[1] = uvMT);
+ (inPositions[2] = wPosLM), (inUVs[2] = uvLM);
+ (inPositions[3] = wPosLT), (inUVs[3] = uvLT);
+ this._radialCut(subChunk, inPositions, inUVs, quadrantStart, quadrantEnd, outPositions, outUVs);
+ }
+
+ {
+ // Third quadrant (180°-270°)
+ if (startAngle >= 270) {
+ quadrantStart = startAngle - 360 - 180;
+ quadrantEnd = endAngle - 360 - 180;
+ } else {
+ quadrantStart = startAngle - 180;
+ quadrantEnd = endAngle - 180;
+ }
+ (inPositions[1] = wPosLM), (inUVs[1] = uvLM);
+ (inPositions[2] = wPosMB), (inUVs[2] = uvMB);
+ (inPositions[3] = wPosLB), (inUVs[3] = uvLB);
+ this._radialCut(subChunk, inPositions, inUVs, quadrantStart, quadrantEnd, outPositions, outUVs);
+ }
+
+ {
+ // Fourth quadrant (270°-360°)
+ if (startAngle >= 360) {
+ quadrantStart = startAngle - 360 - 270;
+ quadrantEnd = endAngle - 360 - 270;
+ } else {
+ quadrantStart = startAngle - 270;
+ quadrantEnd = endAngle - 270;
+ }
+ (inPositions[1] = wPosMB), (inUVs[1] = uvMB);
+ (inPositions[2] = wPosRM), (inUVs[2] = uvRM);
+ (inPositions[3] = wPosRB), (inUVs[3] = uvRB);
+ this._radialCut(subChunk, inPositions, inUVs, quadrantStart, quadrantEnd, outPositions, outUVs);
+ }
+
+ subChunk.indices.length = this._indicesOffset;
+ }
+
+ private static _radialCut(
+ subChunk: SubPrimitiveChunk,
+ positions: Vector3[],
+ uvs: Vector2[],
+ start: number,
+ end: number,
+ outPositions: Vector3[],
+ outUVs: Vector2[]
+ ): void {
+ if (start >= 90 || end <= 0) return;
+ outPositions[0].copyFrom(positions[0]);
+ outUVs[0].copyFrom(uvs[0]);
+
+ if (start <= 0) {
+ outPositions[1].copyFrom(positions[1]);
+ outUVs[1].copyFrom(uvs[1]);
+ } else {
+ const startTan = Math.tan((start * Math.PI) / 180);
+ if (startTan < 1) {
+ Vector3.lerp(positions[1], positions[3], startTan, outPositions[1]);
+ Vector2.lerp(uvs[1], uvs[3], startTan, outUVs[1]);
+ } else {
+ Vector3.lerp(positions[2], positions[3], 1 / startTan, outPositions[1]);
+ Vector2.lerp(uvs[2], uvs[3], 1 / startTan, outUVs[1]);
+ }
+ }
+
+ if (end >= 90) {
+ outPositions[2].copyFrom(positions[2]);
+ outUVs[2].copyFrom(uvs[2]);
+ } else {
+ const endTan = Math.tan((end * Math.PI) / 180);
+ if (endTan < 1) {
+ Vector3.lerp(positions[1], positions[3], endTan, outPositions[2]);
+ Vector2.lerp(uvs[1], uvs[3], endTan, outUVs[2]);
+ } else {
+ Vector3.lerp(positions[2], positions[3], 1 / endTan, outPositions[2]);
+ Vector2.lerp(uvs[2], uvs[3], 1 / endTan, outUVs[2]);
+ }
+ }
+
+ if (start < 45 && end > 45) {
+ outPositions[3].copyFrom(positions[3]);
+ outUVs[3].copyFrom(uvs[3]);
+ this._addQuad(subChunk, outPositions, outUVs);
+ } else {
+ this._addTriangle(subChunk, outPositions, outUVs);
+ }
+ }
+
+ private static _addTriangle(subChunk: SubPrimitiveChunk, positions: Vector3[], uvs: Vector2[]): void {
+ const vertices = subChunk.chunk.vertices;
+ const indices = subChunk.indices;
+ const vertexOffset = this._vertexOffset;
+ const vertexCount = vertexOffset / 9;
+ const start = subChunk.vertexArea.start + vertexOffset;
+ for (let i = 0, o = start; i < 3; ++i, o += 9) {
+ const position = positions[i];
+ const uv = uvs[i];
+ vertices[o] = position.x;
+ vertices[o + 1] = position.y;
+ vertices[o + 2] = position.z;
+ vertices[o + 3] = uv.x;
+ vertices[o + 4] = uv.y;
+ }
+ const indicesOffset = this._indicesOffset;
+ indices[indicesOffset] = vertexCount;
+ indices[indicesOffset + 1] = vertexCount + 1;
+ indices[indicesOffset + 2] = vertexCount + 2;
+ this._vertexOffset += 3 * 9;
+ this._indicesOffset += 3;
+ }
+
+ private static _addQuad(subChunk: SubPrimitiveChunk, positions: Vector3[], uvs: Vector2[]): void {
+ const vertices = subChunk.chunk.vertices;
+ const indices = subChunk.indices;
+ const vertexOffset = this._vertexOffset;
+ const vertexCount = vertexOffset / 9;
+ const start = subChunk.vertexArea.start + vertexOffset;
+ for (let i = 0, o = start; i < 4; ++i, o += 9) {
+ const position = positions[i];
+ const uv = uvs[i];
+ vertices[o] = position.x;
+ vertices[o + 1] = position.y;
+ vertices[o + 2] = position.z;
+ vertices[o + 3] = uv.x;
+ vertices[o + 4] = uv.y;
+ }
+ const indicesOffset = this._indicesOffset;
+ indices[indicesOffset] = vertexCount;
+ indices[indicesOffset + 1] = vertexCount + 1;
+ indices[indicesOffset + 2] = vertexCount + 2;
+ indices[indicesOffset + 3] = vertexCount + 2;
+ indices[indicesOffset + 4] = vertexCount + 1;
+ indices[indicesOffset + 5] = vertexCount + 3;
+ this._vertexOffset += 4 * 9;
+ this._indicesOffset += 6;
+ }
+}
diff --git a/packages/core/src/2d/assembler/ISpriteRenderer.ts b/packages/core/src/2d/assembler/ISpriteRenderer.ts
index a72f4e9436..04b50b3fc9 100644
--- a/packages/core/src/2d/assembler/ISpriteRenderer.ts
+++ b/packages/core/src/2d/assembler/ISpriteRenderer.ts
@@ -1,6 +1,8 @@
import { Color } from "@galacean/engine-math";
import { PrimitiveChunkManager } from "../../RenderPipeline/PrimitiveChunkManager";
import { SubPrimitiveChunk } from "../../RenderPipeline/SubPrimitiveChunk";
+import { SpriteFilledMode } from "../enums/SpriteFilledMode";
+import { SpriteFilledOrigin } from "../enums/SpriteFilledOrigin";
import { SpriteTileMode } from "../enums/SpriteTileMode";
import { Sprite } from "../sprite";
@@ -12,6 +14,10 @@ export interface ISpriteRenderer {
color?: Color;
tileMode?: SpriteTileMode;
tiledAdaptiveThreshold?: number;
+ filledMode?: SpriteFilledMode;
+ filledAmount?: number;
+ filledOrigin?: SpriteFilledOrigin;
+ filledClockWise?: boolean;
_subChunk: SubPrimitiveChunk;
_getChunkManager(): PrimitiveChunkManager;
}
diff --git a/packages/core/src/2d/assembler/SimpleSpriteAssembler.ts b/packages/core/src/2d/assembler/SimpleSpriteAssembler.ts
index 563f812106..6e0e07c368 100644
--- a/packages/core/src/2d/assembler/SimpleSpriteAssembler.ts
+++ b/packages/core/src/2d/assembler/SimpleSpriteAssembler.ts
@@ -66,20 +66,25 @@ export class SimpleSpriteAssembler {
}
static updateUVs(renderer: ISpriteRenderer): void {
+ // sprite._uvs 16 个网格点 (column-major, index=column*4+row),4 corner: [0]=LB / [3]=LT / [12]=RB / [15]=RT
+ // atlasRotated 由 sprite._updateUVs 内部处理,此处直接用结果,无需关心朝向
const spriteUVs = renderer.sprite._getUVs();
- const { x: left, y: bottom } = spriteUVs[0];
- const { x: right, y: top } = spriteUVs[3];
const subChunk = renderer._subChunk;
const vertices = subChunk.chunk.vertices;
const offset = subChunk.vertexArea.start + 3;
- vertices[offset] = left;
- vertices[offset + 1] = bottom;
- vertices[offset + 9] = right;
- vertices[offset + 10] = bottom;
- vertices[offset + 18] = left;
- vertices[offset + 19] = top;
- vertices[offset + 27] = right;
- vertices[offset + 28] = top;
+ const uvLB = spriteUVs[0];
+ const uvLT = spriteUVs[3];
+ const uvRB = spriteUVs[12];
+ const uvRT = spriteUVs[15];
+ // SimpleAssembler 的 vertex 顺序:0=LB, 1=RB, 2=LT, 3=RT (按 _rectangleTriangles 的拓扑)
+ vertices[offset] = uvLB.x;
+ vertices[offset + 1] = uvLB.y;
+ vertices[offset + 9] = uvRB.x;
+ vertices[offset + 10] = uvRB.y;
+ vertices[offset + 18] = uvLT.x;
+ vertices[offset + 19] = uvLT.y;
+ vertices[offset + 27] = uvRT.x;
+ vertices[offset + 28] = uvRT.y;
}
static updateColor(renderer: ISpriteRenderer, alpha: number): void {
diff --git a/packages/core/src/2d/assembler/SlicedSpriteAssembler.ts b/packages/core/src/2d/assembler/SlicedSpriteAssembler.ts
index 31d19d149c..09abce5be3 100644
--- a/packages/core/src/2d/assembler/SlicedSpriteAssembler.ts
+++ b/packages/core/src/2d/assembler/SlicedSpriteAssembler.ts
@@ -126,14 +126,15 @@ export class SlicedSpriteAssembler {
}
static updateUVs(renderer: ISpriteRenderer): void {
+ // 16 UV 网格 (column-major: index = i*4+j, i=column, j=row),与 vertex 索引一一对应
const subChunk = renderer._subChunk;
const vertices = subChunk.chunk.vertices;
const spriteUVs = renderer.sprite._getUVs();
for (let i = 0, o = subChunk.vertexArea.start + 3; i < 4; i++) {
- const rowU = spriteUVs[i].x;
for (let j = 0; j < 4; j++, o += 9) {
- vertices[o] = rowU;
- vertices[o + 1] = spriteUVs[j].y;
+ const uv = spriteUVs[i * 4 + j];
+ vertices[o] = uv.x;
+ vertices[o + 1] = uv.y;
}
}
}
diff --git a/packages/core/src/2d/assembler/TiledSpriteAssembler.ts b/packages/core/src/2d/assembler/TiledSpriteAssembler.ts
index bc765c45f9..448d0543a4 100644
--- a/packages/core/src/2d/assembler/TiledSpriteAssembler.ts
+++ b/packages/core/src/2d/assembler/TiledSpriteAssembler.ts
@@ -188,7 +188,12 @@ export class TiledSpriteAssembler {
const spritePositions = sprite._getPositions();
const { x: left, y: bottom } = spritePositions[0];
const { x: right, y: top } = spritePositions[3];
- const [spriteUV0, spriteUV1, spriteUV2, spriteUV3] = sprite._getUVs();
+ // 16 UV column-major: [0]=LB(left,bottom), [5]=border-LB(bLeft,bBottom), [10]=border-RT(bRight,bTop), [15]=RT(right,top)
+ const allUVs = sprite._getUVs();
+ const spriteUV0 = allUVs[0];
+ const spriteUV1 = allUVs[5];
+ const spriteUV2 = allUVs[10];
+ const spriteUV3 = allUVs[15];
const expectWidth = sprite.width * referenceResolutionPerUnit;
const expectHeight = sprite.height * referenceResolutionPerUnit;
const fixedL = expectWidth * border.x;
diff --git a/packages/core/src/2d/atlas/FontAtlas.ts b/packages/core/src/2d/atlas/FontAtlas.ts
index 36170f2f49..102c9620cc 100644
--- a/packages/core/src/2d/atlas/FontAtlas.ts
+++ b/packages/core/src/2d/atlas/FontAtlas.ts
@@ -12,10 +12,10 @@ export class FontAtlas extends ReferResource {
texture: Texture2D;
_charInfoMap: Record = {};
- private _space: number = 1;
- private _curX: number = 1;
- private _curY: number = 1;
- private _nextY: number = 1;
+ private _space: number = 4;
+ private _curX: number = 4;
+ private _curY: number = 4;
+ private _nextY: number = 4;
constructor(engine: Engine) {
super(engine);
diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts
index 44adadbfdd..93828efe2e 100644
--- a/packages/core/src/2d/atlas/SpriteAtlas.ts
+++ b/packages/core/src/2d/atlas/SpriteAtlas.ts
@@ -45,7 +45,7 @@ export class SpriteAtlas extends ReferResource {
sprite.name === name && outSprites.push(sprite);
}
} else {
- console.warn("The name of the sprite you want to find is not exit in SpriteAtlas.");
+ console.warn("There is no sprite named " + name + " in the atlas.");
}
return outSprites;
}
diff --git a/packages/core/src/2d/enums/SpriteDrawMode.ts b/packages/core/src/2d/enums/SpriteDrawMode.ts
index 46bcfd3783..6f35d8c1ef 100644
--- a/packages/core/src/2d/enums/SpriteDrawMode.ts
+++ b/packages/core/src/2d/enums/SpriteDrawMode.ts
@@ -7,5 +7,7 @@ export enum SpriteDrawMode {
/** When modifying the size of the renderer, it scales to fill the range according to the sprite border settings. */
Sliced,
/** When modifying the size of the renderer, it will tile to fill the range according to the sprite border settings. */
- Tiled
+ Tiled,
+ /** Fill the sprite partially, controlled by fill amount, mode and origin. */
+ Filled
}
diff --git a/packages/core/src/2d/enums/SpriteFilledMode.ts b/packages/core/src/2d/enums/SpriteFilledMode.ts
new file mode 100644
index 0000000000..a01cda9e53
--- /dev/null
+++ b/packages/core/src/2d/enums/SpriteFilledMode.ts
@@ -0,0 +1,15 @@
+/**
+ * Sprite's filled mode enumeration.
+ */
+export enum SpriteFilledMode {
+ /** Fill horizontally. */
+ Horizontal,
+ /** Fill vertically. */
+ Vertical,
+ /** Fill radially over 90 degrees. */
+ Radial90,
+ /** Fill radially over 180 degrees. */
+ Radial180,
+ /** Fill radially over 360 degrees. */
+ Radial360
+}
diff --git a/packages/core/src/2d/enums/SpriteFilledOrigin.ts b/packages/core/src/2d/enums/SpriteFilledOrigin.ts
new file mode 100644
index 0000000000..e9b3193970
--- /dev/null
+++ b/packages/core/src/2d/enums/SpriteFilledOrigin.ts
@@ -0,0 +1,21 @@
+/**
+ * Sprite's filled origin enumeration.
+ */
+export enum SpriteFilledOrigin {
+ /** Origin at the right. */
+ Right,
+ /** Origin at the top-right. */
+ TopRight,
+ /** Origin at the top. */
+ Top,
+ /** Origin at the top-left. */
+ TopLeft,
+ /** Origin at the left. */
+ Left,
+ /** Origin at the bottom-left. */
+ BottomLeft,
+ /** Origin at the bottom. */
+ Bottom,
+ /** Origin at the bottom-right. */
+ BottomRight
+}
diff --git a/packages/core/src/2d/enums/TextOverflow.ts b/packages/core/src/2d/enums/TextOverflow.ts
index adef863f1f..bf8fdf8aa4 100644
--- a/packages/core/src/2d/enums/TextOverflow.ts
+++ b/packages/core/src/2d/enums/TextOverflow.ts
@@ -1,9 +1,11 @@
/**
- * The way to handle the situation where wrapped text is too tall to fit in the height.
+ * The way to handle the situation where the text is too large to fit in the bounds.
*/
export enum OverflowMode {
/** Overflow when the text is too tall */
Overflow = 0,
/** Truncate with height when the text is too tall */
- Truncate = 1
+ Truncate = 1,
+ /** Shrink the font size until the text fits within the bounds (both width and height) */
+ Shrink = 2
}
diff --git a/packages/core/src/2d/index.ts b/packages/core/src/2d/index.ts
index 47be64ccfc..5481f42b28 100644
--- a/packages/core/src/2d/index.ts
+++ b/packages/core/src/2d/index.ts
@@ -1,11 +1,14 @@
export type { ISpriteAssembler } from "./assembler/ISpriteAssembler";
export type { ISpriteRenderer } from "./assembler/ISpriteRenderer";
+export { FilledSpriteAssembler } from "./assembler/FilledSpriteAssembler";
export { SimpleSpriteAssembler } from "./assembler/SimpleSpriteAssembler";
export { SlicedSpriteAssembler } from "./assembler/SlicedSpriteAssembler";
export { TiledSpriteAssembler } from "./assembler/TiledSpriteAssembler";
export { SpriteAtlas } from "./atlas/SpriteAtlas";
export { FontStyle } from "./enums/FontStyle";
export { SpriteDrawMode } from "./enums/SpriteDrawMode";
+export { SpriteFilledMode } from "./enums/SpriteFilledMode";
+export { SpriteFilledOrigin } from "./enums/SpriteFilledOrigin";
export { SpriteMaskInteraction } from "./enums/SpriteMaskInteraction";
export { SpriteModifyFlags } from "./enums/SpriteModifyFlags";
export { SpriteTileMode } from "./enums/SpriteTileMode";
diff --git a/packages/core/src/2d/sprite/MaskRenderable.ts b/packages/core/src/2d/sprite/MaskRenderable.ts
new file mode 100644
index 0000000000..441492ba85
--- /dev/null
+++ b/packages/core/src/2d/sprite/MaskRenderable.ts
@@ -0,0 +1,398 @@
+import { BoundingBox, Vector2, Vector3 } from "@galacean/engine-math";
+import { RenderElement } from "../../RenderPipeline/RenderElement";
+import { VertexMergeBatcher } from "../../RenderPipeline/VertexMergeBatcher";
+import { Renderer, RendererUpdateFlags } from "../../Renderer";
+import { assignmentClone, ignoreClone } from "../../clone/CloneManager";
+import { SpriteMaskLayer } from "../../enums/SpriteMaskLayer";
+import { ShaderProperty } from "../../shader/ShaderProperty";
+import type { ISpriteRenderer } from "../assembler/ISpriteRenderer";
+import { SimpleSpriteAssembler } from "../assembler/SimpleSpriteAssembler";
+import { SpriteModifyFlags } from "../enums/SpriteModifyFlags";
+import { Sprite } from "./Sprite";
+import { SpriteMaskUtils } from "./SpriteMaskUtils";
+
+/**
+ * Public contract of the MaskRenderable mixin, used for declaration file generation.
+ */
+export interface IMaskRenderable {
+ influenceLayers: SpriteMaskLayer;
+ flipX: boolean;
+ flipY: boolean;
+ sprite: Sprite;
+ alphaCutoff: number;
+ _renderElement: RenderElement;
+ _maskIndex: number;
+ _containsWorldPoint(worldPoint: Vector3): boolean;
+ _initMask(): void;
+ _cloneMaskData(target: IMaskRenderable): void;
+ _destroyMaskResources(): void;
+ _updateMaskBounds(worldBounds: BoundingBox): void;
+ _renderMask(distanceForSort: number): void;
+ _onSpriteChange(type: SpriteModifyFlags): void;
+ _onSpriteChangeExtra(type: SpriteModifyFlags): void;
+ _getSpriteWidth(): number;
+ _getSpriteHeight(): number;
+ _getSpritePivot(): Vector2;
+}
+
+type RendererConstructor = abstract new (...args: any[]) => Renderer;
+
+/**
+ * Mixin that provides shared mask rendering logic for both 2D SpriteMask and UI Mask.
+ */
+export function MaskRenderable(
+ Base: T
+): (abstract new (...args: any[]) => IMaskRenderable) & T {
+ abstract class MaskRenderableBase extends Base implements IMaskRenderable {
+ private static _maskTextureProperty = ShaderProperty.getByName("renderer_MaskTexture");
+ private static _alphaCutoffProperty = ShaderProperty.getByName("renderer_MaskAlphaCutoff");
+
+ @assignmentClone
+ private _influenceLayers: SpriteMaskLayer = SpriteMaskLayer.Everything;
+ /** @internal */
+ @ignoreClone
+ _renderElement: RenderElement;
+ /** @internal */
+ @ignoreClone
+ _maskIndex: number = -1;
+ @ignoreClone
+ private _sprite: Sprite = null;
+ @assignmentClone
+ private _flipX: boolean = false;
+ @assignmentClone
+ private _flipY: boolean = false;
+ @assignmentClone
+ private _alphaCutoff: number = 0.5;
+
+ /**
+ * The mask layers the sprite mask influence to.
+ */
+ get influenceLayers(): SpriteMaskLayer {
+ return this._influenceLayers;
+ }
+
+ set influenceLayers(value: SpriteMaskLayer) {
+ if (this._influenceLayers !== value) {
+ this._influenceLayers = value;
+ // @ts-ignore
+ if (this._phasedActiveInScene) {
+ // @ts-ignore
+ this.scene._maskManager.onMaskInfluenceLayersChange();
+ }
+ }
+ }
+
+ /**
+ * Flips the sprite on the X axis.
+ */
+ get flipX(): boolean {
+ return this._flipX;
+ }
+
+ set flipX(value: boolean) {
+ if (this._flipX !== value) {
+ this._flipX = value;
+ // @ts-ignore
+ this._dirtyUpdateFlag |= RendererUpdateFlags.WorldVolume;
+ }
+ }
+
+ /**
+ * Flips the sprite on the Y axis.
+ */
+ get flipY(): boolean {
+ return this._flipY;
+ }
+
+ set flipY(value: boolean) {
+ if (this._flipY !== value) {
+ this._flipY = value;
+ // @ts-ignore
+ this._dirtyUpdateFlag |= RendererUpdateFlags.WorldVolume;
+ }
+ }
+
+ /**
+ * The Sprite to render.
+ */
+ get sprite(): Sprite {
+ return this._sprite;
+ }
+
+ set sprite(value: Sprite | null) {
+ const lastSprite = this._sprite;
+ if (lastSprite !== value) {
+ if (lastSprite) {
+ // @ts-ignore
+ this._addResourceReferCount(lastSprite, -1);
+ lastSprite._updateFlagManager.removeListener(this._onSpriteChange);
+ }
+ // @ts-ignore
+ this._dirtyUpdateFlag |= MaskDirtyFlags.All;
+ if (value) {
+ // @ts-ignore
+ this._addResourceReferCount(value, 1);
+ value._updateFlagManager.addListener(this._onSpriteChange);
+ // @ts-ignore
+ this.shaderData.setTexture(MaskRenderableBase._maskTextureProperty, value.texture);
+ } else {
+ // @ts-ignore
+ this.shaderData.setTexture(MaskRenderableBase._maskTextureProperty, null);
+ }
+ this._sprite = value;
+ }
+ }
+
+ /**
+ * The minimum alpha value used by the mask to select the area of influence defined over the mask's sprite. Value between 0 and 1.
+ */
+ get alphaCutoff(): number {
+ return this._alphaCutoff;
+ }
+
+ set alphaCutoff(value: number) {
+ if (this._alphaCutoff !== value) {
+ this._alphaCutoff = value;
+ // @ts-ignore
+ this.shaderData.setFloat(MaskRenderableBase._alphaCutoffProperty, value);
+ }
+ }
+
+ /**
+ * @internal
+ */
+ // @ts-ignore
+ override _canBatch(preElement: RenderElement, curElement: RenderElement): boolean {
+ return VertexMergeBatcher.canBatchSpriteMask(preElement, curElement);
+ }
+
+ /**
+ * @internal
+ */
+ // @ts-ignore
+ override _batch(preElement: RenderElement | null, curElement: RenderElement): void {
+ VertexMergeBatcher.batch(preElement, curElement);
+ }
+
+ /**
+ * @internal
+ */
+ // @ts-ignore
+ override _onEnableInScene(): void {
+ // @ts-ignore
+ super._onEnableInScene();
+ // @ts-ignore
+ this.scene._maskManager.addSpriteMask(this);
+ }
+
+ /**
+ * @internal
+ */
+ // @ts-ignore
+ override _onDisableInScene(): void {
+ // @ts-ignore
+ super._onDisableInScene();
+ // @ts-ignore
+ this.scene._maskManager.removeSpriteMask(this);
+ }
+
+ /**
+ * @internal
+ */
+ _containsWorldPoint(worldPoint: Vector3): boolean {
+ return SpriteMaskUtils.containsWorldPoint(
+ worldPoint,
+ this._sprite,
+ // @ts-ignore
+ this._transformEntity.transform.worldMatrix,
+ this._getSpriteWidth(),
+ this._getSpriteHeight(),
+ this._getSpritePivot(),
+ this._flipX,
+ this._flipY,
+ this._alphaCutoff
+ );
+ }
+
+ /**
+ * @internal
+ * Initialize shared mask resources. Must be called from subclass constructor.
+ */
+ _initMask(): void {
+ SimpleSpriteAssembler.resetData(this as unknown as ISpriteRenderer);
+ // @ts-ignore
+ this.setMaterial(this._engine._basicResources.spriteMaskDefaultMaterial);
+ // @ts-ignore
+ this.shaderData.setFloat(MaskRenderableBase._alphaCutoffProperty, this._alphaCutoff);
+ this._renderElement = new RenderElement();
+ this._onSpriteChange = this._onSpriteChange.bind(this);
+ }
+
+ /**
+ * @internal
+ * Clone mask data to target. Called from subclass _cloneTo.
+ */
+ _cloneMaskData(target: MaskRenderableBase): void {
+ target.sprite = this._sprite;
+ }
+
+ /**
+ * @internal
+ * Release mask sprite resources. Called from subclass _onDestroy.
+ */
+ _destroyMaskResources(): void {
+ const sprite = this._sprite;
+ if (sprite) {
+ // @ts-ignore
+ this._addResourceReferCount(sprite, -1);
+ sprite._updateFlagManager.removeListener(this._onSpriteChange);
+ }
+ this._sprite = null;
+ this._renderElement = null;
+ }
+
+ /**
+ * @internal
+ * Update bounds using SimpleSpriteAssembler directly.
+ */
+ _updateMaskBounds(worldBounds: BoundingBox): void {
+ const sprite = this._sprite;
+ if (sprite) {
+ SimpleSpriteAssembler.updatePositions(
+ this as unknown as ISpriteRenderer,
+ // @ts-ignore
+ this._transformEntity.transform.worldMatrix,
+ this._getSpriteWidth(),
+ this._getSpriteHeight(),
+ this._getSpritePivot(),
+ this._flipX,
+ this._flipY
+ );
+ } else {
+ // @ts-ignore
+ const { worldPosition } = this._transformEntity.transform;
+ worldBounds.min.copyFrom(worldPosition);
+ worldBounds.max.copyFrom(worldPosition);
+ }
+ }
+
+ /**
+ * @internal
+ * Shared render logic for mask geometry.
+ */
+ _renderMask(distanceForSort: number): void {
+ const { _sprite: sprite } = this;
+ const width = this._getSpriteWidth();
+ const height = this._getSpriteHeight();
+ if (!sprite?.texture || !width || !height) {
+ return;
+ }
+
+ // @ts-ignore
+ let material = this.getMaterial();
+ if (!material) {
+ return;
+ }
+ if (material.destroyed) {
+ // @ts-ignore
+ material = this._engine._basicResources.spriteMaskDefaultMaterial;
+ }
+
+ // Update position
+ // @ts-ignore
+ if (this._dirtyUpdateFlag & RendererUpdateFlags.WorldVolume) {
+ SimpleSpriteAssembler.updatePositions(
+ this as unknown as ISpriteRenderer,
+ // @ts-ignore
+ this._transformEntity.transform.worldMatrix,
+ width,
+ height,
+ this._getSpritePivot(),
+ this._flipX,
+ this._flipY
+ );
+ // @ts-ignore
+ this._dirtyUpdateFlag &= ~RendererUpdateFlags.WorldVolume;
+ }
+
+ // Update uv
+ // @ts-ignore
+ if (this._dirtyUpdateFlag & MaskDirtyFlags.UV) {
+ SimpleSpriteAssembler.updateUVs(this as unknown as ISpriteRenderer);
+ // @ts-ignore
+ this._dirtyUpdateFlag &= ~MaskDirtyFlags.UV;
+ }
+
+ const renderElement = this._renderElement;
+ const subChunk = (this as any)._subChunk;
+ // @ts-ignore
+ renderElement.set(this, material, subChunk.chunk.primitive, subChunk.subMesh, sprite.texture, subChunk);
+ // @ts-ignore
+ renderElement.priority = this.priority;
+ renderElement.distanceForSort = distanceForSort;
+ renderElement.subShader = material.shader.subShaders[0];
+ }
+
+ /** @internal */
+ @ignoreClone
+ _onSpriteChange(type: SpriteModifyFlags): void {
+ switch (type) {
+ case SpriteModifyFlags.texture:
+ // @ts-ignore
+ this.shaderData.setTexture(MaskRenderableBase._maskTextureProperty, this.sprite.texture);
+ break;
+ case SpriteModifyFlags.region:
+ case SpriteModifyFlags.atlasRegionOffset:
+ // @ts-ignore
+ this._dirtyUpdateFlag |= MaskDirtyFlags.WorldVolumeAndUV;
+ break;
+ case SpriteModifyFlags.atlasRegion:
+ // @ts-ignore
+ this._dirtyUpdateFlag |= MaskDirtyFlags.UV;
+ break;
+ case SpriteModifyFlags.destroy:
+ this.sprite = null;
+ break;
+ default:
+ this._onSpriteChangeExtra(type);
+ break;
+ }
+ }
+
+ /**
+ * @internal
+ * Hook for subclass-specific sprite change handling.
+ * SpriteMask overrides this to handle size/pivot changes.
+ */
+ _onSpriteChangeExtra(type: SpriteModifyFlags): void {}
+
+ /** @internal */
+ _getSpriteWidth(): number {
+ return 0;
+ }
+ /** @internal */
+ _getSpriteHeight(): number {
+ return 0;
+ }
+ /** @internal */
+ _getSpritePivot(): Vector2 {
+ return null;
+ }
+ }
+
+ return MaskRenderableBase as unknown as (abstract new (...args: any[]) => IMaskRenderable) & T;
+}
+
+/**
+ * @remarks Extends `RendererUpdateFlags`.
+ */
+export enum MaskDirtyFlags {
+ /** UV. */
+ UV = 0x2,
+ /** Automatic Size. */
+ AutomaticSize = 0x8,
+ /** WorldVolume and UV. */
+ WorldVolumeAndUV = 0x3,
+ /** All. */
+ All = 0xb
+}
diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts
index 5232001c9a..b7f01f342e 100644
--- a/packages/core/src/2d/sprite/Sprite.ts
+++ b/packages/core/src/2d/sprite/Sprite.ts
@@ -20,7 +20,16 @@ export class Sprite extends ReferResource {
private _customHeight: number = undefined;
private _positions: Vector2[] = [new Vector2(), new Vector2(), new Vector2(), new Vector2()];
- private _uvs: Vector2[] = [new Vector2(), new Vector2(), new Vector2(), new Vector2()];
+ // 16 UV 顶点构成 4×4 网格(含 9-slice 内边界)。column-major:index = i*4 + j,i=column(0=left..3=right),j=row(0=bottom..3=top)。
+ // 与 SlicedAssembler/TiledAssembler 的 vertex 索引图一致:[0]=LB, [3]=LT, [12]=RB, [15]=RT。
+ // SimpleAssembler 取 [0]/[3]/[12]/[15] 4 个 corner;Sliced/Tiled 用全 16。
+ // prettier-ignore
+ private _uvs: Vector2[] = [
+ new Vector2(), new Vector2(), new Vector2(), new Vector2(),
+ new Vector2(), new Vector2(), new Vector2(), new Vector2(),
+ new Vector2(), new Vector2(), new Vector2(), new Vector2(),
+ new Vector2(), new Vector2(), new Vector2(), new Vector2(),
+ ];
private _bounds: BoundingBox = new BoundingBox();
private _texture: Texture2D = null;
@@ -287,16 +296,18 @@ export class Sprite extends ReferResource {
private _calDefaultSize(): void {
if (this._texture) {
- const { _texture, _atlasRegion, _atlasRegionOffset, _region } = this;
- const pixelsPerUnitReciprocal = 1.0 / Engine._pixelsPerUnit;
+ const { _texture, _atlasRegion, _atlasRegionOffset, _region, _atlasRotated } = this;
+ const ppuReciprocal = 1.0 / Engine._pixelsPerUnit;
+ // 先算 atlas 中绝对像素(texture 不一定是方形,必须各自乘对应维度)
+ const atlasPxW = _texture.width * _atlasRegion.width;
+ const atlasPxH = _texture.height * _atlasRegion.height;
+ // atlas 顺时针 pack 90°:原图 W×H 在 atlas 中占 H×W 区域,仅交换 atlasPx 的 W/H
+ const originWidth = _atlasRotated ? atlasPxH : atlasPxW;
+ const originHeight = _atlasRotated ? atlasPxW : atlasPxH;
this._automaticWidth =
- ((_texture.width * _atlasRegion.width) / (1 - _atlasRegionOffset.x - _atlasRegionOffset.z)) *
- _region.width *
- pixelsPerUnitReciprocal;
+ (originWidth / (1 - _atlasRegionOffset.x - _atlasRegionOffset.z)) * _region.width * ppuReciprocal;
this._automaticHeight =
- ((_texture.height * _atlasRegion.height) / (1 - _atlasRegionOffset.y - _atlasRegionOffset.w)) *
- _region.height *
- pixelsPerUnitReciprocal;
+ (originHeight / (1 - _atlasRegionOffset.y - _atlasRegionOffset.w)) * _region.height * ppuReciprocal;
} else {
this._automaticWidth = this._automaticHeight = 0;
}
@@ -332,34 +343,52 @@ export class Sprite extends ReferResource {
}
private _updateUVs(): void {
- const { _uvs: uv, _atlasRegionOffset: atlasRegionOffset } = this;
- const { x: regionX, y: regionY, width: regionW, height: regionH } = this._region;
- const regionRight = 1 - regionX - regionW;
- const regionBottom = 1 - regionY - regionH;
+ const { _uvs: uvs, _atlasRotated: atlasRotated, _border: border } = this;
+ const { x: regionLeft, y: regionTop, width: regionW, height: regionH } = this._region;
+ const regionRight = 1 - regionLeft - regionW;
+ const regionBottom = 1 - regionTop - regionH;
const { x: atlasRegionX, y: atlasRegionY, width: atlasRegionW, height: atlasRegionH } = this._atlasRegion;
- const { x: offsetLeft, y: offsetTop, z: offsetRight, w: offsetBottom } = atlasRegionOffset;
+ const { x: offsetLeft, y: offsetTop, z: offsetRight, w: offsetBottom } = this._atlasRegionOffset;
const realWidth = atlasRegionW / (1 - offsetLeft - offsetRight);
const realHeight = atlasRegionH / (1 - offsetTop - offsetBottom);
- // Coordinates of the four boundaries.
- const left = Math.max(regionX - offsetLeft, 0) * realWidth + atlasRegionX;
- const top = Math.max(regionBottom - offsetTop, 0) * realHeight + atlasRegionY;
- const right = atlasRegionW + atlasRegionX - Math.max(regionRight - offsetRight, 0) * realWidth;
- const bottom = atlasRegionH + atlasRegionY - Math.max(regionY - offsetBottom, 0) * realHeight;
- const { x: borderLeft, y: borderBottom, z: borderRight, w: borderTop } = this._border;
- // Left-Bottom
- uv[0].set(left, bottom);
- // Border ( Left-Bottom )
- uv[1].set(
- (regionX - offsetLeft + borderLeft * regionW) * realWidth + atlasRegionX,
- atlasRegionH + atlasRegionY - (regionY - offsetBottom + borderBottom * regionH) * realHeight
- );
- // Border ( Right-Top )
- uv[2].set(
- atlasRegionW + atlasRegionX - (regionRight - offsetRight + borderRight * regionW) * realWidth,
- (regionBottom - offsetTop + borderTop * regionH) * realHeight + atlasRegionY
- );
- // Right-Top
- uv[3].set(right, top);
+ // 4 个外边界 + 4 个 9-slice 内边界
+ let left: number, top: number, right: number, bottom: number;
+ let bLeft: number, bTop: number, bRight: number, bBottom: number;
+ if (atlasRotated) {
+ // 原图 region/offset (left/top/right/bottom) 在 atlas 中映射为 (bottom/left/top/right)
+ left = Math.max(regionBottom - offsetLeft, 0) * realWidth + atlasRegionX;
+ top = Math.max(regionLeft - offsetTop, 0) * realHeight + atlasRegionY;
+ right = atlasRegionW + atlasRegionX - Math.max(regionTop - offsetRight, 0) * realWidth;
+ bottom = atlasRegionH + atlasRegionY - Math.max(regionRight - offsetBottom, 0) * realHeight;
+ bLeft = (regionBottom - offsetLeft + border.y * regionH) * realWidth + atlasRegionX;
+ bTop = (regionLeft - offsetTop + border.x * regionW) * realHeight + atlasRegionY;
+ bRight = atlasRegionW + atlasRegionX - (regionTop - offsetRight + border.w * regionH) * realWidth;
+ bBottom = atlasRegionH + atlasRegionY - (regionRight - offsetBottom + border.z * regionW) * realHeight;
+ } else {
+ left = Math.max(regionLeft - offsetLeft, 0) * realWidth + atlasRegionX;
+ top = Math.max(regionBottom - offsetTop, 0) * realHeight + atlasRegionY;
+ right = atlasRegionW + atlasRegionX - Math.max(regionRight - offsetRight, 0) * realWidth;
+ bottom = atlasRegionH + atlasRegionY - Math.max(regionTop - offsetBottom, 0) * realHeight;
+ bLeft = (regionLeft - offsetLeft + border.x * regionW) * realWidth + atlasRegionX;
+ bTop = (regionBottom - offsetTop + border.w * regionH) * realHeight + atlasRegionY;
+ bRight = atlasRegionW + atlasRegionX - (regionRight - offsetRight + border.z * regionW) * realWidth;
+ bBottom = atlasRegionH + atlasRegionY - (regionTop - offsetBottom + border.y * regionH) * realHeight;
+ }
+
+ // 16 UV 网格填充(column-major:index=i*4+j,i=column[0=left..3=right],j=row[0=bottom..3=top])
+ // - 非 rotated:column 决定 atlas X (left/bLeft/bRight/right),row 决定 atlas Y (bottom/bBottom/bTop/top)
+ // - rotated 90° 顺时针 packed:display column 对应 atlas Y 的反向(顶→底),display row 对应 atlas X
+ if (atlasRotated) {
+ uvs[0].set(left, top), uvs[1].set(bLeft, top), uvs[2].set(bRight, top), uvs[3].set(right, top);
+ uvs[4].set(left, bTop), uvs[5].set(bLeft, bTop), uvs[6].set(bRight, bTop), uvs[7].set(right, bTop);
+ uvs[8].set(left, bBottom), uvs[9].set(bLeft, bBottom), uvs[10].set(bRight, bBottom), uvs[11].set(right, bBottom);
+ uvs[12].set(left, bottom), uvs[13].set(bLeft, bottom), uvs[14].set(bRight, bottom), uvs[15].set(right, bottom);
+ } else {
+ uvs[0].set(left, bottom), uvs[1].set(left, bBottom), uvs[2].set(left, bTop), uvs[3].set(left, top);
+ uvs[4].set(bLeft, bottom), uvs[5].set(bLeft, bBottom), uvs[6].set(bLeft, bTop), uvs[7].set(bLeft, top);
+ uvs[8].set(bRight, bottom), uvs[9].set(bRight, bBottom), uvs[10].set(bRight, bTop), uvs[11].set(bRight, top);
+ uvs[12].set(right, bottom), uvs[13].set(right, bBottom), uvs[14].set(right, bTop), uvs[15].set(right, top);
+ }
this._dirtyUpdateFlag &= ~SpriteUpdateFlags.uvs;
}
diff --git a/packages/core/src/2d/sprite/SpriteMask.ts b/packages/core/src/2d/sprite/SpriteMask.ts
index 141f352d35..771faead34 100644
--- a/packages/core/src/2d/sprite/SpriteMask.ts
+++ b/packages/core/src/2d/sprite/SpriteMask.ts
@@ -1,46 +1,25 @@
import { BoundingBox } from "@galacean/engine-math";
import { Entity } from "../../Entity";
-import { RenderQueueFlags } from "../../RenderPipeline/BasicRenderPipeline";
-import { BatchUtils } from "../../RenderPipeline/BatchUtils";
import { PrimitiveChunkManager } from "../../RenderPipeline/PrimitiveChunkManager";
import { RenderContext } from "../../RenderPipeline/RenderContext";
-import { RenderElement } from "../../RenderPipeline/RenderElement";
import { SubPrimitiveChunk } from "../../RenderPipeline/SubPrimitiveChunk";
-import { SubRenderElement } from "../../RenderPipeline/SubRenderElement";
import { Renderer, RendererUpdateFlags } from "../../Renderer";
import { assignmentClone, ignoreClone } from "../../clone/CloneManager";
-import { SpriteMaskLayer } from "../../enums/SpriteMaskLayer";
import { ShaderProperty } from "../../shader/ShaderProperty";
-import { ISpriteRenderer } from "../assembler/ISpriteRenderer";
-import { SimpleSpriteAssembler } from "../assembler/SimpleSpriteAssembler";
import { SpriteModifyFlags } from "../enums/SpriteModifyFlags";
-import { Sprite } from "./Sprite";
+import { MaskDirtyFlags, MaskRenderable } from "./MaskRenderable";
/**
* A component for masking Sprites.
*/
-export class SpriteMask extends Renderer implements ISpriteRenderer {
- /** @internal */
- static _textureProperty: ShaderProperty = ShaderProperty.getByName("renderer_MaskTexture");
+export class SpriteMask extends MaskRenderable(Renderer) {
/** @internal */
static _alphaCutoffProperty: ShaderProperty = ShaderProperty.getByName("renderer_MaskAlphaCutoff");
-
- /** The mask layers the sprite mask influence to. */
- @assignmentClone
- influenceLayers: SpriteMaskLayer = SpriteMaskLayer.Everything;
/** @internal */
- @ignoreClone
- _renderElement: RenderElement;
-
+ static _textureProperty: ShaderProperty = ShaderProperty.getByName("renderer_MaskTexture");
/** @internal */
@ignoreClone
_subChunk: SubPrimitiveChunk;
- /** @internal */
- @ignoreClone
- _maskIndex: number = -1;
-
- @ignoreClone
- private _sprite: Sprite = null;
@ignoreClone
private _automaticWidth: number = 0;
@@ -50,13 +29,6 @@ export class SpriteMask extends Renderer implements ISpriteRenderer {
private _customWidth: number = undefined;
@assignmentClone
private _customHeight: number = undefined;
- @assignmentClone
- private _flipX: boolean = false;
- @assignmentClone
- private _flipY: boolean = false;
-
- @assignmentClone
- private _alphaCutoff: number = 0.5;
/**
* Render width (in world coordinates).
@@ -69,7 +41,7 @@ export class SpriteMask extends Renderer implements ISpriteRenderer {
if (this._customWidth !== undefined) {
return this._customWidth;
} else {
- this._dirtyUpdateFlag & SpriteMaskUpdateFlags.AutomaticSize && this._calDefaultSize();
+ this._dirtyUpdateFlag & MaskDirtyFlags.AutomaticSize && this._calDefaultSize();
return this._automaticWidth;
}
}
@@ -92,7 +64,7 @@ export class SpriteMask extends Renderer implements ISpriteRenderer {
if (this._customHeight !== undefined) {
return this._customHeight;
} else {
- this._dirtyUpdateFlag & SpriteMaskUpdateFlags.AutomaticSize && this._calDefaultSize();
+ this._dirtyUpdateFlag & MaskDirtyFlags.AutomaticSize && this._calDefaultSize();
return this._automaticHeight;
}
}
@@ -104,93 +76,20 @@ export class SpriteMask extends Renderer implements ISpriteRenderer {
}
}
- /**
- * Flips the sprite on the X axis.
- */
- get flipX(): boolean {
- return this._flipX;
- }
-
- set flipX(value: boolean) {
- if (this._flipX !== value) {
- this._flipX = value;
- this._dirtyUpdateFlag |= RendererUpdateFlags.WorldVolume;
- }
- }
-
- /**
- * Flips the sprite on the Y axis.
- */
- get flipY(): boolean {
- return this._flipY;
- }
-
- set flipY(value: boolean) {
- if (this._flipY !== value) {
- this._flipY = value;
- this._dirtyUpdateFlag |= RendererUpdateFlags.WorldVolume;
- }
- }
-
- /**
- * The Sprite to render.
- */
- get sprite(): Sprite {
- return this._sprite;
- }
-
- set sprite(value: Sprite | null) {
- const lastSprite = this._sprite;
- if (lastSprite !== value) {
- if (lastSprite) {
- this._addResourceReferCount(lastSprite, -1);
- lastSprite._updateFlagManager.removeListener(this._onSpriteChange);
- }
- this._dirtyUpdateFlag |= SpriteMaskUpdateFlags.All;
- if (value) {
- this._addResourceReferCount(value, 1);
- value._updateFlagManager.addListener(this._onSpriteChange);
- this.shaderData.setTexture(SpriteMask._textureProperty, value.texture);
- } else {
- this.shaderData.setTexture(SpriteMask._textureProperty, null);
- }
- this._sprite = value;
- }
- }
-
- /**
- * The minimum alpha value used by the mask to select the area of influence defined over the mask's sprite. Value between 0 and 1.
- */
- get alphaCutoff(): number {
- return this._alphaCutoff;
- }
-
- set alphaCutoff(value: number) {
- if (this._alphaCutoff !== value) {
- this._alphaCutoff = value;
- this.shaderData.setFloat(SpriteMask._alphaCutoffProperty, value);
- }
- }
-
/**
* @internal
*/
constructor(entity: Entity) {
super(entity);
- SimpleSpriteAssembler.resetData(this);
- this.setMaterial(this._engine._basicResources.spriteMaskDefaultMaterial);
- this.shaderData.setFloat(SpriteMask._alphaCutoffProperty, this._alphaCutoff);
- this._renderElement = new RenderElement();
- this._renderElement.addSubRenderElement(new SubRenderElement());
- this._onSpriteChange = this._onSpriteChange.bind(this);
+ this._initMask();
}
/**
* @internal
*/
- override _updateTransformShaderData(context: RenderContext, onlyMVP: boolean, batched: boolean): void {
+ override _updateTransformShaderData(context: RenderContext, onlyMVP: boolean): void {
//@todo: Always update world positions to buffer, should opt
- super._updateTransformShaderData(context, onlyMVP, true);
+ this._updateWorldSpaceTransformShaderData(context, onlyMVP);
}
/**
@@ -198,37 +97,7 @@ export class SpriteMask extends Renderer implements ISpriteRenderer {
*/
override _cloneTo(target: SpriteMask): void {
super._cloneTo(target);
- target.sprite = this._sprite;
- }
-
- /**
- * @internal
- */
- override _canBatch(elementA: SubRenderElement, elementB: SubRenderElement): boolean {
- return BatchUtils.canBatchSpriteMask(elementA, elementB);
- }
-
- /**
- * @internal
- */
- override _batch(elementA: SubRenderElement, elementB?: SubRenderElement): void {
- BatchUtils.batchFor2D(elementA, elementB);
- }
-
- /**
- * @internal
- */
- override _onEnableInScene(): void {
- super._onEnableInScene();
- this.scene._maskManager.addSpriteMask(this);
- }
-
- /**
- * @internal
- */
- override _onDisableInScene(): void {
- super._onDisableInScene();
- this.scene._maskManager.removeSpriteMask(this);
+ this._cloneMaskData(target);
}
/**
@@ -239,147 +108,64 @@ export class SpriteMask extends Renderer implements ISpriteRenderer {
}
protected override _updateBounds(worldBounds: BoundingBox): void {
- const sprite = this._sprite;
- if (sprite) {
- SimpleSpriteAssembler.updatePositions(
- this,
- this._transformEntity.transform.worldMatrix,
- this.width,
- this.height,
- sprite.pivot,
- this._flipX,
- this._flipY
- );
- } else {
- const { worldPosition } = this._transformEntity.transform;
- worldBounds.min.copyFrom(worldPosition);
- worldBounds.max.copyFrom(worldPosition);
- }
+ this._updateMaskBounds(worldBounds);
}
/**
* @inheritdoc
*/
protected override _render(context: RenderContext): void {
- const { _sprite: sprite } = this;
- if (!sprite?.texture || !this.width || !this.height) {
- return;
- }
-
- let material = this.getMaterial();
- if (!material) {
- return;
- }
- const { _engine: engine } = this;
- // @todo: This question needs to be raised rather than hidden.
- if (material.destroyed) {
- material = engine._basicResources.spriteMaskDefaultMaterial;
- }
-
- // Update position
- if (this._dirtyUpdateFlag & RendererUpdateFlags.WorldVolume) {
- SimpleSpriteAssembler.updatePositions(
- this,
- this._transformEntity.transform.worldMatrix,
- this.width,
- this.height,
- sprite.pivot,
- this._flipX,
- this._flipY
- );
- this._dirtyUpdateFlag &= ~RendererUpdateFlags.WorldVolume;
- }
-
- // Update uv
- if (this._dirtyUpdateFlag & SpriteMaskUpdateFlags.UV) {
- SimpleSpriteAssembler.updateUVs(this);
- this._dirtyUpdateFlag &= ~SpriteMaskUpdateFlags.UV;
- }
-
- const renderElement = this._renderElement;
- const subRenderElement = renderElement.subRenderElements[0];
- renderElement.set(this.priority, this._distanceForSort);
-
- const subChunk = this._subChunk;
- subRenderElement.set(this, material, subChunk.chunk.primitive, subChunk.subMesh, this.sprite.texture, subChunk);
- subRenderElement.shaderPasses = material.shader.subShaders[0].passes;
- subRenderElement.renderQueueFlags = RenderQueueFlags.All;
- renderElement.addSubRenderElement(subRenderElement);
+ this._renderMask(this._distanceForSort);
}
/**
* @inheritdoc
*/
protected override _onDestroy(): void {
- const sprite = this._sprite;
- if (sprite) {
- this._addResourceReferCount(sprite, -1);
- sprite._updateFlagManager.removeListener(this._onSpriteChange);
- }
+ this._destroyMaskResources();
super._onDestroy();
- this._sprite = null;
if (this._subChunk) {
this._getChunkManager().freeSubChunk(this._subChunk);
this._subChunk = null;
}
+ }
- this._renderElement = null;
+ override _getSpriteWidth(): number {
+ return this.width;
}
- private _calDefaultSize(): void {
- const sprite = this._sprite;
- if (sprite) {
- this._automaticWidth = sprite.width;
- this._automaticHeight = sprite.height;
- } else {
- this._automaticWidth = this._automaticHeight = 0;
- }
- this._dirtyUpdateFlag &= ~SpriteMaskUpdateFlags.AutomaticSize;
+ override _getSpriteHeight(): number {
+ return this.height;
}
- @ignoreClone
- private _onSpriteChange(type: SpriteModifyFlags): void {
+ override _getSpritePivot() {
+ return this.sprite?.pivot;
+ }
+
+ override _onSpriteChangeExtra(type: SpriteModifyFlags): void {
switch (type) {
- case SpriteModifyFlags.texture:
- this.shaderData.setTexture(SpriteMask._textureProperty, this.sprite.texture);
- break;
case SpriteModifyFlags.size:
- this._dirtyUpdateFlag |= SpriteMaskUpdateFlags.AutomaticSize;
+ this._dirtyUpdateFlag |= MaskDirtyFlags.AutomaticSize;
if (this._customWidth === undefined || this._customHeight === undefined) {
this._dirtyUpdateFlag |= RendererUpdateFlags.WorldVolume;
}
break;
- case SpriteModifyFlags.region:
- case SpriteModifyFlags.atlasRegionOffset:
- this._dirtyUpdateFlag |= SpriteMaskUpdateFlags.WorldVolumeAndUV;
- break;
- case SpriteModifyFlags.atlasRegion:
- this._dirtyUpdateFlag |= SpriteMaskUpdateFlags.UV;
- break;
case SpriteModifyFlags.pivot:
this._dirtyUpdateFlag |= RendererUpdateFlags.WorldVolume;
break;
- case SpriteModifyFlags.destroy:
- this.sprite = null;
- break;
- default:
- break;
}
}
-}
-/**
- * @remarks Extends `RendererUpdateFlags`.
- */
-enum SpriteMaskUpdateFlags {
- /** UV. */
- UV = 0x2,
- /** Automatic Size. */
- AutomaticSize = 0x4,
- /** WorldVolume and UV. */
- WorldVolumeAndUV = 0x3,
- /** All. */
- All = 0x7
+ private _calDefaultSize(): void {
+ const sprite = this.sprite;
+ if (sprite) {
+ this._automaticWidth = sprite.width;
+ this._automaticHeight = sprite.height;
+ } else {
+ this._automaticWidth = this._automaticHeight = 0;
+ }
+ this._dirtyUpdateFlag &= ~MaskDirtyFlags.AutomaticSize;
+ }
}
diff --git a/packages/core/src/2d/sprite/SpriteMaskUtils.ts b/packages/core/src/2d/sprite/SpriteMaskUtils.ts
new file mode 100644
index 0000000000..b7033ee800
--- /dev/null
+++ b/packages/core/src/2d/sprite/SpriteMaskUtils.ts
@@ -0,0 +1,136 @@
+import { Matrix, Vector2, Vector3 } from "@galacean/engine-math";
+import { Texture2D, TextureFormat } from "../../texture";
+import { Sprite } from "./Sprite";
+
+/**
+ * Internal helpers for sprite mask hit testing.
+ * @internal
+ */
+export class SpriteMaskUtils {
+ private static _tempMat: Matrix = new Matrix();
+ private static _tempVec3: Vector3 = new Vector3();
+ private static _u8Buffer1 = new Uint8Array(1);
+ private static _u8Buffer2 = new Uint8Array(2);
+ private static _u8Buffer4 = new Uint8Array(4);
+ private static _u16Buffer1 = new Uint16Array(1);
+ private static _u16Buffer4 = new Uint16Array(4);
+ private static _f32Buffer4 = new Float32Array(4);
+ private static _u32Buffer4 = new Uint32Array(4);
+
+ static containsWorldPoint(
+ worldPoint: Vector3,
+ sprite: Sprite | null,
+ worldMatrix: Matrix,
+ width: number,
+ height: number,
+ pivot: Vector2,
+ flipX: boolean,
+ flipY: boolean,
+ alphaCutoff: number = 0
+ ): boolean {
+ if (!sprite || !width || !height) {
+ return false;
+ }
+
+ const worldMatrixInv = SpriteMaskUtils._tempMat;
+ Matrix.invert(worldMatrix, worldMatrixInv);
+ const localPosition = SpriteMaskUtils._tempVec3;
+ Vector3.transformCoordinate(worldPoint, worldMatrixInv, localPosition);
+
+ const sx = flipX ? -width : width;
+ const sy = flipY ? -height : height;
+ if (!sx || !sy) {
+ return false;
+ }
+
+ const spriteX = localPosition.x / sx + pivot.x;
+ const spriteY = localPosition.y / sy + pivot.y;
+ const spritePositions = sprite._getPositions();
+ const { x: left, y: bottom } = spritePositions[0];
+ const { x: right, y: top } = spritePositions[3];
+ if (!(spriteX >= left && spriteX <= right && spriteY >= bottom && spriteY <= top)) {
+ return false;
+ }
+
+ if (alphaCutoff <= 0) {
+ return true;
+ }
+
+ const texture = sprite.texture;
+ if (!texture) {
+ return false;
+ }
+
+ const spriteUVs = sprite._getUVs();
+ const leftU = spriteUVs[0].x;
+ const bottomV = spriteUVs[0].y;
+ const rightU = spriteUVs[3].x;
+ const topV = spriteUVs[3].y;
+ const positionWidth = right - left;
+ const positionHeight = top - bottom;
+ if (!positionWidth || !positionHeight) {
+ return false;
+ }
+
+ const tx = (spriteX - left) / positionWidth;
+ const ty = (spriteY - bottom) / positionHeight;
+ const u = leftU + (rightU - leftU) * tx;
+ const v = bottomV + (topV - bottomV) * ty;
+ const x = Math.min(Math.max(Math.floor(u * texture.width), 0), texture.width - 1);
+ const y = Math.min(Math.max(Math.floor(v * texture.height), 0), texture.height - 1);
+ return SpriteMaskUtils._sampleTextureAlpha(texture, x, y) >= alphaCutoff;
+ }
+
+ private static _sampleTextureAlpha(texture: Texture2D, x: number, y: number): number {
+ try {
+ switch (texture.format) {
+ case TextureFormat.R8G8B8A8: {
+ const buffer = SpriteMaskUtils._u8Buffer4;
+ texture.getPixelBuffer(x, y, 1, 1, buffer);
+ return buffer[3] / 255;
+ }
+ case TextureFormat.R4G4B4A4: {
+ const buffer = SpriteMaskUtils._u16Buffer1;
+ texture.getPixelBuffer(x, y, 1, 1, buffer);
+ return (buffer[0] & 0xf) / 15;
+ }
+ case TextureFormat.R5G5B5A1: {
+ const buffer = SpriteMaskUtils._u16Buffer1;
+ texture.getPixelBuffer(x, y, 1, 1, buffer);
+ return buffer[0] & 0x1;
+ }
+ case TextureFormat.Alpha8:
+ case TextureFormat.R8: {
+ const buffer = SpriteMaskUtils._u8Buffer1;
+ texture.getPixelBuffer(x, y, 1, 1, buffer);
+ return buffer[0] / 255;
+ }
+ case TextureFormat.LuminanceAlpha:
+ case TextureFormat.R8G8: {
+ const buffer = SpriteMaskUtils._u8Buffer2;
+ texture.getPixelBuffer(x, y, 1, 1, buffer);
+ return buffer[1] / 255;
+ }
+ case TextureFormat.R16G16B16A16: {
+ const buffer = SpriteMaskUtils._u16Buffer4;
+ texture.getPixelBuffer(x, y, 1, 1, buffer);
+ return buffer[3] / 65535;
+ }
+ case TextureFormat.R32G32B32A32: {
+ const buffer = SpriteMaskUtils._f32Buffer4;
+ texture.getPixelBuffer(x, y, 1, 1, buffer);
+ return buffer[3];
+ }
+ case TextureFormat.R32G32B32A32_UInt: {
+ const buffer = SpriteMaskUtils._u32Buffer4;
+ texture.getPixelBuffer(x, y, 1, 1, buffer);
+ return buffer[3] / 4294967295;
+ }
+ default:
+ return 1;
+ }
+ } catch {
+ return 1;
+ }
+ }
+}
diff --git a/packages/core/src/2d/sprite/SpriteRenderer.ts b/packages/core/src/2d/sprite/SpriteRenderer.ts
index c1b183ee7a..06bd3d3c6c 100644
--- a/packages/core/src/2d/sprite/SpriteRenderer.ts
+++ b/packages/core/src/2d/sprite/SpriteRenderer.ts
@@ -1,19 +1,22 @@
import { BoundingBox, Color, MathUtil } from "@galacean/engine-math";
import { Entity } from "../../Entity";
-import { BatchUtils } from "../../RenderPipeline/BatchUtils";
+import { VertexMergeBatcher } from "../../RenderPipeline/VertexMergeBatcher";
import { PrimitiveChunkManager } from "../../RenderPipeline/PrimitiveChunkManager";
import { RenderContext } from "../../RenderPipeline/RenderContext";
import { SubPrimitiveChunk } from "../../RenderPipeline/SubPrimitiveChunk";
-import { SubRenderElement } from "../../RenderPipeline/SubRenderElement";
+import { RenderElement } from "../../RenderPipeline/RenderElement";
import { Renderer, RendererUpdateFlags } from "../../Renderer";
import { assignmentClone, deepClone, ignoreClone } from "../../clone/CloneManager";
import { ShaderProperty } from "../../shader/ShaderProperty";
import { ISpriteAssembler } from "../assembler/ISpriteAssembler";
import { ISpriteRenderer } from "../assembler/ISpriteRenderer";
+import { FilledSpriteAssembler } from "../assembler/FilledSpriteAssembler";
import { SimpleSpriteAssembler } from "../assembler/SimpleSpriteAssembler";
import { SlicedSpriteAssembler } from "../assembler/SlicedSpriteAssembler";
import { TiledSpriteAssembler } from "../assembler/TiledSpriteAssembler";
import { SpriteDrawMode } from "../enums/SpriteDrawMode";
+import { SpriteFilledMode } from "../enums/SpriteFilledMode";
+import { SpriteFilledOrigin } from "../enums/SpriteFilledOrigin";
import { SpriteMaskInteraction } from "../enums/SpriteMaskInteraction";
import { SpriteModifyFlags } from "../enums/SpriteModifyFlags";
import { SpriteTileMode } from "../enums/SpriteTileMode";
@@ -39,6 +42,14 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer {
private _tileMode: SpriteTileMode = SpriteTileMode.Continuous;
@assignmentClone
private _tiledAdaptiveThreshold: number = 0.5;
+ @assignmentClone
+ private _filledMode: SpriteFilledMode = SpriteFilledMode.Radial360;
+ @assignmentClone
+ private _filledAmount: number = 1;
+ @assignmentClone
+ private _filledOrigin: SpriteFilledOrigin = SpriteFilledOrigin.Bottom;
+ @assignmentClone
+ private _filledClockWise: boolean = true;
@deepClone
private _color: Color = new Color(1, 1, 1, 1);
@@ -78,6 +89,9 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer {
case SpriteDrawMode.Tiled:
this._assembler = TiledSpriteAssembler;
break;
+ case SpriteDrawMode.Filled:
+ this._assembler = FilledSpriteAssembler;
+ break;
default:
break;
}
@@ -119,6 +133,74 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer {
}
}
+ /**
+ * The fill amount of the sprite renderer, range from 0 to 1. (Only works in filled mode.)
+ */
+ get filledAmount(): number {
+ return this._filledAmount;
+ }
+
+ set filledAmount(value: number) {
+ value = MathUtil.clamp(value, 0, 1);
+ if (this._filledAmount !== value) {
+ this._filledAmount = value;
+ if (this._drawMode === SpriteDrawMode.Filled) {
+ this._dirtyUpdateFlag |= SpriteRendererUpdateFlags.WorldVolumeAndUV;
+ }
+ }
+ }
+
+ /**
+ * The fill mode of the sprite renderer. (Only works in filled mode.)
+ */
+ get filledMode(): SpriteFilledMode {
+ return this._filledMode;
+ }
+
+ set filledMode(value: SpriteFilledMode) {
+ if (this._filledMode !== value) {
+ this._filledMode = value;
+ // Reset origin to a valid default for the new mode
+ this._filledOrigin =
+ value === SpriteFilledMode.Radial90 ? SpriteFilledOrigin.BottomLeft : SpriteFilledOrigin.Bottom;
+ if (this._drawMode === SpriteDrawMode.Filled) {
+ this._dirtyUpdateFlag |= SpriteRendererUpdateFlags.WorldVolumeAndUV;
+ }
+ }
+ }
+
+ /**
+ * The fill origin of the sprite renderer. (Only works in filled mode.)
+ */
+ get filledOrigin(): SpriteFilledOrigin {
+ return this._filledOrigin;
+ }
+
+ set filledOrigin(value: SpriteFilledOrigin) {
+ if (this._filledOrigin !== value) {
+ this._filledOrigin = value;
+ if (this._drawMode === SpriteDrawMode.Filled) {
+ this._dirtyUpdateFlag |= SpriteRendererUpdateFlags.WorldVolumeAndUV;
+ }
+ }
+ }
+
+ /**
+ * Whether the fill is clockwise. (Only works in filled radial mode.)
+ */
+ get filledClockWise(): boolean {
+ return this._filledClockWise;
+ }
+
+ set filledClockWise(value: boolean) {
+ if (this._filledClockWise !== value) {
+ this._filledClockWise = value;
+ if (this._drawMode === SpriteDrawMode.Filled) {
+ this._dirtyUpdateFlag |= SpriteRendererUpdateFlags.WorldVolumeAndUV;
+ }
+ }
+ }
+
/**
* The Sprite to render.
*/
@@ -278,9 +360,9 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer {
/**
* @internal
*/
- override _updateTransformShaderData(context: RenderContext, onlyMVP: boolean, batched: boolean): void {
+ override _updateTransformShaderData(context: RenderContext, onlyMVP: boolean): void {
//@todo: Always update world positions to buffer, should opt
- super._updateTransformShaderData(context, onlyMVP, true);
+ this._updateWorldSpaceTransformShaderData(context, onlyMVP);
}
/**
@@ -295,15 +377,15 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer {
/**
* @internal
*/
- override _canBatch(elementA: SubRenderElement, elementB: SubRenderElement): boolean {
- return BatchUtils.canBatchSprite(elementA, elementB);
+ override _canBatch(preElement: RenderElement, curElement: RenderElement): boolean {
+ return VertexMergeBatcher.canBatchSprite(preElement, curElement);
}
/**
* @internal
*/
- override _batch(elementA: SubRenderElement, elementB?: SubRenderElement): void {
- BatchUtils.batchFor2D(elementA, elementB);
+ override _batch(preElement: RenderElement | null, curElement: RenderElement): void {
+ VertexMergeBatcher.batch(preElement, curElement);
}
/**
@@ -377,11 +459,10 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer {
const camera = context.camera;
const engine = camera.engine;
const renderElement = engine._renderElementPool.get();
- renderElement.set(this.priority, this._distanceForSort);
- const subRenderElement = engine._subRenderElementPool.get();
const subChunk = this._subChunk;
- subRenderElement.set(this, material, subChunk.chunk.primitive, subChunk.subMesh, this.sprite.texture, subChunk);
- renderElement.addSubRenderElement(subRenderElement);
+ renderElement.set(this, material, subChunk.chunk.primitive, subChunk.subMesh, this.sprite.texture, subChunk);
+ renderElement.priority = this.priority;
+ renderElement.distanceForSort = this._distanceForSort;
camera._renderPipeline.pushRenderElement(context, renderElement);
}
@@ -438,6 +519,9 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer {
case SpriteDrawMode.Tiled:
this._dirtyUpdateFlag |= SpriteRendererUpdateFlags.WorldVolumeUVAndColor;
break;
+ case SpriteDrawMode.Filled:
+ this._dirtyUpdateFlag |= SpriteRendererUpdateFlags.WorldVolumeUVAndColor;
+ break;
}
break;
case SpriteModifyFlags.border:
diff --git a/packages/core/src/2d/sprite/index.ts b/packages/core/src/2d/sprite/index.ts
index 162d016472..c48fb9261b 100644
--- a/packages/core/src/2d/sprite/index.ts
+++ b/packages/core/src/2d/sprite/index.ts
@@ -1,3 +1,6 @@
+export type { IMaskRenderable } from "./MaskRenderable";
+export { MaskDirtyFlags, MaskRenderable } from "./MaskRenderable";
export { Sprite } from "./Sprite";
export { SpriteMask } from "./SpriteMask";
+export { SpriteMaskUtils } from "./SpriteMaskUtils";
export { SpriteRenderer } from "./SpriteRenderer";
diff --git a/packages/core/src/2d/text/TextRenderer.ts b/packages/core/src/2d/text/TextRenderer.ts
index 143e46a7da..7d2c26b2e0 100644
--- a/packages/core/src/2d/text/TextRenderer.ts
+++ b/packages/core/src/2d/text/TextRenderer.ts
@@ -1,14 +1,15 @@
-import { BoundingBox, Color, Vector3 } from "@galacean/engine-math";
+import { BoundingBox, Color, Vector2, Vector3 } from "@galacean/engine-math";
import { Engine } from "../../Engine";
import { Entity } from "../../Entity";
-import { BatchUtils } from "../../RenderPipeline/BatchUtils";
import { PrimitiveChunkManager } from "../../RenderPipeline/PrimitiveChunkManager";
import { RenderContext } from "../../RenderPipeline/RenderContext";
+import { RenderElement } from "../../RenderPipeline/RenderElement";
import { SubPrimitiveChunk } from "../../RenderPipeline/SubPrimitiveChunk";
-import { SubRenderElement } from "../../RenderPipeline/SubRenderElement";
+import { VertexMergeBatcher } from "../../RenderPipeline/VertexMergeBatcher";
import { Renderer } from "../../Renderer";
import { TransformModifyFlags } from "../../Transform";
import { assignmentClone, deepClone, ignoreClone } from "../../clone/CloneManager";
+import { SpriteMaskLayer } from "../../enums/SpriteMaskLayer";
import { ShaderData, ShaderProperty } from "../../shader";
import { ShaderDataGroup } from "../../shader/enums/ShaderDataGroup";
import { Texture2D } from "../../texture";
@@ -21,15 +22,18 @@ import { Font } from "./Font";
import { ITextRenderer } from "./ITextRenderer";
import { SubFont } from "./SubFont";
import { TextUtils } from "./TextUtils";
-import { SpriteMaskLayer } from "../../enums/SpriteMaskLayer";
/**
* Renders a text for 2D graphics.
*/
export class TextRenderer extends Renderer implements ITextRenderer {
- private static _textureProperty = ShaderProperty.getByName("renderElement_TextTexture");
+ private static _tempVec20 = new Vector2();
private static _tempVec30 = new Vector3();
private static _tempVec31 = new Vector3();
+ private static _textureProperty = ShaderProperty.getByName("renderElement_TextTexture");
+ private static _textTextureSizeProperty = ShaderProperty.getByName("renderElement_TextTextureSize");
+ private static _outlineColorProperty = ShaderProperty.getByName("renderer_OutlineColor");
+ private static _outlineWidthProperty = ShaderProperty.getByName("renderer_OutlineWidth");
private static _worldPositions = [new Vector3(), new Vector3(), new Vector3(), new Vector3()];
private static _charRenderInfos: CharRenderInfo[] = [];
@@ -69,6 +73,10 @@ export class TextRenderer extends Renderer implements ITextRenderer {
private _enableWrapping = false;
@assignmentClone
private _overflowMode = OverflowMode.Overflow;
+ @deepClone
+ private _outlineColor = new Color(0, 0, 0, 1);
+ @ignoreClone
+ private _outlineWidth = 0;
/**
* Rendering color for the Text.
@@ -255,6 +263,35 @@ export class TextRenderer extends Renderer implements ITextRenderer {
}
}
+ /**
+ * The outline width in pixels. 0 means outline is disabled. Clamped to [0, 8].
+ */
+ get outlineWidth(): number {
+ return this._outlineWidth;
+ }
+
+ set outlineWidth(value: number) {
+ value = Math.max(0, Math.min(value, 3));
+ if (this._outlineWidth !== value) {
+ this._outlineWidth = value;
+ this.shaderData.setFloat(TextRenderer._outlineWidthProperty, value);
+ this._setDirtyFlagTrue(DirtyFlag.Position);
+ }
+ }
+
+ /**
+ * The outline color. Only effective when outlineWidth > 0.
+ */
+ get outlineColor(): Color {
+ return this._outlineColor;
+ }
+
+ set outlineColor(value: Color) {
+ if (this._outlineColor !== value) {
+ this._outlineColor.copyFrom(value);
+ }
+ }
+
/**
* Interacts with the masks.
*/
@@ -309,8 +346,13 @@ export class TextRenderer extends Renderer implements ITextRenderer {
this._font = engine._textDefaultFont;
this._addResourceReferCount(this._font, 1);
this.setMaterial(engine._basicResources.textDefaultMaterial);
+ const shaderData = this.shaderData;
+ shaderData.setFloat(TextRenderer._outlineWidthProperty, this._outlineWidth);
+ shaderData.setColor(TextRenderer._outlineColorProperty, this._outlineColor);
//@ts-ignore
this._color._onValueChanged = this._onColorChanged.bind(this);
+ // @ts-ignore
+ this._outlineColor._onValueChanged = this._onOutlineColorChanged.bind(this);
}
/**
@@ -337,6 +379,7 @@ export class TextRenderer extends Renderer implements ITextRenderer {
super._cloneTo(target);
target.font = this._font;
target._subFont = this._subFont;
+ target.outlineWidth = this._outlineWidth;
}
/**
@@ -373,23 +416,23 @@ export class TextRenderer extends Renderer implements ITextRenderer {
/**
* @internal
*/
- override _updateTransformShaderData(context: RenderContext, onlyMVP: boolean, batched: boolean): void {
+ override _updateTransformShaderData(context: RenderContext, onlyMVP: boolean): void {
//@todo: Always update world positions to buffer, should opt
- super._updateTransformShaderData(context, onlyMVP, true);
+ this._updateWorldSpaceTransformShaderData(context, onlyMVP);
}
/**
* @internal
*/
- override _canBatch(elementA: SubRenderElement, elementB: SubRenderElement): boolean {
- return BatchUtils.canBatchSprite(elementA, elementB);
+ override _canBatch(preElement: RenderElement, curElement: RenderElement): boolean {
+ return VertexMergeBatcher.canBatchText(preElement, curElement);
}
/**
* @internal
*/
- override _batch(elementA: SubRenderElement, elementB?: SubRenderElement): void {
- BatchUtils.batchFor2D(elementA, elementB);
+ override _batch(preElement: RenderElement | null, curElement: RenderElement): void {
+ VertexMergeBatcher.batch(preElement, curElement);
}
/**
@@ -430,20 +473,27 @@ export class TextRenderer extends Renderer implements ITextRenderer {
const camera = context.camera;
const engine = camera.engine;
- const textSubRenderElementPool = engine._textSubRenderElementPool;
+ const textRenderElementPool = engine._textRenderElementPool;
const material = this.getMaterial();
- const renderElement = engine._renderElementPool.get();
- renderElement.set(this.priority, this._distanceForSort);
+ const priority = this.priority;
+ const distanceForSort = this._distanceForSort;
+ const renderPipeline = camera._renderPipeline;
const textChunks = this._textChunks;
+ const textTextureSize = TextRenderer._tempVec20;
for (let i = 0, n = textChunks.length; i < n; ++i) {
const { subChunk, texture } = textChunks[i];
- const subRenderElement = textSubRenderElementPool.get();
- subRenderElement.set(this, material, subChunk.chunk.primitive, subChunk.subMesh, texture, subChunk);
- subRenderElement.shaderData ||= new ShaderData(ShaderDataGroup.RenderElement);
- subRenderElement.shaderData.setTexture(TextRenderer._textureProperty, texture);
- renderElement.addSubRenderElement(subRenderElement);
+ const renderElement = textRenderElementPool.get();
+ renderElement.set(this, material, subChunk.chunk.primitive, subChunk.subMesh, texture, subChunk);
+ renderElement.shaderData ||= new ShaderData(ShaderDataGroup.RenderElement);
+ renderElement.shaderData.setTexture(TextRenderer._textureProperty, texture);
+ renderElement.shaderData.setVector2(
+ TextRenderer._textTextureSizeProperty,
+ textTextureSize.set(texture.width, texture.height)
+ );
+ renderElement.priority = priority;
+ renderElement.distanceForSort = distanceForSort;
+ renderPipeline.pushRenderElement(context, renderElement);
}
- camera._renderPipeline.pushRenderElement(context, renderElement);
}
private _resetSubFont(): void {
@@ -452,6 +502,16 @@ export class TextRenderer extends Renderer implements ITextRenderer {
this._subFont.nativeFontString = TextUtils.getNativeFontString(font.name, this.fontSize, this.fontStyle);
}
+ /**
+ * Switch the sub font to a specific font size, used by the SHRINK overflow measurement.
+ */
+ private _applyFontSizeForShrink(fontSize: number): void {
+ const font = this._font;
+ const subFont = font._getSubFont(fontSize, this._fontStyle);
+ subFont.nativeFontString = TextUtils.getNativeFontString(font.name, fontSize, this._fontStyle);
+ this._subFont = subFont;
+ }
+
private _updatePosition(): void {
const { transform } = this.entity;
const e = transform.worldMatrix.elements;
@@ -525,22 +585,37 @@ export class TextRenderer extends Renderer implements ITextRenderer {
const { _pixelsPerUnit } = Engine;
const { min, max } = this._localBounds;
const charRenderInfos = TextRenderer._charRenderInfos;
+ const rendererWidth = this.width * _pixelsPerUnit;
+ const rendererHeight = this.height * _pixelsPerUnit;
+ let fontSize = this._fontSize;
+ let textMetrics: ReturnType;
+ if (this._overflowMode === OverflowMode.Shrink) {
+ const result = TextUtils.measureTextWithShrink(
+ this,
+ rendererWidth,
+ rendererHeight,
+ this._fontSize,
+ this._lineSpacing,
+ this._characterSpacing,
+ this.enableWrapping,
+ (size) => this._applyFontSizeForShrink(size)
+ );
+ fontSize = result.fontSize;
+ textMetrics = result.metrics;
+ } else {
+ const characterSpacing = this._characterSpacing * fontSize;
+ textMetrics = this.enableWrapping
+ ? TextUtils.measureTextWithWrap(
+ this,
+ rendererWidth,
+ rendererHeight,
+ this._lineSpacing * fontSize,
+ characterSpacing
+ )
+ : TextUtils.measureTextWithoutWrap(this, rendererHeight, this._lineSpacing * fontSize, characterSpacing);
+ }
const charFont = this._getSubFont();
- const characterSpacing = this._characterSpacing * this._fontSize;
- const textMetrics = this.enableWrapping
- ? TextUtils.measureTextWithWrap(
- this,
- this.width * _pixelsPerUnit,
- this.height * _pixelsPerUnit,
- this._lineSpacing * this._fontSize,
- characterSpacing
- )
- : TextUtils.measureTextWithoutWrap(
- this,
- this.height * _pixelsPerUnit,
- this._lineSpacing * this._fontSize,
- characterSpacing
- );
+ const characterSpacing = this._characterSpacing * fontSize;
const { height, lines, lineWidths, lineHeight, lineMaxSizes } = textMetrics;
const charRenderInfoPool = this.engine._charRenderInfoPool;
const linesLen = lines.length;
@@ -549,9 +624,7 @@ export class TextRenderer extends Renderer implements ITextRenderer {
if (linesLen > 0) {
const { horizontalAlignment } = this;
const pixelsPerUnitReciprocal = 1.0 / _pixelsPerUnit;
- const rendererWidth = this._width * _pixelsPerUnit;
const halfRendererWidth = rendererWidth * 0.5;
- const rendererHeight = this._height * _pixelsPerUnit;
const halfLineHeight = lineHeight * 0.5;
let startY = 0;
@@ -562,7 +635,10 @@ export class TextRenderer extends Renderer implements ITextRenderer {
startY = rendererHeight * 0.5 - halfLineHeight + topDiff;
break;
case TextVerticalAlignment.Center:
- startY = height * 0.5 - halfLineHeight - (bottomDiff - topDiff) * 0.5;
+ // Center the text block (lineHeight * lineCount) within the renderer, independent of
+ // `height` — which equals the renderer height for Truncate/Shrink and would otherwise
+ // push the text upward by (rendererHeight - blockHeight) / 2 when the box is taller.
+ startY = lineHeight * linesLen * 0.5 - halfLineHeight - (bottomDiff - topDiff) * 0.5;
break;
case TextVerticalAlignment.Bottom:
startY = height - rendererHeight * 0.5 - halfLineHeight - bottomDiff;
@@ -604,10 +680,11 @@ export class TextRenderer extends Renderer implements ITextRenderer {
charRenderInfo.texture = charFont._getTextureByIndex(charInfo.index);
charRenderInfo.uvs = charInfo.uvs;
const { w, ascent, descent } = charInfo;
- const left = startX * pixelsPerUnitReciprocal;
- const right = (startX + w) * pixelsPerUnitReciprocal;
- const top = (startY + ascent) * pixelsPerUnitReciprocal;
- const bottom = (startY - descent) * pixelsPerUnitReciprocal;
+ const ow = this._outlineWidth * pixelsPerUnitReciprocal;
+ const left = startX * pixelsPerUnitReciprocal - ow;
+ const right = (startX + w) * pixelsPerUnitReciprocal + ow;
+ const top = (startY + ascent) * pixelsPerUnitReciprocal + ow;
+ const bottom = (startY - descent) * pixelsPerUnitReciprocal - ow;
localPositions.set(left, top, right, bottom);
i === firstLine && (maxY = Math.max(maxY, top));
minY = Math.min(minY, bottom);
@@ -686,7 +763,7 @@ export class TextRenderer extends Renderer implements ITextRenderer {
this._text === "" ||
this._fontSize === 0 ||
(this.enableWrapping && this.width <= 0) ||
- (this.overflowMode === OverflowMode.Truncate && this.height <= 0)
+ ((this.overflowMode === OverflowMode.Truncate || this.overflowMode === OverflowMode.Shrink) && this.height <= 0)
);
}
@@ -698,6 +775,10 @@ export class TextRenderer extends Renderer implements ITextRenderer {
const vertices = subChunk.chunk.vertices;
const indices = (subChunk.indices = []);
const charRenderInfos = textChunk.charRenderInfos;
+ const ow = this._outlineWidth;
+ const texture = textChunk.texture;
+ const owU = ow > 0 ? ow / texture.width : 0;
+ const owV = ow > 0 ? ow / texture.height : 0;
for (let i = 0, ii = 0, io = 0, vo = subChunk.vertexArea.start + 3; i < count; ++i, io += 4) {
const charRenderInfo = charRenderInfos[i];
charRenderInfo.indexInChunk = i;
@@ -707,10 +788,13 @@ export class TextRenderer extends Renderer implements ITextRenderer {
indices[ii++] = tempIndices[j] + io;
}
- // Set uv and color for vertices
+ // Set uv and color for vertices, expand uv outward by outline width
for (let j = 0; j < 4; ++j, vo += 9) {
const uv = charRenderInfo.uvs[j];
- uv.copyToArray(vertices, vo);
+ const su = j === 1 || j === 2 ? 1 : -1;
+ const sv = j >= 2 ? 1 : -1;
+ vertices[vo] = uv.x + owU * su;
+ vertices[vo + 1] = uv.y + owV * sv;
vertices[vo + 2] = r;
vertices[vo + 3] = g;
vertices[vo + 4] = b;
@@ -743,6 +827,11 @@ export class TextRenderer extends Renderer implements ITextRenderer {
private _onColorChanged(): void {
this._setDirtyFlagTrue(DirtyFlag.Color);
}
+
+ @ignoreClone
+ private _onOutlineColorChanged(): void {
+ this.shaderData.setColor(TextRenderer._outlineColorProperty, this._outlineColor);
+ }
}
class TextChunk {
diff --git a/packages/core/src/2d/text/TextUtils.ts b/packages/core/src/2d/text/TextUtils.ts
index ce2440573f..5305049fa9 100644
--- a/packages/core/src/2d/text/TextUtils.ts
+++ b/packages/core/src/2d/text/TextUtils.ts
@@ -100,7 +100,8 @@ export class TextUtils {
rendererWidth: number,
rendererHeight: number,
lineSpacing: number,
- characterSpacing: number
+ characterSpacing: number,
+ uploadCharTexture: boolean = true
): TextMetrics {
const subFont = renderer._getSubFont();
const fontString = subFont.nativeFontString;
@@ -137,7 +138,7 @@ export class TextUtils {
for (let j = 0, m = subText.length; j < m; ++j) {
const char = subText[j];
- const charInfo = TextUtils._getCharInfo(char, fontString, subFont);
+ const charInfo = TextUtils._getCharInfo(char, fontString, subFont, uploadCharTexture);
const charCode = char.charCodeAt(0);
const isSpace = charCode === 32;
@@ -284,7 +285,8 @@ export class TextUtils {
renderer: ITextRenderer,
rendererHeight: number,
lineSpacing: number,
- characterSpacing: number
+ characterSpacing: number,
+ uploadCharTexture: boolean = true
): TextMetrics {
const subFont = renderer._getSubFont();
const fontString = subFont.nativeFontString;
@@ -306,7 +308,7 @@ export class TextUtils {
let maxDescent = 0;
for (let j = 0; j < lineLength; ++j) {
- const charInfo = TextUtils._getCharInfo(line[j], fontString, subFont);
+ const charInfo = TextUtils._getCharInfo(line[j], fontString, subFont, uploadCharTexture);
curWidth += charInfo.xAdvance;
const { offsetY } = charInfo;
const halfH = charInfo.h * 0.5;
@@ -337,6 +339,85 @@ export class TextUtils {
};
}
+ /**
+ * Measure text in SHRINK overflow mode: keep shrinking the font size until the text fits
+ * within the bounds (both width and height). Mirrors Cocos Creator's `Overflow.SHRINK`.
+ *
+ * @param renderer - The text renderer
+ * @param rendererWidth - The width of the bounds in pixels
+ * @param rendererHeight - The height of the bounds in pixels
+ * @param originalFontSize - The font size set on the renderer (the upper bound, never enlarged)
+ * @param lineSpacing - The line spacing ratio (relative to font size)
+ * @param characterSpacing - The character spacing ratio (relative to font size)
+ * @param enableWrapping - Whether wrapping is enabled
+ * @param applyFontSize - Callback that switches the renderer's sub font to the given font size
+ * @returns The fitted text metrics and the actual font size used for layout
+ */
+ static measureTextWithShrink(
+ renderer: ITextRenderer,
+ rendererWidth: number,
+ rendererHeight: number,
+ originalFontSize: number,
+ lineSpacing: number,
+ characterSpacing: number,
+ enableWrapping: boolean,
+ applyFontSize: (fontSize: number) => void
+ ): { metrics: TextMetrics; fontSize: number } {
+ // During the binary search we only need the text dimensions, so pass `uploadCharTexture=false`
+ // to avoid building GPU font atlases for the intermediate font sizes that won't be used.
+ // The fitted size is then re-measured once with upload=true to populate its atlas. This trades
+ // one extra full measure pass (CPU) for skipping ~log2(size) throwaway atlas textures (GPU);
+ // we can't reuse the search's last measure because its char bitmaps were never cached.
+ const measureAt = (fontSize: number, uploadCharTexture: boolean): TextMetrics => {
+ applyFontSize(fontSize);
+ return enableWrapping
+ ? TextUtils.measureTextWithWrap(
+ renderer,
+ rendererWidth,
+ rendererHeight,
+ lineSpacing * fontSize,
+ characterSpacing * fontSize,
+ uploadCharTexture
+ )
+ : TextUtils.measureTextWithoutWrap(
+ renderer,
+ rendererHeight,
+ lineSpacing * fontSize,
+ characterSpacing * fontSize,
+ uploadCharTexture
+ );
+ };
+ // The content height is `lineHeight * lineCount`; `metrics.height` is clamped to the bounds
+ // unless overflowMode is Overflow, so it can't be used to detect vertical overflow here.
+ const isFit = (metrics: TextMetrics): boolean =>
+ metrics.width <= rendererWidth && metrics.lineHeight * metrics.lines.length <= rendererHeight;
+
+ // If the text already fits at the original size, keep it (SHRINK only shrinks, never enlarges).
+ let metrics = measureAt(originalFontSize, false);
+ if (isFit(metrics)) {
+ metrics = measureAt(originalFontSize, true);
+ return { metrics, fontSize: originalFontSize };
+ }
+
+ // Binary search for the largest integer font size that fits (measure only, no atlas upload).
+ let low = 1;
+ let high = Math.floor(originalFontSize);
+ let fitFontSize = low;
+ while (low <= high) {
+ const mid = (low + high) >> 1;
+ metrics = measureAt(mid, false);
+ if (isFit(metrics)) {
+ fitFontSize = mid;
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ }
+ // Re-measure the fitted size with upload=true so the sub font / atlas correspond to it.
+ metrics = measureAt(fitFontSize, true);
+ return { metrics, fontSize: fitFontSize };
+ }
+
/**
* Get native font hash.
* @param fontName - The font name
@@ -462,12 +543,16 @@ export class TextUtils {
/**
* @internal
*/
- static _getCharInfo(char: string, fontString: string, font: SubFont): CharInfo {
+ static _getCharInfo(char: string, fontString: string, font: SubFont, uploadCharTexture: boolean = true): CharInfo {
let charInfo = font._getCharInfo(char);
if (!charInfo) {
charInfo = TextUtils.measureChar(char, fontString);
- font._uploadCharTexture(charInfo);
- font._addCharInfo(char, charInfo);
+ // SHRINK 的二分阶段只需字符尺寸(measureChar 已给出),传 uploadCharTexture=false 跳过 GPU
+ // 字形图集上传与缓存,避免给用不到的中间字号建字体 atlas 纹理。
+ if (uploadCharTexture) {
+ font._uploadCharTexture(charInfo);
+ font._addCharInfo(char, charInfo);
+ }
}
return charInfo;
diff --git a/packages/core/src/Camera.ts b/packages/core/src/Camera.ts
index 53b689dd84..3faf19a2ef 100644
--- a/packages/core/src/Camera.ts
+++ b/packages/core/src/Camera.ts
@@ -9,7 +9,7 @@ import { Transform } from "./Transform";
import { UpdateFlagManager } from "./UpdateFlagManager";
import { VirtualCamera } from "./VirtualCamera";
import { GLCapabilityType, Logger } from "./base";
-import { deepClone, ignoreClone } from "./clone/CloneManager";
+import { assignmentClone, deepClone, ignoreClone } from "./clone/CloneManager";
import { AntiAliasing } from "./enums/AntiAliasing";
import { CameraClearFlags } from "./enums/CameraClearFlags";
import { CameraModifyFlags } from "./enums/CameraModifyFlags";
@@ -125,8 +125,10 @@ export class Camera extends Component {
@deepClone
_virtualCamera: VirtualCamera = new VirtualCamera();
/** @internal */
+ @assignmentClone
_replacementShader: Shader = null;
/** @internal */
+ @assignmentClone
_replacementSubShaderTag: ShaderTagKey = null;
/** @internal */
_replacementFailureStrategy: ReplacementFailureStrategy = null;
@@ -934,7 +936,15 @@ export class Camera extends Component {
private _getInvViewProjMat(): Matrix {
if (this._isInvViewProjDirty.flag) {
this._isInvViewProjDirty.flag = false;
- Matrix.multiply(this._entity.transform.worldMatrix, this._getInverseProjectionMatrix(), this._invViewProjMat);
+ const matrix = this._invViewProjMat;
+ if (this._isCustomViewMatrix) {
+ Matrix.invert(this.viewMatrix, matrix);
+ } else {
+ // Ignore scale, consistent with viewMatrix getter
+ const transform = this._entity.transform;
+ Matrix.rotationTranslation(transform.worldRotationQuaternion, transform.worldPosition, matrix);
+ }
+ matrix.multiply(this._getInverseProjectionMatrix());
}
return this._invViewProjMat;
}
diff --git a/packages/core/src/ComponentsManager.ts b/packages/core/src/ComponentsManager.ts
index e9b9a1cc0d..7df837a336 100644
--- a/packages/core/src/ComponentsManager.ts
+++ b/packages/core/src/ComponentsManager.ts
@@ -36,9 +36,6 @@ export class ComponentsManager {
// Render
private _onUpdateRenderers = new DisorderedArray();
- // Delay dispose active/inActive Pool
- private _componentsContainerPool: Component[][] = [];
-
addCamera(camera: Camera) {
camera._cameraIndex = this._activeCameras.length;
this._activeCameras.add(camera);
@@ -270,15 +267,6 @@ export class ComponentsManager {
);
}
- getActiveChangedTempList(): Component[] {
- return this._componentsContainerPool.length ? this._componentsContainerPool.pop() : [];
- }
-
- putActiveChangedTempList(componentContainer: Component[]): void {
- componentContainer.length = 0;
- this._componentsContainerPool.push(componentContainer);
- }
-
/**
* @internal
*/
diff --git a/packages/core/src/Engine.ts b/packages/core/src/Engine.ts
index 55b0ea0b4c..69bceed1f3 100644
--- a/packages/core/src/Engine.ts
+++ b/packages/core/src/Engine.ts
@@ -15,9 +15,8 @@ import { EngineSettings } from "./EngineSettings";
import { Entity } from "./Entity";
import { BatcherManager } from "./RenderPipeline/BatcherManager";
import { RenderContext } from "./RenderPipeline/RenderContext";
-import { RenderElement } from "./RenderPipeline/RenderElement";
import { RenderTargetPool } from "./RenderPipeline/RenderTargetPool";
-import { SubRenderElement } from "./RenderPipeline/SubRenderElement";
+import { RenderElement } from "./RenderPipeline/RenderElement";
import { Scene } from "./Scene";
import { SceneManager } from "./SceneManager";
import { RenderingStatistics } from "./asset/RenderingStatistics";
@@ -33,7 +32,8 @@ import { Shader } from "./shader/Shader";
import { ShaderMacro } from "./shader/ShaderMacro";
import { ShaderMacroCollection } from "./shader/ShaderMacroCollection";
import { ShaderPool } from "./shader/ShaderPool";
-import { ShaderProgramPool } from "./shader/ShaderProgramPool";
+import { ShaderProgramMap } from "./shader/ShaderProgramMap";
+import { ShaderProgram } from "./shader/ShaderProgram";
import { RenderState } from "./shader/state/RenderState";
import { Texture2D, TextureFormat } from "./texture";
import { UIUtils } from "./ui/UIUtils";
@@ -93,9 +93,7 @@ export class Engine extends EventDispatcher {
/* @internal */
_renderElementPool = new ClearableObjectPool(RenderElement);
/* @internal */
- _subRenderElementPool = new ClearableObjectPool(SubRenderElement);
- /* @internal */
- _textSubRenderElementPool = new ClearableObjectPool(SubRenderElement);
+ _textRenderElementPool = new ClearableObjectPool(RenderElement);
/* @internal */
_charRenderInfoPool = new ReturnableObjectPool(CharRenderInfo, 50);
@@ -112,7 +110,7 @@ export class Engine extends EventDispatcher {
/* @internal */
_renderCount: number = 0;
/* @internal */
- _shaderProgramPools: ShaderProgramPool[] = [];
+ _shaderProgramMaps: ShaderProgramMap[] = [];
/** @internal */
_fontMap: Record = {};
/** @internal */
@@ -140,6 +138,22 @@ export class Engine extends EventDispatcher {
private _waitingGC: boolean = false;
private _postProcessPasses = new Array();
private _activePostProcessPasses = new Array();
+ private _lastCanvasWidth: number = -1;
+ private _lastCanvasHeight: number = -1;
+
+ /** Evict pool entries sized to the previous canvas dimensions. */
+ private _onCanvasResize = (): void => {
+ const canvas = this._canvas;
+ const newWidth = canvas.width;
+ const newHeight = canvas.height;
+ if (this._lastCanvasWidth !== newWidth || this._lastCanvasHeight !== newHeight) {
+ if (this._lastCanvasWidth >= 0) {
+ this._renderTargetPool.evictBySize(this._lastCanvasWidth, this._lastCanvasHeight);
+ }
+ this._lastCanvasWidth = newWidth;
+ this._lastCanvasHeight = newHeight;
+ }
+ };
private _animate = () => {
if (this._vSyncCount) {
@@ -258,6 +272,9 @@ export class Engine extends EventDispatcher {
this._batcherManager = new BatcherManager(this);
this._renderTargetPool = new RenderTargetPool(this);
+ this._lastCanvasWidth = canvas.width;
+ this._lastCanvasHeight = canvas.height;
+ canvas._sizeUpdateFlagManager.addListener(this._onCanvasResize);
this.inputManager = new InputManager(this, configuration.input);
const { xrDevice } = configuration;
@@ -326,12 +343,13 @@ export class Engine extends EventDispatcher {
const time = this._time;
time._update();
+ this._renderTargetPool.tick(time.frameCount);
+
const deltaTime = time.deltaTime;
this._frameInProcess = true;
- this._subRenderElementPool.clear();
- this._textSubRenderElementPool.clear();
this._renderElementPool.clear();
+ this._textRenderElementPool.clear();
this.xrManager?._update();
const { inputManager, _physicsInitialized: physicsInitialized } = this;
@@ -504,6 +522,8 @@ export class Engine extends EventDispatcher {
this._destroyed = true;
this._waitingDestroy = false;
+ this._canvas._sizeUpdateFlagManager.removeListener(this._onCanvasResize);
+
this._sceneManager._destroyAllScene();
this._resourceManager._destroy();
@@ -541,18 +561,18 @@ export class Engine extends EventDispatcher {
/**
* @internal
*/
- _getShaderProgramPool(index: number, trackPools?: ShaderProgramPool[]): ShaderProgramPool {
- const shaderProgramPools = this._shaderProgramPools;
- let pool = shaderProgramPools[index];
- if (!pool) {
+ _getShaderProgramMap(index: number, trackMaps?: ShaderProgramMap[]): ShaderProgramMap {
+ const shaderProgramMaps = this._shaderProgramMaps;
+ let map = shaderProgramMaps[index];
+ if (!map) {
const length = index + 1;
- if (length > shaderProgramPools.length) {
- shaderProgramPools.length = length;
+ if (length > shaderProgramMaps.length) {
+ shaderProgramMaps.length = length;
}
- shaderProgramPools[index] = pool = new ShaderProgramPool(this);
- trackPools?.push(pool);
+ shaderProgramMaps[index] = map = new ShaderProgramMap(this);
+ trackMaps?.push(map);
}
- return pool;
+ return map;
}
/**
@@ -674,9 +694,9 @@ export class Engine extends EventDispatcher {
private _onDeviceRestored(): void {
this._hardwareRenderer.resetState();
this._lastRenderState = new RenderState();
- // Clear shader pools
+ // Clear shader program maps
Shader._clear(this);
- this._shaderProgramPools.length = 0;
+ this._shaderProgramMaps.length = 0;
const { resourceManager } = this;
// Restore graphic resources
@@ -697,9 +717,8 @@ export class Engine extends EventDispatcher {
}
private _gc(): void {
- this._subRenderElementPool.garbageCollection();
- this._textSubRenderElementPool.garbageCollection();
this._renderElementPool.garbageCollection();
+ this._textRenderElementPool.garbageCollection();
this._renderContext.garbageCollection();
const scenes = this._sceneManager._scenes.getLoopArray();
for (let i = 0, n = scenes.length; i < n; i++) {
diff --git a/packages/core/src/Entity.ts b/packages/core/src/Entity.ts
index 08503f5498..5b0c929ac1 100644
--- a/packages/core/src/Entity.ts
+++ b/packages/core/src/Entity.ts
@@ -9,7 +9,7 @@ import { Script } from "./Script";
import { Transform } from "./Transform";
import { UpdateFlagManager } from "./UpdateFlagManager";
import { ReferResource } from "./asset/ReferResource";
-import { EngineObject } from "./base";
+import { EngineObject, Logger } from "./base";
import { CloneUtils } from "./clone/CloneUtils";
import { ComponentCloner } from "./clone/ComponentCloner";
import { ActiveChangeFlag } from "./enums/ActiveChangeFlag";
@@ -122,7 +122,7 @@ export class Entity extends EngineObject {
private _transform: Transform;
private _templateResource: ReferResource;
private _parent: Entity = null;
- private _activeChangedComponents: Component[];
+ private _isActiveChanging: boolean = false;
private _modifyFlagManager: UpdateFlagManager;
/**
@@ -212,16 +212,14 @@ export class Entity extends EngineObject {
}
set siblingIndex(value: number) {
- if (this._siblingIndex === -1) {
- throw `The entity ${this.name} is not in the hierarchy`;
- }
-
if (this._isRoot) {
this._setSiblingIndex(this._scene._rootEntities, value);
- } else {
+ } else if (this._parent) {
const parent = this._parent;
this._setSiblingIndex(parent._children, value);
parent._dispatchModify(EntityModifyFlags.Child, parent);
+ } else {
+ Logger.warn(`The entity ${this.name} is not in the hierarchy`);
}
}
@@ -403,6 +401,11 @@ export class Entity extends EngineObject {
for (let i = children.length - 1; i >= 0; i--) {
const child = children[i];
child._parent = null;
+ child._siblingIndex = -1;
+ // Dispatch `Child` to the old parent before `_processInActive` (which unregisters
+ // UI listeners via `cleanRootCanvas`), so subscribers such as UICanvas can react
+ // to the hierarchy change while still attached.
+ this._dispatchModify(EntityModifyFlags.Child, this);
let activeChangeFlag = ActiveChangeFlag.None;
child._isActiveInHierarchy && (activeChangeFlag |= ActiveChangeFlag.Hierarchy);
@@ -410,6 +413,8 @@ export class Entity extends EngineObject {
activeChangeFlag && child._processInActive(activeChangeFlag);
Entity._traverseSetOwnerScene(child, null); // Must after child._processInActive().
+
+ child._setParentChange();
}
children.length = 0;
}
@@ -565,24 +570,24 @@ export class Entity extends EngineObject {
* @internal
*/
_processActive(activeChangeFlag: ActiveChangeFlag): void {
- if (this._activeChangedComponents) {
+ if (this._isActiveChanging) {
throw "Note: can't set the 'main inActive entity' active in hierarchy, if the operation is in main inActive entity or it's children script's onDisable Event.";
}
- this._activeChangedComponents = this._scene._componentsManager.getActiveChangedTempList();
- this._setActiveInHierarchy(this._activeChangedComponents, activeChangeFlag);
- this._setActiveComponents(true, activeChangeFlag);
+ this._isActiveChanging = true;
+ this._setActiveInHierarchy(activeChangeFlag);
+ this._isActiveChanging = false;
}
/**
* @internal
*/
_processInActive(activeChangeFlag: ActiveChangeFlag): void {
- if (this._activeChangedComponents) {
+ if (this._isActiveChanging) {
throw "Note: can't set the 'main active entity' inActive in hierarchy, if the operation is in main active entity or it's children script's onEnable Event.";
}
- this._activeChangedComponents = this._scene._componentsManager.getActiveChangedTempList();
- this._setInActiveInHierarchy(this._activeChangedComponents, activeChangeFlag);
- this._setActiveComponents(false, activeChangeFlag);
+ this._isActiveChanging = true;
+ this._setInActiveInHierarchy(activeChangeFlag);
+ this._isActiveChanging = false;
}
/**
@@ -679,53 +684,46 @@ export class Entity extends EngineObject {
}
private _getComponentsInChildren(type: ComponentConstructor, results: T[]): void {
- for (let i = this._components.length - 1; i >= 0; i--) {
- const component = this._components[i];
+ const components = this._components;
+ for (let i = 0, n = components.length; i < n; i++) {
+ const component = components[i];
if (component instanceof type) {
results.push(component);
}
}
- for (let i = this._children.length - 1; i >= 0; i--) {
- this._children[i]._getComponentsInChildren(type, results);
- }
- }
-
- private _setActiveComponents(isActive: boolean, activeChangeFlag: ActiveChangeFlag): void {
- const activeChangedComponents = this._activeChangedComponents;
- for (let i = 0, length = activeChangedComponents.length; i < length; ++i) {
- activeChangedComponents[i]._setActive(isActive, activeChangeFlag);
+ const children = this._children;
+ for (let i = 0, n = children.length; i < n; i++) {
+ children[i]._getComponentsInChildren(type, results);
}
- this._scene._componentsManager.putActiveChangedTempList(activeChangedComponents);
- this._activeChangedComponents = null;
}
- private _setActiveInHierarchy(activeChangedComponents: Component[], activeChangeFlag: ActiveChangeFlag): void {
+ private _setActiveInHierarchy(activeChangeFlag: ActiveChangeFlag): void {
activeChangeFlag & ActiveChangeFlag.Hierarchy && (this._isActiveInHierarchy = true);
activeChangeFlag & ActiveChangeFlag.Scene && (this._isActiveInScene = true);
const components = this._components;
for (let i = 0, n = components.length; i < n; i++) {
const component = components[i];
- (component.enabled || !component._awoken) && activeChangedComponents.push(component);
+ (component.enabled || !component._awoken) && component._setActive(true, activeChangeFlag);
}
const children = this._children;
- for (let i = 0, n = children.length; i < n; i++) {
+ for (let i = children.length - 1; i >= 0; i--) {
const child = children[i];
- child.isActive && child._setActiveInHierarchy(activeChangedComponents, activeChangeFlag);
+ child.isActive && child._setActiveInHierarchy(activeChangeFlag);
}
}
- private _setInActiveInHierarchy(activeChangedComponents: Component[], activeChangeFlag: ActiveChangeFlag): void {
+ private _setInActiveInHierarchy(activeChangeFlag: ActiveChangeFlag): void {
activeChangeFlag & ActiveChangeFlag.Hierarchy && (this._isActiveInHierarchy = false);
activeChangeFlag & ActiveChangeFlag.Scene && (this._isActiveInScene = false);
const components = this._components;
for (let i = 0, n = components.length; i < n; i++) {
const component = components[i];
- component.enabled && activeChangedComponents.push(component);
+ component.enabled && component._setActive(false, activeChangeFlag);
}
const children = this._children;
- for (let i = 0, n = children.length; i < n; i++) {
+ for (let i = children.length - 1; i >= 0; i--) {
const child = children[i];
- child.isActive && child._setInActiveInHierarchy(activeChangedComponents, activeChangeFlag);
+ child.isActive && child._setInActiveInHierarchy(activeChangeFlag);
}
}
diff --git a/packages/core/src/RenderPipeline/BasicRenderPipeline.ts b/packages/core/src/RenderPipeline/BasicRenderPipeline.ts
index dee2231a0a..98522ef90e 100644
--- a/packages/core/src/RenderPipeline/BasicRenderPipeline.ts
+++ b/packages/core/src/RenderPipeline/BasicRenderPipeline.ts
@@ -10,7 +10,7 @@ import { ScalableAmbientObscurancePass } from "../lighting/ambientOcclusion/Scal
import { FinalPass } from "../postProcess";
import { Shader } from "../shader/Shader";
import { ShaderMacroCollection } from "../shader/ShaderMacroCollection";
-import { ShaderPass } from "../shader/ShaderPass";
+import { SubShader } from "../shader/SubShader";
import { RenderQueueType } from "../shader/enums/RenderQueueType";
import { RenderState } from "../shader/state/RenderState";
import { CascadedShadowCasterPass } from "../shadow/CascadedShadowCasterPass";
@@ -30,7 +30,6 @@ import { OpaqueTexturePass } from "./OpaqueTexturePass";
import { PipelineUtils } from "./PipelineUtils";
import { ContextRendererUpdateFlag, RenderContext } from "./RenderContext";
import { RenderElement } from "./RenderElement";
-import { SubRenderElement } from "./SubRenderElement";
import { PipelineStage } from "./enums/PipelineStage";
/**
* Basic render pipeline.
@@ -132,15 +131,17 @@ export class BasicRenderPipeline {
this._cascadedShadowCasterPass.onRender(context);
}
- const batcherManager = engine._batcherManager;
cullingResults.reset();
// Depth use camera's view and projection matrix
this._cullingResults.setRenderUpdateFlagTrue(ContextRendererUpdateFlag.viewProjectionMatrix);
context.applyVirtualCamera(camera._virtualCamera, depthPassEnabled);
- this._prepareRender(context);
+ this._prepareWorldRender(context);
- cullingResults.sortBatch(batcherManager);
+ const batcherManager = engine._batcherManager;
+ cullingResults.sort();
+ this._prepareUIRender(context);
+ cullingResults.batch(batcherManager);
batcherManager.uploadBuffer();
if (depthPassEnabled) {
@@ -198,19 +199,8 @@ export class BasicRenderPipeline {
}
this._internalColorTarget = internalColorTarget;
- } else {
- const internalColorTarget = this._internalColorTarget;
- const copyBackgroundTexture = this._copyBackgroundTexture;
- const pool = engine._renderTargetPool;
- if (internalColorTarget) {
- pool.freeRenderTarget(internalColorTarget);
- this._internalColorTarget = null;
- }
- if (copyBackgroundTexture) {
- pool.freeTexture(copyBackgroundTexture);
- this._copyBackgroundTexture = null;
- }
}
+ // Both fields are released at the end of `_drawRenderPass`, so they're null on every entry here.
// Scalable ambient obscurance pass
// Before opaque pass so materials can sample ambient occlusion in BRDF
@@ -361,6 +351,17 @@ export class BasicRenderPipeline {
cameraRenderTarget?._blitRenderTarget();
cameraRenderTarget?.generateMipmaps();
+
+ // Release per-frame leases so the next camera with matching shape can reuse them.
+ const pool = engine._renderTargetPool;
+ if (this._internalColorTarget) {
+ pool.freeRenderTarget(this._internalColorTarget);
+ this._internalColorTarget = null;
+ }
+ if (this._copyBackgroundTexture) {
+ pool.freeTexture(this._copyBackgroundTexture);
+ this._copyBackgroundTexture = null;
+ }
}
/**
@@ -369,84 +370,87 @@ export class BasicRenderPipeline {
* @param renderElement - Render element
*/
pushRenderElement(context: RenderContext, renderElement: RenderElement): void {
- renderElement.renderQueueFlags = RenderQueueFlags.None;
- const subRenderElements = renderElement.subRenderElements;
- for (let i = 0, n = subRenderElements.length; i < n; ++i) {
- const subRenderElement = subRenderElements[i];
- const { material } = subRenderElement;
- const { renderStates } = material;
- const materialSubShader = material.shader.subShaders[0];
- const replacementShader = context.replacementShader;
- if (replacementShader) {
- const replacementSubShaders = replacementShader.subShaders;
- const { replacementTag } = context;
- if (replacementTag) {
- let replacementSuccess = false;
- for (let j = 0, m = replacementSubShaders.length; j < m; j++) {
- const subShader = replacementSubShaders[j];
- if (subShader.getTagValue(replacementTag) === materialSubShader.getTagValue(replacementTag)) {
- this.pushRenderElementByType(renderElement, subRenderElement, subShader.passes, renderStates);
- replacementSuccess = true;
- }
+ const { material } = renderElement;
+ const { renderStates } = material;
+ const materialSubShader = material.shader.subShaders[0];
+ const replacementShader = context.replacementShader;
+ if (replacementShader) {
+ const replacementSubShaders = replacementShader.subShaders;
+ const { replacementTag } = context;
+ if (replacementTag) {
+ let replacementSuccess = false;
+ for (let j = 0, m = replacementSubShaders.length; j < m; j++) {
+ const subShader = replacementSubShaders[j];
+ if (subShader.getTagValue(replacementTag) === materialSubShader.getTagValue(replacementTag)) {
+ this._pushRenderElementByType(renderElement, subShader, renderStates);
+ replacementSuccess = true;
}
+ }
- if (
- !replacementSuccess &&
- context.replacementFailureStrategy === ReplacementFailureStrategy.KeepOriginalShader
- ) {
- this.pushRenderElementByType(renderElement, subRenderElement, materialSubShader.passes, renderStates);
- }
- } else {
- this.pushRenderElementByType(renderElement, subRenderElement, replacementSubShaders[0].passes, renderStates);
+ if (
+ !replacementSuccess &&
+ context.replacementFailureStrategy === ReplacementFailureStrategy.KeepOriginalShader
+ ) {
+ this._pushRenderElementByType(renderElement, materialSubShader, renderStates);
}
} else {
- this.pushRenderElementByType(renderElement, subRenderElement, materialSubShader.passes, renderStates);
+ this._pushRenderElementByType(renderElement, replacementSubShaders[0], renderStates);
}
+ } else {
+ this._pushRenderElementByType(renderElement, materialSubShader, renderStates);
}
}
- private pushRenderElementByType(
+ private _pushRenderElementByType(
renderElement: RenderElement,
- subRenderElement: SubRenderElement,
- shaderPasses: ReadonlyArray,
+ subShader: SubShader,
renderStates: ReadonlyArray
): void {
+ const shaderPasses = subShader.passes;
const cullingResults = this._cullingResults;
+ renderElement.subShader = subShader;
+ let pushedQueueFlags = RenderQueueFlags.None;
for (let i = 0, n = shaderPasses.length; i < n; i++) {
- // Get render queue type
let renderQueueType: RenderQueueType;
const shaderPass = shaderPasses[i];
const renderState = shaderPass._renderState;
if (renderState) {
renderQueueType = renderState._getRenderQueueByShaderData(
shaderPass._renderStateDataMap,
- subRenderElement.material.shaderData
+ renderElement.material.shaderData
);
} else {
renderQueueType = renderStates[i].renderQueueType;
}
const flag = 1 << renderQueueType;
-
- subRenderElement.shaderPasses = shaderPasses;
- subRenderElement.renderQueueFlags |= flag;
-
- if (renderElement.renderQueueFlags & flag) {
+ if (pushedQueueFlags & flag) {
continue;
}
+ // First queue keeps the original element; subsequent queues each get an isolated
+ // clone so per-queue batch state (`_isBatched`, `instancedRenderers`) doesn't
+ // leak across queues for multi-pass materials (e.g. Opaque + Transparent)
+ let elementForQueue: RenderElement;
+ if (pushedQueueFlags === RenderQueueFlags.None) {
+ elementForQueue = renderElement;
+ } else {
+ elementForQueue = renderElement.component.engine._renderElementPool.get();
+ elementForQueue._cloneFrom(renderElement);
+ }
+
switch (renderQueueType) {
case RenderQueueType.Opaque:
- cullingResults.opaqueQueue.pushRenderElement(renderElement);
+ cullingResults.opaqueQueue.pushRenderElement(elementForQueue);
break;
case RenderQueueType.AlphaTest:
- cullingResults.alphaTestQueue.pushRenderElement(renderElement);
+ cullingResults.alphaTestQueue.pushRenderElement(elementForQueue);
break;
case RenderQueueType.Transparent:
- cullingResults.transparentQueue.pushRenderElement(renderElement);
+ cullingResults.transparentQueue.pushRenderElement(elementForQueue);
break;
}
- renderElement.renderQueueFlags |= flag;
+ pushedQueueFlags |= flag;
}
}
@@ -482,10 +486,10 @@ export class BasicRenderPipeline {
rhi.drawPrimitive(mesh._primitive, mesh.subMesh, program);
}
- private _prepareRender(context: RenderContext): void {
+ private _prepareWorldRender(context: RenderContext): void {
const camera = context.camera;
const { engine, enableFrustumCulling, cullingMask, _frustum: frustum } = camera;
- const { _renderers: renderers, _canvases: canvases } = camera.scene._componentsManager;
+ const renderers = camera.scene._componentsManager._renderers;
const rendererElements = renderers._elements;
for (let i = renderers.length - 1; i >= 0; --i) {
@@ -504,7 +508,12 @@ export class BasicRenderPipeline {
renderer._prepareRender(context);
renderer._renderFrameCount = engine.time.frameCount;
}
+ }
+ private _prepareUIRender(context: RenderContext): void {
+ const camera = context.camera;
+ const { cullingMask } = camera;
+ const canvases = camera.scene._componentsManager._canvases;
const canvasesElements = canvases._elements;
for (let i = canvases.length - 1; i >= 0; i--) {
const canvas = canvasesElements[i];
@@ -516,7 +525,10 @@ export class BasicRenderPipeline {
continue;
}
canvas._prepareRender(context);
- this.pushRenderElement(context, canvas._renderElement);
+ const canvasElements = canvas._renderElements;
+ for (let j = 0, m = canvasElements.length; j < m; j++) {
+ this.pushRenderElement(context, canvasElements[j]);
+ }
}
}
}
diff --git a/packages/core/src/RenderPipeline/BatchUtils.ts b/packages/core/src/RenderPipeline/BatchUtils.ts
deleted file mode 100644
index 387c951f93..0000000000
--- a/packages/core/src/RenderPipeline/BatchUtils.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { SpriteMask, SpriteMaskInteraction, SpriteRenderer } from "../2d";
-import { ShaderTagKey } from "../shader";
-import { SubRenderElement } from "./SubRenderElement";
-
-/**
- * @internal
- */
-export class BatchUtils {
- protected static _disableBatchTag: ShaderTagKey = ShaderTagKey.getByName("spriteDisableBatching");
-
- static canBatchSprite(elementA: SubRenderElement, elementB: SubRenderElement): boolean {
- if (elementB.shaderPasses[0].getTagValue(BatchUtils._disableBatchTag) === true) {
- return false;
- }
- if (elementA.subChunk.chunk !== elementB.subChunk.chunk) {
- return false;
- }
-
- const rendererA = elementA.component;
- const rendererB = elementB.component;
- const maskInteractionA = rendererA.maskInteraction;
-
- // Compare mask, texture and material
- return (
- maskInteractionA === rendererB.maskInteraction &&
- (maskInteractionA === SpriteMaskInteraction.None || rendererA.maskLayer === rendererB.maskLayer) &&
- elementA.texture === elementB.texture &&
- elementA.material === elementB.material
- );
- }
-
- static canBatchSpriteMask(elementA: SubRenderElement, elementB: SubRenderElement): boolean {
- if (elementA.subChunk.chunk !== elementB.subChunk.chunk) {
- return false;
- }
-
- const alphaCutoffProperty = SpriteMask._alphaCutoffProperty;
-
- // Compare renderer property
- return (
- elementA.texture === elementB.texture &&
- (elementA.component).shaderData.getFloat(alphaCutoffProperty) ===
- (elementB.component).shaderData.getFloat(alphaCutoffProperty)
- );
- }
-
- static batchFor2D(elementA: SubRenderElement, elementB?: SubRenderElement): void {
- const subChunk = elementB ? elementB.subChunk : elementA.subChunk;
- const { chunk, indices: subChunkIndices } = subChunk;
-
- const length = subChunkIndices.length;
- let startIndex = chunk.updateIndexLength;
- if (elementB) {
- elementA.subChunk.subMesh.count += length;
- } else {
- // Reset subMesh
- const subMesh = subChunk.subMesh;
- subMesh.start = startIndex;
- subMesh.count = length;
- }
-
- const { start, size } = subChunk.vertexArea;
- const vertexOffset = start / 9;
- const indices = chunk.indices;
- for (let i = 0; i < length; ++i) {
- indices[startIndex++] = vertexOffset + subChunkIndices[i];
- }
- chunk.updateIndexLength += length;
- chunk.updateVertexStart = Math.min(chunk.updateVertexStart, start);
- chunk.updateVertexEnd = Math.max(chunk.updateVertexEnd, start + size);
- }
-}
diff --git a/packages/core/src/RenderPipeline/BatcherManager.ts b/packages/core/src/RenderPipeline/BatcherManager.ts
index 702a4a01e8..05ec1b7e89 100644
--- a/packages/core/src/RenderPipeline/BatcherManager.ts
+++ b/packages/core/src/RenderPipeline/BatcherManager.ts
@@ -1,8 +1,9 @@
import { Engine } from "../Engine";
import { Renderer } from "../Renderer";
+import { InstanceBuffer } from "./InstanceBuffer";
import { PrimitiveChunkManager } from "./PrimitiveChunkManager";
import { RenderQueue } from "./RenderQueue";
-import { SubRenderElement } from "./SubRenderElement";
+import { RenderElement } from "./RenderElement";
/**
* @internal
@@ -11,9 +12,14 @@ export class BatcherManager {
private _primitiveChunkManager2D: PrimitiveChunkManager;
private _primitiveChunkManagerMask: PrimitiveChunkManager;
private _primitiveChunkManagerUI: PrimitiveChunkManager;
+ private _instanceBuffer: InstanceBuffer;
constructor(public engine: Engine) {}
+ get instanceBuffer(): InstanceBuffer {
+ return (this._instanceBuffer ||= new InstanceBuffer(this.engine));
+ }
+
get primitiveChunkManager2D(): PrimitiveChunkManager {
return (this._primitiveChunkManager2D ||= new PrimitiveChunkManager(this.engine));
}
@@ -39,47 +45,40 @@ export class BatcherManager {
this._primitiveChunkManagerUI.destroy();
this._primitiveChunkManagerUI = null;
}
+ if (this._instanceBuffer) {
+ this._instanceBuffer.destroy();
+ this._instanceBuffer = null;
+ }
}
batch(renderQueue: RenderQueue): void {
- const { elements, batchedSubElements, renderQueueType } = renderQueue;
- let preSubElement: SubRenderElement;
+ const { elements, batchedElements } = renderQueue;
+
+ let preElement: RenderElement;
let preRenderer: Renderer;
let preConstructor: Function;
for (let i = 0, n = elements.length; i < n; ++i) {
- const subElements = elements[i].subRenderElements;
- for (let j = 0, m = subElements.length; j < m; ++j) {
- const subElement = subElements[j];
-
- // Some sub render elements may not belong to the current render queue
- if (!(subElement.renderQueueFlags & (1 << renderQueueType))) {
- continue;
- }
-
- const renderer = subElement.component;
- const constructor = renderer.constructor;
- if (preSubElement) {
- if (preConstructor === constructor && preRenderer._canBatch(preSubElement, subElement)) {
- preRenderer._batch(preSubElement, subElement);
- preSubElement.batched = true;
- } else {
- batchedSubElements.push(preSubElement);
- preSubElement = subElement;
- preRenderer = renderer;
- preConstructor = constructor;
- renderer._batch(subElement);
- subElement.batched = false;
- }
+ const curElement = elements[i];
+ const renderer = curElement.component;
+ const constructor = renderer.constructor;
+ if (preElement) {
+ if (preConstructor === constructor && preRenderer._canBatch(preElement, curElement)) {
+ preRenderer._batch(preElement, curElement);
} else {
- preSubElement = subElement;
+ batchedElements.push(preElement);
+ preElement = curElement;
preRenderer = renderer;
preConstructor = constructor;
- renderer._batch(subElement);
- subElement.batched = false;
+ renderer._batch(null, curElement);
}
+ } else {
+ preElement = curElement;
+ preRenderer = renderer;
+ preConstructor = constructor;
+ renderer._batch(null, curElement);
}
}
- preSubElement && batchedSubElements.push(preSubElement);
+ preElement && batchedElements.push(preElement);
}
uploadBuffer() {
diff --git a/packages/core/src/RenderPipeline/CullingResults.ts b/packages/core/src/RenderPipeline/CullingResults.ts
index 673e0694d3..6ff5c298a7 100644
--- a/packages/core/src/RenderPipeline/CullingResults.ts
+++ b/packages/core/src/RenderPipeline/CullingResults.ts
@@ -30,6 +30,18 @@ export class CullingResults {
this.transparentQueue.sortBatch(RenderQueue.compareForTransparent, batcherManager);
}
+ sort() {
+ this.opaqueQueue.sort(RenderQueue.compareForOpaque);
+ this.alphaTestQueue.sort(RenderQueue.compareForOpaque);
+ this.transparentQueue.sort(RenderQueue.compareForTransparent);
+ }
+
+ batch(batcherManager: BatcherManager): void {
+ this.opaqueQueue.batch(batcherManager);
+ this.alphaTestQueue.batch(batcherManager);
+ this.transparentQueue.batch(batcherManager);
+ }
+
setRenderUpdateFlagTrue(rendererUpdateFlag: ContextRendererUpdateFlag): void {
this.opaqueQueue.rendererUpdateFlag |= rendererUpdateFlag;
this.transparentQueue.rendererUpdateFlag |= rendererUpdateFlag;
diff --git a/packages/core/src/RenderPipeline/InstanceBuffer.ts b/packages/core/src/RenderPipeline/InstanceBuffer.ts
new file mode 100644
index 0000000000..f62253f78c
--- /dev/null
+++ b/packages/core/src/RenderPipeline/InstanceBuffer.ts
@@ -0,0 +1,86 @@
+import { Engine } from "../Engine";
+import { Buffer } from "../graphic/Buffer";
+import { BufferBindFlag } from "../graphic/enums/BufferBindFlag";
+import { BufferUsage } from "../graphic/enums/BufferUsage";
+import { SetDataOptions } from "../graphic/enums/SetDataOptions";
+import { Renderer } from "../Renderer";
+import { ShaderMacro } from "../shader/ShaderMacro";
+import { InstanceBufferLayout } from "../shaderlib/ShaderFactory";
+
+/**
+ * @internal
+ * Manages a UBO for GPU instancing, packing per-instance renderer data (ModelMat, Layer, etc.).
+ */
+export class InstanceBuffer {
+ static gpuInstanceMacro = ShaderMacro.getByName("RENDERER_GPU_INSTANCE");
+
+ buffer: Buffer;
+
+ private _engine: Engine;
+ private _layout: InstanceBufferLayout;
+ private _data: ArrayBuffer;
+ private _floatView: Float32Array;
+ private _intView: Int32Array;
+
+ constructor(engine: Engine) {
+ this._engine = engine;
+ }
+
+ /**
+ * Set UBO layout and allocate buffer if needed.
+ */
+ setLayout(layout: InstanceBufferLayout): void {
+ this._layout = layout;
+ const totalBytes = layout.instanceMaxCount * layout.structSize;
+ // Only reallocate when buffer is too small
+ if (!this.buffer || totalBytes > this.buffer.byteLength) {
+ this._data = new ArrayBuffer(totalBytes);
+ this._floatView = new Float32Array(this._data);
+ this._intView = new Int32Array(this._data);
+ this.buffer?.destroy();
+ this.buffer = new Buffer(this._engine, BufferBindFlag.ConstantBuffer, totalBytes, BufferUsage.Dynamic);
+ }
+ }
+
+ /**
+ * Pack renderer data into UBO and upload to GPU.
+ */
+ upload(renderers: Renderer[], start: number, count: number): void {
+ const { instanceFields, structSize } = this._layout;
+ const elementsPerInstance = structSize / 4;
+ const { _floatView: floatView, _intView: intView } = this;
+ const modelMatId = Renderer._worldMatrixProperty._uniqueId;
+
+ for (let i = 0; i < count; i++) {
+ const renderer = renderers[start + i];
+ const propertyValueMap = renderer.shaderData._propertyValueMap;
+ const baseOffset = i * elementsPerInstance;
+
+ for (let j = 0, n = instanceFields.length; j < n; j++) {
+ const field = instanceFields[j];
+ const fieldOffset = baseOffset + field.offsetInElements;
+ const propertyId = field.property._uniqueId;
+
+ if (propertyId === modelMatId) {
+ // Instancing skips _updateTransformShaderData, so worldMatrix is not in propertyValueMap
+ // Must read from transform getter to trigger lazy update
+ field.pack(floatView, fieldOffset, renderer.entity.transform.worldMatrix);
+ } else {
+ const value = propertyValueMap[propertyId];
+ if (value != null) {
+ field.pack(field.useIntView ? intView : floatView, fieldOffset, value);
+ }
+ }
+ }
+ }
+
+ this.buffer.setData(floatView, 0, 0, count * elementsPerInstance, SetDataOptions.Discard);
+ }
+
+ destroy(): void {
+ this.buffer?.destroy();
+ this._data = null;
+ this._floatView = null;
+ this._intView = null;
+ }
+}
diff --git a/packages/core/src/RenderPipeline/MaskManager.ts b/packages/core/src/RenderPipeline/MaskManager.ts
index 8454e1d584..12284ca611 100644
--- a/packages/core/src/RenderPipeline/MaskManager.ts
+++ b/packages/core/src/RenderPipeline/MaskManager.ts
@@ -1,4 +1,6 @@
-import { SpriteMask } from "../2d";
+import { Vector3 } from "@galacean/engine-math";
+import { SpriteMaskInteraction } from "../2d/enums/SpriteMaskInteraction";
+import { IMaskRenderable } from "../2d/sprite/MaskRenderable";
import { CameraClearFlags } from "../enums/CameraClearFlags";
import { SpriteMaskLayer } from "../enums/SpriteMaskLayer";
import { Material } from "../material";
@@ -28,17 +30,49 @@ export class MaskManager {
hasStencilWritten = false;
private _preMaskLayer = SpriteMaskLayer.Nothing;
- private _allSpriteMasks = new DisorderedArray();
+ private _allSpriteMasks = new DisorderedArray();
+ private _filteredMasksByLayer = new Map();
+ private _isFilteredMasksDirty = true;
- addSpriteMask(mask: SpriteMask): void {
+ addSpriteMask(mask: IMaskRenderable): void {
mask._maskIndex = this._allSpriteMasks.length;
this._allSpriteMasks.add(mask);
+ this._setFilteredMasksDirty();
}
- removeSpriteMask(mask: SpriteMask): void {
+ removeSpriteMask(mask: IMaskRenderable): void {
const replaced = this._allSpriteMasks.deleteByIndex(mask._maskIndex);
replaced && (replaced._maskIndex = mask._maskIndex);
mask._maskIndex = -1;
+ this._setFilteredMasksDirty();
+ }
+
+ onMaskInfluenceLayersChange(): void {
+ this._setFilteredMasksDirty();
+ }
+
+ isVisibleByMask(maskInteraction: SpriteMaskInteraction, maskLayer: SpriteMaskLayer, worldPoint: Vector3): boolean {
+ if (maskInteraction === SpriteMaskInteraction.None) {
+ return true;
+ }
+
+ const masks = this._getMasksByLayer(maskLayer);
+ let insideMask = false;
+ for (let i = 0, n = masks.length; i < n; i++) {
+ if (masks[i]._containsWorldPoint(worldPoint)) {
+ insideMask = true;
+ break;
+ }
+ }
+
+ switch (maskInteraction) {
+ case SpriteMaskInteraction.VisibleInsideMask:
+ return insideMask;
+ case SpriteMaskInteraction.VisibleOutsideMask:
+ return !insideMask;
+ default:
+ return true;
+ }
}
drawMask(context: RenderContext, pipelineStageTagValue: string, maskLayer: SpriteMaskLayer): void {
@@ -118,6 +152,38 @@ export class MaskManager {
const allSpriteMasks = this._allSpriteMasks;
allSpriteMasks.length = 0;
allSpriteMasks.garbageCollection();
+ this._filteredMasksByLayer.clear();
+ this._isFilteredMasksDirty = true;
+ }
+
+ private _setFilteredMasksDirty(): void {
+ this._isFilteredMasksDirty = true;
+ }
+
+ private _getMasksByLayer(maskLayer: SpriteMaskLayer): IMaskRenderable[] {
+ if (maskLayer === SpriteMaskLayer.Nothing) {
+ return [];
+ }
+
+ if (this._isFilteredMasksDirty) {
+ this._filteredMasksByLayer.clear();
+ this._isFilteredMasksDirty = false;
+ }
+
+ let filteredMasks = this._filteredMasksByLayer.get(maskLayer);
+ if (!filteredMasks) {
+ filteredMasks = [];
+ const allMasks = this._allSpriteMasks;
+ const maskElements = allMasks._elements;
+ for (let i = 0, n = allMasks.length; i < n; i++) {
+ const mask = maskElements[i];
+ if (mask.influenceLayers & maskLayer) {
+ filteredMasks.push(mask);
+ }
+ }
+ this._filteredMasksByLayer.set(maskLayer, filteredMasks);
+ }
+ return filteredMasks;
}
private _buildMaskRenderElement(
diff --git a/packages/core/src/RenderPipeline/RenderElement.ts b/packages/core/src/RenderPipeline/RenderElement.ts
index 582e72f47d..50cbf8ffe9 100644
--- a/packages/core/src/RenderPipeline/RenderElement.ts
+++ b/packages/core/src/RenderPipeline/RenderElement.ts
@@ -1,24 +1,67 @@
+import { Renderer } from "../Renderer";
+import { Primitive, SubMesh } from "../graphic";
+import { Material } from "../material";
+import { ShaderData, SubShader } from "../shader";
+import { Texture2D } from "../texture";
import { IPoolElement } from "../utils/ObjectPool";
-import { RenderQueueFlags } from "./BasicRenderPipeline";
-import { SubRenderElement } from "./SubRenderElement";
+import { SubPrimitiveChunk } from "./SubPrimitiveChunk";
export class RenderElement implements IPoolElement {
priority: number;
distanceForSort: number;
- subRenderElements = Array();
- renderQueueFlags: RenderQueueFlags;
+ component: Renderer;
+ primitive: Primitive;
+ material: Material;
+ subPrimitive: SubMesh;
+ subShader: SubShader;
+ shaderData?: ShaderData;
+ instancedRenderers: Renderer[] = [];
- set(priority: number, distanceForSort: number): void {
- this.priority = priority;
- this.distanceForSort = distanceForSort;
- this.subRenderElements.length = 0;
+ // @todo: maybe should remove later
+ texture?: Texture2D;
+ subChunk?: SubPrimitiveChunk;
+
+ set(
+ component: Renderer,
+ material: Material,
+ primitive: Primitive,
+ subPrimitive: SubMesh,
+ texture?: Texture2D,
+ subChunk?: SubPrimitiveChunk
+ ): void {
+ this.component = component;
+ this.material = material;
+ this.primitive = primitive;
+ this.subPrimitive = subPrimitive;
+ this.texture = texture;
+ this.subChunk = subChunk;
+ this.instancedRenderers.length = 0;
}
- addSubRenderElement(element: SubRenderElement): void {
- this.subRenderElements.push(element);
+ /**
+ * @internal
+ * Copy identity fields from `source` and reset per-queue batch state, so the same
+ * logical element can live in multiple queues (e.g. an Opaque + Transparent multi-pass
+ * material) without one queue's batching corrupting another's `instancedRenderers`.
+ */
+ _cloneFrom(source: RenderElement): void {
+ this.set(source.component, source.material, source.primitive, source.subPrimitive, source.texture, source.subChunk);
+ this.priority = source.priority;
+ this.distanceForSort = source.distanceForSort;
+ this.subShader = source.subShader;
+ this.shaderData = source.shaderData;
}
dispose(): void {
- this.subRenderElements.length = 0;
+ this.component = null;
+ this.material = null;
+ this.primitive = null;
+ this.subPrimitive = null;
+ this.subShader = null;
+ this.shaderData && (this.shaderData = null);
+ this.instancedRenderers = null;
+
+ this.texture && (this.texture = null);
+ this.subChunk && (this.subChunk = null);
}
}
diff --git a/packages/core/src/RenderPipeline/RenderQueue.ts b/packages/core/src/RenderPipeline/RenderQueue.ts
index 3558679996..5f4a4d89af 100644
--- a/packages/core/src/RenderPipeline/RenderQueue.ts
+++ b/packages/core/src/RenderPipeline/RenderQueue.ts
@@ -3,10 +3,11 @@ import { BasicResources, RenderStateElementMap } from "../BasicResources";
import { Utils } from "../Utils";
import { RenderQueueType, Shader } from "../shader";
import { ShaderMacroCollection } from "../shader/ShaderMacroCollection";
+import { ConstantBufferBindingPoint } from "../shader/enums/ConstantBufferBindingPoint";
import { BatcherManager } from "./BatcherManager";
+import { InstanceBuffer } from "./InstanceBuffer";
import { ContextRendererUpdateFlag, RenderContext } from "./RenderContext";
import { RenderElement } from "./RenderElement";
-import { SubRenderElement } from "./SubRenderElement";
import { RenderQueueMaskType } from "./enums/RenderQueueMaskType";
/**
@@ -14,7 +15,11 @@ import { RenderQueueMaskType } from "./enums/RenderQueueMaskType";
*/
export class RenderQueue {
static compareForOpaque(a: RenderElement, b: RenderElement): number {
- return a.priority - b.priority || a.distanceForSort - b.distanceForSort;
+ return (
+ a.priority - b.priority ||
+ a.material.instanceId - b.material.instanceId ||
+ a.primitive.instanceId - b.primitive.instanceId
+ );
}
static compareForTransparent(a: RenderElement, b: RenderElement): number {
@@ -22,7 +27,7 @@ export class RenderQueue {
}
readonly elements = new Array();
- readonly batchedSubElements = new Array();
+ readonly batchedElements = new Array();
rendererUpdateFlag = ContextRendererUpdateFlag.None;
constructor(public renderQueueType: RenderQueueType) {}
@@ -36,6 +41,10 @@ export class RenderQueue {
this.batch(batcherManager);
}
+ sort(compareFunc: Function): void {
+ Utils._quickSort(this.elements, 0, this.elements.length, compareFunc);
+ }
+
batch(batcherManager: BatcherManager): void {
batcherManager.batch(this);
}
@@ -45,8 +54,8 @@ export class RenderQueue {
pipelineStageTagValue: string,
maskType: RenderQueueMaskType = RenderQueueMaskType.No
): void {
- const batchedSubElements = this.batchedSubElements;
- const length = batchedSubElements.length;
+ const batchedElements = this.batchedElements;
+ const length = batchedElements.length;
if (length === 0) {
return;
}
@@ -57,34 +66,33 @@ export class RenderQueue {
const rhi = engine._hardwareRenderer;
const pipelineStageKey = RenderContext.pipelineStageKey;
const renderQueueType = this.renderQueueType;
+ const needMaskType = maskType !== RenderQueueMaskType.No;
for (let i = 0; i < length; i++) {
- const subElement = batchedSubElements[i];
- const { component: renderer, batched, material } = subElement;
-
- // @todo: Can optimize update view projection matrix updated
- if (
- this.rendererUpdateFlag & ContextRendererUpdateFlag.WorldViewMatrix ||
- renderer._batchedTransformShaderData != batched
- ) {
- // Update world matrix and view matrix and model matrix
- renderer._updateTransformShaderData(context, false, batched);
- renderer._batchedTransformShaderData = batched;
- } else if (this.rendererUpdateFlag & ContextRendererUpdateFlag.ProjectionMatrix) {
- // Only projection matrix need updated
- renderer._updateTransformShaderData(context, true, batched);
+ const curElement = batchedElements[i];
+ const { component, material } = curElement;
+ const isInstanced = curElement.instancedRenderers.length > 0;
+
+ // Update transform shader data
+ // Instancing packs per-renderer transforms into the instance UBO at draw time, so skip here
+ if (!isInstanced) {
+ if (this.rendererUpdateFlag & ContextRendererUpdateFlag.WorldViewMatrix) {
+ component._updateTransformShaderData(context, false);
+ } else if (this.rendererUpdateFlag & ContextRendererUpdateFlag.ProjectionMatrix) {
+ component._updateTransformShaderData(context, true);
+ }
}
- const maskInteraction = renderer._maskInteraction;
+ // Resolve mask render states
+ const maskInteraction = component._maskInteraction;
const needMaskInteraction = maskInteraction !== SpriteMaskInteraction.None;
- const needMaskType = maskType !== RenderQueueMaskType.No;
let customStates: RenderStateElementMap = null;
if (needMaskType) {
customStates = BasicResources.getMaskTypeRenderStates(maskType);
} else {
if (needMaskInteraction) {
- maskManager.drawMask(context, pipelineStageTagValue, subElement.component._maskLayer);
+ maskManager.drawMask(context, pipelineStageTagValue, component._maskLayer);
customStates = BasicResources.getMaskInteractionRenderStates(maskInteraction);
} else {
maskManager.isReadStencil(material) && maskManager.clearMask(context, pipelineStageTagValue);
@@ -92,21 +100,27 @@ export class RenderQueue {
maskManager.isStencilWritten(material) && (maskManager.hasStencilWritten = true);
}
- const compileMacros = Shader._compileMacros;
- const { primitive, shaderPasses, shaderData: renderElementShaderData } = subElement;
- const { shaderData: rendererData, instanceId: rendererId } = renderer;
+ const { shaderData: renderElementShaderData } = curElement;
+ const shaderPasses = curElement.subShader.passes;
+ const { shaderData: rendererData, instanceId: rendererId } = component;
const { shaderData: materialData, instanceId: materialId, renderStates } = material;
- // Union render global macro and material self macro
- ShaderMacroCollection.unionCollection(renderer._globalShaderMacro, materialData._macroCollection, compileMacros);
+ // Build compile macros
+ const compileMacros = Shader._compileMacros;
+ ShaderMacroCollection.unionCollection(component._globalShaderMacro, materialData._macroCollection, compileMacros);
ShaderMacroCollection.unionCollection(compileMacros, engine._macroCollection, compileMacros);
+ if (isInstanced) {
+ compileMacros.enable(InstanceBuffer.gpuInstanceMacro);
+ }
+
for (let j = 0, m = shaderPasses.length; j < m; j++) {
const shaderPass = shaderPasses[j];
if (shaderPass.getTagValue(pipelineStageKey) !== pipelineStageTagValue) {
continue;
}
+ // Pick render state and filter by queue type
let renderState = shaderPass._renderState;
if (needMaskType) {
// Mask don't care render queue type
@@ -134,18 +148,21 @@ export class RenderQueue {
const switchProgram = program.bind();
const switchRenderCount = renderCount !== program._uploadRenderCount;
+ // Upload uniforms (cache-aware per block). Renderer block carries plain samplers/arrays
+ // even on the instanced path (GLSL forbids them in UBOs); `_canBatch` ensures the whole
+ // batch agrees on those, so the leader's values are correct for the draw call
if (switchRenderCount) {
program.groupingOtherUniformBlock();
program.uploadAll(program.sceneUniformBlock, sceneData);
program.uploadAll(program.cameraUniformBlock, cameraData);
program.uploadAll(program.rendererUniformBlock, rendererData);
+ program._uploadRendererId = isInstanced ? -1 : rendererId;
program.uploadAll(program.materialUniformBlock, materialData);
renderElementShaderData && program.uploadAll(program.renderElementUniformBlock, renderElementShaderData);
- // UnGroup textures should upload default value, texture uint maybe change by logic of texture bind.
+ // UnGroup textures should upload default value, texture uint maybe change by logic of texture bind
program.uploadUnGroupTextures();
program._uploadSceneId = sceneId;
program._uploadCameraId = cameraId;
- program._uploadRendererId = rendererId;
program._uploadMaterialId = materialId;
program._uploadRenderCount = renderCount;
} else {
@@ -163,7 +180,11 @@ export class RenderQueue {
program.uploadTextures(program.cameraUniformBlock, cameraData);
}
- if (program._uploadRendererId !== rendererId) {
+ if (isInstanced) {
+ // Different batches may have different leaders, re-upload every time
+ program.uploadAll(program.rendererUniformBlock, rendererData);
+ program._uploadRendererId = -1;
+ } else if (program._uploadRendererId !== rendererId) {
program.uploadAll(program.rendererUniformBlock, rendererData);
program._uploadRendererId = rendererId;
} else if (switchProgram) {
@@ -179,20 +200,41 @@ export class RenderQueue {
renderElementShaderData && program.uploadAll(program.renderElementUniformBlock, renderElementShaderData);
- // We only consider switchProgram case, because UnGroup texture's value is always default.
+ // We only consider switchProgram case, because UnGroup texture's value is always default
if (switchProgram) {
program.uploadUnGroupTextures();
}
}
+ // Apply render state
renderState._applyStates(
engine,
- renderer._isFrontFaceInvert(),
+ component._isFrontFaceInvert(),
shaderPass._renderStateDataMap,
material.shaderData,
customStates
);
- rhi.drawPrimitive(primitive, subElement.subPrimitive, program);
+
+ // Draw
+ const layout = program._instanceLayout;
+ if (isInstanced && layout) {
+ const { primitive, subPrimitive, instancedRenderers } = curElement;
+ const totalCount = instancedRenderers.length;
+ const maxCount = layout.instanceMaxCount;
+ const instanceBuffer = engine._batcherManager.instanceBuffer;
+
+ instanceBuffer.setLayout(layout);
+ rhi.bindUniformBufferBase(ConstantBufferBindingPoint.RendererInstance, instanceBuffer.buffer._platformBuffer);
+ for (let start = 0; start < totalCount; start += maxCount) {
+ const count = Math.min(maxCount, totalCount - start);
+ instanceBuffer.upload(instancedRenderers, start, count);
+ primitive.instanceCount = count;
+ rhi.drawPrimitive(primitive, subPrimitive, program);
+ }
+ primitive.instanceCount = 0;
+ } else {
+ rhi.drawPrimitive(curElement.primitive, curElement.subPrimitive, program);
+ }
}
}
@@ -201,7 +243,7 @@ export class RenderQueue {
clear(): void {
this.elements.length = 0;
- this.batchedSubElements.length = 0;
+ this.batchedElements.length = 0;
}
destroy(): void {}
diff --git a/packages/core/src/RenderPipeline/RenderTargetPool.ts b/packages/core/src/RenderPipeline/RenderTargetPool.ts
index 3eae130d62..b5d6ecb382 100644
--- a/packages/core/src/RenderPipeline/RenderTargetPool.ts
+++ b/packages/core/src/RenderPipeline/RenderTargetPool.ts
@@ -5,9 +5,73 @@ import { RenderTarget, Texture2D, TextureFilterMode, TextureFormat, TextureWrapM
* @internal
*/
export class RenderTargetPool {
+ private static _destroyRenderTargetResource(rt: RenderTarget): void {
+ const colorTexture = rt.getColorTexture(0);
+ const depthTexture = rt.depthTexture;
+ rt.destroy(true);
+ colorTexture?.destroy(true);
+ if (depthTexture && depthTexture !== colorTexture) {
+ depthTexture.destroy(true);
+ }
+ }
+
+ private static _matchRenderTarget(
+ renderTarget: RenderTarget,
+ width: number,
+ height: number,
+ colorFormat: TextureFormat | null,
+ depthFormat: TextureFormat | null,
+ needDepthTexture: boolean,
+ mipmap: boolean,
+ isSRGBColorSpace: boolean,
+ antiAliasing: number
+ ): boolean {
+ if (renderTarget.width !== width || renderTarget.height !== height || renderTarget.antiAliasing !== antiAliasing) {
+ return false;
+ }
+
+ const colorTexture = renderTarget.getColorTexture(0) as Texture2D;
+ if (colorFormat != null) {
+ if (
+ !colorTexture ||
+ colorTexture.format !== colorFormat ||
+ colorTexture.mipmapCount > 1 !== mipmap ||
+ colorTexture.isSRGBColorSpace !== isSRGBColorSpace
+ ) {
+ return false;
+ }
+ } else if (colorTexture) {
+ return false;
+ }
+
+ const depthTexture = renderTarget.depthTexture;
+ if (needDepthTexture) {
+ if (depthFormat) {
+ if (!depthTexture || (depthTexture as Texture2D).format !== depthFormat) {
+ return false;
+ }
+ } else if (depthTexture) {
+ return false;
+ }
+ } else {
+ if (renderTarget._depthFormat !== depthFormat) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /** Frames an entry may sit idle before `tick()` destroys it. */
+ maxFreeAgeFrames: number = 60;
+
+ private _engine: Engine;
+
private _freeRenderTargets: RenderTarget[] = [];
+ private _freeRenderTargetFrames: number[] = [];
+
private _freeTextures: Texture2D[] = [];
- private _engine: Engine;
+ private _freeTextureFrames: number[] = [];
constructor(engine: Engine) {
this._engine = engine;
@@ -41,8 +105,7 @@ export class RenderTargetPool {
antiAliasing
)
) {
- freeRenderTargets[i] = freeRenderTargets[freeRenderTargets.length - 1];
- freeRenderTargets.length--;
+ this._removeFreeRenderTargetAt(i);
const colorTexture = renderTarget.getColorTexture(0) as Texture2D;
if (colorTexture) {
colorTexture.wrapModeU = colorTexture.wrapModeV = wrapMode;
@@ -103,8 +166,7 @@ export class RenderTargetPool {
texture.mipmapCount > 1 === mipmap &&
texture.isSRGBColorSpace === isSRGBColorSpace
) {
- freeTextures[i] = freeTextures[freeTextures.length - 1];
- freeTextures.length--;
+ this._removeFreeTextureAt(i);
texture.wrapModeU = texture.wrapModeV = wrapMode;
texture.filterMode = filterMode;
return texture;
@@ -122,78 +184,97 @@ export class RenderTargetPool {
freeRenderTarget(renderTarget: RenderTarget): void {
if (!renderTarget || renderTarget.destroyed) return;
this._freeRenderTargets.push(renderTarget);
+ this._freeRenderTargetFrames.push(this._engine.time.frameCount);
}
freeTexture(texture: Texture2D): void {
if (!texture || texture.destroyed) return;
this._freeTextures.push(texture);
+ this._freeTextureFrames.push(this._engine.time.frameCount);
+ }
+
+ tick(currentFrame: number): void {
+ const maxAge = this.maxFreeAgeFrames;
+ const rtFrames = this._freeRenderTargetFrames;
+ for (let i = rtFrames.length - 1; i >= 0; i--) {
+ if (currentFrame - rtFrames[i] > maxAge) {
+ this._destroyFreeRenderTargetAt(i);
+ }
+ }
+ const texFrames = this._freeTextureFrames;
+ for (let i = texFrames.length - 1; i >= 0; i--) {
+ if (currentFrame - texFrames[i] > maxAge) {
+ this._destroyFreeTextureAt(i);
+ }
+ }
+ }
+
+ evictBySize(width: number, height: number): void {
+ const freeRenderTargets = this._freeRenderTargets;
+ for (let i = freeRenderTargets.length - 1; i >= 0; i--) {
+ const rt = freeRenderTargets[i];
+ if (rt.width === width && rt.height === height) {
+ this._destroyFreeRenderTargetAt(i);
+ }
+ }
+ const freeTextures = this._freeTextures;
+ for (let i = freeTextures.length - 1; i >= 0; i--) {
+ const tex = freeTextures[i];
+ if (tex.width === width && tex.height === height) {
+ this._destroyFreeTextureAt(i);
+ }
+ }
}
gc(): void {
const freeRenderTargets = this._freeRenderTargets;
for (let i = 0, n = freeRenderTargets.length; i < n; i++) {
- const renderTarget = freeRenderTargets[i];
- const colorTexture = renderTarget.getColorTexture(0);
- const depthTexture = renderTarget.depthTexture;
- renderTarget.destroy(true);
- colorTexture?.destroy(true);
- if (depthTexture && depthTexture !== colorTexture) {
- depthTexture.destroy(true);
- }
+ RenderTargetPool._destroyRenderTargetResource(freeRenderTargets[i]);
}
freeRenderTargets.length = 0;
+ this._freeRenderTargetFrames.length = 0;
const freeTextures = this._freeTextures;
for (let i = 0, n = freeTextures.length; i < n; i++) {
freeTextures[i].destroy(true);
}
freeTextures.length = 0;
+ this._freeTextureFrames.length = 0;
}
- private static _matchRenderTarget(
- renderTarget: RenderTarget,
- width: number,
- height: number,
- colorFormat: TextureFormat | null,
- depthFormat: TextureFormat | null,
- needDepthTexture: boolean,
- mipmap: boolean,
- isSRGBColorSpace: boolean,
- antiAliasing: number
- ): boolean {
- if (renderTarget.width !== width || renderTarget.height !== height || renderTarget.antiAliasing !== antiAliasing) {
- return false;
+ private _removeFreeRenderTargetAt(index: number): void {
+ const rts = this._freeRenderTargets;
+ const frames = this._freeRenderTargetFrames;
+ const last = rts.length - 1;
+ if (index !== last) {
+ rts[index] = rts[last];
+ frames[index] = frames[last];
}
+ rts.length = last;
+ frames.length = last;
+ }
- const colorTexture = renderTarget.getColorTexture(0) as Texture2D;
- if (colorFormat != null) {
- if (
- !colorTexture ||
- colorTexture.format !== colorFormat ||
- colorTexture.mipmapCount > 1 !== mipmap ||
- colorTexture.isSRGBColorSpace !== isSRGBColorSpace
- ) {
- return false;
- }
- } else if (colorTexture) {
- return false;
+ private _removeFreeTextureAt(index: number): void {
+ const texs = this._freeTextures;
+ const frames = this._freeTextureFrames;
+ const last = texs.length - 1;
+ if (index !== last) {
+ texs[index] = texs[last];
+ frames[index] = frames[last];
}
+ texs.length = last;
+ frames.length = last;
+ }
- const depthTexture = renderTarget.depthTexture;
- if (needDepthTexture) {
- if (depthFormat) {
- if (!depthTexture || (depthTexture as Texture2D).format !== depthFormat) {
- return false;
- }
- } else if (depthTexture) {
- return false;
- }
- } else {
- if (renderTarget._depthFormat !== depthFormat) {
- return false;
- }
- }
+ private _destroyFreeRenderTargetAt(index: number): void {
+ const rt = this._freeRenderTargets[index];
+ this._removeFreeRenderTargetAt(index);
+ RenderTargetPool._destroyRenderTargetResource(rt);
+ }
- return true;
+ private _destroyFreeTextureAt(index: number): void {
+ const tex = this._freeTextures[index];
+ this._removeFreeTextureAt(index);
+ tex.destroy(true);
}
}
diff --git a/packages/core/src/RenderPipeline/SubRenderElement.ts b/packages/core/src/RenderPipeline/SubRenderElement.ts
deleted file mode 100644
index f8c86c114d..0000000000
--- a/packages/core/src/RenderPipeline/SubRenderElement.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { Renderer } from "../Renderer";
-import { Primitive, SubMesh } from "../graphic";
-import { Material } from "../material";
-import { ShaderData, ShaderPass } from "../shader";
-import { Texture2D } from "../texture";
-import { IPoolElement } from "../utils/ObjectPool";
-import { RenderQueueFlags } from "./BasicRenderPipeline";
-import { SubPrimitiveChunk } from "./SubPrimitiveChunk";
-
-export class SubRenderElement implements IPoolElement {
- component: Renderer;
- primitive: Primitive;
- material: Material;
- subPrimitive: SubMesh;
- shaderPasses: ReadonlyArray;
- shaderData?: ShaderData;
- batched: boolean;
- renderQueueFlags: RenderQueueFlags;
-
- // @todo: maybe should remove later
- texture?: Texture2D;
- subChunk?: SubPrimitiveChunk;
-
- set(
- component: Renderer,
- material: Material,
- primitive: Primitive,
- subPrimitive: SubMesh,
- texture?: Texture2D,
- subChunk?: SubPrimitiveChunk
- ): void {
- this.component = component;
- this.material = material;
- this.primitive = primitive;
- this.subPrimitive = subPrimitive;
- this.texture = texture;
- this.subChunk = subChunk;
- }
-
- dispose(): void {
- this.component = null;
- this.material = null;
- this.primitive = null;
- this.subPrimitive = null;
- this.shaderPasses = null;
- this.shaderData && (this.shaderData = null);
-
- this.texture && (this.texture = null);
- this.subChunk && (this.subChunk = null);
- }
-}
diff --git a/packages/core/src/RenderPipeline/VertexMergeBatcher.ts b/packages/core/src/RenderPipeline/VertexMergeBatcher.ts
new file mode 100644
index 0000000000..67565f8071
--- /dev/null
+++ b/packages/core/src/RenderPipeline/VertexMergeBatcher.ts
@@ -0,0 +1,121 @@
+import { SpriteMask, SpriteMaskInteraction, SpriteRenderer } from "../2d";
+import { ShaderTagKey } from "../shader";
+import { RenderElement } from "./RenderElement";
+
+/**
+ * @internal
+ */
+export class VertexMergeBatcher {
+ protected static _disableBatchTag: ShaderTagKey = ShaderTagKey.getByName("spriteDisableBatching");
+
+ static canBatchSprite(preElement: RenderElement, curElement: RenderElement): boolean {
+ const preRenderer = preElement.component;
+ const renderer = curElement.component;
+ const maskInteraction = preRenderer.maskInteraction;
+
+ const preRendererAny = preRenderer as any;
+ const curRendererAny = renderer as any;
+ const rectMaskEnabledA = preRendererAny._rectMaskEnabled;
+ if (rectMaskEnabledA !== curRendererAny._rectMaskEnabled) {
+ return false;
+ }
+ if (rectMaskEnabledA) {
+ const rectMaskRectA = preRendererAny._rectMaskRect;
+ const rectMaskRectB = curRendererAny._rectMaskRect;
+ const rectMaskSoftnessA = preRendererAny._rectMaskSoftness;
+ const rectMaskSoftnessB = curRendererAny._rectMaskSoftness;
+ if (
+ !rectMaskRectA ||
+ !rectMaskRectB ||
+ !rectMaskSoftnessA ||
+ !rectMaskSoftnessB ||
+ rectMaskRectA.x !== rectMaskRectB.x ||
+ rectMaskRectA.y !== rectMaskRectB.y ||
+ rectMaskRectA.z !== rectMaskRectB.z ||
+ rectMaskRectA.w !== rectMaskRectB.w ||
+ rectMaskSoftnessA.x !== rectMaskSoftnessB.x ||
+ rectMaskSoftnessA.y !== rectMaskSoftnessB.y ||
+ rectMaskSoftnessA.z !== rectMaskSoftnessB.z ||
+ rectMaskSoftnessA.w !== rectMaskSoftnessB.w ||
+ preRendererAny._rectMaskHardClip !== curRendererAny._rectMaskHardClip
+ ) {
+ return false;
+ }
+ }
+
+ // Order: cheap reference checks → mask state → tag lookup (rare opt-out)
+ return (
+ preElement.subChunk.chunk === curElement.subChunk.chunk &&
+ preElement.texture === curElement.texture &&
+ preElement.material === curElement.material &&
+ maskInteraction === renderer.maskInteraction &&
+ (maskInteraction === SpriteMaskInteraction.None || preRenderer.maskLayer === renderer.maskLayer) &&
+ curElement.subShader.passes[0].getTagValue(VertexMergeBatcher._disableBatchTag) !== true
+ );
+ }
+
+ /**
+ * Text-specific batch check: extends sprite check with outline parity.
+ * Different outlineWidth or outlineColor must split into separate draw calls,
+ * because outline uniforms are shared per draw call.
+ */
+ static canBatchText(preElement: RenderElement, curElement: RenderElement): boolean {
+ if (!VertexMergeBatcher.canBatchSprite(preElement, curElement)) {
+ return false;
+ }
+ const preRendererAny = preElement.component as any;
+ const curRendererAny = curElement.component as any;
+ if (preRendererAny._outlineWidth !== curRendererAny._outlineWidth) {
+ return false;
+ }
+ if (preRendererAny._outlineWidth > 0) {
+ const a = preRendererAny._outlineColor;
+ const b = curRendererAny._outlineColor;
+ if (a.r !== b.r || a.g !== b.g || a.b !== b.b || a.a !== b.a) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static canBatchSpriteMask(preElement: RenderElement, curElement: RenderElement): boolean {
+ if (preElement.subChunk.chunk !== curElement.subChunk.chunk) {
+ return false;
+ }
+
+ const alphaCutoffProperty = SpriteMask._alphaCutoffProperty;
+
+ // Compare renderer property
+ return (
+ preElement.texture === curElement.texture &&
+ (preElement.component).shaderData.getFloat(alphaCutoffProperty) ===
+ (curElement.component).shaderData.getFloat(alphaCutoffProperty)
+ );
+ }
+
+ static batch(preElement: RenderElement | null, curElement: RenderElement): void {
+ const subChunk = curElement.subChunk;
+ const { chunk, indices: subChunkIndices } = subChunk;
+
+ const length = subChunkIndices.length;
+ let startIndex = chunk.updateIndexLength;
+ if (preElement) {
+ preElement.subChunk.subMesh.count += length;
+ } else {
+ // Reset subMesh
+ const subMesh = subChunk.subMesh;
+ subMesh.start = startIndex;
+ subMesh.count = length;
+ }
+
+ const { start, size } = subChunk.vertexArea;
+ const vertexOffset = start / 9;
+ const indices = chunk.indices;
+ for (let i = 0; i < length; ++i) {
+ indices[startIndex++] = vertexOffset + subChunkIndices[i];
+ }
+ chunk.updateIndexLength += length;
+ chunk.updateVertexStart = Math.min(chunk.updateVertexStart, start);
+ chunk.updateVertexEnd = Math.max(chunk.updateVertexEnd, start + size);
+ }
+}
diff --git a/packages/core/src/RenderPipeline/index.ts b/packages/core/src/RenderPipeline/index.ts
index 7161b57757..a95c0f8f46 100644
--- a/packages/core/src/RenderPipeline/index.ts
+++ b/packages/core/src/RenderPipeline/index.ts
@@ -1,5 +1,6 @@
export { BasicRenderPipeline, RenderQueueFlags } from "./BasicRenderPipeline";
-export { BatchUtils } from "./BatchUtils";
+export { VertexMergeBatcher } from "./VertexMergeBatcher";
export { Blitter } from "./Blitter";
-export { RenderQueue } from "./RenderQueue";
export { PipelineStage } from "./enums/PipelineStage";
+export { RenderElement } from "./RenderElement";
+export { RenderQueue } from "./RenderQueue";
diff --git a/packages/core/src/Renderer.ts b/packages/core/src/Renderer.ts
index ab9f743c0c..c6444d5354 100644
--- a/packages/core/src/Renderer.ts
+++ b/packages/core/src/Renderer.ts
@@ -5,7 +5,7 @@ import { Component } from "./Component";
import { DependentMode, dependentComponents } from "./ComponentsDependencies";
import { Entity } from "./Entity";
import { RenderContext } from "./RenderPipeline/RenderContext";
-import { SubRenderElement } from "./RenderPipeline/SubRenderElement";
+import { RenderElement } from "./RenderPipeline/RenderElement";
import { Transform, TransformModifyFlags } from "./Transform";
import { assignmentClone, deepClone, ignoreClone } from "./clone/CloneManager";
import { SpriteMaskLayer } from "./enums/SpriteMaskLayer";
@@ -23,14 +23,15 @@ import { ShaderDataGroup } from "./shader/enums/ShaderDataGroup";
export class Renderer extends Component {
private static _tempVector0 = new Vector3();
+ /** @internal */
+ static _worldMatrixProperty = ShaderProperty.getByName("renderer_ModelMat");
+ /** @internal */
+ static _rendererLayerProperty = ShaderProperty.getByName("renderer_Layer");
+
private static _receiveShadowMacro = ShaderMacro.getByName("RENDERER_IS_RECEIVE_SHADOWS");
- private static _localMatrixProperty = ShaderProperty.getByName("renderer_LocalMat");
- private static _worldMatrixProperty = ShaderProperty.getByName("renderer_ModelMat");
private static _mvMatrixProperty = ShaderProperty.getByName("renderer_MVMat");
private static _mvpMatrixProperty = ShaderProperty.getByName("renderer_MVPMat");
- private static _mvInvMatrixProperty = ShaderProperty.getByName("renderer_MVInvMat");
private static _normalMatrixProperty = ShaderProperty.getByName("renderer_NormalMat");
- private static _rendererLayerProperty = ShaderProperty.getByName("renderer_Layer");
/** @internal */
@ignoreClone
@@ -46,12 +47,8 @@ export class Renderer extends Component {
_globalShaderMacro: ShaderMacroCollection = new ShaderMacroCollection();
@ignoreClone
_renderFrameCount: number;
- /** @internal */
@assignmentClone
_maskInteraction: SpriteMaskInteraction = SpriteMaskInteraction.None;
- /** @internal */
- @ignoreClone
- _batchedTransformShaderData: boolean = false;
@assignmentClone
_maskLayer: SpriteMaskLayer = SpriteMaskLayer.Layer0;
@@ -74,8 +71,6 @@ export class Renderer extends Component {
@ignoreClone
private _mvpMatrix: Matrix = new Matrix();
@ignoreClone
- private _mvInvMatrix: Matrix = new Matrix();
- @ignoreClone
private _normalMatrix: Matrix = new Matrix();
@ignoreClone
private _materialsInstanced: boolean[] = [];
@@ -384,7 +379,6 @@ export class Renderer extends Component {
this._shaderData = null;
this._mvMatrix = null;
this._mvpMatrix = null;
- this._mvInvMatrix = null;
this._normalMatrix = null;
this._materialsInstanced = null;
this._rendererLayer = null;
@@ -393,74 +387,66 @@ export class Renderer extends Component {
/**
* @internal
*/
- _updateTransformShaderData(context: RenderContext, onlyMVP: boolean, batched: boolean): void {
+ _updateTransformShaderData(context: RenderContext, onlyMVP: boolean): void {
const worldMatrix = this._transformEntity.transform.worldMatrix;
+ const { shaderData } = this;
if (onlyMVP) {
- this._updateProjectionRelatedShaderData(context, worldMatrix, batched);
+ const mvpMatrix = this._mvpMatrix;
+ Matrix.multiply(context.viewProjectionMatrix, worldMatrix, mvpMatrix);
+ shaderData.setMatrix(Renderer._mvpMatrixProperty, mvpMatrix);
} else {
- this._updateWorldViewRelatedShaderData(context, worldMatrix, batched);
+ const mvMatrix = this._mvMatrix;
+ const normalMatrix = this._normalMatrix;
+
+ Matrix.multiply(context.viewMatrix, worldMatrix, mvMatrix);
+ Matrix.invert(worldMatrix, normalMatrix);
+ normalMatrix.transpose();
+
+ shaderData.setMatrix(Renderer._worldMatrixProperty, worldMatrix);
+ shaderData.setMatrix(Renderer._mvMatrixProperty, mvMatrix);
+ shaderData.setMatrix(Renderer._normalMatrixProperty, normalMatrix);
+
+ const mvpMatrix = this._mvpMatrix;
+ Matrix.multiply(context.viewProjectionMatrix, worldMatrix, mvpMatrix);
+ shaderData.setMatrix(Renderer._mvpMatrixProperty, mvpMatrix);
}
}
/**
* @internal
*/
- _canBatch(elementA: SubRenderElement, elementB: SubRenderElement): boolean {
+ _canBatch(preElement: RenderElement, curElement: RenderElement): boolean {
return false;
}
/**
* @internal
*/
- _batch(elementA: SubRenderElement, elementB?: SubRenderElement): void {}
+ _batch(preElement: RenderElement | null, curElement: RenderElement): void {}
/**
- * Update once per frame per renderer, not influenced by batched.
+ * Update once per frame per renderer.
*/
protected _update(context: RenderContext): void {
const { layer } = this.entity;
this._rendererLayer.set(layer & 65535, (layer >>> 16) & 65535, 0, 0);
}
- protected _updateWorldViewRelatedShaderData(context: RenderContext, worldMatrix: Matrix, batched: boolean): void {
- const { shaderData, _mvInvMatrix: mvInvMatrix } = this;
- if (batched) {
+ /**
+ * Update transform shader data for world-space vertices (2D renderers).
+ * Vertices are already in world space, so model matrix is identity.
+ */
+ protected _updateWorldSpaceTransformShaderData(context: RenderContext, onlyMVP: boolean): void {
+ const { shaderData } = this;
+ if (onlyMVP) {
+ shaderData.setMatrix(Renderer._mvpMatrixProperty, context.viewProjectionMatrix);
+ } else {
// @ts-ignore
const identityMatrix = Matrix._identity;
-
- Matrix.invert(context.viewMatrix, mvInvMatrix);
-
- shaderData.setMatrix(Renderer._localMatrixProperty, identityMatrix);
shaderData.setMatrix(Renderer._worldMatrixProperty, identityMatrix);
shaderData.setMatrix(Renderer._mvMatrixProperty, context.viewMatrix);
- shaderData.setMatrix(Renderer._mvInvMatrixProperty, mvInvMatrix);
shaderData.setMatrix(Renderer._normalMatrixProperty, identityMatrix);
- } else {
- const mvMatrix = this._mvMatrix;
- const normalMatrix = this._normalMatrix;
-
- Matrix.multiply(context.viewMatrix, worldMatrix, mvMatrix);
- Matrix.invert(mvMatrix, mvInvMatrix);
- Matrix.invert(worldMatrix, normalMatrix);
- normalMatrix.transpose();
-
- shaderData.setMatrix(Renderer._localMatrixProperty, this._transformEntity.transform.localMatrix);
- shaderData.setMatrix(Renderer._worldMatrixProperty, worldMatrix);
- shaderData.setMatrix(Renderer._mvMatrixProperty, mvMatrix);
- shaderData.setMatrix(Renderer._mvInvMatrixProperty, mvInvMatrix);
- shaderData.setMatrix(Renderer._normalMatrixProperty, normalMatrix);
- }
-
- this._updateProjectionRelatedShaderData(context, worldMatrix, batched);
- }
-
- protected _updateProjectionRelatedShaderData(context: RenderContext, worldMatrix: Matrix, batched: boolean): void {
- if (batched) {
- this.shaderData.setMatrix(Renderer._mvpMatrixProperty, context.viewProjectionMatrix);
- } else {
- const mvpMatrix = this._mvpMatrix;
- Matrix.multiply(context.viewProjectionMatrix, worldMatrix, mvpMatrix);
- this.shaderData.setMatrix(Renderer._mvpMatrixProperty, mvpMatrix);
+ shaderData.setMatrix(Renderer._mvpMatrixProperty, context.viewProjectionMatrix);
}
}
diff --git a/packages/core/src/SceneManager.ts b/packages/core/src/SceneManager.ts
index 92de60d66b..1fa51ba265 100644
--- a/packages/core/src/SceneManager.ts
+++ b/packages/core/src/SceneManager.ts
@@ -91,10 +91,21 @@ export class SceneManager {
* @returns scene promise
*/
loadScene(url: string, destroyOldScene: boolean = true): AssetPromise {
- const scenePromise = this.engine.resourceManager.load({ url, type: AssetType.Scene });
+ const resourceManager = this.engine.resourceManager;
+ // Evict the Scene asset cache for managed scenes about to be destroyed, so a fresh Scene
+ // instance is created by the loader instead of returning the same instance we're about to
+ // destroy (self-destroy would leave the active scene in a zombie state).
+ if (destroyOldScene) {
+ const realPath = resourceManager._virtualPathResourceMap[url]?.path ?? url;
+ const cached = resourceManager.getFromCache(realPath);
+ if (cached && this._scenes.indexOf(cached) !== -1) {
+ resourceManager._deleteAsset(cached);
+ }
+ }
+ const scenePromise = resourceManager.load({ url, type: AssetType.Scene });
scenePromise.then((scene: Scene) => {
if (destroyOldScene) {
- const scenes = this._scenes.getArray();
+ const scenes = this._scenes.getLoopArray();
for (let i = 0, n = scenes.length; i < n; i++) {
scenes[i].destroy();
}
diff --git a/packages/core/src/Signal.ts b/packages/core/src/Signal.ts
index a18b86cf5a..e337e03d9d 100644
--- a/packages/core/src/Signal.ts
+++ b/packages/core/src/Signal.ts
@@ -20,9 +20,11 @@ export class Signal {
on(fn: (...args: T) => void, target?: any): void;
/**
* Add a structured binding listener. Structured bindings support clone remapping.
+ * The target method will be invoked as `method(...signalArgs, ...args)` —
+ * runtime signal arguments come first, bound arguments are appended.
* @param target - The target component
* @param methodName - The method name to invoke on the target
- * @param args - Pre-resolved arguments
+ * @param args - Pre-resolved arguments appended after the runtime signal arguments
*/
on(target: Component, methodName: string, ...args: any[]): void;
on(fnOrTarget: ((...args: T) => void) | Component, targetOrMethodName?: any, ...args: any[]): void {
@@ -37,9 +39,11 @@ export class Signal {
once(fn: (...args: T) => void, target?: any): void;
/**
* Add a one-time structured binding listener.
+ * The target method will be invoked as `method(...signalArgs, ...args)` —
+ * runtime signal arguments come first, bound arguments are appended.
* @param target - The target component
* @param methodName - The method name to invoke on the target
- * @param args - Pre-resolved arguments
+ * @param args - Pre-resolved arguments appended after the runtime signal arguments
*/
once(target: Component, methodName: string, ...args: any[]): void;
once(fnOrTarget: ((...args: T) => void) | Component, targetOrMethodName?: any, ...args: any[]): void {
@@ -171,7 +175,7 @@ export class Signal {
const methodName = targetOrMethodName as string;
const fn =
args.length > 0
- ? (...signalArgs: any[]) => (target as any)[methodName](...args, ...signalArgs)
+ ? (...signalArgs: any[]) => (target as any)[methodName](...signalArgs, ...args)
: (...signalArgs: any[]) => (target as any)[methodName](...signalArgs);
this._listeners.push({
fn: fn as (...args: T) => void,
diff --git a/packages/core/src/Transform.ts b/packages/core/src/Transform.ts
index f822474dcc..f408814c93 100644
--- a/packages/core/src/Transform.ts
+++ b/packages/core/src/Transform.ts
@@ -48,6 +48,8 @@ export class Transform extends Component {
private _worldRight: Vector3 = null;
@ignoreClone
private _worldUp: Vector3 = null;
+ @ignoreClone
+ private _frontFaceInvert: boolean = false;
@ignoreClone
protected _isParentDirty: boolean = true;
@@ -563,18 +565,42 @@ export class Transform extends Component {
*/
_parentChange(): void {
this._isParentDirty = true;
- this._updateAllWorldFlag(TransformModifyFlags.WmWpWeWqWsWus);
+ // Reparent invalidates the world state of the entire subtree:
+ // 1) `_updateAllWorldFlag` has an early-exit that skips propagation when
+ // self's world dirty flags are already set — invalid after reparent.
+ // 2) Descendants may have cached a stale `_parentTransformCache` (e.g. if
+ // `_getParentTransform` was ever called while their ancestor chain was
+ // partially constructed during clone/instantiate). Force them to
+ // re-resolve the parent transform on next access.
+ this._propagateReparentDirty(TransformModifyFlags.WmWpWeWqWsWus);
+ }
+
+ private _propagateReparentDirty(flags: TransformModifyFlags): void {
+ this._worldAssociatedChange(flags);
+ const children = this._entity._children;
+ for (let i = 0, n = children.length; i < n; i++) {
+ const transform = children[i].transform;
+ if (transform) {
+ transform._isParentDirty = true;
+ transform._propagateReparentDirty(flags);
+ }
+ }
}
/**
* @internal
*/
_isFrontFaceInvert(): boolean {
- const scale = this.lossyWorldScale;
- let isInvert = scale.x < 0;
- scale.y < 0 && (isInvert = !isInvert);
- scale.z < 0 && (isInvert = !isInvert);
- return isInvert;
+ if (this._isContainDirtyFlag(TransformModifyFlags.WorldFrontFaceInvert)) {
+ const s = this._scale;
+ let invert = s.x < 0;
+ s.y < 0 && (invert = !invert);
+ s.z < 0 && (invert = !invert);
+ const parent = this._getParentTransform();
+ this._frontFaceInvert = parent ? invert !== parent._isFrontFaceInvert() : invert;
+ this._setDirtyFlagFalse(TransformModifyFlags.WorldFrontFaceInvert);
+ }
+ return this._frontFaceInvert;
}
/**
@@ -597,8 +623,13 @@ export class Transform extends Component {
// @ts-ignore
scale._onValueChanged = target._onScaleChanged;
- // When cloning, other components may obtain properties such as `rotationQuaternion` in the constructor, local related dirty flags need to be corrected
+ // When cloning, other components may obtain properties such as `rotationQuaternion` in the constructor, local related dirty flags need to be corrected.
+ // Earlier in this Entity's construction other components (e.g. DynamicCollider) may have queried
+ // `worldPosition`, which clears the WorldPosition / WorldMatrix dirty flags as a side effect of caching
+ // the computed value. After this _cloneTo writes new local values, those world-derived caches are stale,
+ // so re-dirty them and notify listeners (Collider._updateFlag etc.) so subsequent reads recompute correctly.
target._setDirtyFlagTrue(TransformModifyFlags.LocalQuat | TransformModifyFlags.LocalMatrix);
+ target._worldAssociatedChange(TransformModifyFlags.WmWpWeWqWsWus);
}
protected _onLocalMatrixChanging(): void {
@@ -909,24 +940,27 @@ export enum TransformModifyFlags {
*/
IsWorldUniformScaling = 0x100,
+ // Note: 0x200 (UITransformModifyFlags.Size) and 0x400 (Pivot) are reserved by UITransform — do not reuse.
+ WorldFrontFaceInvert = 0x800,
+
/** WorldMatrix | WorldPosition */
WmWp = 0x84,
/** WorldMatrix | WorldEuler | WorldQuat */
WmWeWq = 0x98,
- /** WorldMatrix | WorldEuler | WorldQuat | WorldScale*/
- WmWeWqWs = 0xb8,
+ /** WorldMatrix | WorldEuler | WorldQuat | WorldScale | WorldFrontFaceInvert */
+ WmWeWqWs = 0x8b8,
/** WorldMatrix | WorldPosition | WorldEuler | WorldQuat */
WmWpWeWq = 0x9c,
- /** WorldMatrix | WorldScale */
- WmWs = 0xa0,
- /** WorldMatrix | WorldScale | WorldUniformScaling */
- WmWsWus = 0x1a0,
- /** WorldMatrix | WorldPosition | WorldScale */
- WmWpWs = 0xa4,
- /** WorldMatrix | WorldPosition | WorldEuler | WorldQuat | WorldScale */
- WmWpWeWqWs = 0xbc,
- /** WorldMatrix | WorldPosition | WorldEuler | WorldQuat | WorldScale | WorldUniformScaling */
- WmWpWeWqWsWus = 0x1bc,
- /** LocalQuat | LocalMatrix | WorldMatrix | WorldPosition | WorldEuler | WorldQuat | WorldScale | WorldUniformScaling */
- LqLmWmWpWeWqWsWus = 0x1fe
+ /** WorldMatrix | WorldScale | WorldFrontFaceInvert */
+ WmWs = 0x8a0,
+ /** WorldMatrix | WorldScale | WorldUniformScaling | WorldFrontFaceInvert */
+ WmWsWus = 0x9a0,
+ /** WorldMatrix | WorldPosition | WorldScale | WorldFrontFaceInvert */
+ WmWpWs = 0x8a4,
+ /** WorldMatrix | WorldPosition | WorldEuler | WorldQuat | WorldScale | WorldFrontFaceInvert */
+ WmWpWeWqWs = 0x8bc,
+ /** WorldMatrix | WorldPosition | WorldEuler | WorldQuat | WorldScale | WorldUniformScaling | WorldFrontFaceInvert */
+ WmWpWeWqWsWus = 0x9bc,
+ /** LocalQuat | LocalMatrix | WorldMatrix | WorldPosition | WorldEuler | WorldQuat | WorldScale | WorldUniformScaling | WorldFrontFaceInvert */
+ LqLmWmWpWeWqWsWus = 0x9fe
}
diff --git a/packages/core/src/animation/AnimationClip.ts b/packages/core/src/animation/AnimationClip.ts
index adb0c07c91..9f7b6779fc 100644
--- a/packages/core/src/animation/AnimationClip.ts
+++ b/packages/core/src/animation/AnimationClip.ts
@@ -204,6 +204,15 @@ export class AnimationClip extends EngineObject {
this._length = 0;
}
+ /**
+ * Samples an animation at a given time.
+ * @param entity - The animated entity
+ * @param time - The time to sample an animation
+ */
+ sampleAnimation(entity: Entity, time: number): void {
+ this._sampleAnimation(entity, time);
+ }
+
/**
* @internal
* Samples an animation at a given time.
diff --git a/packages/core/src/animation/AnimationClipCurveBinding.ts b/packages/core/src/animation/AnimationClipCurveBinding.ts
index 906c154087..e6e4066181 100644
--- a/packages/core/src/animation/AnimationClipCurveBinding.ts
+++ b/packages/core/src/animation/AnimationClipCurveBinding.ts
@@ -33,7 +33,7 @@ export class AnimationClipCurveBinding {
/** The animation curve. */
curve: AnimationCurve;
- private _tempCurveOwner: Record> = {};
+ private _tempCurveOwner = new WeakMap>();
/**
* @internal
@@ -63,10 +63,11 @@ export class AnimationClipCurveBinding {
* @internal
*/
_getTempCurveOwner(entity: Entity, component: Component): AnimationCurveOwner {
- const { instanceId } = entity;
- if (!this._tempCurveOwner[instanceId]) {
- this._tempCurveOwner[instanceId] = this._createCurveOwner(entity, component);
+ let owner = this._tempCurveOwner.get(entity);
+ if (!owner) {
+ owner = this._createCurveOwner(entity, component);
+ this._tempCurveOwner.set(entity, owner);
}
- return this._tempCurveOwner[instanceId];
+ return owner;
}
}
diff --git a/packages/core/src/animation/Animator.ts b/packages/core/src/animation/Animator.ts
index 180c2d1356..0bb5fea0c0 100644
--- a/packages/core/src/animation/Animator.ts
+++ b/packages/core/src/animation/Animator.ts
@@ -19,6 +19,7 @@ import { AnimatorCullingMode } from "./enums/AnimatorCullingMode";
import { AnimatorLayerBlendingMode } from "./enums/AnimatorLayerBlendingMode";
import { AnimatorStatePlayState } from "./enums/AnimatorStatePlayState";
import { LayerState } from "./enums/LayerState";
+import { WrapMode } from "./enums/WrapMode";
import { AnimationCurveLayerOwner } from "./internal/AnimationCurveLayerOwner";
import { AnimationEventHandler } from "./internal/AnimationEventHandler";
import { AnimatorLayerData } from "./internal/AnimatorLayerData";
@@ -37,6 +38,9 @@ export class Animator extends Component {
/** The playback speed of the Animator, 1.0 is normal playback speed. */
@assignmentClone
speed = 1.0;
+ /** Whether the Animator sends AnimationEvent callbacks. */
+ @assignmentClone
+ fireEvents = true;
/** @internal */
_playFrameCount = -1;
@@ -53,7 +57,7 @@ export class Animator extends Component {
@ignoreClone
private _animatorLayersData = new Array();
@ignoreClone
- private _curveOwnerPool: Record>> = Object.create(null);
+ private _curveOwnerPool: WeakMap>> = new WeakMap();
@ignoreClone
private _animationEventHandlerPool = new ClearableObjectPool(AnimationEventHandler);
@ignoreClone
@@ -218,13 +222,31 @@ export class Animator extends Component {
* @param stateName - The state name
* @param layerIndex - The layer index(default -1). If layer is -1, find the first state with the given state name
*/
- findAnimatorState(stateName: string, layerIndex: number = -1): AnimatorState {
- return this._getAnimatorStateInfo(stateName, layerIndex).state;
+ /**
+ * Find the per-instance play data for a state by name.
+ * The returned object's `speed` and `wrapMode` are per-instance and safe to modify without affecting other Animator instances.
+ * @param stateName - The state name
+ * @param layerIndex - The layer index (default -1, searches all layers)
+ * @returns Per-instance AnimatorStatePlayData, or null if not found
+ */
+ findAnimatorState(stateName: string, layerIndex: number = -1): AnimatorStatePlayData {
+ const { state, layerIndex: foundLayer } = this._getAnimatorStateInfo(stateName, layerIndex);
+ if (!state || foundLayer < 0) return null;
+ const layerData = this._animatorLayersData[foundLayer];
+ if (!layerData) return null;
+ // Check srcPlayData and destPlayData for the matching state
+ if (layerData.srcPlayData.state === state) return layerData.srcPlayData;
+ if (layerData.destPlayData.state === state) return layerData.destPlayData;
+ // State exists in controller but not currently playing — return srcPlayData initialized with the state
+ return layerData.srcPlayData;
}
/**
* Get the layer by name.
* @param name - The layer's name.
+ * @todo Return per-instance layer data (like AnimatorStatePlayData for states) instead of shared asset.
+ * Currently returns the shared AnimatorControllerLayer — modifying `weight` affects all instances.
+ * Should follow Unity's pattern: Animator.SetLayerWeight/GetLayerWeight (per-instance).
*/
findLayerByName(name: string): AnimatorControllerLayer {
return this._animatorController?._layersMap[name];
@@ -312,17 +334,32 @@ export class Animator extends Component {
* @internal
*/
_reset(): void {
- const { _curveOwnerPool: animationCurveOwners } = this;
- for (let instanceId in animationCurveOwners) {
- const propertyOwners = animationCurveOwners[instanceId];
- for (let property in propertyOwners) {
- const owner = propertyOwners[property];
- owner.revertDefaultValue();
+ // revertDefaultValue 可能在死 target 上抛;listener 清理必须始终发生,否则
+ // polyfill 的 clipChangedListener 会留在共享的 AnimatorState 上,捕获 this(Animator)→Entity 整树。
+ // 用 try/catch 隔离 revert,listener 块紧跟其后无条件执行。
+ const layersData = this._animatorLayersData;
+ for (let i = 0, n = layersData.length; i < n; i++) {
+ const stateDataMap = layersData[i].animatorStateDataMap;
+ for (const stateName in stateDataMap) {
+ const stateData = stateDataMap[stateName];
+ const layerOwners = stateData.curveLayerOwner;
+ for (let j = 0, m = layerOwners.length; j < m; j++) {
+ try {
+ layerOwners[j]?.curveOwner?.revertDefaultValue();
+ } catch (e) {
+ Logger.warn("Animator._reset: revertDefaultValue threw", e);
+ }
+ }
+ if (stateData.clipChangedListener && stateData.state) {
+ stateData.state._updateFlagManager.removeListener(stateData.clipChangedListener);
+ stateData.clipChangedListener = null;
+ stateData.state = null;
+ }
}
}
this._animatorLayersData.length = 0;
- this._curveOwnerPool = Object.create(null);
+ this._curveOwnerPool = new WeakMap();
this._parametersValueMap = Object.create(null);
this._animationEventHandlerPool.clear();
@@ -344,6 +381,7 @@ export class Animator extends Component {
protected override _onDestroy(): void {
super._onDestroy();
+ this._reset();
const controller = this._animatorController;
if (controller) {
this._addResourceReferCount(controller, -1);
@@ -442,17 +480,20 @@ export class Animator extends Component {
}
const { property } = curve;
- const { instanceId } = component;
- // Get owner
- const propertyOwners = (curveOwnerPool[instanceId] ||= >>(
- Object.create(null)
- ));
+ // Get owner — WeakMap keyed by Component so dead components let entries GC.
+ let propertyOwners = curveOwnerPool.get(component);
+ if (!propertyOwners) {
+ propertyOwners = >>Object.create(null);
+ curveOwnerPool.set(component, propertyOwners);
+ }
const owner = (propertyOwners[property] ||= curve._createCurveOwner(targetEntity, component));
- // Get layer owner
- const layerPropertyOwners = (layerCurveOwnerPool[instanceId] ||= >(
- Object.create(null)
- ));
+ // Get layer owner — same WeakMap-by-Component pattern (was Record which kept dead components pinned for the Animator's lifetime).
+ let layerPropertyOwners = layerCurveOwnerPool.get(component);
+ if (!layerPropertyOwners) {
+ layerPropertyOwners = >Object.create(null);
+ layerCurveOwnerPool.set(component, layerPropertyOwners);
+ }
const layerOwner = (layerPropertyOwners[property] ||= curve._createCurveLayerOwner(owner));
if (mask && mask.pathMasks.length) {
@@ -495,6 +536,8 @@ export class Animator extends Component {
};
clipChangedListener();
state._updateFlagManager.addListener(clipChangedListener);
+ animatorStateData.state = state;
+ animatorStateData.clipChangedListener = clipChangedListener;
}
private _clearCrossData(animatorLayerData: AnimatorLayerData): void {
@@ -616,7 +659,7 @@ export class Animator extends Component {
const { srcPlayData } = layerData;
const { state } = srcPlayData;
- const playSpeed = state.speed * this.speed;
+ const playSpeed = srcPlayData.speed * this.speed;
const playDeltaTime = playSpeed * deltaTime;
srcPlayData.updateOrientation(playDeltaTime);
@@ -753,8 +796,8 @@ export class Animator extends Component {
return;
}
- const srcPlaySpeed = srcState.speed * speed;
- const dstPlaySpeed = destState.speed * speed;
+ const srcPlaySpeed = srcPlayData.speed * speed;
+ const dstPlaySpeed = destPlayData.speed * speed;
const dstPlayDeltaTime = dstPlaySpeed * deltaTime;
srcPlayData && srcPlayData.updateOrientation(srcPlaySpeed * deltaTime);
@@ -883,7 +926,7 @@ export class Animator extends Component {
return;
}
- const playSpeed = state.speed * this.speed;
+ const playSpeed = destPlayData.speed * this.speed;
const playDeltaTime = playSpeed * deltaTime;
destPlayData.updateOrientation(playDeltaTime);
@@ -989,7 +1032,7 @@ export class Animator extends Component {
): void {
const playData = layerData.srcPlayData;
const { state } = playData;
- const actualSpeed = state.speed * this.speed;
+ const actualSpeed = playData.speed * this.speed;
const actualDeltaTime = actualSpeed * deltaTime;
playData.updateOrientation(actualDeltaTime);
@@ -1452,23 +1495,28 @@ export class Animator extends Component {
lastClipTime: number,
deltaTime: number
): void {
- const { state, isForward, clipTime } = playData;
+ const { state, isForward, clipTime, wrapMode } = playData;
const startTime = state._getClipActualStartTime();
const endTime = state._getClipActualEndTime();
+ const canWrap = wrapMode === WrapMode.Loop;
if (isForward) {
if (lastClipTime + deltaTime >= endTime) {
this._fireSubAnimationEvents(playData, eventHandlers, lastClipTime, endTime);
- playData.currentEventIndex = 0;
- this._fireSubAnimationEvents(playData, eventHandlers, startTime, clipTime);
+ if (canWrap) {
+ playData.currentEventIndex = 0;
+ this._fireSubAnimationEvents(playData, eventHandlers, startTime, clipTime);
+ }
} else {
this._fireSubAnimationEvents(playData, eventHandlers, lastClipTime, clipTime);
}
} else {
if (lastClipTime + deltaTime <= startTime) {
this._fireBackwardSubAnimationEvents(playData, eventHandlers, lastClipTime, startTime);
- playData.currentEventIndex = eventHandlers.length - 1;
- this._fireBackwardSubAnimationEvents(playData, eventHandlers, endTime, clipTime);
+ if (canWrap) {
+ playData.currentEventIndex = eventHandlers.length - 1;
+ this._fireBackwardSubAnimationEvents(playData, eventHandlers, endTime, clipTime);
+ }
} else {
this._fireBackwardSubAnimationEvents(playData, eventHandlers, lastClipTime, clipTime);
}
@@ -1565,7 +1613,9 @@ export class Animator extends Component {
deltaTime: number
) {
const { eventHandlers } = playData.stateData;
- eventHandlers.length && this._fireAnimationEvents(playData, eventHandlers, lastClipTime, deltaTime);
+ this.fireEvents &&
+ eventHandlers.length &&
+ this._fireAnimationEvents(playData, eventHandlers, lastClipTime, deltaTime);
if (lastPlayState === AnimatorStatePlayState.UnStarted) {
state._callOnEnter(this, layerIndex);
diff --git a/packages/core/src/animation/index.ts b/packages/core/src/animation/index.ts
index b829ffde54..bd201a871f 100644
--- a/packages/core/src/animation/index.ts
+++ b/packages/core/src/animation/index.ts
@@ -11,6 +11,7 @@ export { Animator } from "./Animator";
export { AnimatorController } from "./AnimatorController";
export { AnimatorControllerLayer } from "./AnimatorControllerLayer";
export { AnimatorState } from "./AnimatorState";
+export { AnimatorStatePlayData } from "./internal/AnimatorStatePlayData";
export { AnimatorStateMachine } from "./AnimatorStateMachine";
export { AnimatorStateTransition } from "./AnimatorStateTransition";
export { AnimatorConditionMode } from "./enums/AnimatorConditionMode";
diff --git a/packages/core/src/animation/internal/AnimatorLayerData.ts b/packages/core/src/animation/internal/AnimatorLayerData.ts
index a7bd04f1a5..959aadf208 100644
--- a/packages/core/src/animation/internal/AnimatorLayerData.ts
+++ b/packages/core/src/animation/internal/AnimatorLayerData.ts
@@ -1,3 +1,4 @@
+import { Component } from "../../Component";
import { AnimatorControllerLayer } from "../AnimatorControllerLayer";
import { AnimatorStateTransition } from "../AnimatorStateTransition";
import { LayerState } from "../enums/LayerState";
@@ -11,7 +12,7 @@ import { AnimatorStatePlayData } from "./AnimatorStatePlayData";
export class AnimatorLayerData {
layerIndex: number;
layer: AnimatorControllerLayer;
- curveOwnerPool: Record> = Object.create(null);
+ curveOwnerPool: WeakMap> = new WeakMap();
animatorStateDataMap: Record = {};
srcPlayData: AnimatorStatePlayData = new AnimatorStatePlayData();
destPlayData: AnimatorStatePlayData = new AnimatorStatePlayData();
diff --git a/packages/core/src/animation/internal/AnimatorStateData.ts b/packages/core/src/animation/internal/AnimatorStateData.ts
index 146c0bb52f..42b50edd87 100644
--- a/packages/core/src/animation/internal/AnimatorStateData.ts
+++ b/packages/core/src/animation/internal/AnimatorStateData.ts
@@ -1,3 +1,4 @@
+import { AnimatorState } from "../AnimatorState";
import { AnimationCurveLayerOwner } from "./AnimationCurveLayerOwner";
import { AnimationEventHandler } from "./AnimationEventHandler";
@@ -7,4 +8,6 @@ import { AnimationEventHandler } from "./AnimationEventHandler";
export class AnimatorStateData {
curveLayerOwner: AnimationCurveLayerOwner[] = [];
eventHandlers: AnimationEventHandler[] = [];
+ state: AnimatorState = null;
+ clipChangedListener: () => void = null;
}
diff --git a/packages/core/src/animation/internal/AnimatorStatePlayData.ts b/packages/core/src/animation/internal/AnimatorStatePlayData.ts
index 7d10fc2324..7adc90c2f4 100644
--- a/packages/core/src/animation/internal/AnimatorStatePlayData.ts
+++ b/packages/core/src/animation/internal/AnimatorStatePlayData.ts
@@ -1,20 +1,60 @@
+import { AnimationClip } from "../AnimationClip";
import { AnimatorState } from "../AnimatorState";
+import { AnimatorStateTransition } from "../AnimatorStateTransition";
import { AnimatorStatePlayState } from "../enums/AnimatorStatePlayState";
import { WrapMode } from "../enums/WrapMode";
+import { StateMachineScript } from "../StateMachineScript";
import { AnimatorStateData } from "./AnimatorStateData";
/**
- * @internal
+ * Per-instance runtime data for an AnimatorState.
+ * Proxies read-only properties from the shared AnimatorState asset,
+ * while providing per-instance mutable properties (e.g. speed, wrapMode).
*/
export class AnimatorStatePlayData {
+ /** @internal */
state: AnimatorState;
+ /** @internal */
stateData: AnimatorStateData;
+ /** @internal */
playedTime: number;
playState: AnimatorStatePlayState;
+ /** @internal */
clipTime: number;
+ /** @internal */
currentEventIndex: number;
+ /** @internal */
isForward = true;
+ /** @internal */
offsetFrameTime: number;
+ /** Per-instance speed. Initialized from AnimatorState.speed, safe to modify without affecting other instances. */
+ speed: number = 1.0;
+ /** Per-instance wrap mode. Initialized from AnimatorState.wrapMode, safe to modify without affecting other instances. */
+ wrapMode: WrapMode = WrapMode.Loop;
+
+ // ── Proxy properties from AnimatorState (read-only) ──
+
+ /** The name of the state. */
+ get name(): string {
+ return this.state.name;
+ }
+
+ /** The clip played by this state. */
+ get clip(): AnimationClip {
+ return this.state.clip;
+ }
+
+ /** The transitions going out of this state. */
+ get transitions(): Readonly {
+ return this.state.transitions;
+ }
+
+ /**
+ * Add a state machine script to the underlying AnimatorState.
+ */
+ addStateMachineScript(scriptType: new () => T): T {
+ return this.state.addStateMachineScript(scriptType);
+ }
private _changedOrientation = false;
@@ -27,6 +67,8 @@ export class AnimatorStatePlayData {
this.clipTime = state.clipStartTime * state.clip.length;
this.currentEventIndex = 0;
this.isForward = true;
+ this.speed = state.speed;
+ this.wrapMode = state.wrapMode;
this.state._transitionCollection.needResetCurrentCheckIndex = true;
}
@@ -47,7 +89,7 @@ export class AnimatorStatePlayData {
let time = this.playedTime + this.offsetFrameTime;
const duration = state._getDuration();
this.playState = AnimatorStatePlayState.Playing;
- if (state.wrapMode === WrapMode.Loop) {
+ if (this.wrapMode === WrapMode.Loop) {
time = duration ? time % duration : 0;
} else {
if (Math.abs(time) >= duration) {
diff --git a/packages/core/src/asset/AssetPromise.ts b/packages/core/src/asset/AssetPromise.ts
index 99f00e86f2..9e9dc261c0 100644
--- a/packages/core/src/asset/AssetPromise.ts
+++ b/packages/core/src/asset/AssetPromise.ts
@@ -164,16 +164,16 @@ export class AssetPromise implements PromiseLike {
* @returns AssetPromise
*/
onProgress(
- onTaskComplete: (loaded: number, total: number) => void,
+ onTaskComplete?: (loaded: number, total: number) => void,
onTaskDetail?: (identifier: string, loaded: number, total: number) => void
): AssetPromise {
const completeProgress = this._taskCompleteProgress;
const detailProgress = this._taskDetailProgress;
- if (completeProgress) {
+ if (completeProgress && onTaskComplete) {
onTaskComplete(completeProgress.loaded, completeProgress.total);
}
- if (detailProgress) {
+ if (detailProgress && onTaskDetail) {
for (let url in detailProgress) {
const { loaded, total } = detailProgress[url];
onTaskDetail(url, loaded, total);
diff --git a/packages/core/src/asset/AssetType.ts b/packages/core/src/asset/AssetType.ts
index 17ee100043..fad0e75043 100644
--- a/packages/core/src/asset/AssetType.ts
+++ b/packages/core/src/asset/AssetType.ts
@@ -46,7 +46,7 @@ export enum AssetType {
Font = "Font",
/** Source Font, include ttf, otf and woff. */
SourceFont = "SourceFont",
- /** AudioClip, include ogg, wav and mp3. */
+ /** AudioClip, include ogg, wav, mp3, m4a, aac and flac. */
Audio = "Audio",
/** Project asset. */
Project = "project",
diff --git a/packages/core/src/asset/ResourceManager.ts b/packages/core/src/asset/ResourceManager.ts
index 0395b2b29b..94cb7ee946 100644
--- a/packages/core/src/asset/ResourceManager.ts
+++ b/packages/core/src/asset/ResourceManager.ts
@@ -340,7 +340,12 @@ export class ResourceManager {
}
private _assignDefaultOptions(assetInfo: LoadItem): LoadItem {
- assetInfo.type = assetInfo.type ?? ResourceManager._getTypeByUrl(assetInfo.url);
+ const remoteConfig = this._virtualPathResourceMap[assetInfo.url];
+ if (remoteConfig) {
+ assetInfo.type = remoteConfig.type;
+ } else {
+ assetInfo.type = assetInfo.type ?? ResourceManager._getTypeByUrl(assetInfo.url);
+ }
if (assetInfo.type === undefined) {
throw `asset type should be specified: ${assetInfo.url}`;
}
diff --git a/packages/core/src/audio/AudioManager.ts b/packages/core/src/audio/AudioManager.ts
index ad93f5ed59..b1c4c5651b 100644
--- a/packages/core/src/audio/AudioManager.ts
+++ b/packages/core/src/audio/AudioManager.ts
@@ -1,3 +1,9 @@
+type ResumableAudioSource = {
+ _resumePendingPlayback(): void;
+ _suspendPlaybackForInterruption(): boolean;
+ _resumeInterruptedPlayback(): void;
+};
+
/**
* Audio Manager for managing global audio context and settings.
*/
@@ -7,15 +13,22 @@ export class AudioManager {
private static _context: AudioContext;
private static _gainNode: GainNode;
- private static _resumePromise: Promise = null;
private static _needsUserGestureResume = false;
+ private static _pendingSources = new Set();
+ private static _playingSources = new Set();
+ private static _interruptedSources = new Set();
+ private static _foregroundRestoreDelay = 300;
+ private static _foregroundRestoreTimer: number | undefined;
+ private static _hidden = false;
+ private static _eventsBound = false;
/**
* Suspend the audio context.
* @returns A promise that resolves when the audio context is suspended
*/
static suspend(): Promise {
- return AudioManager._context.suspend();
+ AudioManager._suspendActiveSourcesForInterruption();
+ return AudioManager._context?.suspend() ?? Promise.resolve();
}
/**
@@ -24,14 +37,48 @@ export class AudioManager {
* @returns A promise that resolves when the audio context is resumed
*/
static resume(): Promise {
- return (AudioManager._resumePromise ??= AudioManager._context
- .resume()
- .then(() => {
- AudioManager._needsUserGestureResume = false;
- })
- .finally(() => {
- AudioManager._resumePromise = null;
- }));
+ const context = AudioManager._context;
+ if (!context) {
+ return Promise.resolve();
+ }
+ if (context.state === "running") {
+ AudioManager._clearForegroundRestore();
+ AudioManager._needsUserGestureResume = false;
+ AudioManager._resumePendingSources();
+ AudioManager._resumeInterruptedSources();
+ return Promise.resolve();
+ }
+ return context.resume().then(() => {
+ AudioManager._clearForegroundRestore();
+ AudioManager._needsUserGestureResume = false;
+ AudioManager._resumePendingSources();
+ AudioManager._resumeInterruptedSources();
+ });
+ }
+
+ /** @internal */
+ static _registerPendingSource(source: ResumableAudioSource): void {
+ AudioManager._pendingSources.add(source);
+ }
+
+ /** @internal */
+ static _unregisterPendingSource(source: ResumableAudioSource): void {
+ AudioManager._pendingSources.delete(source);
+ }
+
+ /** @internal */
+ static _registerPlayingSource(source: ResumableAudioSource): void {
+ AudioManager._playingSources.add(source);
+ }
+
+ /** @internal */
+ static _unregisterPlayingSource(source: ResumableAudioSource): void {
+ AudioManager._playingSources.delete(source);
+ }
+
+ /** @internal */
+ static _unregisterInterruptedSource(source: ResumableAudioSource): void {
+ AudioManager._interruptedSources.delete(source);
}
/**
@@ -41,11 +88,12 @@ export class AudioManager {
let context = AudioManager._context;
if (!context) {
AudioManager._context = context = new window.AudioContext();
- document.addEventListener("visibilitychange", AudioManager._onVisibilityChange);
- // iOS Safari requires user gesture to resume AudioContext
- document.addEventListener("touchstart", AudioManager._resumeAfterInterruption, { passive: true });
- document.addEventListener("touchend", AudioManager._resumeAfterInterruption, { passive: true });
- document.addEventListener("click", AudioManager._resumeAfterInterruption);
+ context.onstatechange = AudioManager._onContextStateChange;
+ if (!AudioManager._eventsBound) {
+ AudioManager._eventsBound = true;
+ AudioManager._bindLifecycleEvents();
+ AudioManager._bindGestureEvents();
+ }
}
return context;
}
@@ -70,22 +118,153 @@ export class AudioManager {
return AudioManager.getContext().state === "running";
}
- private static _onVisibilityChange(): void {
- if (!document.hidden && AudioManager._playingCount > 0 && !AudioManager.isAudioContextRunning()) {
- // iOS WKWebView WebKit bug(Triggered in LingGuang App): AudioContext may be in a "zombie" state where
- // state reports "suspended" but resume() alone won't restart audio rendering.
- // Calling suspend() first forces a clean internal state reset before user gesture triggers resume.
- // Related: https://bugs.webkit.org/show_bug.cgi?id=263627
- AudioManager.suspend();
- AudioManager._needsUserGestureResume = true;
+ private static _onContextStateChange(): void {
+ if (AudioManager._context?.state === "running") {
+ if (AudioManager._hidden || AudioManager._needsUserGestureResume) {
+ return;
+ }
+ AudioManager._needsUserGestureResume = false;
+ AudioManager._resumePendingSources();
+ AudioManager._resumeInterruptedSources();
+ }
+ }
+
+ private static _resumePendingSources(): void {
+ if (!AudioManager._pendingSources.size || !AudioManager.isAudioContextRunning()) {
+ return;
+ }
+
+ const pendingSources = Array.from(AudioManager._pendingSources);
+ AudioManager._pendingSources.clear();
+
+ for (let i = 0, n = pendingSources.length; i < n; i++) {
+ pendingSources[i]._resumePendingPlayback();
+ }
+ }
+
+ private static _suspendActiveSourcesForInterruption(): void {
+ if (!AudioManager._playingSources.size) {
+ return;
+ }
+
+ const playingSources = Array.from(AudioManager._playingSources);
+ for (let i = 0, n = playingSources.length; i < n; i++) {
+ const source = playingSources[i];
+ if (source._suspendPlaybackForInterruption()) {
+ AudioManager._interruptedSources.add(source);
+ }
+ }
+ }
+
+ private static _resumeInterruptedSources(): void {
+ if (!AudioManager._interruptedSources.size || !AudioManager.isAudioContextRunning()) {
+ return;
+ }
+
+ const interruptedSources = Array.from(AudioManager._interruptedSources);
+ AudioManager._interruptedSources.clear();
+
+ for (let i = 0, n = interruptedSources.length; i < n; i++) {
+ interruptedSources[i]._resumeInterruptedPlayback();
+ }
+ }
+
+ private static _bindLifecycleEvents(): void {
+ const hiddenProp = AudioManager._getHiddenProp();
+ const visibilityEvents = [
+ "visibilitychange",
+ "mozvisibilitychange",
+ "msvisibilitychange",
+ "webkitvisibilitychange",
+ "qbrowserVisibilityChange"
+ ];
+
+ for (let i = 0, n = visibilityEvents.length; i < n; i++) {
+ document.addEventListener(visibilityEvents[i], (event) => {
+ const hidden = hiddenProp ? Boolean((document as any)[hiddenProp] || (event as any)?.hidden) : document.hidden;
+ hidden ? AudioManager._onHidden() : AudioManager._onShown();
+ });
+ }
+
+ window.addEventListener("pagehide", AudioManager._onHidden);
+ window.addEventListener("pageshow", AudioManager._onShown);
+ document.addEventListener("pagehide", AudioManager._onHidden);
+ document.addEventListener("pageshow", AudioManager._onShown);
+ }
+
+ private static _bindGestureEvents(): void {
+ const gestureEvents = ["pointerdown", "pointerup", "touchstart", "touchend", "mouseup", "click"];
+ for (let i = 0, n = gestureEvents.length; i < n; i++) {
+ document.addEventListener(gestureEvents[i], AudioManager._resumeAfterInterruption, { passive: true });
+ }
+ }
+
+ private static _getHiddenProp(): string {
+ const doc = document as any;
+ if (typeof doc.hidden !== "undefined") return "hidden";
+ if (typeof doc.mozHidden !== "undefined") return "mozHidden";
+ if (typeof doc.msHidden !== "undefined") return "msHidden";
+ if (typeof doc.webkitHidden !== "undefined") return "webkitHidden";
+ return "";
+ }
+
+ private static _hasResumeWork(): boolean {
+ return (
+ AudioManager._needsUserGestureResume ||
+ AudioManager._pendingSources.size > 0 ||
+ AudioManager._interruptedSources.size > 0
+ );
+ }
+
+ private static _onHidden(): void {
+ if (AudioManager._hidden) {
+ return;
+ }
+ AudioManager._hidden = true;
+ AudioManager._clearForegroundRestore();
+ AudioManager.suspend().catch(() => {});
+ }
+
+ private static _onShown(): void {
+ if (!AudioManager._hidden) {
+ return;
+ }
+ AudioManager._hidden = false;
+
+ if (AudioManager._hasResumeWork()) {
+ AudioManager._prepareGestureResume();
+ AudioManager._scheduleForegroundRestore();
}
}
private static _resumeAfterInterruption(): void {
- if (AudioManager._needsUserGestureResume) {
+ if (AudioManager._hasResumeWork()) {
AudioManager.resume().catch((e) => {
console.warn("Failed to resume AudioContext:", e);
});
}
}
+
+ private static _scheduleForegroundRestore(): void {
+ AudioManager._clearForegroundRestore();
+ AudioManager._foregroundRestoreTimer = window.setTimeout(() => {
+ AudioManager._foregroundRestoreTimer = undefined;
+ AudioManager.resume().catch(() => AudioManager._prepareGestureResume());
+ }, AudioManager._foregroundRestoreDelay);
+ }
+
+ private static _clearForegroundRestore(): void {
+ if (AudioManager._foregroundRestoreTimer === undefined) {
+ return;
+ }
+ window.clearTimeout(AudioManager._foregroundRestoreTimer);
+ AudioManager._foregroundRestoreTimer = undefined;
+ }
+
+ private static _prepareGestureResume(): Promise {
+ // iOS WKWebView may report a resumable state while rendering is still frozen.
+ // Force a clean context edge, then let a gesture or foreground retry restore sources.
+ AudioManager._needsUserGestureResume = true;
+ return AudioManager.suspend().catch(() => {});
+ }
}
diff --git a/packages/core/src/audio/AudioSource.ts b/packages/core/src/audio/AudioSource.ts
index 29a3ed8bc1..3dacf53ec3 100644
--- a/packages/core/src/audio/AudioSource.ts
+++ b/packages/core/src/audio/AudioSource.ts
@@ -159,27 +159,11 @@ export class AudioSource extends Component {
if (AudioManager.isAudioContextRunning()) {
this._startPlayback();
} else {
- // iOS Safari requires resume() to be called within the same user gesture callback that triggers playback.
- // Document-level events won't work - must call resume() directly here in play().
this._pendingPlay = true;
- AudioManager.resume().then(
- () => {
- // Check if cancelled by stop()/pause()
- if (!this._pendingPlay) {
- return;
- }
- this._pendingPlay = false;
- // Check if still valid to play after async resume
- if (this._destroyed || !this.enabled || !this._clip) {
- return;
- }
- this._startPlayback();
- },
- (e) => {
- this._pendingPlay = false;
- console.warn("Failed to resume AudioContext:", e);
- }
- );
+ AudioManager._registerPendingSource(this);
+ AudioManager.resume().catch((e) => {
+ console.warn("Failed to resume AudioContext:", e);
+ });
}
}
@@ -187,23 +171,27 @@ export class AudioSource extends Component {
* Stops playing the clip.
*/
stop(): void {
- this._pendingPlay = false;
+ this._cancelPendingPlayback();
+ AudioManager._unregisterInterruptedSource(this);
if (this._isPlaying) {
this._clearSourceNode();
this._isPlaying = false;
- this._pausedTime = -1;
- this._playTime = -1;
AudioManager._playingCount--;
+ AudioManager._unregisterPlayingSource(this);
}
+
+ this._pausedTime = -1;
+ this._playTime = -1;
}
/**
* Pauses playing the clip.
*/
pause(): void {
- this._pendingPlay = false;
+ this._cancelPendingPlayback();
+ AudioManager._unregisterInterruptedSource(this);
if (this._isPlaying) {
this._clearSourceNode();
@@ -211,6 +199,7 @@ export class AudioSource extends Component {
this._pausedTime = AudioManager.getContext().currentTime;
this._isPlaying = false;
AudioManager._playingCount--;
+ AudioManager._unregisterPlayingSource(this);
}
}
@@ -250,34 +239,120 @@ export class AudioSource extends Component {
this.stop();
}
+ /** @internal */
+ _resumePendingPlayback(): void {
+ if (!this._pendingPlay) {
+ return;
+ }
+
+ this._pendingPlay = false;
+
+ if (this._destroyed || !this.enabled || !this._clip?._getAudioSource()) {
+ return;
+ }
+
+ this._startPlayback();
+ }
+
+ /** @internal */
+ _suspendPlaybackForInterruption(): boolean {
+ if (!this._isPlaying) {
+ return false;
+ }
+
+ const pausedTime = AudioManager.getContext().currentTime;
+ this._clearSourceNode();
+
+ this._pausedTime = pausedTime;
+ this._isPlaying = false;
+ AudioManager._playingCount--;
+ AudioManager._unregisterPlayingSource(this);
+
+ return true;
+ }
+
+ /** @internal */
+ _resumeInterruptedPlayback(): void {
+ if (
+ this._destroyed ||
+ !this.enabled ||
+ this._isPlaying ||
+ this._pendingPlay ||
+ !this._clip?._getAudioSource() ||
+ this._playTime < 0
+ ) {
+ return;
+ }
+
+ if (AudioManager.isAudioContextRunning()) {
+ this._startPlayback();
+ } else {
+ this._pendingPlay = true;
+ AudioManager._registerPendingSource(this);
+ }
+ }
+
private _startPlayback(): void {
const startTime = this._pausedTime > 0 ? this._pausedTime - this._playTime : 0;
- this._initSourceNode(startTime);
+ if (!this._initSourceNode(startTime)) {
+ this._pausedTime = -1;
+ this._playTime = -1;
+ return;
+ }
this._playTime = AudioManager.getContext().currentTime - startTime;
this._pausedTime = -1;
this._isPlaying = true;
AudioManager._playingCount++;
+ AudioManager._registerPlayingSource(this);
}
- private _initSourceNode(startTime: number): void {
+ private _initSourceNode(startTime: number): boolean {
const context = AudioManager.getContext();
const sourceNode = context.createBufferSource();
+ const audioBuffer = this._clip._getAudioSource();
+ const duration = audioBuffer.duration;
+ let offset = Math.max(0, startTime);
+
+ if (duration > 0) {
+ if (this._loop) {
+ offset %= duration;
+ } else if (offset >= duration) {
+ return false;
+ }
+ }
- sourceNode.buffer = this._clip._getAudioSource();
+ sourceNode.buffer = audioBuffer;
sourceNode.playbackRate.value = this._playbackRate;
sourceNode.loop = this._loop;
sourceNode.onended = this._onPlayEnd;
this._sourceNode = sourceNode;
sourceNode.connect(this._gainNode);
- sourceNode.start(0, startTime);
+ sourceNode.start(0, offset);
+ return true;
}
private _clearSourceNode(): void {
- this._sourceNode.stop();
- this._sourceNode.disconnect();
- this._sourceNode.onended = null;
+ const sourceNode = this._sourceNode;
+ if (!sourceNode) {
+ return;
+ }
+
+ sourceNode.onended = null;
+ try {
+ sourceNode.stop();
+ } catch {}
+ sourceNode.disconnect();
this._sourceNode = null;
}
+
+ private _cancelPendingPlayback(): void {
+ if (!this._pendingPlay) {
+ return;
+ }
+
+ this._pendingPlay = false;
+ AudioManager._unregisterPendingSource(this);
+ }
}
diff --git a/packages/core/src/clone/CloneManager.ts b/packages/core/src/clone/CloneManager.ts
index be46462982..d827140224 100644
--- a/packages/core/src/clone/CloneManager.ts
+++ b/packages/core/src/clone/CloneManager.ts
@@ -1,5 +1,6 @@
import { Entity } from "../Entity";
import { TypedArray } from "../base/Constant";
+import { ReferResource } from "../asset/ReferResource";
import { ICustomClone } from "./ComponentCloner";
import { CloneMode } from "./enums/CloneMode";
@@ -105,21 +106,46 @@ export class CloneManager {
): void {
const sourceProperty = source[k];
- // Remappable references (Entity/Component) are always remapped, regardless of clone decorator
+ // 1. Remappable references (Entity/Component) are always remapped, highest priority
if (sourceProperty instanceof Object && (sourceProperty)._remap) {
target[k] = (sourceProperty)._remap(srcRoot, targetRoot);
return;
}
+ // 2. Explicit ignore
if (cloneMode === CloneMode.Ignore) return;
- // Primitives, undecorated, or @assignmentClone: direct assign
- if (!(sourceProperty instanceof Object) || cloneMode === undefined || cloneMode === CloneMode.Assignment) {
+ // 3. Primitives / null / undefined - direct assign
+ if (!(sourceProperty instanceof Object)) {
target[k] = sourceProperty;
return;
}
- // @shallowClone / @deepClone: deep copy complex objects
+ // 4. Determine effective clone mode
+ let effectiveCloneMode: CloneMode = cloneMode;
+ if (effectiveCloneMode === undefined) {
+ // Undecorated: infer from runtime type
+ effectiveCloneMode = CloneManager._inferCloneMode(sourceProperty, target[k]);
+ } else if (effectiveCloneMode !== CloneMode.Assignment) {
+ // Decorated Shallow/Deep: upgrade to Deep if target already has independent same-type instance.
+ // Assignment is never upgraded — it means the user explicitly wants a reference copy.
+ const targetProperty = target[k];
+ if (
+ targetProperty &&
+ targetProperty !== sourceProperty &&
+ targetProperty.constructor === sourceProperty.constructor
+ ) {
+ effectiveCloneMode = CloneMode.Deep;
+ }
+ }
+
+ // 5. Assignment - direct reference copy
+ if (effectiveCloneMode === CloneMode.Assignment) {
+ target[k] = sourceProperty;
+ return;
+ }
+
+ // 6. Shallow/Deep clone for complex types
const type = sourceProperty.constructor;
switch (type) {
case Uint8Array:
@@ -137,6 +163,37 @@ export class CloneManager {
targetPropertyT.set(sourceProperty);
}
break;
+ case Map:
+ let targetPropertyM =