Codrops 원본
Building a Fully-Featured 3D World in the Browser with Blender and Three.js
섹션 · MIT
갤러리 안에서는 화면을 벗어나거나 움직임을 멈추면 예제도 닫힙니다. 다시 재생할 때는 처음부터 시작해요.
SOURCE FILES
원본 코드 읽기
수집한 원본 소스와 실행 안내를 함께 제공합니다.
eslint.config.js
import js from '@eslint/js'
import globals from 'globals'
import react from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
export default [
{ ignores: ['dist'] },
{
files: ['**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
settings: { react: { version: '18.3' } },
plugins: {
react,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...js.configs.recommended.rules,
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
'react/jsx-no-target-blank': 'off',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
]
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<link
rel="icon"
type="image/png"
href="/media/favicon-96x96.png"
sizes="96x96"
/>
<link rel="icon" type="image/svg+xml" href="/media/favicon.svg" />
<link rel="shortcut icon" href="/media/favicon.ico" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/media/apple-touch-icon.png"
/>
<link rel="manifest" href="/media/site.webmanifest" />
<meta name="language" content="English" />
<meta property="og:locale" content="en_US" />
<meta property="og:type" content="website" />
<meta property="og:image" content="/media/og-image.webp" />
<meta
property="og:description"
content="Fan-made Immersive 3D museum experience for Codrops."
/>
<meta
name="description"
content="Fan-made Immersive 3D museum experience for Codrops."
/>
<title>Codrops Fan Museum</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
함께 쓰는 파일 42개 보기
public/basis/basis_transcoder.js
var BASIS = (() => {
var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined;
if (typeof __filename != 'undefined') _scriptName ||= __filename;
return (
function(moduleArg = {}) {
var moduleRtn;
var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){}var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("fs");var nodePath=require("path");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);var ret=fs.readFileSync(filename);return ret};readAsync=(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return new Promise((resolve,reject)=>{fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)reject(err);else resolve(binary?data.buffer:data)})})};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=url=>{if(isFileURI(url)){return new Promise((reject,resolve)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response)}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}return fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))})}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);var isFileURI=filename=>filename.startsWith("file://");function findWasmBinary(){var f="basis_transcoder.wasm";if(!isDataURI(f)){return locateFile(f)}return f}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["L"];updateMemoryViews();wasmTable=wasmExports["P"];addOnInit(wasmExports["M"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}if(!wasmBinaryFile)wasmBinaryFile=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var noExitRuntime=Module["noExitRuntime"]||true;class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}get_exception_ptr(){var isPointer=___cxa_is_pointer_type(this.get_type());if(isPointer){return HEAPU32[this.excPtr>>2]}var adjusted=this.get_adjusted_ptr();if(adjusted!==0)return adjusted;return this.excPtr}}var exceptionLast=0;var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var __abort_js=()=>{abort("")};var structRegistrations={};var runDestructors=destructors=>{while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}};function readPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var InternalError;var throwInternalError=message=>{throw new InternalError(message)};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i<myTypes.length;++i){registerType(myTypes[i],myTypeConverters[i])}}var typeConverters=new Array(dependentTypes.length);var unregisteredTypes=[];var registered=0;dependentTypes.forEach((dt,i)=>{if(registeredTypes.hasOwnProperty(dt)){typeConverters[i]=registeredTypes[dt]}else{unregisteredTypes.push(dt);if(!awaitingDependencies.hasOwnProperty(dt)){awaitingDependencies[dt]=[]}awaitingDependencies[dt].push(()=>{typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}});if(0===unregisteredTypes.length){onComplete(typeConverters)}};var __embind_finalize_value_object=structType=>{var reg=structRegistrations[structType];delete structRegistrations[structType];var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;var fieldRecords=reg.fields;var fieldTypes=fieldRecords.map(field=>field.getterReturnType).concat(fieldRecords.map(field=>field.setterArgumentType));whenDependentTypesAreResolved([structType],fieldTypes,fieldTypes=>{var fields={};fieldRecords.forEach((field,i)=>{var fieldName=field.fieldName;var getterReturnType=fieldTypes[i];var getter=field.getter;var getterContext=field.getterContext;var setterArgumentType=fieldTypes[i+fieldRecords.length];var setter=field.setter;var setterContext=field.setterContext;fields[fieldName]={read:ptr=>getterReturnType["fromWireType"](getter(getterContext,ptr)),write:(ptr,o)=>{var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,o));runDestructors(destructors)}}});return[{name:reg.name,fromWireType:ptr=>{var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},toWireType:(destructors,o)=>{for(var fieldName in fields){if(!(fieldName in o)){throw new TypeError(`Missing field: "${fieldName}"`)}}var ptr=rawConstructor();for(fieldName in fields){fields[fieldName].write(ptr,o[fieldName])}if(destructors!==null){destructors.push(rawDestructor,ptr)}return ptr},argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction:rawDestructor}]})};var __embind_register_bigint=(primitiveType,name,size,minRange,maxRange)=>{};var embind_init_charCodes=()=>{var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes};var embind_charCodes;var readLatin1String=ptr=>{var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret};var BindingError;var throwBindingError=message=>{throw new BindingError(message)};function sharedRegisterType(rawType,registeredInstance,options={}){var name=registeredInstance.name;if(!rawType){throwBindingError(`type "${name}" must have a positive integer typeid pointer`)}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError(`Cannot register type '${name}' twice`)}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(cb=>cb())}}function registerType(rawType,registeredInstance,options={}){if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}return sharedRegisterType(rawType,registeredInstance,options)}var GenericWireTypeSize=8;var __embind_register_bool=(rawType,name,trueValue,falseValue)=>{name=readLatin1String(name);registerType(rawType,{name:name,fromWireType:function(wt){return!!wt},toWireType:function(destructors,o){return o?trueValue:falseValue},argPackAdvance:GenericWireTypeSize,readValueFromPointer:function(pointer){return this["fromWireType"](HEAPU8[pointer])},destructorFunction:null})};var shallowCopyInternalPointer=o=>({count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType});var throwInstanceAlreadyDeleted=obj=>{function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")};var finalizationRegistry=false;var detachFinalizer=handle=>{};var runDestructor=$$=>{if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}};var releaseClassHandle=$$=>{$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}};var downcastPointer=(ptr,ptrClass,desiredClass)=>{if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)};var registeredPointers={};var getInheritedInstanceCount=()=>Object.keys(registeredInstances).length;var getLiveInheritedInstances=()=>{var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv};var deletionQueue=[];var flushPendingDeletes=()=>{while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}};var delayFunction;var setDelayFunction=fn=>{delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}};var init_embind=()=>{Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction};var registeredInstances={};var getBasestPointer=(class_,ptr)=>{if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr};var getInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]};var makeClassHandle=(prototype,record)=>{if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record,writable:true}}))};function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}var attachFinalizer=handle=>{if("undefined"===typeof FinalizationRegistry){attachFinalizer=handle=>handle;return handle}finalizationRegistry=new FinalizationRegistry(info=>{releaseClassHandle(info.$$)});attachFinalizer=handle=>{var $$=handle.$$;var hasSmartPtr=!!$$.smartPtr;if(hasSmartPtr){var info={$$:$$};finalizationRegistry.register(handle,info,handle)}return handle};detachFinalizer=handle=>finalizationRegistry.unregister(handle);return attachFinalizer(handle)};var init_ClassHandle=()=>{Object.assign(ClassHandle.prototype,{isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;other.$$=other.$$;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right},clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}},delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}},isDeleted(){return!this.$$.ptr},deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}})};function ClassHandle(){}var createNamedFunction=(name,body)=>Object.defineProperty(body,"name",{value:name});var ensureOverloadTable=(proto,methodName,humanName)=>{if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(...args){if(!proto[methodName].overloadTable.hasOwnProperty(args.length)){throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`)}return proto[methodName].overloadTable[args.length].apply(this,args)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}};var exposePublicSymbol=(name,value,numArguments)=>{if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError(`Cannot register public name '${name}' twice`)}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`)}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}};var char_0=48;var char_9=57;var makeLegalFunctionName=name=>{if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return`_${name}`}return name};function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}var upcastPointer=(ptr,ptrClass,desiredClass)=>{while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError(`Expected null or instance of ${desiredClass.name}, got an instance of ${ptrClass.name}`)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr};function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle||!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,Emval.toHandle(()=>clonedHandle["delete"]()));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}var init_RegisteredPointer=()=>{Object.assign(RegisteredPointer.prototype,{getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr},destructor(ptr){this.rawDestructor?.(ptr)},argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,fromWireType:RegisteredPointer_fromWireType})};function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}var replacePublicSymbol=(name,value,numArguments)=>{if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistent public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var dynCallLegacy=(sig,ptr,args)=>{sig=sig.replace(/p/g,"i");var f=Module["dynCall_"+sig];return f(ptr,...args)};var wasmTableMirror=[];var wasmTable;var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var dynCall=(sig,ptr,args=[])=>{if(sig.includes("j")){return dynCallLegacy(sig,ptr,args)}var rtn=getWasmTableEntry(ptr)(...args);return rtn};var getDynCaller=(sig,ptr)=>(...args)=>dynCall(sig,ptr,args);var embind__requireFunction=(signature,rawFunction)=>{signature=readLatin1String(signature);function makeDynCaller(){if(signature.includes("j")){return getDynCaller(signature,rawFunction)}return getWasmTableEntry(rawFunction)}var fp=makeDynCaller();if(typeof fp!="function"){throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`)}return fp};var extendError=(baseErrorType,errorName)=>{var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return`${this.name}: ${this.message}`}};return errorClass};var UnboundTypeError;var getTypeName=type=>{var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv};var throwUnboundTypeError=(message,types)=>{var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(`${message}: `+unboundTypes.map(getTypeName).join([", "]))};var __embind_register_class=(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor)=>{name=readLatin1String(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);upcast&&=embind__requireFunction(upcastSignature,upcast);downcast&&=embind__requireFunction(downcastSignature,downcast);rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError(`Cannot construct ${name} due to unbound types`,[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],base=>{base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(name,function(...args){if(Object.getPrototypeOf(this)!==instancePrototype){throw new BindingError("Use 'new' to construct "+name)}if(undefined===registeredClass.constructor_body){throw new BindingError(name+" has no accessible constructor")}var body=registeredClass.constructor_body[args.length];if(undefined===body){throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`)}return body.apply(this,args)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);if(registeredClass.baseClass){registeredClass.baseClass.__derivedClasses??=[];registeredClass.baseClass.__derivedClasses.push(registeredClass)}var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})};var heap32VectorToArray=(count,firstElement)=>{var array=[];for(var i=0;i<count;i++){array.push(HEAPU32[firstElement+i*4>>2])}return array};function usesDestructorStack(argTypes){for(var i=1;i<argTypes.length;++i){if(argTypes[i]!==null&&argTypes[i].destructorFunction===undefined){return true}}return false}function newFunc(constructor,argumentList){if(!(constructor instanceof Function)){throw new TypeError(`new_ called with constructor type ${typeof constructor} which is not a function`)}var dummy=createNamedFunction(constructor.name||"unknownFunctionName",function(){});dummy.prototype=constructor.prototype;var obj=new dummy;var r=constructor.apply(obj,argumentList);return r instanceof Object?r:obj}function createJsInvoker(argTypes,isClassMethodFunc,returns,isAsync){var needsDestructorStack=usesDestructorStack(argTypes);var argCount=argTypes.length;var argsList="";var argsListWired="";for(var i=0;i<argCount-2;++i){argsList+=(i!==0?", ":"")+"arg"+i;argsListWired+=(i!==0?", ":"")+"arg"+i+"Wired"}var invokerFnBody=`\n return function (${argsList}) {\n if (arguments.length !== ${argCount-2}) {\n throwBindingError('function ' + humanName + ' called with ' + arguments.length + ' arguments, expected ${argCount-2}');\n }`;if(needsDestructorStack){invokerFnBody+="var destructors = [];\n"}var dtorStack=needsDestructorStack?"destructors":"null";var args1=["humanName","throwBindingError","invoker","fn","runDestructors","retType","classParam"];if(isClassMethodFunc){invokerFnBody+="var thisWired = classParam['toWireType']("+dtorStack+", this);\n"}for(var i=0;i<argCount-2;++i){invokerFnBody+="var arg"+i+"Wired = argType"+i+"['toWireType']("+dtorStack+", arg"+i+");\n";args1.push("argType"+i)}if(isClassMethodFunc){argsListWired="thisWired"+(argsListWired.length>0?", ":"")+argsListWired}invokerFnBody+=(returns||isAsync?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i<argTypes.length;++i){var paramName=i===1?"thisWired":"arg"+(i-2)+"Wired";if(argTypes[i].destructorFunction!==null){invokerFnBody+=`${paramName}_dtor(${paramName});\n`;args1.push(`${paramName}_dtor`)}}}if(returns){invokerFnBody+="var ret = retType['fromWireType'](rv);\n"+"return ret;\n"}else{}invokerFnBody+="}\n";return[args1,invokerFnBody]}function craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc,isAsync){var argCount=argTypes.length;if(argCount<2){throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!")}var isClassMethodFunc=argTypes[1]!==null&&classType!==null;var needsDestructorStack=usesDestructorStack(argTypes);var returns=argTypes[0].name!=="void";var closureArgs=[humanName,throwBindingError,cppInvokerFunc,cppTargetFunc,runDestructors,argTypes[0],argTypes[1]];for(var i=0;i<argCount-2;++i){closureArgs.push(argTypes[i+2])}if(!needsDestructorStack){for(var i=isClassMethodFunc?1:2;i<argTypes.length;++i){if(argTypes[i].destructorFunction!==null){closureArgs.push(argTypes[i].destructorFunction)}}}let[args,invokerFnBody]=createJsInvoker(argTypes,isClassMethodFunc,returns,isAsync);args.push(invokerFnBody);var invokerFn=newFunc(Function,args)(...closureArgs);return createNamedFunction(humanName,invokerFn)}var __embind_register_class_constructor=(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`constructor ${classType.name}`;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError(`Cannot register multiple constructors with identical number of parameters (${argCount-1}) for class '${classType.name}'! Overload resolution is currently only performed using the parameter count, not actual type info!`)}classType.registeredClass.constructor_body[argCount-1]=()=>{throwUnboundTypeError(`Cannot construct ${classType.name} due to unbound types`,rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{argTypes.splice(1,0,null);classType.registeredClass.constructor_body[argCount-1]=craftInvokerFunction(humanName,argTypes,null,invoker,rawConstructor);return[]});return[]})};var getFunctionName=signature=>{signature=signature.trim();const argsIndex=signature.indexOf("(");if(argsIndex!==-1){return signature.substr(0,argsIndex)}else{return signature}};var __embind_register_class_function=(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual,isAsync)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`${classType.name}.${methodName}`;if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}if(isPureVirtual){classType.registeredClass.pureVirtualFunctions.push(methodName)}function unboundTypesHandler(){throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`,rawArgTypes)}var proto=classType.registeredClass.instancePrototype;var method=proto[methodName];if(undefined===method||undefined===method.overloadTable&&method.className!==classType.name&&method.argCount===argCount-2){unboundTypesHandler.argCount=argCount-2;unboundTypesHandler.className=classType.name;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-2]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context,isAsync);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})};var __embind_register_constant=(name,type,value)=>{name=readLatin1String(name);whenDependentTypesAreResolved([],[type],type=>{type=type[0];Module[name]=type["fromWireType"](value);return[]})};var emval_freelist=[];var emval_handles=[];var __emval_decref=handle=>{if(handle>9&&0===--emval_handles[handle+1]){emval_handles[handle]=undefined;emval_freelist.push(handle)}};var count_emval_handles=()=>emval_handles.length/2-5-emval_freelist.length;var init_emval=()=>{emval_handles.push(0,1,undefined,1,null,1,true,1,false,1);Module["count_emval_handles"]=count_emval_handles};var Emval={toValue:handle=>{if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handles[handle]},toHandle:value=>{switch(value){case undefined:return 2;case null:return 4;case true:return 6;case false:return 8;default:{const handle=emval_freelist.pop()||emval_handles.length;emval_handles[handle]=value;emval_handles[handle+1]=1;return handle}}}};var EmValType={name:"emscripten::val",fromWireType:handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},toWireType:(destructors,value)=>Emval.toHandle(value),argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction:null};var __embind_register_emval=rawType=>registerType(rawType,EmValType);var enumReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?function(pointer){return this["fromWireType"](HEAP8[pointer])}:function(pointer){return this["fromWireType"](HEAPU8[pointer])};case 2:return signed?function(pointer){return this["fromWireType"](HEAP16[pointer>>1])}:function(pointer){return this["fromWireType"](HEAPU16[pointer>>1])};case 4:return signed?function(pointer){return this["fromWireType"](HEAP32[pointer>>2])}:function(pointer){return this["fromWireType"](HEAPU32[pointer>>2])};default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_enum=(rawType,name,size,isSigned)=>{name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,fromWireType:function(c){return this.constructor.values[c]},toWireType:(destructors,c)=>c.value,argPackAdvance:GenericWireTypeSize,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)};var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`)}return impl};var __embind_register_enum_value=(rawEnumType,name,enumValue)=>{var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(`${enumType.name}_${name}`,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value};var embindRepr=v=>{if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}};var floatReadValueFromPointer=(name,width)=>{switch(width){case 4:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 8:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError(`invalid float width (${width}): ${name}`)}};var __embind_register_float=(rawType,name,size)=>{name=readLatin1String(name);registerType(rawType,{name:name,fromWireType:value=>value,toWireType:(destructors,value)=>value,argPackAdvance:GenericWireTypeSize,readValueFromPointer:floatReadValueFromPointer(name,size),destructorFunction:null})};var __embind_register_function=(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn,isAsync)=>{var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);name=getFunctionName(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError(`Cannot call ${name} due to unbound types`,argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn,isAsync),argCount-1);return[]})};var integerReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?pointer=>HEAP8[pointer]:pointer=>HEAPU8[pointer];case 2:return signed?pointer=>HEAP16[pointer>>1]:pointer=>HEAPU16[pointer>>1];case 4:return signed?pointer=>HEAP32[pointer>>2]:pointer=>HEAPU32[pointer>>2];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_integer=(primitiveType,name,size,minRange,maxRange)=>{name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var fromWireType=value=>value;if(minRange===0){var bitshift=32-8*size;fromWireType=value=>value<<bitshift>>>bitshift}var isUnsignedType=name.includes("unsigned");var checkAssertions=(value,toTypeName)=>{};var toWireType;if(isUnsignedType){toWireType=function(destructors,value){checkAssertions(value,this.name);return value>>>0}}else{toWireType=function(destructors,value){checkAssertions(value,this.name);return value}}registerType(primitiveType,{name:name,fromWireType:fromWireType,toWireType:toWireType,argPackAdvance:GenericWireTypeSize,readValueFromPointer:integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})};var __embind_register_memory_view=(rawType,dataTypeIndex,name)=>{var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){var size=HEAPU32[handle>>2];var data=HEAPU32[handle+4>>2];return new TA(HEAP8.buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,fromWireType:decodeMemoryView,argPackAdvance:GenericWireTypeSize,readValueFromPointer:decodeMemoryView},{ignoreDuplicateRegistrations:true})};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx,maxBytesToRead)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var __embind_register_std_string=(rawType,name)=>{name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,fromWireType(value){var length=HEAPU32[value>>2];var payload=value+4;var str;if(stdStringIsUTF8){var decodeStartPtr=payload;for(var i=0;i<=length;++i){var currentBytePtr=payload+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAPU8[payload+i])}str=a.join("")}_free(value);return str},toWireType(destructors,value){if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var length;var valueIsOfTypeString=typeof value=="string";if(!(valueIsOfTypeString||value instanceof Uint8Array||value instanceof Uint8ClampedArray||value instanceof Int8Array)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){length=lengthBytesUTF8(value)}else{length=value.length}var base=_malloc(4+length+1);var ptr=base+4;HEAPU32[base>>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr,length+1)}else{if(valueIsOfTypeString){for(var i=0;i<length;++i){var charCode=value.charCodeAt(i);if(charCode>255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+i]=charCode}}else{for(var i=0;i<length;++i){HEAPU8[ptr+i]=value[i]}}}if(destructors!==null){destructors.push(_free,base)}return base},argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var UTF16Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead)=>{var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder)return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr));var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str};var stringToUTF16=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite<str.length*2?maxBytesToWrite/2:str.length;for(var i=0;i<numCharsToWrite;++i){var codeUnit=str.charCodeAt(i);HEAP16[outPtr>>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead)=>{var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343)++i;len+=4}return len};var __embind_register_std_wstring=(rawType,charSize,name)=>{name=readLatin1String(name);var decodeString,encodeString,readCharAt,lengthBytesUTF;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16;readCharAt=pointer=>HEAPU16[pointer>>1]}else if(charSize===4){decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32;readCharAt=pointer=>HEAPU32[pointer>>2]}registerType(rawType,{name:name,fromWireType:value=>{var length=HEAPU32[value>>2];var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||readCharAt(currentBytePtr)==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},toWireType:(destructors,value)=>{if(!(typeof value=="string")){throwBindingError(`Cannot pass non-string to C++ string type ${name}`)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length/charSize;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var __embind_register_value_object=(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor)=>{structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}};var __embind_register_value_object_field=(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext)=>{structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})};var __embind_register_void=(rawType,name)=>{name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,argPackAdvance:0,fromWireType:()=>undefined,toWireType:(destructors,o)=>undefined})};var __emscripten_memcpy_js=(dest,src,num)=>HEAPU8.copyWithin(dest,src,src+num);var emval_returnValue=(returnType,destructorsRef,handle)=>{var destructors=[];var result=returnType["toWireType"](destructors,handle);if(destructors.length){HEAPU32[destructorsRef>>2]=Emval.toHandle(destructors)}return result};var __emval_as=(handle,returnType,destructorsRef)=>{handle=Emval.toValue(handle);returnType=requireRegisteredType(returnType,"emval::as");return emval_returnValue(returnType,destructorsRef,handle)};var emval_methodCallers=[];var __emval_call=(caller,handle,destructorsRef,args)=>{caller=emval_methodCallers[caller];handle=Emval.toValue(handle);return caller(null,handle,destructorsRef,args)};var emval_symbols={};var getStringOrSymbol=address=>{var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}return symbol};var __emval_call_method=(caller,objHandle,methodName,destructorsRef,args)=>{caller=emval_methodCallers[caller];objHandle=Emval.toValue(objHandle);methodName=getStringOrSymbol(methodName);return caller(objHandle,objHandle[methodName],destructorsRef,args)};var emval_get_global=()=>{if(typeof globalThis=="object"){return globalThis}return function(){return Function}()("return this")()};var __emval_get_global=name=>{if(name===0){return Emval.toHandle(emval_get_global())}else{name=getStringOrSymbol(name);return Emval.toHandle(emval_get_global()[name])}};var emval_addMethodCaller=caller=>{var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id};var emval_lookupTypes=(argCount,argTypes)=>{var a=new Array(argCount);for(var i=0;i<argCount;++i){a[i]=requireRegisteredType(HEAPU32[argTypes+i*4>>2],"parameter "+i)}return a};var reflectConstruct=Reflect.construct;var __emval_get_method_caller=(argCount,argTypes,kind)=>{var types=emval_lookupTypes(argCount,argTypes);var retType=types.shift();argCount--;var functionBody=`return function (obj, func, destructorsRef, args) {\n`;var offset=0;var argsList=[];if(kind===0){argsList.push("obj")}var params=["retType"];var args=[retType];for(var i=0;i<argCount;++i){argsList.push("arg"+i);params.push("argType"+i);args.push(types[i]);functionBody+=` var arg${i} = argType${i}.readValueFromPointer(args${offset?"+"+offset:""});\n`;offset+=types[i]["argPackAdvance"]}var invoker=kind===1?"new func":"func.call";functionBody+=` var rv = ${invoker}(${argsList.join(", ")});\n`;if(!retType.isVoid){params.push("emval_returnValue");args.push(emval_returnValue);functionBody+=" return emval_returnValue(retType, destructorsRef, rv);\n"}functionBody+="};\n";params.push(functionBody);var invokerFunction=newFunc(Function,params)(...args);var functionName=`methodCaller<(${types.map(t=>t.name).join(", ")}) => ${retType.name}>`;return emval_addMethodCaller(createNamedFunction(functionName,invokerFunction))};var __emval_get_module_property=name=>{name=getStringOrSymbol(name);return Emval.toHandle(Module[name])};var __emval_get_property=(handle,key)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);return Emval.toHandle(handle[key])};var __emval_incref=handle=>{if(handle>9){emval_handles[handle+1]+=1}};var __emval_new_cstring=v=>Emval.toHandle(getStringOrSymbol(v));var __emval_run_destructors=handle=>{var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)};var getHeapMax=()=>2147483648;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var _fd_close=fd=>52;var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;function _fd_seek(fd,offset_low,offset_high,whence,newOffset){var offset=convertI32PairToI53Checked(offset_low,offset_high);return 70}var printCharBuffers=[null,[],[]];var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j<len;j++){printChar(fd,HEAPU8[ptr+j])}num+=len}HEAPU32[pnum>>2]=num;return 0};InternalError=Module["InternalError"]=class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}};embind_init_charCodes();BindingError=Module["BindingError"]=class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}};init_ClassHandle();init_embind();init_RegisteredPointer();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();var wasmImports={K:___cxa_throw,G:__abort_js,s:__embind_finalize_value_object,C:__embind_register_bigint,I:__embind_register_bool,w:__embind_register_class,v:__embind_register_class_constructor,d:__embind_register_class_function,m:__embind_register_constant,H:__embind_register_emval,o:__embind_register_enum,a:__embind_register_enum_value,A:__embind_register_float,i:__embind_register_function,l:__embind_register_integer,f:__embind_register_memory_view,z:__embind_register_std_string,u:__embind_register_std_wstring,t:__embind_register_value_object,c:__embind_register_value_object_field,J:__embind_register_void,F:__emscripten_memcpy_js,n:__emval_as,q:__emval_call,p:__emval_call_method,b:__emval_decref,x:__emval_get_global,j:__emval_get_method_caller,r:__emval_get_module_property,g:__emval_get_property,k:__emval_incref,h:__emval_new_cstring,e:__emval_run_destructors,D:_emscripten_resize_heap,E:_fd_close,B:_fd_seek,y:_fd_write};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["M"])();var ___getTypeName=a0=>(___getTypeName=wasmExports["N"])(a0);var _malloc=a0=>(_malloc=wasmExports["O"])(a0);var _free=a0=>(_free=wasmExports["Q"])(a0);var ___cxa_is_pointer_type=a0=>(___cxa_is_pointer_type=wasmExports["R"])(a0);var dynCall_jiji=Module["dynCall_jiji"]=(a0,a1,a2,a3,a4)=>(dynCall_jiji=Module["dynCall_jiji"]=wasmExports["S"])(a0,a1,a2,a3,a4);var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise;
return moduleRtn;
}
);
})();
if (typeof exports === 'object' && typeof module === 'object')
module.exports = BASIS;
else if (typeof define === 'function' && define['amd'])
define([], () => BASIS);
src/App.jsx
import "./App.scss";
import Experience from "./Experience/Experience";
import Modal from "./components/Modal/Modal";
import LoadingScreen from "./components/LoadingScreen/LoadingScreen";
function App() {
return (
<>
<LoadingScreen />
<Modal />
<Experience />
</>
);
}
export default App;
src/App.scss
#root,
html,
body {
margin: 0;
padding: 0;
box-sizing: border-box;
height: 100%;
width: 100%;
overflow: hidden;
touch-action: none;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-family: "Eagle Lake", sans-serif;
}
button {
font-family: inherit;
}
src/Experience/Experience.jsx
import React, { useRef, useEffect, useState } from "react";
import Scene from "./Scene";
import { Canvas } from "@react-three/fiber";
import * as THREE from "three";
import { PerspectiveCamera, OrbitControls } from "@react-three/drei";
import { useModalStore } from "../stores/useModalStore";
import normalizeWheel from "normalize-wheel";
import { useExperienceStore } from "../stores/experienceStore";
import { PerformanceMonitor } from "@react-three/drei";
const Experience = () => {
const camera = useRef();
const cameraGroup = useRef();
const [scrollProgress, setscrollProgress] = useState(0);
const targetScrollProgress = useRef(0);
const scrollSpeed = 0.005;
const lerpFactor = 0.1;
const isSwiping = useRef(false);
const mousePositionOffset = useRef(new THREE.Vector3());
const mouseRotationOffset = useRef(new THREE.Euler());
const { isModalOpen } = useModalStore();
const lastTouchY = useRef(null);
const { isExperienceReady } = useExperienceStore();
useEffect(() => {
if (!isExperienceReady) return;
const handleWheel = (e) => {
if (isModalOpen) return;
const normalized = normalizeWheel(e);
targetScrollProgress.current +=
Math.sign(normalized.pixelY) *
scrollSpeed *
Math.min(Math.abs(normalized.pixelY) / 100, 1);
};
const handleMouseMove = (e) => {
const mouseX = (e.clientX / window.innerWidth) * 2 - 1;
const mouseY = (e.clientY / window.innerHeight) * 2 - 1;
const sensitivityX = 0.25;
const sensitivityY = 0.25;
const rotationSensitivityX = 0.1;
const rotationSensitivityY = 0.1;
mousePositionOffset.current.x = mouseX * sensitivityX;
mousePositionOffset.current.y = mouseY * sensitivityY;
mouseRotationOffset.current.x = mouseY * rotationSensitivityX;
mouseRotationOffset.current.y = mouseX * rotationSensitivityY;
};
const handleTouchStart = (e) => {
if (isModalOpen) return;
isSwiping.current = true;
lastTouchY.current = e.touches[0].clientY;
};
const handleTouchMove = (e) => {
if (!isSwiping.current) return;
if (lastTouchY.current !== null) {
const deltaY = e.touches[0].clientY - lastTouchY.current;
const touchMultiplier = 0.3;
targetScrollProgress.current +=
Math.sign(deltaY) * scrollSpeed * touchMultiplier;
}
lastTouchY.current = e.touches[0].clientY;
};
const handleTouchEnd = (e) => {
isSwiping.current = false;
lastTouchY.current = null;
};
const handleMouseDown = (e) => {
if (isModalOpen || e.pointerType === "touch") return;
isSwiping.current = true;
};
const handleMouseDrag = (e) => {
if (!isSwiping.current || e.pointerType === "touch") return;
const mouseMultiplier = 0.2;
targetScrollProgress.current +=
Math.sign(e.movementY) * scrollSpeed * mouseMultiplier;
};
const handleMouseUp = () => {
isSwiping.current = false;
};
window.addEventListener("wheel", handleWheel, { passive: false });
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mousedown", handleMouseDown);
window.addEventListener("mousemove", handleMouseDrag);
window.addEventListener("mouseup", handleMouseUp);
window.addEventListener("touchstart", handleTouchStart, { passive: false });
window.addEventListener("touchmove", handleTouchMove, { passive: false });
window.addEventListener("touchend", handleTouchEnd);
return () => {
window.removeEventListener("wheel", handleWheel);
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mousedown", handleMouseDown);
window.removeEventListener("mousemove", handleMouseDrag);
window.removeEventListener("mouseup", handleMouseUp);
window.removeEventListener("touchstart", handleTouchStart);
window.removeEventListener("touchmove", handleTouchMove);
window.removeEventListener("touchend", handleTouchEnd);
};
}, [isModalOpen, isExperienceReady]);
return (
<>
<Canvas eventSource={document.getElementById("root")}>
<group ref={cameraGroup}>
<PerspectiveCamera ref={camera} fov={72} makeDefault />
</group>
{/* <OrbitControls camera={camera.current} enableZoom={false} /> */}
<Scene
cameraGroup={cameraGroup}
camera={camera}
scrollProgress={scrollProgress}
setscrollProgress={setscrollProgress}
targetScrollProgress={targetScrollProgress}
lerpFactor={lerpFactor}
mousePositionOffset={mousePositionOffset}
mouseRotationOffset={mouseRotationOffset}
/>
</Canvas>
</>
);
};
export default Experience;
src/Experience/Scene.jsx
import { React, Suspense, useState, useRef, useEffect } from "react";
import * as THREE from "three";
import { useFrame } from "@react-three/fiber";
import { Environment, useProgress } from "@react-three/drei";
import First from "./models/First";
import Second from "./models/Second";
import Third from "./models/Third";
import Fourth from "./models/Fourth";
import Fifth from "./models/Fifth";
import Sixth from "./models/Sixth";
import Seventh from "./models/Seventh";
import Eighth from "./models/Eighth";
import Ninth from "./models/Ninth";
import Tenth from "./models/Tenth";
import Eleventh from "./models/Eleventh";
import Bird from "./models/Bird";
import Background from "./models/Background";
import {
cameraCurve,
DebugCurve,
CameraHelper,
rotationTargets,
} from "./utils/curve";
import Fire from "./components/Fire";
import WaterFall from "./components/WaterFall";
import { useExperienceStore } from "../stores/experienceStore";
const LoadingManager = () => {
const { active, progress } = useProgress();
const { setIsExperienceLoading } = useExperienceStore();
useEffect(() => {
setIsExperienceLoading(active);
}, [active, progress, setIsExperienceLoading]);
return null;
};
const useChunkedLoading = () => {
const [loadingStage, setLoadingStage] = useState(0);
const { active } = useProgress();
const prevActiveRef = useRef(true);
const { incrementLoadedChunks } = useExperienceStore();
useEffect(() => {
if (prevActiveRef.current && !active) {
setLoadingStage((prev) => prev + 1);
incrementLoadedChunks();
}
prevActiveRef.current = active;
}, [active, incrementLoadedChunks]);
return {
shouldRenderChunk: (chunkIndex) => loadingStage >= chunkIndex,
};
};
const Scene = ({
cameraGroup,
camera,
scrollProgress,
setscrollProgress,
targetScrollProgress,
lerpFactor,
mousePositionOffset,
mouseRotationOffset,
}) => {
const [pulseIntensity, setPulseIntensity] = useState(0);
const [rotationBufferQuat] = useState(
new THREE.Quaternion().setFromEuler(rotationTargets[0].rotation)
);
const timeRef = useRef(0);
const { shouldRenderChunk } = useChunkedLoading();
const getLerpedRotation = (progress) => {
for (let i = 0; i < rotationTargets.length - 1; i++) {
const start = rotationTargets[i];
const end = rotationTargets[i + 1];
if (progress >= start.progress && progress <= end.progress) {
const lerpFactor =
(progress - start.progress) / (end.progress - start.progress);
const startQuaternion = new THREE.Quaternion().setFromEuler(
start.rotation
);
const endQuaternion = new THREE.Quaternion().setFromEuler(end.rotation);
const lerpingQuaternion = new THREE.Quaternion();
lerpingQuaternion.slerpQuaternions(
startQuaternion,
endQuaternion,
lerpFactor
);
return lerpingQuaternion;
}
}
return new THREE.Quaternion().setFromEuler(
rotationTargets[rotationTargets.length - 1].rotation
);
};
useFrame((state) => {
if (camera) {
// Pulsating Time
timeRef.current = state.clock.getElapsedTime();
const newPulseIntensity = (Math.sin(state.clock.elapsedTime * 3) + 1) / 2;
setPulseIntensity(newPulseIntensity);
// Lerp to new position
let newProgress = THREE.MathUtils.lerp(
scrollProgress,
targetScrollProgress.current,
lerpFactor
);
// So the camera can loop
if (newProgress > 1) {
newProgress = 0;
targetScrollProgress.current = 0;
} else if (newProgress < 0) {
newProgress = 1;
targetScrollProgress.current = 1;
}
setscrollProgress(newProgress);
// Lerp to new camera offset position
const basePoint = cameraCurve.getPoint(newProgress);
cameraGroup.current.position.x = THREE.MathUtils.lerp(
cameraGroup.current.position.x,
basePoint.x,
0.1
);
cameraGroup.current.position.y = THREE.MathUtils.lerp(
cameraGroup.current.position.y,
basePoint.y,
0.1
);
cameraGroup.current.position.z = THREE.MathUtils.lerp(
cameraGroup.current.position.z,
basePoint.z,
0.1
);
camera.current.position.x = THREE.MathUtils.lerp(
camera.current.position.x,
mousePositionOffset.current.x,
0.1
);
camera.current.position.y = THREE.MathUtils.lerp(
camera.current.position.y,
-mousePositionOffset.current.y,
0.1
);
camera.current.position.z = 0;
const targetRotation = getLerpedRotation(newProgress);
// Use slerp to smoothly interpolate between our target rotations
rotationBufferQuat.slerp(targetRotation, 0.05);
cameraGroup.current.quaternion.copy(rotationBufferQuat);
// Direct camera rotation, NOT the group
camera.current.rotation.x = THREE.MathUtils.lerp(
camera.current.rotation.x,
-mouseRotationOffset.current.x,
0.1
);
camera.current.rotation.y = THREE.MathUtils.lerp(
camera.current.rotation.y,
-mouseRotationOffset.current.y,
0.1
);
}
});
return (
<>
<LoadingManager />
<Environment
background={true}
backgroundRotation={[0, Math.PI / 2, 0]}
files={[
"/cubemap/px.webp",
"/cubemap/nx.webp",
"/cubemap/py.webp",
"/cubemap/ny.webp",
"/cubemap/pz.webp",
"/cubemap/nz.webp",
]}
/>
<fogExp2 attach="fog" color="#403e3e" density={0.0125} />
{/* <DebugCurve curve={cameraCurve} /> */}
<Suspense fallback={null}>
<First />
<Second />
<Background />
</Suspense>
{shouldRenderChunk(1) && (
<Suspense fallback={null}>
<Third />
<Fourth />
<Fifth />
</Suspense>
)}
{shouldRenderChunk(2) && (
<Suspense fallback={null}>
<Sixth />
<Seventh />
<Eighth time={timeRef.current} />
</Suspense>
)}
{shouldRenderChunk(3) && (
<Suspense fallback={null}>
<Ninth progress={scrollProgress} pulseIntensity={pulseIntensity} />
<Tenth />
<Eleventh />
<Bird time={timeRef.current} position={[-20, 40, -45]} scale={0.02} />
<Fire time={timeRef.current} />
<WaterFall time={timeRef.current} />
</Suspense>
)}
</>
);
};
export default Scene;
src/Experience/components/Fire.jsx
import * as THREE from "three";
import { useLayoutEffect, useRef, useEffect, useState } from "react";
import { extend, useLoader } from "@react-three/fiber";
import { PositionalAudio } from "@react-three/drei";
import { useExperienceStore } from "../../stores/experienceStore";
import { getAudioFileExtension } from "../../utils/platformDetector";
// Fire shader credits to drcmda
// See here: https://codesandbox.io/p/sandbox/3878x
class FireMaterial extends THREE.ShaderMaterial {
constructor() {
super({
defines: { ITERATIONS: "10", OCTIVES: "3" },
uniforms: {
fireTex: { type: "t", value: null },
color: { type: "c", value: null },
time: { type: "f", value: 0.0 },
seed: { type: "f", value: 0.0 },
invModelMatrix: { type: "m4", value: null },
scale: { type: "v3", value: null },
noiseScale: { type: "v4", value: new THREE.Vector4(1, 2, 1, 0.3) },
magnitude: { type: "f", value: 2.5 },
lacunarity: { type: "f", value: 3.0 },
gain: { type: "f", value: 0.25 },
},
vertexShader: `
varying vec3 vWorldPos;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
vWorldPos = (modelMatrix * vec4(position, 1.0)).xyz;
}`,
fragmentShader: `
// Simplex 3D Noise
// by Ian McEwan, Stefan Gustavson (https://github.com/stegu/webgl-noise)
vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
float snoise(vec3 v){
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0. + 0.0 * C
vec3 x1 = x0 - i1 + 1.0 * C.xxx;
vec3 x2 = x0 - i2 + 2.0 * C.xxx;
vec3 x3 = x0 - 1. + 3.0 * C.xxx;
// Permutations
i = mod(i, 289.0 );
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients
// ( N*N points uniformly over a square, mapped onto an octahedron.)
float n_ = 1.0/7.0; // N=7
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z *ns.z); // mod(p,N*N)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
//Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
dot(p2,x2), dot(p3,x3) ) );
}
uniform vec3 color;
uniform float time;
uniform float seed;
uniform mat4 invModelMatrix;
uniform vec3 scale;
uniform vec4 noiseScale;
uniform float magnitude;
uniform float lacunarity;
uniform float gain;
uniform sampler2D fireTex;
varying vec3 vWorldPos;
float turbulence(vec3 p) {
float sum = 0.0;
float freq = 1.0;
float amp = 1.0;
for(int i = 0; i < OCTIVES; i++) {
sum += abs(snoise(p * freq)) * amp;
freq *= lacunarity;
amp *= gain;
}
return sum;
}
vec4 samplerFire (vec3 p, vec4 scale) {
vec2 st = vec2(sqrt(dot(p.xz, p.xz)), p.y);
if(st.x <= 0.0 || st.x >= 1.0 || st.y <= 0.0 || st.y >= 1.0) return vec4(0.0);
p.y -= (seed + time) * scale.w;
p *= scale.xyz;
st.y += sqrt(st.y) * magnitude * turbulence(p);
if(st.y <= 0.0 || st.y >= 1.0) return vec4(0.0);
return texture2D(fireTex, st);
}
vec3 localize(vec3 p) {
return (invModelMatrix * vec4(p, 1.0)).xyz;
}
void main() {
vec3 rayPos = vWorldPos;
vec3 rayDir = normalize(rayPos - cameraPosition);
float rayLen = 0.0388 * length(scale.xyz);
vec4 col = vec4(0.0);
for(int i = 0; i < ITERATIONS; i++) {
rayPos += rayDir * rayLen;
vec3 lp = localize(rayPos);
lp.y += 0.5;
lp.xz *= 2.0;
col += samplerFire(lp, noiseScale);
}
float alpha = col.r;
// alpha = smoothstep(0.4, 0.5, alpha);
col.a = alpha;
col.rgb *= 1.05;
if (col.a < 0.1) discard;
gl_FragColor = col;
}`,
});
}
}
extend({ FireMaterial });
function FireElement({ color, time, withAudio = false, ...props }) {
const meshRef = useRef();
const materialRef = useRef();
const audioRef = useRef();
const texture = useLoader(THREE.TextureLoader, "/images/fire.png");
const { isExperienceReady } = useExperienceStore();
useLayoutEffect(() => {
if (!materialRef.current) return;
texture.magFilter = texture.minFilter = THREE.LinearFilter;
texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;
materialRef.current.uniforms.fireTex.value = texture;
materialRef.current.uniforms.color.value =
color || new THREE.Color(0xeeeeee);
materialRef.current.uniforms.invModelMatrix.value = new THREE.Matrix4();
materialRef.current.uniforms.scale.value = new THREE.Vector3(1, 1, 1);
materialRef.current.uniforms.seed.value = Math.random() * 19.19;
}, [texture, color]);
useEffect(() => {
if (!meshRef.current || !materialRef.current) return;
const invModelMatrix = materialRef.current.uniforms.invModelMatrix.value;
meshRef.current.updateMatrixWorld();
invModelMatrix.copy(meshRef.current.matrixWorld).invert();
materialRef.current.uniforms.time.value = time;
materialRef.current.uniforms.invModelMatrix.value = invModelMatrix;
materialRef.current.uniforms.scale.value = meshRef.current.scale;
}, [time]);
useEffect(() => {
if (audioRef.current && isExperienceReady && withAudio) {
try {
audioRef.current.play();
} catch (error) {
console.error("Error playing audio:", error);
}
}
}, [isExperienceReady, withAudio]);
return (
<mesh ref={meshRef} {...props} renderOrder={1}>
<boxGeometry />
<fireMaterial ref={materialRef} transparent depthWrite={false} />
{withAudio && (
<PositionalAudio
ref={audioRef}
url={`audio/sfx/torch.${getAudioFileExtension()}`}
distance={1}
maxDistance={1}
loop
/>
)}
</mesh>
);
}
export default function Fire({ time, ...props }) {
return (
<group {...props}>
{/* Braizer Fires */}
<FireElement
time={time}
scale={[1.4, 4, 1.4]}
position={[-13.1, 9.52, -14.4]}
withAudio={true}
/>
<FireElement
time={time}
scale={[1.4, 4, 1.4]}
position={[-9.29, 9.52, -14.4]}
withAudio={true}
/>
<FireElement
time={time}
scale={[1.4, 4, 1.4]}
position={[21.279, 9.52, -14.4]}
withAudio={true}
/>
<FireElement
time={time}
scale={[1.4, 4, 1.4]}
position={[25, 9.52, -14.4]}
withAudio={true}
/>
<FireElement
time={time}
scale={[1.4, 4, 1.4]}
position={[28.789, 9.52, -14.4]}
withAudio={true}
/>
{/* Outside Torches */}
<FireElement
time={time}
scale={[0.38, 1.4, 0.38]}
rotation={[0.3, 0, 0]}
position={[9.1, 10.32, -18.4]}
withAudio={true}
/>
<FireElement
time={time}
scale={[0.38, 1.4, 0.38]}
rotation={[0.3, 0, 0]}
position={[3.28, 10.32, -18.4]}
withAudio={true}
/>
<FireElement
time={time}
scale={[0.38, 1.4, 0.38]}
rotation={[0.3, 0, 0]}
position={[5.69, 17.25, -15.49]}
withAudio={true}
/>
{/* Inside Torches with Positional Audio */}
<FireElement
time={time}
scale={[0.38, 2, 0.38]}
position={[11.27, 8.62, -27.25]}
withAudio={true}
/>
<FireElement
time={time}
scale={[0.38, 2, 0.38]}
position={[1.4, 8.62, -27.15]}
withAudio={true}
/>
<FireElement
time={time}
scale={[0.38, 2, 0.38]}
position={[11.27, 8.62, -45.25]}
withAudio={true}
/>
<FireElement
time={time}
scale={[0.38, 2, 0.38]}
position={[1.4, 8.62, -45.15]}
withAudio={true}
/>
</group>
);
}
src/Experience/components/WaterFall.jsx
import * as THREE from "three";
import { useRef, useEffect, useMemo } from "react";
import { useGLTF, useTexture } from "@react-three/drei";
export default function WaterfallModel({
time,
speed = 0.1,
repeatY = 1,
edgeFade = 0.1,
tintColor = [0.651, 0.604, 0.702],
tintIntensity = 1,
...props
}) {
const { nodes } = useGLTF("/models/Waterfall.glb");
const texture = useTexture("/images/waterfall.webp");
const shaderRef = useRef();
const meshRef = useRef();
const bounds = useMemo(() => {
if (!nodes.Waterfall.geometry.boundingBox) {
nodes.Waterfall.geometry.computeBoundingBox();
}
return nodes.Waterfall.geometry.boundingBox;
}, [nodes.Waterfall.geometry]);
useEffect(() => {
if (texture) {
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(1, repeatY);
texture.minFilter = THREE.LinearMipMapLinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.anisotropy = 16;
if (!texture.generateMipmaps) {
texture.generateMipmaps = true;
texture.needsUpdate = true;
}
texture.onUpdate = () => {
if (shaderRef.current) {
shaderRef.current.uniforms.textureAspect.value =
texture.image.width / texture.image.height;
shaderRef.current.uniforms.repeatY.value = repeatY;
}
};
}
}, [texture, repeatY]);
useEffect(() => {
if (shaderRef.current) {
shaderRef.current.uniforms.time.value = time;
}
}, [time]);
const waterfallMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: time },
map: { value: texture },
textureAspect: { value: 0.7 },
repeatY: { value: repeatY },
edgeFade: { value: edgeFade },
boundsMin: { value: bounds?.min || new THREE.Vector3() },
boundsMax: { value: bounds?.max || new THREE.Vector3() },
tintColor: { value: new THREE.Vector3().fromArray(tintColor) },
tintIntensity: { value: tintIntensity },
},
vertexShader: `
uniform vec3 boundsMin;
uniform vec3 boundsMax;
varying vec2 vUv;
varying float vWidthFactor;
void main() {
vUv = uv;
// Calculate horizontal position factor (0-1 across the width)
float width = boundsMax.x - boundsMin.x;
vWidthFactor = (position.x - boundsMin.x) / width;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float time;
uniform sampler2D map;
uniform float textureAspect;
uniform float repeatY;
uniform float edgeFade;
uniform vec3 tintColor;
uniform float tintIntensity;
varying vec2 vUv;
varying float vWidthFactor;
void main() {
vec2 uv = vUv;
uv.x = (uv.x - 0.5) / textureAspect + 0.5;
// Scrolling effect with mirroring
float scroll = time * ${speed};
float phase = -uv.y * repeatY + scroll;
float wrapped = mod(phase, 2.0);
uv.y = (wrapped > 1.0) ? 2.0 - wrapped : wrapped;
// Sample texture
vec4 color = texture2D(map, uv);
// Edge fading based on mesh width
float fade = smoothstep(0.0, edgeFade, vWidthFactor) *
(1.0 - smoothstep(1.0 - edgeFade, 1.0, vWidthFactor));
// Apply fading
color.a *= fade;
// Add some tint to it to blend better
vec3 tintedColor = mix(color.rgb, color.rgb * tintColor, tintIntensity);
color.rgb = tintedColor;
gl_FragColor = color;
}
`,
transparent: true,
side: THREE.DoubleSide,
alphaTest: 0.1,
});
return (
<group {...props} dispose={null}>
<mesh
ref={meshRef}
geometry={nodes.Waterfall.geometry}
material={waterfallMaterial}
position={[32.444, 68.9, -73.738]}
rotation={[Math.PI, -1.45, Math.PI]}
>
<primitive
object={waterfallMaterial}
ref={shaderRef}
attach="material"
/>
</mesh>
</group>
);
}
useGLTF.preload("/models/Waterfall.glb");
src/Experience/models/Background.jsx
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.5.3 Background.glb
*/
import React, { useMemo } from "react";
import { useGLTF, Instances, Instance } from "@react-three/drei";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
export default function Model(props) {
const { nodes, materials } = useGLTF("/models/Background.glb");
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
return (
<Instances
limit={4}
geometry={nodes.background.geometry}
material={newMaterials.background}
{...props}
>
{/* First instance */}
<Instance
position={[-170.311, 66.468, -124.652]}
rotation={[Math.PI / 2, 0, -0.765]}
scale={210.189}
/>
<Instance
position={[-170.311, 44.468, -54.652]}
rotation={[Math.PI / 2, 0, -1.765]}
scale={205.189}
/>
{/* Second instance */}
<Instance
position={[190.311, 28.468, -118.652]}
rotation={[Math.PI / 2, 0, 0.5]}
scale={211}
/>
<Instance
position={[200.311, -42.468, -90.652]}
rotation={[Math.PI / 2, 0, 0.2]}
scale={145}
/>
</Instances>
);
}
useGLTF.preload("/models/Background.glb");
src/Experience/models/Bird.jsx
import { useMemo, useEffect, useRef } from "react";
import { useGraph, useFrame } from "@react-three/fiber";
import { SkeletonUtils } from "three-stdlib";
import { useGLTFWithKTX2 } from "../utils/useGLTFWithKTX2";
import { useAnimations } from "@react-three/drei";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
import { createBirdPath, getBirdPosition } from "../utils/birdPath";
export default function Bird({ time, speed = 0.01, ...props }) {
const group = useRef();
const { scene, animations } = useGLTFWithKTX2("/models/Bird.glb");
const clone = useMemo(() => SkeletonUtils.clone(scene), [scene]);
const { nodes, materials } = useGraph(clone);
const { actions, names } = useAnimations(animations, group);
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
const birdPath = useMemo(() => createBirdPath(), []);
useEffect(() => {
if (actions) {
Object.values(actions).forEach((action) => {
action.play();
});
}
}, [actions]);
useEffect(() => {
if (actions) {
const action = actions[names[0]];
if (action) {
action.time = (time * 1) % action.getClip().duration;
}
}
}, [time, actions, names]);
useEffect(() => {
if (group.current) {
const position = getBirdPosition(birdPath, time, speed);
group.current.position.copy(position);
}
}, [time]);
return (
<group ref={group} {...props} dispose={null}>
<group name="Scene">
<group name="Lol_what_even_is_this" rotation={[0, 0, 0]}>
<group name="Eagle" rotation={[0, 0, 0]}>
<primitive object={nodes._rootJoint} />
<group name="White_Eagle" />
<skinnedMesh
name="Object_7_Baked"
geometry={nodes.Object_7_Baked.geometry}
material={newMaterials.Bird_Real_Baked}
skeleton={nodes.Object_7_Baked.skeleton}
/>
</group>
</group>
</group>
</group>
);
}
src/Experience/models/Eighth.jsx
import { useRef, useEffect, useMemo, forwardRef } from "react";
import { Instance, Instances } from "@react-three/drei";
import { useGLTFWithKTX2 } from "../utils/useGLTFWithKTX2";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
import * as THREE from "three";
const TreeSwayMaterial = forwardRef(({ time, ...props }, ref) => {
const uniforms = useRef({
time: { value: 0 },
swayAmount: { value: 0.3 },
swaySpeed: { value: 0.7 },
baseFrequency: { value: 0.8 },
map: { value: props.map },
});
useEffect(() => {
if (uniforms.current) {
uniforms.current.time.value = time;
}
}, [time]);
const vertexShader = `
uniform float time;
uniform float swayAmount;
uniform float swaySpeed;
uniform float baseFrequency;
attribute float instanceScale;
varying vec2 vUv;
varying float vHeight;
void main() {
vUv = uv;
vHeight = position.y;
// Normalize the height for easier calculations (0 at base, 1 at top)
float normalizedHeight = position.y / 10.0; // Divisor based on tree height, but honestly doesn't really matter too much
// Multi-frequency sway for more organic movement
float sway1 = sin(time * swaySpeed * 0.8 + position.x * baseFrequency) * 0.5;
float sway2 = cos(time * swaySpeed * 1.3 + position.z * baseFrequency * 1.7) * 0.3;
float sway3 = sin(time * swaySpeed * 0.5 + position.x * baseFrequency * 0.3) * 0.2;
// Combine sway effects with more influence at the top
float combinedSway = (sway1 + sway2 + sway3) * swayAmount;
float trunkStiffness = 1.0 - smoothstep(0.0, 0.3, normalizedHeight); // More stiff at bottom of tree
// Apply sway with height influence and trunk stiffness
vec3 swayedPosition = position;
swayedPosition.x += combinedSway * 0.3 * normalizedHeight * (1.0 - trunkStiffness);
swayedPosition.z += combinedSway * 0.4 * normalizedHeight * (1.0 - trunkStiffness);
// Add a slight vertical movement for leaf flutter
swayedPosition.y += sin(time * swaySpeed * 2.0 + position.x) * 0.02 * normalizedHeight;
// Some wind direction for variation
float windDirection = sin(time * 0.1) * 0.5 + 0.5;
swayedPosition.xz += windDirection * combinedSway * 0.1 * normalizedHeight;
// Adjust the position
vec4 modelPosition = instanceMatrix * vec4(swayedPosition, 1.0);
vec4 viewPosition = viewMatrix * modelPosition;
vec4 projectedPosition = projectionMatrix * viewPosition;
gl_Position = projectedPosition;
}
`;
const fragmentShader = `
uniform sampler2D map;
varying vec2 vUv;
varying float vHeight;
void main() {
// Use the texture with original colors
vec4 texColor = texture2D(map, vUv);
// Discard transparency pixels below threshold
if (texColor.a < 0.1) discard;
gl_FragColor = texColor;
}
`;
return (
<shaderMaterial
ref={ref}
uniforms={uniforms.current}
vertexShader={vertexShader}
fragmentShader={fragmentShader}
transparent={true}
alphaTest={0.5}
side={THREE.DoubleSide}
renderOrder={1}
/>
);
});
export default function Model(props) {
const { nodes, materials } = useGLTFWithKTX2("/models/Eighth.glb");
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
const materialRef = useRef();
return (
<group {...props} dispose={null}>
<mesh
geometry={nodes.Eighth_Baked.geometry}
material={newMaterials.Eighth_Baked}
position={[-5.2, 9.818, -23.394]}
rotation={[0, -0.091, 0]}
renderOrder={2}
/>
<Instances limit={10} geometry={nodes.Eighth_Tree_Baked.geometry}>
<TreeSwayMaterial
ref={materialRef}
time={props.time}
map={newMaterials.Eighth_Baked.map}
transparent={newMaterials.Eighth_Baked.transparent}
alphaTest={newMaterials.Eighth_Baked.alphaTest}
/>
<Instance
position={[29.885, -1.852, -2.243]}
rotation={[0.095, 0.4214, -0.048]}
scale={0.9}
/>
<Instance
position={[13, -1.852, 4.543]}
rotation={[0.093, 0.714, -0.048]}
scale={0.82}
/>
<Instance
position={[-2.885, -1.852, 5.243]}
rotation={[0.093, 0.914, -0.048]}
scale={0.9}
/>
<Instance
position={[21.885, -1.852, 0.243]}
rotation={[0.093, 0.32, -0.048]}
scale={1}
/>
<Instance
position={[-10, -1.852, -2.243]}
rotation={[0.093, 0.5, -0.048]}
scale={1}
/>
<Instance
position={[-20, 3.852, -2.243]}
rotation={[0.093, 1.2, -0.058]}
scale={0.8}
/>
<Instance
position={[-22, 4.852, -15.243]}
rotation={[0.093, 0.3, -0.048]}
scale={1.2}
/>
<Instance
position={[-19, 4.252, -25.243]}
rotation={[0.093, 0.45, -0.048]}
scale={1.4}
/>
<Instance
position={[39.885, 2.252, -23.243]}
rotation={[0.093, 1.5, -0.028]}
scale={1.2}
/>
<Instance
position={[39.885, 2.252, -45.243]}
rotation={[0.093, 1.1, -0.048]}
scale={2}
/>
</Instances>
</group>
);
}
src/Experience/models/Eleventh.jsx
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.5.3 Eleventh.glb
*/
import { useMemo } from "react";
import { useGLTFWithKTX2 } from "../utils/useGLTFWithKTX2";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
export default function Model(props) {
const { nodes, materials } = useGLTFWithKTX2("/models/Eleventh.glb");
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
return (
<group {...props} dispose={null}>
<mesh
geometry={nodes.Eleven_Front_Entrance_Baked.geometry}
material={newMaterials.Eleventh_Final_Actual_Baked}
position={[4.568, 6.749, -38.719]}
rotation={[0, Math.PI / 2, 0]}
/>
</group>
);
}
src/Experience/models/Fifth.jsx
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.5.3 Fifth.glb
*/
import React, { useMemo } from "react";
import { useGLTFWithKTX2 } from "../utils/useGLTFWithKTX2";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
export default function Model(props) {
const { nodes, materials } = useGLTFWithKTX2("/models/Fifth.glb");
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
return (
<group {...props} dispose={null}>
<mesh
geometry={nodes.Fifth_Decorative_Stuff_Baked.geometry}
material={newMaterials.Fifth_Baked}
position={[19.576, 8.488, -28.211]}
rotation={[-Math.PI / 2, 0, 0]}
/>
</group>
);
}
src/Experience/models/First.jsx
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.5.3 First.glb
*/
import { useMemo } from "react";
import { useGLTFWithKTX2 } from "../utils/useGLTFWithKTX2";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
export default function Model(props) {
const { nodes, materials } = useGLTFWithKTX2("/models/First.glb");
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
return (
<group {...props} dispose={null}>
<mesh
geometry={nodes.First_Roof_Baked.geometry}
material={newMaterials.First_Two_Baked}
position={[5.714, 9.731, -22.866]}
/>
</group>
);
}
src/Experience/models/Fourth.jsx
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.5.3 Fourth.glb
*/
import React, { useMemo } from "react";
import { useGLTFWithKTX2 } from "../utils/useGLTFWithKTX2";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
export default function Model(props) {
const { nodes, materials } = useGLTFWithKTX2("/models/Fourth.glb");
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
return (
<group {...props} dispose={null}>
<mesh
geometry={nodes.Fourth_Left_Mountain_Baked.geometry}
material={newMaterials.Fourth_Baked}
position={[31.093, 40.967, -91.882]}
/>
</group>
);
}
src/Experience/models/Ninth.jsx
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.5.3 Ninth.glb
*/
import React, { useEffect, useState, useMemo } from "react";
import { useGLTFWithKTX2 } from "../utils/useGLTFWithKTX2";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
import * as THREE from "three";
import { useModalStore } from "../../stores/useModalStore";
import { playSound } from "../../utils/audioSystem.js";
export default function Model({ progress = 0, pulseIntensity = 0, ...props }) {
const { nodes, materials } = useGLTFWithKTX2("/models/Ninth.glb");
const [hoveredMesh, setHoveredMesh] = useState(null);
const { openModal, setModalID, isModalOpen } = useModalStore();
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
const derivedMaterials = useMemo(() => {
const baseMaterial = newMaterials["Ninth_real_actual_Baked"].clone();
const brightHoveredMaterial =
newMaterials["Ninth_real_actual_Baked"].clone();
brightHoveredMaterial.color = new THREE.Color(2.5, 2.5, 2.5);
const pulsingMaterial = newMaterials["Ninth_real_actual_Baked"].clone();
return {
baseMaterial,
brightHoveredMaterial,
pulsingMaterial,
};
}, [newMaterials]);
const getMaterial = useMemo(
() => (elementID, progressRange) => {
if (
hoveredMesh === elementID &&
progress >= 0.399 &&
progress <= 0.6 &&
!isModalOpen
)
return derivedMaterials.brightHoveredMaterial;
const [min, max] = progressRange;
if (progress >= min && progress <= max) {
const pulseColor = 1 + pulseIntensity * 1.5;
derivedMaterials.pulsingMaterial.color.setRGB(
pulseColor,
pulseColor,
pulseColor
);
return derivedMaterials.pulsingMaterial;
}
return derivedMaterials.baseMaterial;
},
[hoveredMesh, progress, pulseIntensity, isModalOpen, derivedMaterials]
);
useEffect(() => {
if (progress <= 0.399 || progress >= 0.6 || isModalOpen) return;
document.body.style.cursor = hoveredMesh ? "pointer" : "auto";
return () => {
document.body.style.cursor = "auto";
};
}, [hoveredMesh, progress, isModalOpen]);
const handleClick = useMemo(
() => (elementID) => {
if (progress <= 0.399 || progress >= 0.6 || isModalOpen) return;
openModal();
setModalID(elementID);
playSound("thumpHover");
},
[progress, isModalOpen, openModal, setModalID]
);
const handlePointerOver = useMemo(
() => (meshId) => {
setHoveredMesh(meshId);
if (progress <= 0.399 || progress >= 0.6 || isModalOpen) return;
playSound("thumpHover");
},
[isModalOpen, progress]
);
const meshes = useMemo(
() => [
{
id: "demoshub",
geometry: nodes.Ninth_DemosHub_Baked.geometry,
progressRange: [0.399, 0.6],
position: [6.136, 9.285, -48.469],
rotation: [Math.PI / 2, 0, 0],
},
{
id: "thecollective",
geometry: nodes.Ninth_TheCollective_Baked.geometry,
progressRange: [0.399, 0.6],
position: [-0.457, 9.285, -48.469],
rotation: [Math.PI / 2, 0, 0],
},
{
id: "webzibition",
geometry: nodes.Ninth_Webzibition_Baked.geometry,
progressRange: [0.399, 0.6],
position: [2.778, 9.285, -48.469],
rotation: [Math.PI / 2, 0, 0],
},
{
id: "developerspotlight",
geometry: nodes.Ninth_DeveloperSpotlight_Baked.geometry,
progressRange: [0.399, 0.6],
position: [12.806, 9.285, -48.469],
rotation: [Math.PI / 2, 0, 0],
},
{
id: "designerspotlight",
geometry: nodes.Ninth_DesignerSpotlight_Baked.geometry,
progressRange: [0.399, 0.6],
position: [9.538, 9.285, -48.469],
rotation: [Math.PI / 2, 0, 0],
},
],
[nodes]
);
return (
<group {...props} dispose={null}>
{meshes.map((mesh) => (
<mesh
key={mesh.id}
geometry={mesh.geometry}
material={getMaterial(mesh.id, mesh.progressRange)}
onPointerOver={() => handlePointerOver(mesh.id)}
onPointerOut={() => setHoveredMesh(null)}
onClick={() => handleClick(mesh.id)}
position={mesh.position}
rotation={mesh.rotation}
/>
))}
<mesh
geometry={nodes.Ninth_Paper_Baked.geometry}
material={newMaterials["Ninth_real_actual_Baked"]}
position={[6.122, 6.808, -35.966]}
/>
</group>
);
}
src/Experience/models/Second.jsx
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.5.3 Second.glb
*/
import React, { useMemo } from "react";
import { useGLTFWithKTX2 } from "../utils/useGLTFWithKTX2";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
export default function Model(props) {
const { nodes, materials } = useGLTFWithKTX2("/models/Second.glb");
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
return (
<group {...props} dispose={null}>
<mesh
geometry={nodes.Second_Base_Mountain_Baked.geometry}
material={newMaterials.Second_Baked}
position={[31.093, 40.967, -91.882]}
/>
</group>
);
}
src/Experience/models/Seventh.jsx
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.5.3 Seventh.glb
*/
import React, { useMemo } from "react";
import { useGLTFWithKTX2 } from "../utils/useGLTFWithKTX2";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
export default function Model(props) {
const { nodes, materials } = useGLTFWithKTX2("/models/Seventh.glb");
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
return (
<group {...props} dispose={null}>
<mesh
geometry={nodes.Seventh_Floors_Baked.geometry}
material={newMaterials.Seventh_Four_Baked}
position={[7.796, 5.736, -12.207]}
/>
</group>
);
}
src/Experience/models/Sixth.jsx
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.5.3 Sixth.glb
*/
import React, { useMemo } from "react";
import { useGLTFWithKTX2 } from "../utils/useGLTFWithKTX2";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
export default function Model(props) {
const { nodes, materials } = useGLTFWithKTX2("/models/Sixth.glb");
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
return (
<group {...props} dispose={null}>
<mesh
geometry={nodes.Sixth_Walls_Baked.geometry}
material={newMaterials.Sixth_Baked}
position={[4.568, 6.749, -38.719]}
rotation={[0, Math.PI / 2, 0]}
/>
</group>
);
}
src/Experience/models/Tenth.jsx
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.5.3 Tenth.glb
*/
import React, { useMemo } from "react";
import { useGLTFWithKTX2 } from "../utils/useGLTFWithKTX2";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
export default function Model(props) {
const { nodes, materials } = useGLTFWithKTX2("/models/Tenth.glb");
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
return (
<group {...props} dispose={null}>
<mesh
geometry={nodes.Tenth_Stone_Floor_Baked.geometry}
material={newMaterials["Tenth_Baked.001"]}
position={[7.796, 5.736, -12.207]}
/>
</group>
);
}
src/Experience/models/Third.jsx
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.5.3 Third.glb
*/
import React, { useMemo } from "react";
import { useGLTFWithKTX2 } from "../utils/useGLTFWithKTX2";
import { convertMaterialsToBasic } from "../utils/convertToBasic";
export default function Model(props) {
const { nodes, materials } = useGLTFWithKTX2("/models/Third.glb");
const newMaterials = useMemo(
() => convertMaterialsToBasic(materials),
[materials]
);
return (
<group {...props} dispose={null}>
<mesh
geometry={nodes.Third_Right_Mountain_Baked.geometry}
material={newMaterials.Third_Baked}
position={[31.093, 40.967, -91.882]}
/>
</group>
);
}
src/Experience/utils/birdPath.jsx
import * as THREE from "three";
export const createBirdPath = () => {
const curve = new THREE.CatmullRomCurve3(
[
new THREE.Vector3(-90, 25, -45),
new THREE.Vector3(90, 40, -45),
new THREE.Vector3(100, 50, -90),
new THREE.Vector3(50, 65, -90),
new THREE.Vector3(-120, 25, -65),
],
true
);
return curve;
};
export const getBirdPosition = (curve, time, speed = 0.1) => {
const t = (time * speed) % 1;
return curve.getPointAt(t);
};
export const getBirdOrientation = (curve, time, speed = 0.1) => {
const t = (time * speed) % 1;
const currentPos = curve.getPointAt(t);
const lookAheadAmount = 0.001;
let nextT;
if (t + lookAheadAmount >= 1) {
nextT = t + lookAheadAmount - 1;
} else {
nextT = t + lookAheadAmount;
}
const targetPos = curve.getPointAt(nextT);
const tempObj = new THREE.Object3D();
tempObj.position.copy(currentPos);
tempObj.lookAt(targetPos);
return tempObj.rotation.clone();
};
src/Experience/utils/convertToBasic.jsx
import * as THREE from "three";
export function convertMaterialsToBasic(materials, alphaTestValue = 0) {
const newMaterials = {};
Object.keys(materials).forEach((key) => {
const oldMaterial = materials[key];
const newMaterial = new THREE.MeshBasicMaterial({
map: oldMaterial.map,
transparent: oldMaterial.transparent,
side: oldMaterial.transparent ? THREE.DoubleSide : THREE.FrontSide,
alphaTest: oldMaterial.transparent ? 0.4 : alphaTestValue,
});
newMaterials[key] = newMaterial;
});
return newMaterials;
}
src/Experience/utils/curve.jsx
import * as THREE from "three";
export const cameraCurve = new THREE.CatmullRomCurve3(
[
new THREE.Vector3(
6.577763360438033,
window.innerWidth < 764 ? 12 : 8.856284964410769,
window.innerWidth < 764 ? 31.640379680866076 : 20.640379680866076
),
new THREE.Vector3(6.129, 9.16248066285629, -9.514943289676541),
new THREE.Vector3(6.139, 9.16248066285629, -25.514943289676541),
new THREE.Vector3(6.121, 8.58385544864918, -35.5852064804525),
new THREE.Vector3(6.1, 8.6385544864918, -39.15952064804525),
new THREE.Vector3(-0.6, 9.095692752216793, -44.84820648294106),
new THREE.Vector3(
12.932496218694319,
9.072285787822777,
-44.69183302104726
),
new THREE.Vector3(7.458086050034908, 8.73452167183442, -40.5039287861451),
new THREE.Vector3(
5.984547855718962,
14.010925354273802,
-21.804431479523224
),
new THREE.Vector3(7.75929286794663, 18.796052392871356, 0.810478836223162),
new THREE.Vector3(-0.25929286794663, 16.796052392871356, 6.810478836223162),
new THREE.Vector3(
6.577763360438033,
window.innerWidth < 764 ? 12 : 8.856284964410769,
window.innerWidth < 764 ? 31.640379680866076 : 20.640379680866076
),
],
true
);
export const DebugCurve = ({ curve }) => {
const points = curve.getPoints(50);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
return (
<line geometry={geometry}>
<lineBasicMaterial color={"red"} />
</line>
);
};
export const CameraHelper = ({ cameraRef }) => {
useHelper(cameraRef, THREE.CameraHelper);
return null;
};
export const rotationTargets = [
{
progress: 0,
rotation: new THREE.Euler(
window.innerWidth < 764 ? -0.1 : 0,
0.00479125845971665,
0.000597178775549178
),
},
{
progress: 0.07,
rotation: new THREE.Euler(
-0.04277011042878412,
-0.00866103427209704,
-0.00037065478236413864
),
},
{
progress: 0.2,
rotation: new THREE.Euler(
-0.04277011042878412,
-0.00866103427209704,
-0.00037065478236413864
),
},
{
progress: 0.25,
rotation: new THREE.Euler(
-0.8851668165265633,
0.003786054557443451,
0.003635853660383532
),
},
{
progress: 0.32,
rotation: new THREE.Euler(
-0.086339131170143176,
0.398491298300412,
-0.1343243154257062
),
},
{
progress: 0.36,
rotation: new THREE.Euler(
-0.086339131170143176,
0.708491298300412,
-0.0143243154257062
),
},
{
progress: 0.41,
rotation: new THREE.Euler(
-0.046339131170143176,
0.0298491298300412,
0.07343243154257062
),
},
{
progress: 0.49,
rotation: new THREE.Euler(
-0.026015217004565497,
-0.019361968160347905,
-0.0405037879475436998
),
},
{
progress: 0.61,
rotation: new THREE.Euler(
-0.038854519527552256,
-0.5056419965908544,
-0.01657632912947032
),
},
{
progress: 0.65,
rotation: new THREE.Euler(
-0.3535558580154114,
0.05311086541781096,
0.005869310093750261
),
},
{
progress: 0.75,
rotation: new THREE.Euler(
-0.05535558580154114,
0.32017130864367585,
0.02072972153013933
),
},
{
progress: 0.8,
rotation: new THREE.Euler(
-0.13535558580154114,
-0.13017130864367585,
0.02072972153013933
),
},
{
progress: 0.9,
rotation: new THREE.Euler(
0.01492353012229583,
-0.073017130864367585,
0.02072972153013933
),
},
{
progress: 1,
rotation: new THREE.Euler(
window.innerWidth < 764 ? -0.1 : 0,
0.00479125845971665,
0.000597178775549178
),
},
];
src/Experience/utils/useGLTFWithKTX2.jsx
import { useGLTF } from "@react-three/drei";
import { useThree } from "@react-three/fiber";
import { KTX2Loader } from "three-stdlib";
const ktx2Loader = new KTX2Loader();
ktx2Loader.setTranscoderPath("/basis/");
export function useGLTFWithKTX2(path) {
const { gl } = useThree();
return useGLTF(path, true, true, (loader) => {
loader.setKTX2Loader(ktx2Loader.detectSupport(gl));
});
}
src/components/LoadingScreen/LoadingScreen.jsx
import React, { useState, useEffect, useRef } from "react";
import "./LoadingScreen.scss";
import { playBackgroundMusic, playSound } from "../../utils/audioSystem.js";
import { useExperienceStore } from "../../stores/experienceStore.js";
const LoadingScreen = () => {
const [isRevealed, setIsRevealed] = useState(false);
const [isAnimationFinished, setIsAnimationFinished] = useState(false);
const [displayedProgress, setDisplayedProgress] = useState(0);
const [hasCompletedAnimation, setHasCompletedAnimation] = useState(false); // New state
const animationRef = useRef(null);
const {
setIsExperienceReady,
isExperienceLoading,
loadedChunks,
totalChunks,
} = useExperienceStore();
const loadingProgress = Math.round((loadedChunks / totalChunks) * 100);
// Smoothly animate the displayed progress number
useEffect(() => {
if (loadingProgress > displayedProgress || !hasCompletedAnimation) {
const animate = () => {
setDisplayedProgress((prev) => {
const step = Math.ceil((loadingProgress - prev) * 0.1);
const newValue = prev + step;
if (newValue >= loadingProgress) {
const finalValue = Math.min(loadingProgress, 100);
if (finalValue === 100) {
setHasCompletedAnimation(true);
}
return finalValue;
}
animationRef.current = requestAnimationFrame(animate);
return newValue;
});
};
animationRef.current = requestAnimationFrame(animate);
}
return () => {
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
}
};
}, [loadingProgress, hasCompletedAnimation]);
const handleReveal = () => {
setIsRevealed(true);
playBackgroundMusic();
playSound("backgroundAmbience");
playSound("thumpHover");
setIsExperienceReady();
};
const handleAnimationFinished = () => {
setIsAnimationFinished(true);
};
if (isAnimationFinished) {
return null;
}
// Only show button if BOTH conditions are met:
// 1. Loading is technically complete (loadedChunks >= totalChunks)
// 2. The animation has visually reached 100%
// Without visual jump to 100 it'd show the enter button when it's done loading.
// I want it to go to 100 THEN show the enter button not jump to it when it's done.
const showEnterButton =
!isExperienceLoading &&
loadedChunks >= totalChunks &&
hasCompletedAnimation &&
!isRevealed;
return (
<>
<div className="loading-screen">
<div
className={`background-top-half ${isRevealed ? "revealed" : ""}`}
onTransitionEnd={handleAnimationFinished}
></div>
<div
className={`background-bottom-half ${isRevealed ? "revealed" : ""}`}
></div>
<div className="loading-screen-info-container">
<div
className={`instructions-container ${isRevealed ? "revealed" : ""}`}
>
Slowly Drag or Scroll to Navigate
</div>
{!isRevealed && !showEnterButton && (
<div className="loading-bar-container">
<div
className="loading-bar"
style={{ width: `${Math.min(displayedProgress, 100)}%` }}
></div>
<div className="percentage">
{Math.min(displayedProgress, 100)}%
</div>
</div>
)}
{showEnterButton && (
<button className="loading-screen-button" onClick={handleReveal}>
Enter World
</button>
)}
</div>
</div>
</>
);
};
export default LoadingScreen;
src/components/LoadingScreen/LoadingScreen.scss
.loading-screen {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
overflow: hidden;
z-index: 999;
}
.background-top-half,
.background-bottom-half {
position: absolute;
height: 50%;
width: 100%;
background-color: rgb(11, 11, 11);
transition: transform 1s ease-in-out 0.5s;
}
.background-top-half {
top: 0;
&.revealed {
transform: translateY(-100%);
}
}
.background-bottom-half {
top: 50%;
&.revealed {
transform: translateY(100%);
}
}
.loading-screen-button {
background-color: #fff;
border: none;
border-radius: none;
padding: 10px 10px;
color: black;
&:hover {
color: white;
background-color: rgb(11, 11, 11);
cursor: pointer;
}
}
.loading-screen-info-container {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
place-content: center;
justify-content: center;
align-items: center;
gap: 24px;
}
.instructions-container {
position: absolute;
bottom: 5%;
left: 50%;
width: 100%;
display: flex;
height: fit-content;
place-content: center;
justify-content: center;
align-items: center;
transform: translate(-50%, -50%);
color: #fff;
font-size: 12px;
@media (max-width: 480px) {
font-size: 12px;
}
@media (max-width: 390px) {
font-size: 12px;
}
&.revealed {
display: none;
}
}
.loading-bar-container {
width: 350px;
height: 12px;
border: 2px solid #ffffff;
}
.loading-bar {
width: 0;
height: 100%;
background-color: #ffffff;
border: 1px solid black;
will-change: width;
}
.percentage {
font-size: 12px;
color: #fff;
display: flex;
flex-direction: column;
place-content: center;
justify-content: center;
align-items: center;
margin-top: 24px;
}
src/components/Modal/Modal.jsx
import React, { useEffect, useRef } from "react";
import "./Modal.scss";
import { useModalStore } from "../../stores/useModalStore";
import { modalContent } from "../../data/modalContent";
import { playSound } from "../../utils/audioSystem.js";
const Modal = () => {
const { isModalOpen, modalID, closeModal } = useModalStore();
const modalRef = useRef(null);
const handleClose = () => {
playSound("thumpHover");
closeModal();
};
useEffect(() => {
const handleEscapeKey = (e) => {
if (e.key === "Escape" && isModalOpen) {
closeModal();
}
};
document.addEventListener("keydown", handleEscapeKey);
return () => {
document.removeEventListener("keydown", handleEscapeKey);
};
}, [isModalOpen, closeModal]);
useEffect(() => {
const handleOutsideClick = (e) => {
if (modalRef.current && !modalRef.current.contains(e.target)) {
closeModal();
}
};
if (isModalOpen) {
document.addEventListener("mousedown", handleOutsideClick);
}
return () => {
document.removeEventListener("mousedown", handleOutsideClick);
};
}, [isModalOpen, closeModal]);
useEffect(() => {
if (isModalOpen) {
document.body.style.overflow = "hidden";
document.body.style.cursor = "auto";
} else {
document.body.style.overflow = "auto";
}
return () => {
document.body.style.overflow = "auto";
};
}, [isModalOpen]);
if (!isModalOpen || !modalContent[modalID]) return null;
const { title, link, linkText, paragraphs } = modalContent[modalID];
return (
<div className="modal-overlay">
<div className="modal-container" ref={modalRef}>
<button className="modal-back-button" onClick={handleClose}>
<svg
width="20"
height="20"
class=""
viewBox="0 0 130 134"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M128.986 128.986L1 1" stroke="currentColor" />
<path d="M128.986 128.986L1 1" stroke="currentColor" />
<path d="M1 132.986L128.986 5" stroke="currentColor" />
</svg>
</button>
<div className="modal-content">
<h2 className="modal-title">{title}</h2>
<div className="modal-paragraphs">
{paragraphs.map((paragraph, index) => (
<p key={index}>{paragraph}</p>
))}
</div>
<a
href={link}
target="_blank"
rel="noopener noreferrer"
className="modal-link"
>
{linkText}
</a>
</div>
</div>
</div>
);
};
export default Modal;
src/components/Modal/Modal.scss
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(12, 12, 12, 0.8);
backdrop-filter: blur(3px);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.modal-container {
position: relative;
width: 90%;
max-width: 600px;
max-height: 90vh;
overflow: hidden;
background: transparent;
display: flex;
flex-direction: column;
border: 1px solid #f1f1f1;
}
.modal-back-button {
position: absolute;
top: 26px;
right: 28px;
background: none;
border: none;
color: #f1f1f1;
display: flex;
align-items: center;
font-size: 14px;
font-weight: 500;
cursor: pointer;
padding: 8px 12px;
transition: background-color 0.2s;
z-index: 1;
svg {
stroke-width: 6px;
}
&:hover {
text-decoration: underline;
}
}
.modal-content {
padding: 40px 40px;
overflow-y: auto;
color: #f1f1f1;
}
.modal-title {
font-size: 18px;
margin-bottom: 24px;
color: #f1f1f1;
position: relative;
}
.modal-paragraphs {
margin-bottom: 30px;
p {
margin-bottom: 16px;
line-height: 1.6;
font-size: 12px;
color: rgba(255, 255, 255, 0.9);
&:last-child {
margin-bottom: 0;
}
}
}
.modal-link {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 12px 24px;
color: #f1f1f1;
text-decoration: none;
border: 1px solid #f1f1f1;
font-weight: 600;
font-size: 11px;
&:hover {
background: linear-gradient(90deg, #715116, #a93e1b);
box-shadow: 0 4px 20px rgba(58, 134, 255, 0.3);
svg {
transform: translateX(3px);
}
}
}
@media (max-width: 768px) {
.modal-content {
padding: 40px 22px;
}
}
src/data/modalContent.js
export const modalContent = {
demoshub: {
title: "Demos Hub",
link: "https://tympanus.net/codrops/demos/",
linkText: "See the demos",
paragraphs: [
"The demos hub is a curated collection of 500+ free demos, prototypes, animations, templates, layouts, UI styles, and experimental design concepts. They are available to download and use freely under an MIT license.",
],
},
thecollective: {
title: "The Collective",
link: "https://tympanus.net/codrops/news/",
linkText: "Visit The Collective",
paragraphs: [
"This page features daily updated hand-picked interesting links for web creators, including thought pieces, blogs, open source code, tutorials, events, and many others - all carefully curated to inspire and inform the web community.",
],
},
webzibition: {
title: "Webzibition",
link: "https://tympanus.net/codrops/webzibition/",
linkText: "Visit the Webzibition",
paragraphs: ["Freshly hand-picked websites for your inspiration."],
},
developerspotlight: {
title: "Developer Spotlight",
link: "https://tympanus.net/codrops/tag/developer-spotlight/",
linkText: "Meet the developers",
paragraphs: [
"In their own words, developers share the stories behind their work—the paths they’ve taken, the tools they trust, and the ideas driving their practice. A closer look at the people pushing the web forward.",
],
},
designerspotlight: {
title: "Designer Spotlight",
link: "https://tympanus.net/codrops/tag/designer-spotlight/",
linkText: "Meet the designers",
paragraphs: [
"This page features the creatives shaping design today. Each feature explores their journey, inspiration, and standout work—offering a real look at their process and vision.",
],
},
};
src/index.scss
@use "./styles/fonts.scss";
@use "./styles/reset.scss";
@forward "./styles/fonts.scss";
@forward "./styles/reset.scss";
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
src/main.jsx
import { createRoot } from "react-dom/client";
import "./index.scss";
import App from "./App.jsx";
createRoot(document.getElementById("root")).render(<App />);
src/stores/experienceStore.js
// Global store allowing entire experience to update accodingly.
import { create } from "zustand";
export const useExperienceStore = create((set) => ({
isExperienceReady: false,
isExperienceLoading: true,
loadedChunks: 0,
totalChunks: 4,
setIsExperienceReady: () => set({ isExperienceReady: true }),
setIsExperienceLoading: (isLoading) =>
set({ isExperienceLoading: isLoading }),
incrementLoadedChunks: () =>
set((state) => ({ loadedChunks: state.loadedChunks + 1 })),
}));
src/stores/useModalStore.jsx
import { create } from "zustand";
export const useModalStore = create((set) => ({
isModalOpen: false,
modalID: "",
setModalID: (id) =>
set({
modalID: id,
}),
openModal: () =>
set({
isModalOpen: true,
}),
closeModal: () =>
set({
isModalOpen: false,
}),
}));
src/styles/fonts.scss
@font-face {
font-family: "Eagle Lake";
src: url("/fonts/EagleLake-Regular.woff2") format("woff2"),
url("/fonts/EagleLake-Regular.woff") format("woff");
font-weight: normal;
font-style: normal;
font-display: swap;
}
src/styles/reset.scss
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: "";
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
src/utils/audioSystem.js
import { Howl, Howler } from "howler";
import { getAudioFileExtension } from "./platformDetector";
const sounds = {
backgroundMusic: new Howl({
src: [`/audio/music/background_music.${getAudioFileExtension()}`],
loop: true,
html5: true,
volume: 0.8,
}),
backgroundAmbience: new Howl({
src: [`/audio/sfx/background_ambience.${getAudioFileExtension()}`],
loop: true,
html5: true,
volume: 0.9,
}),
thumpHover: new Howl({
src: [`/audio/sfx/thump_hover.${getAudioFileExtension()}`],
loop: false,
html5: true,
volume: 1,
}),
};
export const playSound = (soundId) => {
sounds[soundId]?.play();
};
export const playBackgroundMusic = () => {
sounds.backgroundMusic.play();
};
export const stopBackgroundMusic = () => {
sounds.backgroundMusic.stop();
};
export const pauseBackgroundMusic = () => {
sounds.backgroundMusic.pause();
};
export { sounds, Howler };
src/utils/platformDetector.js
export const isApplePlatform = () => {
if (typeof navigator === "undefined") return false;
return (
// iOS detection
/iPad|iPhone|iPod/.test(navigator.userAgent) ||
// Modern iPad detection (iOS 13+)
(/Macintosh/.test(navigator.userAgent) && navigator.maxTouchPoints > 1) ||
// macOS detection
(/Mac/.test(navigator.userAgent) &&
!/iPad|iPhone|iPod/.test(navigator.userAgent))
);
};
export const getAudioFileExtension = () => (isApplePlatform() ? "mp3" : "ogg");
vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import glsl from "vite-plugin-glsl";
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), glsl()],
});
Google Draco decoder notices실행 안내·자료
Google Draco 1.5.5 license and bundled notices
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
--------------------------------------------------------------------------------
Files: docs/assets/js/ASCIIMathML.js
Copyright (c) 2014 Peter Jipsen and other ASCIIMathML.js contributors
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:
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.
--------------------------------------------------------------------------------
Files: docs/assets/css/pygments/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
For more information, please refer to <http://unlicense.org>
Media credits and license evidence실행 안내·자료
README.md
# 🥰🥰 Inspo & Credits!!! 🥰🥰
Of course huge shoutout to the whole community for making things possible!! Three.js and Blender are so so amazing!!! Here are a list of the original creators for some of the things I used in this project:
- [The Adam | Blender techniques](https://www.youtube.com/watch?v=gw885XAKYiI)
- [sketching in blender | Blender techniques](https://youtu.be/JSuWu4EXVZw)
- [PolyHaven | Blender HDRI & Textures/PBR materials](https://polyhaven.com/)
- [BlenderKit | Textures/PBR materials](https://www.blendkit.com/get-blenderkit/)
- [ambientCG | Textures/PBR materials](http://ambientcg.com/)
- [Fire Shader](https://codesandbox.io/p/sandbox/3878x)
- [Background music](https://www.epidemicsound.com/track/an7vU7AM16/)
- [Thump SFX](https://pixabay.com/sound-effects/massive-thump-116359/)
- [Torch SFX](https://pixabay.com/users/freesoundsxx-47251115/)
- [Font](https://fonts.google.com/specimen/Eagle+Lake?query=eag)
- [DivyeSh's torch model](https://sketchfab.com/3d-models/torch-d47f1a85c4c846a392cc1d1afca15295)
- [distant_voices' lectern model](https://sketchfab.com/3d-models/lectern-17562d27f70b4b4a834cd778cd5e6c06)
- [Sky_Hunter's medieval brazier model](https://sketchfab.com/3d-models/medieval-brazier-cff29e533e3a4298a5d112cf7bb2558c)
- [GremorySaiyan's bird model](https://sketchfab.com/GremorySaiyan)
public/basis/README.md
## License
[Apache License 2.0](https://github.com/BinomialLLC/basis_universal/blob/master/LICENSE)
LICENSE.md실행 안내·자료
MIT License
Copyright (c) 2025 Andrew Woan
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:
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.
Bundled dependency licenses실행 안내·자료
scheduler@0.25.0 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
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:
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.
react@19.0.0 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
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:
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.
react-dom@19.0.0 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
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:
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.
three@0.173.0 — LICENSE
The MIT License
Copyright © 2010-2025 three.js authors
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:
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.
react-reconciler@0.31.0 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
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:
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.
use-sync-external-store@1.7.0 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
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:
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.
zustand@5.0.3 — LICENSE
MIT License
Copyright (c) 2019 Paul Henschel
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:
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.
suspend-react@0.1.3 — LICENSE
MIT License
Copyright (c) 2021 Paul Henschel
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:
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.
its-fine@2.0.0 — LICENSE
MIT License
Copyright (c) 2022-2025 Poimandres
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:
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.
react-use-measure@2.1.7 — LICENSE
MIT License
Copyright (c) 2019-2025 Poimandres
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:
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.
@babel/runtime@7.29.7 — LICENSE
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
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:
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.
@react-three/drei@10.0.5 — LICENSE
MIT License
Copyright (c) 2020 react-spring
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:
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.
@use-gesture/core@10.3.1 — LICENSE
Copyright (c) 2018-present Paul Henschel <drcmda@gmail.com>
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:
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.
@use-gesture/react@10.3.1 — LICENSE
Copyright (c) 2018-present Paul Henschel <drcmda@gmail.com>
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:
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.
three-stdlib@2.35.14 — LICENSE
MIT License
Copyright (c) 2021-2023 Poimandres
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:
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.
potpack@1.0.2 — LICENSE
ISC License
Copyright (c) 2018, Mapbox
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
fflate@0.6.11 — LICENSE
MIT License
Copyright (c) 2020 Arjun Barrett
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:
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.
troika-worker-utils@0.52.0 — LICENSE
MIT License
Copyright (c) 2019 ProtectWise
Copyright (c) 2021 Jason Johnston
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:
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.
webgl-sdf-generator@1.1.1 — LICENSE.txt
Copyright (c) 2021 Jason Johnston
MIT License
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:
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.
bidi-js@1.1.0 — LICENSE.txt
Copyright (c) 2021 Jason Johnston
MIT License
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:
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.
troika-three-utils@0.52.5 — LICENSE
MIT License
Copyright (c) 2019 ProtectWise
Copyright (c) 2021 Jason Johnston
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:
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.
troika-three-text@0.52.5 — LICENSE
MIT License
Copyright (c) 2019 ProtectWise
Copyright (c) 2021 Jason Johnston
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:
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.
meshline@3.3.1 — LICENSE
MIT License
Copyright (c) 2016 Jaume Sanchez
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:
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.
camera-controls@2.10.1 — LICENSE
MIT License
Copyright (c) 2017 @yomotsu
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:
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.
hls.js@1.7.3 — LICENSE
Copyright (c) 2017 Dailymotion (http://www.dailymotion.com)
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.
src/remux/mp4-generator.js and src/demux/exp-golomb.ts implementation in this project
are derived from the HLS library for video.js (https://github.com/videojs/videojs-contrib-hls)
That work is also covered by the Apache 2 License, following copyright:
Copyright (c) 2013-2015 Brightcove
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.
stats.js@0.17.0 — LICENSE
The MIT License
Copyright (c) 2009-2016 stats.js authors
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:
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.
detect-gpu@5.0.70 — LICENSE
MIT License
Copyright (c) 2020 Tim van Scherpenzeel
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:
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.
three-mesh-bvh@0.8.3 — LICENSE
MIT License
Copyright (c) 2018 Garrett Johnson
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:
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.
@monogrid/gainmap-js@3.4.0 — LICENSE
MIT License
Copyright (c) 2023 MONOGRID
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:
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.
zustand@4.5.7 — LICENSE
MIT License
Copyright (c) 2019 Paul Henschel
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:
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.
tunnel-rat@0.1.2 — LICENSE
MIT License
Copyright (c) 2022 Poimandres
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:
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.
howler@2.2.4 — LICENSE.md
Copyright (c) 2013-2020 James Simpson and GoldFire Studios, Inc.
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:
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.
normalize-wheel@1.0.1 — LICENSE
BSD License
For FixedDataTable software
Copyright (c) 2015, Facebook, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* 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.
* Neither the name Facebook 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 COPYRIGHT HOLDER 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.
BEHIND THE EXAMPLE
이 장면을 만드는 원리
카메라 이동과 모달 입력의 경계
입력·상태·출력·수명과 실제 결과를 명시합니다.
- 이 예제에서는
- Experience는 wheel·터치·마우스 입력을 targetScrollProgress에 모으고 준비 전에는 등록하지 않습니다. wheel과 touchstart는 모달 상태를 검사하며 effect 정리에서 리스너를 제거합니다.
코드와 함께 확인하기
코드에서 찾기
handleTouchMoveExperience.jsx진행 중인 스와이프는 isSwiping으로 판단해 목표 진행률을 바꿉니다.
직접 해보기
스와이프 도중 모달을 열고 닫거나 장면을 제거합니다.
살펴볼 변화모달 뒤 카메라가 계속 움직이지 않는지와 진행 중 입력의 취소·정리 경로를 확인해야 합니다.
