dom-sync-gl 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,58 @@
1
+ import { IUniform, Vector2, WebGLRenderer } from 'three';
2
+ import { default as GUI } from 'lil-gui';
3
+ import { EffectTarget, EffectPass } from '../EffectComposer';
4
+ export interface BaseEffectConfig {
5
+ fragmentShader: string;
6
+ uniforms?: {
7
+ [key: string]: IUniform;
8
+ };
9
+ }
10
+ export declare abstract class BaseEffect {
11
+ protected pass: EffectPass | null;
12
+ /**
13
+ * GUI の on/off チェックボックスから操作される。false にすると
14
+ * EffectComposer がこのパスをスキップする(lil-gui バインド用に public)。
15
+ */
16
+ private _enabled;
17
+ get enabled(): boolean;
18
+ set enabled(value: boolean);
19
+ protected abstract getConfig(): BaseEffectConfig;
20
+ /**
21
+ * addEffect() 呼び出し時に内部で呼ばれる。直接使わない。
22
+ *
23
+ * 1 インスタンスを `webgl.addEffect()` と `domPlane.addEffect()` の両方に
24
+ * 渡すと、`update()` が同一フレームに 2 回走り内部状態が二重進行する
25
+ * (例: `FluidEffect` の dye が倍速で進む)。DEV では警告を出す。
26
+ */
27
+ _register(target: EffectTarget): void;
28
+ /**
29
+ * Core/DomPlane の addEffect から `_register` の直前に呼ばれる。
30
+ * renderer を必要とするエフェクト(FluidEffect 等)はこれを override して受け取る。
31
+ * @internal
32
+ */
33
+ _setRenderer?(_renderer: WebGLRenderer): void;
34
+ update(_time: number, _mouse?: Vector2): void;
35
+ resize?(_width: number, _height: number): void;
36
+ /**
37
+ * lil-gui の親 GUI / Folder を受け取り、自分用のフォルダや control を生やす任意フック。
38
+ * `WebGLApp.addEffect()` / `DomPlane.addEffect()` 経由でエフェクトを登録すると、
39
+ * `showGUI` が無効でない限り自動で呼ばれる。
40
+ *
41
+ * 典型例:
42
+ * ```ts
43
+ * setupGUI(gui: GUI) {
44
+ * const folder = gui.addFolder('MyEffect');
45
+ * folder.add(this, 'intensity', 0, 1);
46
+ * }
47
+ * ```
48
+ *
49
+ * @param gui lil-gui の親 GUI(通常は WebGLApp の root GUI)
50
+ * @returns 作った folder(任意)。WebGLApp 側で dispose する時の参照に使える。
51
+ */
52
+ setupGUI?(gui: GUI): GUI | void;
53
+ /** リソース解放。サブクラスで RT などを持つ場合に override する。 */
54
+ dispose?(): void;
55
+ setUniform(key: string, value: unknown): void;
56
+ getUniform(key: string): IUniform | undefined;
57
+ getPass(): EffectPass | null;
58
+ }
package/dist/index.cjs ADDED
@@ -0,0 +1,31 @@
1
+ "use strict";var q=Object.create;var w=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var K=Object.getPrototypeOf,J=Object.prototype.hasOwnProperty;var Q=(l,e,t)=>e in l?w(l,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):l[e]=t;var Z=(l,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of $(e))!J.call(l,o)&&o!==t&&w(l,o,{get:()=>e[o],enumerable:!(i=X(e,o))||i.enumerable});return l};var R=(l,e,t)=>(t=l!=null?q(K(l)):{},Z(e||!l||!l.__esModule?w(t,"default",{value:l,enumerable:!0}):t,l));var s=(l,e,t)=>Q(l,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ee=require("three"),te=require("three/examples/jsm/controls/OrbitControls.js"),se=require("three/examples/jsm/loaders/GLTFLoader.js");function ie(l){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(l){for(const t in l)if(t!=="default"){const i=Object.getOwnPropertyDescriptor(l,t);Object.defineProperty(e,t,i.get?i:{enumerable:!0,get:()=>l[t]})}}return e.default=l,Object.freeze(e)}const a=ie(ee),m=60,E=.1,I=1e4,y=1,A={x:0,y:0,z:0},L=16777215,O=.5,z=16777215,Y=1,g={x:1,y:1,z:1};class b{constructor(e){s(this,"rect");s(this,"instance");this.rect=e;const t=m/2*(Math.PI/180),i=Math.max(1,e.width),o=Math.max(1,e.height),r=o/2/Math.tan(t);this.instance=new a.PerspectiveCamera(m,i/o,E,I),this.instance.position.z=r}resize(e){if(e.width<=0||e.height<=0)return;this.rect=e;const t=Math.max(1,e.width),i=Math.max(1,e.height),o=m/2*(Math.PI/180),r=i/2/Math.tan(o);this.instance.aspect=t/i,this.instance.position.z=r,this.instance.updateProjectionMatrix()}}class G{constructor(e){s(this,"ambientLight");s(this,"directionalLight");s(this,"scene");this.scene=e,this.ambientLight=new a.AmbientLight(L,O),this.scene.add(this.ambientLight),this.directionalLight=new a.DirectionalLight(z,Y),this.directionalLight.position.set(g.x,g.y,g.z),this.scene.add(this.directionalLight),this.scene.add(this.directionalLight.target)}}class M{constructor(e,t){s(this,"element");s(this,"canvasRect");s(this,"positionInfo");s(this,"rect");s(this,"_outPosition",{x:0,y:0});this.element=e,this.canvasRect=t,this.rect=new DOMRect,this.positionInfo={pageTop:0,pageLeft:0,isFixed:!1},this.updatePositionInfo()}updatePositionInfo(){this.rect=this.element.getBoundingClientRect();const e=window.scrollY,t=window.scrollX;this.positionInfo.isFixed?(this.positionInfo.pageTop=this.rect.top,this.positionInfo.pageLeft=this.rect.left):(this.positionInfo.pageTop=this.rect.top+e,this.positionInfo.pageLeft=this.rect.left+t)}refreshPositionType(){const e=window.getComputedStyle(this.element).position;this.positionInfo.isFixed=e==="fixed"||e==="sticky"}calculateWebGLPosition(e=0,t=0){let i,o;return this.positionInfo.isFixed?(i=this.canvasRect.left+this.canvasRect.width/2,o=this.canvasRect.top+this.canvasRect.height/2):(i=this.canvasRect.left+e+this.canvasRect.width/2,o=this.canvasRect.top+t+this.canvasRect.height/2),this._outPosition.x=this.positionInfo.pageLeft+this.rect.width/2-i,this._outPosition.y=-(this.positionInfo.pageTop+this.rect.height/2-o),this._outPosition}setCanvasRect(e){this.canvasRect=e}get isFixed(){return this.positionInfo.isFixed}get pageTop(){return this.positionInfo.pageTop}get pageLeft(){return this.positionInfo.pageLeft}}const oe=`
2
+ varying vec2 vUv;
3
+ void main() {
4
+ vUv = uv;
5
+ gl_Position = vec4(position, 1.0);
6
+ }
7
+ `;class C{constructor(e){s(this,"material");s(this,"enabled",!0);this.material=e}setUniform(e,t){this.material.uniforms[e]!==void 0&&(this.material.uniforms[e].value=t)}getUniform(e){return this.material.uniforms[e]}}class k{constructor(e,t,i){s(this,"renderer");s(this,"passes",[]);s(this,"targetA");s(this,"targetB");s(this,"postScene");s(this,"postCamera");s(this,"postMesh");s(this,"geometry");s(this,"_disposed",!1);this.renderer=e;const o=e.getPixelRatio(),r=Math.max(1,Math.floor(t*o)),h=Math.max(1,Math.floor(i*o)),c={minFilter:a.LinearFilter,magFilter:a.LinearFilter,format:a.RGBAFormat,stencilBuffer:!1};this.targetA=new a.WebGLRenderTarget(r,h,c),this.targetB=new a.WebGLRenderTarget(r,h,c),this.postScene=new a.Scene,this.postCamera=new a.OrthographicCamera(-1,1,1,-1,0,1),this.geometry=new a.PlaneGeometry(2,2),this.postMesh=new a.Mesh(this.geometry),this.postScene.add(this.postMesh)}addEffect(e){if(this._disposed)throw new Error("[EffectComposer] dispose 済みのインスタンスでは addEffect() できません。");const t=new a.ShaderMaterial({uniforms:{tDiffuse:{value:null},...e.uniforms},vertexShader:oe,fragmentShader:e.fragmentShader,transparent:!0}),i=new C(t);return this.passes.push(i),i}removeEffect(e){if(this._disposed)return!1;const t=this.passes.indexOf(e);return t<0?!1:(this.passes.splice(t,1),e.material.dispose(),!0)}render(e,t){if(this._disposed)return;let i=0,o=-1;const r=this.passes;for(let n=0,d=r.length;n<d;n++)r[n].enabled&&(i++,o=n);if(i===0){this.renderer.setRenderTarget(null),this.renderer.render(e,t);return}this.renderer.setRenderTarget(this.targetA),this.renderer.render(e,t);let h=this.targetA,c=this.targetB;for(let n=0;n<r.length;n++){const d=r[n];if(!d.enabled)continue;const u=n===o;if(d.material.uniforms.tDiffuse.value=h.texture,this.postMesh.material=d.material,this.renderer.setRenderTarget(u?null:c),this.renderer.render(this.postScene,this.postCamera),!u){const f=h;h=c,c=f}}}resize(e,t){if(this._disposed)return;const i=this.renderer.getPixelRatio(),o=Math.max(1,Math.floor(e*i)),r=Math.max(1,Math.floor(t*i));this.targetA.setSize(o,r),this.targetB.setSize(o,r)}dispose(){if(!this._disposed){this._disposed=!0,this.targetA.dispose(),this.targetB.dispose(),this.geometry.dispose();for(const e of this.passes)e.material.dispose();this.passes=[]}}}const re=`
8
+ varying vec2 vUv;
9
+ void main() {
10
+ vUv = uv;
11
+ gl_Position = vec4(position, 1.0);
12
+ }
13
+ `;class D{constructor(e,t,i,o,r){s(this,"renderer");s(this,"sourceMesh");s(this,"passes",[]);s(this,"targetA");s(this,"targetB");s(this,"localScene");s(this,"localCamera");s(this,"localMesh");s(this,"postScene");s(this,"postCamera");s(this,"postMesh");s(this,"postGeo");s(this,"proxyMesh");s(this,"proxyMaterial");s(this,"proxyGeo");s(this,"mainScene");s(this,"_bypassed",!1);s(this,"_disposed",!1);this.renderer=e,this.sourceMesh=t,this.mainScene=i;const h=e.getPixelRatio(),c=Math.max(1,Math.floor(o*h)),n=Math.max(1,Math.floor(r*h)),d={minFilter:a.LinearFilter,magFilter:a.LinearFilter,format:a.RGBAFormat,stencilBuffer:!1};this.targetA=new a.WebGLRenderTarget(c,n,d),this.targetB=new a.WebGLRenderTarget(c,n,d),this.localScene=new a.Scene,this.localCamera=new a.OrthographicCamera(-o/2,o/2,r/2,-r/2,.1,10),this.localCamera.position.set(0,0,1),this.localMesh=new a.Mesh(t.geometry,t.material),this.localMesh.position.set(0,0,0),this.localMesh.scale.copy(t.scale),this.localScene.add(this.localMesh),this.postScene=new a.Scene,this.postCamera=new a.OrthographicCamera(-1,1,1,-1,0,1),this.postGeo=new a.PlaneGeometry(2,2),this.postMesh=new a.Mesh(this.postGeo),this.postScene.add(this.postMesh),i.remove(t),this.proxyMaterial=new a.MeshBasicMaterial({map:this.targetA.texture,transparent:!0}),this.proxyGeo=new a.PlaneGeometry(1,1),this.proxyMesh=new a.Mesh(this.proxyGeo,this.proxyMaterial),this.proxyMesh.position.copy(t.position),this.proxyMesh.scale.copy(t.scale),this.proxyMesh.quaternion.copy(t.quaternion),i.add(this.proxyMesh)}addEffect(e){if(this._disposed)throw new Error("[PlaneComposer] dispose 済みのインスタンスでは addEffect() できません。");const t=new a.ShaderMaterial({uniforms:{tDiffuse:{value:null},...e.uniforms},vertexShader:re,fragmentShader:e.fragmentShader,transparent:!0}),i=new C(t);return this.passes.push(i),i}removeEffect(e){if(this._disposed)return!1;const t=this.passes.indexOf(e);return t<0?!1:(this.passes.splice(t,1),e.material.dispose(),!0)}enterBypass(){this._bypassed||(this.mainScene.add(this.sourceMesh),this.proxyMesh.visible=!1,this._bypassed=!0)}exitBypass(){this._bypassed&&(this.mainScene.remove(this.sourceMesh),this.proxyMesh.visible=!0,this._bypassed=!1)}render(){if(this._disposed)return;if(!this.sourceMesh.visible){this.proxyMesh.visible=!1;return}let e=0;for(const o of this.passes)o.enabled&&e++;if(e===0){this.enterBypass();return}this.exitBypass(),this.proxyMesh.visible=!0,this.proxyMesh.position.copy(this.sourceMesh.position),this.proxyMesh.scale.copy(this.sourceMesh.scale),this.proxyMesh.quaternion.copy(this.sourceMesh.quaternion),this.localMesh.scale.copy(this.sourceMesh.scale),this.renderer.setRenderTarget(this.targetA),this.renderer.render(this.localScene,this.localCamera);let t=this.targetA,i=this.targetB;for(const o of this.passes){if(!o.enabled)continue;o.material.uniforms.tDiffuse.value=t.texture,this.postMesh.material=o.material,this.renderer.setRenderTarget(i),this.renderer.render(this.postScene,this.postCamera);const r=t;t=i,i=r}this.proxyMaterial.map=t.texture,this.renderer.setRenderTarget(null)}resize(e,t){if(this._disposed)return;const i=this.renderer.getPixelRatio(),o=Math.max(1,Math.floor(e*i)),r=Math.max(1,Math.floor(t*i));this.targetA.setSize(o,r),this.targetB.setSize(o,r),this.localCamera.left=-e/2,this.localCamera.right=e/2,this.localCamera.top=t/2,this.localCamera.bottom=-t/2,this.localCamera.updateProjectionMatrix()}dispose(){if(!this._disposed){this._disposed=!0,this.mainScene.remove(this.proxyMesh),this._bypassed||this.mainScene.add(this.sourceMesh),this._bypassed=!1,this.targetA.dispose(),this.targetB.dispose(),this.postGeo.dispose(),this.proxyGeo.dispose(),this.proxyMaterial.dispose();for(const e of this.passes)e.material.dispose();this.passes=[]}}}const F=new a.TextureLoader;F.setCrossOrigin("anonymous");const ne=`
14
+ varying vec2 vUv;
15
+
16
+ void main() {
17
+ vUv = uv;
18
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
19
+ }
20
+ `,ae=`
21
+ uniform sampler2D uTexture;
22
+ uniform float uTime;
23
+ uniform vec2 uResolution;
24
+ varying vec2 vUv;
25
+
26
+ void main() {
27
+ vec4 texColor = texture2D(uTexture, vUv);
28
+ gl_FragColor = texColor;
29
+ }
30
+ `;class V{constructor(e,t,i,o,r={},h){s(this,"element");s(this,"texture");s(this,"mesh");s(this,"geometry");s(this,"material");s(this,"scene");s(this,"clock");s(this,"positionCalculator");s(this,"canvasRect");s(this,"isVisible");s(this,"updateRectEveryFrame");s(this,"observer");s(this,"destroyed");s(this,"planeComposer",null);s(this,"renderer");s(this,"effects",[]);s(this,"_effectMouseUV",new a.Vector2);s(this,"guiProvider",null);s(this,"ownsTexture",!1);s(this,"crossOrigin");if(this.element=e,this.scene=t,this.renderer=o,this.texture=null,this.destroyed=!1,this.updateRectEveryFrame=r.updateRectEveryFrame||!1,this.crossOrigin=r.crossOrigin,this.clock=h??new a.Clock,this.canvasRect=i,this.positionCalculator=e?new M(e,i):null,this.isVisible=!e,this.observer=null,e){let d=!1;const u=r.inViewRepeat??!1;this.observer=new IntersectionObserver(f=>{const p=f[0];this.isVisible=p.isIntersecting,this.mesh.visible=this.isVisible,p.isIntersecting?r.onInView&&(!d||u)&&(d=!0,r.onInView(this)):u&&(d=!1,r.onOutView?.(this))},{rootMargin:r.inViewRootMargin??"100%"}),this.observer.observe(e)}const c=r.segments??1;this.geometry=new a.PlaneGeometry(1,1,c,c);const n={uTexture:{value:null},uAlpha:{value:1},uResolution:{value:new a.Vector2},uTime:{value:0},uIsHovered:{value:!1},uMouseUV:{value:new a.Vector2(0,0)},...r.uniforms};this.material=new a.ShaderMaterial({transparent:!0,uniforms:n,vertexShader:r.vertexShader||ne,fragmentShader:r.fragmentShader||ae}),this.mesh=new a.Mesh(this.geometry,this.material),this.mesh.visible=this.isVisible,this.scene.add(this.mesh),this.init()}_tickRead(){!this.isVisible||!this.positionCalculator||this.updateRectEveryFrame&&this.positionCalculator.updatePositionInfo()}_tickApply(e,t,i){this.isVisible&&(this.material.uniforms.uTime.value=e,this.positionCalculator&&this.setPosition(t,i))}_tickRenderComposer(){this.isVisible&&this.planeComposer?.render()}init(){this.loadTexture(),this.resize()}loadTexture(){const e=this.element?.getAttribute("data-texture");if(e){let t;this.crossOrigin!==void 0?(t=new a.TextureLoader,t.setCrossOrigin(this.crossOrigin)):t=F,t.load(e,i=>{if(this.destroyed){i.dispose();return}this.texture=i,this.ownsTexture=!0,this.material.uniforms.uTexture.value=i},void 0,i=>{console.error(`Failed to load texture: ${e}`,i)})}else this.material.uniforms.uTexture.value&&(this.texture=this.material.uniforms.uTexture.value,this.ownsTexture=!1)}updateSize(){if(this.positionCalculator){const e=this.positionCalculator.rect;this.mesh.scale.set(e.width,e.height,1),this.material.uniforms.uResolution.value.set(e.width,e.height)}else this.mesh.scale.set(this.canvasRect.width,this.canvasRect.height,1),this.material.uniforms.uResolution.value.set(this.canvasRect.width,this.canvasRect.height)}setPosition(e,t){if(!this.positionCalculator)return;const{x:i,y:o}=this.positionCalculator.calculateWebGLPosition(e,t);this.mesh.position.set(i,o,0)}setCanvasRect(e){this.canvasRect=e,this.positionCalculator&&this.positionCalculator.setCanvasRect(e)}resize(){if(this.positionCalculator){this.positionCalculator.refreshPositionType(),this.positionCalculator.updatePositionInfo();const e=window.scrollY,t=window.scrollX;this.updateSize(),this.setPosition(t,e)}else this.updateSize(),this.mesh.position.set(0,0,0);if(this.planeComposer){const e=this.positionCalculator?.rect??this.canvasRect;this.planeComposer.resize(e.width,e.height);for(const t of this.effects)t.resize?.(e.width,e.height)}}getMesh(){return this.mesh}reloadTexture(){this.texture&&this.ownsTexture&&(this.texture.dispose(),this.texture=null,this.material.uniforms.uTexture.value=null,this.ownsTexture=!1),this.loadTexture()}setTexture(e,t=!1){this.texture&&this.ownsTexture&&this.texture!==e&&this.texture.dispose(),this.texture=e,this.ownsTexture=t,this.material.uniforms.uTexture.value=e}updateEffects(e,t,i=window.scrollX,o=window.scrollY){if(this.effects.length===0)return;if(t)if(this.positionCalculator){const c=this.positionCalculator,n=this.canvasRect,d=c.rect.width,u=c.rect.height;if(n.width>0&&n.height>0&&d>0&&u>0){const f=c.isFixed?c.pageLeft:c.pageLeft-i,p=c.isFixed?c.pageTop:c.pageTop-o,B=(f-n.left)/n.width,j=(p-n.top)/n.height,W=d/n.width,x=u/n.height,N=1-j-x,v=(t.x-B)/W,_=(t.y-N)/x;v>=0&&v<=1&&_>=0&&_<=1&&this.material.uniforms.uMouseUV.value.set(v,_)}}else this.material.uniforms.uMouseUV.value.copy(t);const r=this.material.uniforms.uMouseUV.value;this._effectMouseUV.copy(r);const h=this.effects;for(let c=0,n=h.length;c<n;c++){const d=h[c];d.enabled&&d.update(e,this._effectMouseUV)}}getMouseUV(){return this.material.uniforms.uMouseUV.value}isHovered(){return this.material.uniforms.uIsHovered.value}setHoverInfo(e,t){this.material.uniforms.uIsHovered.value=e,t&&this.material.uniforms.uMouseUV.value.copy(t)}enableEffects(){if(this.planeComposer)return this.planeComposer;const e=this.positionCalculator?.rect??this.canvasRect;return this.planeComposer=new D(this.renderer,this.mesh,this.scene,e.width,e.height),this.planeComposer}addEffect(e){const t=this.enableEffects();e._setRenderer?.(this.renderer),e._register(t);const i=this.positionCalculator?.rect??this.canvasRect;return e.resize?.(i.width,i.height),this.guiProvider&&e.setupGUI&&this.guiProvider().then(o=>{this.destroyed||e.setupGUI(o)}).catch(o=>{console.warn("[DomPlane] effect.setupGUI に渡す lil-gui の読み込みに失敗しました。npm install lil-gui してください。",o)}),this.effects.push(e),e}removeEffect(e){const t=this.effects.indexOf(e);if(t<0)return!1;this.effects.splice(t,1);const i=e.getPass();return i&&this.planeComposer&&this.planeComposer.removeEffect(i),e.dispose?.(),!0}_setGuiProvider(e){this.guiProvider=e}destroy(){if(!this.destroyed){this.destroyed=!0,this.observer?.disconnect();for(const e of this.effects)e.dispose?.();this.effects=[],this.planeComposer&&(this.planeComposer.dispose(),this.planeComposer=null),this.scene.remove(this.mesh),this.geometry.dispose(),this.material.dispose(),this.texture&&this.ownsTexture&&this.texture.dispose(),this.texture=null}}}class H{constructor(e,t,i,o){s(this,"element");s(this,"model");s(this,"loader");s(this,"mainScene");s(this,"canvasRect");s(this,"options");s(this,"isVisible");s(this,"positionCalculator");s(this,"updateRectEveryFrame");s(this,"observer");s(this,"destroyed");this.element=e,this.mainScene=t,this.canvasRect=i,this.model=null,this.loader=new se.GLTFLoader,this.options={scale:y,offset:{...A},...o},this.updateRectEveryFrame=o.updateRectEveryFrame??!1,this.positionCalculator=e?new M(e,i):null,this.isVisible=!e,this.destroyed=!1,this.observer=null,e&&(this.observer=new IntersectionObserver(r=>{this.isVisible=r[0].isIntersecting,this.model&&(this.model.visible=this.isVisible)},{rootMargin:"100%"}),this.observer.observe(e)),this.loadModel()}_tickRead(){!this.model||!this.isVisible||!this.positionCalculator||this.updateRectEveryFrame&&this.positionCalculator.updatePositionInfo()}_tickApply(e,t){!this.model||!this.isVisible||this.setPosition(e,t)}loadModel(){const e=this.options.modelPath;if(!e){console.error("Dom3DObject: modelPath が指定されていません。");return}this.loader.load(e,t=>{this.destroyed||(this.model=t.scene,this.setupModel())},void 0,t=>{console.error(`Failed to load model: ${e}`,t)})}setupModel(){if(this.model){if(this.positionCalculator){this.positionCalculator.refreshPositionType(),this.positionCalculator.updatePositionInfo();const e=new a.Box3().setFromObject(this.model),t=new a.Vector3,i=new a.Vector3;e.getSize(t),e.getCenter(i);const o=Math.max(t.x,t.y,t.z)||1,r=this.model;r.position.sub(i).multiplyScalar(1/o),r.scale.multiplyScalar(1/o);const h=new a.Group;h.add(r),this.model=h}this.model.visible=this.isVisible,this.mainScene.add(this.model),this.applyScale(),this.setPosition(window.scrollX,window.scrollY)}}applyScale(){if(!this.model)return;const e=this.options.scale??y;if(this.positionCalculator){const{width:t,height:i}=this.positionCalculator.rect,h=((this.options.fitMode??"maxSide")==="contain"?Math.min(t,i):Math.max(t,i))*e;this.model.scale.set(h,h,h)}else this.model.scale.set(e,e,e)}setPosition(e,t){if(!this.model)return;const i=this.options.offset;let o=i.x,r=i.y;const h=i.z;if(this.positionCalculator){const c=this.positionCalculator.calculateWebGLPosition(e,t);o+=c.x,r+=c.y}this.model.position.set(o,r,h)}setCanvasRect(e){this.canvasRect=e,this.positionCalculator?.setCanvasRect(e)}resize(){if(!this.positionCalculator){this.applyScale();return}this.positionCalculator.refreshPositionType(),this.positionCalculator.updatePositionInfo(),this.applyScale(),this.setPosition(window.scrollX,window.scrollY)}getModel(){return this.model}destroy(){this.destroyed||(this.destroyed=!0,this.observer?.disconnect(),this.model&&(this.mainScene.remove(this.model),this.model.traverse(e=>{if(!e.isMesh)return;const t=e;t.geometry.dispose();const i=Array.isArray(t.material)?t.material:[t.material];for(const o of i)o&&(he(o),o.dispose())}),this.model=null))}}function he(l){const e=["map","alphaMap","aoMap","bumpMap","displacementMap","emissiveMap","envMap","lightMap","metalnessMap","normalMap","roughnessMap","specularMap","clearcoatMap","clearcoatNormalMap","clearcoatRoughnessMap","sheenColorMap","sheenRoughnessMap","transmissionMap","thicknessMap","iridescenceMap","iridescenceThicknessMap","anisotropyMap","matcap","gradientMap"],t=l;for(const i of e){const o=t[i];o&&o.isTexture&&o.dispose()}}class U{constructor(e,t={}){s(this,"container");s(this,"_padding");s(this,"_trackStrength");s(this,"_strengthDecay");s(this,"_strength",0);s(this,"_prevScrollY",0);s(this,"_prevTime",0);s(this,"_viewportWidth",0);s(this,"_viewportHeight",0);s(this,"_enabled",!0);s(this,"_widthRatio",1);s(this,"_heightRatio",1);s(this,"_effectivePaddingPx",0);s(this,"_logicalRect",new DOMRect);s(this,"_onResize",null);s(this,"_originalStyles");s(this,"_warnedStrength",!1);this.container=e,this._padding=t.padding??0,this._trackStrength=t.trackStrength??!1,this._strengthDecay=t.strengthDecay??10,this._prevScrollY=window.scrollY,this._prevTime=performance.now()/1e3;const i=e.style;this._originalStyles={position:i.position,left:i.left,top:i.top,width:i.width,height:i.height,overflow:i.overflow,transform:i.transform,pointerEvents:i.pointerEvents,willChange:i.willChange};const o=e.getBoundingClientRect();window.innerWidth>0&&o.width>0&&(this._widthRatio=o.width/window.innerWidth),window.innerHeight>0&&o.height>0&&(this._heightRatio=o.height/window.innerHeight),this.applyContainerStyles(),this.updateSize()}applyContainerStyles(){this.container.style.position="absolute",this.container.style.left="0",this.container.style.top="0",this.container.style.overflow="hidden",this.container.style.pointerEvents="none",this.container.style.willChange="transform"}updateSize(e,t){this._viewportWidth=e??window.innerWidth*this._widthRatio,this._viewportHeight=t??window.innerHeight*this._heightRatio;const i=this._viewportHeight*this._padding;this._effectivePaddingPx=i;const o=this._viewportHeight+2*i;this.container.style.width=`${this._viewportWidth}px`,this.container.style.height=`${o}px`,this._logicalRect=new DOMRect(0,-i,this._viewportWidth,o),this._onResize?.({width:this._viewportWidth,height:o}),this.applyTransform(window.scrollX,window.scrollY)}update(e,t){if(!this._enabled)return;const i=this._viewportHeight*this._padding,o=Math.max(0,document.body.scrollHeight-t-this._viewportHeight),r=Math.min(i,o);if(r!==this._effectivePaddingPx){this._effectivePaddingPx=r;const h=this._viewportHeight+2*r;this.container.style.height=`${h}px`,this._logicalRect=new DOMRect(0,-r,this._viewportWidth,h),this._onResize?.({width:this._viewportWidth,height:h})}this.applyTransform(e,t),this._trackStrength&&this.updateStrength(t)}applyTransform(e,t){this.container.style.transform=`translate3d(${e}px, ${t-this._effectivePaddingPx}px, 0)`}updateStrength(e){const t=e-this._prevScrollY,i=performance.now()/1e3,o=i-this._prevTime;if(o>0){const r=Math.abs(t)*10/this._viewportHeight;this._strength*=Math.exp(-o*this._strengthDecay),this._strength+=Math.min(r,5)}this._prevScrollY=e,this._prevTime=i}setResizeCallback(e){this._onResize=e}get logicalRect(){return this._logicalRect}get effectivePadding(){return this._effectivePaddingPx}get strength(){return this._trackStrength?Math.min(1,this._strength):0}get padding(){return this._padding}set enabled(e){this._enabled=e}get enabled(){return this._enabled}destroy(){const e=this._originalStyles,t=this.container.style;t.position=e.position,t.left=e.left,t.top=e.top,t.width=e.width,t.height=e.height,t.overflow=e.overflow,t.transform=e.transform,t.pointerEvents=e.pointerEvents,t.willChange=e.willChange}}class le{constructor(e,t={}){s(this,"container");s(this,"canvas");s(this,"scene");s(this,"renderer");s(this,"camera");s(this,"light");s(this,"controls");s(this,"updateCallbacks");s(this,"resizeCallbacks");s(this,"rect");s(this,"domPlanes");s(this,"dom3DObjects");s(this,"clock");s(this,"scrollSync",null);s(this,"options");s(this,"rafId",0);s(this,"resizeTimer",null);s(this,"eventAbort",new AbortController);s(this,"_mouseAbort",null);s(this,"_canvasRect",null);s(this,"mouse");s(this,"prevMouse");s(this,"_mouseInside",!1);s(this,"_ndcBuf",new a.Vector2);s(this,"_mouseDeltaBuf",new a.Vector2);s(this,"raycaster");s(this,"hoveredPlane");s(this,"domPlaneMeshes",[]);s(this,"postEffect",null);s(this,"internalComposer",null);s(this,"effects",[]);s(this,"stats",null);s(this,"gui",null);s(this,"_guiLoadPromise",null);s(this,"destroyed",!1);s(this,"invalidateCanvasRect",()=>{this._canvasRect=null});s(this,"_onScrollSyncResize",()=>{if(this.destroyed||!this.scrollSync||(this.rect=this.scrollSync.logicalRect,this.rect.width<=0||this.rect.height<=0))return;this.camera.resize(this.rect),this.renderer.setSize(this.rect.width,this.rect.height);for(let t=0,i=this.domPlanes.length;t<i;t++){const o=this.domPlanes[t];o.setCanvasRect(this.rect),o.element||o.resize()}for(let t=0,i=this.dom3DObjects.length;t<i;t++)this.dom3DObjects[t].setCanvasRect(this.rect);this.postEffect?.resize(this.rect.width,this.rect.height);const e=this.effects;for(let t=0,i=e.length;t<i;t++)e[t].resize?.(this.rect.width,this.rect.height)});s(this,"animate",()=>{if(this.destroyed)return;this.rafId=requestAnimationFrame(this.animate),this.stats?.begin(),this.controls&&this.controls.update();const e=this.domPlanes,t=this.dom3DObjects,i=window.scrollX,o=window.scrollY;this._updateHoverFromMouse();const r=this.updateCallbacks;for(let n=0,d=r.length;n<d;n++)r[n]();const h=this.clock.getElapsedTime(),c=this.effects;for(let n=0,d=c.length;n<d;n++){const u=c[n];u.enabled&&u.update(h,this.mouse)}this.scrollSync?.update(i,o);for(let n=0,d=e.length;n<d;n++)e[n]._tickRead();for(let n=0,d=t.length;n<d;n++)t[n]._tickRead();for(let n=0,d=e.length;n<d;n++)e[n].updateEffects(h,this.mouse,i,o);for(let n=0,d=e.length;n<d;n++)e[n]._tickApply(h,i,o);for(let n=0,d=t.length;n<d;n++)t[n]._tickApply(i,o);for(let n=0,d=e.length;n<d;n++)e[n]._tickRenderComposer();if(this.destroyed){this.stats?.end();return}this.postEffect?this.postEffect.render(this.scene,this.camera.instance):this.renderer.render(this.scene,this.camera.instance),this.prevMouse.copy(this.mouse),this.stats?.end()});const i=typeof e=="string"?document.querySelector(e):e;if(!i)throw new Error(`Container not found: ${e}`);if(this.container=i,this.canvas=document.createElement("canvas"),this.container.appendChild(this.canvas),this.rect=this.container.getBoundingClientRect(),this.scene=new a.Scene,this.renderer=new a.WebGLRenderer({canvas:this.canvas,antialias:!0,alpha:!0}),this.camera=new b(this.rect),this.light=new G(this.scene),this.controls=null,this.updateCallbacks=[],this.resizeCallbacks=[],this.domPlanes=[],this.dom3DObjects=[],this.clock=new a.Clock,this.options={enableMouseTracking:!0,showGUI:!0,...t},this.mouse=new a.Vector2(.5,.5),this.prevMouse=new a.Vector2(.5,.5),this.raycaster=new a.Raycaster,this.hoveredPlane=null,t.scrollSync){const o=typeof t.scrollSync=="object"?t.scrollSync:{};this.scrollSync=new U(this.container,o),this.rect=this.scrollSync.logicalRect,this.scrollSync.setResizeCallback(()=>this._onScrollSyncResize())}this.init(),t.showStats&&import("stats.js").then(({default:o})=>{this.destroyed||(this.stats=new o,this.stats.showPanel(0),(t.statsParent??document.body).appendChild(this.stats.dom))}).catch(o=>{console.warn("[WebGLApp] showStats: true ですが stats.js が読み込めませんでした。npm install stats.js してください。",o)}),this.setupEventListeners(),this.animate()}init(){this.scene.background=null,this.renderer.setSize(this.rect.width,this.rect.height),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,this.options.maxPixelRatio??2)),this.camera.resize(this.rect),this.renderer.outputColorSpace=this.options.outputColorSpace??a.SRGBColorSpace}getScene(){return this.scene}getCamera(){return this.camera}getRenderer(){return this.renderer}getLight(){return this.light}getViewPort(){return this.rect}getMouse(){return this.mouse}getPrevMouse(){return this.prevMouse}getMouseDelta(){return this._mouseDeltaBuf.copy(this.mouse).sub(this.prevMouse)}getScrollSync(){return this.scrollSync}addObject(e){this.scene.add(e)}removeObject(e){this.scene.remove(e)}createPlane(e,t){if(this.destroyed)throw new Error("[WebGLApp] createPlane(): destroy 済みのインスタンスでは使えません。");let i=null;if(e!=null&&(i=typeof e=="string"?document.querySelector(e):e,!i))throw new Error(`Element not found: ${e}`);const o=new V(i,this.scene,this.rect,this.renderer,t,this.clock);return this.options.showGUI!==!1&&o._setGuiProvider(()=>this._ensureGUIAsync()),this.domPlanes.push(o),i&&this.domPlaneMeshes.push(o.getMesh()),o}removePlane(e){if(this.destroyed)return;const t=this.domPlanes.indexOf(e);if(t>-1){this.domPlanes.splice(t,1);const i=this.domPlaneMeshes.indexOf(e.getMesh());i>-1&&this.domPlaneMeshes.splice(i,1),e.destroy()}}create3DObject(e,t){if(this.destroyed)throw new Error("[WebGLApp] create3DObject(): destroy 済みのインスタンスでは使えません。");let i=null;if(e!=null&&(i=typeof e=="string"?document.querySelector(e):e,!i))throw new Error(`Element not found: ${e}`);const o=new H(i,this.scene,this.rect,t);return this.dom3DObjects.push(o),o}remove3DObject(e){if(this.destroyed)return;const t=this.dom3DObjects.indexOf(e);t>-1&&(this.dom3DObjects.splice(t,1),e.destroy())}addUpdateCallback(e){return this.destroyed?()=>{}:(this.updateCallbacks.push(e),()=>{const t=this.updateCallbacks.indexOf(e);t>-1&&this.updateCallbacks.splice(t,1)})}addResizeCallback(e){return this.destroyed?()=>{}:(this.resizeCallbacks.push(e),()=>{const t=this.resizeCallbacks.indexOf(e);t>-1&&this.resizeCallbacks.splice(t,1)})}enableOrbitControls(){if(this.destroyed)throw new Error("[WebGLApp] enableOrbitControls(): destroy 済みのインスタンスでは使えません。");return this.controls||(this.scrollSync&&(this.canvas.style.pointerEvents="auto"),this.controls=new te.OrbitControls(this.camera.instance,this.canvas)),this.controls}getControls(){return this.controls}addEffect(e){if(this.destroyed)throw new Error("[WebGLApp] addEffect(): destroy 済みのインスタンスでは使えません。");return this.postEffect&&this.postEffect!==this.internalComposer&&console.warn("[WebGLApp] addEffect() を呼ぶ前に setPostEffect() でカスタム postEffect が設定されています。内部 EffectComposer で上書きします。カスタム postEffect は手動で dispose してください。"),this.internalComposer||(this.internalComposer=new k(this.renderer,this.rect.width,this.rect.height),this.postEffect=this.internalComposer),e._setRenderer?.(this.renderer),e._register(this.internalComposer),e.resize?.(this.rect.width,this.rect.height),this.options.showGUI&&e.setupGUI&&this._ensureGUIAsync().then(t=>{this.destroyed||e.setupGUI(t)}).catch(t=>{console.warn("[WebGLApp] showGUI: true ですが lil-gui が読み込めませんでした。npm install lil-gui してください。",t)}),this.effects.push(e),e}_ensureGUIAsync(){return this.gui?Promise.resolve(this.gui):(this._guiLoadPromise||(this._guiLoadPromise=import("lil-gui").then(({default:e})=>(this.gui||(this.gui=new e({title:this.options.guiTitle??"Effects"})),this.gui))),this._guiLoadPromise)}getGUI(){return this.options.showGUI===!1?null:this.gui}getGUIAsync(){return this.options.showGUI===!1?Promise.resolve(null):this._ensureGUIAsync()}setPostEffect(e){if(this.destroyed)throw new Error("[WebGLApp] setPostEffect(): destroy 済みのインスタンスでは使えません。");this.effects.length>0&&(console.warn("[WebGLApp] setPostEffect() が呼ばれましたが、addEffect() で追加した effect が既に存在します。内部 EffectComposer を破棄してカスタム postEffect に差し替えます。事前に clearEffects() を呼ぶことを推奨します。"),this.clearEffects()),this.postEffect=e}removeEffect(e){if(this.destroyed)return!1;const t=this.effects.indexOf(e);if(t<0)return!1;this.effects.splice(t,1);const i=e.getPass();return i&&this.internalComposer&&this.internalComposer.removeEffect(i),e.dispose?.(),!0}clearEffects(){if(!this.destroyed){for(const e of this.effects)e.dispose?.();this.effects=[],this.postEffect?.dispose(),this.postEffect=null,this.internalComposer=null}}removePostEffect(){this.clearEffects()}setupEventListeners(){const e=this.eventAbort.signal;window.addEventListener("resize",()=>{this.resizeTimer&&clearTimeout(this.resizeTimer),this.resizeTimer=setTimeout(()=>this.onResize(),100)},{signal:e}),this.scrollSync||window.addEventListener("scroll",this.invalidateCanvasRect,{signal:e,passive:!0}),this.options.enableMouseTracking&&this.setMouseTrackingEnabled(!0)}setMouseTrackingEnabled(e){if(!this.destroyed)if(this.options.enableMouseTracking=e,e){if(this._mouseAbort)return;this._mouseAbort=new AbortController,window.addEventListener("mousemove",t=>this.onMouseMove(t),{signal:this._mouseAbort.signal})}else this._mouseAbort?.abort(),this._mouseAbort=null,this._mouseInside=!1,this.hoveredPlane&&(this.hoveredPlane.setHoverInfo(!1,null),this.hoveredPlane=null)}getCanvasRect(){return this._canvasRect||(this._canvasRect=this.canvas.getBoundingClientRect()),this._canvasRect}onResize(){if(this._canvasRect=null,this.scrollSync?(this.scrollSync.updateSize(),this.rect=this.scrollSync.logicalRect):this.rect=this.container.getBoundingClientRect(),this.rect.width<=0||this.rect.height<=0)return;this.camera.resize(this.rect),this.renderer.setSize(this.rect.width,this.rect.height),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,this.options.maxPixelRatio??2));const e=this.domPlanes;for(let r=0,h=e.length;r<h;r++){const c=e[r];c.setCanvasRect(this.rect),c.resize()}const t=this.dom3DObjects;for(let r=0,h=t.length;r<h;r++){const c=t[r];c.setCanvasRect(this.rect),c.resize()}this.postEffect?.resize(this.rect.width,this.rect.height);const i=this.effects;for(let r=0,h=i.length;r<h;r++)i[r].resize?.(this.rect.width,this.rect.height);const o=this.resizeCallbacks;for(let r=0,h=o.length;r<h;r++)o[r]()}onMouseMove(e){const t=this.getCanvasRect(),i=e.clientX>=t.left&&e.clientX<=t.right&&e.clientY>=t.top&&e.clientY<=t.bottom;this._mouseInside=i,i&&(this.mouse.x=(e.clientX-t.left)/t.width,this.mouse.y=1-(e.clientY-t.top)/t.height)}_updateHoverFromMouse(){if(!this.options.enableMouseTracking||this.domPlaneMeshes.length===0)return;if(!this._mouseInside){this.hoveredPlane&&(this.hoveredPlane.setHoverInfo(!1,null),this.hoveredPlane=null);return}this._ndcBuf.set(this.mouse.x*2-1,this.mouse.y*2-1),this.raycaster.setFromCamera(this._ndcBuf,this.camera.instance);const e=this.raycaster.intersectObjects(this.domPlaneMeshes,!1);if(e.length>0){const t=e[0];if(t.uv){const i=t.object,o=this.domPlanes.find(r=>r.getMesh()===i);if(o){this.hoveredPlane&&this.hoveredPlane!==o&&this.hoveredPlane.setHoverInfo(!1,null),o.setHoverInfo(!0,t.uv),this.hoveredPlane=o;return}}}this.hoveredPlane&&(this.hoveredPlane.setHoverInfo(!1,null),this.hoveredPlane=null)}destroy(){if(!this.destroyed){this.destroyed=!0,cancelAnimationFrame(this.rafId),this.resizeTimer&&clearTimeout(this.resizeTimer),this.eventAbort.abort(),this._mouseAbort?.abort(),this._mouseAbort=null,this.domPlanes.forEach(e=>e.destroy()),this.dom3DObjects.forEach(e=>e.destroy()),this.domPlanes=[],this.dom3DObjects=[],this.domPlaneMeshes=[],this.updateCallbacks=[],this.resizeCallbacks=[],this.scrollSync?.destroy(),this.scrollSync=null;for(const e of this.effects)e.dispose?.();this.effects=[],this.postEffect?.dispose(),this.postEffect=null,this.internalComposer=null,this.controls?.dispose(),this.controls=null,this.renderer.dispose(),this.canvas.remove(),this.stats&&(this.stats.dom.remove(),this.stats=null),this.gui&&(this.gui.destroy(),this.gui=null)}}}const S=.01,ce=50,P=.3,T=1e3/60;class de{constructor(e={}){s(this,"_scrollY");s(this,"_lastAppliedY");s(this,"_maxScroll");s(this,"_rafId",0);s(this,"_enabled",!0);s(this,"_lineHeight");s(this,"_friction");s(this,"_touchPrevY",0);s(this,"_touchPrevTime",0);s(this,"_velocityY",0);s(this,"_isTouching",!1);s(this,"_lastTickTime",0);s(this,"_eventAbort",new AbortController);s(this,"_resizeObserver",null);s(this,"_destroyed",!1);s(this,"onWheel",e=>{if(!this._enabled)return;e.preventDefault(),this._velocityY=0;let t=e.deltaY;e.deltaMode===1?t*=this._lineHeight:e.deltaMode===2&&(t*=window.innerHeight),this._scrollY=this.clamp(this._scrollY+t)});s(this,"onTouchStart",e=>{this._enabled&&e.touches.length!==0&&(this._velocityY=0,this._isTouching=!0,this._touchPrevY=e.touches[0].clientY,this._touchPrevTime=performance.now())});s(this,"onTouchMove",e=>{if(!this._enabled||e.touches.length===0)return;e.preventDefault();const t=e.touches[0].clientY,i=performance.now(),o=this._touchPrevY-t,r=i-this._touchPrevTime;if(this._scrollY=this.clamp(this._scrollY+o),r>0){const h=o/r;this._velocityY=this._velocityY*(1-P)+h*P}this._touchPrevY=t,this._touchPrevTime=i});s(this,"onTouchEnd",()=>{this._isTouching=!1,performance.now()-this._touchPrevTime>ce&&(this._velocityY=0)});s(this,"onResize",()=>{this._maxScroll=this.calcMaxScroll(),this._scrollY=this.clamp(this._scrollY)});s(this,"tick",(e=performance.now())=>{if(this._destroyed)return;const t=this._lastTickTime===0?T:e-this._lastTickTime;if(this._lastTickTime=e,!this._isTouching&&Math.abs(this._velocityY)>S){const i=this._scrollY,o=this.clamp(this._scrollY+this._velocityY*t);this._scrollY=o,o===i?this._velocityY=0:(this._velocityY*=Math.pow(this._friction,t/T),Math.abs(this._velocityY)<S&&(this._velocityY=0))}this._scrollY!==this._lastAppliedY&&(window.scrollTo(0,this._scrollY),this._lastAppliedY=this._scrollY),this._rafId=requestAnimationFrame(this.tick)});this._lineHeight=e.lineHeight??16,this._friction=e.touchFriction??.95,this._scrollY=window.scrollY,this._lastAppliedY=this._scrollY,this._maxScroll=this.calcMaxScroll(),this.setupEventListeners(),this.setupResizeObserver(),this._rafId=requestAnimationFrame(this.tick)}calcMaxScroll(){return Math.max(0,document.documentElement.scrollHeight-window.innerHeight)}setupEventListeners(){const e=this._eventAbort.signal;window.addEventListener("wheel",this.onWheel,{passive:!1,signal:e}),window.addEventListener("touchstart",this.onTouchStart,{passive:!1,signal:e}),window.addEventListener("touchmove",this.onTouchMove,{passive:!1,signal:e}),window.addEventListener("touchend",this.onTouchEnd,{passive:!0,signal:e}),window.addEventListener("touchcancel",this.onTouchEnd,{passive:!0,signal:e}),window.addEventListener("resize",this.onResize,{signal:e})}setupResizeObserver(){typeof ResizeObserver>"u"||(this._resizeObserver=new ResizeObserver(()=>{this._maxScroll=this.calcMaxScroll(),this._scrollY=this.clamp(this._scrollY)}),this._resizeObserver.observe(document.documentElement))}clamp(e){return Math.min(this._maxScroll,Math.max(0,e))}get scrollY(){return this._scrollY}set enabled(e){const t=!this._enabled;this._enabled=e,e||(this._velocityY=0,this._isTouching=!1),e&&t&&(this._scrollY=window.scrollY,this._lastAppliedY=this._scrollY)}get enabled(){return this._enabled}destroy(){this._destroyed||(this._destroyed=!0,cancelAnimationFrame(this._rafId),this._eventAbort.abort(),this._resizeObserver?.disconnect(),this._resizeObserver=null)}}class ue{constructor(e){s(this,"scene");s(this,"camera");s(this,"rect");this.scene=new a.Scene,this.camera=new b(e),this.rect=e}resize(e){this.rect=e,this.camera.resize(e)}dispose(){}}class fe{constructor(){s(this,"pass",null);s(this,"_enabled",!0)}get enabled(){return this._enabled}set enabled(e){this._enabled=e,this.pass&&(this.pass.enabled=e)}_register(e){this.pass;const t=this.getConfig();this.pass=e.addEffect({fragmentShader:t.fragmentShader,uniforms:t.uniforms}),this.pass.enabled=this._enabled}update(e,t){}setUniform(e,t){this.pass?.setUniform(e,t)}getUniform(e){return this.pass?.getUniform(e)}getPass(){return this.pass}}exports.THREE=a;exports.AMBIENT_LIGHT_COLOR=L;exports.AMBIENT_LIGHT_INTENSITY=O;exports.BaseEffect=fe;exports.BaseScene=ue;exports.CAMERA_FAR=I;exports.CAMERA_FOV=m;exports.CAMERA_NEAR=E;exports.Camera=b;exports.DEFAULT_OFFSET=A;exports.DEFAULT_SCALE=y;exports.DIRECTIONAL_LIGHT_COLOR=z;exports.DIRECTIONAL_LIGHT_INTENSITY=Y;exports.DIRECTIONAL_LIGHT_POSITION=g;exports.Dom3DObject=H;exports.DomPlane=V;exports.DomPositionCalculator=M;exports.EffectComposer=k;exports.EffectPass=C;exports.Light=G;exports.PlaneComposer=D;exports.RafScroll=de;exports.ScrollSync=U;exports.WebGLApp=le;
31
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/constants.ts","../src/Camera.ts","../src/Light.ts","../src/DomPositionCalculator.ts","../src/EffectComposer.ts","../src/PlaneComposer.ts","../src/DomPlane.ts","../src/Dom3DObject.ts","../src/ScrollSync.ts","../src/Core.ts","../src/RafScroll.ts","../src/scenes/BaseScene.ts","../src/effects/BaseEffect.ts"],"sourcesContent":["// カメラ設定\nexport const CAMERA_FOV = 60;\nexport const CAMERA_NEAR = 0.1;\n/**\n * Camera の far plane。座標系は「DOM 1px = WebGL 1unit」。\n * カメラ距離は `viewport高 / 2 / tan(fov/2)` で ~519px (viewport=600px) 前後になるため\n * far=10000 はかなり余裕がある(カメラ距離の ~20 倍)。\n *\n * **注意**: near=0.1 / far=10000 で精度レンジが 5 桁開いており、z 軸を奥行きに\n * 積極的に使うと z-fighting / shadow map の精度劣化が出やすい。\n * 奥行き表現を多用する場合は near/far をユースケースに合わせて絞ること。\n */\nexport const CAMERA_FAR = 10000;\n\n// デフォルト値\nexport const DEFAULT_SCALE = 1;\nexport const DEFAULT_OFFSET = { x: 0, y: 0, z: 0 };\n\n// ライト設定\nexport const AMBIENT_LIGHT_COLOR = 0xffffff;\nexport const AMBIENT_LIGHT_INTENSITY = 0.5;\nexport const DIRECTIONAL_LIGHT_COLOR = 0xffffff;\nexport const DIRECTIONAL_LIGHT_INTENSITY = 1.0;\nexport const DIRECTIONAL_LIGHT_POSITION = { x: 1, y: 1, z: 1 };\n","import * as THREE from \"three\";\nimport { CAMERA_FOV, CAMERA_NEAR, CAMERA_FAR } from \"./constants\";\n\nexport class Camera {\n rect: DOMRect;\n /**\n * 内部 THREE.PerspectiveCamera。外部から `lookAt` 等の操作は可能。\n *\n * **注意**: `position.z` は「DOM 1px = WebGL 1unit」を成立させるための値で、\n * `resize()` のたびに `rect.height / 2 / tan(fov/2)` で**上書きされる**。\n * 外から position.z を書き換えても resize で消えるので、ズームや視点調整を\n * 永続化したい場合は別途 update ループで再設定するか、座標系が変わってもよい\n * 設計にすること。\n */\n instance: THREE.PerspectiveCamera;\n\n constructor(rect: DOMRect) {\n this.rect = rect;\n const fovRad = (CAMERA_FOV / 2) * (Math.PI / 180);\n // container が display:none / レイアウト未確定状態で渡されると 0 になる。\n // height=0 / width=0 で `tan` の除算が無限大、aspect が NaN になり\n // projectionMatrix が壊れて以後復旧不能になるので、最低 1px にクランプ。\n const safeWidth = Math.max(1, rect.width);\n const safeHeight = Math.max(1, rect.height);\n const distance = safeHeight / 2 / Math.tan(fovRad);\n this.instance = new THREE.PerspectiveCamera(\n CAMERA_FOV,\n safeWidth / safeHeight,\n CAMERA_NEAR,\n CAMERA_FAR,\n );\n this.instance.position.z = distance;\n }\n\n resize(rect: DOMRect) {\n // container が display:none やレイアウト未確定のときに 0 が来ると aspect が NaN になり\n // projectionMatrix が壊れるので早期 return。\n if (rect.width <= 0 || rect.height <= 0) return;\n\n this.rect = rect;\n\n // constructor と同じく安全側にクランプ(0 が来ない前提だが計算順による微小値で\n // tan の除算が暴れるケースに備える)。\n const safeWidth = Math.max(1, rect.width);\n const safeHeight = Math.max(1, rect.height);\n\n const fovRad = (CAMERA_FOV / 2) * (Math.PI / 180);\n const distance = safeHeight / 2 / Math.tan(fovRad);\n\n this.instance.aspect = safeWidth / safeHeight;\n this.instance.position.z = distance;\n this.instance.updateProjectionMatrix();\n }\n}\n","import * as THREE from \"three\";\nimport {\n AMBIENT_LIGHT_COLOR,\n AMBIENT_LIGHT_INTENSITY,\n DIRECTIONAL_LIGHT_COLOR,\n DIRECTIONAL_LIGHT_INTENSITY,\n DIRECTIONAL_LIGHT_POSITION,\n} from \"./constants\";\n\n/**\n * シンプルな環境光 + 平行光の組み合わせを scene に追加するクラス。\n *\n * **制約**:\n * - shadow(影)は無効。`castShadow` / `receiveShadow` を有効化したい場合は\n * `directionalLight.castShadow = true` を外から設定し、別途 shadow map 設定が必要。\n *\n * 簡易ライティングで十分なケースを想定。複雑なライト設計が必要なら、\n * `WebGLApp.getLight()` から内部 light を取り出して直接操作するか、\n * `WebGLApp.getScene()` に独自ライトを追加する。\n */\nexport class Light {\n readonly ambientLight: THREE.AmbientLight;\n readonly directionalLight: THREE.DirectionalLight;\n private readonly scene: THREE.Scene;\n\n constructor(scene: THREE.Scene) {\n this.scene = scene;\n\n // 環境光\n this.ambientLight = new THREE.AmbientLight(\n AMBIENT_LIGHT_COLOR,\n AMBIENT_LIGHT_INTENSITY,\n );\n this.scene.add(this.ambientLight);\n\n // 平行光源\n this.directionalLight = new THREE.DirectionalLight(\n DIRECTIONAL_LIGHT_COLOR,\n DIRECTIONAL_LIGHT_INTENSITY,\n );\n this.directionalLight.position.set(\n DIRECTIONAL_LIGHT_POSITION.x,\n DIRECTIONAL_LIGHT_POSITION.y,\n DIRECTIONAL_LIGHT_POSITION.z,\n );\n this.scene.add(this.directionalLight);\n // `directionalLight.target` を scene に add しておかないと、外部から\n // `target.position` を動かしても更新が反映されない (three.js の仕様)。\n // 既定は scene 原点を向くが、利用側で動的に向きを変えられるよう add しておく。\n this.scene.add(this.directionalLight.target);\n }\n}\n","import type { DOMPositionInfo } from \"./types\";\n\n/**\n * DOM要素の位置計算を担当するユーティリティクラス\n */\nexport class DomPositionCalculator {\n private element: HTMLElement;\n private canvasRect: DOMRect;\n private positionInfo: DOMPositionInfo;\n rect: DOMRect;\n // calculateWebGLPosition の戻り値を使い回すバッファ(GC削減)\n private readonly _outPosition = { x: 0, y: 0 };\n\n constructor(element: HTMLElement, canvasRect: DOMRect) {\n this.element = element;\n this.canvasRect = canvasRect;\n this.rect = new DOMRect();\n this.positionInfo = {\n pageTop: 0,\n pageLeft: 0,\n isFixed: false,\n };\n // constructor では `getBoundingClientRect` 1 回だけに留め、`getComputedStyle`\n // (refreshPositionType) は呼ばない。両方呼ぶと layout 強制が 2 回走り、\n // 多数 plane / object 構築時の reflow 累積コストが大きくなる。\n // 位置タイプ判定は利用側 (DomPlane の init→resize / Dom3DObject の setupModel)\n // で初期化される。\n this.updatePositionInfo();\n }\n\n /**\n * DOM要素の位置情報を更新(毎フレーム呼ばれる)\n */\n updatePositionInfo(): void {\n this.rect = this.element.getBoundingClientRect();\n\n const scrollY = window.scrollY;\n const scrollX = window.scrollX;\n\n if (this.positionInfo.isFixed) {\n this.positionInfo.pageTop = this.rect.top;\n this.positionInfo.pageLeft = this.rect.left;\n } else {\n this.positionInfo.pageTop = this.rect.top + scrollY;\n this.positionInfo.pageLeft = this.rect.left + scrollX;\n }\n }\n\n /**\n * position: fixed または sticky かどうかを再チェック(初期化・リサイズ時のみ呼ぶ)。\n * fixed / sticky 要素は document 上で位置が固定されないため、毎フレーム rect.top を\n * viewport 座標として扱う isFixed branch を使う。\n */\n refreshPositionType(): void {\n const position = window.getComputedStyle(this.element).position;\n this.positionInfo.isFixed = position === \"fixed\" || position === \"sticky\";\n }\n\n /**\n * WebGL座標系での位置を計算。\n * 戻り値は内部バッファを使い回すため、保持する場合は呼び出し側でコピーすること。\n */\n calculateWebGLPosition(\n scrollX: number = 0,\n scrollY: number = 0,\n ): { x: number; y: number } {\n let canvasCenterX: number;\n let canvasCenterY: number;\n\n if (this.positionInfo.isFixed) {\n // position: fixed の場合、スクロール位置を考慮しない\n canvasCenterX = this.canvasRect.left + this.canvasRect.width / 2;\n canvasCenterY = this.canvasRect.top + this.canvasRect.height / 2;\n } else {\n // 通常の場合、スクロール位置を考慮する\n canvasCenterX =\n this.canvasRect.left + scrollX + this.canvasRect.width / 2;\n canvasCenterY =\n this.canvasRect.top + scrollY + this.canvasRect.height / 2;\n }\n\n this._outPosition.x =\n this.positionInfo.pageLeft + this.rect.width / 2 - canvasCenterX;\n this._outPosition.y = -(\n this.positionInfo.pageTop +\n this.rect.height / 2 -\n canvasCenterY\n );\n\n return this._outPosition;\n }\n\n /**\n * Canvas矩形を更新\n */\n setCanvasRect(canvasRect: DOMRect): void {\n this.canvasRect = canvasRect;\n }\n\n /**\n * position: fixedかどうか\n */\n get isFixed(): boolean {\n return this.positionInfo.isFixed;\n }\n\n /**\n * ページトップ位置\n */\n get pageTop(): number {\n return this.positionInfo.pageTop;\n }\n\n /**\n * ページレフト位置\n */\n get pageLeft(): number {\n return this.positionInfo.pageLeft;\n }\n}\n","import * as THREE from 'three';\n\n/**\n * `WebGLApp.setPostEffect()` で差し込めるポストエフェクトの最小契約。\n * `EffectComposer` は実装している。自前で差し込みたい場合は構造的にこれを満たせばよい。\n */\nexport interface EffectLike {\n render(scene: THREE.Scene, camera: THREE.Camera): void;\n resize(width: number, height: number): void;\n dispose(): void;\n}\n\nexport interface EffectOptions {\n fragmentShader: string;\n uniforms?: { [key: string]: THREE.IUniform };\n}\n\nexport interface EffectTarget {\n addEffect(options: EffectOptions): EffectPass;\n}\n\nconst defaultVertexShader = `\n varying vec2 vUv;\n void main() {\n vUv = uv;\n gl_Position = vec4(position, 1.0);\n }\n`;\n\n/**\n * addEffect() で返されるハンドル。uniform の更新に使う。\n */\nexport class EffectPass {\n readonly material: THREE.ShaderMaterial;\n /** false の時、EffectComposer はこのパスをスキップする(パススルー扱い)。 */\n enabled = true;\n\n constructor(material: THREE.ShaderMaterial) {\n this.material = material;\n }\n\n setUniform(key: string, value: unknown): void {\n if (this.material.uniforms[key] === undefined) {\n // dev 環境のみ警告。本番(minify 済み)では import.meta.env.DEV が false でスキップされる。\n if (import.meta.env?.DEV) {\n console.warn(\n `[EffectPass] uniform \"${key}\" は定義されていません。タイポか、` +\n `BaseEffect.getConfig() の uniforms に追加し忘れている可能性があります。`\n );\n }\n return;\n }\n this.material.uniforms[key].value = value;\n }\n\n getUniform(key: string): THREE.IUniform | undefined {\n return this.material.uniforms[key];\n }\n}\n\n/**\n * 複数のポストエフェクトをチェーンで適用するクラス。\n *\n * RenderTarget はピンポン方式で2つのみ使用。\n * エフェクト数に関わらずメモリ使用量は一定。\n *\n * render flow (エフェクト3つの場合):\n * scene → TargetA\n * pass1: A → B\n * pass2: B → A\n * pass3: A → canvas (null)\n */\nexport class EffectComposer implements EffectTarget, EffectLike {\n private renderer: THREE.WebGLRenderer;\n private passes: Array<EffectPass> = [];\n\n private targetA: THREE.WebGLRenderTarget;\n private targetB: THREE.WebGLRenderTarget;\n\n private postScene: THREE.Scene;\n private postCamera: THREE.OrthographicCamera;\n private postMesh: THREE.Mesh;\n private geometry: THREE.PlaneGeometry;\n\n /**\n * dispose 後の API 呼び出しを no-op にするフラグ。\n * dispose 済みの RT に render すると INVALID_OPERATION になるため。\n */\n private _disposed: boolean = false;\n\n constructor(renderer: THREE.WebGLRenderer, width: number, height: number) {\n this.renderer = renderer;\n\n const dpr = renderer.getPixelRatio();\n // width/height が 0 を取ると WebGL が INVALID_VALUE を出す実装があるので\n // 1 px 未満にならないようにガードする。\n const w = Math.max(1, Math.floor(width * dpr));\n const h = Math.max(1, Math.floor(height * dpr));\n\n const rtOptions: THREE.RenderTargetOptions = {\n minFilter: THREE.LinearFilter,\n magFilter: THREE.LinearFilter,\n format: THREE.RGBAFormat,\n stencilBuffer: false,\n };\n\n this.targetA = new THREE.WebGLRenderTarget(w, h, rtOptions);\n this.targetB = new THREE.WebGLRenderTarget(w, h, rtOptions);\n\n this.postScene = new THREE.Scene();\n this.postCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);\n this.geometry = new THREE.PlaneGeometry(2, 2);\n this.postMesh = new THREE.Mesh(this.geometry);\n this.postScene.add(this.postMesh);\n }\n\n addEffect(options: EffectOptions): EffectPass {\n if (this._disposed) {\n throw new Error('[EffectComposer] dispose 済みのインスタンスでは addEffect() できません。');\n }\n const material = new THREE.ShaderMaterial({\n uniforms: {\n tDiffuse: { value: null },\n ...options.uniforms,\n },\n vertexShader: defaultVertexShader,\n fragmentShader: options.fragmentShader,\n transparent: true,\n });\n\n const pass = new EffectPass(material);\n this.passes.push(pass);\n return pass;\n }\n\n /**\n * 登録済みパスを 1 つ取り除いて material を dispose する。\n * `addEffect()` の戻り値か、`BaseEffect.getPass()` の値を渡す。\n * 存在しない pass を渡した時は何もしない。\n */\n removeEffect(pass: EffectPass): boolean {\n if (this._disposed) return false;\n const idx = this.passes.indexOf(pass);\n if (idx < 0) return false;\n this.passes.splice(idx, 1);\n pass.material.dispose();\n return true;\n }\n\n /**\n * シーンをレンダリングし、登録されたエフェクトを順に適用する。\n * エフェクトが0個の場合は通常レンダリング。\n */\n render(scene: THREE.Scene, camera: THREE.Camera): void {\n if (this._disposed) return;\n // 無効化されたパスは丸ごとスキップ。チェーン途中で off にしても残りが正しく繋がる\n // よう、active なものだけ走らせる。filter() で配列を毎フレ alloc すると GC 圧に\n // なるので、active 数のカウントと最後の active index だけ取り出して in-place で回す。\n let activeCount = 0;\n let lastActiveIndex = -1;\n const passes = this.passes;\n for (let i = 0, n = passes.length; i < n; i++) {\n if (passes[i].enabled) {\n activeCount++;\n lastActiveIndex = i;\n }\n }\n\n if (activeCount === 0) {\n this.renderer.setRenderTarget(null);\n this.renderer.render(scene, camera);\n return;\n }\n\n // シーンを TargetA に描画\n this.renderer.setRenderTarget(this.targetA);\n this.renderer.render(scene, camera);\n\n let readTarget = this.targetA;\n let writeTarget = this.targetB;\n\n for (let i = 0; i < passes.length; i++) {\n const pass = passes[i];\n if (!pass.enabled) continue;\n const isLast = i === lastActiveIndex;\n\n pass.material.uniforms['tDiffuse'].value = readTarget.texture;\n this.postMesh.material = pass.material;\n\n this.renderer.setRenderTarget(isLast ? null : writeTarget);\n this.renderer.render(this.postScene, this.postCamera);\n\n if (!isLast) {\n const tmp = readTarget;\n readTarget = writeTarget;\n writeTarget = tmp;\n }\n }\n }\n\n resize(width: number, height: number): void {\n if (this._disposed) return;\n const dpr = this.renderer.getPixelRatio();\n const w = Math.max(1, Math.floor(width * dpr));\n const h = Math.max(1, Math.floor(height * dpr));\n this.targetA.setSize(w, h);\n this.targetB.setSize(w, h);\n }\n\n dispose(): void {\n if (this._disposed) return;\n this._disposed = true;\n this.targetA.dispose();\n this.targetB.dispose();\n this.geometry.dispose();\n for (const pass of this.passes) {\n pass.material.dispose();\n }\n this.passes = [];\n }\n}\n","import * as THREE from 'three';\nimport type { EffectOptions, EffectTarget } from './EffectComposer';\nimport { EffectPass } from './EffectComposer';\n\nconst defaultVertexShader = `\n varying vec2 vUv;\n void main() {\n vUv = uv;\n gl_Position = vec4(position, 1.0);\n }\n`;\n\n/**\n * DomPlane 単体にエフェクトチェーンを適用するクラス。\n *\n * render flow:\n * plane.mesh (local scene) → targetA\n * pass1: A → B\n * pass2: B → A\n * ...\n * final target → proxyMesh.material.map (main scene に表示)\n *\n * plane.mesh はコンストラクト時に main scene から取り出し local scene へ移動。\n * proxyMesh がその位置に差し替わり、エフェクト適用後のテクスチャを表示する。\n *\n * **制約・落とし穴**:\n * - main scene に置く `proxyMesh` は固定で `MeshBasicMaterial({ transparent: true })`。\n * `DomPlane` 本体の `ShaderMaterial` の depthWrite / depthTest / blending 設定は反映されない。\n * 不透明背景のシーンに混ぜると z 順がおかしく見えることがある(透過レイヤ前提のデザイン推奨)。\n * - `localMesh` は `sourceMesh.geometry` / `material` を**共有**して描画する。\n * `DomPlane.setHoverInfo()` 等で uniform を更新すれば自動でこちらにも反映される。\n * ただし raycast の hit ターゲットは main scene にある `proxyMesh`(material が違う)に\n * なるため、`uv` 結果は plane の物理位置に対して正しく出る。\n * - `addEffect` を 0 回しか呼ばないケースでも `render()` は targetA 経由でプロキシに焼く。\n * この場合 proxy はエフェクトなしの sourceMesh の見た目をそのまま表示する。\n */\nexport class PlaneComposer implements EffectTarget {\n private renderer: THREE.WebGLRenderer;\n private sourceMesh: THREE.Mesh;\n private passes: EffectPass[] = [];\n\n private targetA: THREE.WebGLRenderTarget;\n private targetB: THREE.WebGLRenderTarget;\n\n // plane の内容を FBO に焼くための専用シーン\n private localScene: THREE.Scene;\n private localCamera: THREE.OrthographicCamera;\n private localMesh: THREE.Mesh;\n\n // エフェクトパス適用用シーン\n private postScene: THREE.Scene;\n private postCamera: THREE.OrthographicCamera;\n private postMesh: THREE.Mesh;\n private postGeo: THREE.PlaneGeometry;\n\n // main scene に表示するプロキシ\n private proxyMesh: THREE.Mesh;\n private proxyMaterial: THREE.MeshBasicMaterial;\n private proxyGeo: THREE.PlaneGeometry;\n private mainScene: THREE.Scene;\n\n /**\n * 全 effect が enabled=false の時、PlaneComposer をスキップして sourceMesh を\n * 直接 main scene で描画する状態(bypass)。 fullscreen pass 1 回ぶんを丸ごと\n * 削減できる。effect が再度 enable された時は通常レンダリングに復帰する。\n */\n private _bypassed: boolean = false;\n /** dispose 済みフラグ。dispose 後の render / addEffect を no-op にする。 */\n private _disposed: boolean = false;\n\n constructor(\n renderer: THREE.WebGLRenderer,\n sourceMesh: THREE.Mesh,\n mainScene: THREE.Scene,\n width: number,\n height: number,\n ) {\n this.renderer = renderer;\n this.sourceMesh = sourceMesh;\n this.mainScene = mainScene;\n\n const dpr = renderer.getPixelRatio();\n const w = Math.max(1, Math.floor(width * dpr));\n const h = Math.max(1, Math.floor(height * dpr));\n\n const rtOptions: THREE.RenderTargetOptions = {\n minFilter: THREE.LinearFilter,\n magFilter: THREE.LinearFilter,\n format: THREE.RGBAFormat,\n stencilBuffer: false,\n };\n this.targetA = new THREE.WebGLRenderTarget(w, h, rtOptions);\n this.targetB = new THREE.WebGLRenderTarget(w, h, rtOptions);\n\n // local scene: plane の mesh を正面から orthographic で撮る\n this.localScene = new THREE.Scene();\n this.localCamera = new THREE.OrthographicCamera(\n -width / 2, width / 2, height / 2, -height / 2, 0.1, 10,\n );\n this.localCamera.position.set(0, 0, 1);\n\n // sourceMesh と同じ geometry/material を共有するローカルレンダリング用メッシュ\n this.localMesh = new THREE.Mesh(sourceMesh.geometry, sourceMesh.material);\n this.localMesh.position.set(0, 0, 0);\n this.localMesh.scale.copy(sourceMesh.scale);\n this.localScene.add(this.localMesh);\n\n // post-process シーン(フルスクリーンクワッド)\n this.postScene = new THREE.Scene();\n this.postCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);\n this.postGeo = new THREE.PlaneGeometry(2, 2);\n this.postMesh = new THREE.Mesh(this.postGeo);\n this.postScene.add(this.postMesh);\n\n // main scene から sourceMesh を外してプロキシに差し替え\n mainScene.remove(sourceMesh);\n\n this.proxyMaterial = new THREE.MeshBasicMaterial({\n map: this.targetA.texture,\n transparent: true,\n });\n this.proxyGeo = new THREE.PlaneGeometry(1, 1);\n this.proxyMesh = new THREE.Mesh(this.proxyGeo, this.proxyMaterial);\n this.proxyMesh.position.copy(sourceMesh.position);\n this.proxyMesh.scale.copy(sourceMesh.scale);\n this.proxyMesh.quaternion.copy(sourceMesh.quaternion);\n mainScene.add(this.proxyMesh);\n }\n\n addEffect(options: EffectOptions): EffectPass {\n if (this._disposed) {\n throw new Error('[PlaneComposer] dispose 済みのインスタンスでは addEffect() できません。');\n }\n const material = new THREE.ShaderMaterial({\n uniforms: {\n tDiffuse: { value: null },\n ...options.uniforms,\n },\n vertexShader: defaultVertexShader,\n fragmentShader: options.fragmentShader,\n transparent: true,\n });\n const pass = new EffectPass(material);\n this.passes.push(pass);\n return pass;\n }\n\n /**\n * 登録済みパスを 1 つ取り除いて material を dispose する。\n * 存在しない pass を渡した時は何もしない。\n */\n removeEffect(pass: EffectPass): boolean {\n if (this._disposed) return false;\n const idx = this.passes.indexOf(pass);\n if (idx < 0) return false;\n this.passes.splice(idx, 1);\n pass.material.dispose();\n return true;\n }\n\n /**\n * bypass 状態に切り替える: sourceMesh を main scene に戻し proxyMesh を隠す。\n * 全 effect が disabled の時に呼ばれ、PlaneComposer.render の fullscreen pass を\n * まるごと省く (= 1 pass / 6.35M フラグメント分の節約)。\n */\n private enterBypass(): void {\n if (this._bypassed) return;\n this.mainScene.add(this.sourceMesh);\n this.proxyMesh.visible = false;\n this._bypassed = true;\n }\n\n /** bypass 状態から通常レンダリングへ復帰する。 */\n private exitBypass(): void {\n if (!this._bypassed) return;\n this.mainScene.remove(this.sourceMesh);\n this.proxyMesh.visible = true;\n this._bypassed = false;\n }\n\n render(): void {\n if (this._disposed) return;\n if (!this.sourceMesh.visible) {\n this.proxyMesh.visible = false;\n return;\n }\n\n // 全 effect disabled なら main scene 直描画にバイパス\n // (sourceMesh が main scene にいる状態を維持し、PlaneComposer は no-op)\n let activeCount = 0;\n for (const p of this.passes) if (p.enabled) activeCount++;\n if (activeCount === 0) {\n this.enterBypass();\n return;\n }\n\n // active effect あり → bypass から復帰\n this.exitBypass();\n\n // sourceMesh の visibility/transform をプロキシに同期\n this.proxyMesh.visible = true;\n this.proxyMesh.position.copy(this.sourceMesh.position);\n this.proxyMesh.scale.copy(this.sourceMesh.scale);\n this.proxyMesh.quaternion.copy(this.sourceMesh.quaternion);\n this.localMesh.scale.copy(this.sourceMesh.scale);\n\n // 1. plane の内容を targetA に描画\n this.renderer.setRenderTarget(this.targetA);\n this.renderer.render(this.localScene, this.localCamera);\n\n // 2. エフェクトパスを pingpong で適用 (active なものだけ)\n let read = this.targetA;\n let write = this.targetB;\n\n for (const pass of this.passes) {\n if (!pass.enabled) continue;\n pass.material.uniforms['tDiffuse'].value = read.texture;\n this.postMesh.material = pass.material;\n this.renderer.setRenderTarget(write);\n this.renderer.render(this.postScene, this.postCamera);\n\n const tmp = read;\n read = write;\n write = tmp;\n }\n\n // 最終結果をプロキシに反映。\n // `material.needsUpdate = true` を毎フレ呼ぶと three.js は version 衝突として\n // shader を recompile する。`map` の差し替えだけなら不要なので呼ばない。\n // (texture の中身の更新は Texture.needsUpdate のジョブで、ここでは別物。)\n this.proxyMaterial.map = read.texture;\n this.renderer.setRenderTarget(null);\n }\n\n resize(width: number, height: number): void {\n if (this._disposed) return;\n const dpr = this.renderer.getPixelRatio();\n const w = Math.max(1, Math.floor(width * dpr));\n const h = Math.max(1, Math.floor(height * dpr));\n\n this.targetA.setSize(w, h);\n this.targetB.setSize(w, h);\n\n this.localCamera.left = -width / 2;\n this.localCamera.right = width / 2;\n this.localCamera.top = height / 2;\n this.localCamera.bottom = -height / 2;\n this.localCamera.updateProjectionMatrix();\n }\n\n dispose(): void {\n if (this._disposed) return;\n this._disposed = true;\n // bypass 中なら sourceMesh は既に main scene にいる。THREE.Object3D.add は同一 parent でも\n // 内部で removeFromParent → push し直すため、children 配列の末尾に移動する\n // (= sortObjects=false 運用で描画順が変わる)。bypass 中はスキップする。\n this.mainScene.remove(this.proxyMesh);\n if (!this._bypassed) {\n this.mainScene.add(this.sourceMesh);\n }\n this._bypassed = false;\n\n this.targetA.dispose();\n this.targetB.dispose();\n this.postGeo.dispose();\n this.proxyGeo.dispose();\n this.proxyMaterial.dispose();\n for (const pass of this.passes) {\n pass.material.dispose();\n }\n this.passes = [];\n }\n}\n","import * as THREE from \"three\";\nimport type GUI from \"lil-gui\";\nimport type { CreatePlaneOptions } from \"./types\";\nimport { DomPositionCalculator } from \"./DomPositionCalculator\";\nimport { PlaneComposer } from \"./PlaneComposer\";\nimport type { BaseEffect } from \"./effects/BaseEffect\";\n\n// 全インスタンスで共有する TextureLoader(同一の CrossOrigin 設定)\nconst sharedTextureLoader = new THREE.TextureLoader();\nsharedTextureLoader.setCrossOrigin(\"anonymous\");\n\nconst defaultVertexShader = `\n varying vec2 vUv;\n\n void main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n`;\n\nconst defaultFragmentShader = `\n uniform sampler2D uTexture;\n uniform float uTime;\n uniform vec2 uResolution;\n varying vec2 vUv;\n\n void main() {\n vec4 texColor = texture2D(uTexture, vUv);\n gl_FragColor = texColor;\n }\n`;\n\nexport class DomPlane {\n element: HTMLElement | null;\n texture: THREE.Texture | null;\n mesh: THREE.Mesh;\n geometry: THREE.PlaneGeometry;\n material: THREE.ShaderMaterial;\n scene: THREE.Scene;\n clock: THREE.Clock;\n positionCalculator: DomPositionCalculator | null;\n canvasRect: DOMRect;\n isVisible: boolean;\n private updateRectEveryFrame: boolean;\n private observer: IntersectionObserver | null;\n private destroyed: boolean;\n private planeComposer: PlaneComposer | null = null;\n private renderer: THREE.WebGLRenderer;\n private effects: BaseEffect[] = [];\n /**\n * effect.update に渡す mouse UV のスクラッチ。\n * `material.uniforms.uMouseUV.value` を直接渡すと effect 側で `.set()` 等の破壊的操作で\n * plane の uniform が書き換わる事故が起きうるため、毎フレ copy したスクラッチを渡す。\n */\n private readonly _effectMouseUV: THREE.Vector2 = new THREE.Vector2();\n /**\n * effect の `setupGUI` 呼び出し時に root GUI を渡すための provider。\n * WebGLApp.createPlane() でセットされる。null の時は GUI を作らない(showGUI: false 等)。\n *\n * lil-gui は optional peer の dynamic import なので Promise を返す。\n * @internal\n */\n private guiProvider: (() => Promise<GUI>) | null = null;\n /** 自前で load した texture かどうか。destroy() で dispose してよいか判定する。 */\n private ownsTexture: boolean = false;\n /**\n * options.crossOrigin を指定された場合のみ生成する per-instance loader。\n * 指定なしの場合は sharedTextureLoader をそのまま使う。\n */\n private crossOrigin: string | undefined;\n\n constructor(\n el: HTMLElement | null,\n scene: THREE.Scene,\n canvasRect: DOMRect,\n renderer: THREE.WebGLRenderer,\n options: CreatePlaneOptions = {},\n sharedClock?: THREE.Clock,\n ) {\n this.element = el;\n this.scene = scene;\n this.renderer = renderer;\n this.texture = null;\n this.destroyed = false;\n this.updateRectEveryFrame = options.updateRectEveryFrame || false;\n this.crossOrigin = options.crossOrigin;\n this.clock = sharedClock ?? new THREE.Clock();\n this.canvasRect = canvasRect;\n this.positionCalculator = el\n ? new DomPositionCalculator(el, canvasRect)\n : null;\n\n // フルスクリーンは常に表示、DOM要素は IntersectionObserver で監視\n this.isVisible = !el;\n this.observer = null;\n if (el) {\n let hasEnteredView = false;\n const repeat = options.inViewRepeat ?? false;\n\n this.observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[0];\n this.isVisible = entry.isIntersecting;\n this.mesh.visible = this.isVisible;\n\n if (entry.isIntersecting) {\n if (options.onInView && (!hasEnteredView || repeat)) {\n hasEnteredView = true;\n options.onInView(this);\n }\n } else {\n if (repeat) {\n hasEnteredView = false;\n options.onOutView?.(this);\n }\n }\n },\n { rootMargin: options.inViewRootMargin ?? \"100%\" },\n );\n this.observer.observe(el);\n }\n\n const segments = options.segments ?? 1;\n this.geometry = new THREE.PlaneGeometry(1, 1, segments, segments);\n\n // デフォルトのuniformsとカスタムuniformsをマージ\n const uniforms = {\n uTexture: { value: null },\n uAlpha: { value: 1.0 },\n uResolution: { value: new THREE.Vector2() },\n uTime: { value: 0 },\n uIsHovered: { value: false },\n uMouseUV: { value: new THREE.Vector2(0, 0) },\n ...options.uniforms,\n };\n\n this.material = new THREE.ShaderMaterial({\n transparent: true,\n uniforms: uniforms,\n vertexShader: options.vertexShader || defaultVertexShader,\n fragmentShader: options.fragmentShader || defaultFragmentShader,\n });\n this.mesh = new THREE.Mesh(this.geometry, this.material);\n // IntersectionObserver の初回 callback が来るまで(最低 1 frame)に\n // mesh が一瞬表示される問題を避けるため、初期 visibility を反映しておく。\n this.mesh.visible = this.isVisible;\n\n // シーンに自動追加\n this.scene.add(this.mesh);\n\n this.init();\n // 毎フレームの uTime / 位置追従 / planeComposer.render は Core.animate から\n // _tickRead → _tickApply → _tickRenderComposer の順に直接呼ばれる。\n // これによりレイアウト read(getBoundingClientRect)と write(mesh.position 等)が\n // フェーズ分離され、複数 plane 間での強制リフローを避けられる。\n }\n\n /**\n * Phase B-read (DOM read): getBoundingClientRect 等のレイアウト読み取りのみを行う。\n * Core.animate では paint 直前にまとめて呼ばれ、その直後に _tickApply が走る。\n *\n * **更新条件**: `updateRectEveryFrame: true` のときのみ毎フレ更新する。\n * static 要素は document 座標で固定 / fixed 要素は viewport 座標で固定なので、\n * 自身の CSS animation 等で動的に位置が変わるケースのみフラグを true に。\n * (旧仕様では isFixed のとき自動的に毎フレ更新していたが、多くの場合不要な\n * layout 強制になっていたため明示フラグ駆動に変更)\n * @internal Core.animate から呼ばれる。\n */\n public _tickRead(): void {\n if (!this.isVisible || !this.positionCalculator) return;\n if (this.updateRectEveryFrame) {\n this.positionCalculator.updatePositionInfo();\n }\n }\n\n /**\n * Phase B-apply: mesh.position と uniforms の書き込み。DOM には触れない。\n * @internal Core.animate から呼ばれる。\n */\n public _tickApply(elapsedTime: number, scrollX: number, scrollY: number): void {\n if (!this.isVisible) return;\n this.material.uniforms.uTime.value = elapsedTime;\n if (this.positionCalculator) {\n this.setPosition(scrollX, scrollY);\n }\n }\n\n /**\n * Phase C (PlaneComposer render): main scene レンダリング前に\n * 自前の plane をローカル FBO に焼いてエフェクトを通す。\n * @internal Core.animate から呼ばれる。\n */\n public _tickRenderComposer(): void {\n if (!this.isVisible) return;\n this.planeComposer?.render();\n }\n\n private init() {\n this.loadTexture();\n this.resize();\n }\n\n private loadTexture() {\n const texturePath = this.element?.getAttribute(\"data-texture\");\n\n if (texturePath) {\n // options.crossOrigin が指定されていれば per-instance loader を作る。\n // shared loader を毎回書き換えると並行 load の他 plane に副作用が出るため。\n let loader: THREE.TextureLoader;\n if (this.crossOrigin !== undefined) {\n loader = new THREE.TextureLoader();\n loader.setCrossOrigin(this.crossOrigin);\n } else {\n loader = sharedTextureLoader;\n }\n loader.load(\n texturePath,\n (texture: THREE.Texture) => {\n if (this.destroyed) {\n // 既に破棄済みなら自前で dispose して GPU リソースを返す\n texture.dispose();\n return;\n }\n this.texture = texture;\n this.ownsTexture = true; // 自前 load なので所有\n this.material.uniforms.uTexture.value = texture;\n },\n undefined,\n (error: unknown) => {\n console.error(`Failed to load texture: ${texturePath}`, error);\n },\n );\n } else if (this.material.uniforms.uTexture.value) {\n // 外部由来(例:Dom3DObject や uniforms 渡し)。所有しない。\n this.texture = this.material.uniforms.uTexture.value;\n this.ownsTexture = false;\n }\n }\n\n private updateSize() {\n if (this.positionCalculator) {\n const rect = this.positionCalculator.rect;\n this.mesh.scale.set(rect.width, rect.height, 1);\n this.material.uniforms.uResolution.value.set(rect.width, rect.height);\n } else {\n // フルスクリーン: canvasサイズに合わせる\n this.mesh.scale.set(this.canvasRect.width, this.canvasRect.height, 1);\n this.material.uniforms.uResolution.value.set(\n this.canvasRect.width,\n this.canvasRect.height,\n );\n }\n }\n\n private setPosition(scrollX: number, scrollY: number) {\n if (!this.positionCalculator) return;\n const { x, y } = this.positionCalculator.calculateWebGLPosition(\n scrollX,\n scrollY,\n );\n this.mesh.position.set(x, y, 0);\n }\n\n public setCanvasRect(canvasRect: DOMRect) {\n this.canvasRect = canvasRect;\n if (this.positionCalculator) {\n this.positionCalculator.setCanvasRect(canvasRect);\n }\n }\n\n public resize() {\n if (this.positionCalculator) {\n this.positionCalculator.refreshPositionType();\n this.positionCalculator.updatePositionInfo();\n\n const scrollY = window.scrollY;\n const scrollX = window.scrollX;\n\n this.updateSize();\n this.setPosition(scrollX, scrollY);\n } else {\n this.updateSize();\n this.mesh.position.set(0, 0, 0);\n }\n\n if (this.planeComposer) {\n const rect = this.positionCalculator?.rect ?? this.canvasRect;\n this.planeComposer.resize(rect.width, rect.height);\n for (const effect of this.effects) {\n effect.resize?.(rect.width, rect.height);\n }\n }\n }\n\n public getMesh() {\n return this.mesh;\n }\n\n /**\n * テクスチャを差し替える。\n * 直接 `material.uniforms.uTexture.value = tex` する代わりに使うと、\n * 旧テクスチャの所有権(自前 load かどうか)を考慮して安全に dispose してくれる。\n *\n * @param texture 新しい texture\n * @param takeOwnership true なら destroy() 時にこの texture も dispose する。\n * 他で使い回す texture を渡すときは false。\n */\n /**\n * 現在の `data-texture` 属性を再読込してマテリアルに反映する。\n * SPA で plane を貼ったまま画像 URL だけ差し替えたいケース用。\n * 旧 texture が自前 load の場合は dispose する。属性が無ければ何もしない。\n */\n public reloadTexture(): void {\n // 既存自前 texture を dispose してから loadTexture を呼ぶ。\n if (this.texture && this.ownsTexture) {\n this.texture.dispose();\n this.texture = null;\n this.material.uniforms.uTexture.value = null;\n this.ownsTexture = false;\n }\n this.loadTexture();\n }\n\n public setTexture(texture: THREE.Texture, takeOwnership: boolean = false): void {\n // 自前で持っていた旧 texture のみ dispose(外部由来は触らない)\n if (this.texture && this.ownsTexture && this.texture !== texture) {\n this.texture.dispose();\n }\n this.texture = texture;\n this.ownsTexture = takeOwnership;\n this.material.uniforms.uTexture.value = texture;\n }\n\n /**\n * 紐づけられたエフェクトに update を流す。Core.animate から呼ばれる。\n *\n * 引数 `globalMouse` は canvas 全体の UV(0〜1, Y-up)。これを **plane ローカルの UV** に変換して\n * effect.update に渡す。これで FluidEffect 等が plane の中の座標として mouse を扱える。\n *\n * - フルスクリーン plane(element 無し): そのまま globalMouse を渡す\n * - DOM 配置 plane: 自分の DOM rect と canvasRect から local UV を算出\n * - マウスが plane の外にいる時は **last value を据え置き**(uv 更新せず)。\n * これにより solver 側で delta が 0 になり force が立たない、自然な挙動になる。\n *\n * 注意: raycast には依存しない。PlaneComposer が sourceMesh を main scene から外すと\n * raycast が思った通り当たらないケースがあるため。\n *\n * scrollX/Y は Core.animate が 1 rAF tick 上で 1 回確定したスナップショット値を渡す。\n * ここで `window.scrollX/Y` を直接読むと\n *(同一 frame で全コンポーネントが同じ scroll 値を共有する)を壊すため必ず引数経由。\n * 後方互換のため省略可能だが、その場合は window から読む (= 古い挙動)。\n */\n public updateEffects(\n time: number,\n globalMouse?: THREE.Vector2,\n scrollX: number = window.scrollX,\n scrollY: number = window.scrollY,\n ) {\n if (this.effects.length === 0) return;\n\n if (globalMouse) {\n if (this.positionCalculator) {\n const pc = this.positionCalculator;\n const cr = this.canvasRect;\n const rectW = pc.rect.width;\n const rectH = pc.rect.height;\n if (cr.width > 0 && cr.height > 0 && rectW > 0 && rectH > 0) {\n // 旧実装は `positionCalculator.rect.left/top` (viewport 座標) を直接使って\n // いたが、scroll 中は static 要素の viewport top/left が動くため `_tickRead`\n // で毎フレ rect を更新する必要があった。今は `updateRectEveryFrame` フラグ\n // 駆動にしたので rect は scroll で古くなる。\n //\n // 代わりに `pageTop` / `pageLeft` (document 座標、scroll 不変) と\n // 引数の scrollX/Y (Core.animate の 1 frame snapshot) から viewport 上の\n // top/left を再構成する。`getBoundingClientRect` を呼ばないので layout 強制なし。\n // (isFixed 要素は pageTop が viewport 座標で固定なので scroll を引かない)\n const viewportLeft = pc.isFixed ? pc.pageLeft : pc.pageLeft - scrollX;\n const viewportTop = pc.isFixed ? pc.pageTop : pc.pageTop - scrollY;\n\n // plane の bbox を canvas UV 空間(Y-up)で求める\n const planeLeft = (viewportLeft - cr.left) / cr.width;\n const planeTop = (viewportTop - cr.top) / cr.height;\n const planeW = rectW / cr.width;\n const planeH = rectH / cr.height;\n const planeBottomYup = 1 - planeTop - planeH;\n const lx = (globalMouse.x - planeLeft) / planeW;\n const ly = (globalMouse.y - planeBottomYup) / planeH;\n\n // [0,1] の中にいる時だけ更新。外なら据え置き → delta=0 で force 立たず。\n if (lx >= 0 && lx <= 1 && ly >= 0 && ly <= 1) {\n this.material.uniforms.uMouseUV.value.set(lx, ly);\n }\n }\n } else {\n // フルスクリーン: local UV == global UV\n this.material.uniforms.uMouseUV.value.copy(globalMouse);\n }\n }\n\n // uniform の Vector2 をそのまま effect.update に渡すと、effect 側で破壊的操作されると\n // plane の uMouseUV が壊れる。スクラッチに copy して渡すことで isolation を確保する。\n const uniformUV = this.material.uniforms.uMouseUV.value as THREE.Vector2;\n this._effectMouseUV.copy(uniformUV);\n const effects = this.effects;\n for (let i = 0, n = effects.length; i < n; i++) {\n const effect = effects[i];\n if (!effect.enabled) continue;\n effect.update(time, this._effectMouseUV);\n }\n }\n\n /** 直近の plane ローカル UV を取得(updateEffects 内で算出された値、0〜1)。 */\n public getMouseUV(): THREE.Vector2 {\n return this.material.uniforms.uMouseUV.value as THREE.Vector2;\n }\n\n /** マウスが現在 plane の上に乗っているかどうか。 */\n public isHovered(): boolean {\n return this.material.uniforms.uIsHovered.value as boolean;\n }\n\n public setHoverInfo(isHovered: boolean, uv: THREE.Vector2 | null) {\n this.material.uniforms.uIsHovered.value = isHovered;\n if (uv) {\n this.material.uniforms.uMouseUV.value.copy(uv);\n }\n }\n\n private enableEffects(): PlaneComposer {\n if (this.planeComposer) return this.planeComposer;\n const rect = this.positionCalculator?.rect ?? this.canvasRect;\n this.planeComposer = new PlaneComposer(\n this.renderer,\n this.mesh,\n this.scene,\n rect.width,\n rect.height,\n );\n return this.planeComposer;\n }\n\n public addEffect<T extends BaseEffect>(effect: T): T {\n const composer = this.enableEffects();\n // renderer を要求するエフェクト(FluidEffect 等)に注入してから register する\n effect._setRenderer?.(this.renderer);\n effect._register(composer);\n const rect = this.positionCalculator?.rect ?? this.canvasRect;\n effect.resize?.(rect.width, rect.height);\n // setupGUI を実装している場合は自動で lil-gui パネルを生やす。\n // lil-gui は optional peer の dynamic import なので provider が Promise を返す。\n if (this.guiProvider && effect.setupGUI) {\n this.guiProvider()\n .then((gui) => {\n if (this.destroyed) return;\n effect.setupGUI!(gui);\n })\n .catch((err) => {\n console.warn(\n '[DomPlane] effect.setupGUI に渡す lil-gui の読み込みに失敗しました。' +\n 'npm install lil-gui してください。',\n err,\n );\n });\n }\n this.effects.push(effect);\n return effect;\n }\n\n /**\n * `addEffect()` で登録した effect を取り除き、pass material を dispose する。\n * 登録されていない effect を渡した時は何もしない(戻り値 false)。\n */\n public removeEffect(effect: BaseEffect): boolean {\n const idx = this.effects.indexOf(effect);\n if (idx < 0) return false;\n this.effects.splice(idx, 1);\n const pass = effect.getPass();\n if (pass && this.planeComposer) {\n this.planeComposer.removeEffect(pass);\n }\n effect.dispose?.();\n return true;\n }\n\n /**\n * WebGLApp.createPlane() から呼ばれる。GUI lazy 取得関数を渡す。\n * null を渡すと GUI 統合を無効化(showGUI: false 相当)。\n * @internal\n */\n public _setGuiProvider(provider: (() => Promise<GUI>) | null): void {\n this.guiProvider = provider;\n }\n\n public destroy() {\n if (this.destroyed) return;\n this.destroyed = true;\n this.observer?.disconnect();\n\n for (const effect of this.effects) {\n effect.dispose?.();\n }\n this.effects = [];\n\n if (this.planeComposer) {\n this.planeComposer.dispose(); // mesh を scene に戻してから\n this.planeComposer = null;\n }\n this.scene.remove(this.mesh);\n this.geometry.dispose();\n this.material.dispose();\n // 自前で load した texture のみ dispose。\n // 外部から渡された texture を dispose すると他の plane が壊れる。\n if (this.texture && this.ownsTexture) {\n this.texture.dispose();\n }\n this.texture = null;\n }\n}\n","import * as THREE from \"three\";\nimport { GLTFLoader } from \"three/examples/jsm/loaders/GLTFLoader.js\";\nimport type { GLTF } from \"three/examples/jsm/loaders/GLTFLoader.js\";\nimport { DomPositionCalculator } from \"./DomPositionCalculator\";\nimport type { Create3DObjectOptions } from \"./types\";\nimport { DEFAULT_SCALE, DEFAULT_OFFSET } from \"./constants\";\n\n/**\n * DOM 要素の位置・サイズに合わせて GLTF モデルを配置するクラス。\n *\n * **element の扱い**:\n * - `HTMLElement` 指定: DOM 要素位置に追従。bbox 正規化と DOM サイズに対する scale\n * フィットを行う。\n * - `null` 指定: scene 原点に固定(モデル本来のサイズ × userScale)。\n * ScrollSync 無効時はそのまま viewport 中心に固定される。\n * ScrollSync 有効時でも Lenis 等で JS rAF と paint の scrollY が同一に揃っていれば\n * container が viewport に厳密固定され、scene 原点固定 obj も viewport 上で\n * ズレなく見える。\n *\n * **エフェクトについて**: `DomPlane` と異なり `addEffect()` API は持たない。\n * 任意 3D モデル単体に局所ポストエフェクトを当てるのは Plane と違って\n * 1) 任意視点からの再投影が必要、2) 透視テクスチャ再貼り付けでクオリティが落ちる\n * といった問題があり、汎用化が難しいため意図的に提供していない。\n *\n * モデル全体に効くエフェクトを掛けたい場合は `WebGLApp.addEffect()` で\n * 画面全体のポストエフェクトとして適用する。\n * モデル個別にエフェクトが必要な場合は `getModel()` で `THREE.Group` を取り出し、\n * カスタムマテリアル/シェーダーで対応すること。\n *\n * **IntersectionObserver について**: `DomPlane` と違い `onInView` / `onOutView` /\n * `inViewRepeat` / `inViewRootMargin` は提供していない。Dom3DObject は GLTF を\n * scene に置く性質上「画面外で非表示にして無駄な描画を避ける」用途に絞られ、\n * 進行状況通知やリセット系のコールバックは Plane より使い所が薄いため。\n * 必要なら利用側で `getModel()` を取り出して自前で IntersectionObserver を組む。\n * element=null の場合は IntersectionObserver は作らない(常に表示)。\n */\nexport class Dom3DObject {\n element: HTMLElement | null;\n model: THREE.Group | null;\n loader: GLTFLoader;\n mainScene: THREE.Scene;\n canvasRect: DOMRect;\n options: Create3DObjectOptions;\n isVisible: boolean;\n private positionCalculator: DomPositionCalculator | null;\n private updateRectEveryFrame: boolean;\n private observer: IntersectionObserver | null;\n private destroyed: boolean;\n\n constructor(\n element: HTMLElement | null,\n mainScene: THREE.Scene,\n canvasRect: DOMRect,\n options: Create3DObjectOptions,\n ) {\n this.element = element;\n this.mainScene = mainScene;\n this.canvasRect = canvasRect;\n this.model = null;\n this.loader = new GLTFLoader();\n this.options = {\n scale: DEFAULT_SCALE,\n // DEFAULT_OFFSET をそのまま参照すると、複数 Dom3DObject インスタンスが\n // 同一オブジェクトを共有してしまう。誰かが `obj.options.offset.x = 5` で\n // 書き換えると DEFAULT_OFFSET 自身が変わり、以後の全インスタンスに伝播する。\n // インスタンスごとに copy して隔離する。\n offset: { ...DEFAULT_OFFSET },\n ...options,\n };\n this.updateRectEveryFrame = options.updateRectEveryFrame ?? false;\n this.positionCalculator = element\n ? new DomPositionCalculator(element, canvasRect)\n : null;\n // element=null は scene 原点固定で常に表示。HTMLElement の場合は Observer の\n // 初回 callback まで非表示にして「初フレ画面外で見える」を避ける。\n this.isVisible = !element;\n this.destroyed = false;\n this.observer = null;\n\n if (element) {\n this.observer = new IntersectionObserver(\n (entries) => {\n this.isVisible = entries[0].isIntersecting;\n if (this.model) this.model.visible = this.isVisible;\n },\n { rootMargin: '100%' },\n );\n this.observer.observe(element);\n }\n\n // 毎フレームの位置追従は Core.animate から _tickRead → _tickApply の順に直接呼ばれる。\n this.loadModel();\n }\n\n /**\n * Phase B-read (DOM read): getBoundingClientRect のみ。\n * Core.animate で paint 直前にまとめて呼ばれる。\n * element=null の場合は何もしない(scene 原点固定で DOM 追従不要)。\n *\n * **更新条件**: `updateRectEveryFrame: true` のときのみ毎フレ更新。\n * 自身の CSS animation で動的に位置が変わるケースのみ true に。\n * (旧仕様では isFixed で自動毎フレ更新していたが、多くの場合不要な layout 強制\n * になっていたため明示フラグ駆動に変更)\n * @internal Core.animate から呼ばれる。\n */\n public _tickRead(): void {\n if (!this.model || !this.isVisible || !this.positionCalculator) return;\n if (this.updateRectEveryFrame) {\n this.positionCalculator.updatePositionInfo();\n }\n }\n\n /**\n * Phase B-apply: model.position の書き込み。\n * DOM あり/なしどちらでも `setPosition` を呼ぶ。setPosition 内部で positionCalculator の\n * null を扱うため、DOM なしは offset のみ反映される(後から options.offset を書き換えても\n * 毎フレ反映される一貫性を担保)。\n * @internal Core.animate から呼ばれる。\n */\n public _tickApply(scrollX: number, scrollY: number): void {\n if (!this.model || !this.isVisible) return;\n this.setPosition(scrollX, scrollY);\n }\n\n private loadModel() {\n const modelPath = this.options.modelPath;\n\n if (!modelPath) {\n console.error(\"Dom3DObject: modelPath が指定されていません。\");\n return;\n }\n\n this.loader.load(\n modelPath,\n (gltf: GLTF) => {\n if (this.destroyed) return;\n this.model = gltf.scene;\n this.setupModel();\n },\n undefined,\n // GLTFLoader の onError は ProgressEvent / ErrorEvent / Error など複数型を渡してくる。\n // ErrorEvent 固定で受けると `.message` が undefined のケースで static 型上の嘘になる。\n (error: unknown) => {\n console.error(`Failed to load model: ${modelPath}`, error);\n },\n );\n }\n\n private setupModel(): void {\n if (!this.model) return;\n\n // DOMありの場合のみ bbox で正規化(DOMサイズに合わせるため)。\n // DOMなしはモデルのデフォルトサイズをそのまま使う(scene 原点固定)。\n if (this.positionCalculator) {\n // DomPositionCalculator constructor で遅延した位置タイプ判定 (getComputedStyle)\n // をここで実行 + rect 再取得。load 完了後の初期化なので layout 1 回。\n this.positionCalculator.refreshPositionType();\n this.positionCalculator.updatePositionInfo();\n\n const box = new THREE.Box3().setFromObject(this.model);\n const size = new THREE.Vector3();\n const center = new THREE.Vector3();\n box.getSize(size);\n box.getCenter(center);\n\n const maxSide = Math.max(size.x, size.y, size.z) || 1;\n const inner = this.model;\n inner.position.sub(center).multiplyScalar(1 / maxSide);\n inner.scale.multiplyScalar(1 / maxSide);\n\n const wrapper = new THREE.Group();\n wrapper.add(inner);\n this.model = wrapper;\n }\n\n this.model.visible = this.isVisible;\n this.mainScene.add(this.model);\n\n this.applyScale();\n this.setPosition(window.scrollX, window.scrollY);\n }\n\n private applyScale(): void {\n if (!this.model) return;\n const userScale = this.options.scale ?? DEFAULT_SCALE;\n\n if (this.positionCalculator) {\n // DOMあり: DOM要素サイズに合わせる(bbox正規化済み)\n const { width, height } = this.positionCalculator.rect;\n const fitMode = this.options.fitMode ?? \"maxSide\";\n const domScale =\n fitMode === \"contain\" ? Math.min(width, height) : Math.max(width, height);\n const finalScale = domScale * userScale;\n this.model.scale.set(finalScale, finalScale, finalScale);\n } else {\n // DOMなし: モデルのデフォルトサイズ + userScale のみ\n this.model.scale.set(userScale, userScale, userScale);\n }\n }\n\n private setPosition(scrollX: number, scrollY: number): void {\n if (!this.model) return;\n\n // constructor で `offset: { ...DEFAULT_OFFSET }` を必ず展開しているので\n // 常に non-null。`??` のフォールバックは不要。\n const offset = this.options.offset!;\n let x = offset.x;\n let y = offset.y;\n const z = offset.z;\n\n if (this.positionCalculator) {\n const pos = this.positionCalculator.calculateWebGLPosition(scrollX, scrollY);\n x += pos.x;\n y += pos.y;\n }\n\n this.model.position.set(x, y, z);\n }\n\n public setCanvasRect(canvasRect: DOMRect) {\n this.canvasRect = canvasRect;\n this.positionCalculator?.setCanvasRect(canvasRect);\n }\n\n public resize() {\n if (!this.positionCalculator) {\n // DOMなしの場合は scale を userScale だけで再適用 (DOM サイズ依存なし)\n this.applyScale();\n return;\n }\n this.positionCalculator.refreshPositionType();\n this.positionCalculator.updatePositionInfo();\n this.applyScale();\n this.setPosition(window.scrollX, window.scrollY);\n }\n\n public getModel() {\n return this.model;\n }\n\n public destroy() {\n if (this.destroyed) return;\n this.destroyed = true;\n this.observer?.disconnect();\n\n if (this.model) {\n this.mainScene.remove(this.model);\n\n this.model.traverse((child: THREE.Object3D) => {\n if (!(child as THREE.Mesh).isMesh) return;\n const mesh = child as THREE.Mesh;\n mesh.geometry.dispose();\n const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\n for (const m of materials) {\n if (!m) continue;\n disposeMaterialTextures(m);\n m.dispose();\n }\n });\n this.model = null;\n }\n }\n}\n\n/**\n * Material が持つテクスチャ系プロパティを列挙して dispose する。\n * three.js の `Material.dispose()` は texture を一緒に dispose してくれないので\n * 自前で解放しないと GPU メモリにテクスチャが残り続ける。\n *\n * 一般的な PBR / Standard / Basic 系で使われるテクスチャキーを網羅する。\n */\nfunction disposeMaterialTextures(material: THREE.Material): void {\n const textureKeys = [\n \"map\",\n \"alphaMap\",\n \"aoMap\",\n \"bumpMap\",\n \"displacementMap\",\n \"emissiveMap\",\n \"envMap\",\n \"lightMap\",\n \"metalnessMap\",\n \"normalMap\",\n \"roughnessMap\",\n \"specularMap\",\n \"clearcoatMap\",\n \"clearcoatNormalMap\",\n \"clearcoatRoughnessMap\",\n \"sheenColorMap\",\n \"sheenRoughnessMap\",\n \"transmissionMap\",\n \"thicknessMap\",\n \"iridescenceMap\",\n \"iridescenceThicknessMap\",\n \"anisotropyMap\",\n \"matcap\",\n \"gradientMap\",\n ] as const;\n const m = material as unknown as Record<string, unknown>;\n for (const key of textureKeys) {\n const value = m[key];\n if (value && (value as THREE.Texture).isTexture) {\n (value as THREE.Texture).dispose();\n }\n }\n}\n","/**\n * ScrollSync — WebGL スクロール同期。\n *\n * container 要素を `position: absolute; top: 0; left: 0` で document に貼り、毎 rAF で\n * 1 回読んだ `scrollX/Y` を transform に流す。**同じ scrollX/Y を DomPlane の sceneY 計算\n * にも渡してもらう**ことで、paint 時に scroll が進んで rAF↔paint Δ が出ても container と\n * plane が一緒にズレる → 視覚的に DOM ↔ mesh は完璧に一致する。\n *\n * **scrollHeight 非侵襲**:\n * 単純に `height = viewport * (1 + 2*padding)` を貼ると、container の layout box が\n * `1 + 2*padding` 倍の高さを占有し、body の scrollHeight を底上げしてしまう\n * (コンテンツが短いページや、スクロール末尾で「実コンテンツを越えて余白までスクロール」\n * できる現象)。\n *\n * 対策として **毎フレ effective padding を `body.scrollHeight - scrollY - viewport`\n * でクランプ**する(`#updateCanvasSize` と同じ formula)。\n * - 長いページ + 上の方にいる: requested = effective(フル padding)\n * - 末尾に近づく: max が縮み effective も縮む → container も縮む\n * - 末尾ぴったり: effective = 0 → container = viewport ぴったり\n *\n * canvas drawing buffer も同期して resize する必要があるため、resize 通知 callback を\n * `setResizeCallback()` で受け取る。Core 側でこの callback に `renderer.setSize` /\n * `camera.resize` / `postEffect.resize` を繋ぐ。\n *\n */\n\nexport interface ScrollSyncOptions {\n /**\n * 上下パディング比率(0〜1)。canvas の高さを viewport * (1 + padding * 2) にし、\n * rAF↔paint 間の scroll 進行で canvas が viewport から欠ける現象を吸収する。\n * 末尾に近づくと effective 値は自動でクランプされ、body.scrollHeight を底上げしない。\n * @default 0\n */\n padding?: number;\n\n /**\n * スクロール強度(速度)を strength getter で提供するか。\n * @default false\n */\n trackStrength?: boolean;\n\n /**\n * strength の減衰速度。\n * @default 10\n */\n strengthDecay?: number;\n}\n\nexport class ScrollSync {\n private container: HTMLElement;\n private _padding: number;\n private _trackStrength: boolean;\n private _strengthDecay: number;\n private _strength: number = 0;\n private _prevScrollY: number = 0;\n private _prevTime: number = 0;\n private _viewportWidth: number = 0;\n private _viewportHeight: number = 0;\n private _enabled: boolean = true;\n\n /**\n * container の **元 CSS 指定** を window に対する比率として保持する。\n *\n * 例: container の CSS が `width: 100vw; height: 110vh` なら\n * _widthRatio = 1.0, _heightRatio = 1.1\n *\n * `applyContainerStyles()` で container は `position: absolute; width/height = JS制御`\n * に書き換えられて以降「元の CSS による intrinsic size」は測れなくなる。よって\n * **constructor 進入時** に getBoundingClientRect から ratio を snapshot し、以降の\n * resize ではこの ratio を window 寸法に掛けて canvas 物理サイズを決める。\n *\n * これにより `#canvas { width: 100vw; height: 110vh; }` のような CSS が\n * window resize / orientation 変更後も「100vw × 110vh」相当を保ち続ける。\n */\n private _widthRatio: number = 1;\n private _heightRatio: number = 1;\n\n /**\n * 現在 transform / canvas 寸法に反映している padding 値(px)。\n * クランプの出力。`_padding * viewportHeight` の上限と、document の余り\n * 領域の最小から決まる。\n */\n private _effectivePaddingPx: number = 0;\n\n /** 計算用の canvas rect(scroll transform を含まない論理 rect、effective padding 基準) */\n private _logicalRect: DOMRect = new DOMRect();\n\n /**\n * effective padding が変わって canvas drawing buffer の resize が必要な時に呼ぶ callback。\n * 引数は `{ width, height }`(drawing buffer の新サイズ = container の新サイズ)。\n * Core 側でこれに `renderer.setSize` / `camera.resize` / `postEffect.resize` をぶら下げる。\n */\n private _onResize: ((size: { width: number; height: number }) => void) | null = null;\n\n /**\n * destroy 時の復元用に、constructor 進入時の inline style を退避しておく。\n * ユーザーが先に `container.style.position = 'relative'` などを当てていた場合に、\n * destroy で「空文字に潰す」ではなく元の値に戻すために必要。\n */\n private _originalStyles: {\n position: string;\n left: string;\n top: string;\n width: string;\n height: string;\n overflow: string;\n transform: string;\n pointerEvents: string;\n willChange: string;\n };\n\n constructor(container: HTMLElement, options: ScrollSyncOptions = {}) {\n this.container = container;\n this._padding = options.padding ?? 0;\n this._trackStrength = options.trackStrength ?? false;\n this._strengthDecay = options.strengthDecay ?? 10;\n this._prevScrollY = window.scrollY;\n this._prevTime = performance.now() / 1000;\n\n // 元 inline style を snapshot(destroy 時に復元するため)。\n const s = container.style;\n this._originalStyles = {\n position: s.position,\n left: s.left,\n top: s.top,\n width: s.width,\n height: s.height,\n overflow: s.overflow,\n transform: s.transform,\n pointerEvents: s.pointerEvents,\n willChange: s.willChange,\n };\n\n // applyContainerStyles 前に container の CSS-computed rect を測り、window 寸法に\n // 対する比率を snapshot する。CSS の vw/vh 等で書かれた指定はこの時点で window に\n // 応じた px 値として評価済みなので、ratio として保存しておけば後の resize でも\n // 「同じ CSS 指定」を再現できる。\n //\n // container がまだ layout されていない (display:none / jsdom 環境 / size 未指定で\n // 0px 扱い) ケースでは ratio = 0 になって canvas が潰れるので、その場合は default 1.0\n // (= window 寸法そのまま) のまま落とす。\n const initialRect = container.getBoundingClientRect();\n if (window.innerWidth > 0 && initialRect.width > 0) {\n this._widthRatio = initialRect.width / window.innerWidth;\n }\n if (window.innerHeight > 0 && initialRect.height > 0) {\n this._heightRatio = initialRect.height / window.innerHeight;\n }\n\n this.applyContainerStyles();\n this.updateSize();\n }\n\n private applyContainerStyles(): void {\n this.container.style.position = 'absolute';\n this.container.style.left = '0';\n this.container.style.top = '0';\n // 子の canvas overflow を視覚的にクリップ。drawing buffer を resize していない瞬間に\n // visual artifact が出ないようにするため。\n this.container.style.overflow = 'hidden';\n this.container.style.pointerEvents = 'none';\n this.container.style.willChange = 'transform';\n }\n\n /**\n * ビューポートサイズが変わった時に呼ぶ。effective padding は requested で初期化し、\n * 次の update() 呼び出しでクランプが効く。\n *\n * 引数を省略した場合は、constructor で snapshot した container の CSS ratio を\n * window 寸法に掛けて自動算出する。明示的に値を渡せば override 可能(scrollbar を\n * 差し引いた幅にしたい等のケース)。\n */\n updateSize(wrapperWidth?: number, wrapperHeight?: number): void {\n this._viewportWidth = wrapperWidth ?? window.innerWidth * this._widthRatio;\n this._viewportHeight =\n wrapperHeight ?? window.innerHeight * this._heightRatio;\n\n // resize 直後は full padding で開始(次の update() でクランプされる)。\n const requestedPaddingPx = this._viewportHeight * this._padding;\n this._effectivePaddingPx = requestedPaddingPx;\n\n const canvasHeight = this._viewportHeight + 2 * requestedPaddingPx;\n\n this.container.style.width = `${this._viewportWidth}px`;\n this.container.style.height = `${canvasHeight}px`;\n\n this._logicalRect = new DOMRect(\n 0,\n -requestedPaddingPx,\n this._viewportWidth,\n canvasHeight,\n );\n\n // resize callback を発火(Core 側で renderer.setSize 等を呼ばせる)\n this._onResize?.({ width: this._viewportWidth, height: canvasHeight });\n\n // 現在のスクロール位置で transform を即時反映(初期化・リサイズ直後の表示崩れ防止)\n this.applyTransform(window.scrollX, window.scrollY);\n }\n\n /**\n * 毎 rAF で呼ぶ。**plane の sceneY 計算と同一の scrollX/Y を渡すこと**。\n * これが cancel の不変条件。\n *\n * padding clamp:\n * maxPaddingY = body.scrollHeight - scrollY - viewportHeight\n * effective = clamp(requested, 0, maxPaddingY)\n * effective が変わったら canvas drawing buffer も同期して resize する。\n */\n update(scrollX: number, scrollY: number): void {\n if (!this._enabled) return;\n\n // === per-frame padding clamp ===\n const requestedPaddingPx = this._viewportHeight * this._padding;\n const maxPaddingPx = Math.max(\n 0,\n document.body.scrollHeight - scrollY - this._viewportHeight,\n );\n const effective = Math.min(requestedPaddingPx, maxPaddingPx);\n\n if (effective !== this._effectivePaddingPx) {\n this._effectivePaddingPx = effective;\n const newHeight = this._viewportHeight + 2 * effective;\n this.container.style.height = `${newHeight}px`;\n this._logicalRect = new DOMRect(\n 0,\n -effective,\n this._viewportWidth,\n newHeight,\n );\n this._onResize?.({ width: this._viewportWidth, height: newHeight });\n }\n\n this.applyTransform(scrollX, scrollY);\n if (this._trackStrength) {\n this.updateStrength(scrollY);\n }\n }\n\n private applyTransform(scrollX: number, scrollY: number): void {\n // formula。effective padding を使うことで「末尾で padding が縮んだ時に\n // canvas が消えそうな位置にずれる」現象も自然に解決する(padding=0 なら canvas は\n // viewport にぴったり)。\n this.container.style.transform =\n `translate3d(${scrollX}px, ${scrollY - this._effectivePaddingPx}px, 0)`;\n }\n\n /** スクロール速度ベースの strength 値を更新。 */\n private updateStrength(scrollY: number): void {\n const scrollDelta = scrollY - this._prevScrollY;\n const now = performance.now() / 1000;\n const dt = now - this._prevTime;\n\n if (dt > 0) {\n const targetStrength =\n (Math.abs(scrollDelta) * 10) / this._viewportHeight;\n this._strength *= Math.exp(-dt * this._strengthDecay);\n this._strength += Math.min(targetStrength, 5);\n }\n\n this._prevScrollY = scrollY;\n this._prevTime = now;\n }\n\n /**\n * canvas drawing buffer の resize が必要な時に呼ばれる callback を登録する。\n * @internal Core 側で renderer.setSize / camera.resize / postEffect.resize を繋ぐ。\n */\n setResizeCallback(cb: (size: { width: number; height: number }) => void): void {\n this._onResize = cb;\n }\n\n /**\n * DomPositionCalculator 用の **論理** rect。\n *\n * **注意**: これは「ブラウザ上での canvas の getBoundingClientRect」ではない。\n * `top = -effectivePadding`, `height = viewport + 2 * effectivePadding` で、\n * scroll に追従して動く container 内のローカル座標系を表す。effective padding は\n * 末尾近くで自動的に縮む。\n */\n get logicalRect(): DOMRect {\n return this._logicalRect;\n }\n\n /** 現在の effective padding(px)。テスト・デバッグ用。 */\n get effectivePadding(): number {\n return this._effectivePaddingPx;\n }\n\n /**\n * 現在のスクロール速度(0〜1 にクランプ)。\n *\n * **`trackStrength: false` で構築している場合は常に 0 を返す**。値を使いたい場合は\n * options で trackStrength を true にして構築すること。DEV では誤用防止のため\n * 1 度だけ console.warn を出す。\n */\n get strength(): number {\n if (!this._trackStrength) {\n if (import.meta.env?.DEV && !this._warnedStrength) {\n this._warnedStrength = true;\n console.warn(\n '[ScrollSync] strength は trackStrength: true で初期化した時のみ意味のある値を返します。' +\n ' 現在は trackStrength=false なので常に 0 です。'\n );\n }\n return 0;\n }\n return Math.min(1, this._strength);\n }\n private _warnedStrength: boolean = false;\n\n get padding(): number {\n return this._padding;\n }\n\n /**\n * 入力受付の有効/無効。\n *\n * disable 中は `update()` が no-op になり container の transform は触らない(disable 直前の\n * 位置で固定される)。enable に戻したフレから transform 更新が再開する。\n *\n * disable で「container を素の状態に戻したい」場合は `destroy()` を呼ぶこと。\n */\n set enabled(value: boolean) {\n this._enabled = value;\n }\n\n get enabled(): boolean {\n return this._enabled;\n }\n\n destroy(): void {\n // constructor で snapshot した元 inline style に戻す。\n const o = this._originalStyles;\n const s = this.container.style;\n s.position = o.position;\n s.left = o.left;\n s.top = o.top;\n s.width = o.width;\n s.height = o.height;\n s.overflow = o.overflow;\n s.transform = o.transform;\n s.pointerEvents = o.pointerEvents;\n s.willChange = o.willChange;\n }\n}\n","import * as THREE from 'three';\nimport { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';\n// `stats.js` / `lil-gui` は optional peer。runtime では `showStats` / `showGUI` が\n// 有効な時だけ dynamic import するので、ここは type-only import で .d.ts のみに残す\n// (tree-shaking で本体 bundle に含まれない)。\nimport type Stats from 'stats.js';\nimport type GUI from 'lil-gui';\nimport { Camera } from './Camera';\nimport { Light } from './Light';\nimport { DomPlane } from './DomPlane';\nimport { Dom3DObject } from './Dom3DObject';\nimport { ScrollSync } from './ScrollSync';\nimport { EffectComposer } from './EffectComposer';\nimport type { EffectLike } from './EffectComposer';\nimport type { ScrollSyncOptions } from './ScrollSync';\nimport type { BaseEffect } from './effects/BaseEffect';\nimport type {\n CreatePlaneOptions,\n Create3DObjectOptions,\n WebGLAppOptions,\n} from './types';\n\nexport class WebGLApp {\n container: HTMLElement;\n canvas: HTMLCanvasElement;\n scene: THREE.Scene;\n renderer: THREE.WebGLRenderer;\n camera: Camera;\n light: Light;\n controls: OrbitControls | null;\n updateCallbacks: (() => void)[];\n resizeCallbacks: (() => void)[];\n rect: DOMRect;\n domPlanes: DomPlane[];\n dom3DObjects: Dom3DObject[];\n clock: THREE.Clock;\n scrollSync: ScrollSync | null = null;\n private options: WebGLAppOptions;\n private rafId: number = 0;\n private resizeTimer: ReturnType<typeof setTimeout> | null = null;\n private eventAbort: AbortController = new AbortController();\n /**\n * mousemove listener 専用の AbortController。`setMouseTrackingEnabled()` で動的に\n * detach 可能にするため `eventAbort` とは別に持つ。null の時は attach 済みでない。\n */\n private _mouseAbort: AbortController | null = null;\n /**\n * canvas の viewport 上の矩形をキャッシュ。mousemove ごとに `getBoundingClientRect`\n * を呼ぶと layout 強制が走るため、resize 時 + (ScrollSync 無効時の) scroll 時に\n * invalidate する形にしてフィールドアクセスで済むようにする。\n * ScrollSync 有効時は canvas viewport rect が固定 (RafScroll が同 rAF tick で\n * scrollTo するため paint 時 actual と JS rAF が一致 → 動かない) なので\n * scroll 時の invalidate は不要。\n */\n private _canvasRect: DOMRect | null = null;\n private mouse: THREE.Vector2;\n private prevMouse: THREE.Vector2;\n /**\n * マウスが canvas 矩形の内側に居るか。mousemove イベントごとに更新し、\n * rAF tick 内の raycaster はこのフラグで実行を決める。\n * (uMouseUV の更新を完全に rAF 駆動にすることで paint と同期させ、\n * 「mousemove 非同期発火による uniform のちらつき」を消す)\n */\n private _mouseInside: boolean = false;\n /** raycaster で使う NDC バッファ (毎フレ allocate を避ける) */\n private _ndcBuf: THREE.Vector2 = new THREE.Vector2();\n /** getMouseDelta の戻り値スクラッチ (毎フレ clone を避ける) */\n private _mouseDeltaBuf: THREE.Vector2 = new THREE.Vector2();\n private raycaster: THREE.Raycaster;\n private hoveredPlane: DomPlane | null;\n private domPlaneMeshes: THREE.Mesh[] = [];\n private postEffect: EffectLike | null = null;\n private internalComposer: EffectComposer | null = null;\n private effects: BaseEffect[] = [];\n private stats: Stats | null = null;\n /**\n * lil-gui の root インスタンス。\n * showGUI が false なら null のまま。最初の `setupGUI` を持つ effect が\n * `addEffect()` 経由で登録された瞬間に生成する(lazy + dynamic import)。\n */\n private gui: GUI | null = null;\n /**\n * lil-gui の dynamic import promise。複数 effect の addEffect が同時に来た時、\n * 二重 load を防ぐ。一度 resolve したら次回以降は `this.gui` を直接返す。\n */\n private _guiLoadPromise: Promise<GUI> | null = null;\n /**\n * destroy 済みフラグ。animate() 実行中に user callback から destroy() が\n * 呼ばれると、renderer.dispose() 後の続きで renderer.render() を呼んで\n * WebGL エラーになる。各段階の冒頭で見て早期 return する。\n */\n private destroyed: boolean = false;\n\n constructor(selector: string | HTMLElement, options: WebGLAppOptions = {}) {\n // コンテナを取得\n const element =\n typeof selector === 'string'\n ? document.querySelector(selector)\n : selector;\n if (!element) {\n throw new Error(`Container not found: ${selector}`);\n }\n this.container = element as HTMLElement;\n\n // canvasを生成してコンテナに追加\n this.canvas = document.createElement('canvas');\n this.container.appendChild(this.canvas);\n\n this.rect = this.container.getBoundingClientRect();\n this.scene = new THREE.Scene();\n this.renderer = new THREE.WebGLRenderer({\n canvas: this.canvas,\n antialias: true,\n alpha: true,\n });\n this.camera = new Camera(this.rect);\n this.light = new Light(this.scene);\n this.controls = null;\n this.updateCallbacks = [];\n this.resizeCallbacks = [];\n this.domPlanes = [];\n this.dom3DObjects = [];\n this.clock = new THREE.Clock();\n this.options = { enableMouseTracking: true, showGUI: true, ...options };\n this.mouse = new THREE.Vector2(0.5, 0.5);\n this.prevMouse = new THREE.Vector2(0.5, 0.5);\n this.raycaster = new THREE.Raycaster();\n this.hoveredPlane = null;\n\n // ScrollSync の初期化(renderer/camera生成後、init前に実行)\n if (options.scrollSync) {\n const syncOptions: ScrollSyncOptions =\n typeof options.scrollSync === 'object' ? options.scrollSync : {};\n this.scrollSync = new ScrollSync(this.container, syncOptions);\n // ScrollSync有効時はlogicalRectを使用\n this.rect = this.scrollSync.logicalRect;\n // 動的 padding clamp で canvas サイズが変わったら renderer / camera / 全体 RT を\n // 同期 resize する。これをやらないと canvas drawing buffer と container の CSS 寸法\n // が乖離して overflow:hidden で view が欠ける / 引き伸びる。\n this.scrollSync.setResizeCallback(() => this._onScrollSyncResize());\n }\n\n // renderer/cameraを正しいrectでセットアップ\n this.init();\n\n // stats.js の FPS パネル (optional peer)。dynamic import なので\n // showStats: false の利用者は stats.js をインストールする必要がない。\n // animate() 内の begin/end は optional chaining なので、load 完了前は no-op で安全。\n if (options.showStats) {\n void import('stats.js').then(({ default: StatsCtor }) => {\n if (this.destroyed) return;\n this.stats = new StatsCtor();\n this.stats.showPanel(0); // 0: fps, 1: ms, 2: mb\n // 同一ページに複数 WebGLApp を置くと panel が重なるので、\n // 呼び出し側で statsParent を指定すれば任意要素にぶら下げられる。\n (options.statsParent ?? document.body).appendChild(this.stats.dom);\n }).catch((err) => {\n console.warn(\n '[WebGLApp] showStats: true ですが stats.js が読み込めませんでした。' +\n 'npm install stats.js してください。',\n err,\n );\n });\n }\n\n this.setupEventListeners();\n this.animate();\n }\n\n private init() {\n // シーンの背景を透明に\n this.scene.background = null;\n\n // レンダラーの設定(this.rectは ScrollSync有効時はlogicalRect)\n this.renderer.setSize(this.rect.width, this.rect.height);\n this.renderer.setPixelRatio(\n Math.min(window.devicePixelRatio, this.options.maxPixelRatio ?? 2),\n );\n\n // カメラをrectに合わせて再セットアップ\n this.camera.resize(this.rect);\n\n // 出力カラースペース(デフォルト SRGB。Linear が必要な場合は options で指定)\n this.renderer.outputColorSpace =\n this.options.outputColorSpace ?? THREE.SRGBColorSpace;\n }\n\n // sceneのgetterを追加\n getScene() {\n return this.scene;\n }\n\n // カメラのgetterを追加\n getCamera() {\n return this.camera;\n }\n\n // rendererのgetterを追加\n getRenderer() {\n return this.renderer;\n }\n\n // lightのgetterを追加\n getLight() {\n return this.light;\n }\n\n // canvasサイズを追加\n getViewPort() {\n return this.rect;\n }\n\n // マウス座標を取得(UV座標: 0~1)\n getMouse() {\n return this.mouse;\n }\n\n // 前回のマウス座標を取得(UV座標: 0~1)\n getPrevMouse() {\n return this.prevMouse;\n }\n\n // マウス移動量を取得。内部スクラッチを使い回すので、保持したい場合は呼び出し側で clone する。\n getMouseDelta() {\n return this._mouseDeltaBuf.copy(this.mouse).sub(this.prevMouse);\n }\n\n // ScrollSyncを取得\n getScrollSync() {\n return this.scrollSync;\n }\n\n // オブジェクトを追加するメソッド\n addObject(object: THREE.Object3D) {\n this.scene.add(object);\n }\n\n // オブジェクトを削除するメソッド\n removeObject(object: THREE.Object3D) {\n this.scene.remove(object);\n }\n\n // DomPlaneを作成するメソッド\n createPlane(\n selector: string | HTMLElement | null,\n options?: CreatePlaneOptions\n ) {\n if (this.destroyed) {\n throw new Error('[WebGLApp] createPlane(): destroy 済みのインスタンスでは使えません。');\n }\n let element: HTMLElement | null = null;\n\n if (selector !== null && selector !== undefined) {\n element =\n typeof selector === 'string'\n ? (document.querySelector(selector) as HTMLElement)\n : selector;\n\n if (!element) {\n throw new Error(`Element not found: ${selector}`);\n }\n }\n\n const domPlane = new DomPlane(\n element,\n this.scene,\n this.rect,\n this.renderer,\n options,\n this.clock,\n );\n // plane.addEffect() からも GUI を生やせるよう WebGLApp の lazy-getter を渡す。\n // showGUI が false なら provider は渡さない(DomPlane 側で何もしない)。\n // lil-gui は optional peer の dynamic import なので Promise を返す provider。\n if (this.options.showGUI !== false) {\n domPlane._setGuiProvider(() => this._ensureGUIAsync());\n }\n this.domPlanes.push(domPlane);\n // element 無し (= フルスクリーン背景 plane) は raycast 候補から外す。\n // canvas 全面に重なるので element 付き plane と必ず distance 同値で衝突し、\n // stable sort で先勝ち → 後から push された DOM 連動 plane の hover を奪う。\n // 背景 plane は概念的に hover の対象ではないので最初から除外する。\n if (element) {\n this.domPlaneMeshes.push(domPlane.getMesh());\n }\n\n return domPlane;\n }\n\n // DomPlaneを削除するメソッド\n removePlane(domPlane: DomPlane) {\n if (this.destroyed) return;\n const index = this.domPlanes.indexOf(domPlane);\n if (index > -1) {\n this.domPlanes.splice(index, 1);\n // createPlane で element 無し plane は push していないので indexOf で引く。\n const meshIndex = this.domPlaneMeshes.indexOf(domPlane.getMesh());\n if (meshIndex > -1) this.domPlaneMeshes.splice(meshIndex, 1);\n domPlane.destroy();\n }\n }\n\n // Dom3DObject を作成する。\n // - selector あり (string / HTMLElement): DOM 要素位置に追従。\n // - selector が null/undefined: scene 原点に固定 (viewport 中心固定の使い方)。\n // ScrollSync 有効時でも、Lenis のような virtual scroll で JS rAF と paint の\n // scrollY が同一に揃っていれば container は viewport に完璧固定され、scene 原点\n // 固定 obj も視覚的に viewport 固定として機能する。\n create3DObject(\n selector: string | HTMLElement | null,\n options: Create3DObjectOptions,\n ) {\n if (this.destroyed) {\n throw new Error('[WebGLApp] create3DObject(): destroy 済みのインスタンスでは使えません。');\n }\n let element: HTMLElement | null = null;\n\n if (selector !== null && selector !== undefined) {\n element =\n typeof selector === 'string'\n ? (document.querySelector(selector) as HTMLElement | null)\n : selector;\n if (!element) {\n throw new Error(`Element not found: ${selector}`);\n }\n }\n\n const dom3DObject = new Dom3DObject(\n element,\n this.scene,\n this.rect,\n options,\n );\n this.dom3DObjects.push(dom3DObject);\n\n return dom3DObject;\n }\n\n // Dom3DObjectを削除するメソッド\n remove3DObject(dom3DObject: Dom3DObject) {\n if (this.destroyed) return;\n const index = this.dom3DObjects.indexOf(dom3DObject);\n if (index > -1) {\n this.dom3DObjects.splice(index, 1);\n dom3DObject.destroy();\n }\n }\n\n // アニメーションループに更新処理を追加するメソッド(戻り値で削除可能)\n addUpdateCallback(callback: () => void): () => void {\n if (this.destroyed) return () => {};\n this.updateCallbacks.push(callback);\n return () => {\n const index = this.updateCallbacks.indexOf(callback);\n if (index > -1) this.updateCallbacks.splice(index, 1);\n };\n }\n\n // リサイズ時の処理を追加するメソッド(戻り値で削除可能)\n addResizeCallback(callback: () => void): () => void {\n if (this.destroyed) return () => {};\n this.resizeCallbacks.push(callback);\n return () => {\n const index = this.resizeCallbacks.indexOf(callback);\n if (index > -1) this.resizeCallbacks.splice(index, 1);\n };\n }\n\n // OrbitControlsを有効化するメソッド\n enableOrbitControls() {\n if (this.destroyed) {\n throw new Error('[WebGLApp] enableOrbitControls(): destroy 済みのインスタンスでは使えません。');\n }\n if (!this.controls) {\n // ScrollSync で container に pointer-events:none を当てていると\n // 子の canvas も入力を受け取れなくなるので、canvas 側だけ復活させる。\n if (this.scrollSync) {\n this.canvas.style.pointerEvents = 'auto';\n }\n this.controls = new OrbitControls(this.camera.instance, this.canvas);\n }\n return this.controls;\n }\n\n // OrbitControlsを取得するメソッド\n getControls() {\n return this.controls;\n }\n\n /**\n * canvas 全体にエフェクトを追加する。\n * 内部で EffectComposer を自動生成するため、別途 setPostEffect は不要。\n *\n * **注意**: `setPostEffect()` でカスタム postEffect を入れている状態でこれを呼ぶと、\n * 自動生成された EffectComposer で上書きされる(カスタム postEffect の dispose は\n * 呼ばれない=呼び出し側の責務)。両 API の併用は避けること。\n */\n addEffect<T extends BaseEffect>(effect: T): T {\n if (this.destroyed) {\n throw new Error('[WebGLApp] addEffect(): destroy 済みのインスタンスでは使えません。');\n }\n if (this.postEffect && this.postEffect !== this.internalComposer) {\n const msg =\n '[WebGLApp] addEffect() を呼ぶ前に setPostEffect() でカスタム postEffect が設定されています。' +\n '内部 EffectComposer で上書きします。カスタム postEffect は手動で dispose してください。';\n // DEV では事故防止のため throw(カスタム effect の dispose リークになるため)。\n if (import.meta.env?.DEV) throw new Error(msg);\n console.warn(msg);\n }\n if (!this.internalComposer) {\n this.internalComposer = new EffectComposer(\n this.renderer,\n this.rect.width,\n this.rect.height,\n );\n this.postEffect = this.internalComposer;\n }\n // renderer を要求するエフェクト(FluidEffect 等)に注入してから register する\n effect._setRenderer?.(this.renderer);\n effect._register(this.internalComposer);\n effect.resize?.(this.rect.width, this.rect.height);\n // setupGUI を実装している場合は自動で lil-gui パネルを生やす。\n // lil-gui は optional peer なので dynamic import の resolve を await してから呼ぶ。\n if (this.options.showGUI && effect.setupGUI) {\n this._ensureGUIAsync()\n .then((gui) => {\n if (this.destroyed) return;\n effect.setupGUI!(gui);\n })\n .catch((err) => {\n console.warn(\n '[WebGLApp] showGUI: true ですが lil-gui が読み込めませんでした。' +\n 'npm install lil-gui してください。',\n err,\n );\n });\n }\n this.effects.push(effect);\n return effect;\n }\n\n /**\n * lil-gui を dynamic import で読み込み、root インスタンスを lazy 生成して返す。\n * 同時に複数 effect から呼ばれても load promise を共有して 1 インスタンスにまとめる。\n * @internal DomPlane.addEffect / WebGLApp.addEffect から呼ばれる。\n */\n _ensureGUIAsync(): Promise<GUI> {\n if (this.gui) return Promise.resolve(this.gui);\n if (!this._guiLoadPromise) {\n this._guiLoadPromise = import('lil-gui').then(({ default: GuiCtor }) => {\n if (!this.gui) {\n this.gui = new GuiCtor({ title: this.options.guiTitle ?? 'Effects' });\n }\n return this.gui;\n });\n }\n return this._guiLoadPromise;\n }\n\n /**\n * root の lil-gui インスタンスを取得 (sync)。\n *\n * **注意**: lil-gui は dynamic import で読み込むため、初回 effect 登録直後など\n * load 中の段階では `null` を返す。確実にインスタンスを得たい場合は\n * `getGUIAsync()` を使う。`showGUI: false` の場合は常に `null`。\n */\n getGUI(): GUI | null {\n if (this.options.showGUI === false) return null;\n return this.gui;\n }\n\n /**\n * lil-gui を必要に応じて load し、インスタンスを返す。\n * `showGUI: false` の場合は `null` を resolve する。\n */\n getGUIAsync(): Promise<GUI | null> {\n if (this.options.showGUI === false) return Promise.resolve(null);\n return this._ensureGUIAsync();\n }\n\n /**\n * ポストエフェクトを設定(低レベル API)。\n * 自前で `EffectLike`(render/resize/dispose)を実装したオブジェクトを差し込みたい場合のみ使用。\n * 通常は `addEffect()` を使うこと。\n *\n * **注意**: `addEffect()` で追加済みのエフェクトがある状態で呼ぶと、\n * 自動 EffectComposer を捨てて引数の postEffect に差し替える。既存 effect の\n * dispose は呼ばれない(必要なら先に `clearEffects()` を呼ぶこと)。\n */\n setPostEffect(postEffect: EffectLike): void {\n if (this.destroyed) {\n throw new Error('[WebGLApp] setPostEffect(): destroy 済みのインスタンスでは使えません。');\n }\n if (this.effects.length > 0) {\n const msg =\n '[WebGLApp] setPostEffect() が呼ばれましたが、addEffect() で追加した effect が既に存在します。' +\n '内部 EffectComposer を破棄してカスタム postEffect に差し替えます。' +\n '事前に clearEffects() を呼ぶことを推奨します。';\n if (import.meta.env?.DEV) throw new Error(msg);\n console.warn(msg);\n // 既存 effect の clean up(dispose まで)\n this.clearEffects();\n }\n this.postEffect = postEffect;\n }\n\n /**\n * `addEffect()` で登録した effect を 1 つ取り除く。\n *\n * - 内部 EffectComposer から該当 pass を外し、material を dispose する\n * - effect 自体の `dispose()` も呼ぶ(FluidEffect 等の RT も解放)\n * - 全 effect が空になった場合、internalComposer はそのまま残す(次の addEffect で再利用)\n *\n * 登録されていない effect を渡した時は `false` を返して何もしない。\n */\n removeEffect(effect: BaseEffect): boolean {\n if (this.destroyed) return false;\n const idx = this.effects.indexOf(effect);\n if (idx < 0) return false;\n this.effects.splice(idx, 1);\n const pass = effect.getPass();\n if (pass && this.internalComposer) {\n this.internalComposer.removeEffect(pass);\n }\n effect.dispose?.();\n return true;\n }\n\n /**\n * 登録されたエフェクトとポストエフェクトをすべて解除して破棄する。\n * addEffect で追加した全 effect の dispose() を呼び、内部 EffectComposer も解放する。\n */\n clearEffects(): void {\n if (this.destroyed) return;\n for (const effect of this.effects) {\n effect.dispose?.();\n }\n this.effects = [];\n this.postEffect?.dispose();\n this.postEffect = null;\n this.internalComposer = null;\n }\n\n /**\n * @deprecated `clearEffects()` を使ってください。挙動は同一です。\n */\n removePostEffect(): void {\n this.clearEffects();\n }\n\n private setupEventListeners() {\n const signal = this.eventAbort.signal;\n\n window.addEventListener(\n 'resize',\n () => {\n if (this.resizeTimer) clearTimeout(this.resizeTimer);\n this.resizeTimer = setTimeout(() => this.onResize(), 100);\n },\n { signal },\n );\n\n // ScrollSync 無効時は scroll で canvas viewport rect が変わるので invalidate。\n // 有効時は RafScroll により JS rAF と paint が一致するため不要。\n if (!this.scrollSync) {\n window.addEventListener('scroll', this.invalidateCanvasRect, {\n signal,\n passive: true,\n });\n }\n\n // マウスイベントは setMouseTrackingEnabled 経由で attach する(動的切替対応)。\n if (this.options.enableMouseTracking) {\n this.setMouseTrackingEnabled(true);\n }\n }\n\n /**\n * mousemove tracking の動的な ON/OFF。\n * - true: 未 attach なら mousemove listener を追加する\n * - false: attach 済みなら detach する(hover も解除)\n *\n * `destroy()` 時は eventAbort と一緒に自動 detach される(_mouseAbort 個別 abort も呼ぶ)。\n */\n setMouseTrackingEnabled(enabled: boolean): void {\n if (this.destroyed) return;\n this.options.enableMouseTracking = enabled;\n if (enabled) {\n if (this._mouseAbort) return; // すでに attach 済み\n this._mouseAbort = new AbortController();\n window.addEventListener(\n 'mousemove',\n (e: MouseEvent) => this.onMouseMove(e),\n { signal: this._mouseAbort.signal },\n );\n } else {\n this._mouseAbort?.abort();\n this._mouseAbort = null;\n this._mouseInside = false;\n if (this.hoveredPlane) {\n this.hoveredPlane.setHoverInfo(false, null);\n this.hoveredPlane = null;\n }\n }\n }\n\n private invalidateCanvasRect = (): void => {\n this._canvasRect = null;\n };\n\n /**\n * ScrollSync の動的 padding clamp で canvas 寸法が変わった時に呼ばれる。\n *\n * window resize(onResize)と違い、DOM の位置や element 側の bbox は変わっていない。\n * 必要なのは「canvas drawing buffer のサイズに依存するもの」だけ:\n * - renderer.setSize(drawing buffer + canvas.style.{width,height})\n * - camera の aspect / distance(rect.height に応じて変わる)\n * - postEffect の RT サイズ\n * - 各 effect の RT サイズ\n *\n * DomPlane の DOM 連動 plane は positionCalculator の bbox に依存していて canvas 高さに\n * 依存しないので触らない(calculateWebGLPosition の数式が padding を相殺するため不変)。\n * フルスクリーン plane(element=null)は canvasRect.width/height をスケールに使うので、\n * その分だけ canvasRect 参照を更新する。\n */\n private _onScrollSyncResize = (): void => {\n if (this.destroyed || !this.scrollSync) return;\n this.rect = this.scrollSync.logicalRect;\n if (this.rect.width <= 0 || this.rect.height <= 0) return;\n this.camera.resize(this.rect);\n this.renderer.setSize(this.rect.width, this.rect.height);\n for (let i = 0, n = this.domPlanes.length; i < n; i++) {\n const plane = this.domPlanes[i];\n plane.setCanvasRect(this.rect);\n // フルスクリーン plane (element=null) は mesh.scale / uResolution が canvasRect 基準。\n // canvas サイズが変わったら同期して更新しないと camera 投影とズレて「カタカタ」する。\n // DOM 連動 plane は bbox が変わらないので skip(layout 強制を避ける)。\n if (!plane.element) {\n plane.resize();\n }\n }\n for (let i = 0, n = this.dom3DObjects.length; i < n; i++) {\n this.dom3DObjects[i].setCanvasRect(this.rect);\n }\n this.postEffect?.resize(this.rect.width, this.rect.height);\n const effects = this.effects;\n for (let i = 0, n = effects.length; i < n; i++) {\n effects[i].resize?.(this.rect.width, this.rect.height);\n }\n };\n\n /** mousemove 等で頻繁に必要な canvas viewport rect を遅延 + キャッシュで返す。 */\n private getCanvasRect(): DOMRect {\n if (!this._canvasRect) {\n this._canvasRect = this.canvas.getBoundingClientRect();\n }\n return this._canvasRect;\n }\n\n private onResize() {\n // canvas viewport rect は確実に変わるのでキャッシュを invalidate\n this._canvasRect = null;\n\n if (this.scrollSync) {\n // ScrollSync 有効時: container の元 CSS ratio (init 時 snapshot) を基準に\n // window 寸法から再計算させる。引数省略で ratio 経路が走る。\n this.scrollSync.updateSize();\n this.rect = this.scrollSync.logicalRect;\n } else {\n this.rect = this.container.getBoundingClientRect();\n }\n\n // container が一時的に display:none 等になっているとき、以降の計算で NaN が混ざるので skip。\n if (this.rect.width <= 0 || this.rect.height <= 0) return;\n\n // カメラのアスペクト比を更新\n this.camera.resize(this.rect);\n\n // レンダラーのサイズを更新\n this.renderer.setSize(this.rect.width, this.rect.height);\n this.renderer.setPixelRatio(\n Math.min(window.devicePixelRatio, this.options.maxPixelRatio ?? 2),\n );\n\n // すべてのDomPlaneのcanvasRectとサイズと位置を更新\n const planes = this.domPlanes;\n for (let i = 0, n = planes.length; i < n; i++) {\n const plane = planes[i];\n plane.setCanvasRect(this.rect);\n plane.resize();\n }\n\n // すべてのDom3DObjectのcanvasRectとサイズと位置を更新\n const objects = this.dom3DObjects;\n for (let i = 0, n = objects.length; i < n; i++) {\n const obj = objects[i];\n obj.setCanvasRect(this.rect);\n obj.resize();\n }\n\n // ポストエフェクトのリサイズ\n this.postEffect?.resize(this.rect.width, this.rect.height);\n const effects = this.effects;\n for (let i = 0, n = effects.length; i < n; i++) {\n effects[i].resize?.(this.rect.width, this.rect.height);\n }\n\n // 登録された更新処理を実行\n const resizeCallbacks = this.resizeCallbacks;\n for (let i = 0, n = resizeCallbacks.length; i < n; i++) {\n resizeCallbacks[i]();\n }\n }\n\n /**\n * mousemove は **mouse 座標と canvas 内外フラグだけ** 更新する。\n * raycaster / setHoverInfo は呼ばない (= uMouseUV を直接書かない)。\n * uniform 更新は `_updateHoverFromMouse` 経由で rAF tick 内に集約することで、\n * paint と完全同期させ「mousemove 非同期発火による uMouseUV のちらつき」を防ぐ。\n */\n private onMouseMove(event: MouseEvent) {\n const rect = this.getCanvasRect();\n\n const isInside =\n event.clientX >= rect.left &&\n event.clientX <= rect.right &&\n event.clientY >= rect.top &&\n event.clientY <= rect.bottom;\n\n this._mouseInside = isInside;\n if (!isInside) return;\n\n this.mouse.x = (event.clientX - rect.left) / rect.width;\n this.mouse.y = 1.0 - (event.clientY - rect.top) / rect.height;\n }\n\n /**\n * rAF tick 内の Phase A で呼ばれる。最新の `this.mouse` を使って raycaster を投げ、\n * hover 中の plane を判定 + `setHoverInfo` (= uMouseUV / uIsHovered の更新) する。\n * mousemove イベントから切り離すことで全 plane の uniform が 1 tick = 1 確定値で\n * 揃い、paint と同期する。\n */\n private _updateHoverFromMouse(): void {\n if (!this.options.enableMouseTracking) return;\n if (this.domPlaneMeshes.length === 0) return;\n\n // canvas 外なら hover を解除\n if (!this._mouseInside) {\n if (this.hoveredPlane) {\n this.hoveredPlane.setHoverInfo(false, null);\n this.hoveredPlane = null;\n }\n return;\n }\n\n this._ndcBuf.set(this.mouse.x * 2 - 1, this.mouse.y * 2 - 1);\n this.raycaster.setFromCamera(this._ndcBuf, this.camera.instance);\n const intersects = this.raycaster.intersectObjects(this.domPlaneMeshes, false);\n\n if (intersects.length > 0) {\n const intersect = intersects[0];\n // domPlaneMeshes は domPlanes と index 1:1 対応していない\n // (element 無しの背景 plane は domPlaneMeshes に入っていない) ため、\n // mesh から対応する DomPlane を find で逆引きする。\n if (intersect.uv) {\n const hitMesh = intersect.object as THREE.Mesh;\n const plane = this.domPlanes.find((p) => p.getMesh() === hitMesh);\n if (plane) {\n // 別の plane に hover が移った時のみ前 plane を false 化(uniform 書き込みを減らす)。\n if (this.hoveredPlane && this.hoveredPlane !== plane) {\n this.hoveredPlane.setHoverInfo(false, null);\n }\n plane.setHoverInfo(true, intersect.uv);\n this.hoveredPlane = plane;\n return;\n }\n }\n }\n\n // どの plane にも当たっていない: 既存 hover があれば解除\n if (this.hoveredPlane) {\n this.hoveredPlane.setHoverInfo(false, null);\n this.hoveredPlane = null;\n }\n }\n\n destroy() {\n if (this.destroyed) return;\n this.destroyed = true;\n cancelAnimationFrame(this.rafId);\n if (this.resizeTimer) clearTimeout(this.resizeTimer);\n this.eventAbort.abort();\n this._mouseAbort?.abort();\n this._mouseAbort = null;\n\n this.domPlanes.forEach((plane) => plane.destroy());\n this.dom3DObjects.forEach((obj) => obj.destroy());\n this.domPlanes = [];\n this.dom3DObjects = [];\n this.domPlaneMeshes = [];\n this.updateCallbacks = [];\n this.resizeCallbacks = [];\n\n this.scrollSync?.destroy();\n this.scrollSync = null;\n\n for (const effect of this.effects) {\n effect.dispose?.();\n }\n this.effects = [];\n this.postEffect?.dispose();\n this.postEffect = null;\n this.internalComposer = null;\n this.controls?.dispose();\n this.controls = null;\n this.renderer.dispose();\n this.canvas.remove();\n\n if (this.stats) {\n this.stats.dom.remove();\n this.stats = null;\n }\n\n if (this.gui) {\n this.gui.destroy();\n this.gui = null;\n }\n }\n\n private animate = () => {\n if (this.destroyed) return;\n this.rafId = requestAnimationFrame(this.animate);\n this.stats?.begin();\n\n // OrbitControlsの更新\n if (this.controls) {\n this.controls.update();\n }\n\n const planes = this.domPlanes;\n const objects = this.dom3DObjects;\n\n // === scroll snapshot(rAF tick 上で 1 回だけ確定)===\n // 1 frame で読んだ scrollX/Y を以下すべてに同値で配る:\n // (a) ScrollSync の container transform\n // (b) plane / object の scene 位置計算(_tickApply)\n // (c) effect update 内の local-UV 算出(plane.updateEffects → window 読みを禁ずる)\n // Phase A 内で effect.update / plane.updateEffects が global mouse から\n // plane-local UV を再構成する際にも、ここで取った scrollX/Y を使う。\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n // === Phase A: ユーザー callback + マウス hover 確定 + エフェクト update ===\n // raycaster + setHoverInfo を rAF tick 内で呼ぶことで uMouseUV / uIsHovered の\n // 更新タイミングが paint と揃う (mousemove 非同期発火に引きずられない)。\n this._updateHoverFromMouse();\n\n // DOM には触れないユーザー処理を先に消化する。\n // 登録された更新処理(ホットパスのため for ループで回す)\n const callbacks = this.updateCallbacks;\n for (let i = 0, n = callbacks.length; i < n; i++) {\n callbacks[i]();\n }\n\n const elapsed = this.clock.getElapsedTime();\n const effects = this.effects;\n for (let i = 0, n = effects.length; i < n; i++) {\n const effect = effects[i];\n if (!effect.enabled) continue;\n effect.update(elapsed, this.mouse);\n }\n\n // === Phase B: ScrollSync + DOM read → per-plane effect → uniform/transform write ===\n // 3 ステップで進む:\n // (1) _tickRead: `getBoundingClientRect()` 等の **DOM read のみ**\n // (2) updateEffects: **uniform 書き込み** (uMouseUV, effect.update 経由)\n // (3) _tickApply: **mesh.position / scale の書き込み** (THREE.js 側のみ、DOM には触れない)\n //\n // (1)→(2)→(3) の順を守る理由:\n // - (2) は `positionCalculator.rect` を参照するので、(1) で更新済みでないと\n // 「1 フレ古い rect」を使ってしまう(updateRectEveryFrame: true の plane のみ実害)。\n // - (3) は DOM read を発生させないので、(1) と一塊にせず後ろに置いてよい。\n // - DOM read → write の境界は (1) と (2) の間。両者を密接させることで bcr スナップショットの\n // 鮮度を最大化しつつ、複数 plane 間で強制リフローを起こさない。\n this.scrollSync?.update(scrollX, scrollY);\n for (let i = 0, n = planes.length; i < n; i++) {\n planes[i]._tickRead();\n }\n for (let i = 0, n = objects.length; i < n; i++) {\n objects[i]._tickRead();\n }\n for (let i = 0, n = planes.length; i < n; i++) {\n planes[i].updateEffects(elapsed, this.mouse, scrollX, scrollY);\n }\n for (let i = 0, n = planes.length; i < n; i++) {\n planes[i]._tickApply(elapsed, scrollX, scrollY);\n }\n for (let i = 0, n = objects.length; i < n; i++) {\n objects[i]._tickApply(scrollX, scrollY);\n }\n\n // === Phase C: PlaneComposer の per-plane FBO レンダリング ===\n for (let i = 0, n = planes.length; i < n; i++) {\n planes[i]._tickRenderComposer();\n }\n\n // === Phase D: 本体レンダリング ===\n // user callback 内で destroy() が呼ばれている可能性があるので、\n // GL context を触る render はフラグを見てから。renderer.dispose() 後に\n // render を呼ぶと WebGL エラーになる。\n if (this.destroyed) {\n this.stats?.end();\n return;\n }\n if (this.postEffect) {\n // ポストエフェクトあり: シーン→FBO→エフェクト適用→キャンバス\n this.postEffect.render(this.scene, this.camera.instance);\n } else {\n // 通常レンダリング\n this.renderer.render(this.scene, this.camera.instance);\n }\n\n // フレームの最後にマウス座標を同期\n this.prevMouse.copy(this.mouse);\n\n this.stats?.end();\n };\n}\n","/**\n * RafScroll — rAF 同期 virtual scroll の最小実装。\n *\n * wheel / touch を `preventDefault` で受け取り、内部 accumulator に加算した値を\n * 毎 rAF tick で `window.scrollTo()` に流す。\n *\n * これにより:\n * - `window.scrollY` が **rAF tick 上でのみ更新**される\n * - JS rAF 時の `window.scrollY` と paint 時の actual scroll が同一フレームで揃う\n * - ScrollSync の cancel (container.transform と sceneY 計算で同じ scrollY を\n * 共有する不変条件) が paint 時まで厳密に成立する\n *\n * Lenis から「rAF 同期に必要な最小機能」だけ抜き出した実装。wheel/drag 中の smoothing\n * は無し (`lerp: 1` 相当)、accessibility (キーボードスクロール) や programmatic scroll\n * (`window.scrollTo` 外部呼び出し) との同期は対応しない。\n *\n * **touch 慣性 (momentum)**: ネイティブ touch scroll を `preventDefault` で抑止する以上、\n * OS が提供する慣性も失われる。代わりに自前で実装する:\n * - `touchmove` で指の速度 (px/ms) を EMA 平滑化しつつ追跡\n * - `touchend` 時点の velocity を初速として rAF tick 上で指数減衰させて scrollY に積む\n * - 端到達 (clamp) / 次の touchstart / wheel 入力で慣性は即キャンセル\n *\n * @see https://github.com/darkroomengineering/lenis (元設計)\n */\n\nexport interface RafScrollOptions {\n /**\n * `WheelEvent.deltaMode === DOM_DELTA_LINE` のとき 1 行あたりの pixel 数。\n * Firefox の wheel が deltaMode=1 を返すケースで使われる。\n * @default 16\n */\n lineHeight?: number;\n /**\n * touch リリース後の慣性減衰率。16.67ms 換算 1 フレームあたり velocity に乗算される値。\n * 1 に近いほど慣性が長く続き、0 に近いほどすぐ止まる。0 で慣性無効 (即時停止)。\n * @default 0.95\n */\n touchFriction?: number;\n}\n\n/** 慣性を停止する velocity の閾値 (px/ms)。これを下回ったら 0 に丸める。 */\nconst MIN_VELOCITY = 0.01;\n/** touchend 時、最終 touchmove からこれ以上経過していたら velocity を捨てる (ms)。 */\nconst VELOCITY_CUTOFF_MS = 50;\n/** EMA の新サンプル重み。大きいほど直近の velocity を強く反映、小さいほどノイズ耐性。 */\nconst VELOCITY_EMA_ALPHA = 0.3;\n/** dt 正規化の基準フレーム時間 (60fps = 16.67ms)。 */\nconst FRAME_MS = 1000 / 60;\n\nexport class RafScroll {\n private _scrollY: number;\n /**\n * 直近 `window.scrollTo` に渡した値。scrollY が変わっていないフレで\n * scrollTo を no-op で呼ぶと scroll event が連発して page 側 listener を\n * 無駄に叩くので、差分があるときだけ呼び出すための diff キャッシュ。\n */\n private _lastAppliedY: number;\n private _maxScroll: number;\n private _rafId: number = 0;\n private _enabled: boolean = true;\n private _lineHeight: number;\n private _friction: number;\n private _touchPrevY: number = 0;\n private _touchPrevTime: number = 0;\n /** 直近の touch velocity (px/ms)。touchend 後はこの値を初速に慣性が走る。 */\n private _velocityY: number = 0;\n private _isTouching: boolean = false;\n /** tick() の dt 計算用。0 のとき初回 tick (dt は FRAME_MS で初期化)。 */\n private _lastTickTime: number = 0;\n private _eventAbort: AbortController = new AbortController();\n private _resizeObserver: ResizeObserver | null = null;\n private _destroyed: boolean = false;\n\n constructor(options: RafScrollOptions = {}) {\n this._lineHeight = options.lineHeight ?? 16;\n this._friction = options.touchFriction ?? 0.95;\n this._scrollY = window.scrollY;\n this._lastAppliedY = this._scrollY;\n this._maxScroll = this.calcMaxScroll();\n\n this.setupEventListeners();\n this.setupResizeObserver();\n this._rafId = requestAnimationFrame(this.tick);\n }\n\n private calcMaxScroll(): number {\n return Math.max(\n 0,\n document.documentElement.scrollHeight - window.innerHeight,\n );\n }\n\n private setupEventListeners(): void {\n const signal = this._eventAbort.signal;\n\n // wheel: passive=false で preventDefault して native scroll を抑止\n window.addEventListener('wheel', this.onWheel, {\n passive: false,\n signal,\n });\n\n // touch (passive=false で preventDefault → native scroll/慣性を抑止し自前で再現)\n window.addEventListener('touchstart', this.onTouchStart, {\n passive: false,\n signal,\n });\n window.addEventListener('touchmove', this.onTouchMove, {\n passive: false,\n signal,\n });\n window.addEventListener('touchend', this.onTouchEnd, {\n passive: true,\n signal,\n });\n window.addEventListener('touchcancel', this.onTouchEnd, {\n passive: true,\n signal,\n });\n\n // viewport / document サイズ変化に追従\n window.addEventListener('resize', this.onResize, { signal });\n }\n\n /**\n * document の scrollHeight が動的に変わる場合 (画像 lazy load、SPA でのコンテンツ追加 等)\n * に maxScroll を追従させる。ResizeObserver で `<html>` を観察する。\n */\n private setupResizeObserver(): void {\n if (typeof ResizeObserver === 'undefined') return;\n this._resizeObserver = new ResizeObserver(() => {\n this._maxScroll = this.calcMaxScroll();\n this._scrollY = this.clamp(this._scrollY);\n });\n this._resizeObserver.observe(document.documentElement);\n }\n\n private onWheel = (e: WheelEvent): void => {\n if (!this._enabled) return;\n e.preventDefault();\n // wheel 入力が来たら触っていた慣性は捨てる (Lenis 等と同じ挙動)\n this._velocityY = 0;\n let delta = e.deltaY;\n // deltaMode: 0=PIXEL, 1=LINE, 2=PAGE\n if (e.deltaMode === 1) delta *= this._lineHeight;\n else if (e.deltaMode === 2) delta *= window.innerHeight;\n this._scrollY = this.clamp(this._scrollY + delta);\n };\n\n private onTouchStart = (e: TouchEvent): void => {\n if (!this._enabled) return;\n if (e.touches.length === 0) return;\n // 既存の慣性を即キャンセル (指を置いた瞬間に止まる挙動)\n this._velocityY = 0;\n this._isTouching = true;\n this._touchPrevY = e.touches[0].clientY;\n this._touchPrevTime = performance.now();\n };\n\n private onTouchMove = (e: TouchEvent): void => {\n if (!this._enabled) return;\n if (e.touches.length === 0) return;\n e.preventDefault();\n\n const y = e.touches[0].clientY;\n const now = performance.now();\n const dy = this._touchPrevY - y;\n const dt = now - this._touchPrevTime;\n\n this._scrollY = this.clamp(this._scrollY + dy);\n\n // 瞬間 velocity を EMA で平滑化 (タッチ入力は jitter が大きいので生 dy/dt は使わない)\n if (dt > 0) {\n const instantV = dy / dt;\n this._velocityY =\n this._velocityY * (1 - VELOCITY_EMA_ALPHA) +\n instantV * VELOCITY_EMA_ALPHA;\n }\n\n this._touchPrevY = y;\n this._touchPrevTime = now;\n };\n\n private onTouchEnd = (): void => {\n this._isTouching = false;\n // 指を持ち上げる直前に止めていた場合 (最終 touchmove から間が空いている) は\n // 慣性を発火しない。古い velocity が残って意図しない flick になるのを防ぐ。\n if (performance.now() - this._touchPrevTime > VELOCITY_CUTOFF_MS) {\n this._velocityY = 0;\n }\n };\n\n private onResize = (): void => {\n this._maxScroll = this.calcMaxScroll();\n this._scrollY = this.clamp(this._scrollY);\n };\n\n private clamp(y: number): number {\n return Math.min(this._maxScroll, Math.max(0, y));\n }\n\n /**\n * 毎 rAF で `window.scrollTo` を呼ぶ。これが Lenis 方式の核心:\n * scrollY の更新タイミングを「JS rAF tick 上だけ」に集約することで、\n * paint 時に見える scrollY と JS が知る scrollY が完全一致する。\n *\n * 同値時は no-op で呼ばない (scroll event の連発で page 側 listener を\n * 叩くのを避ける)。\n *\n * touch リリース後の慣性: `_velocityY` が閾値以上ある間、毎 tick で\n * `scrollY += velocity * dt` を積みつつ `velocity *= friction^(dt/FRAME_MS)` で減衰。\n */\n private tick = (now: number = performance.now()): void => {\n if (this._destroyed) return;\n\n // dt 正規化 (frame rate 非依存)。初回 tick は基準フレーム時間で扱う。\n const dt = this._lastTickTime === 0 ? FRAME_MS : now - this._lastTickTime;\n this._lastTickTime = now;\n\n // 慣性: touch 中でなく velocity が残っていれば積分\n if (!this._isTouching && Math.abs(this._velocityY) > MIN_VELOCITY) {\n const before = this._scrollY;\n const next = this.clamp(this._scrollY + this._velocityY * dt);\n this._scrollY = next;\n // clamp に当たって動かなかった (端到達) なら velocity を捨てる\n if (next === before) {\n this._velocityY = 0;\n } else {\n this._velocityY *= Math.pow(this._friction, dt / FRAME_MS);\n if (Math.abs(this._velocityY) < MIN_VELOCITY) this._velocityY = 0;\n }\n }\n\n if (this._scrollY !== this._lastAppliedY) {\n window.scrollTo(0, this._scrollY);\n this._lastAppliedY = this._scrollY;\n }\n this._rafId = requestAnimationFrame(this.tick);\n };\n\n /** 現在の virtual scrollY。 */\n get scrollY(): number {\n return this._scrollY;\n }\n\n /**\n * 入力受付の有効/無効。\n *\n * disable 中は wheel/touch ハンドラが **preventDefault する前に** early-return するため、\n * native スクロールが復活する(virtual scroll を一時停止して通常スクロールに戻したい\n * ケース用)。accumulator (`_scrollY`) も更新されず、慣性も止まる。\n *\n * 再 enable 時は `_scrollY` / `_lastAppliedY` を現在の `window.scrollY` に再同期して\n * disable 中の native scroll とのズレを吸収する(同期しないと次の wheel/touch 入力で\n * 古い `_scrollY` からの delta になり巨大ジャンプする)。\n */\n set enabled(value: boolean) {\n const wasDisabled = !this._enabled;\n this._enabled = value;\n if (!value) {\n // disable に切り替わったタイミングで慣性も停止\n this._velocityY = 0;\n this._isTouching = false;\n }\n if (value && wasDisabled) {\n this._scrollY = window.scrollY;\n this._lastAppliedY = this._scrollY;\n }\n }\n\n get enabled(): boolean {\n return this._enabled;\n }\n\n destroy(): void {\n if (this._destroyed) return;\n this._destroyed = true;\n cancelAnimationFrame(this._rafId);\n this._eventAbort.abort();\n this._resizeObserver?.disconnect();\n this._resizeObserver = null;\n }\n}\n","import * as THREE from \"three\";\nimport { Camera } from \"../Camera\";\n\nexport abstract class BaseScene {\n scene: THREE.Scene;\n camera: Camera;\n\n rect: DOMRect;\n\n constructor(rect: DOMRect) {\n this.scene = new THREE.Scene();\n this.camera = new Camera(rect);\n this.rect = rect;\n }\n\n abstract init(): void;\n abstract update(time: number, progress: number): void;\n\n resize(rect: DOMRect): void {\n this.rect = rect;\n this.camera.resize(rect);\n }\n\n dispose(): void {}\n}\n","import type { IUniform, Vector2, WebGLRenderer } from \"three\";\nimport type GUI from \"lil-gui\";\nimport type { EffectTarget, EffectPass } from \"../EffectComposer\";\n\nexport interface BaseEffectConfig {\n fragmentShader: string;\n uniforms?: { [key: string]: IUniform };\n}\n\nexport abstract class BaseEffect {\n protected pass: EffectPass | null = null;\n\n /**\n * GUI の on/off チェックボックスから操作される。false にすると\n * EffectComposer がこのパスをスキップする(lil-gui バインド用に public)。\n */\n private _enabled = true;\n get enabled(): boolean {\n return this._enabled;\n }\n set enabled(value: boolean) {\n this._enabled = value;\n if (this.pass) this.pass.enabled = value;\n }\n\n protected abstract getConfig(): BaseEffectConfig;\n\n /**\n * addEffect() 呼び出し時に内部で呼ばれる。直接使わない。\n *\n * 1 インスタンスを `webgl.addEffect()` と `domPlane.addEffect()` の両方に\n * 渡すと、`update()` が同一フレームに 2 回走り内部状態が二重進行する\n * (例: `FluidEffect` の dye が倍速で進む)。DEV では警告を出す。\n */\n _register(target: EffectTarget): void {\n if (this.pass !== null && import.meta.env?.DEV) {\n console.warn(\n \"[BaseEffect] 同じ effect インスタンスを複数の target に register しています。\" +\n \"`webgl.addEffect()` と `domPlane.addEffect()` を併用する場合は別インスタンスを作ってください。\"\n );\n }\n const config = this.getConfig();\n this.pass = target.addEffect({\n fragmentShader: config.fragmentShader,\n uniforms: config.uniforms,\n });\n // _register 前に enabled を弄られていた場合に備えて pass に反映\n this.pass.enabled = this._enabled;\n }\n\n /**\n * Core/DomPlane の addEffect から `_register` の直前に呼ばれる。\n * renderer を必要とするエフェクト(FluidEffect 等)はこれを override して受け取る。\n * @internal\n */\n _setRenderer?(_renderer: WebGLRenderer): void;\n\n update(_time: number, _mouse?: Vector2): void {}\n\n resize?(_width: number, _height: number): void;\n\n /**\n * lil-gui の親 GUI / Folder を受け取り、自分用のフォルダや control を生やす任意フック。\n * `WebGLApp.addEffect()` / `DomPlane.addEffect()` 経由でエフェクトを登録すると、\n * `showGUI` が無効でない限り自動で呼ばれる。\n *\n * 典型例:\n * ```ts\n * setupGUI(gui: GUI) {\n * const folder = gui.addFolder('MyEffect');\n * folder.add(this, 'intensity', 0, 1);\n * }\n * ```\n *\n * @param gui lil-gui の親 GUI(通常は WebGLApp の root GUI)\n * @returns 作った folder(任意)。WebGLApp 側で dispose する時の参照に使える。\n */\n setupGUI?(gui: GUI): GUI | void;\n\n /** リソース解放。サブクラスで RT などを持つ場合に override する。 */\n dispose?(): void;\n\n setUniform(key: string, value: unknown): void {\n this.pass?.setUniform(key, value);\n }\n\n getUniform(key: string): IUniform | undefined {\n return this.pass?.getUniform(key);\n }\n\n getPass(): EffectPass | null {\n return this.pass;\n }\n}\n"],"names":["CAMERA_FOV","CAMERA_NEAR","CAMERA_FAR","DEFAULT_SCALE","DEFAULT_OFFSET","AMBIENT_LIGHT_COLOR","AMBIENT_LIGHT_INTENSITY","DIRECTIONAL_LIGHT_COLOR","DIRECTIONAL_LIGHT_INTENSITY","DIRECTIONAL_LIGHT_POSITION","Camera","rect","__publicField","fovRad","safeWidth","safeHeight","distance","THREE","Light","scene","DomPositionCalculator","element","canvasRect","scrollY","scrollX","position","canvasCenterX","canvasCenterY","defaultVertexShader","EffectPass","material","key","value","EffectComposer","renderer","width","height","dpr","w","rtOptions","options","pass","idx","camera","activeCount","lastActiveIndex","passes","i","n","readTarget","writeTarget","isLast","tmp","h","PlaneComposer","sourceMesh","mainScene","p","read","write","sharedTextureLoader","defaultFragmentShader","DomPlane","el","sharedClock","hasEnteredView","repeat","entries","entry","segments","uniforms","elapsedTime","texturePath","loader","texture","error","x","y","effect","takeOwnership","time","globalMouse","pc","cr","rectW","rectH","viewportLeft","viewportTop","planeLeft","planeTop","planeW","planeH","planeBottomYup","lx","ly","uniformUV","effects","isHovered","uv","composer","gui","err","provider","Dom3DObject","GLTFLoader","modelPath","gltf","box","size","center","maxSide","inner","wrapper","userScale","finalScale","offset","z","pos","child","mesh","materials","m","disposeMaterialTextures","textureKeys","ScrollSync","container","s","initialRect","wrapperWidth","wrapperHeight","requestedPaddingPx","canvasHeight","maxPaddingPx","effective","newHeight","scrollDelta","now","dt","targetStrength","cb","o","WebGLApp","selector","plane","planes","objects","callbacks","elapsed","syncOptions","StatsCtor","object","domPlane","index","meshIndex","dom3DObject","callback","OrbitControls","GuiCtor","postEffect","signal","enabled","e","obj","resizeCallbacks","event","isInside","intersects","intersect","hitMesh","MIN_VELOCITY","VELOCITY_CUTOFF_MS","VELOCITY_EMA_ALPHA","FRAME_MS","RafScroll","delta","dy","instantV","before","next","wasDisabled","BaseScene","BaseEffect","target","config","_time","_mouse"],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export { WebGLApp } from './Core';
2
+ export { Camera } from './Camera';
3
+ export { Light } from './Light';
4
+ export { DomPlane } from './DomPlane';
5
+ export { Dom3DObject } from './Dom3DObject';
6
+ export { ScrollSync } from './ScrollSync';
7
+ export type { ScrollSyncOptions } from './ScrollSync';
8
+ export { RafScroll } from './RafScroll';
9
+ export type { RafScrollOptions } from './RafScroll';
10
+ export { DomPositionCalculator } from './DomPositionCalculator';
11
+ export type { WebGLAppOptions, CreatePlaneOptions, Create3DObjectOptions, Dom3DObjectFitMode, Offset3D, DOMPositionInfo, } from './types';
12
+ export * from './constants';
13
+ export { BaseScene } from './scenes/BaseScene';
14
+ export { EffectComposer, EffectPass } from './EffectComposer';
15
+ export type { EffectOptions, EffectTarget, EffectLike } from './EffectComposer';
16
+ export { PlaneComposer } from './PlaneComposer';
17
+ export { BaseEffect } from './effects/BaseEffect';
18
+ export type { BaseEffectConfig } from './effects/BaseEffect';
19
+ import * as THREE from "three";
20
+ export { THREE };