diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index c3161a7700b..4a1a9c7fa7e 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,4 +7,3 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut - diff --git a/README.md b/README.md index 19fd0df12ae..0af9bfc0360 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.31.7' + implementation 'com.google.android.filament:filament-android:1.32.0' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ``` -pod 'Filament', '~> 1.31.7' +pod 'Filament', '~> 1.32.0' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e92aef42bd2..5ad820b42ac 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,16 @@ A new header is inserted each time a *tag* is created. Instead, if you are authoring a PR for the main branch, add your release note to [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). +## v1.32.0 + +- fog: fixed fog height falloff and computation precision on mobile [⚠️ **Recompile Materials**] +- materials: new alphaToCoverage property can be used to control alpha to coverage behavior +- materials: added `getUserWorldFromWorldMatrix()` and `getUserWorldPosition()` to retrieve the + API-level (user) world position in materials. Deprecated `getWorldOffset()`. [⚠️ **Recompile + Materials**] +- engine: fix precision issue with `shading_view` in large scenes +- vulkan: readPixels is now async (#6560) + ## v1.31.7 ## v1.31.6 diff --git a/android/filamat-android/src/main/cpp/MaterialBuilder.cpp b/android/filamat-android/src/main/cpp/MaterialBuilder.cpp index 2c70f2526a0..c17505e2848 100644 --- a/android/filamat-android/src/main/cpp/MaterialBuilder.cpp +++ b/android/filamat-android/src/main/cpp/MaterialBuilder.cpp @@ -250,6 +250,13 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderMaskThr builder->maskThreshold(maskThreshold); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderAlphaToCoverage(JNIEnv*, + jclass, jlong nativeBuilder, jboolean enable) { + auto builder = (MaterialBuilder*) nativeBuilder; + builder->alphaToCoverage(enable); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderShadowMultiplier( JNIEnv*, jclass, jlong nativeBuilder, jboolean shadowMultiplier) { diff --git a/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java b/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java index 435f2fe75dd..000262ed95d 100644 --- a/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java +++ b/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java @@ -360,6 +360,12 @@ public MaterialBuilder maskThreshold(float threshold) { return this; } + @NonNull + public MaterialBuilder alphaToCoverage(boolean enable) { + nMaterialBuilderAlphaToCoverage(mNativeObject, enable); + return this; + } + @NonNull public MaterialBuilder shadowMultiplier(boolean shadowMultiplier) { nMaterialBuilderShadowMultiplier(mNativeObject, shadowMultiplier); @@ -584,6 +590,7 @@ private static native void nMaterialBuilderVariable(long nativeBuilder, int vari private static native void nMaterialBuilderDepthCulling(long nativeBuilder, boolean enable); private static native void nMaterialBuilderDoubleSided(long nativeBuilder, boolean doubleSided); private static native void nMaterialBuilderMaskThreshold(long nativeBuilder, float mode); + private static native void nMaterialBuilderAlphaToCoverage(long nativeBuilder, boolean enable); private static native void nMaterialBuilderShadowMultiplier(long mNativeObject, boolean shadowMultiplier); diff --git a/android/filament-android/src/main/cpp/Material.cpp b/android/filament-android/src/main/cpp/Material.cpp index 44b50c5d615..f42119e2dba 100644 --- a/android/filament-android/src/main/cpp/Material.cpp +++ b/android/filament-android/src/main/cpp/Material.cpp @@ -153,6 +153,14 @@ Java_com_google_android_filament_Material_nIsDoubleSided(JNIEnv*, jclass, return (jboolean) material->isDoubleSided(); } +extern "C" +JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_Material_nIsAlphaToCoverageEnabled(JNIEnv*, jclass, + jlong nativeMaterial) { + Material* material = (Material*) nativeMaterial; + return (jboolean) material->isAlphaToCoverageEnabled(); +} + extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_Material_nGetMaskThreshold(JNIEnv*, jclass, diff --git a/android/filament-android/src/main/cpp/MaterialInstance.cpp b/android/filament-android/src/main/cpp/MaterialInstance.cpp index 13561e0f503..5273dc2c808 100644 --- a/android/filament-android/src/main/cpp/MaterialInstance.cpp +++ b/android/filament-android/src/main/cpp/MaterialInstance.cpp @@ -457,7 +457,6 @@ extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_MaterialInstance_nGetMaskThreshold(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nGetMaskThreshold() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->getMaskThreshold(); } @@ -466,7 +465,6 @@ extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_MaterialInstance_nGetSpecularAntiAliasingVariance(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nGetSpecularAntiAliasingVariance() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->getSpecularAntiAliasingVariance(); } @@ -475,7 +473,6 @@ extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_MaterialInstance_nGetSpecularAntiAliasingThreshold(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nGetSpecularAntiAliasingThreshold() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->getSpecularAntiAliasingThreshold(); } @@ -484,7 +481,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsDoubleSided(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nIsDoubleSided() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isDoubleSided(); } @@ -493,7 +489,6 @@ extern "C" JNIEXPORT jint JNICALL Java_com_google_android_filament_MaterialInstance_nGetCullingMode(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nGetCullingMode() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return (jint)instance->getCullingMode(); } @@ -502,7 +497,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsColorWriteEnabled(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nIsColorWriteEnabled() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isColorWriteEnabled(); } @@ -511,7 +505,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsDepthWriteEnabled(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nIsDepthWriteEnabled() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isDepthWriteEnabled(); } @@ -520,7 +513,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsStencilWriteEnabled(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nIsStencilWriteEnabled() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isStencilWriteEnabled(); } @@ -529,7 +521,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsDepthCullingEnabled(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nIsDepthCullingEnabled() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isDepthCullingEnabled(); } diff --git a/android/filament-android/src/main/java/com/google/android/filament/Material.java b/android/filament-android/src/main/java/com/google/android/filament/Material.java index 54aeca69e71..51d6f01bd54 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Material.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Material.java @@ -503,6 +503,17 @@ public boolean isDoubleSided() { return nIsDoubleSided(getNativeObject()); } + /** + * Indicates whether instances of this material will use alpha to coverage. + * + * @see + * + * Rasterization: alphaToCoverage + */ + public boolean isAlphaToCoverageEnabled() { + return nIsAlphaToCoverageEnabled(getNativeObject()); + } + /** * Returns the alpha mask threshold used when the blending mode is set to masked. * @@ -915,6 +926,7 @@ void clearNativeObject() { private static native boolean nIsDepthWriteEnabled(long nativeMaterial); private static native boolean nIsDepthCullingEnabled(long nativeMaterial); private static native boolean nIsDoubleSided(long nativeMaterial); + private static native boolean nIsAlphaToCoverageEnabled(long nativeMaterial); private static native float nGetMaskThreshold(long nativeMaterial); private static native float nGetSpecularAntiAliasingVariance(long nativeMaterial); private static native float nGetSpecularAntiAliasingThreshold(long nativeMaterial); diff --git a/android/gradle.properties b/android/gradle.properties index 1b9b322b135..28dbfd95a7a 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.31.7 +VERSION_NAME=1.32.0 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/android/samples/sample-material-builder/src/main/res/values/strings.xml b/android/samples/sample-material-builder/src/main/res/values/strings.xml index a0c36da0403..0bfa5ebcb8d 100644 --- a/android/samples/sample-material-builder/src/main/res/values/strings.xml +++ b/android/samples/sample-material-builder/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - Image-Based Lighting + Material Builder diff --git a/docs/Materials.md.html b/docs/Materials.md.html index 91888aa7755..8441a903cce 100644 --- a/docs/Materials.md.html +++ b/docs/Materials.md.html @@ -1362,6 +1362,11 @@ ALPHA_TO_COVERAGE is enabled for non-translucent views. See the maskThreshold section for more information. +!!! Note + When `blending` is set to `masked`, alpha to coverage is automatically enabled for the material. + If this behavior is undesirable, refer to the Rasterization: alphaToCoverage section to turn + alpha to coverage off using the `alphaToCoverage` property. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON material { blending : transparent @@ -1602,6 +1607,37 @@ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Rasterization: alphaToCoverage + +Type +: `boolean` + +Value +: `true` or `false`. Defaults to `false`. + +Description +: Enables or disables alpha to coverage. When alpha to coverage is enabled, the coverage of + fragment is derived from its alpha. This property is only meaningful when MSAA is enabled. + Note: setting `blending` to `masked` automatically enables alpha to coverage. If this is not + desired, you can override this behavior by setting alpha to coverage to false as in the + example below. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON +material { + name : "Alpha to coverage", + shadingModel : lit, + blending : masked, + alphaToCoverage : false +} + +fragment { + void material(inout MaterialInputs material) { + prepareMaterial(material); + material.baseColor = materialParams.albedo; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ### Lighting: reflections Type @@ -1910,8 +1946,9 @@ !!! TIP: worldPosition To achieve good precision, the `worldPosition` coordinate in the vertex shader is shifted by the - camera position. To get the true world-space position, users can add this to - `getWorldOffset()`. + camera position. To get the true world-space position, users can use + `getUserWorldPosition()`, however be aware that the true world-position might not + be able to fit in a `float` or might be represented with severely reduced precision. !!! TIP: UV attributes By default the vertex shader of a material will flip the Y coordinate of the UV attributes @@ -2264,8 +2301,9 @@ Name | Type | Description :-----------------------------------|:--------:|:------------------------------------ **getResolution()** | float4 | Dimensions of the view's effective (physical) viewport in pixels: `width`, `height`, `1 / width`, `1 / height`. This might be different from `View::getViewport()` for instance because of added rendering guard-bands. -**getWorldCameraPosition()** | float3 | Position of the camera/eye in world space -**getWorldOffset()** | float3 | The shift required to obtain API-level world space +**getWorldCameraPosition()** | float3 | Position of the camera/eye in world space (see note below) +**getWorldOffset()** | float3 | [deprecated] The shift required to obtain API-level world space. Use getUserWorldPosition() instead +**getUserWorldFromWorldMatrix()** | float4x4 | Matrix that converts from world space to API-level (user) world space. **getTime()** | float | Current time as a remainder of 1 second. Yields a value between 0 and 1 **getUserTime()** | float4 | Current time in seconds: `time`, `(double)time - time`, `0`, `0` **getUserTimeMode(float m)** | float | Current time modulo m in seconds @@ -2275,7 +2313,7 @@ !!! TIP: world space To achieve good precision, the "world space" in Filament's shading system does not necessarily match the API-level world space. To obtain the position of the API-level camera, custom - materials can add `getWorldOffset()` to `getWorldCameraPosition()`. + materials can use `getUserWorldFromWorldMatrix()` to transform `getWorldCameraPosition()`. ### Vertex only @@ -2297,6 +2335,7 @@ :---------------------------------------|:--------:|:------------------------------------ **getWorldTangentFrame()** | float3x3 | Matrix containing in each column the `tangent` (`frame[0]`), `bi-tangent` (`frame[1]`) and `normal` (`frame[2]`) of the vertex in world space. If the material does not compute a tangent space normal for bump mapping or if the shading is not anisotropic, only the `normal` is valid in this matrix. **getWorldPosition()** | float3 | Position of the fragment in world space (see note below about world-space) +**getUserWorldPosition()** | float3 | Position of the fragment in API-level (user) world-space (see note below about world-space) **getWorldViewVector()** | float3 | Normalized vector in world space from the fragment position to the eye **getWorldNormalVector()** | float3 | Normalized normal in world space, after bump mapping (must be used after `prepareMaterial()`) **getWorldGeometricNormalVector()** | float3 | Normalized normal in world space, before bump mapping (can be used before `prepareMaterial()`) @@ -2313,9 +2352,10 @@ **ycbcrToRgb(float, float2)** | float3 | Converts a luminance and CbCr pair to a sRGB color **uvToRenderTargetUV(float2)** | float2 | Transforms a UV coordinate to allow sampling from a `RenderTarget` attachment -!!! TIP: world space - To obtain API-level world space coordinates, custom materials should add `getWorldOffset()` to - `getWorldPosition()` (et al). +!!! TIP: world-space + To obtain API-level world-space coordinates, custom materials should use `getUserWorldPosition()` + or use `getUserWorldFromWorldMatrix()`. Note that API-level world-space coordinates should + never or rarely be used because they may not fit in a float3 or have severely reduced precision. !!! TIP: sampling from render targets When sampling from a `filament::Texture` that is attached to a `filament::RenderTarget` for diff --git a/docs/webgl/albedo.ktx2 b/docs/webgl/albedo.ktx2 new file mode 100644 index 00000000000..9b71b806cd7 Binary files /dev/null and b/docs/webgl/albedo.ktx2 differ diff --git a/docs/webgl/ao.ktx2 b/docs/webgl/ao.ktx2 new file mode 100644 index 00000000000..a1d6bbc218d Binary files /dev/null and b/docs/webgl/ao.ktx2 differ diff --git a/docs/webgl/default_env/default_env_ibl.ktx b/docs/webgl/default_env/default_env_ibl.ktx index 0fd6f9ee1b5..b2e6cbabdb5 100644 Binary files a/docs/webgl/default_env/default_env_ibl.ktx and b/docs/webgl/default_env/default_env_ibl.ktx differ diff --git a/docs/webgl/default_env/default_env_skybox.ktx b/docs/webgl/default_env/default_env_skybox.ktx index fe758498214..74750d424b3 100644 Binary files a/docs/webgl/default_env/default_env_skybox.ktx and b/docs/webgl/default_env/default_env_skybox.ktx differ diff --git a/docs/webgl/default_env/default_env_skybox_tiny.ktx b/docs/webgl/default_env/default_env_skybox_tiny.ktx index 714acad54d2..c34057b23ba 100644 Binary files a/docs/webgl/default_env/default_env_skybox_tiny.ktx and b/docs/webgl/default_env/default_env_skybox_tiny.ktx differ diff --git a/docs/webgl/filament.js b/docs/webgl/filament.js index 024e3e9e6d8..4a33d0b2833 100644 --- a/docs/webgl/filament.js +++ b/docs/webgl/filament.js @@ -6,7 +6,7 @@ var Filament = (() => { function(Filament) { Filament = Filament || {}; -var Module=typeof Filament!="undefined"?Filament:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};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";var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;function logExceptionOnExit(e){if(e instanceof ExitStatus)return;let toLog=e;err("exiting due to exception: "+toLog)}var fs;var nodePath;var requireNodeFS;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}requireNodeFS=()=>{if(!nodePath){fs=require("fs");nodePath=require("path")}};read_=function shell_read(filename,binary){requireNodeFS();filename=nodePath["normalize"](filename);return fs.readFileSync(filename,binary?undefined:"utf8")};readBinary=filename=>{var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret};readAsync=(filename,onload,onerror)=>{requireNodeFS();filename=nodePath["normalize"](filename);fs.readFile(filename,function(err,data){if(err)onerror(err);else onload(data.buffer)})};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",function(reason){throw reason});quit_=(status,toThrow)=>{if(keepRuntimeAlive()){process["exitCode"]=status;throw toThrow}logExceptionOnExit(toThrow);process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}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(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};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,onload,onerror)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=title=>document.title=title}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.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 tempRet0=0;var setTempRet0=value=>{tempRet0=value};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort(text)}}var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf8"):undefined;function 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))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=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}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf-16le"):undefined;function 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))}else{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}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function 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}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=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}function lengthBytesUTF32(str){var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function keepRuntimeAlive(){return noExitRuntime}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;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();FS.ignorePermissions=false;TTY.init();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 getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){{if(Module["onAbort"]){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,";function isDataURI(filename){return filename.startsWith(dataURIPrefix)}function isFileURI(filename){return filename.startsWith("file://")}var wasmBinaryFile;wasmBinaryFile="filament.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch=="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["Zb"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["$b"];addOnInit(Module["asm"]["_b"]);removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(function(instance){return instance}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiationResult,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiationResult)})})}else{return instantiateArrayBuffer(receiveInstantiationResult)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var tempDouble;var tempI64;var ASM_CONSTS={1005868:()=>{const options=window.filament_glOptions;const context=window.filament_glContext;const handle=GL.registerContext(context,options);window.filament_contextHandle=handle;GL.makeContextCurrent(handle)},1006082:()=>{const handle=window.filament_contextHandle;GL.makeContextCurrent(handle)},1006163:($0,$1,$2,$3,$4,$5)=>{const fn=Emval.toValue($0);fn({"renderable":Emval.toValue($1),"depth":$2,"fragCoords":[$3,$4,$5]})}};function callRuntimeCallbacks(callbacks){while(callbacks.length>0){callbacks.shift()(Module)}}var wasmTableMirror=[];function getWasmTableEntry(funcPtr){var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:path=>{if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:(l,r)=>{return PATH.normalize(l+"/"+r)}};function getRandomDevice(){if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){var randomBuffer=new Uint8Array(1);return function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");return function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}return function(){abort("randomDevice")}}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){abort()}var MEMFS={ops_table:null,mount:function(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode:function(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}if(!MEMFS.ops_table){MEMFS.ops_table={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}}}var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray:function(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage:function(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr:function(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr:function(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup:function(parent,name){throw FS.genericErrors[44]},mknod:function(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename:function(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp;old_node.parent=new_dir},unlink:function(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir:function(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir:function(node){var entries=[".",".."];for(var key in node.contents){if(!node.contents.hasOwnProperty(key)){continue}entries.push(key)}return entries},symlink:function(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink:function(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read:function(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{path=PATH_FS.resolve(FS.cwd(),path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};opts=Object.assign(defaults,opts);if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(p=>!!p),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:node=>{var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:(parentid,name)=>{var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:node=>{var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:node=>{var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:(parent,name)=>{var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:(parent,name,mode,rdev)=>{var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:node=>{FS.hashRemoveNode(node)},isRoot:node=>{return node===node.parent},isMountpoint:node=>{return!!node.mounted},isFile:mode=>{return(mode&61440)===32768},isDir:mode=>{return(mode&61440)===16384},isLink:mode=>{return(mode&61440)===40960},isChrdev:mode=>{return(mode&61440)===8192},isBlkdev:mode=>{return(mode&61440)===24576},isFIFO:mode=>{return(mode&61440)===4096},isSocket:mode=>{return(mode&49152)===49152},flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:str=>{var flags=FS.flagModes[str];if(typeof flags=="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:flag=>{var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:(node,perms)=>{if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup:dir=>{var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:(dir,name)=>{try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:(dir,name,isdir)=>{var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:(node,flags)=>{if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:(fd_start=0,fd_end=FS.MAX_OPEN_FDS)=>{for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:fd=>FS.streams[fd],createStream:(stream,fd_start,fd_end)=>{if(!FS.FSStream){FS.FSStream=function(){this.shared={}};FS.FSStream.prototype={};Object.defineProperties(FS.FSStream.prototype,{object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}},flags:{get:function(){return this.shared.flags},set:function(val){this.shared.flags=val}},position:{get:function(){return this.shared.position},set:function(val){this.shared.position=val}}})}stream=Object.assign(new FS.FSStream,stream);var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:fd=>{FS.streams[fd]=null},chrdev_stream_ops:{open:stream=>{var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:()=>{throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice:(dev,ops)=>{FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts:mount=>{var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:(populate,callback)=>{if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:(type,opts,mountpoint)=>{var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:mountpoint=>{var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:(parent,name)=>{return parent.node_ops.lookup(parent,name)},mknod:(path,mode,dev)=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:(path,mode)=>{mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:(path,mode)=>{mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:(path,mode)=>{var dirs=path.split("/");var d="";for(var i=0;i{if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink:(oldpath,newpath)=>{if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename:(old_path,new_path)=>{var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name)}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir:path=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir:path=>{var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node.node_ops.readdir){throw new FS.ErrnoError(54)}return node.node_ops.readdir(node)},unlink:path=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink:path=>{var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return PATH_FS.resolve(FS.getPath(link.parent),link.node_ops.readlink(link))},stat:(path,dontFollow)=>{var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;if(!node){throw new FS.ErrnoError(44)}if(!node.node_ops.getattr){throw new FS.ErrnoError(63)}return node.node_ops.getattr(node)},lstat:path=>{return FS.stat(path,true)},chmod:(path,mode,dontFollow)=>{var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{mode:mode&4095|node.mode&~4095,timestamp:Date.now()})},lchmod:(path,mode)=>{FS.chmod(path,mode,true)},fchmod:(fd,mode)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chmod(stream.node,mode)},chown:(path,uid,gid,dontFollow)=>{var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{timestamp:Date.now()})},lchown:(path,uid,gid)=>{FS.chown(path,uid,gid,true)},fchown:(fd,uid,gid)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chown(stream.node,uid,gid)},truncate:(path,len)=>{if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}node.node_ops.setattr(node,{size:len,timestamp:Date.now()})},ftruncate:(fd,len)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.truncate(stream.node,len)},utime:(path,atime,mtime)=>{var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;node.node_ops.setattr(node,{timestamp:Math.max(atime,mtime)})},open:(path,flags,mode)=>{if(path===""){throw new FS.ErrnoError(44)}flags=typeof flags=="string"?FS.modeStringToFlags(flags):flags;mode=typeof mode=="undefined"?438:mode;if(flags&64){mode=mode&4095|32768}else{mode=0}var node;if(typeof path=="object"){node=path}else{path=PATH.normalize(path);try{var lookup=FS.lookupPath(path,{follow:!(flags&131072)});node=lookup.node}catch(e){}}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else{node=FS.mknod(path,mode,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node:node,path:FS.getPath(node),flags:flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(Module["logReadFiles"]&&!(flags&1)){if(!FS.readFiles)FS.readFiles={};if(!(path in FS.readFiles)){FS.readFiles[path]=1}}return stream},close:stream=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed:stream=>{return stream.fd===null},llseek:(stream,offset,whence)=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read:(stream,buffer,offset,length,position)=>{if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write:(stream,buffer,offset,length,position,canOwn)=>{if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},allocate:(stream,offset,length)=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(offset<0||length<=0){throw new FS.ErrnoError(28)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(!FS.isFile(stream.node.mode)&&!FS.isDir(stream.node.mode)){throw new FS.ErrnoError(43)}if(!stream.stream_ops.allocate){throw new FS.ErrnoError(138)}stream.stream_ops.allocate(stream,offset,length)},mmap:(stream,length,position,prot,flags)=>{if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync:(stream,buffer,offset,length,mmapFlags)=>{if(!stream||!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},munmap:stream=>0,ioctl:(stream,cmd,arg)=>{if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile:(path,opts={})=>{opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){throw new Error('Invalid encoding type "'+opts.encoding+'"')}var ret;var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){ret=UTF8ArrayToString(buf,0)}else if(opts.encoding==="binary"){ret=buf}FS.close(stream);return ret},writeFile:(path,data,opts={})=>{opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);if(typeof data=="string"){var buf=new Uint8Array(lengthBytesUTF8(data)+1);var actualNumBytes=stringToUTF8Array(data,buf,0,buf.length);FS.write(stream,buf,0,actualNumBytes,undefined,opts.canOwn)}else if(ArrayBuffer.isView(data)){FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn)}else{throw new Error("Unsupported data type")}FS.close(stream)},cwd:()=>FS.currentPath,chdir:path=>{var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories:()=>{FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices:()=>{FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var random_device=getRandomDevice();FS.createDevice("/dev","random",random_device);FS.createDevice("/dev","urandom",random_device);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories:()=>{FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount:()=>{var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup:(parent,name)=>{var fd=+name;var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams:()=>{if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"])}else{FS.symlink("/dev/tty","/dev/stdin")}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"])}else{FS.symlink("/dev/tty","/dev/stdout")}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"])}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},ensureErrnoError:()=>{if(FS.ErrnoError)return;FS.ErrnoError=function ErrnoError(errno,node){this.node=node;this.setErrno=function(errno){this.errno=errno};this.setErrno(errno);this.message="FS error"};FS.ErrnoError.prototype=new Error;FS.ErrnoError.prototype.constructor=FS.ErrnoError;[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack=""})},staticInit:()=>{FS.ensureErrnoError();FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={"MEMFS":MEMFS}},init:(input,output,error)=>{FS.init.initialized=true;FS.ensureErrnoError();Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit:()=>{FS.init.initialized=false;for(var i=0;i{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode},findObject:(path,dontResolveLastLink)=>{var ret=FS.analyzePath(path,dontResolveLastLink);if(ret.exists){return ret.object}else{return null}},analyzePath:(path,dontResolveLastLink)=>{try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath:(parent,path,canRead,canWrite)=>{parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){}parent=current}return current},createFile:(parent,name,properties,canRead,canWrite)=>{var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile:(parent,name,data,canRead,canWrite,canOwn)=>{var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS.getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){if(typeof data=="string"){var arr=new Array(data.length);for(var i=0,len=data.length;i{var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(!!input,!!output);if(!FS.createDevice.major)FS.createDevice.major=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open:stream=>{stream.seekable=false},close:stream=>{if(output&&output.buffer&&output.buffer.length){output(10)}},read:(stream,buffer,offset,length,pos)=>{var bytesRead=0;for(var i=0;i{for(var i=0;i{if(obj.isDevice||obj.isFolder||obj.link||obj.contents)return true;if(typeof XMLHttpRequest!="undefined"){throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.")}else if(read_){try{obj.contents=intArrayFromString(read_(obj.url),true);obj.usedBytes=obj.contents.length}catch(e){throw new FS.ErrnoError(29)}}else{throw new Error("Cannot load without read() or XMLHttpRequest.")}},createLazyFile:(parent,name,url,canRead,canWrite)=>{function LazyUint8Array(){this.lengthKnown=false;this.chunks=[]}LazyUint8Array.prototype.get=function LazyUint8Array_get(idx){if(idx>this.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr:ptr,allocated:true}};node.stream_ops=stream_ops;return node},createPreloadedFile:(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency("cp "+fullname);function processData(byteArray){function finish(byteArray){if(preFinish)preFinish();if(!dontCreateFile){FS.createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}if(onload)onload();removeRunDependency(dep)}if(Browser.handledByPreloadPlugin(byteArray,fullname,finish,()=>{if(onerror)onerror();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,byteArray=>processData(byteArray),onerror)}else{processData(url)}},indexedDB:()=>{return window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB},DB_NAME:()=>{return"EM_FS_"+window.location.pathname},DB_VERSION:20,DB_STORE_NAME:"FILE_DATA",saveFilesToDB:(paths,onload,onerror)=>{onload=onload||(()=>{});onerror=onerror||(()=>{});var indexedDB=FS.indexedDB();try{var openRequest=indexedDB.open(FS.DB_NAME(),FS.DB_VERSION)}catch(e){return onerror(e)}openRequest.onupgradeneeded=()=>{out("creating db");var db=openRequest.result;db.createObjectStore(FS.DB_STORE_NAME)};openRequest.onsuccess=()=>{var db=openRequest.result;var transaction=db.transaction([FS.DB_STORE_NAME],"readwrite");var files=transaction.objectStore(FS.DB_STORE_NAME);var ok=0,fail=0,total=paths.length;function finish(){if(fail==0)onload();else onerror()}paths.forEach(path=>{var putRequest=files.put(FS.analyzePath(path).object.contents,path);putRequest.onsuccess=()=>{ok++;if(ok+fail==total)finish()};putRequest.onerror=()=>{fail++;if(ok+fail==total)finish()}});transaction.onerror=onerror};openRequest.onerror=onerror},loadFilesFromDB:(paths,onload,onerror)=>{onload=onload||(()=>{});onerror=onerror||(()=>{});var indexedDB=FS.indexedDB();try{var openRequest=indexedDB.open(FS.DB_NAME(),FS.DB_VERSION)}catch(e){return onerror(e)}openRequest.onupgradeneeded=onerror;openRequest.onsuccess=()=>{var db=openRequest.result;try{var transaction=db.transaction([FS.DB_STORE_NAME],"readonly")}catch(e){onerror(e);return}var files=transaction.objectStore(FS.DB_STORE_NAME);var ok=0,fail=0,total=paths.length;function finish(){if(fail==0)onload();else onerror()}paths.forEach(path=>{var getRequest=files.get(path);getRequest.onsuccess=()=>{if(FS.analyzePath(path).exists){FS.unlink(path)}FS.createDataFile(PATH.dirname(path),PATH.basename(path),getRequest.result,true,true,true);ok++;if(ok+fail==total)finish()};getRequest.onerror=()=>{fail++;if(ok+fail==total)finish()}});transaction.onerror=onerror};openRequest.onerror=onerror}};var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt:function(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=FS.getStream(dirfd);if(!dirstream)throw new FS.ErrnoError(8);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat:function(func,path,buf){try{var stat=func(path)}catch(e){if(e&&e.node&&PATH.normalize(path)!==PATH.normalize(FS.getPath(e.node))){return-54}throw e}HEAP32[buf>>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.createStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 5:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 6:case 7:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?SYSCALLS.get():0;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.stat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}var tupleRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAP32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function 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 createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+' "use strict";'+" return body.apply(this, arguments);\n"+"};\n")(body)}function 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 InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function 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{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)}}function __embind_finalize_value_array(rawTupleType){var reg=tupleRegistrations[rawTupleType];delete tupleRegistrations[rawTupleType];var elements=reg.elements;var elementsLength=elements.length;var elementTypes=elements.map(function(elt){return elt.getterReturnType}).concat(elements.map(function(elt){return elt.setterArgumentType}));var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;whenDependentTypesAreResolved([rawTupleType],elementTypes,function(elementTypes){elements.forEach((elt,i)=>{var getterReturnType=elementTypes[i];var getter=elt.getter;var getterContext=elt.getterContext;var setterArgumentType=elementTypes[i+elementsLength];var setter=elt.setter;var setterContext=elt.setterContext;elt.read=ptr=>{return getterReturnType["fromWireType"](getter(getterContext,ptr))};elt.write=(ptr,o)=>{var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,o));runDestructors(destructors)}});return[{name:reg.name,"fromWireType":function(ptr){var rv=new Array(elementsLength);for(var i=0;ifield.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=>{return getterReturnType["fromWireType"](getter(getterContext,ptr))},write:(ptr,o)=>{var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,o));runDestructors(destructors)}}});return[{name:reg.name,"fromWireType":function(ptr){var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},"toWireType":function(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":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:rawDestructor}]})}function __embind_register_bigint(primitiveType,name,size,minRange,maxRange){}function getShiftFromSize(size){switch(size){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+size)}}function 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=undefined;function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret}var BindingError=undefined;function throwBindingError(message){throw new BindingError(message)}function registerType(rawType,registeredInstance,options={}){if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}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 __embind_register_bool(rawType,name,size,trueValue,falseValue){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(wt){return!!wt},"toWireType":function(destructors,o){return o?trueValue:falseValue},"argPackAdvance":8,"readValueFromPointer":function(pointer){var heap;if(size===1){heap=HEAP8}else if(size===2){heap=HEAP16}else if(size===4){heap=HEAP32}else{throw new TypeError("Unknown boolean type size: "+name)}return this["fromWireType"](heap[pointer>>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;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}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationRegistry=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function 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={};function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}var delayFunction=undefined;function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function 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}}))}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})}}function 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)}function ClassHandle_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}}function ClassHandle_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}}function ClassHandle_isDeleted(){return!this.$$.ptr}function ClassHandle_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 init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function 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}}}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=[]}function 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.$$){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(function(){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}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["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}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){var f=Module["dynCall_"+sig];return args&&args.length?f.apply(null,[ptr].concat(args)):f.call(null,ptr)}function dynCall(sig,ptr,args){if(sig.includes("j")){return dynCallLegacy(sig,ptr,args)}var rtn=getWasmTableEntry(ptr).apply(null,args);return rtn}function getDynCaller(sig,ptr){var argCache=[];return function(){argCache.length=0;Object.assign(argCache,arguments);return dynCall(sig,ptr,argCache)}}function 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 UnboundTypeError=undefined;function getTypeName(type){var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv}function 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([", "]))}function __embind_register_class(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor){name=readLatin1String(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);if(upcast){upcast=embind__requireFunction(upcastSignature,upcast)}if(downcast){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]:[],function(base){base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(legalFunctionName,function(){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[arguments.length];if(undefined===body){throw new BindingError("Tried to invoke ctor of "+name+" with invalid number of parameters ("+arguments.length+") - expected ("+Object.keys(registeredClass.constructor_body).toString()+") parameters instead!")}return body.apply(this,arguments)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})}function new_(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 craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc){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=false;for(var i=1;i0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i>2])}return array}function __embind_register_class_class_function(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,fn){var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName=classType.name+"."+methodName;function unboundTypesHandler(){throwUnboundTypeError("Cannot call "+humanName+" due to unbound types",rawArgTypes)}if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}var proto=classType.registeredClass.constructor;if(undefined===proto[methodName]){unboundTypesHandler.argCount=argCount-1;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-1]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));var func=craftInvokerFunction(humanName,invokerArgsArray,null,rawInvoker,fn);if(undefined===proto[methodName].overloadTable){func.argCount=argCount-1;proto[methodName]=func}else{proto[methodName].overloadTable[argCount-1]=func}return[]});return[]})}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);whenDependentTypesAreResolved([],[rawClassType],function(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,function(argTypes){argTypes.splice(1,0,null);classType.registeredClass.constructor_body[argCount-1]=craftInvokerFunction(humanName,argTypes,null,invoker,rawConstructor);return[]});return[]})}function __embind_register_class_function(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual){var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],function(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,function(argTypes){var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})}function validateThis(this_,classType,humanName){if(!(this_ instanceof Object)){throwBindingError(humanName+' with invalid "this": '+this_)}if(!(this_ instanceof classType.registeredClass.constructor)){throwBindingError(humanName+' incompatible with "this" of type '+this_.constructor.name)}if(!this_.$$.ptr){throwBindingError("cannot call emscripten binding method "+humanName+" on deleted object")}return upcastPointer(this_.$$.ptr,this_.$$.ptrType.registeredClass,classType.registeredClass)}function __embind_register_class_property(classType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){fieldName=readLatin1String(fieldName);getter=embind__requireFunction(getterSignature,getter);whenDependentTypesAreResolved([],[classType],function(classType){classType=classType[0];var humanName=classType.name+"."+fieldName;var desc={get:function(){throwUnboundTypeError("Cannot access "+humanName+" due to unbound types",[getterReturnType,setterArgumentType])},enumerable:true,configurable:true};if(setter){desc.set=()=>{throwUnboundTypeError("Cannot access "+humanName+" due to unbound types",[getterReturnType,setterArgumentType])}}else{desc.set=v=>{throwBindingError(humanName+" is a read-only property")}}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);whenDependentTypesAreResolved([],setter?[getterReturnType,setterArgumentType]:[getterReturnType],function(types){var getterReturnType=types[0];var desc={get:function(){var ptr=validateThis(this,classType,humanName+" getter");return getterReturnType["fromWireType"](getter(getterContext,ptr))},enumerable:true};if(setter){setter=embind__requireFunction(setterSignature,setter);var setterArgumentType=types[1];desc.set=function(v){var ptr=validateThis(this,classType,humanName+" setter");var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,v));runDestructors(destructors)}}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);return[]});return[]})}var emval_free_list=[];var emval_handle_array=[{},{value:undefined},{value:null},{value:true},{value:false}];function __emval_decref(handle){if(handle>4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i{if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value},toHandle:value=>{switch(value){case undefined:return 1;case null:return 2;case true:return 3;case false:return 4;default:{var handle=emval_free_list.length?emval_free_list.pop():emval_handle_array.length;emval_handle_array[handle]={refcount:1,value:value};return handle}}}};function __embind_register_emval(rawType,name){name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(handle){var rv=Emval.toValue(handle);__emval_decref(handle);return rv},"toWireType":function(destructors,value){return Emval.toHandle(value)},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:null})}function enumReadValueFromPointer(name,shift,signed){switch(shift){case 0:return function(pointer){var heap=signed?HEAP8:HEAPU8;return this["fromWireType"](heap[pointer])};case 1:return function(pointer){var heap=signed?HEAP16:HEAPU16;return this["fromWireType"](heap[pointer>>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __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}function embindRepr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=value=>value;if(minRange===0){var bitshift=32-8*size;fromWireType=value=>value<>>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":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(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>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_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;iHEAPU16;shift=1}else if(charSize===4){decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32;getHeap=()=>HEAPU32;shift=2}registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==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":function(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>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_array(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){tupleRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),elements:[]}}function __embind_register_value_array_element(rawTupleType,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){tupleRegistrations[rawTupleType].elements.push({getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __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:[]}}function __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})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}var nowIsMonotonic=true;function __emscripten_get_now_is_monotonic(){return nowIsMonotonic}function __emval_as(handle,returnType,destructorsRef){handle=Emval.toValue(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=Emval.toHandle(destructors);HEAPU32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}function __emval_get_property(handle,key){handle=Emval.toValue(handle);key=Emval.toValue(key);return Emval.toHandle(handle[key])}function __emval_incref(handle){if(handle>4){emval_handle_array[handle].refcount+=1}}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}return symbol}function __emval_new_cstring(v){return Emval.toHandle(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)}function __emval_take_value(type,arg){type=requireRegisteredType(type,"_emval_take_value");var v=type["readValueFromPointer"](arg);return Emval.toHandle(v)}function _abort(){abort("")}var readAsmConstArgsArray=[];function readAsmConstArgs(sigPtr,buf){readAsmConstArgsArray.length=0;var ch;buf>>=2;while(ch=HEAPU8[sigPtr++]){buf+=ch!=105&buf;readAsmConstArgsArray.push(ch==105?HEAP32[buf]:HEAPF64[buf++>>1]);++buf}return readAsmConstArgsArray}function _emscripten_asm_const_int(code,sigPtr,argbuf){var args=readAsmConstArgs(sigPtr,argbuf);return ASM_CONSTS[code].apply(null,args)}function getHeapMax(){return 2147483648}function _emscripten_get_heap_max(){return getHeapMax()}var _emscripten_get_now;if(ENVIRONMENT_IS_NODE){_emscripten_get_now=()=>{var t=process["hrtime"]();return t[0]*1e3+t[1]/1e6}}else _emscripten_get_now=()=>performance.now();function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){var oldSize=HEAPU8.length;requestedSize=requestedSize>>>0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}let 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=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}getEnvStrings.strings=strings}return getEnvStrings.strings}function _environ_get(__environ,environ_buf){var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAPU32[penviron_buf_size>>2]=bufSize;return 0}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function doReadv(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function convertI32PairToI53Checked(lo,hi){return hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var offset=convertI32PairToI53Checked(offset_low,offset_high);if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function doWritev(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function _getentropy(buffer,size){if(!_getentropy.randomDevice){_getentropy.randomDevice=getRandomDevice()}for(var i=0;i>0]=_getentropy.randomDevice()}return 0}function __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(ctx){return!!(ctx.dibvbi=ctx.getExtension("WEBGL_draw_instanced_base_vertex_base_instance"))}function __webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(ctx){return!!(ctx.mdibvbi=ctx.getExtension("WEBGL_multi_draw_instanced_base_vertex_base_instance"))}function __webgl_enable_WEBGL_multi_draw(ctx){return!!(ctx.multiDrawWebgl=ctx.getExtension("WEBGL_multi_draw"))}var GL={counter:1,buffers:[],mappedBuffers:{},programs:[],framebuffers:[],renderbuffers:[],textures:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},queries:[],samplers:[],transformFeedbacks:[],syncs:[],byteSizeByTypeRoot:5120,byteSizeByType:[1,1,2,2,4,4,4,2,3,4,8],stringCache:{},stringiCache:{},unpackAlignment:4,recordError:function recordError(errorCode){if(!GL.lastError){GL.lastError=errorCode}},getNewId:function(table){var ret=GL.counter++;for(var i=table.length;i>1;var quadIndexes=new Uint16Array(numIndexes);var i=0,v=0;while(1){quadIndexes[i++]=v;if(i>=numIndexes)break;quadIndexes[i++]=v+1;if(i>=numIndexes)break;quadIndexes[i++]=v+2;if(i>=numIndexes)break;quadIndexes[i++]=v;if(i>=numIndexes)break;quadIndexes[i++]=v+2;if(i>=numIndexes)break;quadIndexes[i++]=v+3;if(i>=numIndexes)break;v+=4}context.GLctx.bufferData(34963,quadIndexes,35044);context.GLctx.bindBuffer(34963,null)}},getTempVertexBuffer:function getTempVertexBuffer(sizeBytes){var idx=GL.log2ceilLookup(sizeBytes);var ringbuffer=GL.currentContext.tempVertexBuffers1[idx];var nextFreeBufferIndex=GL.currentContext.tempVertexBufferCounters1[idx];GL.currentContext.tempVertexBufferCounters1[idx]=GL.currentContext.tempVertexBufferCounters1[idx]+1&GL.numTempVertexBuffersPerSize-1;var vbo=ringbuffer[nextFreeBufferIndex];if(vbo){return vbo}var prevVBO=GLctx.getParameter(34964);ringbuffer[nextFreeBufferIndex]=GLctx.createBuffer();GLctx.bindBuffer(34962,ringbuffer[nextFreeBufferIndex]);GLctx.bufferData(34962,1<>2]:-1;source+=UTF8ToString(HEAP32[string+i*4>>2],len<0?undefined:len)}return source},calcBufLength:function calcBufLength(size,type,stride,count){if(stride>0){return count*stride}var typeSize=GL.byteSizeByType[type-GL.byteSizeByTypeRoot];return size*typeSize*count},usedTempBuffers:[],preDrawHandleClientVertexAttribBindings:function preDrawHandleClientVertexAttribBindings(count){GL.resetBufferBinding=false;for(var i=0;i=2){GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query_webgl2")}if(context.version<2||!GLctx.disjointTimerQueryExt){GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query")}__webgl_enable_WEBGL_multi_draw(GLctx);var exts=GLctx.getSupportedExtensions()||[];exts.forEach(function(ext){if(!ext.includes("lose_context")&&!ext.includes("debug")){GLctx.getExtension(ext)}})}};function _glActiveTexture(x0){GLctx["activeTexture"](x0)}function _glAttachShader(program,shader){GLctx.attachShader(GL.programs[program],GL.shaders[shader])}function _glBeginQuery(target,id){GLctx["beginQuery"](target,GL.queries[id])}function _glBindBuffer(target,buffer){if(target==34962){GLctx.currentArrayBufferBinding=buffer}else if(target==34963){GLctx.currentElementArrayBufferBinding=buffer}if(target==35051){GLctx.currentPixelPackBufferBinding=buffer}else if(target==35052){GLctx.currentPixelUnpackBufferBinding=buffer}GLctx.bindBuffer(target,GL.buffers[buffer])}function _glBindBufferBase(target,index,buffer){GLctx["bindBufferBase"](target,index,GL.buffers[buffer])}function _glBindBufferRange(target,index,buffer,offset,ptrsize){GLctx["bindBufferRange"](target,index,GL.buffers[buffer],offset,ptrsize)}function _glBindFramebuffer(target,framebuffer){GLctx.bindFramebuffer(target,GL.framebuffers[framebuffer])}function _glBindRenderbuffer(target,renderbuffer){GLctx.bindRenderbuffer(target,GL.renderbuffers[renderbuffer])}function _glBindSampler(unit,sampler){GLctx["bindSampler"](unit,GL.samplers[sampler])}function _glBindTexture(target,texture){GLctx.bindTexture(target,GL.textures[texture])}function _glBindVertexArray(vao){GLctx["bindVertexArray"](GL.vaos[vao]);var ibo=GLctx.getParameter(34965);GLctx.currentElementArrayBufferBinding=ibo?ibo.name|0:0}function _glBlendEquationSeparate(x0,x1){GLctx["blendEquationSeparate"](x0,x1)}function _glBlendFuncSeparate(x0,x1,x2,x3){GLctx["blendFuncSeparate"](x0,x1,x2,x3)}function _glBlitFramebuffer(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9){GLctx["blitFramebuffer"](x0,x1,x2,x3,x4,x5,x6,x7,x8,x9)}function _glBufferData(target,size,data,usage){if(true){if(data&&size){GLctx.bufferData(target,HEAPU8,usage,data,size)}else{GLctx.bufferData(target,size,usage)}}else{GLctx.bufferData(target,data?HEAPU8.subarray(data,data+size):size,usage)}}function _glBufferSubData(target,offset,size,data){if(true){size&&GLctx.bufferSubData(target,offset,HEAPU8,data,size);return}GLctx.bufferSubData(target,offset,HEAPU8.subarray(data,data+size))}function _glClearBufferfi(x0,x1,x2,x3){GLctx["clearBufferfi"](x0,x1,x2,x3)}function _glClearBufferfv(buffer,drawbuffer,value){GLctx["clearBufferfv"](buffer,drawbuffer,HEAPF32,value>>2)}function _glClearBufferiv(buffer,drawbuffer,value){GLctx["clearBufferiv"](buffer,drawbuffer,HEAP32,value>>2)}function convertI32PairToI53(lo,hi){return(lo>>>0)+hi*4294967296}function _glClientWaitSync(sync,flags,timeoutLo,timeoutHi){return GLctx.clientWaitSync(GL.syncs[sync],flags,convertI32PairToI53(timeoutLo,timeoutHi))}function _glColorMask(red,green,blue,alpha){GLctx.colorMask(!!red,!!green,!!blue,!!alpha)}function _glCompileShader(shader){GLctx.compileShader(GL.shaders[shader])}function _glCompressedTexSubImage2D(target,level,xoffset,yoffset,width,height,format,imageSize,data){if(true){if(GLctx.currentPixelUnpackBufferBinding||!imageSize){GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,imageSize,data)}else{GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,HEAPU8,data,imageSize)}return}GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,data?HEAPU8.subarray(data,data+imageSize):null)}function _glCompressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data)}else{GLctx["compressedTexSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,HEAPU8,data,imageSize)}}function _glCopyBufferSubData(x0,x1,x2,x3,x4){GLctx["copyBufferSubData"](x0,x1,x2,x3,x4)}function _glCreateProgram(){var id=GL.getNewId(GL.programs);var program=GLctx.createProgram();program.name=id;program.maxUniformLength=program.maxAttributeLength=program.maxUniformBlockNameLength=0;program.uniformIdCounter=1;GL.programs[id]=program;return id}function _glCreateShader(shaderType){var id=GL.getNewId(GL.shaders);GL.shaders[id]=GLctx.createShader(shaderType);return id}function _glCullFace(x0){GLctx["cullFace"](x0)}function _glDeleteBuffers(n,buffers){for(var i=0;i>2];var buffer=GL.buffers[id];if(!buffer)continue;GLctx.deleteBuffer(buffer);buffer.name=0;GL.buffers[id]=null;if(id==GLctx.currentArrayBufferBinding)GLctx.currentArrayBufferBinding=0;if(id==GLctx.currentElementArrayBufferBinding)GLctx.currentElementArrayBufferBinding=0;if(id==GLctx.currentPixelPackBufferBinding)GLctx.currentPixelPackBufferBinding=0;if(id==GLctx.currentPixelUnpackBufferBinding)GLctx.currentPixelUnpackBufferBinding=0}}function _glDeleteFramebuffers(n,framebuffers){for(var i=0;i>2];var framebuffer=GL.framebuffers[id];if(!framebuffer)continue;GLctx.deleteFramebuffer(framebuffer);framebuffer.name=0;GL.framebuffers[id]=null}}function _glDeleteProgram(id){if(!id)return;var program=GL.programs[id];if(!program){GL.recordError(1281);return}GLctx.deleteProgram(program);program.name=0;GL.programs[id]=null}function _glDeleteQueries(n,ids){for(var i=0;i>2];var query=GL.queries[id];if(!query)continue;GLctx["deleteQuery"](query);GL.queries[id]=null}}function _glDeleteRenderbuffers(n,renderbuffers){for(var i=0;i>2];var renderbuffer=GL.renderbuffers[id];if(!renderbuffer)continue;GLctx.deleteRenderbuffer(renderbuffer);renderbuffer.name=0;GL.renderbuffers[id]=null}}function _glDeleteSamplers(n,samplers){for(var i=0;i>2];var sampler=GL.samplers[id];if(!sampler)continue;GLctx["deleteSampler"](sampler);sampler.name=0;GL.samplers[id]=null}}function _glDeleteShader(id){if(!id)return;var shader=GL.shaders[id];if(!shader){GL.recordError(1281);return}GLctx.deleteShader(shader);GL.shaders[id]=null}function _glDeleteSync(id){if(!id)return;var sync=GL.syncs[id];if(!sync){GL.recordError(1281);return}GLctx.deleteSync(sync);sync.name=0;GL.syncs[id]=null}function _glDeleteTextures(n,textures){for(var i=0;i>2];var texture=GL.textures[id];if(!texture)continue;GLctx.deleteTexture(texture);texture.name=0;GL.textures[id]=null}}function _glDeleteVertexArrays(n,vaos){for(var i=0;i>2];GLctx["deleteVertexArray"](GL.vaos[id]);GL.vaos[id]=null}}function _glDepthFunc(x0){GLctx["depthFunc"](x0)}function _glDepthMask(flag){GLctx.depthMask(!!flag)}function _glDepthRangef(x0,x1){GLctx["depthRange"](x0,x1)}function _glDetachShader(program,shader){GLctx.detachShader(GL.programs[program],GL.shaders[shader])}function _glDisable(x0){GLctx["disable"](x0)}function _glDisableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=false;GLctx.disableVertexAttribArray(index)}var tempFixedLengthArray=[];function _glDrawBuffers(n,bufs){var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx["drawBuffers"](bufArray)}function _glDrawElementsInstanced(mode,count,type,indices,primcount){GLctx["drawElementsInstanced"](mode,count,type,indices,primcount)}function _glDrawElements(mode,count,type,indices){var buf;if(!GLctx.currentElementArrayBufferBinding){var size=GL.calcBufLength(1,type,0,count);buf=GL.getTempIndexBuffer(size);GLctx.bindBuffer(34963,buf);GLctx.bufferSubData(34963,0,HEAPU8.subarray(indices,indices+size));indices=0}GL.preDrawHandleClientVertexAttribBindings(count);GLctx.drawElements(mode,count,type,indices);GL.postDrawHandleClientVertexAttribBindings(count);if(!GLctx.currentElementArrayBufferBinding){GLctx.bindBuffer(34963,null)}}function _glDrawRangeElements(mode,start,end,count,type,indices){_glDrawElements(mode,count,type,indices)}function _glEnable(x0){GLctx["enable"](x0)}function _glEnableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=true;GLctx.enableVertexAttribArray(index)}function _glEndQuery(x0){GLctx["endQuery"](x0)}function _glFenceSync(condition,flags){var sync=GLctx.fenceSync(condition,flags);if(sync){var id=GL.getNewId(GL.syncs);sync.name=id;GL.syncs[id]=sync;return id}else{return 0}}function _glFinish(){GLctx["finish"]()}function _glFlush(){GLctx["flush"]()}function _glFramebufferRenderbuffer(target,attachment,renderbuffertarget,renderbuffer){GLctx.framebufferRenderbuffer(target,attachment,renderbuffertarget,GL.renderbuffers[renderbuffer])}function _glFramebufferTexture2D(target,attachment,textarget,texture,level){GLctx.framebufferTexture2D(target,attachment,textarget,GL.textures[texture],level)}function _glFramebufferTextureLayer(target,attachment,texture,level,layer){GLctx.framebufferTextureLayer(target,attachment,GL.textures[texture],level,layer)}function _glFrontFace(x0){GLctx["frontFace"](x0)}function __glGenObject(n,buffers,createFunction,objectTable){for(var i=0;i>2]=id}}function _glGenBuffers(n,buffers){__glGenObject(n,buffers,"createBuffer",GL.buffers)}function _glGenFramebuffers(n,ids){__glGenObject(n,ids,"createFramebuffer",GL.framebuffers)}function _glGenQueries(n,ids){__glGenObject(n,ids,"createQuery",GL.queries)}function _glGenRenderbuffers(n,renderbuffers){__glGenObject(n,renderbuffers,"createRenderbuffer",GL.renderbuffers)}function _glGenSamplers(n,samplers){__glGenObject(n,samplers,"createSampler",GL.samplers)}function _glGenTextures(n,textures){__glGenObject(n,textures,"createTexture",GL.textures)}function _glGenVertexArrays(n,arrays){__glGenObject(n,arrays,"createVertexArray",GL.vaos)}function _glGenerateMipmap(x0){GLctx["generateMipmap"](x0)}function _glGetBufferSubData(target,offset,size,data){if(!data){GL.recordError(1281);return}size&&GLctx["getBufferSubData"](target,offset,HEAPU8,data,size)}function _glGetError(){var error=GLctx.getError()||GL.lastError;GL.lastError=0;return error}function writeI53ToI64(ptr,num){HEAPU32[ptr>>2]=num;HEAPU32[ptr+4>>2]=(num-HEAPU32[ptr>>2])/4294967296}function emscriptenWebGLGet(name_,p,type){if(!p){GL.recordError(1281);return}var ret=undefined;switch(name_){case 36346:ret=1;break;case 36344:if(type!=0&&type!=1){GL.recordError(1280)}return;case 34814:case 36345:ret=0;break;case 34466:var formats=GLctx.getParameter(34467);ret=formats?formats.length:0;break;case 33309:if(GL.currentContext.version<2){GL.recordError(1282);return}var exts=GLctx.getSupportedExtensions()||[];ret=2*exts.length;break;case 33307:case 33308:if(GL.currentContext.version<2){GL.recordError(1280);return}ret=name_==33307?3:0;break}if(ret===undefined){var result=GLctx.getParameter(name_);switch(typeof result){case"number":ret=result;break;case"boolean":ret=result?1:0;break;case"string":GL.recordError(1280);return;case"object":if(result===null){switch(name_){case 34964:case 35725:case 34965:case 36006:case 36007:case 32873:case 34229:case 36662:case 36663:case 35053:case 35055:case 36010:case 35097:case 35869:case 32874:case 36389:case 35983:case 35368:case 34068:{ret=0;break}default:{GL.recordError(1280);return}}}else if(result instanceof Float32Array||result instanceof Uint32Array||result instanceof Int32Array||result instanceof Array){for(var i=0;i>2]=result[i];break;case 2:HEAPF32[p+i*4>>2]=result[i];break;case 4:HEAP8[p+i>>0]=result[i]?1:0;break}}return}else{try{ret=result.name|0}catch(e){GL.recordError(1280);err("GL_INVALID_ENUM in glGet"+type+"v: Unknown object returned from WebGL getParameter("+name_+")! (error: "+e+")");return}}break;default:GL.recordError(1280);err("GL_INVALID_ENUM in glGet"+type+"v: Native code calling glGet"+type+"v("+name_+") and it returns "+result+" of type "+typeof result+"!");return}}switch(type){case 1:writeI53ToI64(p,ret);break;case 0:HEAP32[p>>2]=ret;break;case 2:HEAPF32[p>>2]=ret;break;case 4:HEAP8[p>>0]=ret?1:0;break}}function _glGetFloatv(name_,p){emscriptenWebGLGet(name_,p,2)}function _glGetIntegerv(name_,p){emscriptenWebGLGet(name_,p,0)}function _glGetProgramInfoLog(program,maxLength,length,infoLog){var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _glGetProgramiv(program,pname,p){if(!p){GL.recordError(1281);return}if(program>=GL.counter){GL.recordError(1281);return}program=GL.programs[program];if(pname==35716){var log=GLctx.getProgramInfoLog(program);if(log===null)log="(unknown error)";HEAP32[p>>2]=log.length+1}else if(pname==35719){if(!program.maxUniformLength){for(var i=0;i>2]=program.maxUniformLength}else if(pname==35722){if(!program.maxAttributeLength){for(var i=0;i>2]=program.maxAttributeLength}else if(pname==35381){if(!program.maxUniformBlockNameLength){for(var i=0;i>2]=program.maxUniformBlockNameLength}else{HEAP32[p>>2]=GLctx.getProgramParameter(program,pname)}}function _glGetQueryObjectuiv(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.queries[id];var param=GLctx["getQueryParameter"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret}function _glGetShaderInfoLog(shader,maxLength,length,infoLog){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _glGetShaderiv(shader,pname,p){if(!p){GL.recordError(1281);return}if(pname==35716){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var logLength=log?log.length+1:0;HEAP32[p>>2]=logLength}else if(pname==35720){var source=GLctx.getShaderSource(GL.shaders[shader]);var sourceLength=source?source.length+1:0;HEAP32[p>>2]=sourceLength}else{HEAP32[p>>2]=GLctx.getShaderParameter(GL.shaders[shader],pname)}}function stringToNewUTF8(jsString){var length=lengthBytesUTF8(jsString)+1;var cString=_malloc(length);stringToUTF8(jsString,cString,length);return cString}function _glGetString(name_){var ret=GL.stringCache[name_];if(!ret){switch(name_){case 7939:var exts=GLctx.getSupportedExtensions()||[];exts=exts.concat(exts.map(function(e){return"GL_"+e}));ret=stringToNewUTF8(exts.join(" "));break;case 7936:case 7937:case 37445:case 37446:var s=GLctx.getParameter(name_);if(!s){GL.recordError(1280)}ret=s&&stringToNewUTF8(s);break;case 7938:var glVersion=GLctx.getParameter(7938);if(true)glVersion="OpenGL ES 3.0 ("+glVersion+")";else{glVersion="OpenGL ES 2.0 ("+glVersion+")"}ret=stringToNewUTF8(glVersion);break;case 35724:var glslVersion=GLctx.getParameter(35724);var ver_re=/^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/;var ver_num=glslVersion.match(ver_re);if(ver_num!==null){if(ver_num[1].length==3)ver_num[1]=ver_num[1]+"0";glslVersion="OpenGL ES GLSL ES "+ver_num[1]+" ("+glslVersion+")"}ret=stringToNewUTF8(glslVersion);break;default:GL.recordError(1280)}GL.stringCache[name_]=ret}return ret}function _glGetUniformBlockIndex(program,uniformBlockName){return GLctx["getUniformBlockIndex"](GL.programs[program],UTF8ToString(uniformBlockName))}function jstoi_q(str){return parseInt(str)}function webglGetLeftBracePos(name){return name.slice(-1)=="]"&&name.lastIndexOf("[")}function webglPrepareUniformLocationsBeforeFirstUse(program){var uniformLocsById=program.uniformLocsById,uniformSizeAndIdsByName=program.uniformSizeAndIdsByName,i,j;if(!uniformLocsById){program.uniformLocsById=uniformLocsById={};program.uniformArrayNamesById={};for(i=0;i0?nm.slice(0,lb):nm;var id=program.uniformIdCounter;program.uniformIdCounter+=sz;uniformSizeAndIdsByName[arrayName]=[sz,id];for(j=0;j0){arrayIndex=jstoi_q(name.slice(leftBrace+1))>>>0;uniformBaseName=name.slice(0,leftBrace)}var sizeAndId=program.uniformSizeAndIdsByName[uniformBaseName];if(sizeAndId&&arrayIndex>2]}GLctx["invalidateFramebuffer"](target,list)}function _glLinkProgram(program){program=GL.programs[program];GLctx.linkProgram(program);program.uniformLocsById=0;program.uniformSizeAndIdsByName={}}function emscriptenWebGLGetBufferBinding(target){switch(target){case 34962:target=34964;break;case 34963:target=34965;break;case 35051:target=35053;break;case 35052:target=35055;break;case 35982:target=35983;break;case 36662:target=36662;break;case 36663:target=36663;break;case 35345:target=35368;break}var buffer=GLctx.getParameter(target);if(buffer)return buffer.name|0;else return 0}function emscriptenWebGLValidateMapBufferTarget(target){switch(target){case 34962:case 34963:case 36662:case 36663:case 35051:case 35052:case 35882:case 35982:case 35345:return true;default:return false}}function _glMapBufferRange(target,offset,length,access){if(access!=26&&access!=10){err("glMapBufferRange is only supported when access is MAP_WRITE|INVALIDATE_BUFFER");return 0}if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glMapBufferRange");return 0}var mem=_malloc(length);if(!mem)return 0;GL.mappedBuffers[emscriptenWebGLGetBufferBinding(target)]={offset:offset,length:length,mem:mem,access:access};return mem}function _glPixelStorei(pname,param){if(pname==3317){GL.unpackAlignment=param}GLctx.pixelStorei(pname,param)}function _glPolygonOffset(x0,x1){GLctx["polygonOffset"](x0,x1)}function computeUnpackAlignedImageSize(width,height,sizePerPixel,alignment){function roundedToNextMultipleOf(x,y){return x+y-1&-y}var plainRowSize=width*sizePerPixel;var alignedRowSize=roundedToNextMultipleOf(plainRowSize,alignment);return height*alignedRowSize}function __colorChannelsInGlTextureFormat(format){var colorChannels={5:3,6:4,8:2,29502:3,29504:4,26917:2,26918:2,29846:3,29847:4};return colorChannels[format-6402]||1}function heapObjectForWebGLType(type){type-=5120;if(type==0)return HEAP8;if(type==1)return HEAPU8;if(type==2)return HEAP16;if(type==4)return HEAP32;if(type==6)return HEAPF32;if(type==5||type==28922||type==28520||type==30779||type==30782)return HEAPU32;return HEAPU16}function heapAccessShiftForWebGLHeap(heap){return 31-Math.clz32(heap.BYTES_PER_ELEMENT)}function emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,internalFormat){var heap=heapObjectForWebGLType(type);var shift=heapAccessShiftForWebGLHeap(heap);var byteSize=1<>shift,pixels+bytes>>shift)}function _glReadPixels(x,y,width,height,format,type,pixels){if(true){if(GLctx.currentPixelPackBufferBinding){GLctx.readPixels(x,y,width,height,format,type,pixels)}else{var heap=heapObjectForWebGLType(type);GLctx.readPixels(x,y,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}return}var pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,format);if(!pixelData){GL.recordError(1280);return}GLctx.readPixels(x,y,width,height,format,type,pixelData)}function _glRenderbufferStorage(x0,x1,x2,x3){GLctx["renderbufferStorage"](x0,x1,x2,x3)}function _glRenderbufferStorageMultisample(x0,x1,x2,x3,x4){GLctx["renderbufferStorageMultisample"](x0,x1,x2,x3,x4)}function _glSamplerParameterf(sampler,pname,param){GLctx["samplerParameterf"](GL.samplers[sampler],pname,param)}function _glSamplerParameteri(sampler,pname,param){GLctx["samplerParameteri"](GL.samplers[sampler],pname,param)}function _glScissor(x0,x1,x2,x3){GLctx["scissor"](x0,x1,x2,x3)}function _glShaderSource(shader,count,string,length){var source=GL.getSource(shader,count,string,length);GLctx.shaderSource(GL.shaders[shader],source)}function _glStencilFuncSeparate(x0,x1,x2,x3){GLctx["stencilFuncSeparate"](x0,x1,x2,x3)}function _glStencilMaskSeparate(x0,x1){GLctx["stencilMaskSeparate"](x0,x1)}function _glStencilOpSeparate(x0,x1,x2,x3){GLctx["stencilOpSeparate"](x0,x1,x2,x3)}function _glTexParameterf(x0,x1,x2){GLctx["texParameterf"](x0,x1,x2)}function _glTexParameteri(x0,x1,x2){GLctx["texParameteri"](x0,x1,x2)}function _glTexStorage2D(x0,x1,x2,x3,x4){GLctx["texStorage2D"](x0,x1,x2,x3,x4)}function _glTexStorage3D(x0,x1,x2,x3,x4,x5){GLctx["texStorage3D"](x0,x1,x2,x3,x4,x5)}function _glTexSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,null)}return}var pixelData=null;if(pixels)pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,0);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixelData)}function _glTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels){if(GLctx.currentPixelUnpackBufferBinding){GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,null)}}function webglGetUniformLocation(location){var p=GLctx.currentProgram;if(p){var webglLoc=p.uniformLocsById[location];if(typeof webglLoc=="number"){p.uniformLocsById[location]=webglLoc=GLctx.getUniformLocation(p,p.uniformArrayNamesById[location]+(webglLoc>0?"["+webglLoc+"]":""))}return webglLoc}else{GL.recordError(1282)}}function _glUniform1i(location,v0){GLctx.uniform1i(webglGetUniformLocation(location),v0)}function _glUniformBlockBinding(program,uniformBlockIndex,uniformBlockBinding){program=GL.programs[program];GLctx["uniformBlockBinding"](program,uniformBlockIndex,uniformBlockBinding)}function _glUnmapBuffer(target){if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glUnmapBuffer");return 0}var buffer=emscriptenWebGLGetBufferBinding(target);var mapping=GL.mappedBuffers[buffer];if(!mapping){GL.recordError(1282);err("buffer was never mapped in glUnmapBuffer");return 0}GL.mappedBuffers[buffer]=null;if(!(mapping.access&16))if(true){GLctx.bufferSubData(target,mapping.offset,HEAPU8,mapping.mem,mapping.length)}else{GLctx.bufferSubData(target,mapping.offset,HEAPU8.subarray(mapping.mem,mapping.mem+mapping.length))}_free(mapping.mem);return 1}function _glUseProgram(program){program=GL.programs[program];GLctx.useProgram(program);GLctx.currentProgram=program}function _glVertexAttrib4f(x0,x1,x2,x3,x4){GLctx["vertexAttrib4f"](x0,x1,x2,x3,x4)}function _glVertexAttribI4ui(x0,x1,x2,x3,x4){GLctx["vertexAttribI4ui"](x0,x1,x2,x3,x4)}function _glVertexAttribIPointer(index,size,type,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=false;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribIPointer(index,size,type,stride,ptr)};return}cb.clientside=false;GLctx["vertexAttribIPointer"](index,size,type,stride,ptr)}function _glVertexAttribPointer(index,size,type,normalized,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=normalized;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribPointer(index,size,type,normalized,stride,ptr)};return}cb.clientside=false;GLctx.vertexAttribPointer(index,size,type,!!normalized,stride,ptr)}function _glViewport(x0,x1,x2,x3){GLctx["viewport"](x0,x1,x2,x3)}function _glWaitSync(sync,flags,timeoutLo,timeoutHi){GLctx.waitSync(GL.syncs[sync],flags,convertI32PairToI53(timeoutLo,timeoutHi))}function _setTempRet0(val){setTempRet0(val)}function __isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}function __arraySum(array,index){var sum=0;for(var i=0;i<=index;sum+=array[i++]){}return sum}var __MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31];var __MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31];function __addDays(date,days){var newDate=new Date(date.getTime());while(days>0){var leap=__isLeapYear(newDate.getFullYear());var currentMonth=newDate.getMonth();var daysInCurrentMonth=(leap?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR)[currentMonth];if(days>daysInCurrentMonth-newDate.getDate()){days-=daysInCurrentMonth-newDate.getDate()+1;newDate.setDate(1);if(currentMonth<11){newDate.setMonth(currentMonth+1)}else{newDate.setMonth(0);newDate.setFullYear(newDate.getFullYear()+1)}}else{newDate.setDate(newDate.getDate()+days);return newDate}}return newDate}function _strftime(s,maxsize,format,tm){var tm_zone=HEAP32[tm+40>>2];var date={tm_sec:HEAP32[tm>>2],tm_min:HEAP32[tm+4>>2],tm_hour:HEAP32[tm+8>>2],tm_mday:HEAP32[tm+12>>2],tm_mon:HEAP32[tm+16>>2],tm_year:HEAP32[tm+20>>2],tm_wday:HEAP32[tm+24>>2],tm_yday:HEAP32[tm+28>>2],tm_isdst:HEAP32[tm+32>>2],tm_gmtoff:HEAP32[tm+36>>2],tm_zone:tm_zone?UTF8ToString(tm_zone):""};var pattern=UTF8ToString(format);var EXPANSION_RULES_1={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var rule in EXPANSION_RULES_1){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_1[rule])}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];function leadingSomething(value,digits,character){var str=typeof value=="number"?value.toString():value||"";while(str.length0?1:0}var compare;if((compare=sgn(date1.getFullYear()-date2.getFullYear()))===0){if((compare=sgn(date1.getMonth()-date2.getMonth()))===0){compare=sgn(date1.getDate()-date2.getDate())}}return compare}function getFirstWeekStartDate(janFourth){switch(janFourth.getDay()){case 0:return new Date(janFourth.getFullYear()-1,11,29);case 1:return janFourth;case 2:return new Date(janFourth.getFullYear(),0,3);case 3:return new Date(janFourth.getFullYear(),0,2);case 4:return new Date(janFourth.getFullYear(),0,1);case 5:return new Date(janFourth.getFullYear()-1,11,31);case 6:return new Date(janFourth.getFullYear()-1,11,30)}}function getWeekBasedYear(date){var thisDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);var janFourthThisYear=new Date(thisDate.getFullYear(),0,4);var janFourthNextYear=new Date(thisDate.getFullYear()+1,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);if(compareByDay(firstWeekStartThisYear,thisDate)<=0){if(compareByDay(firstWeekStartNextYear,thisDate)<=0){return thisDate.getFullYear()+1}else{return thisDate.getFullYear()}}else{return thisDate.getFullYear()-1}}var EXPANSION_RULES_2={"%a":function(date){return WEEKDAYS[date.tm_wday].substring(0,3)},"%A":function(date){return WEEKDAYS[date.tm_wday]},"%b":function(date){return MONTHS[date.tm_mon].substring(0,3)},"%B":function(date){return MONTHS[date.tm_mon]},"%C":function(date){var year=date.tm_year+1900;return leadingNulls(year/100|0,2)},"%d":function(date){return leadingNulls(date.tm_mday,2)},"%e":function(date){return leadingSomething(date.tm_mday,2," ")},"%g":function(date){return getWeekBasedYear(date).toString().substring(2)},"%G":function(date){return getWeekBasedYear(date)},"%H":function(date){return leadingNulls(date.tm_hour,2)},"%I":function(date){var twelveHour=date.tm_hour;if(twelveHour==0)twelveHour=12;else if(twelveHour>12)twelveHour-=12;return leadingNulls(twelveHour,2)},"%j":function(date){return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900)?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,date.tm_mon-1),3)},"%m":function(date){return leadingNulls(date.tm_mon+1,2)},"%M":function(date){return leadingNulls(date.tm_min,2)},"%n":function(){return"\n"},"%p":function(date){if(date.tm_hour>=0&&date.tm_hour<12){return"AM"}else{return"PM"}},"%S":function(date){return leadingNulls(date.tm_sec,2)},"%t":function(){return"\t"},"%u":function(date){return date.tm_wday||7},"%U":function(date){var days=date.tm_yday+7-date.tm_wday;return leadingNulls(Math.floor(days/7),2)},"%V":function(date){var val=Math.floor((date.tm_yday+7-(date.tm_wday+6)%7)/7);if((date.tm_wday+371-date.tm_yday-2)%7<=2){val++}if(!val){val=52;var dec31=(date.tm_wday+7-date.tm_yday-1)%7;if(dec31==4||dec31==5&&__isLeapYear(date.tm_year%400-1)){val++}}else if(val==53){var jan1=(date.tm_wday+371-date.tm_yday)%7;if(jan1!=4&&(jan1!=3||!__isLeapYear(date.tm_year)))val=1}return leadingNulls(val,2)},"%w":function(date){return date.tm_wday},"%W":function(date){var days=date.tm_yday+7-(date.tm_wday+6)%7;return leadingNulls(Math.floor(days/7),2)},"%y":function(date){return(date.tm_year+1900).toString().substring(2)},"%Y":function(date){return date.tm_year+1900},"%z":function(date){var off=date.tm_gmtoff;var ahead=off>=0;off=Math.abs(off)/60;off=off/60*100+off%60;return(ahead?"+":"-")+String("0000"+off).slice(-4)},"%Z":function(date){return date.tm_zone},"%%":function(){return"%"}};pattern=pattern.replace(/%%/g,"\0\0");for(var rule in EXPANSION_RULES_2){if(pattern.includes(rule)){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_2[rule](date))}}pattern=pattern.replace(/\0\0/g,"%");var bytes=intArrayFromString(pattern,false);if(bytes.length>maxsize){return 0}writeArrayToMemory(bytes,s);return bytes.length-1}function _strftime_l(s,maxsize,format,tm){return _strftime(s,maxsize,format,tm)}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_embind();init_RegisteredPointer();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();var GLctx;for(var i=0;i<32;++i)tempFixedLengthArray.push(new Array(i));function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var asmLibraryArg={"Ga":___syscall_fcntl64,"yb":___syscall_ioctl,"zb":___syscall_openat,"ub":___syscall_stat64,"x":__embind_finalize_value_array,"m":__embind_finalize_value_object,"nb":__embind_register_bigint,"Eb":__embind_register_bool,"e":__embind_register_class,"l":__embind_register_class_class_function,"k":__embind_register_class_constructor,"a":__embind_register_class_function,"w":__embind_register_class_property,"Db":__embind_register_emval,"h":__embind_register_enum,"b":__embind_register_enum_value,"Ia":__embind_register_float,"T":__embind_register_function,"D":__embind_register_integer,"p":__embind_register_memory_view,"Ha":__embind_register_std_string,"ea":__embind_register_std_wstring,"y":__embind_register_value_array,"f":__embind_register_value_array_element,"n":__embind_register_value_object,"d":__embind_register_value_object_field,"Fb":__embind_register_void,"Bb":__emscripten_get_now_is_monotonic,"s":__emval_as,"g":__emval_decref,"t":__emval_get_property,"S":__emval_incref,"H":__emval_new_cstring,"r":__emval_run_destructors,"u":__emval_take_value,"c":_abort,"ca":_emscripten_asm_const_int,"qb":_emscripten_get_heap_max,"Ab":_emscripten_get_now,"Cb":_emscripten_memcpy_big,"tb":_emscripten_resize_heap,"vb":_environ_get,"wb":_environ_sizes_get,"da":_fd_close,"xb":_fd_read,"mb":_fd_seek,"Fa":_fd_write,"rb":_getentropy,"i":_glActiveTexture,"W":_glAttachShader,"Wb":_glBeginQuery,"o":_glBindBuffer,"ta":_glBindBufferBase,"U":_glBindBufferRange,"q":_glBindFramebuffer,"Na":_glBindRenderbuffer,"aa":_glBindSampler,"j":_glBindTexture,"A":_glBindVertexArray,"wa":_glBlendEquationSeparate,"va":_glBlendFuncSeparate,"fa":_glBlitFramebuffer,"K":_glBufferData,"ha":_glBufferSubData,"Kb":_glClearBufferfi,"E":_glClearBufferfv,"Jb":_glClearBufferiv,"ob":_glClientWaitSync,"_":_glColorMask,"eb":_glCompileShader,"Ka":_glCompressedTexSubImage2D,"Ja":_glCompressedTexSubImage3D,"Ra":_glCopyBufferSubData,"db":_glCreateProgram,"gb":_glCreateShader,"xa":_glCullFace,"$":_glDeleteBuffers,"ia":_glDeleteFramebuffers,"oa":_glDeleteProgram,"Va":_glDeleteQueries,"ja":_glDeleteRenderbuffers,"za":_glDeleteSamplers,"X":_glDeleteShader,"L":_glDeleteSync,"ka":_glDeleteTextures,"ib":_glDeleteVertexArrays,"ba":_glDepthFunc,"Z":_glDepthMask,"qa":_glDepthRangef,"pa":_glDetachShader,"v":_glDisable,"Nb":_glDisableVertexAttribArray,"Xa":_glDrawBuffers,"Xb":_glDrawElementsInstanced,"Yb":_glDrawRangeElements,"G":_glEnable,"Qb":_glEnableVertexAttribArray,"Vb":_glEndQuery,"V":_glFenceSync,"Da":_glFinish,"lb":_glFlush,"M":_glFramebufferRenderbuffer,"Pa":_glFramebufferTexture2D,"Oa":_glFramebufferTextureLayer,"ya":_glFrontFace,"O":_glGenBuffers,"la":_glGenFramebuffers,"Wa":_glGenQueries,"Y":_glGenRenderbuffers,"Ba":_glGenSamplers,"N":_glGenTextures,"hb":_glGenVertexArrays,"Ta":_glGenerateMipmap,"Ib":_glGetBufferSubData,"Ca":_glGetError,"kb":_glGetFloatv,"C":_glGetIntegerv,"ab":_glGetProgramInfoLog,"na":_glGetProgramiv,"Ub":_glGetQueryObjectuiv,"bb":_glGetShaderInfoLog,"ma":_glGetShaderiv,"I":_glGetString,"$a":_glGetUniformBlockIndex,"Za":_glGetUniformLocation,"jb":_glHint,"ga":_glInvalidateFramebuffer,"cb":_glLinkProgram,"Hb":_glMapBufferRange,"B":_glPixelStorei,"ua":_glPolygonOffset,"Sa":_glReadPixels,"Lb":_glRenderbufferStorage,"Mb":_glRenderbufferStorageMultisample,"Aa":_glSamplerParameterf,"J":_glSamplerParameteri,"sa":_glScissor,"fb":_glShaderSource,"Q":_glStencilFuncSeparate,"F":_glStencilMaskSeparate,"P":_glStencilOpSeparate,"Ua":_glTexParameterf,"z":_glTexParameteri,"Tb":_glTexStorage2D,"Qa":_glTexStorage3D,"Ma":_glTexSubImage2D,"La":_glTexSubImage3D,"Ya":_glUniform1i,"_a":_glUniformBlockBinding,"Gb":_glUnmapBuffer,"R":_glUseProgram,"Ob":_glVertexAttrib4f,"Pb":_glVertexAttribI4ui,"Sb":_glVertexAttribIPointer,"Rb":_glVertexAttribPointer,"ra":_glViewport,"pb":_glWaitSync,"Ea":_setTempRet0,"sb":_strftime_l};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["_b"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["ac"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["bc"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["cc"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["dc"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["ec"]).apply(null,arguments)};var dynCall_iiiiij=Module["dynCall_iiiiij"]=function(){return(dynCall_iiiiij=Module["dynCall_iiiiij"]=Module["asm"]["fc"]).apply(null,arguments)};var dynCall_jii=Module["dynCall_jii"]=function(){return(dynCall_jii=Module["dynCall_jii"]=Module["asm"]["gc"]).apply(null,arguments)};var dynCall_iiij=Module["dynCall_iiij"]=function(){return(dynCall_iiij=Module["dynCall_iiij"]=Module["asm"]["hc"]).apply(null,arguments)};var dynCall_vij=Module["dynCall_vij"]=function(){return(dynCall_vij=Module["dynCall_vij"]=Module["asm"]["ic"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["jc"]).apply(null,arguments)};var dynCall_viijii=Module["dynCall_viijii"]=function(){return(dynCall_viijii=Module["dynCall_viijii"]=Module["asm"]["kc"]).apply(null,arguments)};var dynCall_iiiiijj=Module["dynCall_iiiiijj"]=function(){return(dynCall_iiiiijj=Module["dynCall_iiiiijj"]=Module["asm"]["lc"]).apply(null,arguments)};var dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=function(){return(dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=Module["asm"]["mc"]).apply(null,arguments)};var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;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);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run(); +var Module=typeof Filament!="undefined"?Filament:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};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";var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;function logExceptionOnExit(e){if(e instanceof ExitStatus)return;let toLog=e;err("exiting due to exception: "+toLog)}var fs;var nodePath;var requireNodeFS;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}requireNodeFS=()=>{if(!nodePath){fs=require("fs");nodePath=require("path")}};read_=function shell_read(filename,binary){requireNodeFS();filename=nodePath["normalize"](filename);return fs.readFileSync(filename,binary?undefined:"utf8")};readBinary=filename=>{var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret};readAsync=(filename,onload,onerror)=>{requireNodeFS();filename=nodePath["normalize"](filename);fs.readFile(filename,function(err,data){if(err)onerror(err);else onload(data.buffer)})};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",function(reason){throw reason});quit_=(status,toThrow)=>{if(keepRuntimeAlive()){process["exitCode"]=status;throw toThrow}logExceptionOnExit(toThrow);process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}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(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};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,onload,onerror)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=title=>document.title=title}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.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 tempRet0=0;var setTempRet0=value=>{tempRet0=value};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort(text)}}var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf8"):undefined;function 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))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=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}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf-16le"):undefined;function 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))}else{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}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function 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}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=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}function lengthBytesUTF32(str){var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function keepRuntimeAlive(){return noExitRuntime}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;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();FS.ignorePermissions=false;TTY.init();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 getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){{if(Module["onAbort"]){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,";function isDataURI(filename){return filename.startsWith(dataURIPrefix)}function isFileURI(filename){return filename.startsWith("file://")}var wasmBinaryFile;wasmBinaryFile="filament.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch=="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["Zb"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["$b"];addOnInit(Module["asm"]["_b"]);removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(function(instance){return instance}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiationResult,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiationResult)})})}else{return instantiateArrayBuffer(receiveInstantiationResult)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var tempDouble;var tempI64;var ASM_CONSTS={1007244:()=>{const options=window.filament_glOptions;const context=window.filament_glContext;const handle=GL.registerContext(context,options);window.filament_contextHandle=handle;GL.makeContextCurrent(handle)},1007458:()=>{const handle=window.filament_contextHandle;GL.makeContextCurrent(handle)},1007539:($0,$1,$2,$3,$4,$5)=>{const fn=Emval.toValue($0);fn({"renderable":Emval.toValue($1),"depth":$2,"fragCoords":[$3,$4,$5]})}};function callRuntimeCallbacks(callbacks){while(callbacks.length>0){callbacks.shift()(Module)}}var wasmTableMirror=[];function getWasmTableEntry(funcPtr){var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:path=>{if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:(l,r)=>{return PATH.normalize(l+"/"+r)}};function getRandomDevice(){if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){var randomBuffer=new Uint8Array(1);return function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");return function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}return function(){abort("randomDevice")}}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){abort()}var MEMFS={ops_table:null,mount:function(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode:function(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}if(!MEMFS.ops_table){MEMFS.ops_table={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}}}var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray:function(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage:function(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr:function(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr:function(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup:function(parent,name){throw FS.genericErrors[44]},mknod:function(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename:function(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp;old_node.parent=new_dir},unlink:function(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir:function(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir:function(node){var entries=[".",".."];for(var key in node.contents){if(!node.contents.hasOwnProperty(key)){continue}entries.push(key)}return entries},symlink:function(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink:function(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read:function(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{path=PATH_FS.resolve(FS.cwd(),path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};opts=Object.assign(defaults,opts);if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(p=>!!p),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:node=>{var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:(parentid,name)=>{var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:node=>{var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:node=>{var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:(parent,name)=>{var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:(parent,name,mode,rdev)=>{var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:node=>{FS.hashRemoveNode(node)},isRoot:node=>{return node===node.parent},isMountpoint:node=>{return!!node.mounted},isFile:mode=>{return(mode&61440)===32768},isDir:mode=>{return(mode&61440)===16384},isLink:mode=>{return(mode&61440)===40960},isChrdev:mode=>{return(mode&61440)===8192},isBlkdev:mode=>{return(mode&61440)===24576},isFIFO:mode=>{return(mode&61440)===4096},isSocket:mode=>{return(mode&49152)===49152},flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:str=>{var flags=FS.flagModes[str];if(typeof flags=="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:flag=>{var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:(node,perms)=>{if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup:dir=>{var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:(dir,name)=>{try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:(dir,name,isdir)=>{var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:(node,flags)=>{if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:(fd_start=0,fd_end=FS.MAX_OPEN_FDS)=>{for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:fd=>FS.streams[fd],createStream:(stream,fd_start,fd_end)=>{if(!FS.FSStream){FS.FSStream=function(){this.shared={}};FS.FSStream.prototype={};Object.defineProperties(FS.FSStream.prototype,{object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}},flags:{get:function(){return this.shared.flags},set:function(val){this.shared.flags=val}},position:{get:function(){return this.shared.position},set:function(val){this.shared.position=val}}})}stream=Object.assign(new FS.FSStream,stream);var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:fd=>{FS.streams[fd]=null},chrdev_stream_ops:{open:stream=>{var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:()=>{throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice:(dev,ops)=>{FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts:mount=>{var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:(populate,callback)=>{if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:(type,opts,mountpoint)=>{var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:mountpoint=>{var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:(parent,name)=>{return parent.node_ops.lookup(parent,name)},mknod:(path,mode,dev)=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:(path,mode)=>{mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:(path,mode)=>{mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:(path,mode)=>{var dirs=path.split("/");var d="";for(var i=0;i{if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink:(oldpath,newpath)=>{if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename:(old_path,new_path)=>{var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name)}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir:path=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir:path=>{var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node.node_ops.readdir){throw new FS.ErrnoError(54)}return node.node_ops.readdir(node)},unlink:path=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink:path=>{var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return PATH_FS.resolve(FS.getPath(link.parent),link.node_ops.readlink(link))},stat:(path,dontFollow)=>{var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;if(!node){throw new FS.ErrnoError(44)}if(!node.node_ops.getattr){throw new FS.ErrnoError(63)}return node.node_ops.getattr(node)},lstat:path=>{return FS.stat(path,true)},chmod:(path,mode,dontFollow)=>{var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{mode:mode&4095|node.mode&~4095,timestamp:Date.now()})},lchmod:(path,mode)=>{FS.chmod(path,mode,true)},fchmod:(fd,mode)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chmod(stream.node,mode)},chown:(path,uid,gid,dontFollow)=>{var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{timestamp:Date.now()})},lchown:(path,uid,gid)=>{FS.chown(path,uid,gid,true)},fchown:(fd,uid,gid)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chown(stream.node,uid,gid)},truncate:(path,len)=>{if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}node.node_ops.setattr(node,{size:len,timestamp:Date.now()})},ftruncate:(fd,len)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.truncate(stream.node,len)},utime:(path,atime,mtime)=>{var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;node.node_ops.setattr(node,{timestamp:Math.max(atime,mtime)})},open:(path,flags,mode)=>{if(path===""){throw new FS.ErrnoError(44)}flags=typeof flags=="string"?FS.modeStringToFlags(flags):flags;mode=typeof mode=="undefined"?438:mode;if(flags&64){mode=mode&4095|32768}else{mode=0}var node;if(typeof path=="object"){node=path}else{path=PATH.normalize(path);try{var lookup=FS.lookupPath(path,{follow:!(flags&131072)});node=lookup.node}catch(e){}}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else{node=FS.mknod(path,mode,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node:node,path:FS.getPath(node),flags:flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(Module["logReadFiles"]&&!(flags&1)){if(!FS.readFiles)FS.readFiles={};if(!(path in FS.readFiles)){FS.readFiles[path]=1}}return stream},close:stream=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed:stream=>{return stream.fd===null},llseek:(stream,offset,whence)=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read:(stream,buffer,offset,length,position)=>{if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write:(stream,buffer,offset,length,position,canOwn)=>{if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},allocate:(stream,offset,length)=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(offset<0||length<=0){throw new FS.ErrnoError(28)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(!FS.isFile(stream.node.mode)&&!FS.isDir(stream.node.mode)){throw new FS.ErrnoError(43)}if(!stream.stream_ops.allocate){throw new FS.ErrnoError(138)}stream.stream_ops.allocate(stream,offset,length)},mmap:(stream,length,position,prot,flags)=>{if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync:(stream,buffer,offset,length,mmapFlags)=>{if(!stream||!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},munmap:stream=>0,ioctl:(stream,cmd,arg)=>{if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile:(path,opts={})=>{opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){throw new Error('Invalid encoding type "'+opts.encoding+'"')}var ret;var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){ret=UTF8ArrayToString(buf,0)}else if(opts.encoding==="binary"){ret=buf}FS.close(stream);return ret},writeFile:(path,data,opts={})=>{opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);if(typeof data=="string"){var buf=new Uint8Array(lengthBytesUTF8(data)+1);var actualNumBytes=stringToUTF8Array(data,buf,0,buf.length);FS.write(stream,buf,0,actualNumBytes,undefined,opts.canOwn)}else if(ArrayBuffer.isView(data)){FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn)}else{throw new Error("Unsupported data type")}FS.close(stream)},cwd:()=>FS.currentPath,chdir:path=>{var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories:()=>{FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices:()=>{FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var random_device=getRandomDevice();FS.createDevice("/dev","random",random_device);FS.createDevice("/dev","urandom",random_device);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories:()=>{FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount:()=>{var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup:(parent,name)=>{var fd=+name;var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams:()=>{if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"])}else{FS.symlink("/dev/tty","/dev/stdin")}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"])}else{FS.symlink("/dev/tty","/dev/stdout")}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"])}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},ensureErrnoError:()=>{if(FS.ErrnoError)return;FS.ErrnoError=function ErrnoError(errno,node){this.node=node;this.setErrno=function(errno){this.errno=errno};this.setErrno(errno);this.message="FS error"};FS.ErrnoError.prototype=new Error;FS.ErrnoError.prototype.constructor=FS.ErrnoError;[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack=""})},staticInit:()=>{FS.ensureErrnoError();FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={"MEMFS":MEMFS}},init:(input,output,error)=>{FS.init.initialized=true;FS.ensureErrnoError();Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit:()=>{FS.init.initialized=false;for(var i=0;i{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode},findObject:(path,dontResolveLastLink)=>{var ret=FS.analyzePath(path,dontResolveLastLink);if(ret.exists){return ret.object}else{return null}},analyzePath:(path,dontResolveLastLink)=>{try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath:(parent,path,canRead,canWrite)=>{parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){}parent=current}return current},createFile:(parent,name,properties,canRead,canWrite)=>{var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile:(parent,name,data,canRead,canWrite,canOwn)=>{var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS.getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){if(typeof data=="string"){var arr=new Array(data.length);for(var i=0,len=data.length;i{var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(!!input,!!output);if(!FS.createDevice.major)FS.createDevice.major=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open:stream=>{stream.seekable=false},close:stream=>{if(output&&output.buffer&&output.buffer.length){output(10)}},read:(stream,buffer,offset,length,pos)=>{var bytesRead=0;for(var i=0;i{for(var i=0;i{if(obj.isDevice||obj.isFolder||obj.link||obj.contents)return true;if(typeof XMLHttpRequest!="undefined"){throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.")}else if(read_){try{obj.contents=intArrayFromString(read_(obj.url),true);obj.usedBytes=obj.contents.length}catch(e){throw new FS.ErrnoError(29)}}else{throw new Error("Cannot load without read() or XMLHttpRequest.")}},createLazyFile:(parent,name,url,canRead,canWrite)=>{function LazyUint8Array(){this.lengthKnown=false;this.chunks=[]}LazyUint8Array.prototype.get=function LazyUint8Array_get(idx){if(idx>this.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr:ptr,allocated:true}};node.stream_ops=stream_ops;return node},createPreloadedFile:(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency("cp "+fullname);function processData(byteArray){function finish(byteArray){if(preFinish)preFinish();if(!dontCreateFile){FS.createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}if(onload)onload();removeRunDependency(dep)}if(Browser.handledByPreloadPlugin(byteArray,fullname,finish,()=>{if(onerror)onerror();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,byteArray=>processData(byteArray),onerror)}else{processData(url)}},indexedDB:()=>{return window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB},DB_NAME:()=>{return"EM_FS_"+window.location.pathname},DB_VERSION:20,DB_STORE_NAME:"FILE_DATA",saveFilesToDB:(paths,onload,onerror)=>{onload=onload||(()=>{});onerror=onerror||(()=>{});var indexedDB=FS.indexedDB();try{var openRequest=indexedDB.open(FS.DB_NAME(),FS.DB_VERSION)}catch(e){return onerror(e)}openRequest.onupgradeneeded=()=>{out("creating db");var db=openRequest.result;db.createObjectStore(FS.DB_STORE_NAME)};openRequest.onsuccess=()=>{var db=openRequest.result;var transaction=db.transaction([FS.DB_STORE_NAME],"readwrite");var files=transaction.objectStore(FS.DB_STORE_NAME);var ok=0,fail=0,total=paths.length;function finish(){if(fail==0)onload();else onerror()}paths.forEach(path=>{var putRequest=files.put(FS.analyzePath(path).object.contents,path);putRequest.onsuccess=()=>{ok++;if(ok+fail==total)finish()};putRequest.onerror=()=>{fail++;if(ok+fail==total)finish()}});transaction.onerror=onerror};openRequest.onerror=onerror},loadFilesFromDB:(paths,onload,onerror)=>{onload=onload||(()=>{});onerror=onerror||(()=>{});var indexedDB=FS.indexedDB();try{var openRequest=indexedDB.open(FS.DB_NAME(),FS.DB_VERSION)}catch(e){return onerror(e)}openRequest.onupgradeneeded=onerror;openRequest.onsuccess=()=>{var db=openRequest.result;try{var transaction=db.transaction([FS.DB_STORE_NAME],"readonly")}catch(e){onerror(e);return}var files=transaction.objectStore(FS.DB_STORE_NAME);var ok=0,fail=0,total=paths.length;function finish(){if(fail==0)onload();else onerror()}paths.forEach(path=>{var getRequest=files.get(path);getRequest.onsuccess=()=>{if(FS.analyzePath(path).exists){FS.unlink(path)}FS.createDataFile(PATH.dirname(path),PATH.basename(path),getRequest.result,true,true,true);ok++;if(ok+fail==total)finish()};getRequest.onerror=()=>{fail++;if(ok+fail==total)finish()}});transaction.onerror=onerror};openRequest.onerror=onerror}};var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt:function(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=FS.getStream(dirfd);if(!dirstream)throw new FS.ErrnoError(8);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat:function(func,path,buf){try{var stat=func(path)}catch(e){if(e&&e.node&&PATH.normalize(path)!==PATH.normalize(FS.getPath(e.node))){return-54}throw e}HEAP32[buf>>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.createStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 5:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 6:case 7:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?SYSCALLS.get():0;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.stat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}var tupleRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAP32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function 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 createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+' "use strict";'+" return body.apply(this, arguments);\n"+"};\n")(body)}function 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 InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function 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{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)}}function __embind_finalize_value_array(rawTupleType){var reg=tupleRegistrations[rawTupleType];delete tupleRegistrations[rawTupleType];var elements=reg.elements;var elementsLength=elements.length;var elementTypes=elements.map(function(elt){return elt.getterReturnType}).concat(elements.map(function(elt){return elt.setterArgumentType}));var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;whenDependentTypesAreResolved([rawTupleType],elementTypes,function(elementTypes){elements.forEach((elt,i)=>{var getterReturnType=elementTypes[i];var getter=elt.getter;var getterContext=elt.getterContext;var setterArgumentType=elementTypes[i+elementsLength];var setter=elt.setter;var setterContext=elt.setterContext;elt.read=ptr=>{return getterReturnType["fromWireType"](getter(getterContext,ptr))};elt.write=(ptr,o)=>{var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,o));runDestructors(destructors)}});return[{name:reg.name,"fromWireType":function(ptr){var rv=new Array(elementsLength);for(var i=0;ifield.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=>{return getterReturnType["fromWireType"](getter(getterContext,ptr))},write:(ptr,o)=>{var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,o));runDestructors(destructors)}}});return[{name:reg.name,"fromWireType":function(ptr){var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},"toWireType":function(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":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:rawDestructor}]})}function __embind_register_bigint(primitiveType,name,size,minRange,maxRange){}function getShiftFromSize(size){switch(size){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+size)}}function 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=undefined;function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret}var BindingError=undefined;function throwBindingError(message){throw new BindingError(message)}function registerType(rawType,registeredInstance,options={}){if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}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 __embind_register_bool(rawType,name,size,trueValue,falseValue){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(wt){return!!wt},"toWireType":function(destructors,o){return o?trueValue:falseValue},"argPackAdvance":8,"readValueFromPointer":function(pointer){var heap;if(size===1){heap=HEAP8}else if(size===2){heap=HEAP16}else if(size===4){heap=HEAP32}else{throw new TypeError("Unknown boolean type size: "+name)}return this["fromWireType"](heap[pointer>>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;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}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationRegistry=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function 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={};function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}var delayFunction=undefined;function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function 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}}))}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})}}function 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)}function ClassHandle_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}}function ClassHandle_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}}function ClassHandle_isDeleted(){return!this.$$.ptr}function ClassHandle_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 init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function 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}}}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=[]}function 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.$$){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(function(){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}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["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}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){var f=Module["dynCall_"+sig];return args&&args.length?f.apply(null,[ptr].concat(args)):f.call(null,ptr)}function dynCall(sig,ptr,args){if(sig.includes("j")){return dynCallLegacy(sig,ptr,args)}var rtn=getWasmTableEntry(ptr).apply(null,args);return rtn}function getDynCaller(sig,ptr){var argCache=[];return function(){argCache.length=0;Object.assign(argCache,arguments);return dynCall(sig,ptr,argCache)}}function 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 UnboundTypeError=undefined;function getTypeName(type){var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv}function 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([", "]))}function __embind_register_class(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor){name=readLatin1String(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);if(upcast){upcast=embind__requireFunction(upcastSignature,upcast)}if(downcast){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]:[],function(base){base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(legalFunctionName,function(){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[arguments.length];if(undefined===body){throw new BindingError("Tried to invoke ctor of "+name+" with invalid number of parameters ("+arguments.length+") - expected ("+Object.keys(registeredClass.constructor_body).toString()+") parameters instead!")}return body.apply(this,arguments)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})}function new_(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 craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc){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=false;for(var i=1;i0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i>2])}return array}function __embind_register_class_class_function(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,fn){var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName=classType.name+"."+methodName;function unboundTypesHandler(){throwUnboundTypeError("Cannot call "+humanName+" due to unbound types",rawArgTypes)}if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}var proto=classType.registeredClass.constructor;if(undefined===proto[methodName]){unboundTypesHandler.argCount=argCount-1;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-1]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));var func=craftInvokerFunction(humanName,invokerArgsArray,null,rawInvoker,fn);if(undefined===proto[methodName].overloadTable){func.argCount=argCount-1;proto[methodName]=func}else{proto[methodName].overloadTable[argCount-1]=func}return[]});return[]})}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);whenDependentTypesAreResolved([],[rawClassType],function(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,function(argTypes){argTypes.splice(1,0,null);classType.registeredClass.constructor_body[argCount-1]=craftInvokerFunction(humanName,argTypes,null,invoker,rawConstructor);return[]});return[]})}function __embind_register_class_function(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual){var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],function(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,function(argTypes){var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})}function validateThis(this_,classType,humanName){if(!(this_ instanceof Object)){throwBindingError(humanName+' with invalid "this": '+this_)}if(!(this_ instanceof classType.registeredClass.constructor)){throwBindingError(humanName+' incompatible with "this" of type '+this_.constructor.name)}if(!this_.$$.ptr){throwBindingError("cannot call emscripten binding method "+humanName+" on deleted object")}return upcastPointer(this_.$$.ptr,this_.$$.ptrType.registeredClass,classType.registeredClass)}function __embind_register_class_property(classType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){fieldName=readLatin1String(fieldName);getter=embind__requireFunction(getterSignature,getter);whenDependentTypesAreResolved([],[classType],function(classType){classType=classType[0];var humanName=classType.name+"."+fieldName;var desc={get:function(){throwUnboundTypeError("Cannot access "+humanName+" due to unbound types",[getterReturnType,setterArgumentType])},enumerable:true,configurable:true};if(setter){desc.set=()=>{throwUnboundTypeError("Cannot access "+humanName+" due to unbound types",[getterReturnType,setterArgumentType])}}else{desc.set=v=>{throwBindingError(humanName+" is a read-only property")}}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);whenDependentTypesAreResolved([],setter?[getterReturnType,setterArgumentType]:[getterReturnType],function(types){var getterReturnType=types[0];var desc={get:function(){var ptr=validateThis(this,classType,humanName+" getter");return getterReturnType["fromWireType"](getter(getterContext,ptr))},enumerable:true};if(setter){setter=embind__requireFunction(setterSignature,setter);var setterArgumentType=types[1];desc.set=function(v){var ptr=validateThis(this,classType,humanName+" setter");var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,v));runDestructors(destructors)}}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);return[]});return[]})}var emval_free_list=[];var emval_handle_array=[{},{value:undefined},{value:null},{value:true},{value:false}];function __emval_decref(handle){if(handle>4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i{if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value},toHandle:value=>{switch(value){case undefined:return 1;case null:return 2;case true:return 3;case false:return 4;default:{var handle=emval_free_list.length?emval_free_list.pop():emval_handle_array.length;emval_handle_array[handle]={refcount:1,value:value};return handle}}}};function __embind_register_emval(rawType,name){name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(handle){var rv=Emval.toValue(handle);__emval_decref(handle);return rv},"toWireType":function(destructors,value){return Emval.toHandle(value)},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:null})}function enumReadValueFromPointer(name,shift,signed){switch(shift){case 0:return function(pointer){var heap=signed?HEAP8:HEAPU8;return this["fromWireType"](heap[pointer])};case 1:return function(pointer){var heap=signed?HEAP16:HEAPU16;return this["fromWireType"](heap[pointer>>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __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}function embindRepr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=value=>value;if(minRange===0){var bitshift=32-8*size;fromWireType=value=>value<>>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":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(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>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_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;iHEAPU16;shift=1}else if(charSize===4){decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32;getHeap=()=>HEAPU32;shift=2}registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==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":function(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>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_array(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){tupleRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),elements:[]}}function __embind_register_value_array_element(rawTupleType,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){tupleRegistrations[rawTupleType].elements.push({getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __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:[]}}function __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})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}var nowIsMonotonic=true;function __emscripten_get_now_is_monotonic(){return nowIsMonotonic}function __emval_as(handle,returnType,destructorsRef){handle=Emval.toValue(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=Emval.toHandle(destructors);HEAPU32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}function __emval_get_property(handle,key){handle=Emval.toValue(handle);key=Emval.toValue(key);return Emval.toHandle(handle[key])}function __emval_incref(handle){if(handle>4){emval_handle_array[handle].refcount+=1}}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}return symbol}function __emval_new_cstring(v){return Emval.toHandle(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)}function __emval_take_value(type,arg){type=requireRegisteredType(type,"_emval_take_value");var v=type["readValueFromPointer"](arg);return Emval.toHandle(v)}function _abort(){abort("")}var readAsmConstArgsArray=[];function readAsmConstArgs(sigPtr,buf){readAsmConstArgsArray.length=0;var ch;buf>>=2;while(ch=HEAPU8[sigPtr++]){buf+=ch!=105&buf;readAsmConstArgsArray.push(ch==105?HEAP32[buf]:HEAPF64[buf++>>1]);++buf}return readAsmConstArgsArray}function _emscripten_asm_const_int(code,sigPtr,argbuf){var args=readAsmConstArgs(sigPtr,argbuf);return ASM_CONSTS[code].apply(null,args)}function getHeapMax(){return 2147483648}function _emscripten_get_heap_max(){return getHeapMax()}var _emscripten_get_now;if(ENVIRONMENT_IS_NODE){_emscripten_get_now=()=>{var t=process["hrtime"]();return t[0]*1e3+t[1]/1e6}}else _emscripten_get_now=()=>performance.now();function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){var oldSize=HEAPU8.length;requestedSize=requestedSize>>>0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}let 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=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}getEnvStrings.strings=strings}return getEnvStrings.strings}function _environ_get(__environ,environ_buf){var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAPU32[penviron_buf_size>>2]=bufSize;return 0}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function doReadv(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function convertI32PairToI53Checked(lo,hi){return hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var offset=convertI32PairToI53Checked(offset_low,offset_high);if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function doWritev(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function _getentropy(buffer,size){if(!_getentropy.randomDevice){_getentropy.randomDevice=getRandomDevice()}for(var i=0;i>0]=_getentropy.randomDevice()}return 0}function __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(ctx){return!!(ctx.dibvbi=ctx.getExtension("WEBGL_draw_instanced_base_vertex_base_instance"))}function __webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(ctx){return!!(ctx.mdibvbi=ctx.getExtension("WEBGL_multi_draw_instanced_base_vertex_base_instance"))}function __webgl_enable_WEBGL_multi_draw(ctx){return!!(ctx.multiDrawWebgl=ctx.getExtension("WEBGL_multi_draw"))}var GL={counter:1,buffers:[],mappedBuffers:{},programs:[],framebuffers:[],renderbuffers:[],textures:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},queries:[],samplers:[],transformFeedbacks:[],syncs:[],byteSizeByTypeRoot:5120,byteSizeByType:[1,1,2,2,4,4,4,2,3,4,8],stringCache:{},stringiCache:{},unpackAlignment:4,recordError:function recordError(errorCode){if(!GL.lastError){GL.lastError=errorCode}},getNewId:function(table){var ret=GL.counter++;for(var i=table.length;i>1;var quadIndexes=new Uint16Array(numIndexes);var i=0,v=0;while(1){quadIndexes[i++]=v;if(i>=numIndexes)break;quadIndexes[i++]=v+1;if(i>=numIndexes)break;quadIndexes[i++]=v+2;if(i>=numIndexes)break;quadIndexes[i++]=v;if(i>=numIndexes)break;quadIndexes[i++]=v+2;if(i>=numIndexes)break;quadIndexes[i++]=v+3;if(i>=numIndexes)break;v+=4}context.GLctx.bufferData(34963,quadIndexes,35044);context.GLctx.bindBuffer(34963,null)}},getTempVertexBuffer:function getTempVertexBuffer(sizeBytes){var idx=GL.log2ceilLookup(sizeBytes);var ringbuffer=GL.currentContext.tempVertexBuffers1[idx];var nextFreeBufferIndex=GL.currentContext.tempVertexBufferCounters1[idx];GL.currentContext.tempVertexBufferCounters1[idx]=GL.currentContext.tempVertexBufferCounters1[idx]+1&GL.numTempVertexBuffersPerSize-1;var vbo=ringbuffer[nextFreeBufferIndex];if(vbo){return vbo}var prevVBO=GLctx.getParameter(34964);ringbuffer[nextFreeBufferIndex]=GLctx.createBuffer();GLctx.bindBuffer(34962,ringbuffer[nextFreeBufferIndex]);GLctx.bufferData(34962,1<>2]:-1;source+=UTF8ToString(HEAP32[string+i*4>>2],len<0?undefined:len)}return source},calcBufLength:function calcBufLength(size,type,stride,count){if(stride>0){return count*stride}var typeSize=GL.byteSizeByType[type-GL.byteSizeByTypeRoot];return size*typeSize*count},usedTempBuffers:[],preDrawHandleClientVertexAttribBindings:function preDrawHandleClientVertexAttribBindings(count){GL.resetBufferBinding=false;for(var i=0;i=2){GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query_webgl2")}if(context.version<2||!GLctx.disjointTimerQueryExt){GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query")}__webgl_enable_WEBGL_multi_draw(GLctx);var exts=GLctx.getSupportedExtensions()||[];exts.forEach(function(ext){if(!ext.includes("lose_context")&&!ext.includes("debug")){GLctx.getExtension(ext)}})}};function _glActiveTexture(x0){GLctx["activeTexture"](x0)}function _glAttachShader(program,shader){GLctx.attachShader(GL.programs[program],GL.shaders[shader])}function _glBeginQuery(target,id){GLctx["beginQuery"](target,GL.queries[id])}function _glBindBuffer(target,buffer){if(target==34962){GLctx.currentArrayBufferBinding=buffer}else if(target==34963){GLctx.currentElementArrayBufferBinding=buffer}if(target==35051){GLctx.currentPixelPackBufferBinding=buffer}else if(target==35052){GLctx.currentPixelUnpackBufferBinding=buffer}GLctx.bindBuffer(target,GL.buffers[buffer])}function _glBindBufferBase(target,index,buffer){GLctx["bindBufferBase"](target,index,GL.buffers[buffer])}function _glBindBufferRange(target,index,buffer,offset,ptrsize){GLctx["bindBufferRange"](target,index,GL.buffers[buffer],offset,ptrsize)}function _glBindFramebuffer(target,framebuffer){GLctx.bindFramebuffer(target,GL.framebuffers[framebuffer])}function _glBindRenderbuffer(target,renderbuffer){GLctx.bindRenderbuffer(target,GL.renderbuffers[renderbuffer])}function _glBindSampler(unit,sampler){GLctx["bindSampler"](unit,GL.samplers[sampler])}function _glBindTexture(target,texture){GLctx.bindTexture(target,GL.textures[texture])}function _glBindVertexArray(vao){GLctx["bindVertexArray"](GL.vaos[vao]);var ibo=GLctx.getParameter(34965);GLctx.currentElementArrayBufferBinding=ibo?ibo.name|0:0}function _glBlendEquationSeparate(x0,x1){GLctx["blendEquationSeparate"](x0,x1)}function _glBlendFuncSeparate(x0,x1,x2,x3){GLctx["blendFuncSeparate"](x0,x1,x2,x3)}function _glBlitFramebuffer(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9){GLctx["blitFramebuffer"](x0,x1,x2,x3,x4,x5,x6,x7,x8,x9)}function _glBufferData(target,size,data,usage){if(true){if(data&&size){GLctx.bufferData(target,HEAPU8,usage,data,size)}else{GLctx.bufferData(target,size,usage)}}else{GLctx.bufferData(target,data?HEAPU8.subarray(data,data+size):size,usage)}}function _glBufferSubData(target,offset,size,data){if(true){size&&GLctx.bufferSubData(target,offset,HEAPU8,data,size);return}GLctx.bufferSubData(target,offset,HEAPU8.subarray(data,data+size))}function _glClearBufferfi(x0,x1,x2,x3){GLctx["clearBufferfi"](x0,x1,x2,x3)}function _glClearBufferfv(buffer,drawbuffer,value){GLctx["clearBufferfv"](buffer,drawbuffer,HEAPF32,value>>2)}function _glClearBufferiv(buffer,drawbuffer,value){GLctx["clearBufferiv"](buffer,drawbuffer,HEAP32,value>>2)}function convertI32PairToI53(lo,hi){return(lo>>>0)+hi*4294967296}function _glClientWaitSync(sync,flags,timeoutLo,timeoutHi){return GLctx.clientWaitSync(GL.syncs[sync],flags,convertI32PairToI53(timeoutLo,timeoutHi))}function _glColorMask(red,green,blue,alpha){GLctx.colorMask(!!red,!!green,!!blue,!!alpha)}function _glCompileShader(shader){GLctx.compileShader(GL.shaders[shader])}function _glCompressedTexSubImage2D(target,level,xoffset,yoffset,width,height,format,imageSize,data){if(true){if(GLctx.currentPixelUnpackBufferBinding||!imageSize){GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,imageSize,data)}else{GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,HEAPU8,data,imageSize)}return}GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,data?HEAPU8.subarray(data,data+imageSize):null)}function _glCompressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data)}else{GLctx["compressedTexSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,HEAPU8,data,imageSize)}}function _glCopyBufferSubData(x0,x1,x2,x3,x4){GLctx["copyBufferSubData"](x0,x1,x2,x3,x4)}function _glCreateProgram(){var id=GL.getNewId(GL.programs);var program=GLctx.createProgram();program.name=id;program.maxUniformLength=program.maxAttributeLength=program.maxUniformBlockNameLength=0;program.uniformIdCounter=1;GL.programs[id]=program;return id}function _glCreateShader(shaderType){var id=GL.getNewId(GL.shaders);GL.shaders[id]=GLctx.createShader(shaderType);return id}function _glCullFace(x0){GLctx["cullFace"](x0)}function _glDeleteBuffers(n,buffers){for(var i=0;i>2];var buffer=GL.buffers[id];if(!buffer)continue;GLctx.deleteBuffer(buffer);buffer.name=0;GL.buffers[id]=null;if(id==GLctx.currentArrayBufferBinding)GLctx.currentArrayBufferBinding=0;if(id==GLctx.currentElementArrayBufferBinding)GLctx.currentElementArrayBufferBinding=0;if(id==GLctx.currentPixelPackBufferBinding)GLctx.currentPixelPackBufferBinding=0;if(id==GLctx.currentPixelUnpackBufferBinding)GLctx.currentPixelUnpackBufferBinding=0}}function _glDeleteFramebuffers(n,framebuffers){for(var i=0;i>2];var framebuffer=GL.framebuffers[id];if(!framebuffer)continue;GLctx.deleteFramebuffer(framebuffer);framebuffer.name=0;GL.framebuffers[id]=null}}function _glDeleteProgram(id){if(!id)return;var program=GL.programs[id];if(!program){GL.recordError(1281);return}GLctx.deleteProgram(program);program.name=0;GL.programs[id]=null}function _glDeleteQueries(n,ids){for(var i=0;i>2];var query=GL.queries[id];if(!query)continue;GLctx["deleteQuery"](query);GL.queries[id]=null}}function _glDeleteRenderbuffers(n,renderbuffers){for(var i=0;i>2];var renderbuffer=GL.renderbuffers[id];if(!renderbuffer)continue;GLctx.deleteRenderbuffer(renderbuffer);renderbuffer.name=0;GL.renderbuffers[id]=null}}function _glDeleteSamplers(n,samplers){for(var i=0;i>2];var sampler=GL.samplers[id];if(!sampler)continue;GLctx["deleteSampler"](sampler);sampler.name=0;GL.samplers[id]=null}}function _glDeleteShader(id){if(!id)return;var shader=GL.shaders[id];if(!shader){GL.recordError(1281);return}GLctx.deleteShader(shader);GL.shaders[id]=null}function _glDeleteSync(id){if(!id)return;var sync=GL.syncs[id];if(!sync){GL.recordError(1281);return}GLctx.deleteSync(sync);sync.name=0;GL.syncs[id]=null}function _glDeleteTextures(n,textures){for(var i=0;i>2];var texture=GL.textures[id];if(!texture)continue;GLctx.deleteTexture(texture);texture.name=0;GL.textures[id]=null}}function _glDeleteVertexArrays(n,vaos){for(var i=0;i>2];GLctx["deleteVertexArray"](GL.vaos[id]);GL.vaos[id]=null}}function _glDepthFunc(x0){GLctx["depthFunc"](x0)}function _glDepthMask(flag){GLctx.depthMask(!!flag)}function _glDepthRangef(x0,x1){GLctx["depthRange"](x0,x1)}function _glDetachShader(program,shader){GLctx.detachShader(GL.programs[program],GL.shaders[shader])}function _glDisable(x0){GLctx["disable"](x0)}function _glDisableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=false;GLctx.disableVertexAttribArray(index)}var tempFixedLengthArray=[];function _glDrawBuffers(n,bufs){var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx["drawBuffers"](bufArray)}function _glDrawElementsInstanced(mode,count,type,indices,primcount){GLctx["drawElementsInstanced"](mode,count,type,indices,primcount)}function _glDrawElements(mode,count,type,indices){var buf;if(!GLctx.currentElementArrayBufferBinding){var size=GL.calcBufLength(1,type,0,count);buf=GL.getTempIndexBuffer(size);GLctx.bindBuffer(34963,buf);GLctx.bufferSubData(34963,0,HEAPU8.subarray(indices,indices+size));indices=0}GL.preDrawHandleClientVertexAttribBindings(count);GLctx.drawElements(mode,count,type,indices);GL.postDrawHandleClientVertexAttribBindings(count);if(!GLctx.currentElementArrayBufferBinding){GLctx.bindBuffer(34963,null)}}function _glDrawRangeElements(mode,start,end,count,type,indices){_glDrawElements(mode,count,type,indices)}function _glEnable(x0){GLctx["enable"](x0)}function _glEnableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=true;GLctx.enableVertexAttribArray(index)}function _glEndQuery(x0){GLctx["endQuery"](x0)}function _glFenceSync(condition,flags){var sync=GLctx.fenceSync(condition,flags);if(sync){var id=GL.getNewId(GL.syncs);sync.name=id;GL.syncs[id]=sync;return id}else{return 0}}function _glFinish(){GLctx["finish"]()}function _glFlush(){GLctx["flush"]()}function _glFramebufferRenderbuffer(target,attachment,renderbuffertarget,renderbuffer){GLctx.framebufferRenderbuffer(target,attachment,renderbuffertarget,GL.renderbuffers[renderbuffer])}function _glFramebufferTexture2D(target,attachment,textarget,texture,level){GLctx.framebufferTexture2D(target,attachment,textarget,GL.textures[texture],level)}function _glFramebufferTextureLayer(target,attachment,texture,level,layer){GLctx.framebufferTextureLayer(target,attachment,GL.textures[texture],level,layer)}function _glFrontFace(x0){GLctx["frontFace"](x0)}function __glGenObject(n,buffers,createFunction,objectTable){for(var i=0;i>2]=id}}function _glGenBuffers(n,buffers){__glGenObject(n,buffers,"createBuffer",GL.buffers)}function _glGenFramebuffers(n,ids){__glGenObject(n,ids,"createFramebuffer",GL.framebuffers)}function _glGenQueries(n,ids){__glGenObject(n,ids,"createQuery",GL.queries)}function _glGenRenderbuffers(n,renderbuffers){__glGenObject(n,renderbuffers,"createRenderbuffer",GL.renderbuffers)}function _glGenSamplers(n,samplers){__glGenObject(n,samplers,"createSampler",GL.samplers)}function _glGenTextures(n,textures){__glGenObject(n,textures,"createTexture",GL.textures)}function _glGenVertexArrays(n,arrays){__glGenObject(n,arrays,"createVertexArray",GL.vaos)}function _glGenerateMipmap(x0){GLctx["generateMipmap"](x0)}function _glGetBufferSubData(target,offset,size,data){if(!data){GL.recordError(1281);return}size&&GLctx["getBufferSubData"](target,offset,HEAPU8,data,size)}function _glGetError(){var error=GLctx.getError()||GL.lastError;GL.lastError=0;return error}function writeI53ToI64(ptr,num){HEAPU32[ptr>>2]=num;HEAPU32[ptr+4>>2]=(num-HEAPU32[ptr>>2])/4294967296}function emscriptenWebGLGet(name_,p,type){if(!p){GL.recordError(1281);return}var ret=undefined;switch(name_){case 36346:ret=1;break;case 36344:if(type!=0&&type!=1){GL.recordError(1280)}return;case 34814:case 36345:ret=0;break;case 34466:var formats=GLctx.getParameter(34467);ret=formats?formats.length:0;break;case 33309:if(GL.currentContext.version<2){GL.recordError(1282);return}var exts=GLctx.getSupportedExtensions()||[];ret=2*exts.length;break;case 33307:case 33308:if(GL.currentContext.version<2){GL.recordError(1280);return}ret=name_==33307?3:0;break}if(ret===undefined){var result=GLctx.getParameter(name_);switch(typeof result){case"number":ret=result;break;case"boolean":ret=result?1:0;break;case"string":GL.recordError(1280);return;case"object":if(result===null){switch(name_){case 34964:case 35725:case 34965:case 36006:case 36007:case 32873:case 34229:case 36662:case 36663:case 35053:case 35055:case 36010:case 35097:case 35869:case 32874:case 36389:case 35983:case 35368:case 34068:{ret=0;break}default:{GL.recordError(1280);return}}}else if(result instanceof Float32Array||result instanceof Uint32Array||result instanceof Int32Array||result instanceof Array){for(var i=0;i>2]=result[i];break;case 2:HEAPF32[p+i*4>>2]=result[i];break;case 4:HEAP8[p+i>>0]=result[i]?1:0;break}}return}else{try{ret=result.name|0}catch(e){GL.recordError(1280);err("GL_INVALID_ENUM in glGet"+type+"v: Unknown object returned from WebGL getParameter("+name_+")! (error: "+e+")");return}}break;default:GL.recordError(1280);err("GL_INVALID_ENUM in glGet"+type+"v: Native code calling glGet"+type+"v("+name_+") and it returns "+result+" of type "+typeof result+"!");return}}switch(type){case 1:writeI53ToI64(p,ret);break;case 0:HEAP32[p>>2]=ret;break;case 2:HEAPF32[p>>2]=ret;break;case 4:HEAP8[p>>0]=ret?1:0;break}}function _glGetFloatv(name_,p){emscriptenWebGLGet(name_,p,2)}function _glGetIntegerv(name_,p){emscriptenWebGLGet(name_,p,0)}function _glGetProgramInfoLog(program,maxLength,length,infoLog){var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _glGetProgramiv(program,pname,p){if(!p){GL.recordError(1281);return}if(program>=GL.counter){GL.recordError(1281);return}program=GL.programs[program];if(pname==35716){var log=GLctx.getProgramInfoLog(program);if(log===null)log="(unknown error)";HEAP32[p>>2]=log.length+1}else if(pname==35719){if(!program.maxUniformLength){for(var i=0;i>2]=program.maxUniformLength}else if(pname==35722){if(!program.maxAttributeLength){for(var i=0;i>2]=program.maxAttributeLength}else if(pname==35381){if(!program.maxUniformBlockNameLength){for(var i=0;i>2]=program.maxUniformBlockNameLength}else{HEAP32[p>>2]=GLctx.getProgramParameter(program,pname)}}function _glGetQueryObjectuiv(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.queries[id];var param=GLctx["getQueryParameter"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret}function _glGetShaderInfoLog(shader,maxLength,length,infoLog){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _glGetShaderiv(shader,pname,p){if(!p){GL.recordError(1281);return}if(pname==35716){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var logLength=log?log.length+1:0;HEAP32[p>>2]=logLength}else if(pname==35720){var source=GLctx.getShaderSource(GL.shaders[shader]);var sourceLength=source?source.length+1:0;HEAP32[p>>2]=sourceLength}else{HEAP32[p>>2]=GLctx.getShaderParameter(GL.shaders[shader],pname)}}function stringToNewUTF8(jsString){var length=lengthBytesUTF8(jsString)+1;var cString=_malloc(length);stringToUTF8(jsString,cString,length);return cString}function _glGetString(name_){var ret=GL.stringCache[name_];if(!ret){switch(name_){case 7939:var exts=GLctx.getSupportedExtensions()||[];exts=exts.concat(exts.map(function(e){return"GL_"+e}));ret=stringToNewUTF8(exts.join(" "));break;case 7936:case 7937:case 37445:case 37446:var s=GLctx.getParameter(name_);if(!s){GL.recordError(1280)}ret=s&&stringToNewUTF8(s);break;case 7938:var glVersion=GLctx.getParameter(7938);if(true)glVersion="OpenGL ES 3.0 ("+glVersion+")";else{glVersion="OpenGL ES 2.0 ("+glVersion+")"}ret=stringToNewUTF8(glVersion);break;case 35724:var glslVersion=GLctx.getParameter(35724);var ver_re=/^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/;var ver_num=glslVersion.match(ver_re);if(ver_num!==null){if(ver_num[1].length==3)ver_num[1]=ver_num[1]+"0";glslVersion="OpenGL ES GLSL ES "+ver_num[1]+" ("+glslVersion+")"}ret=stringToNewUTF8(glslVersion);break;default:GL.recordError(1280)}GL.stringCache[name_]=ret}return ret}function _glGetUniformBlockIndex(program,uniformBlockName){return GLctx["getUniformBlockIndex"](GL.programs[program],UTF8ToString(uniformBlockName))}function jstoi_q(str){return parseInt(str)}function webglGetLeftBracePos(name){return name.slice(-1)=="]"&&name.lastIndexOf("[")}function webglPrepareUniformLocationsBeforeFirstUse(program){var uniformLocsById=program.uniformLocsById,uniformSizeAndIdsByName=program.uniformSizeAndIdsByName,i,j;if(!uniformLocsById){program.uniformLocsById=uniformLocsById={};program.uniformArrayNamesById={};for(i=0;i0?nm.slice(0,lb):nm;var id=program.uniformIdCounter;program.uniformIdCounter+=sz;uniformSizeAndIdsByName[arrayName]=[sz,id];for(j=0;j0){arrayIndex=jstoi_q(name.slice(leftBrace+1))>>>0;uniformBaseName=name.slice(0,leftBrace)}var sizeAndId=program.uniformSizeAndIdsByName[uniformBaseName];if(sizeAndId&&arrayIndex>2]}GLctx["invalidateFramebuffer"](target,list)}function _glLinkProgram(program){program=GL.programs[program];GLctx.linkProgram(program);program.uniformLocsById=0;program.uniformSizeAndIdsByName={}}function emscriptenWebGLGetBufferBinding(target){switch(target){case 34962:target=34964;break;case 34963:target=34965;break;case 35051:target=35053;break;case 35052:target=35055;break;case 35982:target=35983;break;case 36662:target=36662;break;case 36663:target=36663;break;case 35345:target=35368;break}var buffer=GLctx.getParameter(target);if(buffer)return buffer.name|0;else return 0}function emscriptenWebGLValidateMapBufferTarget(target){switch(target){case 34962:case 34963:case 36662:case 36663:case 35051:case 35052:case 35882:case 35982:case 35345:return true;default:return false}}function _glMapBufferRange(target,offset,length,access){if(access!=26&&access!=10){err("glMapBufferRange is only supported when access is MAP_WRITE|INVALIDATE_BUFFER");return 0}if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glMapBufferRange");return 0}var mem=_malloc(length);if(!mem)return 0;GL.mappedBuffers[emscriptenWebGLGetBufferBinding(target)]={offset:offset,length:length,mem:mem,access:access};return mem}function _glPixelStorei(pname,param){if(pname==3317){GL.unpackAlignment=param}GLctx.pixelStorei(pname,param)}function _glPolygonOffset(x0,x1){GLctx["polygonOffset"](x0,x1)}function computeUnpackAlignedImageSize(width,height,sizePerPixel,alignment){function roundedToNextMultipleOf(x,y){return x+y-1&-y}var plainRowSize=width*sizePerPixel;var alignedRowSize=roundedToNextMultipleOf(plainRowSize,alignment);return height*alignedRowSize}function __colorChannelsInGlTextureFormat(format){var colorChannels={5:3,6:4,8:2,29502:3,29504:4,26917:2,26918:2,29846:3,29847:4};return colorChannels[format-6402]||1}function heapObjectForWebGLType(type){type-=5120;if(type==0)return HEAP8;if(type==1)return HEAPU8;if(type==2)return HEAP16;if(type==4)return HEAP32;if(type==6)return HEAPF32;if(type==5||type==28922||type==28520||type==30779||type==30782)return HEAPU32;return HEAPU16}function heapAccessShiftForWebGLHeap(heap){return 31-Math.clz32(heap.BYTES_PER_ELEMENT)}function emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,internalFormat){var heap=heapObjectForWebGLType(type);var shift=heapAccessShiftForWebGLHeap(heap);var byteSize=1<>shift,pixels+bytes>>shift)}function _glReadPixels(x,y,width,height,format,type,pixels){if(true){if(GLctx.currentPixelPackBufferBinding){GLctx.readPixels(x,y,width,height,format,type,pixels)}else{var heap=heapObjectForWebGLType(type);GLctx.readPixels(x,y,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}return}var pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,format);if(!pixelData){GL.recordError(1280);return}GLctx.readPixels(x,y,width,height,format,type,pixelData)}function _glRenderbufferStorage(x0,x1,x2,x3){GLctx["renderbufferStorage"](x0,x1,x2,x3)}function _glRenderbufferStorageMultisample(x0,x1,x2,x3,x4){GLctx["renderbufferStorageMultisample"](x0,x1,x2,x3,x4)}function _glSamplerParameterf(sampler,pname,param){GLctx["samplerParameterf"](GL.samplers[sampler],pname,param)}function _glSamplerParameteri(sampler,pname,param){GLctx["samplerParameteri"](GL.samplers[sampler],pname,param)}function _glScissor(x0,x1,x2,x3){GLctx["scissor"](x0,x1,x2,x3)}function _glShaderSource(shader,count,string,length){var source=GL.getSource(shader,count,string,length);GLctx.shaderSource(GL.shaders[shader],source)}function _glStencilFuncSeparate(x0,x1,x2,x3){GLctx["stencilFuncSeparate"](x0,x1,x2,x3)}function _glStencilMaskSeparate(x0,x1){GLctx["stencilMaskSeparate"](x0,x1)}function _glStencilOpSeparate(x0,x1,x2,x3){GLctx["stencilOpSeparate"](x0,x1,x2,x3)}function _glTexParameterf(x0,x1,x2){GLctx["texParameterf"](x0,x1,x2)}function _glTexParameteri(x0,x1,x2){GLctx["texParameteri"](x0,x1,x2)}function _glTexStorage2D(x0,x1,x2,x3,x4){GLctx["texStorage2D"](x0,x1,x2,x3,x4)}function _glTexStorage3D(x0,x1,x2,x3,x4,x5){GLctx["texStorage3D"](x0,x1,x2,x3,x4,x5)}function _glTexSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,null)}return}var pixelData=null;if(pixels)pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,0);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixelData)}function _glTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels){if(GLctx.currentPixelUnpackBufferBinding){GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,null)}}function webglGetUniformLocation(location){var p=GLctx.currentProgram;if(p){var webglLoc=p.uniformLocsById[location];if(typeof webglLoc=="number"){p.uniformLocsById[location]=webglLoc=GLctx.getUniformLocation(p,p.uniformArrayNamesById[location]+(webglLoc>0?"["+webglLoc+"]":""))}return webglLoc}else{GL.recordError(1282)}}function _glUniform1i(location,v0){GLctx.uniform1i(webglGetUniformLocation(location),v0)}function _glUniformBlockBinding(program,uniformBlockIndex,uniformBlockBinding){program=GL.programs[program];GLctx["uniformBlockBinding"](program,uniformBlockIndex,uniformBlockBinding)}function _glUnmapBuffer(target){if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glUnmapBuffer");return 0}var buffer=emscriptenWebGLGetBufferBinding(target);var mapping=GL.mappedBuffers[buffer];if(!mapping){GL.recordError(1282);err("buffer was never mapped in glUnmapBuffer");return 0}GL.mappedBuffers[buffer]=null;if(!(mapping.access&16))if(true){GLctx.bufferSubData(target,mapping.offset,HEAPU8,mapping.mem,mapping.length)}else{GLctx.bufferSubData(target,mapping.offset,HEAPU8.subarray(mapping.mem,mapping.mem+mapping.length))}_free(mapping.mem);return 1}function _glUseProgram(program){program=GL.programs[program];GLctx.useProgram(program);GLctx.currentProgram=program}function _glVertexAttrib4f(x0,x1,x2,x3,x4){GLctx["vertexAttrib4f"](x0,x1,x2,x3,x4)}function _glVertexAttribI4ui(x0,x1,x2,x3,x4){GLctx["vertexAttribI4ui"](x0,x1,x2,x3,x4)}function _glVertexAttribIPointer(index,size,type,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=false;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribIPointer(index,size,type,stride,ptr)};return}cb.clientside=false;GLctx["vertexAttribIPointer"](index,size,type,stride,ptr)}function _glVertexAttribPointer(index,size,type,normalized,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=normalized;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribPointer(index,size,type,normalized,stride,ptr)};return}cb.clientside=false;GLctx.vertexAttribPointer(index,size,type,!!normalized,stride,ptr)}function _glViewport(x0,x1,x2,x3){GLctx["viewport"](x0,x1,x2,x3)}function _glWaitSync(sync,flags,timeoutLo,timeoutHi){GLctx.waitSync(GL.syncs[sync],flags,convertI32PairToI53(timeoutLo,timeoutHi))}function _setTempRet0(val){setTempRet0(val)}function __isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}function __arraySum(array,index){var sum=0;for(var i=0;i<=index;sum+=array[i++]){}return sum}var __MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31];var __MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31];function __addDays(date,days){var newDate=new Date(date.getTime());while(days>0){var leap=__isLeapYear(newDate.getFullYear());var currentMonth=newDate.getMonth();var daysInCurrentMonth=(leap?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR)[currentMonth];if(days>daysInCurrentMonth-newDate.getDate()){days-=daysInCurrentMonth-newDate.getDate()+1;newDate.setDate(1);if(currentMonth<11){newDate.setMonth(currentMonth+1)}else{newDate.setMonth(0);newDate.setFullYear(newDate.getFullYear()+1)}}else{newDate.setDate(newDate.getDate()+days);return newDate}}return newDate}function _strftime(s,maxsize,format,tm){var tm_zone=HEAP32[tm+40>>2];var date={tm_sec:HEAP32[tm>>2],tm_min:HEAP32[tm+4>>2],tm_hour:HEAP32[tm+8>>2],tm_mday:HEAP32[tm+12>>2],tm_mon:HEAP32[tm+16>>2],tm_year:HEAP32[tm+20>>2],tm_wday:HEAP32[tm+24>>2],tm_yday:HEAP32[tm+28>>2],tm_isdst:HEAP32[tm+32>>2],tm_gmtoff:HEAP32[tm+36>>2],tm_zone:tm_zone?UTF8ToString(tm_zone):""};var pattern=UTF8ToString(format);var EXPANSION_RULES_1={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var rule in EXPANSION_RULES_1){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_1[rule])}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];function leadingSomething(value,digits,character){var str=typeof value=="number"?value.toString():value||"";while(str.length0?1:0}var compare;if((compare=sgn(date1.getFullYear()-date2.getFullYear()))===0){if((compare=sgn(date1.getMonth()-date2.getMonth()))===0){compare=sgn(date1.getDate()-date2.getDate())}}return compare}function getFirstWeekStartDate(janFourth){switch(janFourth.getDay()){case 0:return new Date(janFourth.getFullYear()-1,11,29);case 1:return janFourth;case 2:return new Date(janFourth.getFullYear(),0,3);case 3:return new Date(janFourth.getFullYear(),0,2);case 4:return new Date(janFourth.getFullYear(),0,1);case 5:return new Date(janFourth.getFullYear()-1,11,31);case 6:return new Date(janFourth.getFullYear()-1,11,30)}}function getWeekBasedYear(date){var thisDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);var janFourthThisYear=new Date(thisDate.getFullYear(),0,4);var janFourthNextYear=new Date(thisDate.getFullYear()+1,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);if(compareByDay(firstWeekStartThisYear,thisDate)<=0){if(compareByDay(firstWeekStartNextYear,thisDate)<=0){return thisDate.getFullYear()+1}else{return thisDate.getFullYear()}}else{return thisDate.getFullYear()-1}}var EXPANSION_RULES_2={"%a":function(date){return WEEKDAYS[date.tm_wday].substring(0,3)},"%A":function(date){return WEEKDAYS[date.tm_wday]},"%b":function(date){return MONTHS[date.tm_mon].substring(0,3)},"%B":function(date){return MONTHS[date.tm_mon]},"%C":function(date){var year=date.tm_year+1900;return leadingNulls(year/100|0,2)},"%d":function(date){return leadingNulls(date.tm_mday,2)},"%e":function(date){return leadingSomething(date.tm_mday,2," ")},"%g":function(date){return getWeekBasedYear(date).toString().substring(2)},"%G":function(date){return getWeekBasedYear(date)},"%H":function(date){return leadingNulls(date.tm_hour,2)},"%I":function(date){var twelveHour=date.tm_hour;if(twelveHour==0)twelveHour=12;else if(twelveHour>12)twelveHour-=12;return leadingNulls(twelveHour,2)},"%j":function(date){return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900)?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,date.tm_mon-1),3)},"%m":function(date){return leadingNulls(date.tm_mon+1,2)},"%M":function(date){return leadingNulls(date.tm_min,2)},"%n":function(){return"\n"},"%p":function(date){if(date.tm_hour>=0&&date.tm_hour<12){return"AM"}else{return"PM"}},"%S":function(date){return leadingNulls(date.tm_sec,2)},"%t":function(){return"\t"},"%u":function(date){return date.tm_wday||7},"%U":function(date){var days=date.tm_yday+7-date.tm_wday;return leadingNulls(Math.floor(days/7),2)},"%V":function(date){var val=Math.floor((date.tm_yday+7-(date.tm_wday+6)%7)/7);if((date.tm_wday+371-date.tm_yday-2)%7<=2){val++}if(!val){val=52;var dec31=(date.tm_wday+7-date.tm_yday-1)%7;if(dec31==4||dec31==5&&__isLeapYear(date.tm_year%400-1)){val++}}else if(val==53){var jan1=(date.tm_wday+371-date.tm_yday)%7;if(jan1!=4&&(jan1!=3||!__isLeapYear(date.tm_year)))val=1}return leadingNulls(val,2)},"%w":function(date){return date.tm_wday},"%W":function(date){var days=date.tm_yday+7-(date.tm_wday+6)%7;return leadingNulls(Math.floor(days/7),2)},"%y":function(date){return(date.tm_year+1900).toString().substring(2)},"%Y":function(date){return date.tm_year+1900},"%z":function(date){var off=date.tm_gmtoff;var ahead=off>=0;off=Math.abs(off)/60;off=off/60*100+off%60;return(ahead?"+":"-")+String("0000"+off).slice(-4)},"%Z":function(date){return date.tm_zone},"%%":function(){return"%"}};pattern=pattern.replace(/%%/g,"\0\0");for(var rule in EXPANSION_RULES_2){if(pattern.includes(rule)){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_2[rule](date))}}pattern=pattern.replace(/\0\0/g,"%");var bytes=intArrayFromString(pattern,false);if(bytes.length>maxsize){return 0}writeArrayToMemory(bytes,s);return bytes.length-1}function _strftime_l(s,maxsize,format,tm){return _strftime(s,maxsize,format,tm)}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_embind();init_RegisteredPointer();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();var GLctx;for(var i=0;i<32;++i)tempFixedLengthArray.push(new Array(i));function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var asmLibraryArg={"Ga":___syscall_fcntl64,"yb":___syscall_ioctl,"zb":___syscall_openat,"ub":___syscall_stat64,"x":__embind_finalize_value_array,"m":__embind_finalize_value_object,"nb":__embind_register_bigint,"Eb":__embind_register_bool,"e":__embind_register_class,"k":__embind_register_class_class_function,"l":__embind_register_class_constructor,"a":__embind_register_class_function,"w":__embind_register_class_property,"Db":__embind_register_emval,"h":__embind_register_enum,"b":__embind_register_enum_value,"Ia":__embind_register_float,"T":__embind_register_function,"D":__embind_register_integer,"p":__embind_register_memory_view,"Ha":__embind_register_std_string,"ea":__embind_register_std_wstring,"y":__embind_register_value_array,"f":__embind_register_value_array_element,"n":__embind_register_value_object,"d":__embind_register_value_object_field,"Fb":__embind_register_void,"Bb":__emscripten_get_now_is_monotonic,"s":__emval_as,"g":__emval_decref,"t":__emval_get_property,"S":__emval_incref,"H":__emval_new_cstring,"r":__emval_run_destructors,"u":__emval_take_value,"c":_abort,"ca":_emscripten_asm_const_int,"qb":_emscripten_get_heap_max,"Ab":_emscripten_get_now,"Cb":_emscripten_memcpy_big,"tb":_emscripten_resize_heap,"vb":_environ_get,"wb":_environ_sizes_get,"da":_fd_close,"xb":_fd_read,"mb":_fd_seek,"Fa":_fd_write,"rb":_getentropy,"i":_glActiveTexture,"W":_glAttachShader,"Wb":_glBeginQuery,"o":_glBindBuffer,"ta":_glBindBufferBase,"U":_glBindBufferRange,"q":_glBindFramebuffer,"Na":_glBindRenderbuffer,"aa":_glBindSampler,"j":_glBindTexture,"A":_glBindVertexArray,"wa":_glBlendEquationSeparate,"va":_glBlendFuncSeparate,"fa":_glBlitFramebuffer,"K":_glBufferData,"ha":_glBufferSubData,"Kb":_glClearBufferfi,"E":_glClearBufferfv,"Jb":_glClearBufferiv,"ob":_glClientWaitSync,"_":_glColorMask,"eb":_glCompileShader,"Ka":_glCompressedTexSubImage2D,"Ja":_glCompressedTexSubImage3D,"Ra":_glCopyBufferSubData,"db":_glCreateProgram,"gb":_glCreateShader,"xa":_glCullFace,"$":_glDeleteBuffers,"ia":_glDeleteFramebuffers,"oa":_glDeleteProgram,"Va":_glDeleteQueries,"ja":_glDeleteRenderbuffers,"za":_glDeleteSamplers,"X":_glDeleteShader,"L":_glDeleteSync,"ka":_glDeleteTextures,"ib":_glDeleteVertexArrays,"ba":_glDepthFunc,"Z":_glDepthMask,"qa":_glDepthRangef,"pa":_glDetachShader,"v":_glDisable,"Nb":_glDisableVertexAttribArray,"Xa":_glDrawBuffers,"Xb":_glDrawElementsInstanced,"Yb":_glDrawRangeElements,"G":_glEnable,"Qb":_glEnableVertexAttribArray,"Vb":_glEndQuery,"V":_glFenceSync,"Da":_glFinish,"lb":_glFlush,"M":_glFramebufferRenderbuffer,"Pa":_glFramebufferTexture2D,"Oa":_glFramebufferTextureLayer,"ya":_glFrontFace,"O":_glGenBuffers,"la":_glGenFramebuffers,"Wa":_glGenQueries,"Y":_glGenRenderbuffers,"Ba":_glGenSamplers,"N":_glGenTextures,"hb":_glGenVertexArrays,"Ta":_glGenerateMipmap,"Ib":_glGetBufferSubData,"Ca":_glGetError,"kb":_glGetFloatv,"C":_glGetIntegerv,"ab":_glGetProgramInfoLog,"na":_glGetProgramiv,"Ub":_glGetQueryObjectuiv,"bb":_glGetShaderInfoLog,"ma":_glGetShaderiv,"I":_glGetString,"$a":_glGetUniformBlockIndex,"Za":_glGetUniformLocation,"jb":_glHint,"ga":_glInvalidateFramebuffer,"cb":_glLinkProgram,"Hb":_glMapBufferRange,"B":_glPixelStorei,"ua":_glPolygonOffset,"Sa":_glReadPixels,"Lb":_glRenderbufferStorage,"Mb":_glRenderbufferStorageMultisample,"Aa":_glSamplerParameterf,"J":_glSamplerParameteri,"sa":_glScissor,"fb":_glShaderSource,"Q":_glStencilFuncSeparate,"F":_glStencilMaskSeparate,"P":_glStencilOpSeparate,"Ua":_glTexParameterf,"z":_glTexParameteri,"Tb":_glTexStorage2D,"Qa":_glTexStorage3D,"Ma":_glTexSubImage2D,"La":_glTexSubImage3D,"Ya":_glUniform1i,"_a":_glUniformBlockBinding,"Gb":_glUnmapBuffer,"R":_glUseProgram,"Ob":_glVertexAttrib4f,"Pb":_glVertexAttribI4ui,"Sb":_glVertexAttribIPointer,"Rb":_glVertexAttribPointer,"ra":_glViewport,"pb":_glWaitSync,"Ea":_setTempRet0,"sb":_strftime_l};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["_b"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["ac"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["bc"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["cc"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["dc"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["ec"]).apply(null,arguments)};var dynCall_iiiiij=Module["dynCall_iiiiij"]=function(){return(dynCall_iiiiij=Module["dynCall_iiiiij"]=Module["asm"]["fc"]).apply(null,arguments)};var dynCall_jii=Module["dynCall_jii"]=function(){return(dynCall_jii=Module["dynCall_jii"]=Module["asm"]["gc"]).apply(null,arguments)};var dynCall_iiij=Module["dynCall_iiij"]=function(){return(dynCall_iiij=Module["dynCall_iiij"]=Module["asm"]["hc"]).apply(null,arguments)};var dynCall_iiiij=Module["dynCall_iiiij"]=function(){return(dynCall_iiiij=Module["dynCall_iiiij"]=Module["asm"]["ic"]).apply(null,arguments)};var dynCall_vij=Module["dynCall_vij"]=function(){return(dynCall_vij=Module["dynCall_vij"]=Module["asm"]["jc"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["kc"]).apply(null,arguments)};var dynCall_viijii=Module["dynCall_viijii"]=function(){return(dynCall_viijii=Module["dynCall_viijii"]=Module["asm"]["lc"]).apply(null,arguments)};var dynCall_iiiiijj=Module["dynCall_iiiiijj"]=function(){return(dynCall_iiiiijj=Module["dynCall_iiiiijj"]=Module["asm"]["mc"]).apply(null,arguments)};var dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=function(){return(dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=Module["asm"]["nc"]).apply(null,arguments)};var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;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);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run(); return Filament.ready diff --git a/docs/webgl/filament.wasm b/docs/webgl/filament.wasm index 8023c92e084..b02a5a6bd31 100644 Binary files a/docs/webgl/filament.wasm and b/docs/webgl/filament.wasm differ diff --git a/docs/webgl/metallic.ktx2 b/docs/webgl/metallic.ktx2 new file mode 100644 index 00000000000..45541a765e7 Binary files /dev/null and b/docs/webgl/metallic.ktx2 differ diff --git a/docs/webgl/normal.ktx2 b/docs/webgl/normal.ktx2 new file mode 100644 index 00000000000..695eb3cbf82 Binary files /dev/null and b/docs/webgl/normal.ktx2 differ diff --git a/docs/webgl/parquet.filamat b/docs/webgl/parquet.filamat index 579d57f36ce..01b1dbf321b 100644 Binary files a/docs/webgl/parquet.filamat and b/docs/webgl/parquet.filamat differ diff --git a/docs/webgl/plastic.filamat b/docs/webgl/plastic.filamat index 44772ae9615..6915a423fbb 100644 Binary files a/docs/webgl/plastic.filamat and b/docs/webgl/plastic.filamat differ diff --git a/docs/webgl/roughness.ktx2 b/docs/webgl/roughness.ktx2 new file mode 100644 index 00000000000..1fce56495ad Binary files /dev/null and b/docs/webgl/roughness.ktx2 differ diff --git a/docs/webgl/suzanne.filamesh b/docs/webgl/suzanne.filamesh index 34a9a990728..3782355f570 100644 Binary files a/docs/webgl/suzanne.filamesh and b/docs/webgl/suzanne.filamesh differ diff --git a/docs/webgl/textured.filamat b/docs/webgl/textured.filamat index 7e1e72b2676..692ee452cd9 100644 Binary files a/docs/webgl/textured.filamat and b/docs/webgl/textured.filamat differ diff --git a/docs/webgl/triangle.filamat b/docs/webgl/triangle.filamat index 0190da781f8..d8b0a5db656 100644 Binary files a/docs/webgl/triangle.filamat and b/docs/webgl/triangle.filamat differ diff --git a/docs/webgl/tutorial_redball.html b/docs/webgl/tutorial_redball.html index c8c40c8f5bc..82691d66274 100644 --- a/docs/webgl/tutorial_redball.html +++ b/docs/webgl/tutorial_redball.html @@ -44,7 +44,7 @@

Define plastic material

Next, invoke matc as follows.

-
matc -a opengl -p mobile -o plastic.filamat plastic.mat
+
matc -a opengl -p mobile -o plastic.filamat plastic.mat
 

You should now have a material archive in your working directory, which we'll use later in the @@ -53,7 +53,7 @@

Bake environment map

Next we'll use Filament's cmgen tool to consume a HDR environment map in latlong format, and produce two cubemap files: a mipmapped IBL and a blurry skybox.

Download pillars_2k.hdr, then invoke the following command in your terminal.

-
cmgen -x pillars_2k --format=ktx --size=256 --extract-blur=0.1 pillars_2k.hdr
+
cmgen -x pillars_2k --format=ktx --size=256 --extract-blur=0.1 pillars_2k.hdr
 

You should now have a pillars_2k folder containing a couple KTX files for the IBL and skybox, as @@ -61,81 +61,81 @@

Bake environment map

IBL KTX contains these coefficients in its metadata.

Create JavaScript

Next, create redball.js with the following content.

-
const environ = 'pillars_2k';
-const ibl_url = `${environ}/${environ}_ibl.ktx`;
-const sky_url = `${environ}/${environ}_skybox.ktx`;
-const filamat_url = 'plastic.filamat'
-
-Filament.init([ filamat_url, ibl_url, sky_url ], () => {
-  // Create some global aliases to enums for convenience.
-  window.VertexAttribute = Filament.VertexAttribute;
-  window.AttributeType = Filament.VertexBuffer$AttributeType;
-  window.PrimitiveType = Filament.RenderableManager$PrimitiveType;
-  window.IndexType = Filament.IndexBuffer$IndexType;
-  window.Fov = Filament.Camera$Fov;
-  window.LightType = Filament.LightManager$Type;
-
-  // Obtain the canvas DOM object and pass it to the App.
-  const canvas = document.getElementsByTagName('canvas')[0];
-  window.app = new App(canvas);
-} );
-
-class App {
-  constructor(canvas) {
-    this.canvas = canvas;
-    const engine = this.engine = Filament.Engine.create(canvas);
-    const scene = engine.createScene();
-
-    // TODO: create material
-    // TODO: create sphere
-    // TODO: create lights
-    // TODO: create IBL
-    // TODO: create skybox
-
-    this.swapChain = engine.createSwapChain();
-    this.renderer = engine.createRenderer();
-    this.camera = engine.createCamera(Filament.EntityManager.get().create());
-    this.view = engine.createView();
-    this.view.setCamera(this.camera);
-    this.view.setScene(scene);
-    this.resize();
-    this.render = this.render.bind(this);
-    this.resize = this.resize.bind(this);
-    window.addEventListener('resize', this.resize);
-    window.requestAnimationFrame(this.render);
-  }
-
-  render() {
-    const eye = [0, 0, 4], center = [0, 0, 0], up = [0, 1, 0];
-    const radians = Date.now() / 10000;
-    vec3.rotateY(eye, eye, center, radians);
-    this.camera.lookAt(eye, center, up);
-    this.renderer.render(this.swapChain, this.view);
-    window.requestAnimationFrame(this.render);
-  }
-
-  resize() {
-    const dpr = window.devicePixelRatio;
-    const width = this.canvas.width = window.innerWidth * dpr;
-    const height = this.canvas.height = window.innerHeight * dpr;
-    this.view.setViewport([0, 0, width, height]);
-    this.camera.setProjectionFov(45, width / height, 1.0, 10.0, Fov.VERTICAL);
-  }
-}
+
const environ = 'pillars_2k';
+const ibl_url = `${environ}/${environ}_ibl.ktx`;
+const sky_url = `${environ}/${environ}_skybox.ktx`;
+const filamat_url = 'plastic.filamat'
+
+Filament.init([ filamat_url, ibl_url, sky_url ], () => {
+  // Create some global aliases to enums for convenience.
+  window.VertexAttribute = Filament.VertexAttribute;
+  window.AttributeType = Filament.VertexBuffer$AttributeType;
+  window.PrimitiveType = Filament.RenderableManager$PrimitiveType;
+  window.IndexType = Filament.IndexBuffer$IndexType;
+  window.Fov = Filament.Camera$Fov;
+  window.LightType = Filament.LightManager$Type;
+
+  // Obtain the canvas DOM object and pass it to the App.
+  const canvas = document.getElementsByTagName('canvas')[0];
+  window.app = new App(canvas);
+} );
+
+class App {
+  constructor(canvas) {
+    this.canvas = canvas;
+    const engine = this.engine = Filament.Engine.create(canvas);
+    const scene = engine.createScene();
+
+    // TODO: create material
+    // TODO: create sphere
+    // TODO: create lights
+    // TODO: create IBL
+    // TODO: create skybox
+
+    this.swapChain = engine.createSwapChain();
+    this.renderer = engine.createRenderer();
+    this.camera = engine.createCamera(Filament.EntityManager.get().create());
+    this.view = engine.createView();
+    this.view.setCamera(this.camera);
+    this.view.setScene(scene);
+    this.resize();
+    this.render = this.render.bind(this);
+    this.resize = this.resize.bind(this);
+    window.addEventListener('resize', this.resize);
+    window.requestAnimationFrame(this.render);
+  }
+
+  render() {
+    const eye = [0, 0, 4], center = [0, 0, 0], up = [0, 1, 0];
+    const radians = Date.now() / 10000;
+    vec3.rotateY(eye, eye, center, radians);
+    this.camera.lookAt(eye, center, up);
+    this.renderer.render(this.swapChain, this.view);
+    window.requestAnimationFrame(this.render);
+  }
+
+  resize() {
+    const dpr = window.devicePixelRatio;
+    const width = this.canvas.width = window.innerWidth * dpr;
+    const height = this.canvas.height = window.innerHeight * dpr;
+    this.view.setViewport([0, 0, width, height]);
+    this.camera.setProjectionFov(45, width / height, 1.0, 10.0, Fov.VERTICAL);
+  }
+}
 

The above boilerplate should be familiar to you from the previous tutorial, although it loads in a new set of assets. We also added some animation to the camera.

Next let's create a material instance from the package that we built at the beginning the tutorial. Replace the create material comment with the following snippet.

-
const material = engine.createMaterial(filamat_url);
-const matinstance = material.createInstance();
-
-const red = [0.8, 0.0, 0.0];
-matinstance.setColor3Parameter('baseColor', Filament.RgbType.sRGB, red);
-matinstance.setFloatParameter('roughness', 0.5);
-matinstance.setFloatParameter('clearCoat', 1.0);
-matinstance.setFloatParameter('clearCoatRoughness', 0.3);
+
const material = engine.createMaterial(filamat_url);
+const matinstance = material.createInstance();
+
+const red = [0.8, 0.0, 0.0];
+matinstance.setColor3Parameter('baseColor', Filament.RgbType.sRGB, red);
+matinstance.setFloatParameter('roughness', 0.5);
+matinstance.setFloatParameter('clearCoat', 1.0);
+matinstance.setFloatParameter('clearCoatRoughness', 0.3);
 

The next step is to create a renderable for the sphere. To help with this, we'll use the IcoSphere @@ -149,33 +149,33 @@

Create JavaScript

Let's go ahead use these arrays to build the vertex buffer and index buffer. Replace create sphere with the following snippet.

-
const renderable = Filament.EntityManager.get().create();
-scene.addEntity(renderable);
-
-const icosphere = new Filament.IcoSphere(5);
-
-const vb = Filament.VertexBuffer.Builder()
-  .vertexCount(icosphere.vertices.length / 3)
-  .bufferCount(2)
-  .attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, 0)
-  .attribute(VertexAttribute.TANGENTS, 1, AttributeType.SHORT4, 0, 0)
-  .normalized(VertexAttribute.TANGENTS)
-  .build(engine);
-
-const ib = Filament.IndexBuffer.Builder()
-  .indexCount(icosphere.triangles.length)
-  .bufferType(IndexType.USHORT)
-  .build(engine);
-
-vb.setBufferAt(engine, 0, icosphere.vertices);
-vb.setBufferAt(engine, 1, icosphere.tangents);
-ib.setBuffer(engine, icosphere.triangles);
-
-Filament.RenderableManager.Builder(1)
-  .boundingBox({ center: [-1, -1, -1], halfExtent: [1, 1, 1] })
-  .material(0, matinstance)
-  .geometry(0, PrimitiveType.TRIANGLES, vb, ib)
-  .build(engine, renderable);
+
const renderable = Filament.EntityManager.get().create();
+scene.addEntity(renderable);
+
+const icosphere = new Filament.IcoSphere(5);
+
+const vb = Filament.VertexBuffer.Builder()
+  .vertexCount(icosphere.vertices.length / 3)
+  .bufferCount(2)
+  .attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, 0)
+  .attribute(VertexAttribute.TANGENTS, 1, AttributeType.SHORT4, 0, 0)
+  .normalized(VertexAttribute.TANGENTS)
+  .build(engine);
+
+const ib = Filament.IndexBuffer.Builder()
+  .indexCount(icosphere.triangles.length)
+  .bufferType(IndexType.USHORT)
+  .build(engine);
+
+vb.setBufferAt(engine, 0, icosphere.vertices);
+vb.setBufferAt(engine, 1, icosphere.tangents);
+ib.setBuffer(engine, icosphere.triangles);
+
+Filament.RenderableManager.Builder(1)
+  .boundingBox({ center: [-1, -1, -1], halfExtent: [1, 1, 1] })
+  .material(0, matinstance)
+  .geometry(0, PrimitiveType.TRIANGLES, vb, ib)
+  .build(engine, renderable);
 

At this point, the app is rendering a sphere, but it is black so it doesn't show up. To prove that @@ -185,94 +185,94 @@

Add lighting

In this section we will create some directional light sources, as well as an image-based light (IBL) defined by one of the KTX files we built at the start of the demo. First, replace the create lights comment with the following snippet.

-
const sunlight = Filament.EntityManager.get().create();
-scene.addEntity(sunlight);
-Filament.LightManager.Builder(LightType.SUN)
-  .color([0.98, 0.92, 0.89])
-  .intensity(110000.0)
-  .direction([0.6, -1.0, -0.8])
-  .sunAngularRadius(1.9)
-  .sunHaloSize(10.0)
-  .sunHaloFalloff(80.0)
-  .build(engine, sunlight);
-
-const backlight = Filament.EntityManager.get().create();
-scene.addEntity(backlight);
-Filament.LightManager.Builder(LightType.DIRECTIONAL)
-        .direction([-1, 0, 1])
-        .intensity(50000.0)
-        .build(engine, backlight);
+
const sunlight = Filament.EntityManager.get().create();
+scene.addEntity(sunlight);
+Filament.LightManager.Builder(LightType.SUN)
+  .color([0.98, 0.92, 0.89])
+  .intensity(110000.0)
+  .direction([0.6, -1.0, -0.8])
+  .sunAngularRadius(1.9)
+  .sunHaloSize(10.0)
+  .sunHaloFalloff(80.0)
+  .build(engine, sunlight);
+
+const backlight = Filament.EntityManager.get().create();
+scene.addEntity(backlight);
+Filament.LightManager.Builder(LightType.DIRECTIONAL)
+        .direction([-1, 0, 1])
+        .intensity(50000.0)
+        .build(engine, backlight);
 

The SUN light source is similar to the DIRECTIONAL light source, but has some extra parameters because Filament will automatically draw a disk into the skybox.

Next we need to create an IndirectLight object from the KTX IBL. One way of doing this is the following (don't type this out, there's an easier way).

-
const format = Filament.PixelDataFormat.RGB;
-const datatype = Filament.PixelDataType.UINT_10F_11F_11F_REV;
-
-// Create a Texture object for the mipmapped cubemap.
-const ibl_package = Filament.Buffer(Filament.assets[ibl_url]);
-const iblktx = new Filament.Ktx1Bundle(ibl_package);
-
-const ibltex = Filament.Texture.Builder()
-  .width(iblktx.info().pixelWidth)
-  .height(iblktx.info().pixelHeight)
-  .levels(iblktx.getNumMipLevels())
-  .sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)
-  .format(Filament.Texture$InternalFormat.RGBA8)
-  .build(engine);
-
-for (let level = 0; level < iblktx.getNumMipLevels(); ++level) {
-  const uint8array = iblktx.getCubeBlob(level).getBytes();
-  const pixelbuffer = Filament.PixelBuffer(uint8array, format, datatype);
-  ibltex.setImageCube(engine, level, pixelbuffer);
-}
-
-// Parse the spherical harmonics metadata.
-const shstring = iblktx.getMetadata('sh');
-const shfloats = shstring.split(/\s/, 9 * 3).map(parseFloat);
-
-// Build the IBL object and insert it into the scene.
-const indirectLight = Filament.IndirectLight.Builder()
-  .reflections(ibltex)
-  .irradianceSh(3, shfloats)
-  .intensity(50000.0)
-  .build(engine);
-
-scene.setIndirectLight(indirectLight);
+
const format = Filament.PixelDataFormat.RGB;
+const datatype = Filament.PixelDataType.UINT_10F_11F_11F_REV;
+
+// Create a Texture object for the mipmapped cubemap.
+const ibl_package = Filament.Buffer(Filament.assets[ibl_url]);
+const iblktx = new Filament.Ktx1Bundle(ibl_package);
+
+const ibltex = Filament.Texture.Builder()
+  .width(iblktx.info().pixelWidth)
+  .height(iblktx.info().pixelHeight)
+  .levels(iblktx.getNumMipLevels())
+  .sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)
+  .format(Filament.Texture$InternalFormat.RGBA8)
+  .build(engine);
+
+for (let level = 0; level < iblktx.getNumMipLevels(); ++level) {
+  const uint8array = iblktx.getCubeBlob(level).getBytes();
+  const pixelbuffer = Filament.PixelBuffer(uint8array, format, datatype);
+  ibltex.setImageCube(engine, level, pixelbuffer);
+}
+
+// Parse the spherical harmonics metadata.
+const shstring = iblktx.getMetadata('sh');
+const shfloats = shstring.split(/\s/, 9 * 3).map(parseFloat);
+
+// Build the IBL object and insert it into the scene.
+const indirectLight = Filament.IndirectLight.Builder()
+  .reflections(ibltex)
+  .irradianceSh(3, shfloats)
+  .intensity(50000.0)
+  .build(engine);
+
+scene.setIndirectLight(indirectLight);
 

Filament provides a JavaScript utility to make this simpler, simply replace the create IBL comment with the following snippet.

-
const indirectLight = engine.createIblFromKtx1(ibl_url);
-indirectLight.setIntensity(50000);
-scene.setIndirectLight(indirectLight);
+
const indirectLight = engine.createIblFromKtx1(ibl_url);
+indirectLight.setIntensity(50000);
+scene.setIndirectLight(indirectLight);
 

Add background

At this point you can run the demo and you should see a red plastic ball against a black background. Without a skybox, the reflections on the ball are not representative of its surroundings. Here's one way to create a texture for the skybox:

-
const sky_package = Filament.Buffer(Filament.assets[sky_url]);
-const skyktx = new Filament.Ktx1Bundle(sky_package);
-const skytex = Filament.Texture.Builder()
-  .width(skyktx.info().pixelWidth)
-  .height(skyktx.info().pixelHeight)
-  .levels(1)
-  .sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)
-  .format(Filament.Texture$InternalFormat.RGBA8)
-  .build(engine);
-
-const uint8array = skyktx.getCubeBlob(0).getBytes();
-const pixelbuffer = Filament.PixelBuffer(uint8array, format, datatype);
-skytex.setImageCube(engine, 0, pixelbuffer);
+
const sky_package = Filament.Buffer(Filament.assets[sky_url]);
+const skyktx = new Filament.Ktx1Bundle(sky_package);
+const skytex = Filament.Texture.Builder()
+  .width(skyktx.info().pixelWidth)
+  .height(skyktx.info().pixelHeight)
+  .levels(1)
+  .sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)
+  .format(Filament.Texture$InternalFormat.RGBA8)
+  .build(engine);
+
+const uint8array = skyktx.getCubeBlob(0).getBytes();
+const pixelbuffer = Filament.PixelBuffer(uint8array, format, datatype);
+skytex.setImageCube(engine, 0, pixelbuffer);
 

Filament provides a Javascript utility to make this easier. Replace create skybox with the following.

-
const skybox = engine.createSkyFromKtx1(sky_url);
-scene.setSkybox(skybox);
+
const skybox = engine.createSkyFromKtx1(sky_url);
+scene.setSkybox(skybox);
 

That's it, we now have a shiny red ball floating in an environment! The complete JavaScript file is diff --git a/docs/webgl/tutorial_suzanne.html b/docs/webgl/tutorial_suzanne.html index bfaf97e97cd..c0edb5ed0af 100644 --- a/docs/webgl/tutorial_suzanne.html +++ b/docs/webgl/tutorial_suzanne.html @@ -15,7 +15,7 @@

Create filamesh file

Filament does not have an asset loading system, but it does provide a binary mesh format called filamesh for simple use cases. Let's create a compressed filamesh file for suzanne by converting this OBJ file:

-
filamesh --compress monkey.obj suzanne.filamesh
+
filamesh --compress monkey.obj suzanne.filamesh
 

Create mipmapped textures

@@ -23,26 +23,26 @@

Create mipmapped textures

non-compressed variants for each texture, since not all platforms support the same compression formats. First copy over the PNG files from the monkey folder, then do:

# Create mipmaps for base color and two compressed variants.
-mipgen albedo.png albedo.ktx
-mipgen --compression=astc_fast_ldr_4x4 albedo.png albedo_astc.ktx
-mipgen --compression=s3tc_rgb_dxt1 albedo.png albedo_s3tc_srgb.ktx
+mipgen albedo.png albedo.ktx
+mipgen --compression=astc_fast_ldr_4x4 albedo.png albedo_astc.ktx
+mipgen --compression=s3tc_rgb_dxt1 albedo.png albedo_s3tc_srgb.ktx
 
 # Create mipmaps for the normal map and a compressed variant.
-mipgen --strip-alpha --kernel=NORMALS --linear normal.png normal.ktx
-mipgen --strip-alpha --kernel=NORMALS --linear --compression=etc_rgb8_normalxyz_40 \
-    normal.png normal_etc.ktx
+mipgen --strip-alpha --kernel=NORMALS --linear normal.png normal.ktx
+mipgen --strip-alpha --kernel=NORMALS --linear --compression=etc_rgb8_normalxyz_40 \
+    normal.png normal_etc.ktx
 
 # Create mipmaps for the single-component roughness map and a compressed variant.
-mipgen --grayscale roughness.png roughness.ktx
-mipgen --grayscale --compression=etc_r11_numeric_40 roughness.png roughness_etc.ktx
+mipgen --grayscale roughness.png roughness.ktx
+mipgen --grayscale --compression=etc_r11_numeric_40 roughness.png roughness_etc.ktx
 
 # Create mipmaps for the single-component metallic map and a compressed variant.
-mipgen --grayscale metallic.png metallic.ktx
-mipgen --grayscale --compression=etc_r11_numeric_40 metallic.png metallic_etc.ktx
+mipgen --grayscale metallic.png metallic.ktx
+mipgen --grayscale --compression=etc_r11_numeric_40 metallic.png metallic_etc.ktx
 
 # Create mipmaps for the single-component occlusion map and a compressed variant.
-mipgen --grayscale ao.png ao.ktx
-mipgen --grayscale --compression=etc_r11_numeric_40 ao.png ao_etc.ktx
+mipgen --grayscale ao.png ao.ktx
+mipgen --grayscale --compression=etc_r11_numeric_40 ao.png ao_etc.ktx
 

For more information on mipgen's arguments and supported formats, do mipgen --help.

@@ -50,12 +50,12 @@

Create mipmapped textures

Bake environment map

Much like the previous tutorial we need to use Filament's cmgen tool to produce cubemap files.

Download venetian_crossroads_2k.hdr, then invoke the following commands in your terminal.

-
cmgen -x . --format=ktx --size=64 --extract-blur=0.1 venetian_crossroads_2k.hdr
-cd venetian* ; mv venetian*_ibl.ktx venetian_crossroads_2k_skybox_tiny.ktx ; cd -
+
cmgen -x . --format=ktx --size=64 --extract-blur=0.1 venetian_crossroads_2k.hdr
+cd venetian* ; mv venetian*_ibl.ktx venetian_crossroads_2k_skybox_tiny.ktx ; cd -
 
-cmgen -x . --format=ktx --size=256 --extract-blur=0.1 venetian_crossroads_2k.hdr
-cmgen -x . --format=ktx --size=256 --extract-blur=0.1 venetian_crossroads_2k.hdr
-cmgen -x . --format=ktx --size=256 --extract-blur=0.1 venetian_crossroads_2k.hdr
+cmgen -x . --format=ktx --size=256 --extract-blur=0.1 venetian_crossroads_2k.hdr
+cmgen -x . --format=ktx --size=256 --extract-blur=0.1 venetian_crossroads_2k.hdr
+cmgen -x . --format=ktx --size=256 --extract-blur=0.1 venetian_crossroads_2k.hdr
 

Define textured material

@@ -91,7 +91,7 @@

Define textured material

Next, invoke matc as follows.

-
matc -a opengl -p mobile -o textured.filamat textured.mat
+
matc -a opengl -p mobile -o textured.filamat textured.mat
 

You should now have a material archive in your working directory. For the suzanne asset, the normal @@ -101,62 +101,62 @@

Create app skeleton

Create a text file called suzanne.html and copy over the HTML that we used in the previous tutorial. Change the last script tag from redball.js to suzanne.js. Next, create suzanne.js with the following content.

-
// TODO: declare asset URLs
-
-Filament.init([ filamat_url, filamesh_url, sky_small_url, ibl_url ], () => {
-    window.app = new App(document.getElementsByTagName('canvas')[0]);
-});
-
-class App {
-    constructor(canvas) {
-        this.canvas = canvas;
-        this.engine = Filament.Engine.create(canvas);
-        this.scene = this.engine.createScene();
-
-        const material = this.engine.createMaterial(filamat_url);
-        this.matinstance = material.createInstance();
-
-        const filamesh = this.engine.loadFilamesh(filamesh_url, this.matinstance);
-        this.suzanne = filamesh.renderable;
-
-        // TODO: create sky box and IBL
-        // TODO: initialize gltumble
-        // TODO: fetch larger assets
-
-        this.swapChain = this.engine.createSwapChain();
-        this.renderer = this.engine.createRenderer();
-        this.camera = this.engine.createCamera(Filament.EntityManager.get().create());
-        this.view = this.engine.createView();
-        this.view.setCamera(this.camera);
-        this.view.setScene(this.scene);
-        this.render = this.render.bind(this);
-        this.resize = this.resize.bind(this);
-        window.addEventListener('resize', this.resize);
-
-        const eye = [0, 0, 4], center = [0, 0, 0], up = [0, 1, 0];
-        this.camera.lookAt(eye, center, up);
-
-        this.resize();
-        window.requestAnimationFrame(this.render);
-    }
-
-    render() {
-        // TODO: apply gltumble matrix
-        this.renderer.render(this.swapChain, this.view);
-        window.requestAnimationFrame(this.render);
-    }
-
-    resize() {
-        const dpr = window.devicePixelRatio;
-        const width = this.canvas.width = window.innerWidth * dpr;
-        const height = this.canvas.height = window.innerHeight * dpr;
-        this.view.setViewport([0, 0, width, height]);
-
-        const aspect = width / height;
-        const Fov = Filament.Camera$Fov, fov = aspect < 1 ? Fov.HORIZONTAL : Fov.VERTICAL;
-        this.camera.setProjectionFov(45, aspect, 1.0, 10.0, fov);
-    }
-}
+
// TODO: declare asset URLs
+
+Filament.init([ filamat_url, filamesh_url, sky_small_url, ibl_url ], () => {
+    window.app = new App(document.getElementsByTagName('canvas')[0]);
+});
+
+class App {
+    constructor(canvas) {
+        this.canvas = canvas;
+        this.engine = Filament.Engine.create(canvas);
+        this.scene = this.engine.createScene();
+
+        const material = this.engine.createMaterial(filamat_url);
+        this.matinstance = material.createInstance();
+
+        const filamesh = this.engine.loadFilamesh(filamesh_url, this.matinstance);
+        this.suzanne = filamesh.renderable;
+
+        // TODO: create sky box and IBL
+        // TODO: initialize gltumble
+        // TODO: fetch larger assets
+
+        this.swapChain = this.engine.createSwapChain();
+        this.renderer = this.engine.createRenderer();
+        this.camera = this.engine.createCamera(Filament.EntityManager.get().create());
+        this.view = this.engine.createView();
+        this.view.setCamera(this.camera);
+        this.view.setScene(this.scene);
+        this.render = this.render.bind(this);
+        this.resize = this.resize.bind(this);
+        window.addEventListener('resize', this.resize);
+
+        const eye = [0, 0, 4], center = [0, 0, 0], up = [0, 1, 0];
+        this.camera.lookAt(eye, center, up);
+
+        this.resize();
+        window.requestAnimationFrame(this.render);
+    }
+
+    render() {
+        // TODO: apply gltumble matrix
+        this.renderer.render(this.swapChain, this.view);
+        window.requestAnimationFrame(this.render);
+    }
+
+    resize() {
+        const dpr = window.devicePixelRatio;
+        const width = this.canvas.width = window.innerWidth * dpr;
+        const height = this.canvas.height = window.innerHeight * dpr;
+        this.view.setViewport([0, 0, width, height]);
+
+        const aspect = width / height;
+        const Fov = Filament.Camera$Fov, fov = aspect < 1 ? Fov.HORIZONTAL : Fov.VERTICAL;
+        this.camera.setProjectionFov(45, aspect, 1.0, 10.0, fov);
+    }
+}
 

Our app will only require a subset of assets to be present for App construction. We'll download @@ -172,29 +172,29 @@

Create app skeleton

In our case, we know that our web server will have astc and s3tc variants for albedo, and etc variants for the other textures. The uncompressed variants (empty string) are always available as a last resort. Go ahead and replace the declare asset URLs comment with the following snippet.

-
const albedo_suffix = Filament.getSupportedFormatSuffix('astc s3tc_srgb');
-const texture_suffix = Filament.getSupportedFormatSuffix('etc');
-
-const environ = 'venetian_crossroads_2k'
-const ibl_url = `${environ}/${environ}_ibl.ktx`;
-const sky_small_url = `${environ}/${environ}_skybox_tiny.ktx`;
-const sky_large_url = `${environ}/${environ}_skybox.ktx`;
-const albedo_url = `albedo${albedo_suffix}.ktx`;
-const ao_url = `ao${texture_suffix}.ktx`;
-const metallic_url = `metallic${texture_suffix}.ktx`;
-const normal_url = `normal${texture_suffix}.ktx`;
-const roughness_url = `roughness${texture_suffix}.ktx`;
-const filamat_url = 'textured.filamat';
-const filamesh_url = 'suzanne.filamesh';
+
const albedo_suffix = Filament.getSupportedFormatSuffix('astc s3tc_srgb');
+const texture_suffix = Filament.getSupportedFormatSuffix('etc');
+
+const environ = 'venetian_crossroads_2k'
+const ibl_url = `${environ}/${environ}_ibl.ktx`;
+const sky_small_url = `${environ}/${environ}_skybox_tiny.ktx`;
+const sky_large_url = `${environ}/${environ}_skybox.ktx`;
+const albedo_url = `albedo${albedo_suffix}.ktx`;
+const ao_url = `ao${texture_suffix}.ktx`;
+const metallic_url = `metallic${texture_suffix}.ktx`;
+const normal_url = `normal${texture_suffix}.ktx`;
+const roughness_url = `roughness${texture_suffix}.ktx`;
+const filamat_url = 'textured.filamat';
+const filamesh_url = 'suzanne.filamesh';
 

Create skybox and IBL

Next, let's create the low-resolution skybox and IBL in the App constructor.

-
this.skybox = this.engine.createSkyFromKtx1(sky_small_url);
-this.scene.setSkybox(this.skybox);
-this.indirectLight = this.engine.createIblFromKtx1(ibl_url);
-this.indirectLight.setIntensity(100000);
-this.scene.setIndirectLight(this.indirectLight);
+
this.skybox = this.engine.createSkyFromKtx1(sky_small_url);
+this.scene.setSkybox(this.skybox);
+this.indirectLight = this.engine.createIblFromKtx1(ibl_url);
+this.indirectLight.setIntensity(100000);
+this.scene.setIndirectLight(this.indirectLight);
 

This allows users to see a reasonable background fairly quickly, before larger assets have finished @@ -206,31 +206,31 @@

Fetch assets asychronously

In our callback, we'll make several setTextureParameter calls on the material instance, then we'll recreate the skybox using a higher-resolution texture. As a last step we unhide the renderable that was created in the app constructor.

-
Filament.fetch([sky_large_url, albedo_url, roughness_url, metallic_url, normal_url, ao_url], () => {
-    const albedo = this.engine.createTextureFromKtx1(albedo_url, {srgb: true});
-    const roughness = this.engine.createTextureFromKtx1(roughness_url);
-    const metallic = this.engine.createTextureFromKtx1(metallic_url);
-    const normal = this.engine.createTextureFromKtx1(normal_url);
-    const ao = this.engine.createTextureFromKtx1(ao_url);
-
-    const sampler = new Filament.TextureSampler(
-        Filament.MinFilter.LINEAR_MIPMAP_LINEAR,
-        Filament.MagFilter.LINEAR,
-        Filament.WrapMode.CLAMP_TO_EDGE);
-
-    this.matinstance.setTextureParameter('albedo', albedo, sampler);
-    this.matinstance.setTextureParameter('roughness', roughness, sampler);
-    this.matinstance.setTextureParameter('metallic', metallic, sampler);
-    this.matinstance.setTextureParameter('normal', normal, sampler);
-    this.matinstance.setTextureParameter('ao', ao, sampler);
-
-    // Replace low-res skybox with high-res skybox.
-    this.engine.destroySkybox(this.skybox);
-    this.skybox = this.engine.createSkyFromKtx1(sky_large_url);
-    this.scene.setSkybox(this.skybox);
-
-    this.scene.addEntity(this.suzanne);
-});
+
Filament.fetch([sky_large_url, albedo_url, roughness_url, metallic_url, normal_url, ao_url], () => {
+    const albedo = this.engine.createTextureFromKtx1(albedo_url, {srgb: true});
+    const roughness = this.engine.createTextureFromKtx1(roughness_url);
+    const metallic = this.engine.createTextureFromKtx1(metallic_url);
+    const normal = this.engine.createTextureFromKtx1(normal_url);
+    const ao = this.engine.createTextureFromKtx1(ao_url);
+
+    const sampler = new Filament.TextureSampler(
+        Filament.MinFilter.LINEAR_MIPMAP_LINEAR,
+        Filament.MagFilter.LINEAR,
+        Filament.WrapMode.CLAMP_TO_EDGE);
+
+    this.matinstance.setTextureParameter('albedo', albedo, sampler);
+    this.matinstance.setTextureParameter('roughness', roughness, sampler);
+    this.matinstance.setTextureParameter('metallic', metallic, sampler);
+    this.matinstance.setTextureParameter('normal', normal, sampler);
+    this.matinstance.setTextureParameter('ao', ao, sampler);
+
+    // Replace low-res skybox with high-res skybox.
+    this.engine.destroySkybox(this.skybox);
+    this.skybox = this.engine.createSkyFromKtx1(sky_large_url);
+    this.scene.setSkybox(this.skybox);
+
+    this.scene.addEntity(this.suzanne);
+});
 

Introduce trackball rotation

@@ -241,13 +241,13 @@

Introduce trackball rotation

Next, replace the initialize gltumble and apply gltumble matrix comments with the following two code snippets.

-
this.trackball = new Trackball(canvas, {startSpin: 0.035});
+
this.trackball = new Trackball(canvas, {startSpin: 0.035});
 
-
const tcm = this.engine.getTransformManager();
-const inst = tcm.getInstance(this.suzanne);
-tcm.setTransform(inst, this.trackball.getMatrix());
-inst.delete();
+
const tcm = this.engine.getTransformManager();
+const inst = tcm.getInstance(this.suzanne);
+tcm.setTransform(inst, this.trackball.getMatrix());
+inst.delete();
 

That's it, we now have a fast-loading interactive demo. The complete JavaScript file is available diff --git a/docs/webgl/tutorial_triangle.html b/docs/webgl/tutorial_triangle.html index ab2df5a7feb..9f87d040549 100644 --- a/docs/webgl/tutorial_triangle.html +++ b/docs/webgl/tutorial_triangle.html @@ -22,9 +22,9 @@

Start your project

<title>Filament Tutorial</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1"> - <style> - body { margin: 0; overflow: hidden; } - canvas { touch-action: none; width: 100%; height: 100%; } + <style> + body { margin: 0; overflow: hidden; } + canvas { touch-action: none; width: 100%; height: 100%; } </style> </head> <body> @@ -48,24 +48,24 @@

Start your project

  • triangle.js will contain your application code.
  • Go ahead and create triangle.js with the following content.

    -
    class App {
    -  constructor() {
    -    // TODO: create entities
    -    this.render = this.render.bind(this);
    -    this.resize = this.resize.bind(this);
    -    window.addEventListener('resize', this.resize);
    -    window.requestAnimationFrame(this.render);
    -  }
    -  render() {
    -    // TODO: render scene
    -    window.requestAnimationFrame(this.render);
    -  }
    -  resize() {
    -    // TODO: adjust viewport and canvas
    -  }
    -}
    -
    -Filament.init(['triangle.filamat'], () => { window.app = new App() } );
    +
    class App {
    +  constructor() {
    +    // TODO: create entities
    +    this.render = this.render.bind(this);
    +    this.resize = this.resize.bind(this);
    +    window.addEventListener('resize', this.resize);
    +    window.requestAnimationFrame(this.render);
    +  }
    +  render() {
    +    // TODO: render scene
    +    window.requestAnimationFrame(this.render);
    +  }
    +  resize() {
    +    // TODO: adjust viewport and canvas
    +  }
    +}
    +
    +Filament.init(['triangle.filamat'], () => { window.app = new App() } );
     

    The two calls to bind() allow us to pass instance methods as callbacks for animation and resize @@ -81,9 +81,9 @@

    Start your project

    Spawn a local server

    Because of CORS restrictions, your web app cannot fetch the material package directly from the file system. One way around this is to create a temporary server using Python or node:

    -
    python3 -m http.server     # Python 3
    -python -m SimpleHTTPServer # Python 2.7
    -npx http-server -p 8000    # nodejs
    +
    python3 -m http.server     # Python 3
    +python -m SimpleHTTPServer # Python 2.7
    +npx http-server -p 8000    # nodejs
     

    To see if this works, navigate to http://localhost:8000 and check if you @@ -93,8 +93,8 @@

    Spawn a local server

    Create the Engine and Scene

    We now have a basic skeleton that can respond to paint and resize events. Let's start adding Filament objects to the app. Insert the following code into the top of the app constructor.

    -
    this.canvas = document.getElementsByTagName('canvas')[0];
    -const engine = this.engine = Filament.Engine.create(this.canvas);
    +
    this.canvas = document.getElementsByTagName('canvas')[0];
    +const engine = this.engine = Filament.Engine.create(this.canvas);
     

    The above snippet creates the Engine by passing it a canvas DOM object. The engine needs the @@ -102,9 +102,9 @@

    Create the Engine and Scene

    The engine is a factory for many Filament entities, including Scene, which is a flat container of entities. Let's go ahead and create a scene, then add a blank entity called triangle into the scene.

    -
    this.scene = engine.createScene();
    -this.triangle = Filament.EntityManager.get().create();
    -this.scene.addEntity(this.triangle);
    +
    this.scene = engine.createScene();
    +this.triangle = Filament.EntityManager.get().create();
    +this.scene.addEntity(this.triangle);
     

    Filament uses an Entity-Component System. @@ -114,28 +114,28 @@

    Create the Engine and Scene

    Construct typed arrays

    Next we'll create two typed arrays: a positions array with XY coordinates for each vertex, and a colors array with a 32-bit word for each vertex.

    -
    const TRIANGLE_POSITIONS = new Float32Array([
    -    1, 0,
    -    Math.cos(Math.PI * 2 / 3), Math.sin(Math.PI * 2 / 3),
    -    Math.cos(Math.PI * 4 / 3), Math.sin(Math.PI * 4 / 3),
    -]);
    +
    const TRIANGLE_POSITIONS = new Float32Array([
    +    1, 0,
    +    Math.cos(Math.PI * 2 / 3), Math.sin(Math.PI * 2 / 3),
    +    Math.cos(Math.PI * 4 / 3), Math.sin(Math.PI * 4 / 3),
    +]);
     
    -const TRIANGLE_COLORS = new Uint32Array([0xffff0000, 0xff00ff00, 0xff0000ff]);
    +const TRIANGLE_COLORS = new Uint32Array([0xffff0000, 0xff00ff00, 0xff0000ff]);
     

    Next we'll use the positions and colors buffers to create a single VertexBuffer object.

    -
    const VertexAttribute = Filament.VertexAttribute;
    -const AttributeType = Filament.VertexBuffer$AttributeType;
    -this.vb = Filament.VertexBuffer.Builder()
    -    .vertexCount(3)
    -    .bufferCount(2)
    -    .attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT2, 0, 8)
    -    .attribute(VertexAttribute.COLOR, 1, AttributeType.UBYTE4, 0, 4)
    -    .normalized(VertexAttribute.COLOR)
    -    .build(engine);
    -
    -this.vb.setBufferAt(engine, 0, TRIANGLE_POSITIONS);
    -this.vb.setBufferAt(engine, 1, TRIANGLE_COLORS);
    +
    const VertexAttribute = Filament.VertexAttribute;
    +const AttributeType = Filament.VertexBuffer$AttributeType;
    +this.vb = Filament.VertexBuffer.Builder()
    +    .vertexCount(3)
    +    .bufferCount(2)
    +    .attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT2, 0, 8)
    +    .attribute(VertexAttribute.COLOR, 1, AttributeType.UBYTE4, 0, 4)
    +    .normalized(VertexAttribute.COLOR)
    +    .build(engine);
    +
    +this.vb.setBufferAt(engine, 0, TRIANGLE_POSITIONS);
    +this.vb.setBufferAt(engine, 1, TRIANGLE_COLORS);
     

    The above snippet first creates aliases for two enum types, then constructs the vertex buffer using @@ -149,12 +149,12 @@

    Construct typed arrays

    buffer slot.

    Next we'll construct an index buffer. The index buffer for our triangle is trivial: it simply holds the integers 0,1,2.

    -
    this.ib = Filament.IndexBuffer.Builder()
    -    .indexCount(3)
    -    .bufferType(Filament.IndexBuffer$IndexType.USHORT)
    -    .build(engine);
    +
    this.ib = Filament.IndexBuffer.Builder()
    +    .indexCount(3)
    +    .bufferType(Filament.IndexBuffer$IndexType.USHORT)
    +    .build(engine);
     
    -this.ib.setBuffer(engine, new Uint16Array([0, 1, 2]));
    +this.ib.setBuffer(engine, new Uint16Array([0, 1, 2]));
     

    Note that constructing an index buffer is similar to constructing a vertex buffer, but it only has @@ -167,29 +167,29 @@

    Finish up initialization

    next tutorial.

    After extracting the material instance, we can finally create a renderable component for the triangle by setting up a bounding box and passing in the vertex and index buffers.

    -
    const mat = engine.createMaterial('triangle.filamat');
    -const matinst = mat.getDefaultInstance();
    -Filament.RenderableManager.Builder(1)
    -    .boundingBox({ center: [-1, -1, -1], halfExtent: [1, 1, 1] })
    -    .material(0, matinst)
    -    .geometry(0, Filament.RenderableManager$PrimitiveType.TRIANGLES, this.vb, this.ib)
    -    .build(engine, this.triangle);
    +
    const mat = engine.createMaterial('triangle.filamat');
    +const matinst = mat.getDefaultInstance();
    +Filament.RenderableManager.Builder(1)
    +    .boundingBox({ center: [-1, -1, -1], halfExtent: [1, 1, 1] })
    +    .material(0, matinst)
    +    .geometry(0, Filament.RenderableManager$PrimitiveType.TRIANGLES, this.vb, this.ib)
    +    .build(engine, this.triangle);
     

    Next let's wrap up the initialization routine by creating the swap chain, renderer, camera, and view.

    -
    this.swapChain = engine.createSwapChain();
    -this.renderer = engine.createRenderer();
    -this.camera = engine.createCamera(Filament.EntityManager.get().create());
    -this.view = engine.createView();
    -this.view.setCamera(this.camera);
    -this.view.setScene(this.scene);
    -
    -// Set up a blue-green background:
    -this.renderer.setClearOptions({clearColor: [0.0, 0.1, 0.2, 1.0], clear: true});
    -
    -// Adjust the initial viewport:
    -this.resize();
    +
    this.swapChain = engine.createSwapChain();
    +this.renderer = engine.createRenderer();
    +this.camera = engine.createCamera(Filament.EntityManager.get().create());
    +this.view = engine.createView();
    +this.view.setCamera(this.camera);
    +this.view.setScene(this.scene);
    +
    +// Set up a blue-green background:
    +this.renderer.setClearOptions({clearColor: [0.0, 0.1, 0.2, 1.0], clear: true});
    +
    +// Adjust the initial viewport:
    +this.resize();
     

    At this point, we're done creating all Filament entities, and the code should run without errors. @@ -197,24 +197,24 @@

    Finish up initialization

    Render and resize handlers

    Recall that our App class has a skeletal render method, which the browser calls every time it needs to repaint. Often this is 60 times a second.

    -
    render() {
    -    // TODO: render scene
    -    window.requestAnimationFrame(this.render);
    -}
    +
    render() {
    +    // TODO: render scene
    +    window.requestAnimationFrame(this.render);
    +}
     

    Let's flesh this out by rotating the triangle and invoking the Filament renderer. Add the following code to the top of the render method.

    -
    // Rotate the triangle.
    -const radians = Date.now() / 1000;
    -const transform = mat4.fromRotation(mat4.create(), radians, [0, 0, 1]);
    -const tcm = this.engine.getTransformManager();
    -const inst = tcm.getInstance(this.triangle);
    -tcm.setTransform(inst, transform);
    -inst.delete();
    -
    -// Render the frame.
    -this.renderer.render(this.swapChain, this.view);
    +
    // Rotate the triangle.
    +const radians = Date.now() / 1000;
    +const transform = mat4.fromRotation(mat4.create(), radians, [0, 0, 1]);
    +const tcm = this.engine.getTransformManager();
    +const inst = tcm.getInstance(this.triangle);
    +tcm.setTransform(inst, transform);
    +inst.delete();
    +
    +// Render the frame.
    +this.renderer.render(this.swapChain, this.view);
     

    The first half of our render method obtains the transform component of the triangle entity and uses @@ -225,14 +225,14 @@

    Render and resize handlers

    One last step. Add the following code to the resize method. This adjusts the resolution of the rendering surface when the window size changes, taking devicePixelRatio into account for high-DPI displays. It also adjusts the camera frustum accordingly.

    -
    const dpr = window.devicePixelRatio;
    -const width = this.canvas.width = window.innerWidth * dpr;
    -const height = this.canvas.height = window.innerHeight * dpr;
    -this.view.setViewport([0, 0, width, height]);
    -
    -const aspect = width / height;
    -const Projection = Filament.Camera$Projection;
    -this.camera.setProjection(Projection.ORTHO, -aspect, aspect, -1, 1, 0, 1);
    +
    const dpr = window.devicePixelRatio;
    +const width = this.canvas.width = window.innerWidth * dpr;
    +const height = this.canvas.height = window.innerHeight * dpr;
    +this.view.setViewport([0, 0, width, height]);
    +
    +const aspect = width / height;
    +const Projection = Filament.Camera$Projection;
    +this.camera.setProjection(Projection.ORTHO, -aspect, aspect, -1, 1, 0, 1);
     

    You should now have a spinning triangle! The completed JavaScript is available diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index 946b55d1c7b..6f68d2802af 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -179,12 +179,15 @@ if (FILAMENT_SUPPORTS_VULKAN) src/vulkan/VulkanPipelineCache.cpp src/vulkan/VulkanPipelineCache.h src/vulkan/VulkanPlatform.cpp + src/vulkan/VulkanReadPixels.cpp + src/vulkan/VulkanReadPixels.h src/vulkan/VulkanSamplerCache.cpp src/vulkan/VulkanSamplerCache.h src/vulkan/VulkanStagePool.cpp src/vulkan/VulkanStagePool.h src/vulkan/VulkanSwapChain.cpp src/vulkan/VulkanSwapChain.h + src/vulkan/VulkanTaskHandler.h src/vulkan/VulkanTexture.cpp src/vulkan/VulkanTexture.h src/vulkan/VulkanUtility.cpp diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 27c57e32f9e..6b3c872efe1 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1287,6 +1287,7 @@ fromRegion:srcRegion mipmapLevel:0]; scheduleDestroy(std::move(*p)); + delete p; }]; } diff --git a/filament/backend/src/opengl/GLUtils.h b/filament/backend/src/opengl/GLUtils.h index bdb1327a285..d37da813044 100644 --- a/filament/backend/src/opengl/GLUtils.h +++ b/filament/backend/src/opengl/GLUtils.h @@ -120,7 +120,7 @@ constexpr inline GLenum getBufferBindingType(BufferObjectBinding bindingType) no case BufferObjectBinding::UNIFORM: return GL_UNIFORM_BUFFER; case BufferObjectBinding::SHADER_STORAGE: -#if defined(GL_VERSION_4_1) || defined(GL_ES_VERSION_3_1) +#if defined(BACKEND_OPENGL_LEVEL_GLES31) return GL_SHADER_STORAGE_BUFFER; #else utils::panic(__func__, __FILE__, __LINE__, "SHADER_STORAGE not supported"); @@ -423,7 +423,7 @@ constexpr /* inline */ GLenum getInternalFormat(TextureFormat format) noexcept { case TextureFormat::RGBA32I: return GL_RGBA32I; // compressed formats -#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_4_3) || defined(GL_ARB_ES3_compatibility) +#if defined(GL_ES_VERSION_3_0) || defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_ARB_ES3_compatibility) case TextureFormat::EAC_R11: return GL_COMPRESSED_R11_EAC; case TextureFormat::EAC_R11_SIGNED: return GL_COMPRESSED_SIGNED_R11_EAC; case TextureFormat::EAC_RG11: return GL_COMPRESSED_RG11_EAC; diff --git a/filament/backend/src/opengl/OpenGLContext.cpp b/filament/backend/src/opengl/OpenGLContext.cpp index 48772dcdf99..d65b3b4043d 100644 --- a/filament/backend/src/opengl/OpenGLContext.cpp +++ b/filament/backend/src/opengl/OpenGLContext.cpp @@ -28,17 +28,17 @@ using namespace utils; namespace filament::backend { bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept { - if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES) { - char const* version = (char const*)glGetString(GL_VERSION); - // This works on all versions of GLES - int const n = version ? sscanf(version, "OpenGL ES %d.%d", major, minor) : 0; - return n == 2; - } else if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) { - // OpenGL version - glGetIntegerv(GL_MAJOR_VERSION, major); - glGetIntegerv(GL_MINOR_VERSION, minor); - return (glGetError() == GL_NO_ERROR); - } +#ifdef BACKEND_OPENGL_VERSION_GLES + char const* version = (char const*)glGetString(GL_VERSION); + // This works on all versions of GLES + int const n = version ? sscanf(version, "OpenGL ES %d.%d", major, minor) : 0; + return n == 2; +#else + // OpenGL version + glGetIntegerv(GL_MAJOR_VERSION, major); + glGetIntegerv(GL_MINOR_VERSION, minor); + return (glGetError() == GL_NO_ERROR); +#endif } OpenGLContext::OpenGLContext() noexcept { @@ -73,50 +73,48 @@ OpenGLContext::OpenGLContext() noexcept { constexpr GLint MAX_VERTEX_SAMPLER_COUNT = caps3.MAX_VERTEX_SAMPLER_COUNT; constexpr GLint MAX_FRAGMENT_SAMPLER_COUNT = caps3.MAX_FRAGMENT_SAMPLER_COUNT; - if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES) { -#if defined(GL_ES_VERSION_2_0) - initExtensionsGLES(); -#endif - if (state.major == 3) { - assert_invariant(gets.max_texture_image_units >= 16); - assert_invariant(gets.max_combined_texture_image_units >= 32); - if (state.minor >= 1) { - features.multisample_texture = true; - // figure out our feature level - if (ext.EXT_texture_cube_map_array) { - mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2; - if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT && - gets.max_combined_texture_image_units >= - (MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) { - mFeatureLevel = FeatureLevel::FEATURE_LEVEL_3; - } - } - } - } - } else if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) { -#if defined(GL_VERSION_4_1) - initExtensionsGL(); -#endif - if (state.major == 4) { - assert_invariant(state.minor >= 1); - mShaderModel = ShaderModel::DESKTOP; - if (state.minor >= 3) { - // cubemap arrays are available as of OpenGL 4.0 +#ifdef BACKEND_OPENGL_VERSION_GLES + initExtensionsGLES(); + if (state.major == 3) { + // Runtime OpenGL version is ES 3.x + assert_invariant(gets.max_texture_image_units >= 16); + assert_invariant(gets.max_combined_texture_image_units >= 32); + if (state.minor >= 1) { + features.multisample_texture = true; + // figure out our feature level + if (ext.EXT_texture_cube_map_array) { mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2; - // figure out our feature level if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT && gets.max_combined_texture_image_units >= (MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) { mFeatureLevel = FeatureLevel::FEATURE_LEVEL_3; } } - features.multisample_texture = true; } - // feedback loops are allowed on GL desktop as long as writes are disabled - bugs.allow_read_only_ancillary_feedback_loop = true; - assert_invariant(gets.max_texture_image_units >= 16); - assert_invariant(gets.max_combined_texture_image_units >= 32); } +#else + initExtensionsGL(); + if (state.major == 4) { + assert_invariant(state.minor >= 1); + mShaderModel = ShaderModel::DESKTOP; + if (state.minor >= 3) { + // cubemap arrays are available as of OpenGL 4.0 + mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2; + // figure out our feature level + if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT && + gets.max_combined_texture_image_units >= + (MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) { + mFeatureLevel = FeatureLevel::FEATURE_LEVEL_3; + } + } + features.multisample_texture = true; + } + // feedback loops are allowed on GL desktop as long as writes are disabled + bugs.allow_read_only_ancillary_feedback_loop = true; + assert_invariant(gets.max_texture_image_units >= 16); + assert_invariant(gets.max_combined_texture_image_units >= 32); +#endif + #ifdef GL_EXT_texture_filter_anisotropic if (ext.EXT_texture_filter_anisotropic) { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gets.max_anisotropy); @@ -337,7 +335,7 @@ void OpenGLContext::setDefaultState() noexcept { // Point sprite size and seamless cubemap filtering are disabled by default in desktop GL. // In OpenGL ES, these flags do not exist because they are always on. -#if BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL +#ifdef BACKEND_OPENGL_VERSION_GL glEnable(GL_PROGRAM_POINT_SIZE); enable(GL_PROGRAM_POINT_SIZE); #endif @@ -351,14 +349,16 @@ void OpenGLContext::setDefaultState() noexcept { glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST); #endif -#if defined(GL_EXT_clip_control) || defined(GL_ARB_clip_control) || defined(GL_VERSION_4_5) if (ext.EXT_clip_control) { +#if defined(BACKEND_OPENGL_VERSION_GL) glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); - } +#elif defined(GL_EXT_clip_control) + glClipControlEXT(GL_LOWER_LEFT_EXT, GL_ZERO_TO_ONE_EXT); #endif + } } -#if defined(GL_ES_VERSION_2_0) +#ifdef BACKEND_OPENGL_VERSION_GLES void OpenGLContext::initExtensionsGLES() noexcept { const char * const extensions = (const char*)glGetString(GL_EXTENSIONS); @@ -394,7 +394,6 @@ void OpenGLContext::initExtensionsGLES() noexcept { ext.KHR_texture_compression_astc_hdr = exts.has("GL_KHR_texture_compression_astc_hdr"sv); ext.KHR_texture_compression_astc_ldr = exts.has("GL_KHR_texture_compression_astc_ldr"sv); ext.OES_EGL_image_external_essl3 = exts.has("GL_OES_EGL_image_external_essl3"sv); - ext.QCOM_tiled_rendering = exts.has("GL_QCOM_tiled_rendering"sv); ext.WEBGL_compressed_texture_etc = exts.has("WEBGL_compressed_texture_etc"sv); ext.WEBGL_compressed_texture_s3tc = exts.has("WEBGL_compressed_texture_s3tc"sv); ext.WEBGL_compressed_texture_s3tc_srgb = exts.has("WEBGL_compressed_texture_s3tc_srgb"sv); @@ -404,9 +403,9 @@ void OpenGLContext::initExtensionsGLES() noexcept { } } -#endif // defined(GL_ES_VERSION_2_0) +#endif // BACKEND_OPENGL_VERSION_GLES -#if defined(GL_VERSION_4_1) +#ifdef BACKEND_OPENGL_VERSION_GL void OpenGLContext::initExtensionsGL() noexcept { GLUtils::unordered_string_set exts; @@ -427,7 +426,7 @@ void OpenGLContext::initExtensionsGL() noexcept { auto minor = state.minor; ext.APPLE_color_buffer_packed_float = true; // Assumes core profile. ext.ARB_shading_language_packing = exts.has("GL_ARB_shading_language_packing"sv) || (major == 4 && minor >= 2); - ext.EXT_clip_control = exts.has("GL_ARB_clip_control"sv) || (major == 4 && minor >= 5); + ext.EXT_clip_control = (major == 4 && minor >= 5); ext.EXT_color_buffer_float = true; // Assumes core profile. ext.EXT_color_buffer_half_float = true; // Assumes core profile. ext.EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv); @@ -448,13 +447,12 @@ void OpenGLContext::initExtensionsGL() noexcept { ext.KHR_texture_compression_astc_hdr = exts.has("GL_KHR_texture_compression_astc_hdr"sv); ext.KHR_texture_compression_astc_ldr = exts.has("GL_KHR_texture_compression_astc_ldr"sv); ext.OES_EGL_image_external_essl3 = false; - ext.QCOM_tiled_rendering = false; ext.WEBGL_compressed_texture_etc = false; ext.WEBGL_compressed_texture_s3tc = false; ext.WEBGL_compressed_texture_s3tc_srgb = false; } -#endif // defined(GL_VERSION_4_1) +#endif // BACKEND_OPENGL_VERSION_GL void OpenGLContext::bindBuffer(GLenum target, GLuint buffer) noexcept { if (target == GL_ELEMENT_ARRAY_BUFFER) { @@ -636,7 +634,7 @@ void OpenGLContext::resetState() noexcept { GLenum const bufferTargets[] = { GL_UNIFORM_BUFFER, GL_TRANSFORM_FEEDBACK_BUFFER, -#if !defined(__EMSCRIPTEN__) && (defined(GL_VERSION_4_1) || defined(GL_ES_VERSION_3_1)) +#if defined(BACKEND_OPENGL_LEVEL_GLES31) GL_SHADER_STORAGE_BUFFER, #endif GL_ARRAY_BUFFER, @@ -667,14 +665,14 @@ void OpenGLContext::resetState() noexcept { { GL_TEXTURE_2D_ARRAY, true }, { GL_TEXTURE_CUBE_MAP, true }, { GL_TEXTURE_3D, true }, -#if !defined(__EMSCRIPTEN__) -#if defined(GL_VERSION_4_1) || defined(GL_ES_VERSION_3_1) +#if defined(BACKEND_OPENGL_LEVEL_GLES31) { GL_TEXTURE_2D_MULTISAMPLE, true }, #endif +#if !defined(__EMSCRIPTEN__) #if defined(GL_OES_EGL_image_external) { GL_TEXTURE_EXTERNAL_OES, ext.OES_EGL_image_external_essl3 }, #endif -#if defined(GL_VERSION_4_1) || defined(GL_EXT_texture_cube_map_array) +#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_texture_cube_map_array) { GL_TEXTURE_CUBE_MAP_ARRAY, ext.EXT_texture_cube_map_array }, #endif #endif diff --git a/filament/backend/src/opengl/OpenGLContext.h b/filament/backend/src/opengl/OpenGLContext.h index ded404f83aa..4b54cc1cf29 100644 --- a/filament/backend/src/opengl/OpenGLContext.h +++ b/filament/backend/src/opengl/OpenGLContext.h @@ -69,6 +69,24 @@ class OpenGLContext { OpenGLContext() noexcept; + constexpr bool isAtLeastGL(int major, int minor) const noexcept { +#ifdef BACKEND_OPENGL_VERSION_GL + return state.major > major || (state.major == major && state.minor >= minor); +#else + (void)major, (void)minor; + return false; +#endif + } + + constexpr bool isAtLeastGLES(int major, int minor) const noexcept { +#ifdef BACKEND_OPENGL_VERSION_GLES + return state.major > major || (state.major == major && state.minor >= minor); +#else + (void)major, (void)minor; + return false; +#endif + } + constexpr static inline size_t getIndexForTextureTarget(GLuint target) noexcept; constexpr inline size_t getIndexForCap(GLenum cap) noexcept; constexpr static inline size_t getIndexForBufferTarget(GLenum target) noexcept; @@ -184,7 +202,6 @@ class OpenGLContext { bool KHR_texture_compression_astc_hdr; bool KHR_texture_compression_astc_ldr; bool OES_EGL_image_external_essl3; - bool QCOM_tiled_rendering; bool WEBGL_compressed_texture_etc; bool WEBGL_compressed_texture_s3tc; bool WEBGL_compressed_texture_s3tc_srgb; @@ -413,10 +430,10 @@ class OpenGLContext { RenderPrimitive mDefaultVAO; // this is chosen to minimize code size -#if defined(GL_ES_VERSION_2_0) +#if defined(BACKEND_OPENGL_VERSION_GLES) void initExtensionsGLES() noexcept; #endif -#if defined(GL_VERSION_4_1) +#if defined(BACKEND_OPENGL_VERSION_GL) void initExtensionsGL() noexcept; #endif @@ -443,7 +460,7 @@ constexpr size_t OpenGLContext::getIndexForTextureTarget(GLuint target) noexcept case GL_TEXTURE_2D: return 0; case GL_TEXTURE_2D_ARRAY: return 1; case GL_TEXTURE_CUBE_MAP: return 2; -#if defined(GL_VERSION_4_1) || defined(GL_ES_VERSION_3_1) +#if defined(BACKEND_OPENGL_LEVEL_GLES31) case GL_TEXTURE_2D_MULTISAMPLE: return 3; #endif case GL_TEXTURE_EXTERNAL_OES: return 4; @@ -469,7 +486,7 @@ constexpr size_t OpenGLContext::getIndexForCap(GLenum cap) noexcept { //NOLINT #ifdef GL_ARB_seamless_cube_map case GL_TEXTURE_CUBE_MAP_SEAMLESS: index = 9; break; #endif -#if BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL +#ifdef BACKEND_OPENGL_VERSION_GL case GL_PROGRAM_POINT_SIZE: index = 10; break; #endif default: break; @@ -484,7 +501,7 @@ constexpr size_t OpenGLContext::getIndexForBufferTarget(GLenum target) noexcept // The indexed buffers MUST be first in this list (those usable with bindBufferRange) case GL_UNIFORM_BUFFER: index = 0; break; case GL_TRANSFORM_FEEDBACK_BUFFER: index = 1; break; -#if defined(GL_VERSION_4_1) || defined(GL_ES_VERSION_3_1) +#if defined(BACKEND_OPENGL_LEVEL_GLES31) case GL_SHADER_STORAGE_BUFFER: index = 2; break; #endif case GL_ARRAY_BUFFER: index = 3; break; diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 0a3ab8ac9a2..c7f5c2d832c 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -131,18 +131,18 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform, return {}; } - if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES) { - if (UTILS_UNLIKELY(!(major >= 3 && minor >= 0))) { - PANIC_LOG("OpenGL ES 3.0 minimum needed (current %d.%d)", major, minor); - goto cleanup; - } - } else if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) { - // we require GL 4.1 headers and minimum version - if (UTILS_UNLIKELY(!((major == 4 && minor >= 1) || major > 4))) { - PANIC_LOG("OpenGL 4.1 minimum needed (current %d.%d)", major, minor); - goto cleanup; - } +#if defined(BACKEND_OPENGL_VERSION_GLES) + if (UTILS_UNLIKELY(!(major >= 3 && minor >= 0))) { + PANIC_LOG("OpenGL ES 3.0 minimum needed (current %d.%d)", major, minor); + goto cleanup; + } +#else + // we require GL 4.1 headers and minimum version + if (UTILS_UNLIKELY(!((major == 4 && minor >= 1) || major > 4))) { + PANIC_LOG("OpenGL 4.1 minimum needed (current %d.%d)", major, minor); + goto cleanup; } +#endif size_t const defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U; Platform::DriverConfig const validConfig { @@ -182,9 +182,13 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi // Timer queries are core in GL 3.3, otherwise we need EXT_disjoint_timer_query // iOS headers don't define GL_EXT_disjoint_timer_query, so make absolutely sure // we won't use it. -#if defined(GL_VERSION_3_3) || defined(GL_EXT_disjoint_timer_query) - if (mContext.ext.EXT_disjoint_timer_query || - BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) { + +#if defined(BACKEND_OPENGL_VERSION_GL) + assert_invariant(mContext.ext.EXT_disjoint_timer_query); +#endif + +#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query) + if (mContext.ext.EXT_disjoint_timer_query) { // timer queries are available if (mContext.bugs.dont_use_timer_query && mPlatform.canCreateFence()) { // however, they don't work well, revert to using fences if we can. @@ -545,25 +549,29 @@ void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t, GLsizei(width), GLsizei(height), GLsizei(depth) * 6); break; } -#if defined(GL_VERSION_4_1) || defined(GL_ES_VERSION_3_1) +#ifdef BACKEND_OPENGL_LEVEL_GLES31 case GL_TEXTURE_2D_MULTISAMPLE: if constexpr (TEXTURE_2D_MULTISAMPLE_SUPPORTED) { // NOTE: if there is a mix of texture and renderbuffers, "fixed_sample_locations" must be true // NOTE: what's the benefit of setting "fixed_sample_locations" to false? -#if BACKEND_OPENGL_LEVEL >= BACKEND_OPENGL_LEVEL_GLES31 - // only supported from GL 4.3 and GLES 3.1 headers - glTexStorage2DMultisample(t->gl.target, t->samples, t->gl.internalFormat, - GLsizei(width), GLsizei(height), GL_TRUE); -#elif defined(GL_VERSION_4_1) - // only supported in GL (GL4.1 doesn't support glTexStorage2DMultisample) - glTexImage2DMultisample(t->gl.target, t->samples, t->gl.internalFormat, - GLsizei(width), GLsizei(height), GL_TRUE); + + if (mContext.isAtLeastGL(4, 3) || mContext.isAtLeastGLES(3, 1)) { + // only supported from GL 4.3 and GLES 3.1 headers + glTexStorage2DMultisample(t->gl.target, t->samples, t->gl.internalFormat, + GLsizei(width), GLsizei(height), GL_TRUE); + } +#ifdef BACKEND_OPENGL_VERSION_GL + else { + // only supported in GL (GL4.1 doesn't support glTexStorage2DMultisample) + glTexImage2DMultisample(t->gl.target, t->samples, t->gl.internalFormat, + GLsizei(width), GLsizei(height), GL_TRUE); + } #endif } else { PANIC_LOG("GL_TEXTURE_2D_MULTISAMPLE is not supported"); } break; -#endif +#endif // BACKEND_OPENGL_LEVEL_GLES31 default: // cannot happen break; } @@ -631,7 +639,7 @@ void OpenGLDriver::createTextureR(Handle th, SamplerType target, uint if (t->samples > 1) { // Note: we can't be here in practice because filament's user API doesn't // allow the creation of multi-sampled textures. -#if defined(GL_VERSION_4_1) || defined(GL_ES_VERSION_3_1) +#if defined(BACKEND_OPENGL_LEVEL_GLES31) if (gl.features.multisample_texture) { // multi-sample texture on GL 3.2 / GLES 3.1 and above t->gl.target = GL_TEXTURE_2D_MULTISAMPLE; @@ -733,7 +741,7 @@ void OpenGLDriver::importTextureR(Handle th, intptr_t id, if (t->samples > 1) { // Note: we can't be here in practice because filament's user API doesn't // allow the creation of multi-sampled textures. -#if defined(GL_VERSION_4_1) || defined(GL_ES_VERSION_3_1) +#if defined(BACKEND_OPENGL_LEVEL_GLES31) if (gl.features.multisample_texture) { // multi-sample texture on GL 3.2 / GLES 3.1 and above t->gl.target = GL_TEXTURE_2D_MULTISAMPLE; @@ -922,7 +930,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo, case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: case GL_TEXTURE_2D: -#if defined(GL_VERSION_4_1) || defined(GL_ES_VERSION_3_1) +#if defined(BACKEND_OPENGL_LEVEL_GLES31) case GL_TEXTURE_2D_MULTISAMPLE: #endif if (any(t->usage & TextureUsage::SAMPLEABLE)) { @@ -1649,7 +1657,7 @@ bool OpenGLDriver::isRenderTargetFormatSupported(TextureFormat format) { // Three-component SRGB is a color-renderable texture format in core OpenGL on desktop. case TextureFormat::SRGB8: - return BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL; + return mContext.isAtLeastGL(4, 5); // Half-float formats, requires extension. case TextureFormat::R16F: @@ -1979,7 +1987,7 @@ void OpenGLDriver::generateMipmaps(Handle th) { auto& gl = mContext; GLTexture* t = handle_cast(th); -#if defined(GL_VERSION_4_1) || defined(GL_ES_VERSION_3_1) +#if defined(BACKEND_OPENGL_LEVEL_GLES31) assert_invariant(t->gl.target != GL_TEXTURE_2D_MULTISAMPLE); #endif // Note: glGenerateMimap can also fail if the internal format is not both @@ -2385,21 +2393,20 @@ void OpenGLDriver::beginRenderPass(Handle rth, gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo); CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER) - // glInvalidateFramebuffer appeared on GLES 3.0 and GL4.3, for simplicity we just - // ignore it on GL (rather than having to do a runtime check). - if (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES && - BACKEND_OPENGL_LEVEL >= BACKEND_OPENGL_LEVEL_GLES30) { - if (!gl.bugs.disable_invalidate_framebuffer) { - AttachmentArray attachments; // NOLINT - GLsizei const attachmentCount = getAttachments(attachments, rt, discardFlags); - if (attachmentCount) { - glInvalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data()); - } - CHECK_GL_ERROR(utils::slog.e) + // glInvalidateFramebuffer appeared on GLES 3.0 and GL4.3 +#if defined(BACKEND_OPENGL_LEVEL_GLES30) || defined(BACKEND_OPENGL_VERSION_GL) + if ((mContext.isAtLeastGLES(3, 0) || mContext.isAtLeastGL(4, 3)) + && !gl.bugs.disable_invalidate_framebuffer) { + AttachmentArray attachments; // NOLINT + GLsizei const attachmentCount = getAttachments(attachments, rt, discardFlags); + if (attachmentCount) { + glInvalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data()); } - } else { - // on GL desktop we assume we don't have glInvalidateFramebuffer, but even if the GPU is - // not a tiler, it's important to clear the framebuffer before drawing, as it resets + CHECK_GL_ERROR(utils::slog.e) + } else +#endif + { + // It's important to clear the framebuffer before drawing, as it resets // the fb to a known state (resets fb compression and possibly other things). // So we use glClear instead of glInvalidateFramebuffer gl.disable(GL_SCISSOR_TEST); @@ -2471,8 +2478,8 @@ void OpenGLDriver::endRenderPass(int) { // glInvalidateFramebuffer appeared on GLES 3.0 and GL4.3, for simplicity we just // ignore it on GL (rather than having to do a runtime check). - if (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES && - BACKEND_OPENGL_LEVEL >= BACKEND_OPENGL_LEVEL_GLES30) { +#if defined(BACKEND_OPENGL_LEVEL_GLES30) || defined(BACKEND_OPENGL_VERSION_GL) + if (mContext.isAtLeastGLES(3, 0) || mContext.isAtLeastGL(4, 3)) { auto effectiveDiscardFlags = discardFlags; if (gl.bugs.invalidate_end_only_if_invalidate_start) { effectiveDiscardFlags &= mRenderPassParams.flags.discardStart; @@ -2488,6 +2495,7 @@ void OpenGLDriver::endRenderPass(int) { CHECK_GL_ERROR(utils::slog.e) } } +#endif #ifndef NDEBUG // clear the discarded buffers in debug builds @@ -3232,7 +3240,7 @@ void OpenGLDriver::dispatchCompute(Handle program, math::uint3 workGr useProgram(p); -#if defined(GL_ES_VERSION_3_1) || defined(GL_VERSION_4_3) +#if defined(BACKEND_OPENGL_LEVEL_GLES31) #if defined(__ANDROID__) // on Android, GLES3.1 and above entry-points are defined in glext @@ -3241,7 +3249,7 @@ void OpenGLDriver::dispatchCompute(Handle program, math::uint3 workGr #endif glDispatchCompute(workGroupCount.x, workGroupCount.y, workGroupCount.z); -#endif +#endif // BACKEND_OPENGL_LEVEL_GLES31 #ifdef FILAMENT_ENABLE_MATDBG CHECK_GL_ERROR_NON_FATAL(utils::slog.e) diff --git a/filament/backend/src/opengl/OpenGLProgram.cpp b/filament/backend/src/opengl/OpenGLProgram.cpp index 2605306dac1..e0ec9dad0f9 100644 --- a/filament/backend/src/opengl/OpenGLProgram.cpp +++ b/filament/backend/src/opengl/OpenGLProgram.cpp @@ -130,7 +130,7 @@ void OpenGLProgram::compileShaders(OpenGLContext& context, glShaderType = GL_FRAGMENT_SHADER; break; case ShaderStage::COMPUTE: -#if defined(GL_VERSION_4_1) || defined(GL_ES_VERSION_3_1) +#if defined(BACKEND_OPENGL_LEVEL_GLES31) glShaderType = GL_COMPUTE_SHADER; #else continue; @@ -192,13 +192,12 @@ std::string_view OpenGLProgram::process_GOOGLE_cpp_style_line_directive(OpenGLCo return { source, len }; } -// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 and +// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and // macOS doesn't support GL_ARB_shading_language_packing std::string_view OpenGLProgram::process_ARB_shading_language_packing(OpenGLContext& context) noexcept { using namespace std::literals; - if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) { - if (context.state.major == 4 && context.state.minor == 1 && - !context.ext.ARB_shading_language_packing) { +#ifdef BACKEND_OPENGL_VERSION_GL + if (!context.isAtLeastGL(4, 2) && !context.ext.ARB_shading_language_packing) { return R"( // these don't handle denormals, NaNs or inf @@ -236,7 +235,7 @@ highp uint packHalf2x16(vec2 v) { } )"sv; } - } +#endif // BACKEND_OPENGL_VERSION_GL return ""sv; } diff --git a/filament/backend/src/opengl/OpenGLTimerQuery.cpp b/filament/backend/src/opengl/OpenGLTimerQuery.cpp index dd07ac30f89..20e6f1aa52e 100644 --- a/filament/backend/src/opengl/OpenGLTimerQuery.cpp +++ b/filament/backend/src/opengl/OpenGLTimerQuery.cpp @@ -34,7 +34,7 @@ OpenGLTimerQueryInterface::~OpenGLTimerQueryInterface() = default; // ------------------------------------------------------------------------------------------------ -#if defined(GL_VERSION_3_3) || defined(GL_EXT_disjoint_timer_query) +#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query) TimerQueryNative::TimerQueryNative(OpenGLContext&) { } diff --git a/filament/backend/src/opengl/OpenGLTimerQuery.h b/filament/backend/src/opengl/OpenGLTimerQuery.h index e6fc88e2c2f..e11235042b8 100644 --- a/filament/backend/src/opengl/OpenGLTimerQuery.h +++ b/filament/backend/src/opengl/OpenGLTimerQuery.h @@ -48,7 +48,7 @@ class OpenGLTimerQueryInterface { virtual uint64_t queryResult(GLTimerQuery* query) = 0; }; -#if defined(GL_VERSION_3_3) || defined(GL_EXT_disjoint_timer_query) +#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query) class TimerQueryNative : public OpenGLTimerQueryInterface { public: diff --git a/filament/backend/src/opengl/gl_headers.cpp b/filament/backend/src/opengl/gl_headers.cpp index 2c29aae796f..5253d3dcb65 100644 --- a/filament/backend/src/opengl/gl_headers.cpp +++ b/filament/backend/src/opengl/gl_headers.cpp @@ -28,10 +28,6 @@ static void getProcAddress(T& pfn, const char* name) noexcept { } namespace glext { -#ifdef GL_QCOM_tiled_rendering -PFNGLSTARTTILINGQCOMPROC glStartTilingQCOM; -PFNGLENDTILINGQCOMPROC glEndTilingQCOM; -#endif #ifdef GL_OES_EGL_image PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; #endif @@ -52,7 +48,7 @@ PFNGLGETDEBUGMESSAGELOGKHRPROC glGetDebugMessageLogKHR; PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64v; #endif #ifdef GL_EXT_clip_control -PFNGLCLIPCONTROLEXTPROC glClipControl; +PFNGLCLIPCONTROLEXTPROC glClipControlEXT; #endif #if defined(__ANDROID__) @@ -65,31 +61,27 @@ static std::once_flag sGlExtInitialized; void importGLESExtensionsEntryPoints() { std::call_once(sGlExtInitialized, +[]() { -#ifdef GL_QCOM_tiled_rendering - getProcAddress(glStartTilingQCOM, "glStartTilingQCOM"); - getProcAddress(glEndTilingQCOM, "glEndTilingQCOM"); -#endif #ifdef GL_OES_EGL_image - getProcAddress(glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES"); + getProcAddress(glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES"); #endif #if GL_EXT_debug_marker - getProcAddress(glInsertEventMarkerEXT, "glInsertEventMarkerEXT"); - getProcAddress(glPushGroupMarkerEXT, "glPushGroupMarkerEXT"); - getProcAddress(glPopGroupMarkerEXT, "glPopGroupMarkerEXT"); + getProcAddress(glInsertEventMarkerEXT, "glInsertEventMarkerEXT"); + getProcAddress(glPushGroupMarkerEXT, "glPushGroupMarkerEXT"); + getProcAddress(glPopGroupMarkerEXT, "glPopGroupMarkerEXT"); #endif #if GL_EXT_multisampled_render_to_texture - getProcAddress(glFramebufferTexture2DMultisampleEXT, "glFramebufferTexture2DMultisampleEXT"); - getProcAddress(glRenderbufferStorageMultisampleEXT, "glRenderbufferStorageMultisampleEXT"); + getProcAddress(glFramebufferTexture2DMultisampleEXT, "glFramebufferTexture2DMultisampleEXT"); + getProcAddress(glRenderbufferStorageMultisampleEXT, "glRenderbufferStorageMultisampleEXT"); #endif #ifdef GL_KHR_debug - getProcAddress(glDebugMessageCallbackKHR, "glDebugMessageCallbackKHR"); - getProcAddress(glGetDebugMessageLogKHR, "glGetDebugMessageLogKHR"); + getProcAddress(glDebugMessageCallbackKHR, "glDebugMessageCallbackKHR"); + getProcAddress(glGetDebugMessageLogKHR, "glGetDebugMessageLogKHR"); #endif #ifdef GL_EXT_disjoint_timer_query - getProcAddress(glGetQueryObjectui64v, "glGetQueryObjectui64vEXT"); + getProcAddress(glGetQueryObjectui64v, "glGetQueryObjectui64vEXT"); #endif #ifdef GL_EXT_clip_control - getProcAddress(glClipControl, "glClipControlEXT"); + getProcAddress(glClipControlEXT, "glClipControlEXT"); #endif #if defined(__ANDROID__) getProcAddress(glDispatchCompute, "glDispatchCompute"); diff --git a/filament/backend/src/opengl/gl_headers.h b/filament/backend/src/opengl/gl_headers.h index e59d341cc29..46c0e8362f0 100644 --- a/filament/backend/src/opengl/gl_headers.h +++ b/filament/backend/src/opengl/gl_headers.h @@ -17,6 +17,26 @@ #ifndef TNT_FILAMENT_BACKEND_OPENGL_GL_HEADERS_H #define TNT_FILAMENT_BACKEND_OPENGL_GL_HEADERS_H +/* + * Configuration we aim to support: + * + * GL 4.5 headers + * - GL 4.1 runtime (for macOS) + * - GL 4.5 runtime + * + * GLES 2.0 headers + * - GLES 2.0 runtime Android only + * + * GLES 3.0 headers + * - GLES 3.0 runtime iOS and WebGL2 only + * + * GLES 3.1 headers + * - GLES 2.0 runtime + * - GLES 3.0 runtime + * - GLES 3.1 runtime + */ + + #if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__) #if defined(__EMSCRIPTEN__) @@ -46,12 +66,22 @@ #endif +/* Validate the header configurations we aim to support */ -#if (!defined(GL_ES_VERSION_2_0) && !defined(GL_VERSION_4_1)) -#error "Minimum header version must be OpenGL ES 2.0 or OpenGL 4.1" +#if defined(GL_VERSION_4_5) +#elif defined(GL_ES_VERSION_3_1) +#elif defined(GL_ES_VERSION_3_0) +# if !defined(IOS) && !defined(__EMSCRIPTEN__) +# error "GLES 3.0 headers only supported on iOS and WebGL2" +# endif +#elif defined(GL_ES_VERSION_2_0) +# if !defined(__ANDROID__) +# error "GLES 2.0 headers only supported on Android" +# endif +#else +# error "Minimum header version must be OpenGL ES 2.0 or OpenGL 4.5" #endif - /* * GLES extensions */ @@ -72,10 +102,6 @@ namespace glext { // it is currently called from PlatformEGL. void importGLESExtensionsEntryPoints(); -#ifdef GL_QCOM_tiled_rendering -extern PFNGLSTARTTILINGQCOMPROC glStartTilingQCOM; -extern PFNGLENDTILINGQCOMPROC glEndTilingQCOM; -#endif #ifdef GL_OES_EGL_image extern PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; #endif @@ -93,7 +119,7 @@ extern PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR; extern PFNGLGETDEBUGMESSAGELOGKHRPROC glGetDebugMessageLogKHR; #endif #ifdef GL_EXT_clip_control -extern PFNGLCLIPCONTROLEXTPROC glClipControl; +extern PFNGLCLIPCONTROLEXTPROC glClipControlEXT; #endif #ifdef GL_EXT_disjoint_timer_query extern PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64v; @@ -157,29 +183,25 @@ void glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void *d } #endif - -#define BACKEND_OPENGL_VERSION_GLES 0 -#define BACKEND_OPENGL_VERSION_GL 1 #if defined(GL_ES_VERSION_2_0) -# define BACKEND_OPENGL_VERSION BACKEND_OPENGL_VERSION_GLES -#elif defined(GL_VERSION_4_1) -# define BACKEND_OPENGL_VERSION BACKEND_OPENGL_VERSION_GL +# define BACKEND_OPENGL_VERSION_GLES +#elif defined(GL_VERSION_4_5) +# define BACKEND_OPENGL_VERSION_GL +#else +# error "Unsupported header version" #endif -#define BACKEND_OPENGL_LEVEL_GLES20 0 -#define BACKEND_OPENGL_LEVEL_GLES30 1 -#define BACKEND_OPENGL_LEVEL_GLES31 2 - -#if defined(GL_VERSION_4_1) -# define BACKEND_OPENGL_LEVEL BACKEND_OPENGL_LEVEL_GLES30 +#if defined(GL_VERSION_4_5) || defined(GL_ES_VERSION_3_1) +# define BACKEND_OPENGL_LEVEL_GLES31 +# ifdef __EMSCRIPTEN__ +# error "__EMSCRIPTEN__ shouldn't be defined with GLES 3.1 headers" +# endif #endif - -#if defined(GL_ES_VERSION_3_1) -# define BACKEND_OPENGL_LEVEL BACKEND_OPENGL_LEVEL_GLES31 -#elif defined(GL_ES_VERSION_3_0) -# define BACKEND_OPENGL_LEVEL BACKEND_OPENGL_LEVEL_GLES30 -#elif defined(GL_ES_VERSION_2_0) -# define BACKEND_OPENGL_LEVEL BACKEND_OPENGL_LEVEL_GLES20 +#if defined(GL_VERSION_4_5) || defined(GL_ES_VERSION_3_0) +# define BACKEND_OPENGL_LEVEL_GLES30 +#endif +#if defined(GL_VERSION_4_5) || defined(GL_ES_VERSION_2_0) +# define BACKEND_OPENGL_LEVEL_GLES20 #endif #include "NullGLES.h" diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index d6ada095b3e..01493659750 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -75,6 +75,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, mContext.commands->setObserver(&mPipelineCache); mPipelineCache.setDevice(mContext.device, mContext.allocator); mPipelineCache.setDummyTexture(mContext.emptyTexture->getPrimaryImageView()); + + mReadPixels.initialize(mContext.device); } VulkanDriver::~VulkanDriver() noexcept = default; @@ -134,6 +136,9 @@ void VulkanDriver::terminate() { void VulkanDriver::tick(int) { mContext.commands->updateFences(); + + // Handle any posted tasks + runTaskHandler(); } // Garbage collection should not occur too frequently, only about once per frame. Internally, the @@ -1011,31 +1016,35 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP rt->transformClientRectToPlatform(&renderPassInfo.renderArea); - VkClearValue clearValues[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + 1] = {}; - - // NOTE: clearValues must be populated in the same order as the attachments array in - // VulkanFboCache::getFramebuffer. Values must be provided regardless of whether Vulkan is - // actually clearing that particular target. - for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { - if (fbkey.color[i]) { - VkClearValue& clearValue = clearValues[renderPassInfo.clearValueCount++]; - clearValue.color.float32[0] = params.clearColor.r; - clearValue.color.float32[1] = params.clearColor.g; - clearValue.color.float32[2] = params.clearColor.b; - clearValue.color.float32[3] = params.clearColor.a; + VkClearValue clearValues[ + MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + + 1] = {}; + if (params.flags.clear != TargetBufferFlags::NONE) { + + // NOTE: clearValues must be populated in the same order as the attachments array in + // VulkanFboCache::getFramebuffer. Values must be provided regardless of whether Vulkan is + // actually clearing that particular target. + for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { + if (fbkey.color[i]) { + VkClearValue &clearValue = clearValues[renderPassInfo.clearValueCount++]; + clearValue.color.float32[0] = params.clearColor.r; + clearValue.color.float32[1] = params.clearColor.g; + clearValue.color.float32[2] = params.clearColor.b; + clearValue.color.float32[3] = params.clearColor.a; + } } - } - // Resolve attachments are not cleared but still have entries in the list, so skip over them. - for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { - if (rpkey.needsResolveMask & (1u << i)) { - renderPassInfo.clearValueCount++; + // Resolve attachments are not cleared but still have entries in the list, so skip over them. + for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { + if (rpkey.needsResolveMask & (1u << i)) { + renderPassInfo.clearValueCount++; + } } + if (fbkey.depth) { + VkClearValue &clearValue = clearValues[renderPassInfo.clearValueCount++]; + clearValue.depthStencil = {(float) params.clearDepth, 0}; + } + renderPassInfo.pClearValues = &clearValues[0]; } - if (fbkey.depth) { - VkClearValue& clearValue = clearValues[renderPassInfo.clearValueCount++]; - clearValue.depthStencil = {(float) params.clearDepth, 0}; - } - renderPassInfo.pClearValues = &clearValues[0]; vkCmdBeginRenderPass(cmdbuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); @@ -1321,152 +1330,18 @@ void VulkanDriver::stopCapture(int) { } -void VulkanDriver::readPixels(Handle src, uint32_t x, uint32_t y, - uint32_t width, uint32_t height, PixelBufferDescriptor&& pbd) { - const VkDevice device = mContext.device; +void VulkanDriver::readPixels(Handle src, uint32_t x, uint32_t y, uint32_t width, + uint32_t height, PixelBufferDescriptor&& pbd) { VulkanRenderTarget* srcTarget = handle_cast(src); - VulkanTexture* srcTexture = srcTarget->getColor(0).texture; - assert_invariant(srcTexture); - const VkFormat srcFormat = srcTexture->getVkFormat(); - const bool swizzle = srcFormat == VK_FORMAT_B8G8R8A8_UNORM; - - // Create a host visible, linearly tiled image as a staging area. - - VkImageCreateInfo imageInfo { - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .imageType = VK_IMAGE_TYPE_2D, - .format = srcFormat, - .extent = { width, height, 1 }, - .mipLevels = 1, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = VK_IMAGE_TILING_LINEAR, - .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - }; - - VkImage stagingImage; - vkCreateImage(device, &imageInfo, VKALLOC, &stagingImage); - - VkMemoryRequirements memReqs; - VkDeviceMemory stagingMemory; - vkGetImageMemoryRequirements(device, stagingImage, &memReqs); - VkMemoryAllocateInfo allocInfo = { - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .allocationSize = memReqs.size, - .memoryTypeIndex = mContext.selectMemoryType(memReqs.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | - VK_MEMORY_PROPERTY_HOST_CACHED_BIT) - }; - - vkAllocateMemory(device, &allocInfo, nullptr, &stagingMemory); - vkBindImageMemory(device, stagingImage, stagingMemory, 0); - - // TODO: don't flush/wait here, this should be asynchronous - - mContext.commands->flush(); - mContext.commands->wait(); - - // Transition the staging image layout. - - const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer; - - transitionImageLayout(cmdbuffer, { - .image = stagingImage, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .subresources = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .srcStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - .srcAccessMask = 0, - .dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - }); - - const VulkanAttachment srcAttachment = srcTarget->getColor(0); - - VkImageCopy imageCopyRegion = { - .srcSubresource = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .mipLevel = srcAttachment.level, - .baseArrayLayer = srcAttachment.layer, - .layerCount = 1, - }, - .srcOffset = { - .x = (int32_t) x, - .y = (int32_t) (srcTarget->getExtent().height - (height + y)), - }, - .dstSubresource = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .layerCount = 1, - }, - .extent = { - .width = width, - .height = height, - .depth = 1, - }, - }; - - // Transition the source image layout (which might be the swap chain) - - const VkImageSubresourceRange srcRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = srcAttachment.level, - .levelCount = 1, - .baseArrayLayer = srcAttachment.layer, - .layerCount = 1, - }; - - srcTexture->transitionLayout(cmdbuffer, srcRange, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - - // Perform the copy into the staging area. At this point we know that the src layout is - // TRANSFER_SRC_OPTIMAL and the staging area is GENERAL. - - UTILS_UNUSED_IN_RELEASE VkExtent2D srcExtent = srcAttachment.getExtent2D(); - assert_invariant(imageCopyRegion.srcOffset.x + imageCopyRegion.extent.width <= srcExtent.width); - assert_invariant(imageCopyRegion.srcOffset.y + imageCopyRegion.extent.height <= srcExtent.height); - - vkCmdCopyImage(cmdbuffer, srcAttachment.getImage(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, stagingImage, VK_IMAGE_LAYOUT_GENERAL, - 1, &imageCopyRegion); - - // Restore the source image layout. Between driver API calls, color images are always kept in - // UNDEFINED layout or in their "usage default" layout (see comment for getDefaultImageLayout). - - srcTexture->transitionLayout(cmdbuffer, srcRange, - getDefaultImageLayout(TextureUsage::COLOR_ATTACHMENT)); - - // TODO: don't flush/wait here -- we should do this asynchronously - - // Flush and wait. - mContext.commands->flush(); - mContext.commands->wait(); - - VkImageSubresource subResource { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT }; - VkSubresourceLayout subResourceLayout; - vkGetImageSubresourceLayout(device, stagingImage, &subResource, &subResourceLayout); - - // Map image memory so we can start copying from it. - - const uint8_t* srcPixels; - vkMapMemory(device, stagingMemory, 0, VK_WHOLE_SIZE, 0, (void**) &srcPixels); - srcPixels += subResourceLayout.offset; - - if (!DataReshaper::reshapeImage(&pbd, getComponentType(srcFormat), getComponentCount(srcFormat), - srcPixels, subResourceLayout.rowPitch, width, height, swizzle)) { - utils::slog.e << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl; - } - - vkUnmapMemory(device, stagingMemory); - vkDestroyImage(device, stagingImage, nullptr); - vkFreeMemory(device, stagingMemory, nullptr); - - scheduleDestroy(std::move(pbd)); + mReadPixels.run( + srcTarget, x, y, width, height, mContext.graphicsQueueFamilyIndex, std::move(pbd), + getTaskHandler(), + [&context = mContext](uint32_t reqs, VkFlags flags) { + return context.selectMemoryType(reqs, flags); + }, + [this](PixelBufferDescriptor&& pbd) { + this->scheduleDestroy(std::move(pbd)); + }); } void VulkanDriver::readBufferSubData(backend::BufferObjectHandle boh, diff --git a/filament/backend/src/vulkan/VulkanDriver.h b/filament/backend/src/vulkan/VulkanDriver.h index e403d0926fe..793f08bd9f3 100644 --- a/filament/backend/src/vulkan/VulkanDriver.h +++ b/filament/backend/src/vulkan/VulkanDriver.h @@ -23,8 +23,10 @@ #include "VulkanConstants.h" #include "VulkanContext.h" #include "VulkanFboCache.h" +#include "VulkanReadPixels.h" #include "VulkanSamplerCache.h" #include "VulkanStagePool.h" +#include "VulkanTaskHandler.h" #include "VulkanUtility.h" #include "private/backend/Driver.h" @@ -39,11 +41,14 @@ namespace filament::backend { class VulkanPlatform; struct VulkanSamplerGroup; -class VulkanDriver final : public DriverBase { +class VulkanDriver final : public DriverBase, private VulkanTaskHandler::Host { public: static Driver* create(VulkanPlatform* platform, const char* const* ppEnabledExtensions, uint32_t enabledExtensionCount, const Platform::DriverConfig& driverConfig) noexcept; + VulkanDriver(VulkanDriver const&) = delete; + VulkanDriver& operator = (VulkanDriver const&) = delete; + private: void debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept override; @@ -73,9 +78,6 @@ class VulkanDriver final : public DriverBase { #include "private/backend/DriverAPI.inc" - VulkanDriver(VulkanDriver const&) = delete; - VulkanDriver& operator = (VulkanDriver const&) = delete; - private: HandleAllocatorVK mHandleAllocator; @@ -151,6 +153,7 @@ class VulkanDriver final : public DriverBase { VulkanSamplerCache mSamplerCache; VulkanBlitter mBlitter; VulkanSamplerGroup* mSamplerBindings[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {}; + VulkanReadPixels mReadPixels; }; } // namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanReadPixels.cpp b/filament/backend/src/vulkan/VulkanReadPixels.cpp new file mode 100644 index 00000000000..fb1388a8f6e --- /dev/null +++ b/filament/backend/src/vulkan/VulkanReadPixels.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "VulkanReadPixels.h" + +#include "DataReshaper.h" +#include "VulkanHandles.h" +#include "VulkanTaskHandler.h" +#include "VulkanTexture.h" + +#include + +using namespace bluevk; + +namespace filament::backend { + +VulkanReadPixels::~VulkanReadPixels() noexcept { + assert_invariant(mDevice != VK_NULL_HANDLE); + if (mCommandPool == VK_NULL_HANDLE) { + return; + } + vkDestroyCommandPool(mDevice, mCommandPool, VKALLOC); +} + +void VulkanReadPixels::initialize(VkDevice device) { + mDevice = device; +} + +void VulkanReadPixels::run(VulkanRenderTarget const* srcTarget, uint32_t const x, uint32_t const y, + uint32_t const width, uint32_t const height, uint32_t const graphicsQueueFamilyIndex, + PixelBufferDescriptor&& pbd, VulkanTaskHandler& taskHandler, + SelecteMemoryFunction const& selectMemoryFunc, + OnReadCompleteFunction const& readCompleteFunc) { + assert_invariant(mDevice != VK_NULL_HANDLE); + + VkDevice device = mDevice; + + if (mCommandPool == VK_NULL_HANDLE) { + // Create a command pool if one has not been created. + VkCommandPoolCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT + | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, + .queueFamilyIndex = graphicsQueueFamilyIndex, + }; + vkCreateCommandPool(device, &createInfo, VKALLOC, &mCommandPool); + } + VkCommandPool cmdpool = mCommandPool; + + VulkanTexture* srcTexture = srcTarget->getColor(0).texture; + assert_invariant(srcTexture); + VkFormat const srcFormat = srcTexture->getVkFormat(); + bool const swizzle + = srcFormat == VK_FORMAT_B8G8R8A8_UNORM || srcFormat == VK_FORMAT_B8G8R8A8_SRGB; + + // Create a host visible, linearly tiled image as a staging area. + VkImageCreateInfo const imageInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = srcFormat, + .extent = {width, height, 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_LINEAR, + .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }; + + VkImage stagingImage; + vkCreateImage(device, &imageInfo, VKALLOC, &stagingImage); + + VkMemoryRequirements memReqs; + VkDeviceMemory stagingMemory; + vkGetImageMemoryRequirements(device, stagingImage, &memReqs); + VkMemoryAllocateInfo const allocInfo = {.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = memReqs.size, + .memoryTypeIndex = selectMemoryFunc(memReqs.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + | VK_MEMORY_PROPERTY_HOST_CACHED_BIT)}; + + vkAllocateMemory(device, &allocInfo, VKALLOC, &stagingMemory); + vkBindImageMemory(device, stagingImage, stagingMemory, 0); + + VkCommandBuffer cmdbuffer; + VkCommandBufferAllocateInfo const allocateInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = cmdpool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + vkAllocateCommandBuffers(device, &allocateInfo, &cmdbuffer); + + VkCommandBufferBeginInfo const binfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + vkBeginCommandBuffer(cmdbuffer, &binfo); + + transitionImageLayout(cmdbuffer, { + .image = stagingImage, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .subresources = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .srcStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + .srcAccessMask = 0, + .dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + }); + + VulkanAttachment const srcAttachment = srcTarget->getColor(0); + + VkImageCopy const imageCopyRegion = { + .srcSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = srcAttachment.level, + .baseArrayLayer = srcAttachment.layer, + .layerCount = 1, + }, + .srcOffset = { + .x = (int32_t) x, + .y = (int32_t) (srcTarget->getExtent().height - (height + y)), + }, + .dstSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .layerCount = 1, + }, + .extent = { + .width = width, + .height = height, + .depth = 1, + }, + }; + + // Transition the source image layout (which might be the swap chain) + VkImageSubresourceRange const srcRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = srcAttachment.level, + .levelCount = 1, + .baseArrayLayer = srcAttachment.layer, + .layerCount = 1, + }; + + srcTexture->transitionLayout(cmdbuffer, srcRange, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + // Perform the copy into the staging area. At this point we know that the src layout is + // TRANSFER_SRC_OPTIMAL and the staging area is GENERAL. + UTILS_UNUSED_IN_RELEASE VkExtent2D srcExtent = srcAttachment.getExtent2D(); + assert_invariant(imageCopyRegion.srcOffset.x + imageCopyRegion.extent.width <= srcExtent.width); + assert_invariant( + imageCopyRegion.srcOffset.y + imageCopyRegion.extent.height <= srcExtent.height); + + vkCmdCopyImage(cmdbuffer, srcAttachment.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + stagingImage, VK_IMAGE_LAYOUT_GENERAL, 1, &imageCopyRegion); + + // Restore the source image layout. Between driver API calls, color images are always kept in + // UNDEFINED layout or in their "usage default" layout (see comment for getDefaultImageLayout). + srcTexture->transitionLayout(cmdbuffer, srcRange, + getDefaultImageLayout(TextureUsage::COLOR_ATTACHMENT)); + + vkEndCommandBuffer(cmdbuffer); + + VkQueue queue; + vkGetDeviceQueue(device, graphicsQueueFamilyIndex, 0, &queue); + + VkSubmitInfo const submitInfo{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .waitSemaphoreCount = 0, + .pWaitSemaphores = VK_NULL_HANDLE, + .pWaitDstStageMask = VK_NULL_HANDLE, + .commandBufferCount = 1, + .pCommandBuffers = &cmdbuffer, + .signalSemaphoreCount = 0, + .pSignalSemaphores = VK_NULL_HANDLE, + }; + VkFence fence; + VkFenceCreateInfo const fenceCreateInfo{ + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + }; + vkCreateFence(device, &fenceCreateInfo, VKALLOC, &fence); + vkQueueSubmit(queue, 1, &submitInfo, fence); + + auto* const pUserBuffer = new PixelBufferDescriptor(std::move(pbd)); + auto const waitTaskId = taskHandler.createTask( + [device, width, height, swizzle, srcFormat, fence, stagingImage, stagingMemory, cmdpool, + cmdbuffer, pUserBuffer, readCompleteFunc, + &taskHandler](VulkanTaskHandler::TaskId taskId, void* data) mutable { + PixelBufferDescriptor& p = *pUserBuffer; + vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX); + VkResult status = vkGetFenceStatus(device, fence); + + // Fence hasn't been reached. Try waiting again. + if (status == VK_NOT_READY) { + taskHandler.post(taskId); + return; + } + + // Need to abort the readPixels if the device is lost. + if (status == VK_ERROR_DEVICE_LOST) { + utils::slog.e << "Device lost while in VulkanReadPixels::run" + << utils::io::endl; + taskHandler.completed(taskId); + + // Try to free the pbd anyway + readCompleteFunc(std::move(p)); + delete pUserBuffer; + return; + } + + VkImageSubresource subResource{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT}; + VkSubresourceLayout subResourceLayout; + vkGetImageSubresourceLayout(device, stagingImage, &subResource, &subResourceLayout); + + // Map image memory so that we can start copying from it. + uint8_t const* srcPixels; + vkMapMemory(device, stagingMemory, 0, VK_WHOLE_SIZE, 0, (void**) &srcPixels); + srcPixels += subResourceLayout.offset; + + if (!DataReshaper::reshapeImage(&p, getComponentType(srcFormat), + getComponentCount(srcFormat), srcPixels, + static_cast(subResourceLayout.rowPitch), static_cast(width), + static_cast(height), swizzle)) { + utils::slog.e << "Unsupported PixelDataFormat or PixelDataType" + << utils::io::endl; + } + + vkUnmapMemory(device, stagingMemory); + vkDestroyImage(device, stagingImage, VKALLOC); + vkFreeMemory(device, stagingMemory, VKALLOC); + vkDestroyFence(device, fence, VKALLOC); + vkFreeCommandBuffers(device, cmdpool, 1, &cmdbuffer); + readCompleteFunc(std::move(p)); + delete pUserBuffer; + + taskHandler.completed(taskId); + }, + nullptr); + + taskHandler.post(waitTaskId); +} + +}// namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanReadPixels.h b/filament/backend/src/vulkan/VulkanReadPixels.h new file mode 100644 index 00000000000..a402e7eab04 --- /dev/null +++ b/filament/backend/src/vulkan/VulkanReadPixels.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H +#define TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H + +#include "VulkanTaskHandler.h" +#include "private/backend/Driver.h" + +#include +#include + +namespace filament::backend { + +struct VulkanContext; +struct VulkanRenderTarget; + +class VulkanReadPixels { +public: + using OnReadCompleteFunction = std::function; + using SelecteMemoryFunction = std::function; + + ~VulkanReadPixels() noexcept; + + void initialize(VkDevice device); + + void run(VulkanRenderTarget const* srcTarget, uint32_t x, uint32_t y, uint32_t width, + uint32_t height, uint32_t graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd, + VulkanTaskHandler& taskHandler, SelecteMemoryFunction const& selectMemoryFunc, + OnReadCompleteFunction const& readCompleteFunc); + +private: + VkDevice mDevice = VK_NULL_HANDLE; + VkCommandPool mCommandPool = VK_NULL_HANDLE; +}; + +}// namespace filament::backend + +#endif//TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H diff --git a/filament/backend/src/vulkan/VulkanTaskHandler.h b/filament/backend/src/vulkan/VulkanTaskHandler.h new file mode 100644 index 00000000000..65004652ea1 --- /dev/null +++ b/filament/backend/src/vulkan/VulkanTaskHandler.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_BACKEND_VULKANTASKHANDLER_H +#define TNT_FILAMENT_BACKEND_VULKANTASKHANDLER_H + +#include "utils/Panic.h" + +#include +#include +#include +#include + +namespace filament::backend { + +class VulkanTaskHandler { +public: + // The Host class is meant to be called on the thread that processes the tasks. It has access to + // the handle function. + class Host { + public: + Host(Host const&) = delete; + Host& operator=(Host const&) = delete; + protected: + Host() + : mHandler(std::make_unique()) {} + + void runTaskHandler() noexcept { + mHandler->handle(); + } + + VulkanTaskHandler& getTaskHandler() noexcept { + return *mHandler; + } + private: + std::unique_ptr mHandler; + }; + + using TaskId = uint32_t; + using TaskFunc = std::function; + using Task = std::pair; + + TaskId createTask(TaskFunc const& func, void* data) noexcept { + TaskId const id = mNextTaskId++; + mTasks[id] = std::pair(func, data); + return id; + } + + // Task that will never be put on the queue again should be marked as completed by calling this + // function. + void completed(TaskId taskId) noexcept { + assert_invariant(mTasks.find(taskId) != mTasks.end()); + mTasks.erase(taskId); + } + + void post(TaskId taskId) noexcept { + assert_invariant(mTasks.find(taskId) != mTasks.end()); + mTaskQueue.push(taskId); + } + + VulkanTaskHandler() = default; + + VulkanTaskHandler(VulkanTaskHandler const&) = delete; + VulkanTaskHandler& operator=(VulkanTaskHandler const&) = delete; + +private: + inline void handle() { + while (!mTaskQueue.empty()) { + auto taskId = mTaskQueue.front(); + mTaskQueue.pop(); + // It is possible for taskIds in the queue to refer to a task that has already been + // completed. Just ignore. + if (mTasks.find(taskId) == mTasks.end()) { + continue; + } + + auto& [func, data] = mTasks[taskId]; + func(taskId, data); + } + } + + std::queue mTaskQueue; + std::map mTasks; + uint32_t mNextTaskId = 0; + + friend class Host; +}; + +}// namespace filament::backend + +#endif// TNT_FILAMENT_BACKEND_VULKANTASKHANDLER_H diff --git a/filament/include/filament/Material.h b/filament/include/filament/Material.h index 572de844547..6533577383e 100644 --- a/filament/include/filament/Material.h +++ b/filament/include/filament/Material.h @@ -171,6 +171,9 @@ class UTILS_PUBLIC Material : public FilamentAPI { //! Indicates whether this material is double-sided. bool isDoubleSided() const noexcept; + //! Indicates whether this material uses alpha to coverage. + bool isAlphaToCoverageEnabled() const noexcept; + //! Returns the alpha mask threshold used when the blending mode is set to masked. float getMaskThreshold() const noexcept; diff --git a/filament/src/Material.cpp b/filament/src/Material.cpp index bb2bc69a38f..37ffce55056 100644 --- a/filament/src/Material.cpp +++ b/filament/src/Material.cpp @@ -72,6 +72,10 @@ bool Material::isDoubleSided() const noexcept { return downcast(this)->isDoubleSided(); } +bool Material::isAlphaToCoverageEnabled() const noexcept { + return downcast(this)->isAlphaToCoverageEnabled(); +} + float Material::getMaskThreshold() const noexcept { return downcast(this)->getMaskThreshold(); } diff --git a/filament/src/MaterialParser.cpp b/filament/src/MaterialParser.cpp index d5df62932dc..96e5ef73aca 100644 --- a/filament/src/MaterialParser.cpp +++ b/filament/src/MaterialParser.cpp @@ -246,6 +246,14 @@ bool MaterialParser::getMaskThreshold(float* value) const noexcept { return mImpl.getFromSimpleChunk(ChunkType::MaterialMaskThreshold, value); } +bool MaterialParser::getAlphaToCoverageSet(bool* value) const noexcept { + return mImpl.getFromSimpleChunk(ChunkType::MaterialAlphaToCoverageSet, value); +} + +bool MaterialParser::getAlphaToCoverage(bool* value) const noexcept { + return mImpl.getFromSimpleChunk(ChunkType::MaterialAlphaToCoverage, value); +} + bool MaterialParser::hasShadowMultiplier(bool* value) const noexcept { return mImpl.getFromSimpleChunk(ChunkType::MaterialShadowMultiplier, value); } diff --git a/filament/src/MaterialParser.h b/filament/src/MaterialParser.h index eff0addc23f..e066e547ba4 100644 --- a/filament/src/MaterialParser.h +++ b/filament/src/MaterialParser.h @@ -88,6 +88,8 @@ class MaterialParser { bool getShading(Shading*) const noexcept; bool getBlendingMode(BlendingMode*) const noexcept; bool getMaskThreshold(float*) const noexcept; + bool getAlphaToCoverageSet(bool*) const noexcept; + bool getAlphaToCoverage(bool*) const noexcept; bool hasShadowMultiplier(bool*) const noexcept; bool getRequiredAttributes(AttributeBitset*) const noexcept; bool getRefractionMode(RefractionMode* value) const noexcept; diff --git a/filament/src/PerShadowMapUniforms.cpp b/filament/src/PerShadowMapUniforms.cpp index d31053f3f46..465e5920fe4 100644 --- a/filament/src/PerShadowMapUniforms.cpp +++ b/filament/src/PerShadowMapUniforms.cpp @@ -60,9 +60,8 @@ void PerShadowMapUniforms::prepareCamera(Transaction const& transaction, s.viewFromClipMatrix = viewFromClip; // 1/projection s.clipFromWorldMatrix = clipFromWorld; // projection * view s.worldFromClipMatrix = worldFromClip; // 1/(projection * view) + s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldOrigin)); s.clipTransform = camera.clipTransfrom; - s.cameraPosition = float3{ camera.getPosition() }; - s.worldOffset = camera.getWorldOffset(); s.cameraFar = camera.zf; s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn); s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn); diff --git a/filament/src/PerViewUniforms.cpp b/filament/src/PerViewUniforms.cpp index 98dcc0ac0d2..285b68a58ae 100644 --- a/filament/src/PerViewUniforms.cpp +++ b/filament/src/PerViewUniforms.cpp @@ -76,9 +76,8 @@ void PerViewUniforms::prepareCamera(FEngine& engine, const CameraInfo& camera) n s.viewFromClipMatrix = viewFromClip; // 1/projection s.clipFromWorldMatrix = clipFromWorld; // projection * view s.worldFromClipMatrix = worldFromClip; // 1/(projection * view) + s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldOrigin)); s.clipTransform = camera.clipTransfrom; - s.cameraPosition = float3{ camera.getPosition() }; - s.worldOffset = camera.getWorldOffset(); s.cameraFar = camera.zf; s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn); s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn); @@ -130,15 +129,10 @@ void PerViewUniforms::prepareTemporalNoise(FEngine& engine, } void PerViewUniforms::prepareFog(float3 const& cameraPosition, FogOptions const& options) noexcept { - // this can't be too high because we need density / heightFalloff to produce something - // close to fogOptions.density in the fragment shader which use 16-bits floats. - constexpr float epsilon = 0.001f; - const float heightFalloff = std::max(epsilon, options.heightFalloff); + const float heightFalloff = std::max(0.0f, options.heightFalloff); - // precalculate the constant part of density integral and correct for exp2() in the shader - const float density = ((options.density / heightFalloff) * - std::exp(-heightFalloff * (cameraPosition.y - options.height))) - * float(1.0f / F_LN2); + // precalculate the constant part of density integral + const float density = -heightFalloff * (cameraPosition.y - options.height); auto& s = mUniforms.edit(); s.fogStart = options.distance; @@ -146,7 +140,7 @@ void PerViewUniforms::prepareFog(float3 const& cameraPosition, FogOptions const& s.fogHeight = options.height; s.fogHeightFalloff = heightFalloff; s.fogColor = options.color; - s.fogDensity = density; + s.fogDensity = { options.density, density, options.density * std::exp(density) }; s.fogInscatteringStart = options.inScatteringStart; s.fogInscatteringSize = options.inScatteringSize; s.fogColorFromIbl = options.fogColorFromIbl ? 1.0f : 0.0f; diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index b6690a08e53..c7b93e51c7c 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -357,7 +357,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, if (params.options.stable) { // Use the world origin as reference point, fixed w.r.t. the camera - snapLightFrustum(s, o, Mv, -camera.getWorldOffset(), + snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz, 1.0f / float(shadowMapInfo.shadowDimension)); } diff --git a/filament/src/details/Camera.h b/filament/src/details/Camera.h index ebebb29eca2..edf7e90025f 100644 --- a/filament/src/details/Camera.h +++ b/filament/src/details/Camera.h @@ -219,7 +219,6 @@ struct CameraInfo { float d{}; // focus distance [m] math::float3 const& getPosition() const noexcept { return model[3].xyz; } math::float3 getForwardVector() const noexcept { return normalize(-model[2].xyz); } - math::float3 getWorldOffset() const noexcept { return -worldOrigin[3].xyz; } math::mat4 getUserViewMatrix() const noexcept { return view * worldOrigin; } }; diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 816137649d2..b53b0fb287b 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -298,7 +298,16 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder) parser->getColorWrite(&colorWrite); mRasterState.colorWrite = colorWrite; mRasterState.depthFunc = depthTest ? DepthFunc::GE : DepthFunc::A; - mRasterState.alphaToCoverage = mBlendingMode == BlendingMode::MASKED; + + bool alphaToCoverageSet = false; + parser->getAlphaToCoverageSet(&alphaToCoverageSet); + if (alphaToCoverageSet) { + bool alphaToCoverage = false; + parser->getAlphaToCoverage(&alphaToCoverage); + mRasterState.alphaToCoverage = alphaToCoverage; + } else { + mRasterState.alphaToCoverage = mBlendingMode == BlendingMode::MASKED; + } parser->hasSpecularAntiAliasing(&mSpecularAntiAliasing); if (mSpecularAntiAliasing) { diff --git a/filament/src/details/Material.h b/filament/src/details/Material.h index 6aab3d48eb5..4740e85b698 100644 --- a/filament/src/details/Material.h +++ b/filament/src/details/Material.h @@ -124,6 +124,7 @@ class FMaterial : public Material { } bool isDoubleSided() const noexcept { return mDoubleSided; } bool hasDoubleSidedCapability() const noexcept { return mDoubleSidedCapability; } + bool isAlphaToCoverageEnabled() const noexcept { return mRasterState.alphaToCoverage; } float getMaskThreshold() const noexcept { return mMaskThreshold; } bool hasShadowMultiplier() const noexcept { return mHasShadowMultiplier; } AttributeBitset getRequiredAttributes() const noexcept { return mRequiredAttributes; } diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index bd3ee5747df..b95caa08c8a 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -390,7 +390,7 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc * Directional light (always at index 0) */ - FLightManager::Instance directionalLight = lightData.elementAt(0); + FLightManager::Instance const directionalLight = lightData.elementAt(0); const float3 sceneSpaceDirection = lightData.elementAt(0); // guaranteed normalized mPerViewUniforms.prepareDirectionalLight(engine, exposure, sceneSpaceDirection, directionalLight); mHasDirectionalLight = directionalLight.isValid(); @@ -401,32 +401,31 @@ CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept { /* * We apply a "world origin" to "everything" in order to implement the IBL rotation. - * The "world origin" could also be useful for other things, like keeping the origin - * close to the camera position to improve fp precision in the shader for large scenes. + * The "world origin" is also be used to kee the origin close to the camera position to + * improve fp precision in the shader for large scenes. */ - mat4 worldOriginScene; - FIndirectLight const* const ibl = scene->getIndirectLight(); - if (ibl) { - // the IBL transformation must be a rigid transform - mat3f rotation{ scene->getIndirectLight()->getRotation() }; - // for a rigid-body transform, the inverse is the transpose - worldOriginScene = mat4{ transpose(rotation) }; - } + mat4 translation; + mat4 rotation; /* * Calculate all camera parameters needed to render this View for this frame. */ FCamera const* const camera = mViewingCamera ? mViewingCamera : mCullingCamera; - if (engine.debug.view.camera_at_origin) { // this moves the camera to the origin, effectively doing all shader computations in // view-space, which improves floating point precision in the shader by staying around // zero, where fp precision is highest. This also ensures that when the camera is placed // very far from the origin, objects are still rendered and lit properly. - worldOriginScene[3].xyz -= camera->getPosition(); + translation = mat4::translation( -camera->getPosition() ); + } + + FIndirectLight const* const ibl = scene->getIndirectLight(); + if (ibl) { + // the IBL transformation must be a rigid transform + rotation = mat4{ transpose(scene->getIndirectLight()->getRotation()) }; } - return { *camera, worldOriginScene }; + return { *camera, rotation * translation }; } void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, @@ -610,8 +609,10 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, * Update driver state */ + auto const userModelMatrix = inverse(cameraInfo.getUserViewMatrix()); + auto const userCameraPosition = userModelMatrix[3].xyz; mPerViewUniforms.prepareTime(engine, userTime); - mPerViewUniforms.prepareFog(cameraInfo.getPosition(), mFogOptions); + mPerViewUniforms.prepareFog(userCameraPosition, mFogOptions); mPerViewUniforms.prepareTemporalNoise(engine, mTemporalAntiAliasingOptions); mPerViewUniforms.prepareBlending(needsAlphaChannel); } diff --git a/filament/src/fg/PassNode.cpp b/filament/src/fg/PassNode.cpp index a28c2352a0c..8515c53c978 100644 --- a/filament/src/fg/PassNode.cpp +++ b/filament/src/fg/PassNode.cpp @@ -151,7 +151,12 @@ void RenderPassNode::resolve() noexcept { rt.targetBufferFlags |= target; - if (!rt.outgoing[i] || !rt.outgoing[i]->hasActiveReaders()) { + // Discard at the end only if we are writing to this attachment AND later reading + // from it. (in particular, don't discard if we're not writing at all, because this + // attachment might have other readers after us). + // TODO: we could set the discard flag if we are the last reader, i.e. + // if rt->incoming[i] last reader is us. + if (rt.outgoing[i] && !rt.outgoing[i]->hasActiveReaders()) { rt.backend.params.flags.discardEnd |= target; } if (!rt.outgoing[i] || !rt.outgoing[i]->hasWriterPass()) { @@ -161,10 +166,10 @@ void RenderPassNode::resolve() noexcept { rt.backend.params.readOnlyDepthStencil |= RenderPassParams::READONLY_STENCIL; } } + // Discard at the start if this attachment has no prior writer if (!rt.incoming[i] || !rt.incoming[i]->hasActiveWriters()) { rt.backend.params.flags.discardStart |= target; } - VirtualResource* pResource = mFrameGraph.getResource(rt.descriptor.attachments.array[i]); Resource* pTextureResource = static_cast*>(pResource); diff --git a/filament/test/filament_framegraph_test.cpp b/filament/test/filament_framegraph_test.cpp index d5463a9041b..32eb3a6ed77 100644 --- a/filament/test/filament_framegraph_test.cpp +++ b/filament/test/filament_framegraph_test.cpp @@ -890,3 +890,55 @@ TEST_F(FrameGraphTest, SubResourcesWriteRead) { fg.execute(driverApi); } + +TEST_F(FrameGraphTest, WriteResourceReadAsAttachment) { + + // this check s that using a resources as a read-only attachment doesn't set the + // discard flag on that resource. i.e. that the mere fact of reading a resource as an + // attachment doesn't make it invalid. + // see: https://github.com/google/filament/issues/5005 + + struct PassData { + FrameGraphId input; + }; + auto& writePass = fg.addPass("Write Pass", [&](FrameGraph::Builder& builder, auto& data) { + data.input = builder.create("Output buffer", {.width=16, .height=32}); + data.input = builder.write(data.input, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + builder.declareRenderPass("Output target", { .attachments = { + .color = { data.input } + }}); + builder.sideEffect(); + }, + [=](FrameGraphResources const& resources, auto const&, backend::DriverApi&) { + auto rt = resources.getRenderPassInfo(); + EXPECT_EQ(rt.params.flags.discardStart, TargetBufferFlags::COLOR0); + EXPECT_EQ(rt.params.flags.discardEnd, TargetBufferFlags::NONE); + }); + + fg.addPass("Read Pass", [&](FrameGraph::Builder& builder, auto& data) { + data.input = builder.read(writePass->input, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + builder.declareRenderPass("Input target", { .attachments = { + .color = { data.input } + }}); + builder.sideEffect(); + }, + [=](FrameGraphResources const& resources, auto const&, backend::DriverApi&) { + auto rt = resources.getRenderPassInfo(); + EXPECT_EQ(rt.params.flags.discardStart, TargetBufferFlags::NONE); + EXPECT_EQ(rt.params.flags.discardEnd, TargetBufferFlags::NONE); + }); + + fg.addPass("Sample Pass", [&](FrameGraph::Builder& builder, auto& data) { + data.input = builder.sample(writePass->input); + builder.sideEffect(); + }, + [=](FrameGraphResources const& resources, auto const&, backend::DriverApi&) { + }); + + + EXPECT_TRUE(fg.isAcyclic()); + + fg.compile(); + + fg.execute(driverApi); +} diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index a47b27ad2a2..e81ad3c1a27 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.31.7" + spec.version = "1.32.0" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.31.7/filament-v1.31.7-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.32.0/filament-v1.32.0-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/libs/filabridge/include/filament/MaterialChunkType.h b/libs/filabridge/include/filament/MaterialChunkType.h index c63b7d5414b..eed5dea0792 100644 --- a/libs/filabridge/include/filament/MaterialChunkType.h +++ b/libs/filabridge/include/filament/MaterialChunkType.h @@ -67,15 +67,17 @@ enum UTILS_PUBLIC ChunkType : uint64_t { MaterialReflectionMode = charTo64bitNum("MAT_REFL"), MaterialRequiredAttributes = charTo64bitNum("MAT_REQA"), - MaterialDepthWriteSet = charTo64bitNum("MAT_DEWS"), MaterialDoubleSidedSet = charTo64bitNum("MAT_DOSS"), MaterialDoubleSided = charTo64bitNum("MAT_DOSI"), MaterialColorWrite = charTo64bitNum("MAT_CWRIT"), + MaterialDepthWriteSet = charTo64bitNum("MAT_DEWS"), MaterialDepthWrite = charTo64bitNum("MAT_DWRIT"), MaterialDepthTest = charTo64bitNum("MAT_DTEST"), MaterialInstanced = charTo64bitNum("MAT_INSTA"), MaterialCullingMode = charTo64bitNum("MAT_CUMO"), + MaterialAlphaToCoverageSet = charTo64bitNum("MAT_A2CS"), + MaterialAlphaToCoverage = charTo64bitNum("MAT_A2CO"), MaterialHasCustomDepthShader =charTo64bitNum("MAT_CSDP"), diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index d0a98e8ca97..280a2d2adcb 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -27,7 +27,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 31; +static constexpr size_t MATERIAL_VERSION = 32; /** * Supported shading models diff --git a/libs/filabridge/include/private/filament/UibStructs.h b/libs/filabridge/include/private/filament/UibStructs.h index 7073769ef38..ede611055e9 100644 --- a/libs/filabridge/include/private/filament/UibStructs.h +++ b/libs/filabridge/include/private/filament/UibStructs.h @@ -43,16 +43,17 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) // Values that can be accessed in both surface and post-process materials // -------------------------------------------------------------------------------------------- - math::mat4f viewFromWorldMatrix; - math::mat4f worldFromViewMatrix; - math::mat4f clipFromViewMatrix; - math::mat4f viewFromClipMatrix; - math::mat4f clipFromWorldMatrix; - math::mat4f worldFromClipMatrix; - math::float4 clipTransform; // [sx, sy, tx, ty] only used by VERTEX_DOMAIN_DEVICE + math::mat4f viewFromWorldMatrix; // clip view <- world : view matrix + math::mat4f worldFromViewMatrix; // clip view -> world : model matrix + math::mat4f clipFromViewMatrix; // clip <- view world : projection matrix + math::mat4f viewFromClipMatrix; // clip -> view world : inverse projection matrix + math::mat4f clipFromWorldMatrix; // clip <- view <- world + math::mat4f worldFromClipMatrix; // clip -> view -> world + math::mat4f userWorldFromWorldMatrix; // userWorld <- world + math::float4 clipTransform; // [sx, sy, tx, ty] only used by VERTEX_DOMAIN_DEVICE math::float2 clipControl; // clip control - float time; // time in seconds, with a 1 second period + float time; // time in seconds, with a 1-second period float temporalNoise; // noise [0,1] when TAA is used, 0 otherwise math::float4 userTime; // time(s), (double)time - (float)time, 0, 0 @@ -67,14 +68,9 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) float lodBias; // load bias to apply to user materials float refractionLodOffset; - float padding1; - float padding2; // camera position in view space (when camera_at_origin is enabled), i.e. it's (0,0,0). - // Always add worldOffset in the shader to get the true world-space position of the camera. - math::float3 cameraPosition; float oneOverFarMinusNear; // 1 / (f-n), always positive - math::float3 worldOffset; // this is (0,0,0) when camera_at_origin is disabled float nearOverFarMinusNear; // n / (f-n), always positive float cameraFar; // camera *culling* far-plane distance, always positive (projection far is at +inf) float exposure; @@ -140,16 +136,18 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) // -------------------------------------------------------------------------------------------- // Fog [variant: FOG] // -------------------------------------------------------------------------------------------- + math::float3 fogDensity; // { density, -falloff * yc, density * exp(-fallof * yc) } float fogStart; float fogMaxOpacity; float fogHeight; - float fogHeightFalloff; // falloff * 1.44269 + float fogHeightFalloff; + float fogReserved0; math::float3 fogColor; - float fogDensity; // (density/falloff)*exp(-falloff*(camera.y - fogHeight)) + float fogColorFromIbl; float fogInscatteringStart; float fogInscatteringSize; - float fogColorFromIbl; - float fogReserved0; + float fogReserved1; + float fogReserved2; // -------------------------------------------------------------------------------------------- // Screen-space reflections [variant: SSR (i.e.: VSM | SRE)] @@ -162,7 +160,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) float ssrStride; // ssr texel stride, >= 1.0 // bring PerViewUib to 2 KiB - math::float4 reserved[63]; + math::float4 reserved[60]; }; // 2 KiB == 128 float4s diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index 6fc3b864903..2456951a6a6 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -390,7 +390,10 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { MaterialBuilder& featureLevel(FeatureLevel featureLevel) noexcept; - //! Set the blending mode for this material. + /** + * Set the blending mode for this material. When set to MASKED, alpha to coverage is turned on. + * You can override this behavior using alphaToCoverage(false). + */ MaterialBuilder& blending(BlendingMode blending) noexcept; /** @@ -436,6 +439,14 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { */ MaterialBuilder& maskThreshold(float threshold) noexcept; + /** + * Enables or disables alpha-to-coverage. When enabled, the coverage of a fragment is based + * on its alpha value. This parameter is only useful when MSAA is in use. Alpha to coverage + * is enabled automatically when the blend mode is set to MASKED; this behavior can be + * overridden by calling alphaToCoverage(false). + */ + MaterialBuilder& alphaToCoverage(bool enable) noexcept; + //! The material output is multiplied by the shadowing factor (UNLIT model only). MaterialBuilder& shadowMultiplier(bool shadowMultiplier) noexcept; @@ -791,6 +802,8 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { bool mInstanced = false; bool mDepthWrite = true; bool mDepthWriteSet = false; + bool mAlphaToCoverage = false; + bool mAlphaToCoverageSet = false; bool mSpecularAntiAliasing = false; bool mClearCoatIorChange = true; diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 4dc6ab06342..f8eb0811f25 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -336,6 +336,12 @@ MaterialBuilder& MaterialBuilder::maskThreshold(float threshold) noexcept { return *this; } +MaterialBuilder& MaterialBuilder::alphaToCoverage(bool enable) noexcept { + mAlphaToCoverage = enable; + mAlphaToCoverageSet = true; + return *this; +} + MaterialBuilder& MaterialBuilder::shadowMultiplier(bool shadowMultiplier) noexcept { mShadowMultiplier = shadowMultiplier; return *this; @@ -1226,11 +1232,13 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo& container.addSimpleChild(ChunkType::MaterialBlendingMode, static_cast(mBlendingMode)); container.addSimpleChild(ChunkType::MaterialTransparencyMode, static_cast(mTransparencyMode)); container.addSimpleChild(ChunkType::MaterialReflectionMode, static_cast(mReflectionMode)); - container.addSimpleChild(ChunkType::MaterialDepthWriteSet, mDepthWriteSet); container.addSimpleChild(ChunkType::MaterialColorWrite, mColorWrite); + container.addSimpleChild(ChunkType::MaterialDepthWriteSet, mDepthWriteSet); container.addSimpleChild(ChunkType::MaterialDepthWrite, mDepthWrite); container.addSimpleChild(ChunkType::MaterialDepthTest, mDepthTest); container.addSimpleChild(ChunkType::MaterialInstanced, mInstanced); + container.addSimpleChild(ChunkType::MaterialAlphaToCoverageSet, mAlphaToCoverageSet); + container.addSimpleChild(ChunkType::MaterialAlphaToCoverage, mAlphaToCoverage); container.addSimpleChild(ChunkType::MaterialCullingMode, static_cast(mCullingMode)); uint64_t properties = 0; diff --git a/libs/filamat/src/shaders/UibGenerator.cpp b/libs/filamat/src/shaders/UibGenerator.cpp index bac835d17c2..503adc3cf8b 100644 --- a/libs/filamat/src/shaders/UibGenerator.cpp +++ b/libs/filamat/src/shaders/UibGenerator.cpp @@ -41,6 +41,7 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { { "viewFromClipMatrix", 0, Type::MAT4, Precision::HIGH }, { "clipFromWorldMatrix", 0, Type::MAT4, Precision::HIGH }, { "worldFromClipMatrix", 0, Type::MAT4, Precision::HIGH }, + { "userWorldFromWorldMatrix",0,Type::MAT4, Precision::HIGH }, { "clipTransform", 0, Type::FLOAT4, Precision::HIGH }, { "clipControl", 0, Type::FLOAT2 }, @@ -58,12 +59,8 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { { "lodBias", 0, Type::FLOAT }, { "refractionLodOffset", 0, Type::FLOAT }, - { "padding1", 0, Type::FLOAT }, - { "padding2", 0, Type::FLOAT }, - { "cameraPosition", 0, Type::FLOAT3, Precision::HIGH }, { "oneOverFarMinusNear", 0, Type::FLOAT, Precision::HIGH }, - { "worldOffset", 0, Type::FLOAT3 }, { "nearOverFarMinusNear", 0, Type::FLOAT, Precision::HIGH }, { "cameraFar", 0, Type::FLOAT }, { "exposure", 0, Type::FLOAT, Precision::HIGH }, // high precision to work around #3602 (qualcom), @@ -120,16 +117,18 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { // ------------------------------------------------------------------------------------ // Fog [variant: FOG] // ------------------------------------------------------------------------------------ - { "fogStart", 0, Type::FLOAT }, + { "fogDensity", 0, Type::FLOAT3,Precision::HIGH }, + { "fogStart", 0, Type::FLOAT, Precision::HIGH }, { "fogMaxOpacity", 0, Type::FLOAT }, - { "fogHeight", 0, Type::FLOAT }, + { "fogHeight", 0, Type::FLOAT, Precision::HIGH }, { "fogHeightFalloff", 0, Type::FLOAT }, + { "fogReserved0", 0, Type::FLOAT }, { "fogColor", 0, Type::FLOAT3 }, - { "fogDensity", 0, Type::FLOAT }, - { "fogInscatteringStart", 0, Type::FLOAT }, - { "fogInscatteringSize", 0, Type::FLOAT }, { "fogColorFromIbl", 0, Type::FLOAT }, - { "fogReserved0", 0, Type::FLOAT }, + { "fogInscatteringStart", 0, Type::FLOAT, Precision::HIGH }, + { "fogInscatteringSize", 0, Type::FLOAT }, + { "fogReserved1", 0, Type::FLOAT }, + { "fogReserved2", 0, Type::FLOAT }, // ------------------------------------------------------------------------------------ // Screen-space reflections [variant: SSR (i.e.: VSM | SRE)] diff --git a/libs/filamentapp/include/filamentapp/IBL.h b/libs/filamentapp/include/filamentapp/IBL.h index 8943823ed95..953f07dc3b3 100644 --- a/libs/filamentapp/include/filamentapp/IBL.h +++ b/libs/filamentapp/include/filamentapp/IBL.h @@ -55,6 +55,7 @@ class IBL { return mSkybox; } + bool hasSphericalHarmonics() const { return mHasSphericalHarmonics; } filament::math::float3 const* getSphericalHarmonics() const { return mBands; } private: @@ -71,6 +72,7 @@ class IBL { filament::Engine& mEngine; filament::math::float3 mBands[9] = {}; + bool mHasSphericalHarmonics = false; filament::Texture* mTexture = nullptr; filament::IndirectLight* mIndirectLight = nullptr; diff --git a/libs/filamentapp/src/IBL.cpp b/libs/filamentapp/src/IBL.cpp index 762610db6d2..0e83d62c5c9 100644 --- a/libs/filamentapp/src/IBL.cpp +++ b/libs/filamentapp/src/IBL.cpp @@ -139,6 +139,7 @@ bool IBL::loadFromKtx(const std::string& prefix) { if (!iblKtx->getSphericalHarmonics(mBands)) { return false; } + mHasSphericalHarmonics = true; mIndirectLight = IndirectLight::Builder() .reflections(mTexture) @@ -170,6 +171,7 @@ bool IBL::loadFromDirectory(const utils::Path& path) { } else { return false; } + mHasSphericalHarmonics = true; // Read mip-mapped cubemap const std::string prefix = "m"; diff --git a/libs/geometry/include/geometry/TangentSpaceMesh.h b/libs/geometry/include/geometry/TangentSpaceMesh.h index bb47c030a83..616245a5ce4 100644 --- a/libs/geometry/include/geometry/TangentSpaceMesh.h +++ b/libs/geometry/include/geometry/TangentSpaceMesh.h @@ -27,8 +27,7 @@ namespace geometry { struct TangentSpaceMeshInput; struct TangentSpaceMeshOutput; - /* WARNING: WORK-IN-PROGRESS, PLEASE DO NOT USE */ -/** + /** * This class builds Filament-style TANGENTS buffers given an input mesh. * * This class enables the client to chose between several algorithms. The client can retrieve the @@ -139,8 +138,8 @@ class TangentSpaceMesh { */ Builder& operator=(Builder&& that) noexcept; - Builder(const Builder&) = delete; - Builder& operator=(const Builder&) = delete; + Builder(Builder const&) = delete; + Builder& operator=(Builder const&) = delete; /** * Client must provide this parameter @@ -154,7 +153,7 @@ class TangentSpaceMesh { * @param stride The stride for iterating through `normals` * @return Builder */ - Builder& normals(const filament::math::float3* normals, size_t stride = 0) noexcept; + Builder& normals(filament::math::float3 const* normals, size_t stride = 0) noexcept; /** * @param tangents The input tangents. The `w` component is for use with @@ -162,25 +161,25 @@ class TangentSpaceMesh { * @param stride The stride for iterating through `tangents` * @return Builder */ - Builder& tangents(const filament::math::float4* tangents, size_t stride = 0) noexcept; + Builder& tangents(filament::math::float4 const* tangents, size_t stride = 0) noexcept; /** * @param uvs The input uvs * @param stride The stride for iterating through `uvs` * @return Builder */ - Builder& uvs(const filament::math::float2* uvs, size_t stride = 0) noexcept; + Builder& uvs(filament::math::float2 const* uvs, size_t stride = 0) noexcept; /** * @param positions The input positions * @param stride The stride for iterating through `positions` * @return Builder */ - Builder& positions(const filament::math::float3* positions, size_t stride = 0) noexcept; + Builder& positions(filament::math::float3 const* positions, size_t stride = 0) noexcept; Builder& triangleCount(size_t triangleCount) noexcept; - Builder& triangles(const filament::math::uint3* triangles) noexcept; - Builder& triangles(const filament::math::ushort3* triangles) noexcept; + Builder& triangles(filament::math::uint3 const* triangles) noexcept; + Builder& triangles(filament::math::ushort3 const* triangles) noexcept; Builder& algorithm(Algorithm algorithm) noexcept; @@ -215,8 +214,8 @@ class TangentSpaceMesh { */ TangentSpaceMesh& operator=(TangentSpaceMesh&& that) noexcept; - TangentSpaceMesh(const TangentSpaceMesh&) = delete; - TangentSpaceMesh& operator=(const TangentSpaceMesh&) = delete; + TangentSpaceMesh(TangentSpaceMesh const&) = delete; + TangentSpaceMesh& operator=(TangentSpaceMesh const&) = delete; /** * Number of output vertices diff --git a/libs/geometry/src/MikktspaceImpl.cpp b/libs/geometry/src/MikktspaceImpl.cpp index 6c35647f6ba..f40da14f0b7 100644 --- a/libs/geometry/src/MikktspaceImpl.cpp +++ b/libs/geometry/src/MikktspaceImpl.cpp @@ -40,7 +40,7 @@ int MikktspaceImpl::getNumVerticesOfFace(SMikkTSpaceContext const* context, } void MikktspaceImpl::getPosition(SMikkTSpaceContext const* context, float fvPosOut[], - const int iFace, const int iVert) noexcept { + int const iFace, int const iVert) noexcept { auto const wrapper = MikktspaceImpl::getThis(context); float3 const pos = *pointerAdd(wrapper->mPositions, wrapper->getTriangle(iFace)[iVert], wrapper->mPositionStride); @@ -60,10 +60,10 @@ void MikktspaceImpl::getNormal(SMikkTSpaceContext const* context, float fvNormOu } void MikktspaceImpl::getTexCoord(SMikkTSpaceContext const* context, float fvTexcOut[], - const int iFace, const int iVert) noexcept { + int const iFace, int const iVert) noexcept { auto const wrapper = MikktspaceImpl::getThis(context); - float2 const texc = - *pointerAdd(wrapper->mUVs, wrapper->getTriangle(iFace)[iVert], wrapper->mUVStride); + float2 const texc + = *pointerAdd(wrapper->mUVs, wrapper->getTriangle(iFace)[iVert], wrapper->mUVStride); fvTexcOut[0] = texc.x; fvTexcOut[1] = texc.y; } @@ -102,7 +102,7 @@ MikktspaceImpl* MikktspaceImpl::getThis(SMikkTSpaceContext const* context) noexc return (MikktspaceImpl*) context->m_pUserData; } -inline const uint3 MikktspaceImpl::getTriangle(int triangleIndex) const noexcept { +inline const uint3 MikktspaceImpl::getTriangle(int const triangleIndex) const noexcept { const size_t tstride = mIsTriangle16 ? sizeof(ushort3) : sizeof(uint3); return mIsTriangle16 ? uint3(*(ushort3*) (pointerAdd(mTriangles, triangleIndex, tstride))) : *(uint3*) (pointerAdd(mTriangles, triangleIndex, tstride)); @@ -115,7 +115,7 @@ void MikktspaceImpl::run(TangentSpaceMeshOutput* output) noexcept { .m_getPosition = MikktspaceImpl::getPosition, .m_getNormal = MikktspaceImpl::getNormal, .m_getTexCoord = MikktspaceImpl::getTexCoord, - .m_setTSpaceBasic = MikktspaceImpl::setTSpaceBasic + .m_setTSpaceBasic = MikktspaceImpl::setTSpaceBasic, }; SMikkTSpaceContext context{.m_pInterface = &interface, .m_pUserData = this}; genTangSpaceDefault(&context); @@ -128,12 +128,12 @@ void MikktspaceImpl::run(TangentSpaceMeshOutput* output) noexcept { meshopt_remapVertexBuffer((void*) newVertices.data(), mOutVertices.data(), mOutVertices.size(), sizeof(IOVertex), remap.data()); - uint3* triangles32 = new uint3[mFaceCount]; + uint3* triangles32 = output->triangles32.allocate(mFaceCount); meshopt_remapIndexBuffer((uint32_t*) triangles32, NULL, mOutVertices.size(), remap.data()); - float3* outPositions = new float3[vertexCount]; - float2* outUVs = new float2[vertexCount]; - quatf* outQuats = new quatf[vertexCount]; + float3* outPositions = output->positions.allocate(vertexCount); + float2* outUVs = output->uvs.allocate(vertexCount); + quatf* outQuats = output->tangentSpace.allocate(vertexCount); for (size_t i = 0; i < vertexCount; ++i) { outPositions[i] = newVertices[i].position; @@ -142,10 +142,6 @@ void MikktspaceImpl::run(TangentSpaceMeshOutput* output) noexcept { } output->vertexCount = vertexCount; - output->positions = outPositions; - output->uvs = outUVs; - output->tangentSpace = outQuats; - output->triangles32 = triangles32; output->triangleCount = mFaceCount; } diff --git a/libs/geometry/src/MikktspaceImpl.h b/libs/geometry/src/MikktspaceImpl.h index cc4831d4d66..4ed961bdb0e 100644 --- a/libs/geometry/src/MikktspaceImpl.h +++ b/libs/geometry/src/MikktspaceImpl.h @@ -39,28 +39,28 @@ class MikktspaceImpl { quatf tangentSpace; }; - MikktspaceImpl(const TangentSpaceMeshInput* input) noexcept; + MikktspaceImpl(TangentSpaceMeshInput const* input) noexcept; - MikktspaceImpl(const MikktspaceImpl&) = delete; - MikktspaceImpl& operator=(const MikktspaceImpl&) = delete; + MikktspaceImpl(MikktspaceImpl const&) = delete; + MikktspaceImpl& operator=(MikktspaceImpl const&) = delete; void run(TangentSpaceMeshOutput* output) noexcept; private: static int getNumFaces(SMikkTSpaceContext const* context) noexcept; static int getNumVerticesOfFace(SMikkTSpaceContext const* context, int const iFace) noexcept; - static void getPosition(SMikkTSpaceContext const* context, float fvPosOut[], const int iFace, - const int iVert) noexcept; + static void getPosition(SMikkTSpaceContext const* context, float fvPosOut[], int const iFace, + int const iVert) noexcept; static void getNormal(SMikkTSpaceContext const* context, float fvNormOut[], int const iFace, int const iVert) noexcept; - static void getTexCoord(SMikkTSpaceContext const* context, float fvTexcOut[], const int iFace, - const int iVert) noexcept; + static void getTexCoord(SMikkTSpaceContext const* context, float fvTexcOut[], int const iFace, + int const iVert) noexcept; static void setTSpaceBasic(SMikkTSpaceContext const* context, float const fvTangent[], float const fSign, int const iFace, int const iVert) noexcept; static MikktspaceImpl* getThis(SMikkTSpaceContext const* context) noexcept; - inline const uint3 getTriangle(int triangleIndex) const noexcept; + inline const uint3 getTriangle(int const triangleIndex) const noexcept; int const mFaceCount; float3 const* mPositions; diff --git a/libs/geometry/src/TangentSpaceMesh.cpp b/libs/geometry/src/TangentSpaceMesh.cpp index 8e21c9e3b95..c1027c63989 100644 --- a/libs/geometry/src/TangentSpaceMesh.cpp +++ b/libs/geometry/src/TangentSpaceMesh.cpp @@ -47,7 +47,7 @@ uint8_t const NORMALS = NORMALS_BIT; uint8_t const POSITIONS_INDICES = POSITIONS_BIT | INDICES_BIT; uint8_t const NORMALS_UVS_POSITIONS_INDICES = NORMALS_BIT | UVS_BIT | POSITIONS_BIT | INDICES_BIT; -std::string_view to_string(Algorithm algorithm) noexcept { +std::string_view to_string(Algorithm const algorithm) noexcept { switch (algorithm) { case Algorithm::DEFAULT: return "DEFAULT"; @@ -69,11 +69,11 @@ inline bool isInputType(uint8_t const inputType, uint8_t const checkType) noexce } template -inline void takeStride(InputType*& out, size_t stride) noexcept { +inline void takeStride(InputType*& out, size_t const stride) noexcept { out = pointerAdd(out, 1, stride); } -inline Algorithm selectBestDefaultAlgorithm(uint8_t inputType) { +inline Algorithm selectBestDefaultAlgorithm(uint8_t const inputType) { Algorithm outAlgo; if (isInputType(inputType, NORMALS_UVS_POSITIONS_INDICES)) { outAlgo = Algorithm::MIKKTSPACE; @@ -170,7 +170,7 @@ inline std::pair frisvadKernel(float3 const& n) { void frisvadMethod(TangentSpaceMeshInput const* input, TangentSpaceMeshOutput* output) noexcept { size_t const vertexCount = input->vertexCount; - quatf* quats = new quatf[vertexCount]; + quatf* quats = output->tangentSpace.allocate(vertexCount); float3 const* UTILS_RESTRICT normals = input->normals; size_t nstride = input->normalStride ? input->normalStride : sizeof(float3); @@ -181,19 +181,19 @@ void frisvadMethod(TangentSpaceMeshInput const* input, TangentSpaceMeshOutput* o quats[qindex] = mat3f::packTangentFrame({t, b, n}, sizeof(int32_t)); normals = pointerAdd(normals, 1, nstride); } - output->tangentSpace = quats; + output->vertexCount = input->vertexCount; output->triangleCount = input->triangleCount; - output->uvs = input->uvs; - output->positions = input->positions; - output->triangles32 = input->triangles32; - output->triangles16 = input->triangles16; + output->uvs.borrow(input->uvs); + output->positions.borrow(input->positions); + output->triangles32.borrow(input->triangles32); + output->triangles16.borrow(input->triangles16); } void hughesMollerMethod(TangentSpaceMeshInput const* input, TangentSpaceMeshOutput* output) noexcept { size_t const vertexCount = input->vertexCount; - quatf* quats = new quatf[vertexCount]; + quatf* quats = output->tangentSpace.allocate(vertexCount); float3 const* UTILS_RESTRICT normals = input->normals; size_t nstride = input->normalStride ? input->normalStride : sizeof(float3); @@ -213,37 +213,36 @@ void hughesMollerMethod(TangentSpaceMeshInput const* input, TangentSpaceMeshOutp quats[qindex] = mat3f::packTangentFrame({t, b, n}, sizeof(int32_t)); normals = pointerAdd(normals, 1, nstride); } - output->tangentSpace = quats; output->vertexCount = input->vertexCount; output->triangleCount = input->triangleCount; - output->uvs = input->uvs; - output->positions = input->positions; - output->triangles32 = input->triangles32; - output->triangles16 = input->triangles16; + output->uvs.borrow(input->uvs); + output->positions.borrow(input->positions); + output->triangles32.borrow(input->triangles32); + output->triangles16.borrow(input->triangles16); } void flatShadingMethod(TangentSpaceMeshInput const* input, TangentSpaceMeshOutput* output) noexcept { - const float3* positions = input->positions; - const size_t pstride = input->positionStride ? input->positionStride : sizeof(float3); + float3 const* positions = input->positions; + size_t const pstride = input->positionStride ? input->positionStride : sizeof(float3); - const float2* uvs = input->uvs; - const size_t uvstride = input->uvStride ? input->uvStride : sizeof(float2); + float2 const* uvs = input->uvs; + size_t const uvstride = input->uvStride ? input->uvStride : sizeof(float2); - const bool isTriangle16 = input->triangles16 != nullptr; - const uint8_t* triangles = isTriangle16 ? (const uint8_t*) input->triangles16 : - (const uint8_t*) input->triangles32; + bool const isTriangle16 = input->triangles16 != nullptr; + uint8_t const* triangles = isTriangle16 ? (uint8_t const*) input->triangles16 : + (uint8_t const*) input->triangles32; - const size_t triangleCount = input->triangleCount; - const size_t tstride = isTriangle16 ? sizeof(ushort3) : sizeof(uint3); + size_t const triangleCount = input->triangleCount; + size_t const tstride = isTriangle16 ? sizeof(ushort3) : sizeof(uint3); - const size_t outVertexCount = triangleCount * 3; - float3* outPositions = new float3[outVertexCount]; - float2* outUvs = uvs ? new float2[outVertexCount] : nullptr; - quatf* quats = new quatf[outVertexCount]; + size_t const outVertexCount = triangleCount * 3; + float3* outPositions = output->positions.allocate(outVertexCount); + float2* outUvs = uvs ? output->uvs.allocate(outVertexCount) : nullptr; + quatf* quats = output->tangentSpace.allocate(outVertexCount); - const size_t outTriangleCount = triangleCount; - uint3* outTriangles = new uint3[outTriangleCount]; + size_t const outTriangleCount = triangleCount; + uint3* outTriangles = output->triangles32.allocate(outTriangleCount); size_t vindex = 0; for (size_t tindex = 0; tindex < triangleCount; ++tindex) { @@ -251,9 +250,9 @@ void flatShadingMethod(TangentSpaceMeshInput const* input, TangentSpaceMeshOutpu uint3(*(ushort3*)(pointerAdd(triangles, tindex, tstride))) : *(uint3*)(pointerAdd(triangles, tindex, tstride)); - const float3 pa = *pointerAdd(positions, tri.x, pstride); - const float3 pb = *pointerAdd(positions, tri.y, pstride); - const float3 pc = *pointerAdd(positions, tri.z, pstride); + float3 const pa = *pointerAdd(positions, tri.x, pstride); + float3 const pb = *pointerAdd(positions, tri.y, pstride); + float3 const pc = *pointerAdd(positions, tri.z, pstride); uint32_t i0 = vindex++, i1 = vindex++, i2 = vindex++; outTriangles[tindex] = uint3{i0, i1, i2}; @@ -262,10 +261,10 @@ void flatShadingMethod(TangentSpaceMeshInput const* input, TangentSpaceMeshOutpu outPositions[i1] = pb; outPositions[i2] = pc; - const float3 n = normalize(cross(pc - pb, pa - pb)); + float3 const n = normalize(cross(pc - pb, pa - pb)); const auto [t, b] = frisvadKernel(n); - const quatf tspace = mat3f::packTangentFrame({t, b, n}, sizeof(int32_t)); + quatf const tspace = mat3f::packTangentFrame({t, b, n}, sizeof(int32_t)); quats[i0] = tspace; quats[i1] = tspace; quats[i2] = tspace; @@ -277,11 +276,7 @@ void flatShadingMethod(TangentSpaceMeshInput const* input, TangentSpaceMeshOutpu } } - output->tangentSpace = quats; - output->positions = outPositions; output->vertexCount = outVertexCount; - output->uvs = outUvs; - output->triangles32 = outTriangles; output->triangleCount = outTriangleCount; } @@ -290,7 +285,7 @@ void mikktspaceMethod(TangentSpaceMeshInput const* input, TangentSpaceMeshOutput impl.run(output); } -inline float3 randomPerp(const float3& n) { +inline float3 randomPerp(float3 const& n) { float3 perp = cross(n, float3{1, 0, 0}); float sqrlen = dot(perp, perp); if (sqrlen <= std::numeric_limits::epsilon()) { @@ -356,38 +351,30 @@ void lengyelMethod(TangentSpaceMeshInput const* input, TangentSpaceMeshOutput* o tan2[tri.z] += tdir; } - quatf* quats = new quatf[vertexCount]; + quatf* quats = output->tangentSpace.allocate(vertexCount); for (size_t a = 0; a < vertexCount; a++) { float3 const& n = normals[a]; float3 const& t1 = tan1[a]; float3 const& t2 = tan2[a]; // Gram-Schmidt orthogonalize - float3 t = normalize(t1 - n * dot(n, t1)); + float3 const t = normalize(t1 - n * dot(n, t1)); // Calculate handedness - float w = (dot(cross(n, t1), t2) < 0.0f) ? -1.0f : 1.0f; + float const w = (dot(cross(n, t1), t2) < 0.0f) ? -1.0f : 1.0f; float3 b = w < 0 ? cross(t, n) : cross(n, t); quats[a] = mat3f::packTangentFrame({t, b, n}, sizeof(int32_t)); } - output->tangentSpace = quats; output->vertexCount = vertexCount; output->triangleCount = triangleCount; - output->uvs = uvs; - output->positions = positions; - output->triangles32 = triangles32; - output->triangles16 = triangles16; + output->uvs.borrow(uvs); + output->positions.borrow(positions); + output->triangles32.borrow(triangles32); + output->triangles16.borrow(triangles16); } -template -inline void cleanOutputPointer(DataType*& ptr, InputType inputPtr) noexcept { - if (ptr && ptr != (const DataType*) inputPtr) { - delete[] ptr; - } - ptr = nullptr; -} } // anonymous namespace Builder::Builder() noexcept @@ -411,19 +398,19 @@ Builder& Builder::vertexCount(size_t vertexCount) noexcept { return *this; } -Builder& Builder::normals(const float3* normals, size_t stride) noexcept { +Builder& Builder::normals(float3 const* normals, size_t stride) noexcept { mMesh->mInput->normals = normals; mMesh->mInput->normalStride = stride; return *this; } -Builder& Builder::uvs(const float2* uvs, size_t stride) noexcept { +Builder& Builder::uvs(float2 const* uvs, size_t stride) noexcept { mMesh->mInput->uvs = uvs; mMesh->mInput->uvStride = stride; return *this; } -Builder& Builder::positions(const float3* positions, size_t stride) noexcept { +Builder& Builder::positions(float3 const* positions, size_t stride) noexcept { mMesh->mInput->positions = positions; mMesh->mInput->positionStride = stride; return *this; @@ -434,12 +421,12 @@ Builder& Builder::triangleCount(size_t triangleCount) noexcept { return *this; } -Builder& Builder::triangles(const uint3* triangle32) noexcept { +Builder& Builder::triangles(uint3 const* triangle32) noexcept { mMesh->mInput->triangles32 = triangle32; return *this; } -Builder& Builder::triangles(const ushort3* triangle16) noexcept { +Builder& Builder::triangles(ushort3 const* triangle16) noexcept { mMesh->mInput->triangles16 = triangle16; return *this; } @@ -494,12 +481,6 @@ TangentSpaceMesh::TangentSpaceMesh() noexcept } TangentSpaceMesh::~TangentSpaceMesh() noexcept { - cleanOutputPointer(mOutput->tangentSpace, (quatf*) nullptr); - cleanOutputPointer(mOutput->uvs, mInput->uvs); - cleanOutputPointer(mOutput->positions, mInput->positions); - cleanOutputPointer(mOutput->triangles16, mInput->triangles16); - cleanOutputPointer(mOutput->triangles32, mInput->triangles32); - delete mOutput; delete mInput; } @@ -522,8 +503,9 @@ size_t TangentSpaceMesh::getVertexCount() const noexcept { void TangentSpaceMesh::getPositions(float3* positions, size_t stride) const { ASSERT_PRECONDITION(mInput->positions, "Must provide input positions"); stride = stride ? stride : sizeof(decltype(*positions)); + auto outPositions = mOutput->positions.get(); for (size_t i = 0; i < mOutput->vertexCount; ++i) { - *positions = mOutput->positions[i]; + *positions = outPositions[i]; takeStride(positions, stride); } } @@ -531,8 +513,9 @@ void TangentSpaceMesh::getPositions(float3* positions, size_t stride) const { void TangentSpaceMesh::getUVs(float2* uvs, size_t stride) const { ASSERT_PRECONDITION(mInput->uvs, "Must provide input UVs"); stride = stride ? stride : sizeof(decltype(*uvs)); + auto outUvs = mOutput->uvs.get(); for (size_t i = 0; i < mOutput->vertexCount; ++i) { - *uvs = mOutput->uvs[i]; + *uvs = outUvs[i]; takeStride(uvs, stride); } } @@ -544,10 +527,12 @@ size_t TangentSpaceMesh::getTriangleCount() const noexcept { void TangentSpaceMesh::getTriangles(uint3* out) const { ASSERT_PRECONDITION(mInput->triangles16 || mInput->triangles32, "Must provide input triangles"); - const bool is16 = mOutput->triangles16 != nullptr; - const size_t stride = sizeof(decltype(*out)); + bool const is16 = (bool) mOutput->triangles16; + auto triangles16 = mOutput->triangles16.get(); + auto triangles32 = mOutput->triangles32.get(); + size_t const stride = sizeof(decltype(*out)); for (size_t i = 0; i < mOutput->triangleCount; ++i) { - *out = is16 ? uint3{mOutput->triangles16[i]} : mOutput->triangles32[i]; + *out = is16 ? uint3{triangles16[i]} : triangles32[i]; takeStride(out, stride); } } @@ -555,13 +540,15 @@ void TangentSpaceMesh::getTriangles(uint3* out) const { void TangentSpaceMesh::getTriangles(ushort3* out) const { ASSERT_PRECONDITION(mInput->triangles16 || mInput->triangles32, "Must provide input triangles"); - const bool is16 = mOutput->triangles16 != nullptr; + const bool is16 = (bool) mOutput->triangles16; + auto triangles16 = mOutput->triangles16.get(); + auto triangles32 = mOutput->triangles32.get(); const size_t stride = sizeof(decltype(*out)); for (size_t i = 0, c = mOutput->triangleCount; i < c; ++i) { if (is16) { - *out = mOutput->triangles16[i]; + *out = triangles16[i]; } else { - const uint3 &tri = mOutput->triangles32[i]; + uint3 const& tri = triangles32[i]; ASSERT_PRECONDITION(tri.x <= USHRT_MAX && tri.y <= USHRT_MAX && tri.z <= USHRT_MAX, "Overflow when casting uint3 to ushort3"); @@ -575,8 +562,8 @@ void TangentSpaceMesh::getTriangles(ushort3* out) const { void TangentSpaceMesh::getQuats(quatf* out, size_t stride) const noexcept { stride = stride ? stride : sizeof(decltype((*out))); - const quatf* tangentSpace = mOutput->tangentSpace; - const size_t vertexCount = mOutput->vertexCount; + auto tangentSpace = mOutput->tangentSpace.get(); + size_t const vertexCount = mOutput->vertexCount; for (size_t i = 0; i < vertexCount; ++i) { *out = tangentSpace[i]; takeStride(out, stride); @@ -585,8 +572,8 @@ void TangentSpaceMesh::getQuats(quatf* out, size_t stride) const noexcept { void TangentSpaceMesh::getQuats(short4* out, size_t stride) const noexcept { stride = stride ? stride : sizeof(decltype((*out))); - const quatf* tangentSpace = mOutput->tangentSpace; - const size_t vertexCount = mOutput->vertexCount; + auto tangentSpace = mOutput->tangentSpace.get(); + size_t const vertexCount = mOutput->vertexCount; for (size_t i = 0; i < vertexCount; ++i) { *out = packSnorm16(tangentSpace[i].xyzw); takeStride(out, stride); @@ -595,8 +582,8 @@ void TangentSpaceMesh::getQuats(short4* out, size_t stride) const noexcept { void TangentSpaceMesh::getQuats(quath* out, size_t stride) const noexcept { stride = stride ? stride : sizeof(decltype((*out))); - const quatf* tangentSpace = mOutput->tangentSpace; - const size_t vertexCount = mOutput->vertexCount; + auto tangentSpace = mOutput->tangentSpace.get(); + size_t const vertexCount = mOutput->vertexCount; for (size_t i = 0; i < vertexCount; ++i) { *out = quath(tangentSpace[i].xyzw); takeStride(out, stride); diff --git a/libs/geometry/src/TangentSpaceMeshInternal.h b/libs/geometry/src/TangentSpaceMeshInternal.h index f9beb0684fe..659af962989 100644 --- a/libs/geometry/src/TangentSpaceMeshInternal.h +++ b/libs/geometry/src/TangentSpaceMeshInternal.h @@ -22,11 +22,47 @@ #include #include +#include + +#include + namespace filament::geometry { using namespace filament::math; using Algorithm = TangentSpaceMesh::Algorithm; +template +class InternalArray { +public: + void borrow(T const* ptr) { + assert_invariant(mAllocated.empty()); + mBorrowed = ptr; + } + + T* allocate(size_t size) { + assert_invariant(!mBorrowed); + mAllocated.resize(size); + mAllocated.shrink_to_fit(); + return mAllocated.data(); + } + + explicit operator bool() const noexcept { + return !mBorrowed && !mAllocated.empty(); + } + + const T* get() { + assert_invariant((bool) this); + if (mBorrowed) { + return mBorrowed; + } + return mAllocated.data(); + } + +private: + std::vector mAllocated; + T const* mBorrowed = nullptr; +}; + struct TangentSpaceMeshInput { size_t vertexCount = 0; float3 const* normals = nullptr; @@ -49,11 +85,11 @@ struct TangentSpaceMeshOutput { size_t triangleCount = 0; size_t vertexCount = 0; - quatf const* tangentSpace = nullptr; - float2 const* uvs = nullptr; - float3 const* positions = nullptr; - uint3 const* triangles32 = nullptr; - ushort3 const* triangles16 = nullptr; + InternalArray tangentSpace; + InternalArray uvs; + InternalArray positions; + InternalArray triangles32; + InternalArray triangles16; }; template diff --git a/libs/utils/include/utils/compiler.h b/libs/utils/include/utils/compiler.h index 65b0c4ff116..7d62a389bb8 100644 --- a/libs/utils/include/utils/compiler.h +++ b/libs/utils/include/utils/compiler.h @@ -54,6 +54,12 @@ # define UTILS_NORETURN #endif +#if __has_attribute(fallthrough) +# define UTILS_FALLTHROUGH [[fallthrough]] +#else +# define UTILS_FALLTHROUGH +#endif + #if __has_attribute(visibility) # ifndef TNT_DEV # define UTILS_PRIVATE __attribute__((visibility("hidden"))) diff --git a/samples/material_sandbox.cpp b/samples/material_sandbox.cpp index bcd5e9c3b32..77782464680 100644 --- a/samples/material_sandbox.cpp +++ b/samples/material_sandbox.cpp @@ -333,10 +333,16 @@ static void setup(Engine* engine, View*, Scene* scene) { if (ibl) { auto& params = g_params; IndirectLight* const pIndirectLight = ibl->getIndirectLight(); - params.lightDirection = IndirectLight::getDirectionEstimate(ibl->getSphericalHarmonics()); - float4 c = pIndirectLight->getColorEstimate(ibl->getSphericalHarmonics(), params.lightDirection); - params.lightIntensity = c.w * pIndirectLight->getIntensity(); - params.lightColor = c.rgb; + // If we loaded an equirectangular IBL, we don't have spherical harmonics. In that case, + // simply skip the estimates. + if (ibl->hasSphericalHarmonics()) { + params.lightDirection = + IndirectLight::getDirectionEstimate(ibl->getSphericalHarmonics()); + float4 c = pIndirectLight->getColorEstimate( + ibl->getSphericalHarmonics(), params.lightDirection); + params.lightIntensity = c.w * pIndirectLight->getIntensity(); + params.lightColor = c.rgb; + } } g_params.bloomOptions.dirt = FilamentApp::get().getDirtTexture(); diff --git a/shaders/src/common_getters.glsl b/shaders/src/common_getters.glsl index 4ee94e5df99..d52ef12154f 100644 --- a/shaders/src/common_getters.glsl +++ b/shaders/src/common_getters.glsl @@ -32,6 +32,11 @@ highp mat4 getWorldFromClipMatrix() { return frameUniforms.worldFromClipMatrix; } +/** @public-api */ +highp mat4 getUserWorldFromWorldMatrix() { + return frameUniforms.userWorldFromWorldMatrix; +} + /** @public-api */ float getTime() { return frameUniforms.time; @@ -81,12 +86,12 @@ highp vec4 getResolution() { /** @public-api */ highp vec3 getWorldCameraPosition() { - return frameUniforms.cameraPosition; + return frameUniforms.worldFromViewMatrix[3].xyz; } -/** @public-api */ +/** @public-api, @deprecated use getUserWorldPosition() or getUserWorldFromWorldMatrix() instead */ highp vec3 getWorldOffset() { - return frameUniforms.worldOffset; + return getUserWorldFromWorldMatrix()[3].xyz; } /** @public-api */ diff --git a/shaders/src/fog.fs b/shaders/src/fog.fs index be347b4e321..67061dfe8f4 100644 --- a/shaders/src/fog.fs +++ b/shaders/src/fog.fs @@ -1,62 +1,80 @@ //------------------------------------------------------------------------------ // Fog +// see: Real-time Atmospheric Effects in Games (Carsten Wenzel) //------------------------------------------------------------------------------ -vec4 fog(vec4 color, vec3 view) { - if (frameUniforms.fogDensity > 0.0) { - float A = frameUniforms.fogDensity; - float B = frameUniforms.fogHeightFalloff; +vec4 fog(vec4 color, highp vec3 view) { + // .x = density + // .y = -fallof*(y-height) + // .z = density * exp(-fallof*(y-height)) + highp vec3 density = frameUniforms.fogDensity; + float falloff = frameUniforms.fogHeightFalloff; - float d = length(view); - - float h = max(0.001, view.y); + // Compute the integral of the fog density at a distance of 1m at a given height. + highp float fogDensityIntegralAtOneMeter = density.z; + highp float h = falloff * view.y; + if (abs(h) > 0.01) { // The function below is continuous at h=0, so to avoid a divide-by-zero, we just clamp h - float fogIntegralFunctionOfDistance = A * ((1.0 - exp(-B * h)) / h); - float fogIntegral = fogIntegralFunctionOfDistance * max(d - frameUniforms.fogStart, 0.0); - float fogOpacity = max(1.0 - exp2(-fogIntegral), 0.0); + fogDensityIntegralAtOneMeter = (fogDensityIntegralAtOneMeter - density.x * exp(density.y - h)) / h; + } + + // Compute the integral of the fog density at a given height from fogStart to the fragment + highp float d = length(view); + highp float fogDensityIntegral = + fogDensityIntegralAtOneMeter * max(d - frameUniforms.fogStart, 0.0); + + // Compute the transmittance using the Beer-Lambert Law + float fogTransmittance = exp(-fogDensityIntegral); + + // Compute the opacity from the transmittance + float fogOpacity = min(1.0 - fogTransmittance, frameUniforms.fogMaxOpacity); + + // compute fog color + vec3 fogColor = frameUniforms.fogColor; - // don't go above requested max opacity - fogOpacity = min(fogOpacity, frameUniforms.fogMaxOpacity); + if (frameUniforms.fogColorFromIbl > 0.0) { + // get fog color from envmap + float lod = frameUniforms.iblRoughnessOneLevel; + fogColor *= textureLod(light_iblSpecular, view, lod).rgb * frameUniforms.iblLuminance; + } + + fogColor *= fogOpacity; + + if (frameUniforms.fogInscatteringSize > 0.0) { + // compute a new line-integral for a different start distance + highp float inscatteringDensityIntegral = + fogDensityIntegralAtOneMeter * max(d - frameUniforms.fogInscatteringStart, 0.0); - // compute fog color - vec3 fogColor = frameUniforms.fogColor; + // Compute the transmittance using the Beer-Lambert Law + float inscatteringDensity = exp(-inscatteringDensityIntegral); - if (frameUniforms.fogColorFromIbl > 0.0) { - // get fog color from envmap - float lod = frameUniforms.iblRoughnessOneLevel; - fogColor *= textureLod(light_iblSpecular, view, lod).rgb * frameUniforms.iblLuminance; - } + // Compute the opacity from the transmittance + float inscatteringOpacity = 1.0 - inscatteringDensity; - fogColor *= fogOpacity; - if (frameUniforms.fogInscatteringSize > 0.0) { - // compute a new line-integral for a different start distance - float inscatteringIntegral = fogIntegralFunctionOfDistance * - max(d - frameUniforms.fogInscatteringStart, 0.0); - float inscatteringOpacity = max(1.0 - exp2(-inscatteringIntegral), 0.0); + // Add sun colored fog when looking towards the sun + vec3 sunColor = frameUniforms.lightColorIntensity.rgb * frameUniforms.lightColorIntensity.w; - // Add sun colored fog when looking towards the sun - vec3 sunColor = frameUniforms.lightColorIntensity.rgb * frameUniforms.lightColorIntensity.w; - float sunAmount = max(dot(view, frameUniforms.lightDirection) / d, 0.0); // between 0 and 1 - float sunInscattering = pow(sunAmount, frameUniforms.fogInscatteringSize); + float sunAmount = max(dot(view, frameUniforms.lightDirection) / d, 0.0); // between 0 and 1 + float sunInscattering = pow(sunAmount, frameUniforms.fogInscatteringSize); - fogColor += sunColor * (sunInscattering * inscatteringOpacity); - } + fogColor += sunColor * (sunInscattering * inscatteringOpacity); + } #if defined(BLEND_MODE_OPAQUE) - // nothing to do here + // nothing to do here #elif defined(BLEND_MODE_TRANSPARENT) - fogColor *= color.a; + fogColor *= color.a; #elif defined(BLEND_MODE_ADD) - fogColor = vec3(0.0); + fogColor = vec3(0.0); #elif defined(BLEND_MODE_MASKED) - // nothing to do here + // nothing to do here #elif defined(BLEND_MODE_MULTIPLY) - // FIXME: unclear what to do here + // FIXME: unclear what to do here #elif defined(BLEND_MODE_SCREEN) - // FIXME: unclear what to do here + // FIXME: unclear what to do here #endif - color.rgb = color.rgb * (1.0 - fogOpacity) + fogColor; - } + color.rgb = color.rgb * (1.0 - fogOpacity) + fogColor; + return color; } diff --git a/shaders/src/getters.fs b/shaders/src/getters.fs index e7fc029568c..c7e445d1d53 100644 --- a/shaders/src/getters.fs +++ b/shaders/src/getters.fs @@ -49,6 +49,11 @@ highp vec3 getWorldPosition() { return shading_position; } +/** @public-api */ +highp vec3 getUserWorldPosition() { + return mulMat4x4Float3(getUserWorldFromWorldMatrix(), getWorldPosition()).xyz; +} + /** @public-api */ vec3 getWorldViewVector() { return shading_view; diff --git a/shaders/src/shading_parameters.fs b/shaders/src/shading_parameters.fs index 0ce5ca0caf8..3c7d3aa31bc 100644 --- a/shaders/src/shading_parameters.fs +++ b/shaders/src/shading_parameters.fs @@ -36,10 +36,10 @@ void computeShadingParams() { // With perspective camera, the view vector is cast from the fragment pos to the eye position, // With ortho camera, however, the view vector is the same for all fragments: - shading_view = isPerspectiveProjection() ? - (frameUniforms.cameraPosition - shading_position) : - frameUniforms.worldFromViewMatrix[2].xyz; // ortho camera backward dir - shading_view = normalize(shading_view); + highp vec3 sv = isPerspectiveProjection() ? + (frameUniforms.worldFromViewMatrix[3].xyz - shading_position) : + frameUniforms.worldFromViewMatrix[2].xyz; // ortho camera backward dir + shading_view = normalize(sv); // we do this so we avoid doing (matrix multiply), but we burn 4 varyings: // p = clipFromWorldMatrix * shading_position; diff --git a/third_party/getopt/LICENSE b/third_party/getopt/LICENSE index 0d74ae4f3b2..220832e735e 100644 --- a/third_party/getopt/LICENSE +++ b/third_party/getopt/LICENSE @@ -1,26 +1,20 @@ -Copyright (c) 1987, 1993, 1994, 1995 - The Regents of the University of California. All rights reserved. +Copyright © 2005-2014 Rich Felker, et al. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. Neither the names of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS -IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/getopt/include/getopt/getopt.h b/third_party/getopt/include/getopt/getopt.h old mode 100755 new mode 100644 index f359ab006e9..c1d0df928f7 --- a/third_party/getopt/include/getopt/getopt.h +++ b/third_party/getopt/include/getopt/getopt.h @@ -1,62 +1,31 @@ -/* - * Copyright (c) 1987, 1993, 1994, 1995 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the names of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS - * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __GETOPT_H__ -#define __GETOPT_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -extern int opterr; /* if error message should be printed */ -extern int optind; /* index into parent argv vector */ -extern int optopt; /* character checked for validity */ -extern int optreset; /* reset getopt */ -extern char *optarg; /* argument associated with option */ - -struct option { - const char *name; - int has_arg; - int *flag; - int val; -}; - -#define no_argument 0 -#define required_argument 1 -#define optional_argument 2 - -int getopt(int, char **, const char *); - -int getopt_long(int, char **, const char *, const struct option *, int *); - -#ifdef __cplusplus -} -#endif - -#endif /* __GETOPT_H__ */ \ No newline at end of file +#ifndef _GETOPT_H +#define _GETOPT_H + +#ifdef __cplusplus +extern "C" { +#endif + +int getopt(int, char * const [], const char *); +extern char *optarg; +extern int optind, opterr, optopt, optreset; + +struct option +{ + const char *name; + int has_arg; + int *flag; + int val; +}; + +int getopt_long(int, char *const *, const char *, const struct option *, int *); +int getopt_long_only(int, char *const *, const char *, const struct option *, int *); + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/getopt/src/getopt.c b/third_party/getopt/src/getopt.c old mode 100755 new mode 100644 index 31d7f70aec8..cf2984af0fa --- a/third_party/getopt/src/getopt.c +++ b/third_party/getopt/src/getopt.c @@ -1,139 +1,82 @@ -/* - * Copyright (c) 1987, 1993, 1994, 1995 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the names of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS - * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include - -#ifndef __P -#define __P(x) x -#endif -#define _DIAGASSERT(x) assert(x) - -#ifdef __weak_alias -__weak_alias(getopt, _getopt); -#endif - -int opterr = 1, /* if error message should be printed */ - optind = 1, /* index into parent argv vector */ - optopt, /* character checked for validity */ - optreset; /* reset getopt */ -char *optarg; /* argument associated with option */ - -static char *_progname __P((char *)); - -int getopt_internal __P((int, char *const *, const char *)); - -static char *_progname(nargv0) - - char *nargv0; -{ - char *tmp; - - _DIAGASSERT(nargv0 != NULL); - - tmp = strrchr(nargv0, '/'); - if (tmp) - tmp++; - else - tmp = nargv0; - return (tmp); -} - -#define BADCH (int)'?' -#define BADARG (int)':' -#define EMSG "" - -/* - * getopt -- - * Parse argc/argv argument vector. - */ -int getopt(nargc, nargv, ostr) - - int nargc; -char *const nargv[]; -const char *ostr; -{ - static char *__progname = 0; - static char *place = EMSG; /* option letter processing */ - char *oli; /* option letter list index */ - __progname = __progname ? __progname : _progname(*nargv); - - _DIAGASSERT(nargv != NULL); - _DIAGASSERT(ostr != NULL); - - if (optreset || !*place) { /* update scanning pointer */ - optreset = 0; - if (optind >= nargc || *(place = nargv[optind]) != '-') { - place = EMSG; - return (-1); - } - if (place[1] && *++place == '-' /* found "--" */ - && place[1] == '\0') { - ++optind; - place = EMSG; - return (-1); - } - } /* option letter okay? */ - if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) { - /* - * if the user didn't specify '-' as an option, - * assume it means -1. - */ - if (optopt == (int)'-') - return (-1); - if (!*place) - ++optind; - if (opterr && *ostr != ':') - (void)fprintf(stderr, "%s: illegal option -- %c\n", __progname, optopt); - return (BADCH); - } - if (*++oli != ':') { /* don't need argument */ - optarg = NULL; - if (!*place) - ++optind; - } else { /* need an argument */ - if (*place) /* no white space */ - optarg = place; - else if (nargc <= ++optind) { /* no arg */ - place = EMSG; - if (*ostr == ':') - return (BADARG); - if (opterr) - (void)fprintf(stderr, "%s: option requires an argument -- %c\n", - __progname, optopt); - return (BADCH); - } else /* white space */ - optarg = nargv[optind]; - place = EMSG; - ++optind; - } - return (optopt); /* dump back option letter */ -} +#ifdef _WIN32 +#include +#else +#include +#endif +#include +#include +#include +#include +#include "libc.h" + +char *optarg; +int optind=1, opterr=1, optopt, __optpos, __optreset=0; + +#define optpos __optpos +#ifndef _WIN32 +weak_alias(__optreset, optreset); +#endif + +int getopt(int argc, char * const argv[], const char *optstring) +{ + int i; + wchar_t c, d; + int k, l; + char *optchar; + + if (!optind || __optreset) { + __optreset = 0; + __optpos = 0; + optind = 1; + } + + if (optind >= argc || !argv[optind] || argv[optind][0] != '-' || !argv[optind][1]) + return -1; + if (argv[optind][1] == '-' && !argv[optind][2]) + return optind++, -1; + + if (!optpos) optpos++; + if ((k = mbtowc(&c, argv[optind]+optpos, MB_LEN_MAX)) < 0) { + k = 1; + c = 0xfffd; /* replacement char */ + } + optchar = argv[optind]+optpos; + optopt = c; + optpos += k; + + if (!argv[optind][optpos]) { + optind++; + optpos = 0; + } + + for (i=0; (l = mbtowc(&d, optstring+i, MB_LEN_MAX)) && d!=c; i+=l>0?l:1); + + if (d != c) { + if (optstring[0] != ':' && opterr) { + write(2, argv[0], strlen(argv[0])); + write(2, ": illegal option: ", 18); + write(2, optchar, k); + write(2, "\n", 1); + } + return '?'; + } + if (optstring[i+1] == ':') { + if (optind >= argc) { + if (optstring[0] == ':') return ':'; + if (opterr) { + write(2, argv[0], strlen(argv[0])); + write(2, ": option requires an argument: ", 31); + write(2, optchar, k); + write(2, "\n", 1); + } + return '?'; + } + optarg = argv[optind++] + optpos; + optpos = 0; + } + return c; +} + +#ifndef _WIN32 +weak_alias(getopt, __posix_getopt); +#endif diff --git a/third_party/getopt/src/getopt_long.c b/third_party/getopt/src/getopt_long.c old mode 100755 new mode 100644 index 0f699ee4165..29cdd96bedf --- a/third_party/getopt/src/getopt_long.c +++ b/third_party/getopt/src/getopt_long.c @@ -1,231 +1,59 @@ -/* - * Copyright (c) 1987, 1993, 1994, 1996 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the names of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS - * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "getopt/getopt.h" - -#include -#include -#include -#include -#include - -extern int opterr; /* if error message should be printed */ -extern int optind; /* index into parent argv vector */ -extern int optopt; /* character checked for validity */ -extern int optreset; /* reset getopt */ -extern char *optarg; /* argument associated with option */ - -#ifndef __P -#define __P(x) x -#endif -#define _DIAGASSERT(x) assert(x) - -static char *__progname __P((char *)); - -int getopt_internal __P((int, char *const *, const char *)); - -static char *__progname(nargv0) - - char *nargv0; -{ - char *tmp; - - _DIAGASSERT(nargv0 != NULL); - - tmp = strrchr(nargv0, '/'); - if (tmp) - tmp++; - else - tmp = nargv0; - return (tmp); -} - -#define BADCH (int)'?' -#define BADARG (int)':' -#define EMSG "" - -/* - * getopt -- - * Parse argc/argv argument vector. - */ -int getopt_internal(nargc, nargv, ostr) - - int nargc; -char *const *nargv; -const char *ostr; -{ - static char *place = EMSG; /* option letter processing */ - char *oli; /* option letter list index */ - - _DIAGASSERT(nargv != NULL); - _DIAGASSERT(ostr != NULL); - - if (optreset || !*place) { /* update scanning pointer */ - optreset = 0; - if (optind >= nargc || *(place = nargv[optind]) != '-') { - place = EMSG; - return (-1); - } - if (place[1] && *++place == '-') { /* found "--" */ - /* ++optind; */ - place = EMSG; - return (-2); - } - } /* option letter okay? */ - if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) { - /* - * if the user didn't specify '-' as an option, - * assume it means -1. - */ - if (optopt == (int)'-') - return (-1); - if (!*place) - ++optind; - if (opterr && *ostr != ':') - (void)fprintf(stderr, "%s: illegal option -- %c\n", __progname(nargv[0]), - optopt); - return (BADCH); - } - if (*++oli != ':') { /* don't need argument */ - optarg = NULL; - if (!*place) - ++optind; - } else { /* need an argument */ - if (*place) /* no white space */ - optarg = place; - else if (nargc <= ++optind) { /* no arg */ - place = EMSG; - if ((opterr) && (*ostr != ':')) - (void)fprintf(stderr, "%s: option requires an argument -- %c\n", - __progname(nargv[0]), optopt); - return (BADARG); - } else /* white space */ - optarg = nargv[optind]; - place = EMSG; - ++optind; - } - return (optopt); /* dump back option letter */ -} - -#if 0 -/* - * getopt -- - * Parse argc/argv argument vector. - */ -int -getopt2(nargc, nargv, ostr) - int nargc; - char * const *nargv; - const char *ostr; -{ - int retval; - if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) { - retval = -1; - ++optind; - } - return(retval); -} -#endif - -/* - * getopt_long -- - * Parse argc/argv argument vector. - */ -int getopt_long(nargc, nargv, options, long_options, index) int nargc; -char **nargv; -const char *options; -const struct option *long_options; -int *index; -{ - int retval; - - _DIAGASSERT(nargv != NULL); - _DIAGASSERT(options != NULL); - _DIAGASSERT(long_options != NULL); - /* index may be NULL */ - - if ((retval = getopt_internal(nargc, nargv, options)) == -2) { - char *current_argv = nargv[optind++] + 2, *has_equal; - int i, current_argv_len, match = -1; - - if (*current_argv == '\0') { - return (-1); - } - if ((has_equal = strchr(current_argv, '=')) != NULL) { - current_argv_len = has_equal - current_argv; - has_equal++; - } else - current_argv_len = strlen(current_argv); - - for (i = 0; long_options[i].name; i++) { - if (strncmp(current_argv, long_options[i].name, current_argv_len)) - continue; - - if (strlen(long_options[i].name) == (unsigned)current_argv_len) { - match = i; - break; - } - if (match == -1) - match = i; - } - if (match != -1) { - if (long_options[match].has_arg == required_argument || - long_options[match].has_arg == optional_argument) { - if (has_equal) - optarg = has_equal; - else - optarg = nargv[optind++]; - } - if ((long_options[match].has_arg == required_argument) && - (optarg == NULL)) { - /* - * Missing argument, leading : - * indicates no error should be generated - */ - if ((opterr) && (*options != ':')) - (void)fprintf(stderr, "%s: option requires an argument -- %s\n", - __progname(nargv[0]), current_argv); - return (BADARG); - } - } else { /* No matching argument */ - if ((opterr) && (*options != ':')) - (void)fprintf(stderr, "%s: illegal option -- %s\n", - __progname(nargv[0]), current_argv); - return (BADCH); - } - if (long_options[match].flag) { - *long_options[match].flag = long_options[match].val; - retval = 0; - } else - retval = long_options[match].val; - if (index) - *index = match; - } - return (retval); -} \ No newline at end of file +#define _GNU_SOURCE +#include +#include +#include + +extern int __optpos, __optreset; + +static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly) +{ + if (!optind || __optreset) { + __optreset = 0; + __optpos = 0; + optind = 1; + } + if (optind >= argc || !argv[optind] || argv[optind][0] != '-') return -1; + if ((longonly && argv[optind][1]) || + (argv[optind][1] == '-' && argv[optind][2])) + { + int i; + for (i=0; longopts[i].name; i++) { + const char *name = longopts[i].name; + char *opt = argv[optind]+1; + if (*opt == '-') opt++; + for (; *name && *name == *opt; name++, opt++); + if (*name || (*opt && *opt != '=')) continue; + if (*opt == '=') { + if (!longopts[i].has_arg) continue; + optarg = opt+1; + } else { + if (longopts[i].has_arg == required_argument) { + if (!(optarg = argv[++optind])) + return ':'; + } else optarg = NULL; + } + optind++; + if (idx) *idx = i; + if (longopts[i].flag) { + *longopts[i].flag = longopts[i].val; + return 0; + } + return longopts[i].val; + } + if (argv[optind][1] == '-') { + optind++; + return '?'; + } + } + return getopt(argc, argv, optstring); +} + +int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx) +{ + return __getopt_long(argc, argv, optstring, longopts, idx, 0); +} + +int getopt_long_only(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx) +{ + return __getopt_long(argc, argv, optstring, longopts, idx, 1); +} diff --git a/third_party/getopt/src/libc.h b/third_party/getopt/src/libc.h new file mode 100644 index 00000000000..af1a5307f97 --- /dev/null +++ b/third_party/getopt/src/libc.h @@ -0,0 +1,74 @@ +#ifndef LIBC_H +#define LIBC_H + +#include +#include +#include + +struct __libc { + int has_thread_pointer; + int can_do_threads; + int threaded; + int secure; + size_t *auxv; + volatile int threads_minus_1; + FILE *ofl_head; + int ofl_lock[2]; + size_t tls_size; + size_t page_size; +}; + +extern size_t __hwcap; + +#ifndef PAGE_SIZE +#define PAGE_SIZE libc.page_size +#endif + +#if !defined(__PIC__) || (100*__GNUC__+__GNUC_MINOR__ >= 303 && !defined(__PCC__)) + +#ifdef __PIC__ +#if __GNUC__ < 4 +#define BROKEN_VISIBILITY 1 +#endif +#define ATTR_LIBC_VISIBILITY __attribute__((visibility("hidden"))) +#else +#define ATTR_LIBC_VISIBILITY +#endif + +extern struct __libc __libc ATTR_LIBC_VISIBILITY; +#define libc __libc + +#else + +#define USE_LIBC_ACCESSOR +#define ATTR_LIBC_VISIBILITY +extern struct __libc *__libc_loc(void) __attribute__((const)); +#define libc (*__libc_loc()) + +#endif + + +/* Designed to avoid any overhead in non-threaded processes */ +void __lock(volatile int *) ATTR_LIBC_VISIBILITY; +void __unlock(volatile int *) ATTR_LIBC_VISIBILITY; +int __lockfile(FILE *) ATTR_LIBC_VISIBILITY; +void __unlockfile(FILE *) ATTR_LIBC_VISIBILITY; +#define LOCK(x) __lock(x) +#define UNLOCK(x) __unlock(x) + +void __synccall(void (*)(void *), void *); +int __setxid(int, int, int, int); + +extern char **__environ; + +#undef weak_alias +#define weak_alias(old, new) \ + extern __typeof(old) new __attribute__((weak)) + +#undef LFS64_2 +#define LFS64_2(x, y) weak_alias(x, y) + +#undef LFS64 +#define LFS64(x) LFS64_2(x, x##64) + +#endif diff --git a/tnt/README.md b/tnt/README.md new file mode 100644 index 00000000000..2a89178a7c6 --- /dev/null +++ b/tnt/README.md @@ -0,0 +1,4 @@ +Implementation from musl-1.1.0: +https://musl.libc.org/ +https://musl.libc.org/releases.html + diff --git a/tools/matc/src/matc/ParametersProcessor.cpp b/tools/matc/src/matc/ParametersProcessor.cpp index f340b0ec298..f1745e4b2c1 100644 --- a/tools/matc/src/matc/ParametersProcessor.cpp +++ b/tools/matc/src/matc/ParametersProcessor.cpp @@ -850,6 +850,11 @@ static bool processMaskThreshold(MaterialBuilder& builder, const JsonishValue& v return true; } +static bool processAlphaToCoverage(MaterialBuilder& builder, const JsonishValue& value) { + builder.alphaToCoverage(value.toJsonBool()->getBool()); + return true; +} + static bool processShadowMultiplier(MaterialBuilder& builder, const JsonishValue& value) { builder.shadowMultiplier(value.toJsonBool()->getBool()); return true; @@ -1070,6 +1075,7 @@ ParametersProcessor::ParametersProcessor() { mParameters["transparency"] = { &processTransparencyMode, Type::STRING }; mParameters["reflections"] = { &processReflectionMode, Type::STRING }; mParameters["maskThreshold"] = { &processMaskThreshold, Type::NUMBER }; + mParameters["alphaToCoverage"] = { &processAlphaToCoverage, Type::BOOL }; mParameters["shadowMultiplier"] = { &processShadowMultiplier, Type::BOOL }; mParameters["transparentShadow"] = { &processTransparentShadow, Type::BOOL }; mParameters["shadingModel"] = { &processShading, Type::STRING }; diff --git a/web/docs/build.py b/web/docs/build.py index 79ff79a36f3..7dae56af06a 100755 --- a/web/docs/build.py +++ b/web/docs/build.py @@ -523,12 +523,19 @@ def build_api_reference(): copy_built_file('web/samples/suzanne.html') copy_built_file('web/samples/suzanne.filamesh') + copy_built_file('web/samples/metallic*.ktx') copy_built_file('web/samples/normal*.ktx') copy_built_file('web/samples/roughness*.ktx') copy_built_file('web/samples/ao*.ktx') copy_built_file('web/samples/albedo*.ktx') + copy_built_file('web/samples/metallic*.ktx2') + copy_built_file('web/samples/normal*.ktx2') + copy_built_file('web/samples/roughness*.ktx2') + copy_built_file('web/samples/ao*.ktx2') + copy_built_file('web/samples/albedo*.ktx2') + copy_built_file('web/samples/default_env/default_env*.ktx', 'default_env') # TODO: We do not normally build the following env maps so we should update the tutorials. diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 095887d6757..e75eeb1643d 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.31.7", + "version": "1.32.0", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js",