=y}},"es6","es3");
19 | $jscomp.polyfill("Array.prototype.copyWithin",function(h){function n(k){k=Number(k);return Infinity===k||-Infinity===k?k:k|0}return h?h:function(k,p,l){var y=this.length;k=n(k);p=n(p);l=void 0===l?y:n(l);k=0>k?Math.max(y+k,0):Math.min(k,y);p=0>p?Math.max(y+p,0):Math.min(p,y);l=0>l?Math.max(y+l,0):Math.min(l,y);if(kp;)--l in this?this[--k]=this[l]:delete this[--k];return this}},"es6","es3");
20 | $jscomp.typedArrayCopyWithin=function(h){return h?h:Array.prototype.copyWithin};$jscomp.polyfill("Int8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8ClampedArray.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
21 | $jscomp.polyfill("Uint16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float64Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
22 | var DracoDecoderModule=function(){var h="undefined"!==typeof document&&document.currentScript?document.currentScript.src:void 0;"undefined"!==typeof __filename&&(h=h||__filename);return function(n){function k(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b){if(e){var c=ia;var d=e+b;for(b=e;c[b]&&!(b>=d);)++b;if(16g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}c=d}}else c="";return c}function l(){var e=ja.buffer;a.HEAP8=W=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ia=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=Y=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function y(e){if(a.onAbort)a.onAbort(e);
24 | e="Aborted("+e+")";da(e);sa=!0;e=new WebAssembly.RuntimeError(e+". Build with -sASSERTIONS for more info.");ka(e);throw e;}function f(e){try{if(e==P&&ea)return new Uint8Array(ea);if(ma)return ma(e);throw"both async and sync fetching of the wasm failed";}catch(b){y(b)}}function q(){if(!ea&&(ta||fa)){if("function"==typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+P+"'";return e.arrayBuffer()}).catch(function(){return f(P)});
25 | if(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return f(P)})}function u(e){for(;0>2]=b};this.get_type=function(){return Y[this.ptr+4>>2]};this.set_destructor=function(b){Y[this.ptr+8>>2]=b};this.get_destructor=function(){return Y[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){W[this.ptr+
26 | 12>>0]=b?1:0};this.get_caught=function(){return 0!=W[this.ptr+12>>0]};this.set_rethrown=function(b){W[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=W[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){Y[this.ptr+
27 | 16>>2]=b};this.get_adjusted_ptr=function(){return Y[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ua(this.get_type()))return Y[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function F(){function e(){if(!la&&(la=!0,a.calledRun=!0,!sa)){va=!0;u(oa);wa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)xa.unshift(a.postRun.shift());u(xa)}}if(!(0=d?b++:2047>=d?b+=2:55296<=d&&57343>=
29 | d?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0=t){var aa=e.charCodeAt(++g);t=65536+((t&1023)<<10)|aa&1023}if(127>=t){if(c>=d)break;b[c++]=t}else{if(2047>=t){if(c+1>=d)break;b[c++]=192|t>>6}else{if(65535>=t){if(c+2>=d)break;b[c++]=224|t>>12}else{if(c+3>=d)break;b[c++]=240|t>>18;b[c++]=128|t>>12&63}b[c++]=128|t>>6&63}b[c++]=128|t&63}}b[c]=0}e=r.alloc(b,W);r.copy(b,W,e);return e}return e}function Z(e){if("object"===
30 | typeof e){var b=r.alloc(e,W);r.copy(e,W,b);return b}return e}function X(){throw"cannot construct a VoidPtr, no constructor in IDL";}function S(){this.ptr=za();w(S)[this.ptr]=this}function Q(){this.ptr=Aa();w(Q)[this.ptr]=this}function V(){this.ptr=Ba();w(V)[this.ptr]=this}function x(){this.ptr=Ca();w(x)[this.ptr]=this}function D(){this.ptr=Da();w(D)[this.ptr]=this}function G(){this.ptr=Ea();w(G)[this.ptr]=this}function H(){this.ptr=Fa();w(H)[this.ptr]=this}function E(){this.ptr=Ga();w(E)[this.ptr]=
31 | this}function T(){this.ptr=Ha();w(T)[this.ptr]=this}function C(){throw"cannot construct a Status, no constructor in IDL";}function I(){this.ptr=Ia();w(I)[this.ptr]=this}function J(){this.ptr=Ja();w(J)[this.ptr]=this}function K(){this.ptr=Ka();w(K)[this.ptr]=this}function L(){this.ptr=La();w(L)[this.ptr]=this}function M(){this.ptr=Ma();w(M)[this.ptr]=this}function N(){this.ptr=Na();w(N)[this.ptr]=this}function O(){this.ptr=Oa();w(O)[this.ptr]=this}function z(){this.ptr=Pa();w(z)[this.ptr]=this}function m(){this.ptr=
32 | Qa();w(m)[this.ptr]=this}n=void 0===n?{}:n;var a="undefined"!=typeof n?n:{},wa,ka;a.ready=new Promise(function(e,b){wa=e;ka=b});var Ra=!1,Sa=!1;a.onRuntimeInitialized=function(){Ra=!0;if(Sa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Sa=!0;if(Ra&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if("string"!==typeof e)return!1;e=e.split(".");return 2>e.length||3=e[1]?!0:0!=e[0]||10<
33 | e[1]?!1:!0};var Ta=Object.assign({},a),ta="object"==typeof window,fa="function"==typeof importScripts,Ua="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,U="";if(Ua){var Va=require("fs"),pa=require("path");U=fa?pa.dirname(U)+"/":__dirname+"/";var Wa=function(e,b){e=e.startsWith("file://")?new URL(e):pa.normalize(e);return Va.readFileSync(e,b?void 0:"utf8")};var ma=function(e){e=Wa(e,!0);e.buffer||(e=new Uint8Array(e));return e};var na=function(e,
34 | b,c){e=e.startsWith("file://")?new URL(e):pa.normalize(e);Va.readFile(e,function(d,g){d?c(d):b(g.buffer)})};1>>=0;if(2147483648=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{d=ja.buffer;try{ja.grow(g-d.byteLength+65535>>>16);l();var t=1;break a}catch(aa){}t=void 0}if(t)return!0}return!1}};(function(){function e(g,t){a.asm=g.exports;ja=a.asm.e;l();oa.unshift(a.asm.f);ba--;a.monitorRunDependencies&&a.monitorRunDependencies(ba);0==ba&&(null!==qa&&(clearInterval(qa),qa=null),ha&&(g=ha,ha=null,g()))}function b(g){e(g.instance)}
38 | function c(g){return q().then(function(t){return WebAssembly.instantiate(t,d)}).then(function(t){return t}).then(g,function(t){da("failed to asynchronously prepare wasm: "+t);y(t)})}var d={a:qd};ba++;a.monitorRunDependencies&&a.monitorRunDependencies(ba);if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da("Module.instantiateWasm callback failed with error: "+g),ka(g)}(function(){return ea||"function"!=typeof WebAssembly.instantiateStreaming||P.startsWith("data:application/octet-stream;base64,")||
39 | P.startsWith("file://")||Ua||"function"!=typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(t){da("wasm streaming compile failed: "+t);da("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(ka);return{}})();var Xa=a._emscripten_bind_VoidPtr___destroy___0=function(){return(Xa=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=
40 | function(){return(za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.i).apply(null,arguments)},Ya=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(Ya=a._emscripten_bind_DecoderBuffer_Init_2=a.asm.j).apply(null,arguments)},Za=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return(Za=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.k).apply(null,arguments)},Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=
41 | a.asm.l).apply(null,arguments)},$a=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return($a=a._emscripten_bind_AttributeTransformData_transform_type_0=a.asm.m).apply(null,arguments)},ab=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return(ab=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.n).apply(null,arguments)},Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=
42 | a.asm.o).apply(null,arguments)},bb=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(bb=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.p).apply(null,arguments)},Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.q).apply(null,arguments)},cb=a._emscripten_bind_PointAttribute_size_0=function(){return(cb=a._emscripten_bind_PointAttribute_size_0=a.asm.r).apply(null,arguments)},db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=
43 | function(){return(db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.s).apply(null,arguments)},eb=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(eb=a._emscripten_bind_PointAttribute_attribute_type_0=a.asm.t).apply(null,arguments)},fb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(fb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.u).apply(null,arguments)},gb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(gb=a._emscripten_bind_PointAttribute_num_components_0=
44 | a.asm.v).apply(null,arguments)},hb=a._emscripten_bind_PointAttribute_normalized_0=function(){return(hb=a._emscripten_bind_PointAttribute_normalized_0=a.asm.w).apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return(ib=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.x).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(jb=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.y).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_unique_id_0=
45 | function(){return(kb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.z).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute___destroy___0=function(){return(lb=a._emscripten_bind_PointAttribute___destroy___0=a.asm.A).apply(null,arguments)},Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.B).apply(null,arguments)},mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=
46 | function(){return(mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.C).apply(null,arguments)},nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return(nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.D).apply(null,arguments)},ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.E).apply(null,arguments)},pb=
47 | a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(pb=a._emscripten_bind_AttributeQuantizationTransform_range_0=a.asm.F).apply(null,arguments)},qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return(qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.G).apply(null,arguments)},Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=
48 | a.asm.H).apply(null,arguments)},rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return(rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.I).apply(null,arguments)},sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.J).apply(null,arguments)},tb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(tb=
49 | a._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.K).apply(null,arguments)},Fa=a._emscripten_bind_PointCloud_PointCloud_0=function(){return(Fa=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.L).apply(null,arguments)},ub=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(ub=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.M).apply(null,arguments)},vb=a._emscripten_bind_PointCloud_num_points_0=function(){return(vb=a._emscripten_bind_PointCloud_num_points_0=a.asm.N).apply(null,
50 | arguments)},wb=a._emscripten_bind_PointCloud___destroy___0=function(){return(wb=a._emscripten_bind_PointCloud___destroy___0=a.asm.O).apply(null,arguments)},Ga=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ga=a._emscripten_bind_Mesh_Mesh_0=a.asm.P).apply(null,arguments)},xb=a._emscripten_bind_Mesh_num_faces_0=function(){return(xb=a._emscripten_bind_Mesh_num_faces_0=a.asm.Q).apply(null,arguments)},yb=a._emscripten_bind_Mesh_num_attributes_0=function(){return(yb=a._emscripten_bind_Mesh_num_attributes_0=
51 | a.asm.R).apply(null,arguments)},zb=a._emscripten_bind_Mesh_num_points_0=function(){return(zb=a._emscripten_bind_Mesh_num_points_0=a.asm.S).apply(null,arguments)},Ab=a._emscripten_bind_Mesh___destroy___0=function(){return(Ab=a._emscripten_bind_Mesh___destroy___0=a.asm.T).apply(null,arguments)},Ha=a._emscripten_bind_Metadata_Metadata_0=function(){return(Ha=a._emscripten_bind_Metadata_Metadata_0=a.asm.U).apply(null,arguments)},Bb=a._emscripten_bind_Metadata___destroy___0=function(){return(Bb=a._emscripten_bind_Metadata___destroy___0=
52 | a.asm.V).apply(null,arguments)},Cb=a._emscripten_bind_Status_code_0=function(){return(Cb=a._emscripten_bind_Status_code_0=a.asm.W).apply(null,arguments)},Db=a._emscripten_bind_Status_ok_0=function(){return(Db=a._emscripten_bind_Status_ok_0=a.asm.X).apply(null,arguments)},Eb=a._emscripten_bind_Status_error_msg_0=function(){return(Eb=a._emscripten_bind_Status_error_msg_0=a.asm.Y).apply(null,arguments)},Fb=a._emscripten_bind_Status___destroy___0=function(){return(Fb=a._emscripten_bind_Status___destroy___0=
53 | a.asm.Z).apply(null,arguments)},Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm._).apply(null,arguments)},Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.$).apply(null,arguments)},Hb=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Hb=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.aa).apply(null,arguments)},Ib=
54 | a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Ib=a._emscripten_bind_DracoFloat32Array___destroy___0=a.asm.ba).apply(null,arguments)},Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return(Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.ca).apply(null,arguments)},Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.da).apply(null,arguments)},Kb=a._emscripten_bind_DracoInt8Array_size_0=
55 | function(){return(Kb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ea).apply(null,arguments)},Lb=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return(Lb=a._emscripten_bind_DracoInt8Array___destroy___0=a.asm.fa).apply(null,arguments)},Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ga).apply(null,arguments)},Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=
56 | a.asm.ha).apply(null,arguments)},Nb=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Nb=a._emscripten_bind_DracoUInt8Array_size_0=a.asm.ia).apply(null,arguments)},Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return(Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.ja).apply(null,arguments)},La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.ka).apply(null,arguments)},Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=
57 | function(){return(Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.la).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt16Array_size_0=function(){return(Qb=a._emscripten_bind_DracoInt16Array_size_0=a.asm.ma).apply(null,arguments)},Rb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Rb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.na).apply(null,arguments)},Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=
58 | a.asm.oa).apply(null,arguments)},Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return(Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.pa).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt16Array_size_0=function(){return(Tb=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.qa).apply(null,arguments)},Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.ra).apply(null,arguments)},Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=
59 | function(){return(Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=a.asm.sa).apply(null,arguments)},Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return(Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.ta).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt32Array_size_0=function(){return(Wb=a._emscripten_bind_DracoInt32Array_size_0=a.asm.ua).apply(null,arguments)},Xb=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(Xb=a._emscripten_bind_DracoInt32Array___destroy___0=
60 | a.asm.va).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=a.asm.wa).apply(null,arguments)},Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.xa).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(Zb=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.ya).apply(null,arguments)},$b=a._emscripten_bind_DracoUInt32Array___destroy___0=
61 | function(){return($b=a._emscripten_bind_DracoUInt32Array___destroy___0=a.asm.za).apply(null,arguments)},Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return(Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Aa).apply(null,arguments)},ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Ba).apply(null,arguments)},bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=
62 | a.asm.Ca).apply(null,arguments)},cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=a.asm.Da).apply(null,arguments)},dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ea).apply(null,arguments)},ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Fa).apply(null,
63 | arguments)},fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return(fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ga).apply(null,arguments)},gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.Ha).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(hc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.Ia).apply(null,arguments)},Qa=a._emscripten_bind_Decoder_Decoder_0=
64 | function(){return(Qa=a._emscripten_bind_Decoder_Decoder_0=a.asm.Ja).apply(null,arguments)},ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Ka).apply(null,arguments)},jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.La).apply(null,arguments)},kc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(kc=a._emscripten_bind_Decoder_GetAttributeId_2=
65 | a.asm.Ma).apply(null,arguments)},lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return(lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=a.asm.Na).apply(null,arguments)},mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Oa).apply(null,arguments)},nc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(nc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Pa).apply(null,arguments)},
66 | oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=a.asm.Qa).apply(null,arguments)},pc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(pc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Ra).apply(null,arguments)},qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Sa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=
67 | function(){return(rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Ta).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return(sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Ua).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Va).apply(null,arguments)},uc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(uc=
68 | a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm.Wa).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm.Xa).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.Ya).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=
69 | a.asm.Za).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm._a).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.$a).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=
70 | a.asm.ab).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=function(){return(Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.bb).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.cb).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=
71 | a.asm.db).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.eb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.fb).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=
72 | a.asm.gb).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return(Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.hb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.ib).apply(null,arguments)},Jc=a._emscripten_bind_Decoder___destroy___0=function(){return(Jc=a._emscripten_bind_Decoder___destroy___0=a.asm.jb).apply(null,arguments)},Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=
73 | function(){return(Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=a.asm.kb).apply(null,arguments)},Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.lb).apply(null,arguments)},Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=
74 | a.asm.mb).apply(null,arguments)},Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=function(){return(Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.nb).apply(null,arguments)},Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.ob).apply(null,arguments)},Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=
75 | a.asm.pb).apply(null,arguments)},Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.qb).apply(null,arguments)},Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.rb).apply(null,arguments)},Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=
76 | a.asm.sb).apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.tb).apply(null,arguments)},Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.ub).apply(null,arguments)},Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=
77 | a.asm.vb).apply(null,arguments)},Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.wb).apply(null,arguments)},Xc=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(Xc=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.xb).apply(null,arguments)},Yc=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(Yc=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.yb).apply(null,arguments)},Zc=
78 | a._emscripten_enum_draco_DataType_DT_UINT8=function(){return(Zc=a._emscripten_enum_draco_DataType_DT_UINT8=a.asm.zb).apply(null,arguments)},$c=a._emscripten_enum_draco_DataType_DT_INT16=function(){return($c=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Ab).apply(null,arguments)},ad=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(ad=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Bb).apply(null,arguments)},bd=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(bd=a._emscripten_enum_draco_DataType_DT_INT32=
79 | a.asm.Cb).apply(null,arguments)},cd=a._emscripten_enum_draco_DataType_DT_UINT32=function(){return(cd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Db).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(dd=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Eb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(ed=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Fb).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=
80 | function(){return(fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Gb).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Hb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(hd=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Ib).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=
81 | a.asm.Jb).apply(null,arguments)},jd=a._emscripten_enum_draco_StatusCode_OK=function(){return(jd=a._emscripten_enum_draco_StatusCode_OK=a.asm.Kb).apply(null,arguments)},kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Lb).apply(null,arguments)},ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Mb).apply(null,arguments)},md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=
82 | function(){return(md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=a.asm.Nb).apply(null,arguments)},nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Ob).apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Pb).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Qb).apply(null,arguments)};
83 | a._free=function(){return(a._free=a.asm.Rb).apply(null,arguments)};var ua=function(){return(ua=a.asm.Sb).apply(null,arguments)};a.___start_em_js=11660;a.___stop_em_js=11758;var la;ha=function b(){la||F();la||(ha=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0=r.size?(0>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;gb.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD;case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule);
117 |
--------------------------------------------------------------------------------
/public/font/AlimamaFangYuanTiVF-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/font/AlimamaFangYuanTiVF-Thin.ttf
--------------------------------------------------------------------------------
/public/font/ali.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/font/ali.woff
--------------------------------------------------------------------------------
/public/font/ali.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/font/ali.woff2
--------------------------------------------------------------------------------
/public/mesh/2024-Model3-d.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/2024-Model3-d.glb
--------------------------------------------------------------------------------
/public/mesh/Driving.fbx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/Driving.fbx
--------------------------------------------------------------------------------
/public/mesh/city_road.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/city_road.glb
--------------------------------------------------------------------------------
/public/mesh/line.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/line.glb
--------------------------------------------------------------------------------
/public/mesh/road-d.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/road-d.glb
--------------------------------------------------------------------------------
/public/mesh/road.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/road.glb
--------------------------------------------------------------------------------
/public/mesh/sm_car_data.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/sm_car_data.bin
--------------------------------------------------------------------------------
/public/mesh/sm_car_img0.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/sm_car_img0.webp
--------------------------------------------------------------------------------
/public/mesh/sm_car_img1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/sm_car_img1.webp
--------------------------------------------------------------------------------
/public/mesh/sm_car_img2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/sm_car_img2.webp
--------------------------------------------------------------------------------
/public/mesh/sm_car_img3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/sm_car_img3.webp
--------------------------------------------------------------------------------
/public/mesh/sm_car_img4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/sm_car_img4.webp
--------------------------------------------------------------------------------
/public/mesh/sm_car_img5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/sm_car_img5.webp
--------------------------------------------------------------------------------
/public/mesh/sm_car_img6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/sm_car_img6.webp
--------------------------------------------------------------------------------
/public/mesh/sm_car_img7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/sm_car_img7.webp
--------------------------------------------------------------------------------
/public/mesh/sm_car_img8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/sm_car_img8.webp
--------------------------------------------------------------------------------
/public/mesh/sm_speedup.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "asset": {
3 | "generator": "glTF-Transform v3.10.1",
4 | "version": "2.0"
5 | },
6 | "accessors": [
7 | {
8 | "type": "SCALAR",
9 | "componentType": 5123,
10 | "count": 2592,
11 | "normalized": false,
12 | "byteOffset": 0,
13 | "bufferView": 0
14 | },
15 | {
16 | "type": "VEC2",
17 | "componentType": 5126,
18 | "count": 586,
19 | "normalized": false,
20 | "byteOffset": 0,
21 | "bufferView": 1
22 | },
23 | {
24 | "type": "VEC3",
25 | "componentType": 5120,
26 | "count": 586,
27 | "normalized": true,
28 | "byteOffset": 0,
29 | "bufferView": 2
30 | },
31 | {
32 | "type": "VEC3",
33 | "componentType": 5122,
34 | "count": 586,
35 | "max": [
36 | 32767,
37 | 892,
38 | 1800
39 | ],
40 | "min": [
41 | -32767,
42 | -892,
43 | -1800
44 | ],
45 | "normalized": true,
46 | "byteOffset": 4688,
47 | "bufferView": 1
48 | }
49 | ],
50 | "bufferViews": [
51 | {
52 | "buffer": 0,
53 | "byteOffset": 0,
54 | "byteLength": 5184,
55 | "target": 34963,
56 | "extensions": {
57 | "EXT_meshopt_compression": {
58 | "buffer": 0,
59 | "byteOffset": 0,
60 | "byteLength": 986,
61 | "mode": "TRIANGLES",
62 | "byteStride": 2,
63 | "count": 2592
64 | }
65 | }
66 | },
67 | {
68 | "buffer": 0,
69 | "byteOffset": 5184,
70 | "byteLength": 2164,
71 | "target": 34962,
72 | "byteStride": 8,
73 | "extensions": {
74 | "EXT_meshopt_compression": {
75 | "buffer": 0,
76 | "byteOffset": 988,
77 | "byteLength": 5541,
78 | "mode": "ATTRIBUTES",
79 | "byteStride": 8,
80 | "count": 1172
81 | }
82 | }
83 | },
84 | {
85 | "buffer": 0,
86 | "byteOffset": 7348,
87 | "byteLength": 0,
88 | "target": 34962,
89 | "byteStride": 4,
90 | "extensions": {
91 | "EXT_meshopt_compression": {
92 | "buffer": 0,
93 | "byteOffset": 6532,
94 | "byteLength": 814,
95 | "mode": "ATTRIBUTES",
96 | "filter": "OCTAHEDRAL",
97 | "byteStride": 4,
98 | "count": 586
99 | }
100 | }
101 | }
102 | ],
103 | "buffers": [
104 | {
105 | "uri": "sm_speedup_data.bin",
106 | "byteLength": 7348
107 | }
108 | ],
109 | "meshes": [
110 | {
111 | "name": "Plane.032",
112 | "primitives": [
113 | {
114 | "attributes": {
115 | "TEXCOORD_0": 1,
116 | "NORMAL": 2,
117 | "POSITION": 3
118 | },
119 | "mode": 4,
120 | "indices": 0
121 | }
122 | ]
123 | }
124 | ],
125 | "nodes": [
126 | {
127 | "name": "加速.002",
128 | "translation": [
129 | 0,
130 | 3.334592640399933,
131 | -0.0000049173831939697266
132 | ],
133 | "scale": [
134 | 115.93985748291016,
135 | 115.93985748291016,
136 | 115.93985748291016
137 | ],
138 | "mesh": 0
139 | }
140 | ],
141 | "scenes": [
142 | {
143 | "name": "Scene",
144 | "nodes": [
145 | 0
146 | ]
147 | }
148 | ],
149 | "scene": 0,
150 | "extensionsUsed": [
151 | "KHR_mesh_quantization",
152 | "EXT_meshopt_compression"
153 | ],
154 | "extensionsRequired": [
155 | "KHR_mesh_quantization",
156 | "EXT_meshopt_compression"
157 | ]
158 | }
--------------------------------------------------------------------------------
/public/mesh/sm_speedup_data.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/sm_speedup_data.bin
--------------------------------------------------------------------------------
/public/mesh/sm_startroom.raw.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "asset": {
3 | "generator": "Khronos glTF Blender I/O v3.5.30",
4 | "version": "2.0"
5 | },
6 | "extensionsUsed": [
7 | "KHR_materials_specular",
8 | "KHR_materials_ior"
9 | ],
10 | "scene": 0,
11 | "scenes": [
12 | {
13 | "name": "Scene",
14 | "nodes": [
15 | 0,
16 | 1
17 | ]
18 | }
19 | ],
20 | "nodes": [
21 | {
22 | "mesh": 0,
23 | "name": "light.001",
24 | "rotation": [
25 | 0,
26 | -1,
27 | 0,
28 | 1.6292068494294654e-7
29 | ],
30 | "translation": [
31 | 0,
32 | 2.87214994430542,
33 | 0
34 | ]
35 | },
36 | {
37 | "mesh": 1,
38 | "name": "ReflecFloor",
39 | "rotation": [
40 | 0,
41 | -1,
42 | 0,
43 | 2.821299744937278e-7
44 | ],
45 | "translation": [
46 | 11.865281105041504,
47 | 0.006904084701091051,
48 | 0
49 | ]
50 | }
51 | ],
52 | "materials": [
53 | {
54 | "doubleSided": true,
55 | "name": "light",
56 | "pbrMetallicRoughness": {}
57 | },
58 | {
59 | "doubleSided": true,
60 | "extensions": {
61 | "KHR_materials_specular": {
62 | "specularColorFactor": [
63 | 0,
64 | 0,
65 | 0
66 | ]
67 | },
68 | "KHR_materials_ior": {
69 | "ior": 1.4500000476837158
70 | }
71 | },
72 | "name": "floor",
73 | "pbrMetallicRoughness": {
74 | "metallicFactor": 0.41709843277931213,
75 | "roughnessFactor": 0.14507770538330078
76 | }
77 | }
78 | ],
79 | "meshes": [
80 | {
81 | "name": "Plane.023",
82 | "primitives": [
83 | {
84 | "attributes": {
85 | "POSITION": 0,
86 | "TEXCOORD_0": 1,
87 | "NORMAL": 2
88 | },
89 | "indices": 3,
90 | "material": 0
91 | }
92 | ]
93 | },
94 | {
95 | "name": "Plane.020",
96 | "primitives": [
97 | {
98 | "attributes": {
99 | "POSITION": 4,
100 | "TEXCOORD_0": 5,
101 | "TEXCOORD_1": 6,
102 | "NORMAL": 7
103 | },
104 | "indices": 8,
105 | "material": 1
106 | }
107 | ]
108 | }
109 | ],
110 | "accessors": [
111 | {
112 | "bufferView": 0,
113 | "componentType": 5126,
114 | "count": 4,
115 | "max": [
116 | 2.9542529582977295,
117 | 0.05927114188671112,
118 | 1.4155222177505493
119 | ],
120 | "min": [
121 | -2.9542529582977295,
122 | 0.0592702180147171,
123 | -1.4155222177505493
124 | ],
125 | "type": "VEC3"
126 | },
127 | {
128 | "bufferView": 1,
129 | "componentType": 5126,
130 | "count": 4,
131 | "type": "VEC2"
132 | },
133 | {
134 | "bufferView": 2,
135 | "componentType": 5126,
136 | "count": 4,
137 | "type": "VEC3"
138 | },
139 | {
140 | "bufferView": 3,
141 | "componentType": 5123,
142 | "count": 6,
143 | "type": "SCALAR"
144 | },
145 | {
146 | "bufferView": 4,
147 | "componentType": 5126,
148 | "count": 8,
149 | "max": [
150 | 127.80513000488281,
151 | 0,
152 | 15.004532814025879
153 | ],
154 | "min": [
155 | -104.07454681396484,
156 | 0,
157 | -15.004525184631348
158 | ],
159 | "type": "VEC3"
160 | },
161 | {
162 | "bufferView": 5,
163 | "componentType": 5126,
164 | "count": 8,
165 | "type": "VEC2"
166 | },
167 | {
168 | "bufferView": 6,
169 | "componentType": 5126,
170 | "count": 8,
171 | "type": "VEC2"
172 | },
173 | {
174 | "bufferView": 7,
175 | "componentType": 5126,
176 | "count": 8,
177 | "type": "VEC3"
178 | },
179 | {
180 | "bufferView": 8,
181 | "componentType": 5123,
182 | "count": 18,
183 | "type": "SCALAR"
184 | }
185 | ],
186 | "bufferViews": [
187 | {
188 | "buffer": 0,
189 | "byteLength": 48,
190 | "byteOffset": 0,
191 | "target": 34962
192 | },
193 | {
194 | "buffer": 0,
195 | "byteLength": 32,
196 | "byteOffset": 48,
197 | "target": 34962
198 | },
199 | {
200 | "buffer": 0,
201 | "byteLength": 48,
202 | "byteOffset": 80,
203 | "target": 34962
204 | },
205 | {
206 | "buffer": 0,
207 | "byteLength": 12,
208 | "byteOffset": 128,
209 | "target": 34963
210 | },
211 | {
212 | "buffer": 0,
213 | "byteLength": 96,
214 | "byteOffset": 140,
215 | "target": 34962
216 | },
217 | {
218 | "buffer": 0,
219 | "byteLength": 64,
220 | "byteOffset": 236,
221 | "target": 34962
222 | },
223 | {
224 | "buffer": 0,
225 | "byteLength": 64,
226 | "byteOffset": 300,
227 | "target": 34962
228 | },
229 | {
230 | "buffer": 0,
231 | "byteLength": 96,
232 | "byteOffset": 364,
233 | "target": 34962
234 | },
235 | {
236 | "buffer": 0,
237 | "byteLength": 36,
238 | "byteOffset": 460,
239 | "target": 34963
240 | }
241 | ],
242 | "buffers": [
243 | {
244 | "uri": "sm_startroom.raw_data.bin",
245 | "byteLength": 496
246 | }
247 | ]
248 | }
--------------------------------------------------------------------------------
/public/mesh/sm_startroom.raw_data.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/mesh/sm_startroom.raw_data.bin
--------------------------------------------------------------------------------
/public/texture/decal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/texture/decal.png
--------------------------------------------------------------------------------
/public/texture/t_car_body_AO.raw.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/texture/t_car_body_AO.raw.jpg
--------------------------------------------------------------------------------
/public/texture/t_env_light.hdr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/texture/t_env_light.hdr
--------------------------------------------------------------------------------
/public/texture/t_env_night.hdr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/texture/t_env_night.hdr
--------------------------------------------------------------------------------
/public/texture/t_floor_normal.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/texture/t_floor_normal.webp
--------------------------------------------------------------------------------
/public/texture/t_floor_roughness.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/texture/t_floor_roughness.webp
--------------------------------------------------------------------------------
/public/texture/t_startroom_ao.raw.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/texture/t_startroom_ao.raw.jpg
--------------------------------------------------------------------------------
/public/texture/t_startroom_light.raw.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varrff/Model3ThreeJsExpo/5dad2fba7187303e8c2d3096b1ef36785f890e32/public/texture/t_startroom_light.raw.jpg
--------------------------------------------------------------------------------
/src/Experience/Debug.ts:
--------------------------------------------------------------------------------
1 | import * as dat from "lil-gui";
2 |
3 | export default class Debug {
4 | active: boolean;
5 | ui: dat.GUI | null;
6 | constructor() {
7 | this.active = window.location.hash === "#debug";
8 |
9 | this.ui = null;
10 |
11 | if (this.active) {
12 | this.ui = new dat.GUI();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Experience/Experience.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as THREE from "three";
3 |
4 | import World from "./World/World";
5 |
6 | import Debug from "./Debug";
7 |
8 | import Postprocessing from "./Postprocessing";
9 |
10 | import { resources } from "./resources";
11 |
12 | export default class Experience extends kokomi.Base {
13 | params;
14 | controls: kokomi.CameraControls;
15 | world: World;
16 | debug: Debug;
17 | am: kokomi.AssetManager;
18 | post: Postprocessing;
19 | constructor(sel = "#sketch") {
20 | super(sel, {
21 | autoAdaptMobile: true,
22 | });
23 |
24 | (window as any).experience = this;
25 |
26 | this.params = {
27 | speed: 0,
28 | cameraPos: {
29 | // x: 0,
30 | // y: 0.8,
31 | // z: -11,
32 | x: 0,
33 | y: 20.33,
34 | z: 0.01,
35 | },
36 | isCameraMoving: false,
37 | lightAlpha: 0,
38 | lightIntensity: 0,
39 | envIntensity: 0,
40 | envWeight: 0,
41 | reflectIntensity: 0,
42 | lightOpacity: 1,
43 | floorLerpColor: 0,
44 | carBodyEnvIntensity: 1,
45 | cameraShakeIntensity: 0,
46 | bloomLuminanceSmoothing: 1.6,
47 | bloomIntensity: 1,
48 | speedUpOpacity: 0,
49 | cameraFov: 33.4,
50 | furinaLerpColor: 0,
51 | isRushing: false,
52 | disableInteract: false,
53 | isFurina: window.location.hash === "#furina",
54 | };
55 |
56 | this.debug = new Debug();
57 |
58 | this.renderer.toneMapping = THREE.CineonToneMapping;
59 |
60 | let resourcesToLoad = resources;
61 | if (!this.params.isFurina) {
62 | resourcesToLoad = resourcesToLoad.filter(
63 | (item) => !["driving", "decal"].includes(item.name)
64 | );
65 | }
66 | console.log(resourcesToLoad);
67 |
68 | this.am = new kokomi.AssetManager(this, resourcesToLoad, {
69 | useMeshoptDecoder: true,
70 | useDracoLoader:true
71 | });
72 |
73 | const camera = this.camera as THREE.PerspectiveCamera;
74 | camera.fov = this.params.cameraFov;
75 | camera.updateProjectionMatrix();
76 | const cameraPos = new THREE.Vector3(
77 | this.params.cameraPos.x,
78 | this.params.cameraPos.y,
79 | this.params.cameraPos.z
80 | );
81 | camera.position.copy(cameraPos);
82 | const lookAt = new THREE.Vector3(0, 0.8, 0);
83 | camera.lookAt(lookAt);
84 |
85 | const controls = new kokomi.CameraControls(this);
86 | controls.controls.setTarget(lookAt.x, lookAt.y, lookAt.z);
87 | this.controls = controls;
88 |
89 | this.world = new World(this);
90 |
91 | this.post = new Postprocessing(this);
92 |
93 | // this.update(() => {
94 | // const target = new THREE.Vector3();
95 | // console.log(JSON.stringify(controls.controls.getPosition(target)));
96 | // });
97 |
98 | this.update(() => {
99 | // console.log(this.camera.position)
100 | if (this.params.isCameraMoving) {
101 | this.controls.controls.enabled = false;
102 | this.controls.controls.setPosition(
103 | this.params.cameraPos.x,
104 | this.params.cameraPos.y,
105 | this.params.cameraPos.z
106 | );
107 | } else {
108 | this.controls.controls.enabled = true;
109 | }
110 | });
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/Experience/Postprocessing.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as POSTPROCESSING from "postprocessing";
3 |
4 | import type Experience from "./Experience";
5 |
6 | export default class Postprocessing extends kokomi.Component {
7 | declare base: Experience;
8 | params;
9 | bloom: POSTPROCESSING.BloomEffect;
10 | constructor(base: Experience) {
11 | super(base);
12 |
13 | this.params = {};
14 |
15 | const composer = new POSTPROCESSING.EffectComposer(this.base.renderer, {
16 | multisampling: 8,
17 | });
18 | // @ts-ignore
19 | this.base.composer = composer;
20 |
21 | composer.addPass(
22 | new POSTPROCESSING.RenderPass(this.base.scene, this.base.camera)
23 | );
24 |
25 | const bloom = new POSTPROCESSING.BloomEffect({
26 | blendFunction: POSTPROCESSING.BlendFunction.ADD,
27 | mipmapBlur: true,
28 | luminanceThreshold: 0,
29 | luminanceSmoothing: this.base.params.bloomLuminanceSmoothing,
30 | });
31 | this.bloom = bloom;
32 |
33 | const effectPass = new POSTPROCESSING.EffectPass(this.base.camera, bloom);
34 | composer.addPass(effectPass);
35 | }
36 | setLuminanceSmoothing(value: number) {
37 | this.bloom.luminanceMaterial.smoothing = value;
38 | }
39 | setIntensity(value: number) {
40 | this.bloom.intensity = value;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Experience/Shaders/DynamicEnv/frag.glsl:
--------------------------------------------------------------------------------
1 | uniform float iTime;
2 | uniform vec3 iResolution;
3 | uniform vec4 iMouse;
4 |
5 | varying vec2 vUv;
6 |
7 | uniform sampler2D uEnvmap1;
8 | uniform sampler2D uEnvmap2;
9 | uniform float uWeight;
10 | uniform float uIntensity;
11 |
12 | void main(){
13 | vec2 uv=vUv;
14 | vec3 envmap1=texture(uEnvmap1,uv).xyz;
15 | vec3 envmap2=texture(uEnvmap2,uv).xyz;
16 | vec3 col=mix(envmap1,envmap2,uWeight)*uIntensity;
17 | gl_FragColor=vec4(col,1.);
18 | }
--------------------------------------------------------------------------------
/src/Experience/Shaders/DynamicEnv/vert.glsl:
--------------------------------------------------------------------------------
1 | uniform float iTime;
2 | uniform vec3 iResolution;
3 | uniform vec4 iMouse;
4 |
5 | varying vec2 vUv;
6 |
7 | void main(){
8 | vec3 p=position;
9 | gl_Position=vec4(p,1.);
10 |
11 | vUv=uv;
12 | }
--------------------------------------------------------------------------------
/src/Experience/Shaders/ReflecFloor/frag.glsl:
--------------------------------------------------------------------------------
1 | #include "/node_modules/lygia/lighting/fresnel.glsl"
2 | #include "/node_modules/lygia/sample/bicubic.glsl"
3 | #include "/node_modules/furina/sample/packedTexture2DLOD.glsl"
4 |
5 | uniform float iTime;
6 | uniform vec2 iResolution;
7 | uniform vec2 iMouse;
8 |
9 | varying vec2 vUv_;
10 | varying vec4 vWorldPosition;
11 |
12 | uniform vec3 uColor;
13 | uniform float uSpeed;
14 | uniform mat4 uReflectMatrix;
15 | uniform sampler2D uReflectTexture;
16 | uniform float uReflectIntensity;
17 | uniform vec2 uMipmapTextureSize;
18 |
19 | void main(){
20 | vec2 p=vUv_;
21 |
22 | vec2 surfaceNormalUv=vWorldPosition.xz;
23 | surfaceNormalUv.x+=iTime*uSpeed;
24 | vec3 surfaceNormal=texture(normalMap,surfaceNormalUv).rgb*2.-1.;
25 | surfaceNormal=surfaceNormal.rbg;
26 | surfaceNormal=normalize(surfaceNormal);
27 |
28 | vec3 viewDir=vViewPosition;
29 | float d=length(viewDir);
30 | viewDir=normalize(viewDir);
31 |
32 | vec2 distortion=surfaceNormal.xz*(.001+1./d);
33 |
34 | vec4 reflectPoint=uReflectMatrix*vWorldPosition;
35 | reflectPoint=reflectPoint/reflectPoint.w;
36 |
37 | // vec3 reflectionSample=texture(uReflectTexture,reflectPoint.xy+distortion).xyz;
38 | vec2 roughnessUv=vWorldPosition.xz;
39 | roughnessUv.x+=iTime*uSpeed;
40 | float roughnessValue=texture(roughnessMap,roughnessUv).r;
41 | roughnessValue=roughnessValue*(1.7-.7*roughnessValue);
42 | roughnessValue*=4.;
43 | float level=roughnessValue;
44 | vec2 finalUv=reflectPoint.xy+distortion;
45 | vec3 reflectionSample=packedTexture2DLOD(uReflectTexture,finalUv,level,uMipmapTextureSize).rgb;
46 | reflectionSample*=uReflectIntensity;
47 |
48 | vec3 col=uColor;
49 | // col+=reflectionSample;
50 | col*=3.;
51 | vec3 fres=fresnel(vec3(0.),vNormal,viewDir);
52 | col=mix(col,reflectionSample,fres);
53 |
54 | csm_DiffuseColor=vec4(col,1.);
55 | }
--------------------------------------------------------------------------------
/src/Experience/Shaders/ReflecFloor/vert.glsl:
--------------------------------------------------------------------------------
1 | uniform float iTime;
2 | uniform vec2 iResolution;
3 | uniform vec2 iMouse;
4 |
5 | varying vec2 vUv_;
6 | varying vec4 vWorldPosition;
7 |
8 | void main(){
9 | vec3 p=position;
10 |
11 | csm_Position=p;
12 |
13 | vUv_=uv;
14 | vWorldPosition=modelMatrix*vec4(p,1);
15 | }
--------------------------------------------------------------------------------
/src/Experience/Shaders/Speedup/frag.glsl:
--------------------------------------------------------------------------------
1 | #include "/node_modules/furina/generative/simplex.glsl"
2 | #include "/node_modules/lygia/generative/random.glsl"
3 | #include "/node_modules/lygia/math/saturate.glsl"
4 | #include "/node_modules/lygia/math/map.glsl"
5 | #include "/node_modules/lygia/color/palette.glsl"
6 |
7 | uniform float iTime;
8 | uniform vec3 iResolution;
9 | uniform vec4 iMouse;
10 |
11 | varying vec2 vUv;
12 |
13 | uniform float uSpeed;
14 | uniform float uOpacity;
15 |
16 | vec3 pos2col(vec2 i){
17 | i+=vec2(9.,0.);
18 |
19 | float r=random(i+vec2(12.,2.));
20 | float g=random(i+vec2(7.,5.));
21 | float b=random(i);
22 |
23 | vec3 col=vec3(r,g,b);
24 | return col;
25 | }
26 |
27 | vec3 colorNoise(vec2 uv){
28 | vec2 size=vec2(1.);
29 | vec2 pc=uv*size;
30 | vec2 base=floor(pc);
31 |
32 | vec3 v1=pos2col((base+vec2(0.,0.))/size);
33 | vec3 v2=pos2col((base+vec2(1.,0.))/size);
34 | vec3 v3=pos2col((base+vec2(0.,1.))/size);
35 | vec3 v4=pos2col((base+vec2(1.,1.))/size);
36 |
37 | vec2 f=fract(pc);
38 |
39 | f=smoothstep(0.,1.,f);
40 |
41 | vec3 px1=mix(v1,v2,f.x);
42 | vec3 px2=mix(v3,v4,f.x);
43 | vec3 v=mix(px1,px2,f.y);
44 | return v;
45 | }
46 |
47 | void main(){
48 | vec2 uv=vUv;
49 |
50 | vec3 col=vec3(1.);
51 |
52 | float mask=1.;
53 |
54 | vec2 noiseUv=uv;
55 | noiseUv.x+=-iTime*.5;
56 | float noiseValue=noise(noiseUv*vec2(3.,100.));
57 | mask=noiseValue;
58 | mask=map(mask,-1.,1.,0.,1.);
59 | mask=pow(saturate(mask-.1),11.);
60 | mask=smoothstep(0.,.04,mask);
61 |
62 | // col=palette(mask,vec3(.5),vec3(.5),vec3(1.),vec3(0.,.33,.67));
63 | col=colorNoise(noiseUv*vec2(10.,100.));
64 | col*=vec3(1.5,1.,400.);
65 | // mask=1.;
66 | mask*=smoothstep(.02,.5,uv.x)*smoothstep(.02,.5,1.-uv.x);
67 | mask*=smoothstep(.01,.1,uv.y)*smoothstep(.01,.1,1.-uv.y);
68 | mask*=smoothstep(1.,10.,uSpeed);
69 |
70 | gl_FragColor=vec4(col,mask*uOpacity);
71 | }
--------------------------------------------------------------------------------
/src/Experience/Shaders/Speedup/vert.glsl:
--------------------------------------------------------------------------------
1 | uniform float iTime;
2 | uniform vec3 iResolution;
3 | uniform vec4 iMouse;
4 |
5 | varying vec2 vUv;
6 |
7 | void main(){
8 | vec3 p=position;
9 | gl_Position=projectionMatrix*modelViewMatrix*vec4(p,1.);
10 |
11 | vUv=uv;
12 | }
--------------------------------------------------------------------------------
/src/Experience/Shaders/TestObject/frag.glsl:
--------------------------------------------------------------------------------
1 | uniform float iTime;
2 | uniform vec3 iResolution;
3 | uniform vec4 iMouse;
4 |
5 | varying vec2 vUv;
6 |
7 | void main(){
8 | vec2 uv=vUv;
9 | gl_FragColor=vec4(uv,0.,1.);
10 | }
--------------------------------------------------------------------------------
/src/Experience/Shaders/TestObject/vert.glsl:
--------------------------------------------------------------------------------
1 | #include "/node_modules/lygia/generative/cnoise.glsl"
2 |
3 | uniform float iTime;
4 | uniform vec3 iResolution;
5 | uniform vec4 iMouse;
6 |
7 | varying vec2 vUv;
8 |
9 | uniform float uDistort;
10 |
11 | vec3 distort(vec3 p){
12 | float noise=cnoise(p+iTime);
13 | p+=noise*normal*.3*uDistort;
14 | return p;
15 | }
16 |
17 | void main(){
18 | vec3 p=position;
19 | p=distort(p);
20 | gl_Position=projectionMatrix*modelViewMatrix*vec4(p,1.);
21 |
22 | vUv=uv;
23 | }
--------------------------------------------------------------------------------
/src/Experience/Utils/meshReflectorMaterial.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as THREE from "three";
3 |
4 | export interface MeshReflectorMaterialConfig {
5 | resolution: number;
6 | ignoreObjects: THREE.Object3D[];
7 | }
8 |
9 | class MeshReflectorMaterial extends kokomi.Component {
10 | parent: THREE.Mesh;
11 | ignoreObjects: THREE.Object3D[];
12 | _camera: THREE.PerspectiveCamera;
13 | reflectPlane: THREE.Plane;
14 | _reflectMatrix: THREE.Matrix4;
15 | _renderTexture: kokomi.FBO;
16 | mipmapper: kokomi.PackedMipMapGenerator;
17 | mirrorFBO: THREE.WebGLRenderTarget;
18 | mipmapFBO: kokomi.FBO;
19 | constructor(
20 | base: kokomi.Base,
21 | parent: THREE.Mesh,
22 | config: Partial = {}
23 | ) {
24 | super(base);
25 |
26 | this.parent = parent;
27 |
28 | let { resolution = 256, ignoreObjects = [] } = config;
29 |
30 | this.ignoreObjects = ignoreObjects;
31 |
32 | this._camera = new THREE.PerspectiveCamera();
33 | this.reflectPlane = new THREE.Plane();
34 | this._reflectMatrix = new THREE.Matrix4();
35 | this._renderTexture = new kokomi.FBO(this.base, {
36 | width: resolution,
37 | height: resolution,
38 | options: {
39 | type: THREE.UnsignedByteType,
40 | },
41 | });
42 |
43 | const mipmapper = new kokomi.PackedMipMapGenerator();
44 | this.mipmapper = mipmapper;
45 | const mirrorFBO = this._renderTexture.rt;
46 | this.mirrorFBO = mirrorFBO;
47 | const mipmapFBO = new kokomi.FBO(this.base);
48 | this.mipmapFBO = mipmapFBO;
49 | }
50 | update(): void {
51 | this.beforeRender();
52 |
53 | this.mipmapper.update(
54 | this.mirrorFBO.texture,
55 | this.mipmapFBO.rt,
56 | this.base.renderer
57 | );
58 | }
59 | beforeRender() {
60 | this.reflectPlane.set(new THREE.Vector3(0, 1, 0), 0);
61 | this.reflectPlane.applyMatrix4(this.parent.matrixWorld);
62 | // @ts-ignore
63 | this._camera.copy(this.base.camera);
64 | const r = new THREE.Vector3(0, 0, 1).clone().negate();
65 | const o = this.base.camera.getWorldPosition(new THREE.Vector3());
66 | r.reflect(this.reflectPlane.normal);
67 | const p = new THREE.Vector3();
68 | this.reflectPlane.projectPoint(o, p);
69 | const y = p.clone();
70 | y.sub(o), y.add(p), this._camera.position.copy(y);
71 | const d = new THREE.Vector3(0, 0, -1);
72 | d.applyQuaternion(
73 | this.base.camera.getWorldQuaternion(new THREE.Quaternion())
74 | );
75 | d.add(o);
76 | const E = new THREE.Vector3();
77 | this.parent.getWorldPosition(E);
78 | E.sub(d);
79 | E.reflect(this.reflectPlane.normal).negate();
80 | E.add(this.parent.getWorldPosition(new THREE.Vector3()));
81 | this._camera.up.set(0, 1, 0);
82 | this._camera.applyQuaternion(
83 | this.base.camera.getWorldQuaternion(new THREE.Quaternion())
84 | );
85 | this._camera.up.reflect(this.reflectPlane.normal);
86 | this._camera.lookAt(E);
87 | this._camera.updateMatrixWorld();
88 | const L = new THREE.Matrix4();
89 | L.set(0.5, 0, 0, 0.5, 0, 0.5, 0, 0.5, 0, 0, 0.5, 0.5, 0, 0, 0, 1);
90 | L.multiply(this._camera.projectionMatrix);
91 | L.multiply(this._camera.matrixWorldInverse);
92 | this._reflectMatrix.copy(L);
93 | this.reflectPlane.applyMatrix4(this._camera.matrixWorldInverse);
94 | const k = new THREE.Vector4(
95 | this.reflectPlane.normal.x,
96 | this.reflectPlane.normal.y,
97 | this.reflectPlane.normal.z,
98 | this.reflectPlane.constant
99 | );
100 | const X = this._camera.projectionMatrix;
101 | const $ = new THREE.Vector4();
102 | $.x = (Math.sign(k.x) + X.elements[8]) / X.elements[0];
103 | $.y = (Math.sign(k.y) + X.elements[9]) / X.elements[5];
104 | $.z = -1;
105 | $.w = (1 + X.elements[10]) / X.elements[14];
106 | k.multiplyScalar(2 / k.dot($));
107 | X.elements[2] = k.x;
108 | X.elements[6] = k.y;
109 | X.elements[10] = k.z + 1;
110 | X.elements[14] = k.w;
111 | const Z = this.base.renderer.getRenderTarget();
112 | this.base.renderer.setRenderTarget(this._renderTexture.rt);
113 | this.base.renderer.state.buffers.depth.setMask(true);
114 | this.base.renderer.autoClear === false && this.base.renderer.clear();
115 | this.ignoreObjects.forEach((be) => (be.visible = false));
116 | this.base.renderer.render(this.base.scene, this._camera);
117 | this.ignoreObjects.forEach((be) => (be.visible = true));
118 | this.base.renderer.setRenderTarget(Z);
119 | }
120 | }
121 |
122 | export { MeshReflectorMaterial };
123 |
--------------------------------------------------------------------------------
/src/Experience/World/CameraShake.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as THREE from "three";
3 | import gsap from "gsap";
4 | import * as STDLIB from "three-stdlib";
5 | import { createNoise2D } from "simplex-noise";
6 |
7 | import type Experience from "../Experience";
8 |
9 | // 创建一个 2D simplex 噪声对象
10 | const noise2d = createNoise2D();
11 |
12 | // Fractal Brownian Motion(分形布朗运动)函数
13 | const fbm = ({
14 | octave = 3, // 八度数
15 | frequency = 2, // 频率
16 | amplitude = 0.5, // 振幅
17 | lacunarity = 2, // 空隙度
18 | persistance = 0.5, // 持续度
19 | } = {}) => {
20 | let value = 0;
21 | for (let i = 0; i < octave; i++) {
22 | // 根据参数计算噪声值
23 | const noiseValue = noise2d(frequency, frequency);
24 | value += noiseValue * amplitude;
25 | frequency *= lacunarity;
26 | amplitude *= persistance;
27 | }
28 | return value;
29 | };
30 |
31 | // 相机震动配置接口
32 | export interface CameraShakeConfig {
33 | intensity: number; // 震动强度
34 | }
35 |
36 | // 相机震动类
37 | export default class CameraShake extends kokomi.Component {
38 | declare base: Experience;
39 | tweenedPosOffset: THREE.Vector3; // 缓动的位置偏移向量
40 | intensity: number; // 震动强度
41 | constructor(base: Experience, config: Partial = {}) {
42 | super(base);
43 |
44 | // 解构配置参数,设置默认值
45 | const { intensity = 1 } = config;
46 | this.intensity = intensity;
47 |
48 | // 创建缓动的位置偏移向量
49 | const tweenedPosOffset = new THREE.Vector3(0, 0, 0);
50 | this.tweenedPosOffset = tweenedPosOffset;
51 | }
52 |
53 | // 更新函数
54 | update(): void {
55 | const t = this.base.clock.elapsedTime; // 获取当前时间
56 | // 计算随机位置偏移
57 | const posOffset = new THREE.Vector3(
58 | fbm({
59 | frequency: t * 0.5 + THREE.MathUtils.randFloat(-10000, 0), // 在 x 方向上的噪声
60 | amplitude: 2, // 振幅
61 | }),
62 | fbm({
63 | frequency: t * 0.5 + THREE.MathUtils.randFloat(-10000, 0), // 在 y 方向上的噪声
64 | amplitude: 2, // 振幅
65 | }),
66 | fbm({
67 | frequency: t * 0.5 + THREE.MathUtils.randFloat(-10000, 0), // 在 z 方向上的噪声
68 | amplitude: 2, // 振幅
69 | })
70 | );
71 | posOffset.multiplyScalar(0.1 * this.intensity); // 乘以震动强度
72 | // 使用 GSAP 库创建动画,平滑过渡相机位置
73 | gsap.to(this.tweenedPosOffset, {
74 | x: posOffset.x,
75 | y: posOffset.y,
76 | z: posOffset.z,
77 | duration: 1.2, // 动画持续时间
78 | });
79 |
80 | // 将震动位置偏移应用到相机位置
81 | this.base.camera.position.add(this.tweenedPosOffset);
82 | }
83 |
84 | // 设置震动强度
85 | setIntensity(value: number) {
86 | this.intensity = value;
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/Experience/World/Car.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as THREE from "three";
3 | import * as STDLIB from "three-stdlib";
4 |
5 | import type Experience from "../Experience";
6 |
7 | // 定义 Car 类,继承自 kokomi.Component
8 | export default class Car extends kokomi.Component {
9 | declare base: Experience;
10 | model: STDLIB.GLTF; // 模型
11 | modelParts: THREE.Object3D[]; // 模型部件
12 | bodyMat!: THREE.MeshStandardMaterial; // 车身材质
13 | wheelModel!: THREE.Group; // 轮胎模型
14 |
15 | // 构造函数,接受一个 Experience 实例作为参数
16 | constructor(base: Experience) {
17 | super(base);
18 |
19 | // 从资源管理器中获取汽车模型
20 | const model = this.base.am.items["sm_car"] as STDLIB.GLTF;
21 | this.model = model;
22 | this.model.scene.position.y = 0.75;
23 | this.model.scene.rotation.y += -(Math.PI / 2); // 90 度用弧度表示为 Math.PI / 2
24 | // this.model.scene.scale.set(0.01, 0.01, 0.01);
25 |
26 |
27 | // 扁平化模型,获取模型的所有部件
28 | const modelParts = kokomi.flatModel(model.scene);
29 | kokomi.printModel(modelParts);
30 | this.modelParts = modelParts;
31 |
32 | // // 处理模型
33 | // this.handleModel();
34 | }
35 |
36 | // 添加已存在的模型到容器中
37 | addExisting() {
38 | this.container.add(this.model.scene);
39 | }
40 |
41 | // 更新函数
42 | update(): void {
43 | // 根据速度旋转车轮模型
44 | this.wheelModel?.children.forEach((item) => {
45 | item.rotateZ(-this.base.params.speed * 0.03);
46 | });
47 | }
48 |
49 | // 处理模型函数
50 | handleModel() {
51 | // 获取车身部件
52 | const body = this.modelParts[2] as THREE.Mesh;
53 | // 获取车身材质
54 | const bodyMat = body.material as THREE.MeshStandardMaterial;
55 | this.bodyMat = bodyMat;
56 | // 设置车身颜色
57 | bodyMat.color = new THREE.Color("#26d6e9");
58 | // 如果是 Furina 模式,则更改车身材质和贴图
59 | if (this.base.params.isFurina) {
60 | bodyMat.color = new THREE.Color("white");
61 | bodyMat.map = this.base.am.items["decal"];
62 | }
63 |
64 | // 遍历模型的所有部件
65 | // @ts-ignore
66 | this.modelParts.forEach((item: THREE.Mesh) => {
67 | // 如果部件是网格
68 | if (item.isMesh) {
69 | // 设置环境光遮蔽贴图
70 | const mat = item.material as THREE.MeshStandardMaterial;
71 | mat.aoMap = this.base.am.items["ut_car_body_ao"];
72 | }
73 | });
74 |
75 | // 获取车轮模型
76 | const Wheel = this.modelParts[35] as THREE.Group;
77 | this.wheelModel = Wheel;
78 | }
79 |
80 | // 设置车身环境贴图强度
81 | setBodyEnvmapIntensity(value: number) {
82 | if (this.bodyMat) {
83 | this.bodyMat.envMapIntensity = value;
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/Experience/World/City.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as STDLIB from "three-stdlib";
3 | // import { clone } from "three/examples/jsm/utils/SkeletonUtils";
4 | import gsap from "gsap";
5 | import { Mesh, MeshStandardMaterial } from "three";
6 |
7 | import type Experience from "../Experience";
8 | import * as THREE from "three";
9 |
10 | export default class City extends kokomi.Component {
11 | declare base: Experience;
12 | model: any;
13 | isRun!: boolean;
14 | road!: any;
15 | controlPoints: THREE.Vector3[];
16 | animationLoop: any;
17 | group:any;
18 |
19 | constructor(base: Experience) {
20 | super(base);
21 | this.controlPoints = [];
22 | }
23 |
24 | addExisting() {
25 | this.isRun = true;
26 | const model = this.base.am.items["city_road"] as STDLIB.GLTF;
27 | this.model = model;
28 | this.model.scene.position.y = -3;
29 | this.model.scene.position.z = 51.5;
30 | this.model.scene.position.x = 10;
31 | this.model.scene.scale.set(2, 2, 2);
32 | this.container.add(this.model.scene);
33 | this.createRoad();
34 | this.runRoad();
35 | }
36 |
37 | setCar() {
38 | const model = this.base.am.items["sm_car"] as STDLIB.GLTF;
39 | }
40 |
41 | createRoad() {
42 | const points = [
43 | new THREE.Vector3(0, 0, 0),
44 | new THREE.Vector3(0, 0, 5),
45 | new THREE.Vector3(0, 0, 10),
46 | new THREE.Vector3(-20, 0, 10),
47 | ];
48 | this.controlPoints = points;
49 |
50 | this.updateRoadGeometry();
51 |
52 | // 使用GSAP进行过渡动画
53 | gsap.to(this.road.material, {
54 | duration: 2,
55 | opacity: 1,
56 | onComplete: () => {
57 |
58 | },
59 | });
60 | }
61 |
62 | updateRoadGeometry() {
63 | if (!this.controlPoints || this.controlPoints.length === 0) {
64 |
65 | return;
66 | }
67 |
68 | const curve = new THREE.CatmullRomCurve3(this.controlPoints);
69 | const tubularSegments = 100;
70 | const radius = 1;
71 | const radialSegments = 8;
72 | const closed = false;
73 |
74 | const geometry = new THREE.TubeGeometry(
75 | curve,
76 | tubularSegments,
77 | radius,
78 | radialSegments,
79 | closed
80 | );
81 |
82 | const positionAttribute = geometry.attributes.position;
83 | const vertex = new THREE.Vector3();
84 |
85 | for (let i = 0; i < positionAttribute.count; i++) {
86 | vertex.fromBufferAttribute(positionAttribute, i);
87 | vertex.y *= 0.1;
88 | positionAttribute.setXYZ(i, vertex.x, vertex.y, vertex.z);
89 | }
90 |
91 | const canvas = document.createElement("canvas");
92 | canvas.width = 256;
93 | canvas.height = 1;
94 |
95 | const context = canvas.getContext("2d") as CanvasRenderingContext2D;
96 | const gradient = context.createLinearGradient(0, 0, canvas.width, 0);
97 | gradient.addColorStop(0, "#2076ff");
98 | gradient.addColorStop(1, "#abcbff");
99 |
100 | context.fillStyle = gradient;
101 | context.fillRect(0, 0, canvas.width, canvas.height);
102 |
103 | const gradientTexture = new THREE.CanvasTexture(canvas);
104 |
105 | const material = new THREE.MeshBasicMaterial({
106 | map: gradientTexture,
107 | side: THREE.DoubleSide,
108 | opacity: 0,
109 | transparent: true,
110 | });
111 |
112 | if (this.road) {
113 | this.road.geometry.dispose();
114 | this.road.geometry = geometry;
115 | } else {
116 | this.road = new THREE.Mesh(geometry, material);
117 | this.road.rotation.y = Math.PI / 2;
118 | this.container.add(this.road);
119 | }
120 | }
121 |
122 | updateControlPoint(
123 | index: number,
124 | xValue: number | null = null,
125 | xStep: number | null = 0,
126 | zValue: number | null = null,
127 | zStep: number | null = 0
128 | ): void {
129 | if (!this.controlPoints || this.controlPoints.length <= index) {
130 | console.error(`Control point at index ${index} is not defined.`);
131 | return;
132 | }
133 |
134 | const point = this.controlPoints[index].clone();
135 |
136 | if (xValue !== null && xStep !== null) {
137 | if (point.x < xValue) {
138 | point.setX(point.x + xStep);
139 | } else if (point.x > xValue) {
140 | point.setX(point.x - xStep);
141 | }
142 | }
143 |
144 | if (zValue !== null && zStep !== null) {
145 | if (point.z >= zValue) {
146 | point.setZ(point.z - zStep);
147 | } else if (point.z < zValue) {
148 | point.setZ(point.z + zStep);
149 | }
150 | }
151 |
152 | this.controlPoints[index] = point;
153 | this.updateRoadGeometry();
154 | }
155 |
156 | runRoad() {
157 | const t1 = gsap.to(this.model.scene.position, {
158 | x: 1,
159 | duration: 3,
160 | delay: 2,
161 | onComplete: () => {},
162 | });
163 | const t2 = gsap.to(this.model.scene.position, {
164 | z: 52,
165 | duration: 3,
166 | delay: 2.5,
167 | onComplete: () => {
168 | t4();
169 | console.log("Road added to scene with smooth transition.");
170 | },
171 | });
172 |
173 | const car = this.base.am.items["sm_car"] as STDLIB.GLTF;
174 | const group = new THREE.Group();
175 | group.add(this.model.scene);
176 | group.position.copy(car.scene.position);
177 | this.group = group
178 | this.container.add(this.group);
179 | const t3LINE = () => {
180 | const animateControlPoints = () => {
181 | this.updateControlPoint(3, 0, 0.03, 15, 0.055); //-20, 0, 10
182 | // this.updateControlPoint(4, 0, 0.007, null,null);
183 | // this.animationLoop = requestAnimationFrame(animateControlPoints);
184 | requestAnimationFrame(animateControlPoints);
185 | };
186 |
187 | animateControlPoints();
188 | };
189 | setTimeout(() => {
190 | t3LINE();
191 | }, 1000);
192 | const t3 = gsap.to(group.rotation, {
193 | y: Math.PI / 2,
194 | duration: 6,
195 | delay: 2.5,
196 | onComplete: () => {
197 |
198 | },
199 | });
200 | const t4 = () => {
201 | gsap.to(this.model.scene.position, {
202 | x: 0,
203 | z: 45,
204 | duration: 3,
205 | onComplete: () => {
206 | t5();
207 | cancelAnimationFrame(this.animationLoop);
208 |
209 | },
210 | });
211 | };
212 |
213 | const t5 = () => {
214 | gsap.to(this.model.scene.position, {
215 | x: 0,
216 | z: 10,
217 | duration: 10,
218 | onComplete: () => {
219 |
220 | },
221 | });
222 | };
223 | }
224 | removeAllModelsAndAnimations() {
225 | this.container.remove(this.road);
226 | this.model.scene.position.y = -3;
227 | this.model.scene.position.z = 51.5;
228 | this.model.scene.position.x = 10;
229 | // 取消动画循环
230 | if (this.animationLoop) {
231 | // cancelAnimationFrame(this.animationLoop);
232 | this.animationLoop = null;
233 | }
234 | // 移除道路模型
235 | if (this.model) {
236 | this.container.remove(this.model.scene);
237 | // 释放资源
238 | this.model = null;
239 | }
240 | this.group
241 | if ( this.group) {
242 | this.container.remove( this.group);
243 | // 释放资源
244 | this.group = null;
245 | }
246 | // 移除 tube 对象
247 | if (this.road) {
248 | this.container.remove(this.road);
249 | // 释放资源
250 | this.road.geometry.dispose();
251 | this.road.material.dispose();
252 | this.road = null;
253 | }
254 | }
255 | playAuto() {
256 | const bgm = new Howl({
257 | src: "audio/roadBgm.mp3",
258 | loop: false,
259 | });
260 | bgm.play();
261 | }
262 | }
263 |
--------------------------------------------------------------------------------
/src/Experience/World/DynamicEnv.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as THREE from "three";
3 | import gsap from "gsap";
4 |
5 | import type Experience from "../Experience";
6 |
7 | import dynamicEnvVertexShader from "../Shaders/DynamicEnv/vert.glsl";
8 | import dynamicEnvFragmentShader from "../Shaders/DynamicEnv/frag.glsl";
9 |
10 | export interface DynamicEnvConfig {
11 | envmap1: THREE.Texture;
12 | envmap2: THREE.Texture;
13 | }
14 |
15 | const t1 = gsap.timeline();
16 |
17 | export default class DynamicEnv extends kokomi.Component {
18 | declare base: Experience;
19 | fbo: kokomi.FBO;
20 | material: THREE.ShaderMaterial;
21 | quad: kokomi.FullScreenQuad;
22 | constructor(base: Experience, config: Partial = {}) {
23 | super(base);
24 |
25 | const { envmap1, envmap2 } = config;
26 |
27 | const envData = envmap1?.source.data;
28 |
29 | const fbo = new kokomi.FBO(this.base, {
30 | width: envData.width,
31 | height: envData.height,
32 | });
33 | this.fbo = fbo;
34 |
35 | this.envmap.mapping = THREE.CubeUVReflectionMapping;
36 |
37 | const material = new THREE.ShaderMaterial({
38 | vertexShader: dynamicEnvVertexShader,
39 | fragmentShader: dynamicEnvFragmentShader,
40 | uniforms: {
41 | uEnvmap1: {
42 | value: envmap1,
43 | },
44 | uEnvmap2: {
45 | value: envmap2,
46 | },
47 | uWeight: {
48 | value: 0,
49 | },
50 | uIntensity: {
51 | value: 1,
52 | },
53 | },
54 | });
55 | this.material = material;
56 |
57 | const quad = new kokomi.FullScreenQuad(material);
58 | this.quad = quad;
59 | }
60 | update() {
61 | this.base.renderer.setRenderTarget(this.fbo.rt);
62 | this.quad.render(this.base.renderer);
63 | this.base.renderer.setRenderTarget(null);
64 | }
65 | get envmap() {
66 | return this.fbo.rt.texture;
67 | }
68 | setWeight(value: number) {
69 | this.material.uniforms.uWeight.value = value;
70 | }
71 | setIntensity(value: number) {
72 | this.material.uniforms.uIntensity.value = value;
73 | }
74 | lerpWeight(value: number, duration: number) {
75 | // t1.timeScale(0.2);
76 | t1.to(this.material.uniforms.uWeight, {
77 | value,
78 | duration,
79 | ease: "power2.out",
80 | });
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Experience/World/Furina.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as THREE from "three";
3 |
4 | import type Experience from "../Experience";
5 |
6 | export default class Furina extends kokomi.Component {
7 | declare base: Experience;
8 | model: THREE.Group;
9 | realModel: THREE.Group;
10 | modelParts: THREE.Object3D[];
11 | mixer: THREE.AnimationMixer;
12 | actions: Record;
13 | currentAction: THREE.AnimationAction | null;
14 | isPaused: boolean;
15 | constructor(base: Experience) {
16 | super(base);
17 |
18 | const model = new THREE.Group();
19 | this.model = model;
20 |
21 | // const realModel = this.base.am.items["furina"] as THREE.Group;
22 | const realModel = this.base.am.items["driving"] as THREE.Group;
23 | this.realModel = realModel;
24 |
25 | this.model.add(this.realModel);
26 |
27 | const modelParts = kokomi.flatModel(this.realModel);
28 | // kokomi.printModel(modelParts);
29 | this.modelParts = modelParts;
30 |
31 | this.handleModel();
32 |
33 | const mixer = new THREE.AnimationMixer(this.model);
34 | this.mixer = mixer;
35 |
36 | this.actions = {};
37 |
38 | this.currentAction = null;
39 |
40 | this.addAction("driving", "driving");
41 |
42 | this.playAction("driving");
43 |
44 | this.mixer.update(1);
45 | this.isPaused = true;
46 | }
47 | addExisting() {
48 | this.container.add(this.model);
49 | }
50 | update(): void {
51 | if (this.isPaused) {
52 | return;
53 | }
54 |
55 | this.mixer.update(this.base.clock.deltaTime);
56 | }
57 | handleModel() {
58 | this.model.scale.setScalar(0.074);
59 |
60 | this.model.rotation.y = Math.PI * 0.5;
61 |
62 | this.model.position.set(0.225, 0.15, -0.4);
63 |
64 | // @ts-ignore
65 | this.modelParts.forEach((item: THREE.Mesh) => {
66 | if (item.isMesh) {
67 | if ((item.material as THREE.MeshPhongMaterial).isMeshPhongMaterial) {
68 | const newMat = new THREE.MeshBasicMaterial({
69 | transparent: true,
70 | // @ts-ignore
71 | map: item.material.map || null,
72 | // color: new THREE.Color("#666666"),
73 | });
74 | item.material = newMat;
75 | }
76 | }
77 | });
78 | }
79 | addAction(assetName: string, name: string) {
80 | const animation = (this.base.am?.items[assetName] as THREE.Group)
81 | .animations[0];
82 | const action = this.mixer.clipAction(animation);
83 | this.actions[name] = action;
84 | }
85 | playAction(name: string) {
86 | if (this.currentAction) {
87 | this.currentAction.fadeOut(0.5);
88 | }
89 | const action = this.actions[name];
90 | action.weight = 1;
91 | action.reset().fadeIn(0.5).play();
92 | this.currentAction = action;
93 | return action;
94 | }
95 | setColor(color: THREE.Color) {
96 | // @ts-ignore
97 | this.modelParts.forEach((item: THREE.Mesh) => {
98 | if (item.isMesh) {
99 | const mat = item.material as THREE.MeshBasicMaterial;
100 | mat.color.set(color);
101 | }
102 | });
103 | }
104 | pause() {
105 | this.isPaused = true;
106 | }
107 | drive() {
108 | this.isPaused = false;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/Experience/World/Road.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as STDLIB from "three-stdlib";
3 | // @ts-ignore
4 | import { clone } from "three/examples/jsm/utils/SkeletonUtils";
5 | import gsap from "gsap";
6 | import { Mesh, MeshStandardMaterial } from "three";
7 | import { Howl } from "howler";
8 | import type Experience from "../Experience";
9 | import * as THREE from "three";
10 |
11 | export default class Road extends kokomi.Component {
12 | declare base: Experience;
13 | model: any;
14 | isRun!: boolean;
15 | carShield: Mesh | null = null;
16 | lineMesh: Mesh | null = null;
17 | controlPoints!: Array;
18 | tube: any;
19 | animationLoop: any;
20 | newCar: any;
21 | AnimationFrame: any;
22 |
23 | constructor(base: Experience) {
24 | super(base);
25 | }
26 |
27 | addExisting() {
28 | this.isRun = true;
29 | const model = this.base.am.items["road"] as STDLIB.GLTF;
30 | this.model = model;
31 | this.model.scene.position.z = -2.5;
32 | this.model.scene.scale.set(3, 3, 3);
33 | this.container.add(this.model.scene);
34 | this.run();
35 | }
36 |
37 | run() {
38 | this.carRun();
39 | const animate = () => {
40 | if (!this.isRun) {
41 | return; // 如果标志为false,停止动画
42 | }
43 | requestAnimationFrame(animate);
44 |
45 | // 模型移动速度
46 | const moveSpeed = 0.1;
47 |
48 | this.model.scene.position.x -= moveSpeed;
49 | };
50 |
51 | // 初始调用animate,启动递归动画循环
52 | animate();
53 | }
54 |
55 | carRun() {
56 | // 确保每次都是新的克隆对象
57 | const model = this.base.am.items["sm_car"] as STDLIB.GLTF;
58 | this.newCar = clone(model.scene);
59 | console.log(model)
60 |
61 | // 创建一个新的标准材质,支持光照和反射
62 | const newMaterial = new MeshStandardMaterial({
63 | color: "#8a8886",
64 | metalness: 0.8,
65 | roughness: 0.8,
66 | }); // 这里设置你想要的颜色和材质属性
67 |
68 | // 遍历模型并替换所有材质
69 | this.newCar.traverse(
70 | (child: { isMesh: any; material: MeshStandardMaterial }) => {
71 | if (child.isMesh) {
72 | child.material = newMaterial;
73 | }
74 | }
75 | );
76 |
77 | this.createShield();
78 |
79 | this.container.add(this.newCar);
80 |
81 | // 动画参数
82 | const initialX = -1;
83 | const initialZ = 5;
84 | const forwardDistance = 3; // 向前移动的距离
85 | const sideDistance = 5; // 超车时横向移动的距离
86 | const duration = 5000; // 动画持续时间(毫秒)
87 | const pauseDuration = 1500; // 停顿时间(毫秒)
88 |
89 | let startTime: number | null = null;
90 |
91 | // 动画函数
92 | const animateCar = (time: number) => {
93 | if (!startTime) startTime = time;
94 | const elapsedTime = time - startTime;
95 |
96 | if (elapsedTime < duration / 2) {
97 | // 前半段,向前移动
98 | const progress = elapsedTime / (duration / 2);
99 | this.newCar.position.x = initialX + progress * forwardDistance;
100 | this.newCar.position.z = initialZ;
101 | } else if (elapsedTime < duration) {
102 | // 后半段,向斜前方移动
103 | const progress = (elapsedTime - duration / 2) / (duration / 2);
104 | this.newCar.position.x =
105 | initialX + forwardDistance + progress * sideDistance;
106 | this.newCar.position.z = initialZ - progress * sideDistance;
107 | } else if (elapsedTime < duration + pauseDuration) {
108 | // 停顿时间
109 | // 不进行任何操作,保持车辆静止
110 | } else {
111 | // 加速向前移动
112 | const progress = (elapsedTime - duration - pauseDuration) / 2000; // 加速时间为2秒
113 | this.newCar.position.x =
114 | initialX + forwardDistance + sideDistance + progress * 10; // 加速移动的距离为10
115 | this.newCar.position.z = initialZ - sideDistance;
116 | if (this.newCar.position.x > 20) {
117 | // 超出场景范围,移除车辆
118 | this.container.remove(this.newCar);
119 | const father = document.querySelector(".line") as Element;
120 | //@ts-ignore
121 | father.style.display = "flex"; // 设置元素 display 为 none
122 | cancelAnimationFrame(this.AnimationFrame); // 结束动画
123 | }
124 | }
125 |
126 | this.AnimationFrame = requestAnimationFrame(animateCar);
127 | };
128 |
129 | // 启动动画
130 | requestAnimationFrame(animateCar);
131 | }
132 |
133 | createShield() {
134 | const controlPoints = [
135 | new THREE.Vector3(3, 1, 1),
136 | new THREE.Vector3(2.96, 1, 1.5),
137 | new THREE.Vector3(3, 1, 1.7),
138 | new THREE.Vector3(2.65, 1, 2.7),
139 | new THREE.Vector3(2, 1, 2.65),
140 | new THREE.Vector3(1, 1, 2.7),
141 | new THREE.Vector3(0.5, 1, 2.8),
142 | ];
143 | this.controlPoints = controlPoints;
144 | // Create a Catmull-Rom spline curve
145 | const curve = new THREE.CatmullRomCurve3(this.controlPoints);
146 |
147 | // Create TubeGeometry from the curve
148 | const tubeGeometry = new THREE.TubeGeometry(curve, 100, 0.025, 32, false);
149 |
150 | // 创建画布
151 | const canvas = document.createElement("canvas");
152 | canvas.width = 256;
153 | canvas.height = 1;
154 |
155 | // 获取画布上下文
156 | const context = canvas.getContext("2d") as CanvasRenderingContext2D;
157 |
158 | // 创建线性渐变
159 | const gradient = context.createLinearGradient(0, 0, canvas.width, 0);
160 | gradient.addColorStop(0, "#ffA500"); // 橙色
161 | gradient.addColorStop(1, "#ff0000"); // 红色
162 |
163 | // 填充画布
164 | context.fillStyle = gradient;
165 | context.fillRect(0, 0, canvas.width, canvas.height);
166 |
167 | // 创建渐变纹理
168 | const gradientTexture = new THREE.CanvasTexture(canvas);
169 |
170 | // 创建材质
171 | const tubeMaterial = new THREE.MeshBasicMaterial({
172 | map: gradientTexture,
173 | side: THREE.DoubleSide, // 如果需要双面渲染,可以添加这一行
174 | });
175 | // 设置初始透明度为 0
176 | tubeMaterial.opacity = 0;
177 | tubeMaterial.transparent = true;
178 | const tube = new THREE.Mesh(tubeGeometry, tubeMaterial);
179 | // Position the tube
180 | tube.position.z = -1.2;
181 | tube.position.y = -0.5;
182 | this.tube = tube;
183 | this.container.add(this.tube);
184 |
185 | // 定义动画持续时间
186 | const duration = 2; // 5 秒
187 |
188 | setTimeout(() => {
189 | let count = 0;
190 | const intervalId = setInterval(() => {
191 | // 执行的任务
192 | console.log("任务执行");
193 | this.playAuto();
194 | // 增加计数器
195 | count++;
196 |
197 | // 如果计数器达到3次,则清除定时器
198 | if (count === 2) {
199 | clearInterval(intervalId); // 清除定时器
200 | }
201 | }, 1000); // 每秒执行一次
202 | }, 500);
203 |
204 | gsap.to(tubeMaterial, {
205 | duration: 0.5,
206 | opacity: 1, // 设置目标透明度为 1,即完全不透明
207 | delay: 2.6, // 延迟 1 秒后开始播放动画
208 | ease: "power1.inOut", // 缓动函数,可以根据需要进行调整
209 | onComplete: () => {
210 | // 启动动画函数
211 | animateControlPoints();
212 |
213 | // 创建一个新的标准材质,支持光照和反射
214 | const newMaterial = new MeshStandardMaterial({
215 | color: "#b00000",
216 | metalness: 0.8,
217 | roughness: 0.8,
218 | }); // 这里设置你想要的颜色和材质属性
219 |
220 | // 遍历模型并替换所有材质
221 | this.newCar.traverse(
222 | (child: { isMesh: any; material: MeshStandardMaterial }) => {
223 | if (child.isMesh) {
224 | child.material = newMaterial;
225 | }
226 | }
227 | );
228 |
229 | let countA = 0;
230 | const intervalIdA = setInterval(() => {
231 | // 执行的任务
232 | console.log("任务执行2");
233 | this.playAuto();
234 | // 增加计数器
235 | countA++;
236 |
237 | // 如果计数器达到3次,则清除定时器
238 | if (countA === 5) {
239 | clearInterval(intervalIdA);
240 | }
241 | }, 500); // 每秒执行一次
242 |
243 | gsap.to(this.tube.position, {
244 | duration: 1,
245 | x: 0.2, // 设置 x 轴的目标位置
246 | z: -1, // 设置 z 轴的目标位置
247 | ease: "power1.inOut", // 缓动函数,可以根据需要进行调整
248 | delay: 3.1, // 延迟 1 秒后开始播放动画
249 | onComplete: () => {
250 | // 在动画完成后执行的操作
251 |
252 | // 创建一个新的标准材质,支持光照和反射
253 | const newMaterial = new MeshStandardMaterial({
254 | color: "#8a8886",
255 | metalness: 0.8,
256 | roughness: 0.8,
257 | }); // 这里设置你想要的颜色和材质属性
258 |
259 | // 遍历模型并替换所有材质
260 | this.newCar.traverse(
261 | (child: { isMesh: any; material: MeshStandardMaterial }) => {
262 | if (child.isMesh) {
263 | child.material = newMaterial;
264 | }
265 | }
266 | );
267 | console.log("Animation completed!");
268 | gsap.to(tubeMaterial, {
269 | duration: 0.6,
270 | opacity: 0, //
271 | ease: "power1.inOut", // 缓动函数,可以根据需要进行调整
272 | });
273 | cancelAnimationFrame(this.animationLoop);
274 | },
275 | });
276 | },
277 | });
278 |
279 | // 动画函数
280 | const animateControlPoints = () => {
281 | this.updateControlPoint(3, 2.85, 0.007, 2, 0.007);
282 | this.updateControlPoint(4, null, null, 2.45, 0.0055); //(2, 1, 2.65),
283 | this.updateControlPoint(5, 1.2, 0.003, 2.45, 0.003);
284 |
285 | // new THREE.Vector3(0.5, 1, 2.8),
286 | this.updateControlPoint(6, 1, 0.003, 2.45, 0.0035);
287 | // 更新曲线和几何体
288 | curve.points = this.controlPoints;
289 | tube.geometry.dispose();
290 | tube.geometry = new THREE.TubeGeometry(curve, 100, 0.025, 32, false);
291 |
292 | this.animationLoop = requestAnimationFrame(animateControlPoints);
293 | };
294 | }
295 |
296 | updateControlPoint(
297 | index: number,
298 | xValue: number | null = null,
299 | xStep: number | null = 0,
300 | zValue: number | null = null,
301 | zStep: number | null = 0
302 | ): void {
303 | const point = this.controlPoints[index].clone();
304 |
305 | if (xValue !== null && xStep !== null) {
306 | if (point.x < xValue) {
307 | point.setX(point.x + xStep); // 递增操作
308 | } else if (point.x > xValue) {
309 | point.setX(point.x - xStep); // 递减操作
310 | }
311 | }
312 |
313 | if (zValue !== null && zStep !== null) {
314 | if (point.z >= zValue) {
315 | point.setZ(point.z - zStep); // 递减操作
316 | } else if (point.z < zValue) {
317 | point.setZ(point.z + zStep); // 递增操作
318 | }
319 | }
320 |
321 | this.controlPoints[index] = point;
322 | }
323 |
324 | removeAllModelsAndAnimations() {
325 | this.isRun = false;
326 |
327 | // 移除道路模型
328 | if (this.model) {
329 | this.container.remove(this.model.scene);
330 | this.model = null;
331 | }
332 |
333 | // 移除 tube 对象
334 | if (this.tube) {
335 | this.container.remove(this.tube);
336 | this.tube.geometry.dispose();
337 | this.tube.material.dispose();
338 | this.tube = null;
339 | }
340 |
341 | // 移除汽车模型
342 | if (this.newCar) {
343 | this.container.remove(this.newCar);
344 | this.newCar = null;
345 | }
346 |
347 | // 停止所有动画循环
348 | if (this.AnimationFrame) {
349 | cancelAnimationFrame(this.AnimationFrame);
350 | this.AnimationFrame = null;
351 | }
352 | if (this.animationLoop) {
353 | cancelAnimationFrame(this.animationLoop);
354 | this.animationLoop = null;
355 | }
356 | }
357 |
358 |
359 | playAuto() {
360 | const bgm = new Howl({
361 | src: "audio/roadBgm.mp3",
362 | loop: false,
363 | });
364 | bgm.play();
365 | }
366 | }
367 |
--------------------------------------------------------------------------------
/src/Experience/World/Speedup.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as THREE from "three";
3 | import * as STDLIB from "three-stdlib";
4 |
5 | import type Experience from "../Experience";
6 |
7 | import speedUpVertexShader from "../Shaders/Speedup/vert.glsl";
8 | import speedUpFragmentShader from "../Shaders/Speedup/frag.glsl";
9 |
10 | export default class Speedup extends kokomi.Component {
11 | declare base: Experience;
12 | model: STDLIB.GLTF;
13 | uj: kokomi.UniformInjector;
14 | material: THREE.ShaderMaterial;
15 | constructor(base: Experience) {
16 | super(base);
17 |
18 | const model = this.base.am.items["sm_speedup"] as STDLIB.GLTF;
19 | this.model = model;
20 |
21 | const modelParts = kokomi.flatModel(model.scene);
22 | // kokomi.printModel(modelParts);
23 |
24 | const uj = new kokomi.UniformInjector(this.base);
25 | this.uj = uj;
26 | const material = new THREE.ShaderMaterial({
27 | vertexShader: speedUpVertexShader,
28 | fragmentShader: speedUpFragmentShader,
29 | transparent: true,
30 | depthWrite: false,
31 | uniforms: {
32 | ...uj.shadertoyUniforms,
33 | uSpeed: {
34 | value: this.base.params.speed,
35 | },
36 | uOpacity: {
37 | value: this.base.params.speedUpOpacity,
38 | },
39 | },
40 | });
41 | this.material = material;
42 |
43 | const speedupMesh = modelParts[1] as THREE.Mesh;
44 | speedupMesh.material = material;
45 | }
46 | addExisting() {
47 | this.container.add(this.model.scene);
48 | }
49 | update(): void {
50 | this.uj.injectShadertoyUniforms(this.material.uniforms);
51 |
52 | this.material.uniforms.uSpeed.value = this.base.params.speed;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Experience/World/StartRoom.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as THREE from "three";
3 | import * as STDLIB from "three-stdlib";
4 |
5 | import type Experience from "../Experience";
6 |
7 | import reflecFloorVertexShader from "../Shaders/ReflecFloor/vert.glsl";
8 | import reflecFloorFragmentShader from "../Shaders/ReflecFloor/frag.glsl";
9 |
10 | import { MeshReflectorMaterial } from "../Utils/meshReflectorMaterial";
11 |
12 | export default class StartRoom extends kokomi.Component {
13 | declare base: Experience;
14 | model: STDLIB.GLTF;
15 | uj: kokomi.UniformInjector;
16 | lightMat: THREE.MeshStandardMaterial;
17 | customFloorMat: kokomi.CustomShaderMaterial;
18 | constructor(base: Experience) {
19 | super(base);
20 |
21 | const model = this.base.am.items["sm_startroom"] as STDLIB.GLTF;
22 | this.model = model;
23 |
24 | const modelParts = kokomi.flatModel(model.scene);
25 | // kokomi.printModel(modelParts);
26 |
27 | const light001 = modelParts[1] as THREE.Mesh;
28 | const lightMat = light001.material as THREE.MeshStandardMaterial;
29 | this.lightMat = lightMat;
30 | lightMat.emissive = new THREE.Color("white");
31 | lightMat.emissiveIntensity = 1;
32 | lightMat.toneMapped = false;
33 | lightMat.transparent = true;
34 | this.lightMat.alphaTest = 0.1;
35 |
36 | const ReflecFloor = modelParts[2] as THREE.Mesh;
37 | const floorMat = ReflecFloor.material as THREE.MeshPhysicalMaterial;
38 | floorMat.aoMap = this.base.am.items["ut_startroom_ao"];
39 | floorMat.lightMap = this.base.am.items["ut_startroom_light"];
40 | floorMat.normalMap = this.base.am.items["ut_floor_normal"];
41 | floorMat.roughnessMap = this.base.am.items["ut_floor_roughness"];
42 | floorMat.envMapIntensity = 0;
43 |
44 | // const reflectPlane = new THREE.Mesh(
45 | // new THREE.PlaneGeometry(100, 100),
46 | // new THREE.MeshBasicMaterial()
47 | // );
48 | // reflectPlane.rotation.x = -Math.PI / 2;
49 | // this.container.add(reflectPlane);
50 | // const reflectMat = new kokomi.MeshReflectorMaterial(
51 | // this.base,
52 | // reflectPlane,
53 | // {
54 | // resolution: 1024,
55 | // blur: [1000, 1000],
56 | // mixBlur: 4,
57 | // mixStrength: 1,
58 | // mirror: 1,
59 | // }
60 | // );
61 | // // @ts-ignore
62 | // reflectPlane.material = reflectMat.material;
63 | // ReflecFloor.visible = false;
64 |
65 | const uj = new kokomi.UniformInjector(this.base);
66 | this.uj = uj;
67 |
68 | const customFloorMat = new kokomi.CustomShaderMaterial({
69 | baseMaterial: floorMat,
70 | vertexShader: reflecFloorVertexShader,
71 | fragmentShader: reflecFloorFragmentShader,
72 | uniforms: {
73 | ...uj.shadertoyUniforms,
74 | uColor: {
75 | value: new THREE.Color("#ffffff"),
76 | },
77 | uSpeed: {
78 | value: this.base.params.speed,
79 | },
80 | uReflectMatrix: {
81 | value: new THREE.Matrix4(),
82 | },
83 | uReflectTexture: {
84 | value: null,
85 | },
86 | uReflectIntensity: {
87 | value: 25,
88 | },
89 | uMipmapTextureSize: {
90 | value: new THREE.Vector2(window.innerWidth, window.innerHeight),
91 | },
92 | },
93 | });
94 | this.customFloorMat = customFloorMat;
95 | ReflecFloor.material = customFloorMat;
96 |
97 | this.base.resizer.on("resize", () => {
98 | this.customFloorMat.uniforms.uMipmapTextureSize.value = new THREE.Vector2(
99 | window.innerWidth,
100 | window.innerHeight
101 | );
102 | });
103 |
104 | // const reflectMat = new kokomi.MeshReflectorMaterial(
105 | // this.base,
106 | // ReflecFloor,
107 | // {
108 | // resolution: 1024,
109 | // blur: [1000, 1000],
110 | // mixBlur: 4,
111 | // mixStrength: 1,
112 | // mirror: 1,
113 | // ignoreObjects: [ReflecFloor],
114 | // }
115 | // );
116 | // customFloorMat.uniforms.uReflectMatrix.value =
117 | // reflectMat.material._textureMatrix.value;
118 | // customFloorMat.uniforms.uReflectTexture.value =
119 | // reflectMat.material._tDiffuse.value;
120 |
121 | const reflectMat = new MeshReflectorMaterial(this.base, ReflecFloor, {
122 | resolution: 1024,
123 | ignoreObjects: [light001, ReflecFloor],
124 | });
125 | customFloorMat.uniforms.uReflectMatrix.value = reflectMat._reflectMatrix;
126 | // customFloorMat.uniforms.uReflectTexture.value =
127 | // reflectMat._renderTexture.rt.texture;
128 | customFloorMat.uniforms.uReflectTexture.value =
129 | reflectMat.mipmapFBO.rt.texture;
130 | }
131 | addExisting() {
132 | this.container.add(this.model.scene);
133 | }
134 | toggleModelVisibility(isShow:boolean) {
135 | this.model.scene.visible = isShow;
136 | }
137 | update(): void {
138 | this.uj.injectShadertoyUniforms(this.customFloorMat.uniforms);
139 |
140 | this.customFloorMat.uniforms.uSpeed.value = this.base.params.speed;
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/Experience/World/TestObject.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as THREE from "three";
3 |
4 | import type Experience from "../Experience";
5 |
6 | import testObjectVertexShader from "../Shaders/TestObject/vert.glsl";
7 | import testObjectFragmentShader from "../Shaders/TestObject/frag.glsl";
8 |
9 | export default class TestObject extends kokomi.Component {
10 | declare base: Experience;
11 | material: THREE.ShaderMaterial;
12 | mesh: THREE.Mesh;
13 | uj: kokomi.UniformInjector;
14 | constructor(base: Experience) {
15 | super(base);
16 |
17 | const params = {
18 | uDistort: {
19 | value: 1,
20 | },
21 | };
22 |
23 | const geometry = new THREE.SphereGeometry(2, 64, 64);
24 | // const geometry = new THREE.PlaneGeometry(4, 4);
25 | const material = new THREE.ShaderMaterial({
26 | vertexShader: testObjectVertexShader,
27 | fragmentShader: testObjectFragmentShader,
28 | });
29 | this.material = material;
30 | const mesh = new THREE.Mesh(geometry, material);
31 | this.mesh = mesh;
32 |
33 | const uj = new kokomi.UniformInjector(this.base);
34 | this.uj = uj;
35 | material.uniforms = {
36 | ...material.uniforms,
37 | ...uj.shadertoyUniforms,
38 | ...params,
39 | };
40 |
41 | const debug = this.base.debug;
42 | if (debug.active) {
43 | const debugFolder = debug.ui?.addFolder("testObject");
44 | debugFolder
45 | ?.add(params.uDistort, "value")
46 | .min(0)
47 | .max(2)
48 | .step(0.01)
49 | .name("distort");
50 | }
51 | }
52 | addExisting() {
53 | this.container.add(this.mesh);
54 | }
55 | update() {
56 | this.uj.injectShadertoyUniforms(this.material.uniforms);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Experience/World/World.ts:
--------------------------------------------------------------------------------
1 | import * as kokomi from "kokomi.js";
2 | import * as THREE from "three";
3 | import gsap from "gsap";
4 | // import { Howl } from "howler";
5 | import TWEEN from "three/examples/jsm/libs/tween.module.js";
6 | import type Experience from "../Experience";
7 |
8 | import TestObject from "./TestObject";
9 | import DynamicEnv from "./DynamicEnv";
10 | import StartRoom from "./StartRoom";
11 | import Car from "./Car";
12 | import Speedup from "./Speedup";
13 | import CameraShake from "./CameraShake";
14 | import Furina from "./Furina";
15 | import Road from "./Road";
16 | import City from "./City";
17 | export default class World extends kokomi.Component {
18 | declare base: Experience;
19 | testObject: TestObject | null;
20 | dynamicEnv!: DynamicEnv;
21 | startRoom!: StartRoom;
22 | car!: Car;
23 | road!: Road;
24 | city!: City;
25 | furina!: Furina;
26 | speedup!: Speedup;
27 | environment!: kokomi.Environment;
28 | cameraShake!: CameraShake;
29 | isWheels: boolean;
30 | currentIndex: number;
31 | ambientLight: any;
32 | t1!: ReturnType;
33 | t2!: ReturnType;
34 | t3!: ReturnType;
35 | t4!: ReturnType;
36 | t5!: ReturnType;
37 | t6!: ReturnType;
38 | t7!: ReturnType;
39 | t8!: ReturnType;
40 | t9!: ReturnType;
41 | constructor(base: Experience) {
42 | super(base);
43 |
44 | this.testObject = null;
45 | this.isWheels = false;
46 | this.currentIndex = 0;
47 | this.base.am.on("ready", () => {
48 | this.handleAssets();
49 | this.setLine();
50 | // this.testObject = new TestObject(this.base);
51 | // this.testObject.addExisting();
52 |
53 | const t1 = gsap.timeline();
54 | this.t1 = t1;
55 | const t2 = gsap.timeline();
56 | this.t2 = t2;
57 | const t3 = gsap.timeline();
58 | this.t3 = t3;
59 | const t4 = gsap.timeline();
60 | this.t4 = t4;
61 | const t5 = gsap.timeline();
62 | this.t5 = t5;
63 | const t6 = gsap.timeline();
64 | this.t6 = t6;
65 | const t7 = gsap.timeline();
66 | this.t7 = t7;
67 | const t8 = gsap.timeline();
68 | this.t8 = t8;
69 | const t9 = gsap.timeline();
70 | this.t9 = t9;
71 |
72 | this.base.scene.background = new THREE.Color("black");
73 |
74 | const envmap1 = kokomi.getEnvmapFromHDRTexture(
75 | this.base.renderer,
76 | this.base.am.items["ut_env_night"]
77 | );
78 | const envmap2 = kokomi.getEnvmapFromHDRTexture(
79 | this.base.renderer,
80 | this.base.am.items["ut_env_light"]
81 | );
82 | const dynamicEnv = new DynamicEnv(this.base, {
83 | envmap1,
84 | envmap2,
85 | });
86 | this.dynamicEnv = dynamicEnv;
87 | this.base.scene.environment = dynamicEnv.envmap;
88 | dynamicEnv.setWeight(1);
89 | // dynamicEnv.lerpWeight(1, 4);
90 |
91 | console.log(this.base.controls.controls);
92 | this.base.controls.controls.maxDistance = 30;
93 | const startRoom = new StartRoom(this.base);
94 | this.startRoom = startRoom;
95 | startRoom.addExisting();
96 |
97 | const car = new Car(this.base);
98 | this.car = car;
99 | car.addExisting();
100 |
101 | const road = new Road(this.base);
102 | this.road = road;
103 | const city = new City(this.base);
104 | this.city = city;
105 | if (this.base.params.isFurina) {
106 | const furina = new Furina(this.base);
107 | this.furina = furina;
108 | furina.addExisting();
109 | }
110 |
111 | const speedup = new Speedup(this.base);
112 | this.speedup = speedup;
113 | speedup.addExisting();
114 |
115 | const environment = new kokomi.Environment(this.base, {
116 | resolution: 512,
117 | scene: this.base.scene,
118 | options: {
119 | minFilter: THREE.LinearMipMapLinearFilter,
120 | anisotropy: 0,
121 | depthBuffer: false,
122 | generateMipmaps: true,
123 | },
124 | textureType: THREE.UnsignedByteType,
125 | ignoreObjects: [this.car.model.scene],
126 | });
127 | this.environment = environment;
128 |
129 | const cameraShake = new CameraShake(this.base);
130 | this.cameraShake = cameraShake;
131 | cameraShake.setIntensity(0);
132 |
133 | this.base.interactionManager.add(car.model.scene);
134 | car.model.scene.addEventListener("click", () => {
135 | if (this.currentIndex == 0) {
136 | this.rush();
137 | }
138 | });
139 |
140 | this.on("enter", () => {
141 | this.base.params.disableInteract = false;
142 | });
143 |
144 | this.enter();
145 | // this.enterDirectly();
146 |
147 | // const bgm = new Howl({
148 | // src: "audio/bgm.mp3",
149 | // loop: true,
150 | // });
151 | // bgm.play();
152 | });
153 | }
154 | handleAssets() {
155 | const items = this.base.am.items;
156 | (items["ut_car_body_ao"] as THREE.Texture).flipY = false;
157 | (items["ut_car_body_ao"] as THREE.Texture).colorSpace =
158 | THREE.LinearSRGBColorSpace;
159 | (items["ut_car_body_ao"] as THREE.Texture).minFilter = THREE.NearestFilter;
160 | (items["ut_car_body_ao"] as THREE.Texture).magFilter = THREE.NearestFilter;
161 | (items["ut_car_body_ao"] as THREE.Texture).channel = 1;
162 | (items["ut_startroom_ao"] as THREE.Texture).flipY = false;
163 | (items["ut_startroom_ao"] as THREE.Texture).colorSpace =
164 | THREE.LinearSRGBColorSpace;
165 | (items["ut_startroom_ao"] as THREE.Texture).channel = 1;
166 | (items["ut_startroom_light"] as THREE.Texture).flipY = false;
167 | (items["ut_startroom_light"] as THREE.Texture).colorSpace =
168 | THREE.SRGBColorSpace;
169 | (items["ut_startroom_light"] as THREE.Texture).channel = 1;
170 | (items["ut_floor_normal"] as THREE.Texture).flipY = false;
171 | (items["ut_floor_normal"] as THREE.Texture).colorSpace =
172 | THREE.LinearSRGBColorSpace;
173 | (items["ut_floor_normal"] as THREE.Texture).wrapS = THREE.RepeatWrapping;
174 | (items["ut_floor_normal"] as THREE.Texture).wrapT = THREE.RepeatWrapping;
175 | (items["ut_floor_roughness"] as THREE.Texture).flipY = false;
176 | (items["ut_floor_roughness"] as THREE.Texture).colorSpace =
177 | THREE.LinearSRGBColorSpace;
178 | (items["ut_floor_roughness"] as THREE.Texture).wrapS = THREE.RepeatWrapping;
179 | (items["ut_floor_roughness"] as THREE.Texture).wrapT = THREE.RepeatWrapping;
180 |
181 | if (this.base.params.isFurina) {
182 | (items["decal"] as THREE.Texture).flipY = false;
183 | (items["decal"] as THREE.Texture).colorSpace = THREE.LinearSRGBColorSpace;
184 | }
185 | }
186 | clearAllTweens() {
187 | this.t1.clear();
188 | this.t2.clear();
189 | this.t3.clear();
190 | this.t4.clear();
191 | this.t5.clear();
192 | this.t6.clear();
193 | this.t7.clear();
194 | this.t8.clear();
195 | this.t9.clear();
196 | }
197 | enter() {
198 | if (this.currentIndex == 0) {
199 | this.startRoom.lightMat.opacity = 0;
200 | this.rush();
201 | const options = { x: 0.23, y: 2.85, z: -11.5 };
202 | this.moveCameraToPosition(
203 | options,
204 | () => {
205 | this.rushDone();
206 | this.VisibilityTitle1(true);
207 | },
208 | 4
209 | );
210 | }
211 |
212 | this.base.params.disableInteract = true;
213 | this.dynamicEnv.setWeight(0);
214 | this.startRoom.lightMat.emissive.set(new THREE.Color("#000000"));
215 | this.startRoom.lightMat.emissiveIntensity = 0;
216 | this.dynamicEnv.setIntensity(0);
217 | this.startRoom.customFloorMat.uniforms.uColor.value.set(
218 | new THREE.Color("#000000")
219 | );
220 | this.startRoom.customFloorMat.uniforms.uReflectIntensity.value = 0;
221 | this.furina?.setColor(new THREE.Color("#000000"));
222 |
223 | document.querySelector(".loader-screen")?.classList.add("hollow");
224 |
225 | this.base.params.isCameraMoving = true;
226 | // this.t1.to(this.base.params.cameraPos, {
227 | // x: 0.23,
228 | // y: 2.85,
229 | // z: -11.5,
230 | // duration: 0.1,
231 | // ease: "power2.inOut",
232 | // onComplete: () => {
233 | // this.base.params.isCameraMoving = false;
234 | // this.emit("enter");
235 | // },
236 | // });
237 | const lightColor = new THREE.Color();
238 | const blackColor = new THREE.Color("#000000");
239 | const whiteColor = new THREE.Color("#ffffff");
240 | this.t2.to(this.base.params, {
241 | lightAlpha: 1,
242 | lightIntensity: 1,
243 | reflectIntensity: 25,
244 | furinaLerpColor: 1,
245 | duration: 4,
246 | delay: 1,
247 | ease: "power2.inOut",
248 | onUpdate: () => {
249 | lightColor
250 | .copy(blackColor)
251 | .lerp(whiteColor, this.base.params.lightAlpha);
252 |
253 | this.startRoom.lightMat.emissive.set(lightColor);
254 | this.startRoom.lightMat.emissiveIntensity =
255 | this.base.params.lightIntensity;
256 |
257 | this.startRoom.customFloorMat.uniforms.uColor.value.set(lightColor);
258 | this.startRoom.customFloorMat.uniforms.uReflectIntensity.value =
259 | this.base.params.reflectIntensity;
260 |
261 | this.furina?.setColor(lightColor);
262 | },
263 | });
264 | this.t3
265 | .to(this.base.params, {
266 | envIntensity: 1,
267 | duration: 4,
268 | delay: 0.5,
269 | ease: "power2.inOut",
270 | onUpdate: () => {
271 | this.dynamicEnv.setIntensity(this.base.params.envIntensity);
272 | },
273 | })
274 | .to(
275 | this.base.params,
276 | {
277 | envWeight: 1,
278 | duration: 4,
279 | ease: "power2.inOut",
280 | onUpdate: () => {
281 | this.dynamicEnv.setWeight(this.base.params.envWeight);
282 | },
283 | },
284 | "-=2.5"
285 | );
286 | }
287 | enterDirectly() {
288 | document.querySelector(".loader-screen")?.classList.add("hollow");
289 | this.base.params.isCameraMoving = false;
290 | this.base.controls.controls.setPosition(0, 0.8, -7);
291 | this.base.params.envIntensity = 1;
292 | this.emit("enter");
293 | }
294 | async rush() {
295 | if (this.currentIndex !== 0) {
296 | return;
297 | }
298 | if (this.base.params.isRushing) {
299 | this.rushDone();
300 | return;
301 | }
302 |
303 | if (this.base.params.disableInteract) {
304 | return;
305 | }
306 | this.isWheels = true;
307 | this.wheels();
308 | this.base.params.disableInteract = true;
309 | this.clearAllTweens();
310 | // this.base.controls.controls.setPosition(6.4, 1, -3);
311 | const floorColor = new THREE.Color();
312 | const blackColor = new THREE.Color("#000000");
313 | const camera = this.base.camera as THREE.PerspectiveCamera;
314 |
315 | const furinaColor = new THREE.Color();
316 | const furinaFadeColor = new THREE.Color("#666666");
317 |
318 | this.furina?.drive();
319 |
320 | this.t4
321 | .to(this.base.params, {
322 | speed: 4,
323 | duration: 2,
324 | ease: "power2.out",
325 | onComplete: () => {
326 | this.base.params.isRushing = true;
327 | this.base.params.disableInteract = false;
328 | },
329 | })
330 | .to(this.base.params, {
331 | speed: 10,
332 | duration: 4,
333 | ease: "power2.out",
334 | });
335 | this.t5.to(this.base.params, {
336 | lightOpacity: 0,
337 | duration: 1,
338 | ease: "power2.out",
339 | onUpdate: () => {
340 | //隐藏灯光板
341 | this.startRoom.lightMat.opacity = this.base.params.lightOpacity;
342 | },
343 | });
344 | this.t6.fromTo(
345 | this.base.params,
346 | {
347 | floorLerpColor: 0,
348 | furinaLerpColor: 0,
349 | },
350 | {
351 | floorLerpColor: 1,
352 | furinaLerpColor: 1,
353 | duration: 4,
354 | ease: "none",
355 | onUpdate: () => {
356 | floorColor.lerp(blackColor, this.base.params.floorLerpColor);
357 | this.startRoom.customFloorMat.uniforms.uColor.value.set(floorColor);
358 |
359 | furinaColor.lerp(furinaFadeColor, this.base.params.furinaLerpColor);
360 | this.furina?.setColor(furinaColor);
361 | },
362 | }
363 | );
364 | this.t7.to(this.base.params, {
365 | envIntensity: 0.01,
366 | duration: 1,
367 | ease: "power2.out",
368 | onUpdate: () => {
369 | this.dynamicEnv.setIntensity(this.base.params.envIntensity);
370 | },
371 | });
372 | this.t8.to(this.base.params, {
373 | speedUpOpacity: 1,
374 | cameraFov: 36,
375 | duration: 2,
376 | ease: "power2.out",
377 | onUpdate: () => {
378 | this.speedup.material.uniforms.uOpacity.value =
379 | this.base.params.speedUpOpacity;
380 |
381 | camera.fov = this.base.params.cameraFov;
382 | camera.updateProjectionMatrix();
383 | },
384 | });
385 | await kokomi.sleep(1000);
386 | this.base.scene.environment = this.environment.texture;
387 | this.t9.to(this.base.params, {
388 | carBodyEnvIntensity: 10,
389 | cameraShakeIntensity: 1,
390 | bloomLuminanceSmoothing: 0.4,
391 | bloomIntensity: 2,
392 | duration: 4,
393 | ease: "power2.out",
394 | onUpdate: () => {
395 | this.car.setBodyEnvmapIntensity(this.base.params.carBodyEnvIntensity);
396 | this.cameraShake.setIntensity(this.base.params.cameraShakeIntensity);
397 | this.base.post.setLuminanceSmoothing(
398 | this.base.params.bloomLuminanceSmoothing
399 | );
400 | this.base.post.setIntensity(this.base.params.bloomIntensity);
401 | },
402 | });
403 | }
404 | // 车轮滚动
405 | wheels() {
406 | this.VisibilityMenu(false);
407 | // 从资源管理器中获取汽车模型
408 | const carModel = this.base.am.items["sm_car"];
409 | const wheelFrontLeft = this.findObjectByName(
410 | carModel.scene,
411 | "wheels001_wheels6_0"
412 | );
413 | const wheelFrontRight = this.findObjectByName(
414 | carModel.scene,
415 | "wheels_wheels6_0"
416 | );
417 | // 确保所有车轮对象都存在
418 | if (wheelFrontLeft && wheelFrontRight) {
419 | // 渲染循环
420 | const animate = () => {
421 | if (!this.isWheels) {
422 | this.VisibilityMenu(true);
423 | return; // 如果标志为false,停止动画
424 | }
425 | requestAnimationFrame(animate);
426 | // 旋转车轮
427 | const rotationSpeed = 0.1; // 车轮旋转速度
428 | wheelFrontLeft.rotation.x -= rotationSpeed;
429 | wheelFrontRight.rotation.x -= rotationSpeed;
430 | };
431 |
432 | animate();
433 | } else {
434 | console.log("找不到所有车轮对象");
435 | }
436 | }
437 |
438 | // 查找车轮对象
439 | findObjectByName(
440 | obj: { name: any; children: string | any[] },
441 | name: string
442 | ): any {
443 | if (obj.name === name) {
444 | return obj;
445 | }
446 | for (let i = 0; i < obj.children.length; i++) {
447 | const result = this.findObjectByName(obj.children[i], name);
448 | if (result) {
449 | return result;
450 | }
451 | }
452 | return null;
453 | }
454 | rushDone() {
455 | if (this.base.params.disableInteract) {
456 | return;
457 | }
458 |
459 | this.base.params.disableInteract = true;
460 | this.clearAllTweens();
461 | const floorColor = new THREE.Color();
462 | const whiteColor = new THREE.Color("#ffffff");
463 | const camera = this.base.camera as THREE.PerspectiveCamera;
464 |
465 | const furinaColor = new THREE.Color();
466 | const furinaOriginalColor = new THREE.Color("#ffffff");
467 |
468 | this.furina?.pause();
469 |
470 | this.t4.to(this.base.params, {
471 | speed: 0,
472 | duration: 2,
473 | ease: "power2.out",
474 | onComplete: () => {
475 | this.base.params.isRushing = false;
476 | this.base.params.disableInteract = false;
477 | },
478 | });
479 | this.t5.to(this.base.params, {
480 | lightOpacity: 1,
481 | duration: 1,
482 | ease: "power2.out",
483 | onUpdate: () => {
484 | this.startRoom.lightMat.opacity = this.base.params.lightOpacity;
485 | },
486 | });
487 | this.t6.fromTo(
488 | this.base.params,
489 | { floorLerpColor: 0, furinaLerpColor: 0 },
490 | {
491 | floorLerpColor: 1,
492 | furinaLerpColor: 1,
493 | duration: 4,
494 | ease: "none",
495 | onUpdate: () => {
496 | floorColor.lerp(whiteColor, this.base.params.floorLerpColor);
497 | this.startRoom.customFloorMat.uniforms.uColor.value.set(floorColor);
498 |
499 | furinaColor.lerp(
500 | furinaOriginalColor,
501 | this.base.params.furinaLerpColor
502 | );
503 | this.furina?.setColor(furinaColor);
504 | },
505 | }
506 | );
507 | this.t7.to(this.base.params, {
508 | envIntensity: 1,
509 | duration: 1,
510 | ease: "power2.out",
511 | onUpdate: () => {
512 | this.dynamicEnv.setIntensity(this.base.params.envIntensity);
513 | },
514 | });
515 | this.t8.to(this.base.params, {
516 | speedUpOpacity: 0,
517 | cameraFov: 33.4,
518 | duration: 2,
519 | ease: "power2.out",
520 | onUpdate: () => {
521 | this.speedup.material.uniforms.uOpacity.value =
522 | this.base.params.speedUpOpacity;
523 |
524 | camera.fov = this.base.params.cameraFov;
525 | camera.updateProjectionMatrix();
526 | },
527 | });
528 | this.t9.to(this.base.params, {
529 | carBodyEnvIntensity: 1,
530 | cameraShakeIntensity: 0,
531 | bloomLuminanceSmoothing: 1.6,
532 | bloomIntensity: 1,
533 | duration: 4,
534 | ease: "power2.out",
535 | onUpdate: () => {
536 | this.car.setBodyEnvmapIntensity(this.base.params.carBodyEnvIntensity);
537 | this.cameraShake.setIntensity(this.base.params.cameraShakeIntensity);
538 | this.base.post.setLuminanceSmoothing(
539 | this.base.params.bloomLuminanceSmoothing
540 | );
541 | this.base.post.setIntensity(this.base.params.bloomIntensity);
542 | },
543 | });
544 | this.base.scene.environment = this.dynamicEnv.envmap;
545 | setTimeout(() => {
546 | this.isWheels = false;
547 | const father = document.querySelector(".line") as Element;
548 | //@ts-ignore
549 | father.style.display = "flex"; // 设置元素 display 为 none
550 | }, 1500);
551 | }
552 | rush3() {
553 | this.city.addExisting();
554 | setTimeout(() => {
555 | const targetPosition = { x: -31.82, y: 56.71, z: 3.61 };
556 | this.moveCameraToPosition(targetPosition, null, 5);
557 | setTimeout(() => {
558 | const targetPosition2 = { x: -14.53, y: 8.41, z: 0.01 };
559 | this.moveCameraToPosition(
560 | targetPosition2,
561 | () => {
562 | const father = document.querySelector(".line") as Element;
563 | //@ts-ignore
564 | father.style.display = "flex"; // 设置元素 display 为 none
565 | },
566 | 2
567 | );
568 | }, 5000);
569 | }, 2000);
570 | this.isWheels = true;
571 | this.wheels();
572 | this.base.params.disableInteract = true;
573 | this.clearAllTweens();
574 | setTimeout(() => {
575 | this.VisibilityMenu(true);
576 | this.VisibilityTitle3(true);
577 | }, 7000);
578 | // this.base.controls.controls.setPosition(6.4, 1, -3);
579 | const floorColor = new THREE.Color();
580 | const blackColor = new THREE.Color("#000000");
581 | const camera = this.base.camera as THREE.PerspectiveCamera;
582 | camera.near = 0.1; // 将近剪切面调近
583 | camera.far = 100; // 根据场景实际情况调整远剪切面
584 | camera.updateProjectionMatrix();
585 |
586 | const furinaColor = new THREE.Color();
587 | const furinaFadeColor = new THREE.Color("#666666");
588 |
589 | this.furina?.drive();
590 | const ambientLight = new THREE.AmbientLight("0xffffff", 0.1); // 第一个参数是颜色,第二个参数是光强度 // 第一个参数是颜色,第二个参数是光强度
591 | ambientLight.position.y = ambientLight.position.y + 2; // 设置灯光的位置
592 | this.ambientLight = ambientLight;
593 | this.container.add(this.ambientLight);
594 | this.t4
595 | .to(this.base.params, {
596 | speed: 4,
597 | duration: 2,
598 | ease: "power2.out",
599 | onComplete: () => {
600 | this.base.params.isRushing = true;
601 | this.base.params.disableInteract = false;
602 | },
603 | })
604 | .to(this.base.params, {
605 | speed: 10,
606 | duration: 4,
607 | ease: "power2.out",
608 | });
609 | this.t5.to(this.base.params, {
610 | lightOpacity: 0,
611 | duration: 1,
612 | ease: "power2.out",
613 | onUpdate: () => {
614 | //隐藏灯光板
615 | this.startRoom.lightMat.opacity = this.base.params.lightOpacity;
616 | },
617 | });
618 | this.t6.fromTo(
619 | this.base.params,
620 | {
621 | floorLerpColor: 0,
622 | furinaLerpColor: 0,
623 | },
624 | {
625 | floorLerpColor: 1,
626 | furinaLerpColor: 1,
627 | duration: 4,
628 | ease: "none",
629 | onUpdate: () => {
630 | floorColor.lerp(blackColor, this.base.params.floorLerpColor);
631 | this.startRoom.customFloorMat.uniforms.uColor.value.set(floorColor);
632 |
633 | furinaColor.lerp(furinaFadeColor, this.base.params.furinaLerpColor);
634 | // this.furina?.setColor(furinaColor);
635 | },
636 | }
637 | );
638 |
639 | // ----
640 | this.startRoom.toggleModelVisibility(false);
641 |
642 | // ----
643 | this.t7.to(this.base.params, {
644 | envIntensity: 0.01,
645 | duration: 1,
646 | ease: "power2.out",
647 | onUpdate: () => {
648 | this.dynamicEnv.setIntensity(this.base.params.envIntensity);
649 | },
650 | });
651 | // ----
652 | }
653 | setLine() {
654 | const lines = document.querySelectorAll(".child");
655 | lines.forEach((line, index) => {
656 | line.addEventListener("click", () => {
657 | const oldIndex = Array.from(lines).findIndex((line) =>
658 | line.classList.contains("act")
659 | );
660 | if (oldIndex == index ) {
661 | return;
662 | }
663 | // 获取已经存在act类的元素索引值
664 | const father = document.querySelector(".line") as Element;
665 | //@ts-ignore
666 | father.style.display = "none";
667 | if (oldIndex == 1) {
668 | this.rushDone2();
669 | }
670 | if (oldIndex == 2) {
671 | this.rushDone3();
672 | }
673 | if (this.currentIndex !== null) {
674 | lines.forEach((e, index) => {
675 | lines[index].classList.remove("act");
676 | });
677 | }
678 | line.classList.add("act");
679 | // 根据索引设置不同的目标位置
680 | let targetPosition;
681 | switch (index) {
682 | case 0:
683 | this.VisibilityTitle1(true);
684 | targetPosition = { x: 0.23, y: 2.85, z: -11.5 };
685 | this.moveCameraToPosition(targetPosition, () => {
686 | //@ts-ignore
687 | father.style.display = "flex";
688 | });
689 | break;
690 | case 1:
691 | this.VisibilityTitle1(false);
692 | targetPosition = { x: -23.4, y: 15.05, z: -0.04 };
693 | this.moveCameraToPosition(targetPosition, this.rush2());
694 | break;
695 | case 2:
696 | this.VisibilityTitle1(false);
697 | targetPosition = { x: -49.54, y: 41.86, z: 3.49 };
698 | this.moveCameraToPosition(targetPosition, this.rush3());
699 | break;
700 | // 添加其他 case 以处理其他索引的目标位置
701 | default:
702 | break;
703 | }
704 |
705 | this.currentIndex = index;
706 | });
707 | });
708 | }
709 | rushDone3() {
710 | this.city.removeAllModelsAndAnimations();
711 | this.VisibilityTitle3(false);
712 | this.startRoom.toggleModelVisibility(true);
713 | this.container.remove(this.ambientLight);
714 | this.VisibilityTitle2(false);
715 |
716 | this.base.params.disableInteract = true;
717 | this.clearAllTweens();
718 | const floorColor = new THREE.Color();
719 | const whiteColor = new THREE.Color("#ffffff");
720 | const camera = this.base.camera as THREE.PerspectiveCamera;
721 |
722 | const furinaColor = new THREE.Color();
723 | const furinaOriginalColor = new THREE.Color("#ffffff");
724 |
725 | this.furina?.pause();
726 |
727 | this.t4.to(this.base.params, {
728 | speed: 0,
729 | duration: 2,
730 | ease: "power2.out",
731 | onComplete: () => {
732 | this.base.params.isRushing = false;
733 | this.base.params.disableInteract = false;
734 | },
735 | });
736 | this.t5.to(this.base.params, {
737 | lightOpacity: 1,
738 | duration: 1,
739 | ease: "power2.out",
740 | onUpdate: () => {
741 | this.startRoom.lightMat.opacity = this.base.params.lightOpacity;
742 | },
743 | });
744 | this.t6.fromTo(
745 | this.base.params,
746 | { floorLerpColor: 0, furinaLerpColor: 0 },
747 | {
748 | floorLerpColor: 1,
749 | furinaLerpColor: 1,
750 | duration: 4,
751 | ease: "none",
752 | onUpdate: () => {
753 | floorColor.lerp(whiteColor, this.base.params.floorLerpColor);
754 | this.startRoom.customFloorMat.uniforms.uColor.value.set(floorColor);
755 |
756 | furinaColor.lerp(
757 | furinaOriginalColor,
758 | this.base.params.furinaLerpColor
759 | );
760 | this.furina?.setColor(furinaColor);
761 | },
762 | }
763 | );
764 | this.t7.to(this.base.params, {
765 | envIntensity: 1,
766 | duration: 1,
767 | ease: "power2.out",
768 | onUpdate: () => {
769 | this.dynamicEnv.setIntensity(this.base.params.envIntensity);
770 | },
771 | });
772 | this.t8.to(this.base.params, {
773 | speedUpOpacity: 0,
774 | cameraFov: 33.4,
775 | duration: 2,
776 | ease: "power2.out",
777 | onUpdate: () => {
778 | this.speedup.material.uniforms.uOpacity.value =
779 | this.base.params.speedUpOpacity;
780 |
781 | camera.fov = this.base.params.cameraFov;
782 | camera.updateProjectionMatrix();
783 | },
784 | });
785 | this.t9.to(this.base.params, {
786 | carBodyEnvIntensity: 1,
787 | cameraShakeIntensity: 0,
788 | bloomLuminanceSmoothing: 1.6,
789 | bloomIntensity: 1,
790 | duration: 4,
791 | ease: "power2.out",
792 | onUpdate: () => {
793 | this.car.setBodyEnvmapIntensity(this.base.params.carBodyEnvIntensity);
794 | this.cameraShake.setIntensity(this.base.params.cameraShakeIntensity);
795 | this.base.post.setLuminanceSmoothing(
796 | this.base.params.bloomLuminanceSmoothing
797 | );
798 | this.base.post.setIntensity(this.base.params.bloomIntensity);
799 | },
800 | });
801 | this.base.scene.environment = this.dynamicEnv.envmap;
802 | setTimeout(() => {
803 | this.isWheels = false;
804 | }, 8000);
805 | }
806 | VisibilityMenu(show: boolean) {
807 | const menu = document.querySelector(".line") as HTMLElement;
808 | if (!show) {
809 | menu.style.opacity = "0";
810 | } else {
811 | menu.style.opacity = "1";
812 | }
813 | }
814 | VisibilityTitle1(show: boolean) {
815 | const title1 = document.querySelector(".title1") as HTMLElement;
816 | if (!show) {
817 | title1.style.opacity = "0";
818 | } else {
819 | title1.style.opacity = "1";
820 | }
821 | }
822 | VisibilityTitle2(show: boolean) {
823 | const title1 = document.querySelector(".safety") as HTMLElement;
824 | if (!show) {
825 | title1.style.opacity = "0";
826 | } else {
827 | title1.style.opacity = "1";
828 | }
829 | }
830 | VisibilityTitle3(show: boolean) {
831 | const title1 = document.querySelector(".fsd") as HTMLElement;
832 | if (!show) {
833 | title1.style.opacity = "0";
834 | } else {
835 | title1.style.opacity = "1";
836 | }
837 | }
838 | async rush2() {
839 | this.isWheels = true;
840 | this.wheels();
841 | this.base.params.disableInteract = true;
842 | this.clearAllTweens();
843 | setTimeout(() => {
844 | this.VisibilityMenu(true);
845 | }, 10000);
846 | // this.base.controls.controls.setPosition(6.4, 1, -3);
847 | const floorColor = new THREE.Color();
848 | const blackColor = new THREE.Color("#000000");
849 | const camera = this.base.camera as THREE.PerspectiveCamera;
850 | camera.near = 0.1; // 将近剪切面调近
851 | camera.far = 100; // 根据场景实际情况调整远剪切面
852 | camera.updateProjectionMatrix();
853 |
854 | const furinaColor = new THREE.Color();
855 | const furinaFadeColor = new THREE.Color("#666666");
856 |
857 | this.furina?.drive();
858 | const ambientLight = new THREE.AmbientLight("0xffffff", 0.5); // 第一个参数是颜色,第二个参数是光强度 // 第一个参数是颜色,第二个参数是光强度
859 | ambientLight.position.y = ambientLight.position.y + 2; // 设置灯光的位置
860 | this.ambientLight = ambientLight;
861 | this.container.add(this.ambientLight);
862 | this.t4
863 | .to(this.base.params, {
864 | speed: 4,
865 | duration: 2,
866 | ease: "power2.out",
867 | onComplete: () => {
868 | this.base.params.isRushing = true;
869 | this.base.params.disableInteract = false;
870 | },
871 | })
872 | .to(this.base.params, {
873 | speed: 10,
874 | duration: 4,
875 | ease: "power2.out",
876 | });
877 | this.t5.to(this.base.params, {
878 | lightOpacity: 0,
879 | duration: 1,
880 | ease: "power2.out",
881 | onUpdate: () => {
882 | //隐藏灯光板
883 | this.startRoom.lightMat.opacity = this.base.params.lightOpacity;
884 | },
885 | });
886 | this.t6.fromTo(
887 | this.base.params,
888 | {
889 | floorLerpColor: 0,
890 | furinaLerpColor: 0,
891 | },
892 | {
893 | floorLerpColor: 1,
894 | furinaLerpColor: 1,
895 | duration: 4,
896 | ease: "none",
897 | onUpdate: () => {
898 | floorColor.lerp(blackColor, this.base.params.floorLerpColor);
899 | this.startRoom.customFloorMat.uniforms.uColor.value.set(floorColor);
900 |
901 | furinaColor.lerp(furinaFadeColor, this.base.params.furinaLerpColor);
902 | // this.furina?.setColor(furinaColor);
903 | },
904 | }
905 | );
906 |
907 | // ----
908 | this.startRoom.toggleModelVisibility(false);
909 | this.road.addExisting();
910 | this.VisibilityTitle2(true);
911 | // ----
912 | this.t7.to(this.base.params, {
913 | envIntensity: 0.01,
914 | duration: 1,
915 | ease: "power2.out",
916 | onUpdate: () => {
917 | this.dynamicEnv.setIntensity(this.base.params.envIntensity);
918 | },
919 | });
920 | }
921 | rushDone2() {
922 | this.road.removeAllModelsAndAnimations();
923 | this.startRoom.toggleModelVisibility(true);
924 | this.container.remove(this.ambientLight);
925 | this.VisibilityTitle2(false);
926 |
927 | this.base.params.disableInteract = true;
928 | this.clearAllTweens();
929 | const floorColor = new THREE.Color();
930 | const whiteColor = new THREE.Color("#ffffff");
931 | const camera = this.base.camera as THREE.PerspectiveCamera;
932 |
933 | const furinaColor = new THREE.Color();
934 | const furinaOriginalColor = new THREE.Color("#ffffff");
935 |
936 | this.furina?.pause();
937 |
938 | this.t4.to(this.base.params, {
939 | speed: 0,
940 | duration: 2,
941 | ease: "power2.out",
942 | onComplete: () => {
943 | this.base.params.isRushing = false;
944 | this.base.params.disableInteract = false;
945 | },
946 | });
947 | this.t5.to(this.base.params, {
948 | lightOpacity: 1,
949 | duration: 1,
950 | ease: "power2.out",
951 | onUpdate: () => {
952 | this.startRoom.lightMat.opacity = this.base.params.lightOpacity;
953 | },
954 | });
955 | this.t6.fromTo(
956 | this.base.params,
957 | { floorLerpColor: 0, furinaLerpColor: 0 },
958 | {
959 | floorLerpColor: 1,
960 | furinaLerpColor: 1,
961 | duration: 4,
962 | ease: "none",
963 | onUpdate: () => {
964 | floorColor.lerp(whiteColor, this.base.params.floorLerpColor);
965 | this.startRoom.customFloorMat.uniforms.uColor.value.set(floorColor);
966 |
967 | furinaColor.lerp(
968 | furinaOriginalColor,
969 | this.base.params.furinaLerpColor
970 | );
971 | this.furina?.setColor(furinaColor);
972 | },
973 | }
974 | );
975 | this.t7.to(this.base.params, {
976 | envIntensity: 1,
977 | duration: 1,
978 | ease: "power2.out",
979 | onUpdate: () => {
980 | this.dynamicEnv.setIntensity(this.base.params.envIntensity);
981 | },
982 | });
983 | this.t8.to(this.base.params, {
984 | speedUpOpacity: 0,
985 | cameraFov: 33.4,
986 | duration: 2,
987 | ease: "power2.out",
988 | onUpdate: () => {
989 | this.speedup.material.uniforms.uOpacity.value =
990 | this.base.params.speedUpOpacity;
991 |
992 | camera.fov = this.base.params.cameraFov;
993 | camera.updateProjectionMatrix();
994 | },
995 | });
996 | this.t9.to(this.base.params, {
997 | carBodyEnvIntensity: 1,
998 | cameraShakeIntensity: 0,
999 | bloomLuminanceSmoothing: 1.6,
1000 | bloomIntensity: 1,
1001 | duration: 4,
1002 | ease: "power2.out",
1003 | onUpdate: () => {
1004 | this.car.setBodyEnvmapIntensity(this.base.params.carBodyEnvIntensity);
1005 | this.cameraShake.setIntensity(this.base.params.cameraShakeIntensity);
1006 | this.base.post.setLuminanceSmoothing(
1007 | this.base.params.bloomLuminanceSmoothing
1008 | );
1009 | this.base.post.setIntensity(this.base.params.bloomIntensity);
1010 | },
1011 | });
1012 | this.base.scene.environment = this.dynamicEnv.envmap;
1013 | setTimeout(() => {
1014 | this.isWheels = false;
1015 | }, 1500);
1016 | }
1017 | moveCameraToPosition(
1018 | targetPosition: any,
1019 | onCompleteCallback?: any,
1020 | duration = 2,
1021 | ease = "power2.inOut"
1022 | ) {
1023 | // 获取当前控制器的位置
1024 | const currentPosition = this.base.controls.controls.getPosition(
1025 | new THREE.Vector3()
1026 | );
1027 |
1028 | // 使用 gsap 平滑移动到目标位置
1029 | gsap.to(currentPosition, {
1030 | x: targetPosition.x,
1031 | y: targetPosition.y,
1032 | z: targetPosition.z,
1033 | duration: duration, // 动画持续时间
1034 | ease: ease,
1035 | onUpdate: () => {
1036 | // 更新控制器位置
1037 | this.base.controls.controls.setPosition(
1038 | currentPosition.x,
1039 | currentPosition.y,
1040 | currentPosition.z
1041 | );
1042 | // 更新渲染
1043 | this.base.renderer.render(this.base.scene, this.base.camera);
1044 | },
1045 | onComplete: () => {
1046 | this.base.params.isCameraMoving = false;
1047 | if (onCompleteCallback) {
1048 | console.log(onCompleteCallback);
1049 |
1050 | onCompleteCallback();
1051 | }
1052 | },
1053 | });
1054 |
1055 | this.base.params.isCameraMoving = true;
1056 | }
1057 | }
1058 |
--------------------------------------------------------------------------------
/src/Experience/resources.ts:
--------------------------------------------------------------------------------
1 | import type * as kokomi from "kokomi.js";
2 |
3 | export const resources: kokomi.ResourceItem[] = [
4 | {
5 | name: "roadBgm",
6 | type: "audio",
7 | path: "audio/roadBgm.mp3",
8 | },
9 | {
10 | name: "sm_car",
11 | type: "gltfModel",
12 | path: "mesh/2024-Model3-d.glb",
13 | },
14 | {
15 | name: "sm_startroom",
16 | type: "gltfModel",
17 | path: "mesh/sm_startroom.raw.gltf",
18 | },
19 | {
20 | name: "road",
21 | type: "gltfModel",
22 | path: "mesh/road-d.glb",
23 | },
24 | {
25 | name: "city_road",
26 | type: "gltfModel",
27 | path: "mesh/city_road.glb",
28 | },
29 | {
30 | name: "sm_speedup",
31 | type: "gltfModel",
32 | path: "mesh/sm_speedup.gltf",
33 | },
34 | {
35 | name: "ut_car_body_ao",
36 | type: "texture",
37 | path: "texture/t_car_body_AO.raw.jpg",
38 | },
39 | {
40 | name: "ut_startroom_ao",
41 | type: "texture",
42 | path: "texture/t_startroom_ao.raw.jpg",
43 | },
44 | {
45 | name: "ut_startroom_light",
46 | type: "texture",
47 | path: "texture/t_startroom_light.raw.jpg",
48 | },
49 | {
50 | name: "ut_floor_normal",
51 | type: "texture",
52 | path: "texture/t_floor_normal.webp",
53 | },
54 | {
55 | name: "ut_floor_roughness",
56 | type: "texture",
57 | path: "texture/t_floor_roughness.webp",
58 | },
59 | {
60 | name: "ut_env_night",
61 | type: "hdrTexture",
62 | path: "texture/t_env_night.hdr",
63 | },
64 | {
65 | name: "ut_env_light",
66 | type: "hdrTexture",
67 | path: "texture/t_env_light.hdr",
68 | },
69 | {
70 | name: "driving",
71 | type: "fbxModel",
72 | path: "mesh/Driving.fbx",
73 | },
74 | {
75 | name: "decal",
76 | type: "texture",
77 | path: "texture/decal.png",
78 | },
79 | ];
80 |
--------------------------------------------------------------------------------
/src/global.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.glsl" {
2 | const value: string;
3 | export default value;
4 | }
5 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import "./style.css";
2 |
3 | import { adaptMobileDOM } from "kokomi.js";
4 |
5 | import Experience from "./Experience/Experience";
6 |
7 | document.querySelector("#app")!.innerHTML = `
8 |
9 |
10 |
11 |
12 | L
13 | O
14 | A
15 | D
16 | I
17 | N
18 | G
19 |
20 |
21 |
22 | `;
23 |
24 | const app = document.querySelector("#app")! as HTMLElement;
25 |
26 | adaptMobileDOM(app);
27 | window.addEventListener("resize", () => {
28 | adaptMobileDOM(app);
29 | });
30 |
31 |
32 |
33 | new Experience("#sketch");
34 |
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .hollow {
6 | opacity: 0;
7 | pointer-events: none;
8 | }
9 |
10 | #app {
11 | position: absolute;
12 | top: 0;
13 | left: 0;
14 | width: 100%;
15 | height: 100%;
16 | }
17 |
18 | #sketch {
19 | position: absolute;
20 | top: 0;
21 | left: 0;
22 | width: 100%;
23 | height: 100%;
24 | overflow: hidden;
25 | }
26 |
27 | .loader-screen {
28 | position: fixed;
29 | z-index: 5;
30 | top: 0;
31 | left: 0;
32 | width: 100%;
33 | height: 100%;
34 | transition: 0.3s;
35 | background: black;
36 | }
37 |
38 | .loading-container {
39 | position: absolute;
40 | top: 50%;
41 | left: 50%;
42 | transform: translate(-50%, -50%);
43 | }
44 |
45 | .loading {
46 | color: white;
47 | font-size: 1.875rem;
48 | letter-spacing: 0.1em;
49 | }
50 |
51 | .loading span {
52 | animation: blur 1.5s calc(var(--i) / 5 * 1s) alternate infinite;
53 | }
54 |
55 | @keyframes blur {
56 | to {
57 | filter: blur(2px);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | /* font-family: 'AliFont';
3 | src: url('http://model.newhao2021.top/model3/ali.woff') format('woff'), */
4 | /* url('/public/font/ali.woff') format('woff') */
5 | /* url('/public/font/ali.woff2') format('woff2'); */
6 | }
7 |
8 | * {
9 | transition: all .5s;
10 | }
11 |
12 | body {
13 | width: 100vw;
14 | height: 100vh;
15 | background-color: black;
16 | overflow: hidden;
17 | }
18 |
19 | .line {
20 | position: absolute;
21 | bottom: 5vh;
22 | left: 50%;
23 | opacity: 0;
24 | transform: translateX(-50%);
25 | width: 700px;
26 | height: 3px;
27 | background-color: rgba(255, 255, 255, 0.4);
28 | display: flex;
29 | justify-content: space-between;
30 | align-items: center;
31 | transition: all .5s;
32 | }
33 |
34 | .line .child {
35 | width: 15px;
36 | height: 15px;
37 | border-radius: 50%;
38 | background-color: rgb(173, 173, 173);
39 | /* transform: translateX(-50%); */
40 | position: relative;
41 | cursor: pointer;
42 | /* font-family: "AliFont"; */
43 | }
44 |
45 | .line .child.act {
46 | background-color: #3271ae;
47 | }
48 |
49 | .line .child.act .text {
50 | color: #fff;
51 | }
52 |
53 | .line .child .text {
54 | position: absolute;
55 | top: -25px;
56 | transform: translateX(-10%);
57 | width: 100px;
58 | color: #ffffff96;
59 | }
60 |
61 | .title1 {
62 | position: absolute;
63 | top: 10vh;
64 | left: 50%;
65 | transform: translateX(-50%);
66 | font-size: 3vw;
67 | color: #fff;
68 | opacity: 0;
69 | }
70 |
71 | .safety {
72 | position: absolute;
73 | top: 1vh;
74 | left: 5%;
75 | /* transform: translateX(-50%); */
76 | font-size: 3vw;
77 | color: #fff;
78 | opacity: 0;
79 |
80 | h1 {
81 | font-size: 3vw;
82 | }
83 |
84 | h3 {
85 | font-size: .8vw;
86 | }
87 | }
88 |
89 | .fsd {
90 | position: absolute;
91 | top: 1vh;
92 | left: 50%;
93 | transform: translateX(-50%);
94 | /* transform: translateX(-50%); */
95 | font-size: 3vw;
96 | color: #fff;
97 | opacity: 0;
98 |
99 | h1 {
100 | font-size: 3vw;
101 | }
102 |
103 | h3 {
104 | font-size: 1vw;
105 | }
106 | }
107 |
108 | @media screen and (max-width: 927px) {
109 | .fsd {
110 | top: 45vh;
111 | left: 87%;
112 | width: 800px;
113 | text-align: center;
114 | transform: translateX(-50%) rotate(90deg);
115 | h1{
116 | font-size: 28px;
117 | }
118 | h3{
119 | font-size: 12px;
120 | }
121 | }
122 |
123 | .safety {
124 | top: 10vh;
125 | left: 78%;
126 | transform: translateX(-50%) rotate(90deg);
127 |
128 | h1 {
129 | font-size: 28px;
130 | }
131 |
132 | h3 {
133 | width: 300px;
134 | font-size: 9px;
135 | }
136 | }
137 |
138 | .title1 {
139 | top: 47vh;
140 | left: 85%;
141 | transform: translateX(-50%) rotate(90deg);
142 | font-size: 30px;
143 | width: 500px;
144 | text-align: center;
145 | }
146 |
147 | .line {
148 | bottom: 50vh;
149 | left: 12%;
150 | transform: translateX(-50%) rotate(90deg);
151 | width: 550px;
152 | }
153 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 |
15 | /* Linting */
16 | "strict": true,
17 | "noUnusedLocals": false,
18 | "noUnusedParameters": false,
19 | "noFallthroughCasesInSwitch": true,
20 | "noEmit": true,
21 | "noEmitOnError": true
22 | },
23 | "include": ["src"]
24 | }
25 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 |
3 | import glsl from "vite-plugin-glsl";
4 |
5 | export default defineConfig({
6 | server: {
7 | open: true,
8 | },
9 | plugins: [glsl()],
10 | });
11 |
--------------------------------------------------------------------------------