UNPKG

8.95 kBJavaScriptView Raw
1import { toRad, m4_identity, ViewService, RenderManager, PM_Camera, PM_Visible } from "@croquet/worldcore-kernel";
2
3import { MainDisplay, Scene, Camera, Lights, GeometryBuffer, Framebuffer, SharedStencilFramebuffer, GetGLVersion, SetGLCamera, SetGLPipeline, StartStencilCapture, EndStencil, StartStencilApply } from "./Render";
4import { BasicShader, DecalShader, TranslucentShader, InstancedShader, GeometryShader, InstancedGeometryShader, TranslucentGeometryShader, PassthruShader, BlendShader, AOShader, InstancedDecalShader } from "./Shaders";
5
6
7//------------------------------------------------------------------------------------------
8//-- Visible Mixin -------------------------------------------------------------------------
9//------------------------------------------------------------------------------------------
10
11// Visible pawns have a mesh and a material and they handle inserting and removing themselves
12// from the scene. They should only be used with a companion mixin that has the method "global" that
13// supplies them with a 4x4 transform. Make sure the companion mixin is added first so it will
14// be updated first.
15//
16// Note that destroying a pawn will not clean up the mesh and the material because they may be
17// used by multiple pawns.
18
19export const PM_WebGLVisible = superclass => class extends PM_Visible(superclass) {
20
21 constructor(...args) {
22 super(...args);
23 this.listen("viewGlobalChanged", this.refreshDrawTransform);
24 }
25
26 destroy() {
27 super.destroy();
28 if (this.draw) this.service('WebGLRenderManager').scene.removeDrawCall(this.draw);
29 }
30
31 refreshDrawTransform() {
32 if (this.draw) this.draw.transform.set(this.global);
33 }
34
35 setDrawCall(draw) {
36 if (this.draw === draw) return;
37 const scene = this.service('WebGLRenderManager').scene;
38 if (this.draw) scene.removeDrawCall(this.draw);
39 this.draw = draw;
40 if (this.draw) {
41 this.draw.transform.set(this.global || m4_identity());
42 scene.addDrawCall(this.draw);
43 }
44
45 }
46
47};
48
49//------------------------------------------------------------------------------------------
50//-- InstancedVisible ----------------------------------------------------------------------
51//------------------------------------------------------------------------------------------
52
53// Special version of instanced that uses an instanced mesh. Note that destroy does not
54// remove the draw call from the scene, it just removes the transform for this instance.
55
56
57export const PM_WebGLInstancedVisible = superclass => class extends superclass {
58
59 constructor(...args) {
60 super(...args);
61 this.listen("viewGlobalChanged", this.refreshDrawTransform);
62 }
63
64 destroy() {
65 super.destroy();
66 if (this.draw) this.draw.instances.delete(this.actor.id);
67 }
68
69 refreshDrawTransform() {
70 if (this.draw) this.draw.instances.set(this.actor.id, this.global);
71 }
72
73 setDrawCall(draw) {
74 const scene = this.service('WebGLRenderManager').scene;
75
76 this.draw = draw;
77 if (this.draw) {
78 this.draw.instances.set(this.actor.id, this.global);
79 scene.addDrawCall(this.draw);
80 }
81 }
82
83}
84
85//------------------------------------------------------------------------------------------
86//-- Camera Mixin --------------------------------------------------------------------------
87//------------------------------------------------------------------------------------------
88
89
90
91export const PM_WebGLCamera = superclass => class extends PM_Camera(superclass) {
92
93 constructor(...args) {
94 super(...args);
95 const render = this.service("WebGLRenderManager");
96 if (this.isMyPlayerPawn && render) {
97 render.camera.setLocation(this.lookGlobal);
98 render.camera.setProjection(toRad(60), 1.0, 10000.0);
99 }
100 this.listen("lookGlobalChanged", this.refreshCameraTransform);
101 }
102
103 refreshCameraTransform() {
104 const render = this.service("WebGLRenderManager");
105 if (!this.isMyPlayerPawn || !render) return;
106 render.camera.setLocation(this.lookGlobal);
107 }
108
109};
110
111//------------------------------------------------------------------------------------------
112//-- RenderManager -------------------------------------------------------------------------
113//------------------------------------------------------------------------------------------
114
115// The top render interface that controls the execution of draw passes.
116
117export class WebGLRenderManager extends RenderManager {
118 constructor(options, name) {
119 super(options, name || "WebGLRenderManager");
120 SetGLPipeline(this);
121 this.display = new MainDisplay();
122 this.buildBuffers();
123 this.buildShaders();
124 this.setScene(new Scene());
125 this.setCamera(new Camera());
126 this.setLights(new Lights());
127 this.setBackground([0,0,0,1]);
128 }
129
130 destroy() {
131 this.display.destroy();
132 if (this.geometryBuffer) this.geometryBuffer.destroy();
133 if (this.aoBuffer) this.aoBuffer.destroy();
134 if (this.composeBuffer) this.composeBuffer.destroy();
135 }
136
137 buildBuffers() {
138 if (GetGLVersion() === 0) return;
139 this.geometryBuffer = new GeometryBuffer({autoResize: 1});
140 this.aoBuffer = new Framebuffer({autoResize: 0.5});
141 this.composeBuffer = new SharedStencilFramebuffer(this.geometryBuffer);
142 this.composeBuffer.setBackground([1,1,1,1]);
143 }
144
145 buildShaders() {
146 if (GetGLVersion() === 0) {
147 this.basicShader = new BasicShader();
148 this.decalShader = new DecalShader();
149 this.translucentShader = new TranslucentShader();
150 } else {
151 this.instancedShader = new InstancedShader();
152 this.decalShader = new DecalShader();
153 this.instancedDecalShader = new InstancedDecalShader();
154 this.translucentShader = new TranslucentShader();
155
156 this.geometryShader = new GeometryShader();
157 this.instancedGeometryShader = new InstancedGeometryShader();
158 this.translucentGeometryShader = new TranslucentGeometryShader();
159 this.passthruShader = new PassthruShader();
160 this.blendShader = new BlendShader();
161 this.aoShader = new AOShader();
162 }
163 }
164
165 setLights(lights) {
166 this.lights = lights;
167 }
168
169 setCamera(camera) {
170 SetGLCamera(camera);
171 this.camera = camera;
172 if (GetGLVersion() === 0) {
173 this.display.setCamera(camera);
174 } else {
175 this.geometryBuffer.setCamera(camera);
176 }
177 }
178
179 // Updates shaders that depend on the camera FOV.
180 updateFOV() {
181 if (this.aoShader) this.aoShader.setFOV(this.camera.fov);
182 }
183
184 setBackground(background) {
185 this.background = background;
186 this.display.setBackground(background);
187 if (GetGLVersion() > 0) this.geometryBuffer.setBackground(background);
188 }
189
190 setScene(scene) {
191 this.scene = scene;
192 }
193
194 update() {
195 this.draw();
196 }
197
198 draw() {
199 if (!this.scene) return;
200 if (GetGLVersion() === 0) {
201 this.drawForward();
202 } else {
203 this.drawDeferred();
204 }
205 }
206
207 drawDeferred() {
208
209 // Geometry Pass
210
211 this.geometryBuffer.start();
212
213 StartStencilCapture();
214
215 this.geometryShader.apply();
216 this.lights.apply();
217 this.camera.apply();
218 this.scene.drawPass('opaque');
219
220 this.instancedGeometryShader.apply();
221 this.lights.apply();
222 this.camera.apply();
223 this.scene.drawPass('instanced');
224
225 EndStencil();
226
227 this.translucentGeometryShader.apply();
228 this.camera.apply();
229 this.scene.drawPass('translucent');
230
231 // Lighting Pass
232
233 this.aoBuffer.start();
234 this.aoShader.apply();
235 this.geometryBuffer.normal.apply(0);
236 this.geometryBuffer.position.apply(1);
237 this.aoBuffer.draw();
238
239 StartStencilApply();
240
241 this.composeBuffer.start(); // The ao pass leaves the sky black. This uses the stencil to make the sky white.
242 this.passthruShader.apply();
243 this.aoBuffer.texture.apply(0);
244 this.composeBuffer.draw();
245
246 EndStencil();
247
248 this.display.start();
249 this.blendShader.apply();
250 this.geometryBuffer.diffuse.apply(0);
251 this.composeBuffer.texture.apply(1);
252 this.display.draw();
253 }
254
255 drawForward() {
256 this.display.start();
257
258 this.decalShader.apply();
259 this.lights.apply();
260 this.camera.apply();
261 this.scene.drawPass('opaque');
262
263 this.lights.apply();
264 this.camera.apply();
265 this.scene.drawPass('instanced');
266
267 this.translucentShader.apply();
268 this.camera.apply();
269 this.scene.drawPass('translucent');
270 }
271}