11// Import Third-party Dependencies
22import * as THREE from "three" ;
3+ import { EffectComposer , type Pass } from "three/addons/postprocessing/EffectComposer.js" ;
4+ import { RenderPass } from "three/addons/postprocessing/RenderPass.js" ;
35import { EventEmitter } from "@posva/event-emitter" ;
46
57// Import Internal Dependencies
@@ -8,6 +10,12 @@ import type {
810 RenderComponent
911} from "./GameRenderer.js" ;
1012import type { Scene } from "../Scene.js" ;
13+ import {
14+ type RenderMode ,
15+ type RenderStrategy ,
16+ DirectRenderStrategy ,
17+ ComposerRenderStrategy
18+ } from "./RenderStrategy.js" ;
1119
1220export type ThreeRendererEvents = {
1321 resize : [
@@ -18,28 +26,33 @@ export type ThreeRendererEvents = {
1826 ] ;
1927} ;
2028
29+ export interface ThreeRendererOptions {
30+ /**
31+ * @default "direct"
32+ */
33+ renderMode : RenderMode ;
34+ scene : Scene ;
35+ }
36+
2137export class ThreeRenderer extends EventEmitter <
2238 ThreeRendererEvents
2339> implements GameRenderer {
2440 webGLRenderer : THREE . WebGLRenderer ;
2541 renderComponents : RenderComponent [ ] = [ ] ;
42+ renderStrategy : RenderStrategy ;
2643 ratio : number | null = null ;
44+ scene : Scene ;
2745
2846 constructor (
29- canvas : HTMLCanvasElement
47+ canvas : HTMLCanvasElement ,
48+ options : ThreeRendererOptions
3049 ) {
3150 super ( ) ;
32- this . webGLRenderer = new THREE . WebGLRenderer ( {
33- canvas,
34- antialias : true ,
35- alpha : true
36- } ) ;
37-
38- this . webGLRenderer . setPixelRatio ( window . devicePixelRatio ) ;
39- this . webGLRenderer . shadowMap . enabled = true ;
40- this . webGLRenderer . shadowMap . type = THREE . BasicShadowMap ;
41- this . webGLRenderer . setSize ( 0 , 0 , false ) ;
42- this . webGLRenderer . autoClear = false ;
51+ const { scene, renderMode = "direct" } = options ;
52+
53+ this . scene = scene ;
54+ this . webGLRenderer = createWebGLRenderer ( canvas ) ;
55+ this . setRenderMode ( renderMode ) ;
4356 }
4457
4558 get canvas ( ) {
@@ -50,29 +63,77 @@ export class ThreeRenderer extends EventEmitter<
5063 return this . webGLRenderer ;
5164 }
5265
53- addRenderComponent ( component : RenderComponent ) : void {
66+ addRenderComponent (
67+ component : RenderComponent
68+ ) : void {
5469 this . renderComponents . push ( component ) ;
70+ if ( this . renderStrategy instanceof ComposerRenderStrategy ) {
71+ const renderPass = new RenderPass ( this . scene . getSource ( ) , component ) ;
72+ this . renderStrategy . addEffect ( renderPass ) ;
73+ }
5574 }
5675
57- removeRenderComponent ( component : RenderComponent ) : void {
76+ removeRenderComponent (
77+ component : RenderComponent
78+ ) : void {
5879 const index = this . renderComponents . indexOf ( component ) ;
5980 if ( index !== - 1 ) {
6081 this . renderComponents . splice ( index , 1 ) ;
6182 }
83+
84+ if ( this . renderStrategy instanceof ComposerRenderStrategy ) {
85+ const composer = this . renderStrategy . getComposer ( ) ;
86+ const renderPass = composer . passes . find (
87+ ( pass ) => ( pass as RenderPass ) . camera === component
88+ ) ! ;
89+ this . renderStrategy . removeEffect ( renderPass ) ;
90+ }
91+ }
92+
93+ setRenderMode (
94+ mode : RenderMode
95+ ) : this {
96+ if ( mode === "direct" ) {
97+ this . renderStrategy = new DirectRenderStrategy ( this . webGLRenderer ) ;
98+ }
99+ else {
100+ const composer = new EffectComposer ( this . webGLRenderer ) ;
101+
102+ const scene = this . scene . getSource ( ) ;
103+ for ( const renderComponent of this . renderComponents ) {
104+ const renderPass = new RenderPass ( scene , renderComponent ) ;
105+ composer . addPass ( renderPass ) ;
106+ }
107+
108+ this . renderStrategy = new ComposerRenderStrategy (
109+ composer
110+ ) ;
111+ }
112+ this . resize ( ) ;
113+ this . clear ( ) ;
114+
115+ return this ;
116+ }
117+
118+ setEffects ( ...effects : Pass [ ] ) : this {
119+ if ( this . renderStrategy instanceof ComposerRenderStrategy ) {
120+ for ( const pass of effects ) {
121+ this . renderStrategy . addEffect ( pass ) ;
122+ }
123+ }
124+
125+ return this ;
62126 }
63127
64128 setRatio (
65129 ratio : number | null = null
66130 ) {
67131 this . ratio = ratio ;
68- if ( this . ratio ) {
69- this . webGLRenderer . domElement . style . margin = "0" ;
70- this . webGLRenderer . domElement . style . flex = "1" ;
71- }
72- else {
73- this . webGLRenderer . domElement . style . margin = "auto" ;
74- this . webGLRenderer . domElement . style . flex = "none" ;
75- }
132+
133+ const styles = this . ratio ?
134+ { margin : "0" , flex : "1" } :
135+ { margin : "auto" , flex : "none" } ;
136+ Object . assign ( this . webGLRenderer . domElement . style , styles ) ;
76137 this . resize ( ) ;
77138
78139 return this ;
@@ -107,7 +168,7 @@ export class ThreeRenderer extends EventEmitter<
107168 this . webGLRenderer . domElement . width !== width ||
108169 this . webGLRenderer . domElement . height !== height
109170 ) {
110- this . webGLRenderer . setSize ( width , height , false ) ;
171+ this . renderStrategy . resize ( width , height ) ;
111172 for ( const renderComponent of this . renderComponents ) {
112173 if ( renderComponent instanceof THREE . PerspectiveCamera ) {
113174 renderComponent . aspect = width / height ;
@@ -118,24 +179,14 @@ export class ThreeRenderer extends EventEmitter<
118179 }
119180 } ;
120181
121- draw (
122- scene : Scene
123- ) {
182+ draw ( ) {
124183 this . resize ( ) ;
125184 this . clear ( ) ;
126- // this.renderComponents.sort((a, b) => {
127- // let order = (a.depth - b.depth);
128- // if (order === 0) {
129- // order = this.cachedActors.indexOf(a.actor) - this.cachedActors.indexOf(b.actor);
130- // }
131-
132- // return order;
133- // });
134-
135- const source = scene . getSource ( ) ;
136- for ( const renderComponent of this . renderComponents ) {
137- this . webGLRenderer . render ( source , renderComponent ) ;
138- }
185+
186+ this . renderStrategy . render (
187+ this . scene . getSource ( ) ,
188+ this . renderComponents
189+ ) ;
139190 this . emit ( "draw" , { source : this . webGLRenderer } ) ;
140191 }
141192
@@ -149,3 +200,24 @@ export class ThreeRenderer extends EventEmitter<
149200 this . webGLRenderer . clear ( ) ;
150201 }
151202}
203+
204+ function createWebGLRenderer (
205+ canvas : HTMLCanvasElement
206+ ) : THREE . WebGLRenderer {
207+ const renderer = new THREE . WebGLRenderer ( {
208+ canvas,
209+ antialias : true ,
210+ alpha : true
211+ } ) ;
212+
213+ renderer . setPixelRatio ( window . devicePixelRatio ) ;
214+ renderer . shadowMap . enabled = true ;
215+ renderer . shadowMap . type = THREE . BasicShadowMap ;
216+ renderer . setSize ( 0 , 0 , false ) ;
217+ renderer . autoClear = false ;
218+ renderer . outputColorSpace = THREE . SRGBColorSpace ;
219+ renderer . toneMapping = THREE . NeutralToneMapping ;
220+ renderer . toneMappingExposure = 1.25 ;
221+
222+ return renderer ;
223+ }
0 commit comments