@genex-ai/cli-demo 0.74.0-dev.192 → 0.75.0-dev.194
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +30 -9
- package/package.json +1 -1
- package/templates/controllers/NOTICE.md +10 -0
- package/templates/controllers/assets/basis_transcoder.js +19 -0
- package/templates/controllers/assets/basis_transcoder.wasm +0 -0
- package/templates/controllers/character/meshy/meshy-loader.ts +29 -3
- package/templates/controllers/quality/gltf-loader.ts +67 -0
- package/templates/controllers/quality/governor.ts +21 -3
- package/templates/controllers/quality/pick-asset.ts +69 -0
- package/templates/controllers/quality/tier.ts +62 -6
- package/templates/skills/genex-ai-model/SKILL.md +20 -5
- package/templates/skills/genex-threejs-adaptive-quality/SKILL.md +49 -8
- package/templates/skills/genex-threejs-adaptive-quality/references/adaptive-quality.md +31 -16
package/dist/index.js
CHANGED
|
@@ -1665,7 +1665,8 @@ function scanBundle(files) {
|
|
|
1665
1665
|
contextLossHandler: text.includes("webglcontextlost") || text.includes("device.lost"),
|
|
1666
1666
|
ktx2Loader: text.includes("KTX2Loader") || text.includes(".ktx2"),
|
|
1667
1667
|
touchHints: /joystick/i.test(text) || text.includes("touchstart") && text.includes("touch-action"),
|
|
1668
|
-
adaptiveQuality: text.includes("__GENEX_QUALITY__")
|
|
1668
|
+
adaptiveQuality: text.includes("__GENEX_QUALITY__"),
|
|
1669
|
+
modelRungs: text.includes("__GENEX_MODEL_RUNGS__")
|
|
1669
1670
|
};
|
|
1670
1671
|
return {
|
|
1671
1672
|
images,
|
|
@@ -1718,8 +1719,14 @@ function estimateVram(scan, externalDims) {
|
|
|
1718
1719
|
if (MOBILE_RUNG_SUFFIX_RE.test(role)) continue;
|
|
1719
1720
|
const dims = externalDims.get(url);
|
|
1720
1721
|
if (dims) {
|
|
1721
|
-
|
|
1722
|
-
|
|
1722
|
+
const ktx2 = dims.ktx2 || dims.ktx2Available === true && scan.markers.ktx2Loader;
|
|
1723
|
+
if (dims.glb) {
|
|
1724
|
+
external += textureBytes(dims.width, dims.height, ktx2) + (dims.bytes ?? 0);
|
|
1725
|
+
maxDim = Math.max(maxDim, dims.width);
|
|
1726
|
+
} else {
|
|
1727
|
+
external += textureBytes(dims.width, dims.height, ktx2);
|
|
1728
|
+
maxDim = Math.max(maxDim, dims.width, dims.height);
|
|
1729
|
+
}
|
|
1723
1730
|
} else {
|
|
1724
1731
|
unresolved.push(url);
|
|
1725
1732
|
external += role === SKYBOX_ROLE ? WORST_SKYBOX_BYTES : WORST_IMAGE_BYTES;
|
|
@@ -1777,6 +1784,8 @@ function printMobilePreflight(files, log) {
|
|
|
1777
1784
|
const role = url.split("/").pop() ?? "";
|
|
1778
1785
|
if (scan.markers.adaptiveQuality && role === SKYBOX_ROLE) {
|
|
1779
1786
|
externalDims.set(url, { width: 2048, height: 1024 });
|
|
1787
|
+
} else if (scan.markers.modelRungs && role.endsWith("-glb")) {
|
|
1788
|
+
externalDims.set(url, { width: 1024, height: 2048, glb: true });
|
|
1780
1789
|
} else {
|
|
1781
1790
|
externalDims.set(url, null);
|
|
1782
1791
|
}
|
|
@@ -1795,6 +1804,10 @@ function printMobilePreflight(files, log) {
|
|
|
1795
1804
|
log.warn(
|
|
1796
1805
|
` Skybox loaded full-size (${url.slice(0, 80)}\u2026): ~178 MB decoded on phones. Load it through pickAsset (genex-threejs-adaptive-quality) so phones get the @2048 rung.`
|
|
1797
1806
|
);
|
|
1807
|
+
} else if (role.endsWith("-glb")) {
|
|
1808
|
+
log.warn(
|
|
1809
|
+
` Model loaded without pickModel (${url.slice(0, 80)}\u2026) \u2014 phones fetch the full provider-raw GLB. Load it through loadModelWithFallback (genex-threejs-adaptive-quality) so phones get the @1024 rung.`
|
|
1810
|
+
);
|
|
1798
1811
|
}
|
|
1799
1812
|
}
|
|
1800
1813
|
if (est.textureMaxDim > 2048) {
|
|
@@ -13118,14 +13131,22 @@ var CONTROLLER_FILE_SETS = {
|
|
|
13118
13131
|
]
|
|
13119
13132
|
},
|
|
13120
13133
|
quality: {
|
|
13121
|
-
code: [
|
|
13122
|
-
|
|
13134
|
+
code: [
|
|
13135
|
+
"quality/tier.ts",
|
|
13136
|
+
"quality/governor.ts",
|
|
13137
|
+
"quality/pick-asset.ts",
|
|
13138
|
+
"quality/gltf-loader.ts",
|
|
13139
|
+
NOTICE
|
|
13140
|
+
],
|
|
13141
|
+
// KTX2Loader's basis transcoder (js+wasm) — loaded by PATH at runtime, so
|
|
13142
|
+
// it must live under public/ (Vite drops non-imported files otherwise).
|
|
13143
|
+
assets: ["assets/basis_transcoder.js", "assets/basis_transcoder.wasm"],
|
|
13123
13144
|
skill: "genex-threejs-adaptive-quality",
|
|
13124
13145
|
sketch: [
|
|
13125
|
-
`const tier = detectTier(); // phone-low | phone | desktop \u2014 manual Quality setting wins`,
|
|
13126
|
-
`renderer.
|
|
13127
|
-
`const
|
|
13128
|
-
|
|
13146
|
+
`const tier = detectTier(); // phone-low | phone | desktop-low | desktop \u2014 manual Quality setting wins`,
|
|
13147
|
+
`const renderer = new THREE.WebGLRenderer({ antialias: rendererAntialias(tier, /* willRunPost */ true) });`,
|
|
13148
|
+
`const gltf = createGltfLoader(renderer); // meshopt + KTX2 decoders; model = await loadModelWithFallback(MODEL_URL, tier, (u) => gltf.loader.loadAsync(u), { ktx2: gltf.ktx2 })`,
|
|
13149
|
+
`const gov = new QualityGovernor(tier, { setDprScale: (m) => renderer.setPixelRatio(Math.min(window.devicePixelRatio, tier.dprCap * m)), setShadowQuality: (l) => sun.shadow.mapSize.setScalar(l === 'full' ? tier.shadowMapSize : tier.shadowMapSize / 2) }, renderer);`
|
|
13129
13150
|
]
|
|
13130
13151
|
},
|
|
13131
13152
|
touch: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genex-ai/cli-demo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.75.0-dev.194",
|
|
4
4
|
"description": "Set up your project's agent workspace (.claude/.codex/.cursor in the game folder), authorize, create a game project, generate AI assets, and publish (genex CLI).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -66,3 +66,13 @@ charge, to any person obtaining a copy of this software and associated
|
|
|
66
66
|
documentation files, to deal in the Software without restriction. THE SOFTWARE
|
|
67
67
|
IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. (Full MIT text ships with the
|
|
68
68
|
`@pixiv/three-vrm` package's LICENSE file.)
|
|
69
|
+
|
|
70
|
+
## Basis Universal transcoder (`assets/basis_transcoder.js`, `assets/basis_transcoder.wasm`)
|
|
71
|
+
|
|
72
|
+
Copyright 2019-2024 Binomial LLC — Licensed under the Apache License, Version
|
|
73
|
+
2.0. Vendored from the three.js distribution (examples/jsm/libs/basis) so the
|
|
74
|
+
quality kit's KTX2Loader can transcode GPU-compressed `.ktx2` textures; served
|
|
75
|
+
from the game's own `public/assets/` per KTX2Loader's `setTranscoderPath`
|
|
76
|
+
contract. You may obtain a copy of the License at
|
|
77
|
+
www.apache.org/licenses/LICENSE-2.0. Distributed on an "AS IS" BASIS, WITHOUT
|
|
78
|
+
WARRANTIES OR CONDITIONS OF ANY KIND.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
var BASIS = (() => {
|
|
3
|
+
var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined;
|
|
4
|
+
if (typeof __filename != 'undefined') _scriptName ||= __filename;
|
|
5
|
+
return (
|
|
6
|
+
function(moduleArg = {}) {
|
|
7
|
+
var moduleRtn;
|
|
8
|
+
|
|
9
|
+
var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){}var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};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("fs");var nodePath=require("path");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);var ret=fs.readFileSync(filename);return ret};readAsync=(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return new Promise((resolve,reject)=>{fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)reject(err);else resolve(binary?data.buffer:data)})})};if(!Module["thisProgram"]&&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){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{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=url=>{if(isFileURI(url)){return new Promise((reject,resolve)=>{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)}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}return fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))})}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);var isFileURI=filename=>filename.startsWith("file://");function findWasmBinary(){var f="basis_transcoder.wasm";if(!isDataURI(f)){return locateFile(f)}return f}var wasmBinaryFile;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"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["L"];updateMemoryViews();wasmTable=wasmExports["P"];addOnInit(wasmExports["M"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}if(!wasmBinaryFile)wasmBinaryFile=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var noExitRuntime=Module["noExitRuntime"]||true;class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}get_exception_ptr(){var isPointer=___cxa_is_pointer_type(this.get_type());if(isPointer){return HEAPU32[this.excPtr>>2]}var adjusted=this.get_adjusted_ptr();if(adjusted!==0)return adjusted;return this.excPtr}}var exceptionLast=0;var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var __abort_js=()=>{abort("")};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 registeredTypes={};var typeDependencies={};var InternalError;var throwInternalError=message=>{throw new InternalError(message)};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(function(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<myTypes.length;++i){registerType(myTypes[i],myTypeConverters[i])}}var typeConverters=new Array(dependentTypes.length);var unregisteredTypes=[];var registered=0;dependentTypes.forEach((dt,i)=>{if(registeredTypes.hasOwnProperty(dt)){typeConverters[i]=registeredTypes[dt]}else{unregisteredTypes.push(dt);if(!awaitingDependencies.hasOwnProperty(dt)){awaitingDependencies[dt]=[]}awaitingDependencies[dt].push(()=>{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={};fieldRecords.forEach((field,i)=>{var fieldName=field.fieldName;var getterReturnType=fieldTypes[i];var getter=field.getter;var getterContext=field.getterContext;var setterArgumentType=fieldTypes[i+fieldRecords.length];var setter=field.setter;var setterContext=field.setterContext;fields[fieldName]={read:ptr=>getterReturnType["fromWireType"](getter(getterContext,ptr)),write:(ptr,o)=>{var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,o));runDestructors(destructors)}}});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)){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},argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction:rawDestructor}]})};var __embind_register_bigint=(primitiveType,name,size,minRange,maxRange)=>{};var embind_init_charCodes=()=>{var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes};var embind_charCodes;var readLatin1String=ptr=>{var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret};var BindingError;var throwBindingError=message=>{throw new BindingError(message)};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={}){if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}return sharedRegisterType(rawType,registeredInstance,options)}var GenericWireTypeSize=8;var __embind_register_bool=(rawType,name,trueValue,falseValue)=>{name=readLatin1String(name);registerType(rawType,{name:name,fromWireType:function(wt){return!!wt},toWireType:function(destructors,o){return o?trueValue:falseValue},argPackAdvance:GenericWireTypeSize,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 finalizationRegistry=false;var detachFinalizer=handle=>{};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 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 registeredPointers={};var getInheritedInstanceCount=()=>Object.keys(registeredInstances).length;var getLiveInheritedInstances=()=>{var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv};var deletionQueue=[];var flushPendingDeletes=()=>{while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}};var delayFunction;var setDelayFunction=fn=>{delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}};var init_embind=()=>{Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction};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 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: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 attachFinalizer=handle=>{if("undefined"===typeof 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 init_ClassHandle=()=>{Object.assign(ClassHandle.prototype,{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}})};function ClassHandle(){}var createNamedFunction=(name,body)=>Object.defineProperty(body,"name",{value:name});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.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;if(undefined!==numArguments){Module[name].numArguments=numArguments}}};var char_0=48;var char_9=57;var makeLegalFunctionName=name=>{if(undefined===name){return"_unknown"}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};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("Unsupporting 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 init_RegisteredPointer=()=>{Object.assign(RegisteredPointer.prototype,{getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr},destructor(ptr){this.rawDestructor?.(ptr)},argPackAdvance:GenericWireTypeSize,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 dynCallLegacy=(sig,ptr,args)=>{sig=sig.replace(/p/g,"i");var f=Module["dynCall_"+sig];return f(ptr,...args)};var wasmTableMirror=[];var wasmTable;var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var dynCall=(sig,ptr,args=[])=>{if(sig.includes("j")){return dynCallLegacy(sig,ptr,args)}var rtn=getWasmTableEntry(ptr)(...args);return rtn};var getDynCaller=(sig,ptr)=>(...args)=>dynCall(sig,ptr,args);var embind__requireFunction=(signature,rawFunction)=>{signature=readLatin1String(signature);function makeDynCaller(){if(signature.includes("j")){return getDynCaller(signature,rawFunction)}return getWasmTableEntry(rawFunction)}var fp=makeDynCaller();if(typeof fp!="function"){throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`)}return fp};var extendError=(baseErrorType,errorName)=>{var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return`${this.name}: ${this.message}`}};return errorClass};var UnboundTypeError;var getTypeName=type=>{var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv};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=readLatin1String(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]})};var heap32VectorToArray=(count,firstElement)=>{var array=[];for(var i=0;i<count;i++){array.push(HEAPU32[firstElement+i*4>>2])}return array};function usesDestructorStack(argTypes){for(var i=1;i<argTypes.length;++i){if(argTypes[i]!==null&&argTypes[i].destructorFunction===undefined){return true}}return false}function newFunc(constructor,argumentList){if(!(constructor instanceof Function)){throw new TypeError(`new_ called with constructor type ${typeof constructor} which is not a function`)}var dummy=createNamedFunction(constructor.name||"unknownFunctionName",function(){});dummy.prototype=constructor.prototype;var obj=new dummy;var r=constructor.apply(obj,argumentList);return r instanceof Object?r:obj}function createJsInvoker(argTypes,isClassMethodFunc,returns,isAsync){var needsDestructorStack=usesDestructorStack(argTypes);var argCount=argTypes.length;var argsList="";var argsListWired="";for(var i=0;i<argCount-2;++i){argsList+=(i!==0?", ":"")+"arg"+i;argsListWired+=(i!==0?", ":"")+"arg"+i+"Wired"}var invokerFnBody=`\n return function (${argsList}) {\n if (arguments.length !== ${argCount-2}) {\n throwBindingError('function ' + humanName + ' called with ' + arguments.length + ' arguments, expected ${argCount-2}');\n }`;if(needsDestructorStack){invokerFnBody+="var destructors = [];\n"}var dtorStack=needsDestructorStack?"destructors":"null";var args1=["humanName","throwBindingError","invoker","fn","runDestructors","retType","classParam"];if(isClassMethodFunc){invokerFnBody+="var thisWired = classParam['toWireType']("+dtorStack+", this);\n"}for(var i=0;i<argCount-2;++i){invokerFnBody+="var arg"+i+"Wired = argType"+i+"['toWireType']("+dtorStack+", arg"+i+");\n";args1.push("argType"+i)}if(isClassMethodFunc){argsListWired="thisWired"+(argsListWired.length>0?", ":"")+argsListWired}invokerFnBody+=(returns||isAsync?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i<argTypes.length;++i){var paramName=i===1?"thisWired":"arg"+(i-2)+"Wired";if(argTypes[i].destructorFunction!==null){invokerFnBody+=`${paramName}_dtor(${paramName});\n`;args1.push(`${paramName}_dtor`)}}}if(returns){invokerFnBody+="var ret = retType['fromWireType'](rv);\n"+"return ret;\n"}else{}invokerFnBody+="}\n";return[args1,invokerFnBody]}function craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc,isAsync){var argCount=argTypes.length;if(argCount<2){throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!")}var isClassMethodFunc=argTypes[1]!==null&&classType!==null;var needsDestructorStack=usesDestructorStack(argTypes);var returns=argTypes[0].name!=="void";var closureArgs=[humanName,throwBindingError,cppInvokerFunc,cppTargetFunc,runDestructors,argTypes[0],argTypes[1]];for(var i=0;i<argCount-2;++i){closureArgs.push(argTypes[i+2])}if(!needsDestructorStack){for(var i=isClassMethodFunc?1:2;i<argTypes.length;++i){if(argTypes[i].destructorFunction!==null){closureArgs.push(argTypes[i].destructorFunction)}}}let[args,invokerFnBody]=createJsInvoker(argTypes,isClassMethodFunc,returns,isAsync);args.push(invokerFnBody);var invokerFn=newFunc(Function,args)(...closureArgs);return createNamedFunction(humanName,invokerFn)}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 getFunctionName=signature=>{signature=signature.trim();const argsIndex=signature.indexOf("(");if(argsIndex!==-1){return signature.substr(0,argsIndex)}else{return signature}};var __embind_register_class_function=(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual,isAsync)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);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 __embind_register_constant=(name,type,value)=>{name=readLatin1String(name);whenDependentTypesAreResolved([],[type],type=>{type=type[0];Module[name]=type["fromWireType"](value);return[]})};var emval_freelist=[];var emval_handles=[];var __emval_decref=handle=>{if(handle>9&&0===--emval_handles[handle+1]){emval_handles[handle]=undefined;emval_freelist.push(handle)}};var count_emval_handles=()=>emval_handles.length/2-5-emval_freelist.length;var init_emval=()=>{emval_handles.push(0,1,undefined,1,null,1,true,1,false,1);Module["count_emval_handles"]=count_emval_handles};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}}}};var EmValType={name:"emscripten::val",fromWireType:handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},toWireType:(destructors,value)=>Emval.toHandle(value),argPackAdvance:GenericWireTypeSize,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}`)}};var __embind_register_enum=(rawType,name,size,isSigned)=>{name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,fromWireType:function(c){return this.constructor.values[c]},toWireType:(destructors,c)=>c.value,argPackAdvance:GenericWireTypeSize,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)};var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`)}return impl};var __embind_register_enum_value=(rawEnumType,name,enumValue)=>{var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);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};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}};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=readLatin1String(name);registerType(rawType,{name:name,fromWireType:value=>value,toWireType:(destructors,value)=>value,argPackAdvance:GenericWireTypeSize,readValueFromPointer:floatReadValueFromPointer(name,size),destructorFunction:null})};var __embind_register_function=(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn,isAsync)=>{var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);name=getFunctionName(name);rawInvoker=embind__requireFunction(signature,rawInvoker);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 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];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_integer=(primitiveType,name,size,minRange,maxRange)=>{name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var fromWireType=value=>value;if(minRange===0){var bitshift=32-8*size;fromWireType=value=>value<<bitshift>>>bitshift}var isUnsignedType=name.includes("unsigned");var checkAssertions=(value,toTypeName)=>{};var toWireType;if(isUnsignedType){toWireType=function(destructors,value){checkAssertions(value,this.name);return value>>>0}}else{toWireType=function(destructors,value){checkAssertions(value,this.name);return value}}registerType(primitiveType,{name:name,fromWireType:fromWireType,toWireType:toWireType,argPackAdvance:GenericWireTypeSize,readValueFromPointer:integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})};var __embind_register_memory_view=(rawType,dataTypeIndex,name)=>{var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];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=readLatin1String(name);registerType(rawType,{name:name,fromWireType:decodeMemoryView,argPackAdvance:GenericWireTypeSize,readValueFromPointer:decodeMemoryView},{ignoreDuplicateRegistrations:true})};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=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}}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<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx,maxBytesToRead)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var __embind_register_std_string=(rawType,name)=>{name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,fromWireType(value){var length=HEAPU32[value>>2];var payload=value+4;var str;if(stdStringIsUTF8){var decodeStartPtr=payload;for(var i=0;i<=length;++i){var currentBytePtr=payload+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAPU8[payload+i])}str=a.join("")}_free(value);return str},toWireType(destructors,value){if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var length;var valueIsOfTypeString=typeof value=="string";if(!(valueIsOfTypeString||value instanceof Uint8Array||value instanceof Uint8ClampedArray||value instanceof Int8Array)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){length=lengthBytesUTF8(value)}else{length=value.length}var base=_malloc(4+length+1);var ptr=base+4;HEAPU32[base>>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr,length+1)}else{if(valueIsOfTypeString){for(var i=0;i<length;++i){var charCode=value.charCodeAt(i);if(charCode>255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+i]=charCode}}else{for(var i=0;i<length;++i){HEAPU8[ptr+i]=value[i]}}}if(destructors!==null){destructors.push(_free,base)}return base},argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var UTF16Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead)=>{var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder)return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr));var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str};var stringToUTF16=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite<str.length*2?maxBytesToWrite/2:str.length;for(var i=0;i<numCharsToWrite;++i){var codeUnit=str.charCodeAt(i);HEAP16[outPtr>>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead)=>{var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(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;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343)++i;len+=4}return len};var __embind_register_std_wstring=(rawType,charSize,name)=>{name=readLatin1String(name);var decodeString,encodeString,readCharAt,lengthBytesUTF;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16;readCharAt=pointer=>HEAPU16[pointer>>1]}else if(charSize===4){decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32;readCharAt=pointer=>HEAPU32[pointer>>2]}registerType(rawType,{name:name,fromWireType:value=>{var length=HEAPU32[value>>2];var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||readCharAt(currentBytePtr)==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_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},argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var __embind_register_value_object=(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor)=>{structRegistrations[rawType]={name:readLatin1String(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:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})};var __embind_register_void=(rawType,name)=>{name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,argPackAdvance:0,fromWireType:()=>undefined,toWireType:(destructors,o)=>undefined})};var __emscripten_memcpy_js=(dest,src,num)=>HEAPU8.copyWithin(dest,src,src+num);var emval_returnValue=(returnType,destructorsRef,handle)=>{var destructors=[];var result=returnType["toWireType"](destructors,handle);if(destructors.length){HEAPU32[destructorsRef>>2]=Emval.toHandle(destructors)}return result};var __emval_as=(handle,returnType,destructorsRef)=>{handle=Emval.toValue(handle);returnType=requireRegisteredType(returnType,"emval::as");return emval_returnValue(returnType,destructorsRef,handle)};var emval_methodCallers=[];var __emval_call=(caller,handle,destructorsRef,args)=>{caller=emval_methodCallers[caller];handle=Emval.toValue(handle);return caller(null,handle,destructorsRef,args)};var emval_symbols={};var getStringOrSymbol=address=>{var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}return symbol};var __emval_call_method=(caller,objHandle,methodName,destructorsRef,args)=>{caller=emval_methodCallers[caller];objHandle=Emval.toValue(objHandle);methodName=getStringOrSymbol(methodName);return caller(objHandle,objHandle[methodName],destructorsRef,args)};var emval_get_global=()=>{if(typeof globalThis=="object"){return globalThis}return function(){return Function}()("return this")()};var __emval_get_global=name=>{if(name===0){return Emval.toHandle(emval_get_global())}else{name=getStringOrSymbol(name);return Emval.toHandle(emval_get_global()[name])}};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<argCount;++i){a[i]=requireRegisteredType(HEAPU32[argTypes+i*4>>2],"parameter "+i)}return a};var reflectConstruct=Reflect.construct;var __emval_get_method_caller=(argCount,argTypes,kind)=>{var types=emval_lookupTypes(argCount,argTypes);var retType=types.shift();argCount--;var functionBody=`return function (obj, func, destructorsRef, args) {\n`;var offset=0;var argsList=[];if(kind===0){argsList.push("obj")}var params=["retType"];var args=[retType];for(var i=0;i<argCount;++i){argsList.push("arg"+i);params.push("argType"+i);args.push(types[i]);functionBody+=` var arg${i} = argType${i}.readValueFromPointer(args${offset?"+"+offset:""});\n`;offset+=types[i]["argPackAdvance"]}var invoker=kind===1?"new func":"func.call";functionBody+=` var rv = ${invoker}(${argsList.join(", ")});\n`;if(!retType.isVoid){params.push("emval_returnValue");args.push(emval_returnValue);functionBody+=" return emval_returnValue(retType, destructorsRef, rv);\n"}functionBody+="};\n";params.push(functionBody);var invokerFunction=newFunc(Function,params)(...args);var functionName=`methodCaller<(${types.map(t=>t.name).join(", ")}) => ${retType.name}>`;return emval_addMethodCaller(createNamedFunction(functionName,invokerFunction))};var __emval_get_module_property=name=>{name=getStringOrSymbol(name);return Emval.toHandle(Module[name])};var __emval_get_property=(handle,key)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);return Emval.toHandle(handle[key])};var __emval_incref=handle=>{if(handle>9){emval_handles[handle+1]+=1}};var __emval_new_cstring=v=>Emval.toHandle(getStringOrSymbol(v));var __emval_run_destructors=handle=>{var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)};var getHeapMax=()=>2147483648;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536;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}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;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,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var _fd_close=fd=>52;var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;function _fd_seek(fd,offset_low,offset_high,whence,newOffset){var offset=convertI32PairToI53Checked(offset_low,offset_high);return 70}var printCharBuffers=[null,[],[]];var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j<len;j++){printChar(fd,HEAPU8[ptr+j])}num+=len}HEAPU32[pnum>>2]=num;return 0};InternalError=Module["InternalError"]=class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}};embind_init_charCodes();BindingError=Module["BindingError"]=class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}};init_ClassHandle();init_embind();init_RegisteredPointer();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();var wasmImports={K:___cxa_throw,G:__abort_js,s:__embind_finalize_value_object,C:__embind_register_bigint,I:__embind_register_bool,w:__embind_register_class,v:__embind_register_class_constructor,d:__embind_register_class_function,m:__embind_register_constant,H:__embind_register_emval,o:__embind_register_enum,a:__embind_register_enum_value,A:__embind_register_float,i:__embind_register_function,l:__embind_register_integer,f:__embind_register_memory_view,z:__embind_register_std_string,u:__embind_register_std_wstring,t:__embind_register_value_object,c:__embind_register_value_object_field,J:__embind_register_void,F:__emscripten_memcpy_js,n:__emval_as,q:__emval_call,p:__emval_call_method,b:__emval_decref,x:__emval_get_global,j:__emval_get_method_caller,r:__emval_get_module_property,g:__emval_get_property,k:__emval_incref,h:__emval_new_cstring,e:__emval_run_destructors,D:_emscripten_resize_heap,E:_fd_close,B:_fd_seek,y:_fd_write};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["M"])();var ___getTypeName=a0=>(___getTypeName=wasmExports["N"])(a0);var _malloc=a0=>(_malloc=wasmExports["O"])(a0);var _free=a0=>(_free=wasmExports["Q"])(a0);var ___cxa_is_pointer_type=a0=>(___cxa_is_pointer_type=wasmExports["R"])(a0);var dynCall_jiji=Module["dynCall_jiji"]=(a0,a1,a2,a3,a4)=>(dynCall_jiji=Module["dynCall_jiji"]=wasmExports["S"])(a0,a1,a2,a3,a4);var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
return moduleRtn;
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
})();
|
|
16
|
+
if (typeof exports === 'object' && typeof module === 'object')
|
|
17
|
+
module.exports = BASIS;
|
|
18
|
+
else if (typeof define === 'function' && define['amd'])
|
|
19
|
+
define([], () => BASIS);
|
|
Binary file
|
|
@@ -161,12 +161,38 @@ export function resolveMeshyLocomotion(
|
|
|
161
161
|
return { clips, slots, missingBindings };
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
export
|
|
164
|
+
export interface LoadMeshyCharacterOptions {
|
|
165
|
+
/** A decoder-wired loader (quality kit's createGltfLoader) — required for
|
|
166
|
+
* meshopt/KTX2 model rungs; defaults to a plain GLTFLoader. */
|
|
167
|
+
loader?: GLTFLoader;
|
|
168
|
+
/** Candidate URLs to try for the BASE model, best first — the quality kit's
|
|
169
|
+
* rung ladder: (url) => [pickModel(url, tier, {ktx2}), pickModel(url, tier),
|
|
170
|
+
* url]. Defaults to just the manifest URL. Clips stay per-clip plain loads
|
|
171
|
+
* (animation GLBs have no rungs — sampler data, tiny). Dependency-injected
|
|
172
|
+
* so character/ installs without the quality kit. */
|
|
173
|
+
modelUrlCandidates?: (url: string) => string[];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export async function loadMeshyCharacter(
|
|
177
|
+
manifestUrl: string,
|
|
178
|
+
opts?: LoadMeshyCharacterOptions,
|
|
179
|
+
): Promise<MeshyCharacter> {
|
|
165
180
|
const response = await fetch(manifestUrl, { cache: "no-store" });
|
|
166
181
|
if (!response.ok) throw new Error(`[meshy-character] ${manifestUrl} returned HTTP ${response.status}`);
|
|
167
182
|
const manifest = validateManifest(await response.json());
|
|
168
|
-
const loader = new GLTFLoader();
|
|
169
|
-
const
|
|
183
|
+
const loader = opts?.loader ?? new GLTFLoader();
|
|
184
|
+
const candidates = [...new Set(opts?.modelUrlCandidates?.(manifest.model.url) ?? [manifest.model.url])];
|
|
185
|
+
let base: Awaited<ReturnType<GLTFLoader["loadAsync"]>> | null = null;
|
|
186
|
+
for (const [i, candidate] of candidates.entries()) {
|
|
187
|
+
try {
|
|
188
|
+
base = await loader.loadAsync(candidate);
|
|
189
|
+
break;
|
|
190
|
+
} catch (err) {
|
|
191
|
+
if (i === candidates.length - 1) throw err;
|
|
192
|
+
console.warn(`[meshy-character] ${candidate} failed — trying the next candidate.`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (!base) throw new Error(`[meshy-character] no loadable model candidate for ${manifest.model.url}`);
|
|
170
196
|
const clips: THREE.AnimationClip[] = [...base.animations];
|
|
171
197
|
const loaded = await Promise.all(manifest.clips.map(async (entry) => {
|
|
172
198
|
if (entry.skeletonSignature !== manifest.model.skeletonSignature) {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Genex adaptive-quality: decoder-wired GLTFLoader (mesh-compression lane).
|
|
2
|
+
// Generated models ship mobile rungs — `<role>@1024` (embedded textures ≤1024,
|
|
3
|
+
// meshopt-compressed, simplified where safe) and the `.ktx2` capability
|
|
4
|
+
// sibling (textures stay compressed ON the GPU, ~6x less VRAM). A plain
|
|
5
|
+
// GLTFLoader cannot decode either; this factory wires the decoders once and
|
|
6
|
+
// marks the game as rung-capable for the platform's publish-time scanner.
|
|
7
|
+
//
|
|
8
|
+
// The meshopt decoder is a pure ES module inside three — it bundles, nothing
|
|
9
|
+
// to host. KTX2 needs the basis transcoder files that `npx genex controller
|
|
10
|
+
// quality` copies into public/assets/ (basis_transcoder.js + .wasm) — pass the
|
|
11
|
+
// renderer so KTX2Loader can probe GPU support; skip it and models simply use
|
|
12
|
+
// the universal @1024 rung.
|
|
13
|
+
import type * as THREE from "three";
|
|
14
|
+
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
15
|
+
import { KTX2Loader } from "three/addons/loaders/KTX2Loader.js";
|
|
16
|
+
import { MeshoptDecoder } from "three/addons/libs/meshopt_decoder.module.js";
|
|
17
|
+
|
|
18
|
+
export interface GenexGltfLoader {
|
|
19
|
+
loader: GLTFLoader;
|
|
20
|
+
/** True when KTX2 transcoding is wired AND the GPU supports a target format —
|
|
21
|
+
* pass to pickModel/loadModelWithFallback so phones request `.ktx2` rungs. */
|
|
22
|
+
ktx2: boolean;
|
|
23
|
+
/** Dispose the KTX2 worker pool (call on teardown if you created many). */
|
|
24
|
+
dispose: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Build the game's shared GLTFLoader. Call ONCE at boot, after the renderer:
|
|
29
|
+
*
|
|
30
|
+
* const gltf = createGltfLoader(renderer);
|
|
31
|
+
* const model = await loadModelWithFallback(MODEL_URL, tier, (u) => gltf.loader.loadAsync(u), { ktx2: gltf.ktx2 });
|
|
32
|
+
*
|
|
33
|
+
* Without a renderer (headless/tools), meshopt still decodes and `ktx2` is
|
|
34
|
+
* false — every load falls back to browser-decodable variants.
|
|
35
|
+
*/
|
|
36
|
+
export function createGltfLoader(
|
|
37
|
+
renderer?: THREE.WebGLRenderer,
|
|
38
|
+
opts?: { transcoderPath?: string },
|
|
39
|
+
): GenexGltfLoader {
|
|
40
|
+
const loader = new GLTFLoader();
|
|
41
|
+
loader.setMeshoptDecoder(MeshoptDecoder);
|
|
42
|
+
|
|
43
|
+
// The scanner's marker: this literal survives minification and tells the
|
|
44
|
+
// publish-time scan the game actually loads models through the rung ladder
|
|
45
|
+
// (models have no edge rewrite — scoring is honest only when wired).
|
|
46
|
+
(window as Window & { __GENEX_MODEL_RUNGS__?: boolean }).__GENEX_MODEL_RUNGS__ = true;
|
|
47
|
+
|
|
48
|
+
let ktx2 = false;
|
|
49
|
+
let ktx2Loader: KTX2Loader | null = null;
|
|
50
|
+
if (renderer) {
|
|
51
|
+
try {
|
|
52
|
+
ktx2Loader = new KTX2Loader()
|
|
53
|
+
.setTranscoderPath(opts?.transcoderPath ?? "./assets/")
|
|
54
|
+
.detectSupport(renderer);
|
|
55
|
+
loader.setKTX2Loader(ktx2Loader);
|
|
56
|
+
ktx2 = true;
|
|
57
|
+
} catch {
|
|
58
|
+
ktx2Loader = null; // transcoder missing — universal rungs still work
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
loader,
|
|
64
|
+
ktx2,
|
|
65
|
+
dispose: () => ktx2Loader?.dispose(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
// the runtime-changeable knobs only (DPR, post passes, draw distance, frame
|
|
6
6
|
// cap — NEVER context-creation flags like antialias, those are fixed).
|
|
7
7
|
//
|
|
8
|
-
// Step-down ladder on sustained slowness: DPR ×0.8 → post off →
|
|
9
|
-
// down → 30fps cap. Step-up is slow
|
|
10
|
-
// is never re-attempted. It also self-reports renderer.info counts to
|
|
8
|
+
// Step-down ladder on sustained slowness: DPR ×0.8 → post off → shadows
|
|
9
|
+
// reduced → DPR ×0.65 → draw distance down → 30fps cap. Step-up is slow
|
|
10
|
+
// (hysteresis) and a step whose recovery failed twice is never re-attempted. It also self-reports renderer.info counts to
|
|
11
11
|
// window.__GENEX_QUALITY__ — the crash watchdog attaches them to its beacons,
|
|
12
12
|
// which is how the field data that calibrates the whole program gets its
|
|
13
13
|
// memory signal.
|
|
@@ -18,6 +18,12 @@ export interface GovernorCallbacks {
|
|
|
18
18
|
setDprScale?: (multiplier: number) => void;
|
|
19
19
|
/** Toggle the post stack between the tier's level and 'off'. */
|
|
20
20
|
setPostEnabled?: (enabled: boolean) => void;
|
|
21
|
+
/** Reduce shadow quality — 'reduced' = halve the map (realloc) and/or
|
|
22
|
+
* freeze autoUpdate; 'off' = shadows disabled; 'full' = the tier's budget.
|
|
23
|
+
* Shadows are one of the two big fixed costs the governor previously
|
|
24
|
+
* couldn't touch (the other, context MSAA, is unfixable at runtime —
|
|
25
|
+
* see tier.rendererAntialias). */
|
|
26
|
+
setShadowQuality?: (level: 'full' | 'reduced' | 'off') => void;
|
|
21
27
|
/** Apply a draw-distance multiplier (1 = tier scale). */
|
|
22
28
|
setDrawDistanceScale?: (multiplier: number) => void;
|
|
23
29
|
/** Apply a frame cap (0 = uncapped). Pace to a STABLE 30 over a stuttery 45. */
|
|
@@ -56,6 +62,18 @@ export class QualityGovernor {
|
|
|
56
62
|
revert: () => c.setPostEnabled?.(true),
|
|
57
63
|
failures: 0,
|
|
58
64
|
},
|
|
65
|
+
{
|
|
66
|
+
apply: () => c.setShadowQuality?.('reduced'),
|
|
67
|
+
revert: () => c.setShadowQuality?.('full'),
|
|
68
|
+
failures: 0,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
// Second resolution step — one ×0.8 was often not enough on weak
|
|
72
|
+
// desktops; 0.65 of the tier cap is still readable everywhere.
|
|
73
|
+
apply: () => c.setDprScale?.(0.65),
|
|
74
|
+
revert: () => c.setDprScale?.(0.8),
|
|
75
|
+
failures: 0,
|
|
76
|
+
},
|
|
59
77
|
{
|
|
60
78
|
apply: () => c.setDrawDistanceScale?.(0.6),
|
|
61
79
|
revert: () => c.setDrawDistanceScale?.(1),
|
|
@@ -40,18 +40,87 @@ export function pickAsset(url: string, tier: QualityTier): string {
|
|
|
40
40
|
* const texture = await loadTextureWithFallback(
|
|
41
41
|
* SKYBOX_URL, tier, (u) => new THREE.TextureLoader().loadAsync(u),
|
|
42
42
|
* );
|
|
43
|
+
*
|
|
44
|
+
* KTX2-capable games (createGltfLoader with a renderer) can pass `ktx2Load` —
|
|
45
|
+
* on phone tiers the `.ktx2` capability sibling is tried FIRST (textures stay
|
|
46
|
+
* compressed on the GPU, ~6x less VRAM), then the browser-decodable rung,
|
|
47
|
+
* then the original. Every fallback warns: silent degradation hides real
|
|
48
|
+
* pipeline gaps.
|
|
43
49
|
*/
|
|
44
50
|
export async function loadTextureWithFallback<T>(
|
|
45
51
|
url: string,
|
|
46
52
|
tier: QualityTier,
|
|
47
53
|
load: (resolvedUrl: string) => Promise<T>,
|
|
54
|
+
opts?: { ktx2Load?: (resolvedUrl: string) => Promise<T> },
|
|
48
55
|
): Promise<T> {
|
|
49
56
|
const picked = pickAsset(url, tier);
|
|
57
|
+
if (picked !== url && opts?.ktx2Load) {
|
|
58
|
+
try {
|
|
59
|
+
return await opts.ktx2Load(`${picked}.ktx2`);
|
|
60
|
+
} catch {
|
|
61
|
+
console.warn(`[genex-quality] ktx2 variant missing for ${picked} — using the browser-decodable rung`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
50
64
|
if (picked === url) return load(url);
|
|
51
65
|
try {
|
|
52
66
|
return await load(picked);
|
|
53
67
|
} catch {
|
|
54
68
|
// Missing rung (old asset, un-backfilled env) — degrade to the original.
|
|
69
|
+
console.warn(`[genex-quality] rung missing for ${url} — loading the original`);
|
|
70
|
+
return load(url);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Generated MODELS (mesh-compression lane). GLB rungs use the same numeric
|
|
76
|
+
// suffix — `model-glb@1024` = embedded textures ≤1024 + meshopt (+simplify) —
|
|
77
|
+
// but unlike images there is NO edge rewrite for old games: selection happens
|
|
78
|
+
// ONLY here, in games that wired the decoders (createGltfLoader). Desktop
|
|
79
|
+
// always loads the original.
|
|
80
|
+
|
|
81
|
+
const MODEL_TEXTURE_BUDGET = 1024;
|
|
82
|
+
const MODEL_ROLE_RE = /^(model-glb|character-rigged(-a\d+)?-glb(-r\d+)?)$/;
|
|
83
|
+
|
|
84
|
+
/** Resolve the model URL a THIS-tier device should load. `ktx2: true` (from
|
|
85
|
+
* createGltfLoader) upgrades phones to the GPU-compressed sibling. */
|
|
86
|
+
export function pickModel(url: string, tier: QualityTier, opts?: { ktx2?: boolean }): string {
|
|
87
|
+
if (!GENEX_GENERATIONS_RE.test(url)) return url;
|
|
88
|
+
const role = url.split("/").pop() ?? "";
|
|
89
|
+
if (!MODEL_ROLE_RE.test(role)) return url;
|
|
90
|
+
if (tier.name !== "phone" && tier.name !== "phone-low") return url;
|
|
91
|
+
const rung = `${url}@${MODEL_TEXTURE_BUDGET}`;
|
|
92
|
+
return opts?.ktx2 ? `${rung}.ktx2` : rung;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Load a generated model through the rung ladder with the full fallback chain
|
|
97
|
+
* (`.ktx2` → `@1024` → original). Use with the decoder-wired loader:
|
|
98
|
+
*
|
|
99
|
+
* const gltf = createGltfLoader(renderer);
|
|
100
|
+
* const model = await loadModelWithFallback(MODEL_URL, tier, (u) => gltf.loader.loadAsync(u), { ktx2: gltf.ktx2 });
|
|
101
|
+
*
|
|
102
|
+
* The worst case is today's behavior (the full original) — never a broken boot.
|
|
103
|
+
*/
|
|
104
|
+
export async function loadModelWithFallback<T>(
|
|
105
|
+
url: string,
|
|
106
|
+
tier: QualityTier,
|
|
107
|
+
load: (resolvedUrl: string) => Promise<T>,
|
|
108
|
+
opts?: { ktx2?: boolean },
|
|
109
|
+
): Promise<T> {
|
|
110
|
+
const withKtx2 = pickModel(url, tier, opts);
|
|
111
|
+
const universal = pickModel(url, tier, { ktx2: false });
|
|
112
|
+
if (withKtx2 !== universal) {
|
|
113
|
+
try {
|
|
114
|
+
return await load(withKtx2);
|
|
115
|
+
} catch {
|
|
116
|
+
console.warn(`[genex-quality] ktx2 model rung missing for ${url} — trying the universal rung`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (universal === url) return load(url);
|
|
120
|
+
try {
|
|
121
|
+
return await load(universal);
|
|
122
|
+
} catch {
|
|
123
|
+
console.warn(`[genex-quality] model rung missing for ${url} — loading the original`);
|
|
55
124
|
return load(url);
|
|
56
125
|
}
|
|
57
126
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// small lookup; the runtime governor measures actual frame times and corrects
|
|
11
11
|
// both directions. All heuristics here only pick the STARTING tier.
|
|
12
12
|
|
|
13
|
-
export type TierName = 'phone-low' | 'phone' | 'desktop' | 'desktop-high';
|
|
13
|
+
export type TierName = 'phone-low' | 'phone' | 'desktop-low' | 'desktop' | 'desktop-high';
|
|
14
14
|
|
|
15
15
|
export interface QualityTier {
|
|
16
16
|
name: TierName;
|
|
@@ -30,6 +30,12 @@ export interface QualityTier {
|
|
|
30
30
|
frameCap: number;
|
|
31
31
|
/** Max remote players fully animated/drawn (multiplayer); rest billboard. */
|
|
32
32
|
remoteAvatarCap: number;
|
|
33
|
+
/** MSAA samples for the EffectComposer's render target. Context `antialias`
|
|
34
|
+
* is ~wasted under a composer (it MSAAs a buffer the composer never reads)
|
|
35
|
+
* — AA under post comes from `new EffectComposer(renderer, new
|
|
36
|
+
* THREE.WebGLRenderTarget(w, h, { samples: tier.composerSamples }))`.
|
|
37
|
+
* See rendererAntialias() for the context flag itself. */
|
|
38
|
+
composerSamples: number;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
export const TIERS: Record<TierName, QualityTier> = {
|
|
@@ -43,6 +49,7 @@ export const TIERS: Record<TierName, QualityTier> = {
|
|
|
43
49
|
drawDistanceScale: 0.5,
|
|
44
50
|
frameCap: 30,
|
|
45
51
|
remoteAvatarCap: 4,
|
|
52
|
+
composerSamples: 0,
|
|
46
53
|
},
|
|
47
54
|
phone: {
|
|
48
55
|
name: 'phone',
|
|
@@ -54,6 +61,23 @@ export const TIERS: Record<TierName, QualityTier> = {
|
|
|
54
61
|
drawDistanceScale: 0.75,
|
|
55
62
|
frameCap: 60,
|
|
56
63
|
remoteAvatarCap: 8,
|
|
64
|
+
composerSamples: 0,
|
|
65
|
+
},
|
|
66
|
+
// Weak desktops (Intel iGPU MacBooks, old integrated AMD): the full desktop
|
|
67
|
+
// path — DPR 2 + 4x MSAA + 2048 PCFSoft shadows + full bloom — is a
|
|
68
|
+
// slideshow there, and the governor can't rescue context-locked MSAA. The
|
|
69
|
+
// Quality picker still overrides (a player can force High).
|
|
70
|
+
'desktop-low': {
|
|
71
|
+
name: 'desktop-low',
|
|
72
|
+
dprCap: 1.5,
|
|
73
|
+
antialias: false,
|
|
74
|
+
shadowMapSize: 1024,
|
|
75
|
+
postLevel: 'light',
|
|
76
|
+
particleScale: 0.75,
|
|
77
|
+
drawDistanceScale: 1,
|
|
78
|
+
frameCap: 60,
|
|
79
|
+
remoteAvatarCap: 64,
|
|
80
|
+
composerSamples: 0,
|
|
57
81
|
},
|
|
58
82
|
desktop: {
|
|
59
83
|
name: 'desktop',
|
|
@@ -65,6 +89,7 @@ export const TIERS: Record<TierName, QualityTier> = {
|
|
|
65
89
|
drawDistanceScale: 1,
|
|
66
90
|
frameCap: 60,
|
|
67
91
|
remoteAvatarCap: 64,
|
|
92
|
+
composerSamples: 4,
|
|
68
93
|
},
|
|
69
94
|
'desktop-high': {
|
|
70
95
|
name: 'desktop-high',
|
|
@@ -76,9 +101,22 @@ export const TIERS: Record<TierName, QualityTier> = {
|
|
|
76
101
|
drawDistanceScale: 1,
|
|
77
102
|
frameCap: 240,
|
|
78
103
|
remoteAvatarCap: 64,
|
|
104
|
+
composerSamples: 4,
|
|
79
105
|
},
|
|
80
106
|
};
|
|
81
107
|
|
|
108
|
+
/**
|
|
109
|
+
* The context `antialias` flag a game should ACTUALLY construct with. Context
|
|
110
|
+
* MSAA only benefits games that render straight to the canvas — under an
|
|
111
|
+
* EffectComposer it multisamples a buffer the composer never reads (pure
|
|
112
|
+
* memory/fill waste, the classic weak-MacBook lag recipe). Post games get
|
|
113
|
+
* their AA from `tier.composerSamples` on the composer's render target
|
|
114
|
+
* instead; a post-waived desktop game keeps real MSAA.
|
|
115
|
+
*/
|
|
116
|
+
export function rendererAntialias(tier: QualityTier, willRunPost: boolean): boolean {
|
|
117
|
+
return tier.antialias && !willRunPost;
|
|
118
|
+
}
|
|
119
|
+
|
|
82
120
|
/** Quality setting persisted PER DEVICE (localStorage) — quality is a property
|
|
83
121
|
* of the phone, not the player, so it deliberately does not ride account
|
|
84
122
|
* state. 'auto' = heuristics + governor. */
|
|
@@ -122,21 +160,34 @@ function isTouchDevice(): boolean {
|
|
|
122
160
|
* meaningful on Android/desktop anyway, and the governor corrects mistakes. */
|
|
123
161
|
const STRONG_ANDROID_GPU = /Adreno \(TM\) [67]\d\d|Adreno \(TM\) 8|Mali-G7[18]|Mali-G[89]\d|Immortalis|Xclipse/i;
|
|
124
162
|
|
|
125
|
-
|
|
163
|
+
/** Weak DESKTOP GPUs (unmasked on desktop, unlike iOS): Intel HD/UHD/pre-Xe
|
|
164
|
+
* Iris iGPUs and old integrated AMD — the machines the full desktop path
|
|
165
|
+
* (DPR 2 + MSAA + 2048 shadows + full bloom) turns into a slideshow.
|
|
166
|
+
* Trademark tokens vary by driver era — "Iris(R) Xe" / "Iris Xe" and
|
|
167
|
+
* "Radeon(TM)" / "Radeon (TM)" all occur in real renderer strings — so the
|
|
168
|
+
* Xe exclusion and the AMD prefix both tolerate them. */
|
|
169
|
+
const WEAK_DESKTOP_GPU = /Intel(\(R\))? (HD|UHD|Iris(?! ?(\((R|TM)\) ?)?Xe))|GMA |Radeon ?(\(TM\))? (R[2-5]|Vega [23]) Graphics|SwiftShader|llvmpipe|Software/i;
|
|
170
|
+
|
|
171
|
+
/** One boot-time probe context, freed immediately — never at play time. */
|
|
172
|
+
function probeGpuRenderer(): string {
|
|
126
173
|
try {
|
|
127
174
|
const canvas = document.createElement('canvas');
|
|
128
175
|
const gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
|
|
129
|
-
if (!gl) return
|
|
176
|
+
if (!gl) return '';
|
|
130
177
|
const info = gl.getExtension('WEBGL_debug_renderer_info');
|
|
131
178
|
const renderer = info ? String(gl.getParameter(info.UNMASKED_RENDERER_WEBGL)) : '';
|
|
132
179
|
const ext = gl.getExtension('WEBGL_lose_context');
|
|
133
180
|
if (ext) ext.loseContext(); // free the probe context immediately
|
|
134
|
-
return
|
|
181
|
+
return renderer;
|
|
135
182
|
} catch {
|
|
136
|
-
return
|
|
183
|
+
return '';
|
|
137
184
|
}
|
|
138
185
|
}
|
|
139
186
|
|
|
187
|
+
function androidGpuLooksStrong(): boolean {
|
|
188
|
+
return STRONG_ANDROID_GPU.test(probeGpuRenderer());
|
|
189
|
+
}
|
|
190
|
+
|
|
140
191
|
/**
|
|
141
192
|
* Pick the STARTING tier. Manual setting wins; 'auto' uses heuristics:
|
|
142
193
|
* - non-touch → desktop
|
|
@@ -149,7 +200,12 @@ export function detectTier(): QualityTier {
|
|
|
149
200
|
if (setting === 'medium') return TIERS.phone;
|
|
150
201
|
if (setting === 'high') return TIERS.desktop;
|
|
151
202
|
|
|
152
|
-
if (!isTouchDevice())
|
|
203
|
+
if (!isTouchDevice()) {
|
|
204
|
+
// Weak-desktop demotion: a 2015 Intel Air and an M3 Max are not the same
|
|
205
|
+
// machine. Desktop renderer strings are unmasked (unlike iOS), so one
|
|
206
|
+
// boot probe separates them; the Quality picker still overrides either way.
|
|
207
|
+
return WEAK_DESKTOP_GPU.test(probeGpuRenderer()) ? TIERS['desktop-low'] : TIERS.desktop;
|
|
208
|
+
}
|
|
153
209
|
|
|
154
210
|
const ua = navigator.userAgent;
|
|
155
211
|
const kit = (window as Window & { __GENEX_KIT__?: { realDpr?: number } }).__GENEX_KIT__;
|
|
@@ -65,22 +65,37 @@ side — reads as broken at a glance.
|
|
|
65
65
|
|
|
66
66
|
## Load it into the scene
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
Load through the quality kit's decoder-wired loader and the model rung ladder
|
|
69
|
+
(`$genex-threejs-adaptive-quality`; `npx genex controller quality` installs
|
|
70
|
+
it). The bare URL is the provider-raw original — multiple 1–4K PBR textures +
|
|
71
|
+
dense geometry; phones load the `@1024` rung instead (textures budgeted to
|
|
72
|
+
1024, meshopt-compressed, simplified) and KTX2-capable games get the
|
|
73
|
+
GPU-compressed sibling. Desktop always loads the original. R2 sends the right
|
|
74
|
+
CORS headers, so cross-origin loading just works:
|
|
70
75
|
|
|
71
76
|
```ts
|
|
72
|
-
import {
|
|
77
|
+
import { createGltfLoader } from "./controllers/quality/gltf-loader.ts";
|
|
78
|
+
import { loadModelWithFallback } from "./controllers/quality/pick-asset.ts";
|
|
79
|
+
import { detectTier } from "./controllers/quality/tier.ts";
|
|
73
80
|
|
|
74
|
-
const
|
|
81
|
+
const tier = detectTier(); // reuse the boot tier if you already have it
|
|
82
|
+
const gltfLoader = createGltfLoader(renderer); // once at boot — meshopt + KTX2
|
|
75
83
|
// the URL `npx genex model` printed:
|
|
76
84
|
const MODEL_URL = "https://assets.genex.technology/generations/<id>/model-glb";
|
|
77
|
-
const gltf = await
|
|
85
|
+
const gltf = await loadModelWithFallback(
|
|
86
|
+
MODEL_URL, tier, (u) => gltfLoader.loader.loadAsync(u), { ktx2: gltfLoader.ktx2 },
|
|
87
|
+
);
|
|
78
88
|
const model = gltf.scene;
|
|
79
89
|
model.scale.setScalar(1); // tune to taste
|
|
80
90
|
model.position.set(0, 0, 0);
|
|
81
91
|
scene.add(model);
|
|
82
92
|
```
|
|
83
93
|
|
|
94
|
+
A missing rung falls back to the original automatically (each fallback warns —
|
|
95
|
+
never silent). Without the quality kit a plain
|
|
96
|
+
`new GLTFLoader().loadAsync(MODEL_URL)` still works, but every device pays for
|
|
97
|
+
the full original — phones included.
|
|
98
|
+
|
|
84
99
|
Never swallow a failed load silently: if you wrap a generated-asset load in a
|
|
85
100
|
`catch`, `console.warn` the asset name in it — your own self-check reads the
|
|
86
101
|
console, and a bare `.catch(() => null)` hides a missing model from you too.
|
|
@@ -23,20 +23,32 @@ you still verify on desktop only.
|
|
|
23
23
|
npx genex controller quality
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Installs `src/controllers/quality/{tier.ts, governor.ts, pick-asset.ts
|
|
27
|
-
game-owned code, edit freely
|
|
26
|
+
Installs `src/controllers/quality/{tier.ts, governor.ts, pick-asset.ts,
|
|
27
|
+
gltf-loader.ts}` — game-owned code, edit freely — plus the KTX2 basis
|
|
28
|
+
transcoder (`basis_transcoder.js` + `.wasm`) into `public/assets/` (loaded by
|
|
29
|
+
path at runtime; Vite would drop it anywhere else).
|
|
28
30
|
|
|
29
31
|
## Wire the tier at boot (before renderer construction)
|
|
30
32
|
|
|
31
33
|
```ts
|
|
32
|
-
import { detectTier } from "./controllers/quality/tier.ts";
|
|
34
|
+
import { detectTier, rendererAntialias } from "./controllers/quality/tier.ts";
|
|
33
35
|
import { QualityGovernor } from "./controllers/quality/governor.ts";
|
|
34
36
|
|
|
35
|
-
const tier = detectTier(); // phone-low | phone | desktop; manual Quality setting wins
|
|
36
|
-
|
|
37
|
+
const tier = detectTier(); // phone-low | phone | desktop-low | desktop; manual Quality setting wins
|
|
38
|
+
// Context MSAA is WASTED under an EffectComposer (it multisamples a buffer the
|
|
39
|
+
// composer never reads — the classic weak-MacBook lag recipe). Games with a
|
|
40
|
+
// post stack pass willRunPost=true and get their AA from the composer target:
|
|
41
|
+
const renderer = new THREE.WebGLRenderer({ antialias: rendererAntialias(tier, true) });
|
|
37
42
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, tier.dprCap));
|
|
43
|
+
// With post: AA comes from the composer's multisampled target instead —
|
|
44
|
+
const target = new THREE.WebGLRenderTarget(innerWidth, innerHeight, { samples: tier.composerSamples });
|
|
45
|
+
const composer = new EffectComposer(renderer, target);
|
|
38
46
|
```
|
|
39
47
|
|
|
48
|
+
`detectTier()` also demotes WEAK desktops (Intel iGPU MacBooks, old integrated
|
|
49
|
+
AMD — desktop GPU strings are unmasked, unlike iOS) to `desktop-low`: DPR 1.5,
|
|
50
|
+
no MSAA, 1024 shadows, light post. The Quality picker still overrides.
|
|
51
|
+
|
|
40
52
|
The tier owns every budget decision: `dprCap` (1.5 on phones — the single
|
|
41
53
|
biggest framebuffer lever), `antialias` (off on phones; it is fixed at context
|
|
42
54
|
creation and can never change live), `shadowMapSize` (1024 phone / 2048
|
|
@@ -51,6 +63,10 @@ knobs may change at runtime vs load time vs never: [references/adaptive-quality.
|
|
|
51
63
|
const governor = new QualityGovernor(tier, {
|
|
52
64
|
setDprScale: (m) => renderer.setPixelRatio(Math.min(window.devicePixelRatio, tier.dprCap * m)),
|
|
53
65
|
setPostEnabled: (on) => (composerEnabled = on),
|
|
66
|
+
setShadowQuality: (level) => {
|
|
67
|
+
sun.shadow.mapSize.setScalar(level === 'full' ? tier.shadowMapSize : tier.shadowMapSize / 2);
|
|
68
|
+
sun.shadow.map?.dispose(); sun.shadow.map = null; // realloc at the new size
|
|
69
|
+
},
|
|
54
70
|
setDrawDistanceScale: (m) => (scene.fog!.far = baseFogFar * tier.drawDistanceScale * m),
|
|
55
71
|
}, renderer);
|
|
56
72
|
|
|
@@ -58,9 +74,9 @@ const governor = new QualityGovernor(tier, {
|
|
|
58
74
|
governor.frame(deltaMs);
|
|
59
75
|
```
|
|
60
76
|
|
|
61
|
-
Sustained slow frames step down (DPR ×0.8 → post off →
|
|
62
|
-
30 fps cap); twenty smooth seconds step back up; a
|
|
63
|
-
stays down for the session. It keeps governing forever — thermal throttling
|
|
77
|
+
Sustained slow frames step down (DPR ×0.8 → post off → shadows reduced →
|
|
78
|
+
DPR ×0.65 → draw distance → 30 fps cap); twenty smooth seconds step back up; a
|
|
79
|
+
knob whose recovery failed twice stays down for the session. It keeps governing forever — thermal throttling
|
|
64
80
|
arrives at minute eight, not second thirty. It also pauses judgment when the
|
|
65
81
|
tab is hidden and publishes memory counts the platform's crash telemetry
|
|
66
82
|
reads; pause your own loop and audio on `visibilitychange` too.
|
|
@@ -84,6 +100,31 @@ Desktop loads the original, phones the `@2048` rung (~11 MB), and a missing
|
|
|
84
100
|
rung falls back to the original — never a broken boot. The `$genex-ai-skybox`
|
|
85
101
|
and `$genex-ai-texture` skills show the wiring in place.
|
|
86
102
|
|
|
103
|
+
## Generated models: load through the rungs
|
|
104
|
+
|
|
105
|
+
Generated GLBs are provider-raw — multiple 1–4K PBR textures + dense geometry.
|
|
106
|
+
Every model ships a `@1024` mobile rung (textures budgeted to 1024, meshopt-
|
|
107
|
+
compressed, simplified where safe) plus a `.ktx2` sibling whose textures stay
|
|
108
|
+
compressed ON the GPU (~6× less texture VRAM). A plain `GLTFLoader` can decode
|
|
109
|
+
neither — wire the decoders once and load through the ladder:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { createGltfLoader } from "./controllers/quality/gltf-loader.ts";
|
|
113
|
+
import { loadModelWithFallback } from "./controllers/quality/pick-asset.ts";
|
|
114
|
+
|
|
115
|
+
const gltfLoader = createGltfLoader(renderer); // meshopt always; KTX2 when the transcoder is present
|
|
116
|
+
const gltf = await loadModelWithFallback(
|
|
117
|
+
MODEL_URL, tier, (u) => gltfLoader.loader.loadAsync(u), { ktx2: gltfLoader.ktx2 },
|
|
118
|
+
);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Fallback chain: `.ktx2` rung → universal `@1024` rung → original — each step
|
|
122
|
+
warns, the worst case is today's full download, never a broken boot. Desktop
|
|
123
|
+
always loads the original. Meshy characters take the same ladder through
|
|
124
|
+
`loadMeshyCharacter(manifestUrl, { loader: gltfLoader.loader, modelUrlCandidates: (u) => [pickModel(u, tier, { ktx2: gltfLoader.ktx2 }), pickModel(u, tier), u] })`.
|
|
125
|
+
KTX2-capable games can also pass `ktx2Load` to `loadTextureWithFallback` so
|
|
126
|
+
skyboxes/textures use their `.ktx2` variants.
|
|
127
|
+
|
|
87
128
|
## Quality picker in settings
|
|
88
129
|
|
|
89
130
|
The pause/settings screen (see `$genex-threejs-game-ui`) always carries a
|
|
@@ -14,19 +14,28 @@ cost of guessing high is a dead page.
|
|
|
14
14
|
|
|
15
15
|
## The tier ladder
|
|
16
16
|
|
|
17
|
-
| Knob | phone-low | phone | desktop |
|
|
18
|
-
|
|
19
|
-
| DPR cap | 1.0 | 1.5 | 2 |
|
|
20
|
-
| antialias (context) | off | off | on |
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
17
|
+
| Knob | phone-low | phone | desktop-low | desktop |
|
|
18
|
+
|---|---|---|---|---|
|
|
19
|
+
| DPR cap | 1.0 | 1.5 | 1.5 | 2 |
|
|
20
|
+
| antialias (context, no-post games) | off | off | off | on |
|
|
21
|
+
| Composer MSAA samples | 0 | 0 | 0 | 4 |
|
|
22
|
+
| Shadow map | 512 (static-cached) | 1024 | 1024 | 2048 |
|
|
23
|
+
| Post level | tone map only | + FXAA/vignette | + FXAA/vignette | full named stack |
|
|
24
|
+
| Skybox rung | @2048 (~11 MB) | @4096 (~45 MB) | original | original |
|
|
25
|
+
| Model rung | @1024 (+.ktx2 when wired) | @1024 (+.ktx2 when wired) | original | original |
|
|
26
|
+
| Texture rung (props) | @1024 | @2048 | original | original |
|
|
27
|
+
| Particles/scatter | 0.25× | 0.5× | 0.75× | 1× |
|
|
28
|
+
| Draw distance | 0.5× | 0.75× | 1× | 1× |
|
|
29
|
+
| Frame target | stable 30 | 60 | 60 | 60 |
|
|
30
|
+
| Remote avatars animated | 4 | 8 | all | all |
|
|
31
|
+
| Prop colliders | hull/cuboid | hull | as designed | as designed |
|
|
32
|
+
|
|
33
|
+
`desktop-low` is the weak-desktop demotion (Intel iGPU MacBooks, old integrated
|
|
34
|
+
AMD): desktop GPU renderer strings are UNMASKED (unlike iOS), so one boot probe
|
|
35
|
+
separates a 2015 Intel Air from an M3 Max. The full desktop path on those
|
|
36
|
+
machines — DPR 2 + 4× MSAA + 2048 PCFSoft shadows + full bloom — is the classic
|
|
37
|
+
"huge lags on a MacBook" recipe, and two of its costs (context MSAA, shadow
|
|
38
|
+
budget) were previously invisible to the governor.
|
|
30
39
|
|
|
31
40
|
A DPR drop from 3 (raw iPhone) to 1.5 cuts every full-screen surface — color,
|
|
32
41
|
depth, and each post target — to a quarter of the bytes. It is the single
|
|
@@ -40,7 +49,10 @@ Getting this wrong produces silent no-ops or a session stuck ugly:
|
|
|
40
49
|
`stencil`, `powerPreference` on WebGL. Changing them means a new context and
|
|
41
50
|
a full re-init — the tier must decide them BEFORE the renderer exists.
|
|
42
51
|
(WebGPU differs: MSAA is per-render-target sample count and is runtime-
|
|
43
|
-
changeable.)
|
|
52
|
+
changeable.) **Context MSAA under an EffectComposer is pure waste** — it
|
|
53
|
+
multisamples a buffer the composer never reads. Post games construct with
|
|
54
|
+
`rendererAntialias(tier, true)` (false) and put their AA on the composer's
|
|
55
|
+
own target via `tier.composerSamples`; only no-post games keep context MSAA.
|
|
44
56
|
- **Load-time (fixed for the session once fetched):** asset rungs (skybox and
|
|
45
57
|
texture resolutions), model LOD sets. `pickAsset` decides them from the tier
|
|
46
58
|
at load; switching later means a re-fetch — treat as fixed.
|
|
@@ -56,8 +68,11 @@ Getting this wrong produces silent no-ops or a session stuck ugly:
|
|
|
56
68
|
`renderer.compileAsync` during the loader screen removes most spikes at the
|
|
57
69
|
source (and keeps first-frame jank from reading as a stall to the platform's
|
|
58
70
|
telemetry).
|
|
59
|
-
- Step-down order: DPR ×0.8 → post off →
|
|
60
|
-
|
|
71
|
+
- Step-down order: DPR ×0.8 → post off → shadows reduced (half the map +
|
|
72
|
+
realloc via `setShadowQuality`) → DPR ×0.65 → draw distance ×0.6 → 30 fps
|
|
73
|
+
cap. Each step is the cheapest remaining lever with the biggest headroom
|
|
74
|
+
return; the shadow rung and second DPR step exist because one ×0.8 was
|
|
75
|
+
often not enough on weak desktops.
|
|
61
76
|
- Step-up needs 20 smooth seconds (hysteresis), and a step that had to be
|
|
62
77
|
re-applied twice is pinned for the session — oscillating quality reads worse
|
|
63
78
|
than stable-low.
|