1 /** @file rendersystem.cpp Renderer subsystem.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2006-2015 Daniel Swanson <danij@dengine.net>
5 *
6 * @par License
7 * GPL: http://www.gnu.org/licenses/gpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15 * Public License for more details. You should have received a copy of the GNU
16 * General Public License along with this program; if not, see:
17 * http://www.gnu.org/licenses</small>
18 */
19
20 #include "de_platform.h"
21 #include "render/rendersystem.h"
22
23 #include <de/memory.h>
24 #include <de/memoryzone.h>
25 #include <de/Function>
26 #include <de/GLUniform>
27 #include <de/PackageLoader>
28 #include <de/ScriptSystem>
29 #include <de/ScriptedInfo>
30 #include <de/TextValue>
31 #include "clientapp.h"
32 #include "render/environ.h"
33 #include "render/rend_main.h"
34 #include "render/rend_halo.h"
35 #include "render/angleclipper.h"
36 #include "render/modelrenderer.h"
37 #include "render/skydrawable.h"
38 #include "render/store.h"
39 #include "world/clientserverworld.h"
40
41 #include "gl/gl_main.h"
42 #include "gl/gl_texmanager.h"
43
44 #include "ConvexSubspace"
45 #include "Subsector"
46 #include "Surface"
47
48 #include "Contact"
49 #include "misc/r_util.h"
50
51 using namespace de;
52
DENG2_PIMPL(RenderSystem)53 DENG2_PIMPL(RenderSystem)
54 , DENG2_OBSERVES(PackageLoader, Load)
55 , DENG2_OBSERVES(PackageLoader, Unload)
56 {
57 Binder binder;
58 Record renderModule;
59
60 render::Environment environment;
61 ModelRenderer models;
62 SkyDrawable sky;
63 ConfigProfiles settings;
64 ConfigProfiles appearanceSettings;
65 ImageBank images;
66
67 AngleClipper clipper;
68
69 Store buffer;
70 DrawLists drawLists;
71
72 GLUniform uMapTime { "uMapTime", GLUniform::Float };
73 GLUniform uViewMatrix { "uViewMatrix", GLUniform::Mat4 };
74 GLUniform uProjectionMatrix { "uProjectionMatrix", GLUniform::Mat4 };
75
76 // Texture => world surface projection lists.
77 struct ProjectionLists
78 {
79 duint listCount = 0;
80 duint cursorList = 0;
81 ProjectionList *lists = nullptr;
82
83 void init()
84 {
85 ProjectionList::init();
86
87 // All memory for the lists is allocated from Zone so we can "forget" it.
88 lists = nullptr;
89 listCount = 0;
90 cursorList = 0;
91 }
92
93 void reset()
94 {
95 ProjectionList::rewind(); // start reusing list nodes.
96
97 // Clear the lists.
98 cursorList = 0;
99 if(listCount)
100 {
101 std::memset(lists, 0, listCount * sizeof *lists);
102 }
103 }
104
105 ProjectionList *tryFindList(duint listIdx) const
106 {
107 if(listIdx > 0 && listIdx <= listCount)
108 {
109 return &lists[listIdx - 1];
110 }
111 return nullptr; // not found.
112 }
113
114 ProjectionList &findList(duint listIdx) const
115 {
116 if(ProjectionList *found = tryFindList(listIdx)) return *found;
117 /// @throw MissingListError Invalid index specified.
118 throw Error("RenderSystem::projector::findList", "Invalid index #" + String::number(listIdx));
119 }
120
121 ProjectionList &findOrCreateList(duint *listIdx, bool sortByLuma)
122 {
123 DENG2_ASSERT(listIdx);
124
125 // Do we need to allocate a list?
126 if(!(*listIdx))
127 {
128 // Do we need to allocate more lists?
129 if(++cursorList >= listCount)
130 {
131 listCount *= 2;
132 if(!listCount) listCount = 2;
133
134 lists = (ProjectionList *) Z_Realloc(lists, listCount * sizeof(*lists), PU_MAP);
135 }
136
137 ProjectionList *list = &lists[cursorList - 1];
138 list->head = nullptr;
139 list->sortByLuma = sortByLuma;
140
141 *listIdx = cursorList;
142 }
143
144 return lists[(*listIdx) - 1]; // 1-based index.
145 }
146 } projector;
147
148 /// VectorLight => object affection lists.
149 struct VectorLights
150 {
151 duint listCount = 0;
152 duint cursorList = 0;
153 VectorLightList *lists = nullptr;
154
155 void init()
156 {
157 VectorLightList::init();
158
159 // All memory for the lists is allocated from Zone so we can "forget" it.
160 lists = nullptr;
161 listCount = 0;
162 cursorList = 0;
163 }
164
165 void reset()
166 {
167 VectorLightList::rewind(); // start reusing list nodes.
168
169 // Clear the lists.
170 cursorList = 0;
171 if(listCount)
172 {
173 std::memset(lists, 0, listCount * sizeof *lists);
174 }
175 }
176
177 VectorLightList *tryFindList(duint listIdx) const
178 {
179 if(listIdx > 0 && listIdx <= listCount)
180 {
181 return &lists[listIdx - 1];
182 }
183 return nullptr; // not found.
184 }
185
186 VectorLightList &findList(duint listIdx) const
187 {
188 if(VectorLightList *found = tryFindList(listIdx)) return *found;
189 /// @throw MissingListError Invalid index specified.
190 throw Error("RenderSystem::vlights::findList", "Invalid index #" + String::number(listIdx));
191 }
192
193 VectorLightList &findOrCreateList(duint *listIdx)
194 {
195 DENG2_ASSERT(listIdx);
196
197 // Do we need to allocate a list?
198 if(!(*listIdx))
199 {
200 // Do we need to allocate more lists?
201 if(++cursorList >= listCount)
202 {
203 listCount *= 2;
204 if(!listCount) listCount = 2;
205
206 lists = (VectorLightList *) Z_Realloc(lists, listCount * sizeof(*lists), PU_MAP);
207 }
208
209 VectorLightList *list = &lists[cursorList - 1];
210 list->head = nullptr;
211
212 *listIdx = cursorList;
213 }
214
215 return lists[(*listIdx) - 1]; // 1-based index.
216 }
217 } vlights;
218
219 Impl(Public *i) : Base(i)
220 {
221 LOG_AS("RenderSystem");
222
223 ModelRenderer::initBindings(binder, renderModule);
224 ClientApp::scriptSystem().addNativeModule("Render", renderModule);
225
226 // Packages are checked for shaders when (un)loaded.
227 App::packageLoader().audienceForLoad() += this;
228 App::packageLoader().audienceForUnload() += this;
229
230 // Load the required packages.
231 App::packageLoader().load("net.dengine.client.renderer");
232 App::packageLoader().load("net.dengine.client.renderer.lensflares");
233
234 loadImages();
235
236 typedef ConfigProfiles SReg;
237
238 // Initialize settings.
239 settings.define(SReg::FloatCVar, "rend-camera-fov", 95.f)
240 .define(SReg::ConfigVariable, "render.pixelDensity")
241 .define(SReg::IntCVar, "rend-model-mirror-hud", 0)
242 .define(SReg::IntCVar, "rend-model-precache", 1)
243 .define(SReg::IntCVar, "rend-sprite-precache", 1)
244 .define(SReg::IntCVar, "rend-tex", 1)
245 .define(SReg::IntCVar, "rend-dev-wireframe", 0)
246 .define(SReg::IntCVar, "rend-dev-thinker-ids", 0)
247 .define(SReg::IntCVar, "rend-dev-mobj-bbox", 0)
248 .define(SReg::IntCVar, "rend-dev-polyobj-bbox", 0)
249 .define(SReg::IntCVar, "rend-dev-sector-show-indices", 0)
250 .define(SReg::IntCVar, "rend-dev-vertex-show-indices", 0)
251 .define(SReg::IntCVar, "rend-dev-generator-show-indices", 0);
252
253 appearanceSettings.setPersistentName("renderer");
254 appearanceSettings
255 .define(SReg::IntCVar, "rend-light", 1)
256 .define(SReg::IntCVar, "rend-light-decor", 1)
257 .define(SReg::IntCVar, "rend-light-blend", 0)
258 .define(SReg::IntCVar, "rend-light-num", 0)
259 .define(SReg::FloatCVar, "rend-light-bright", .7f)
260 .define(SReg::FloatCVar, "rend-light-fog-bright", .15f)
261 .define(SReg::FloatCVar, "rend-light-radius-scale", 5.2f)
262 .define(SReg::IntCVar, "rend-light-radius-max", 320)
263 .define(SReg::IntCVar, "rend-light-ambient", 0)
264 .define(SReg::FloatCVar, "rend-light-compression", 0)
265 .define(SReg::IntCVar, "rend-light-attenuation", 924)
266 .define(SReg::IntCVar, "rend-light-sky-auto", 1)
267 .define(SReg::FloatCVar, "rend-light-sky", .273f)
268 .define(SReg::IntCVar, "rend-light-wall-angle-smooth", 1)
269 .define(SReg::FloatCVar, "rend-light-wall-angle", 1.2f)
270
271 .define(SReg::IntCVar, "rend-vignette", 1)
272 .define(SReg::FloatCVar, "rend-vignette-darkness", 1)
273 .define(SReg::FloatCVar, "rend-vignette-width", 1)
274
275 .define(SReg::IntCVar, "rend-halo-realistic", 1)
276 .define(SReg::IntCVar, "rend-halo", 5)
277 .define(SReg::IntCVar, "rend-halo-bright", 45)
278 .define(SReg::IntCVar, "rend-halo-size", 80)
279 .define(SReg::IntCVar, "rend-halo-occlusion", 48)
280 .define(SReg::FloatCVar, "rend-halo-radius-min", 20)
281 .define(SReg::FloatCVar, "rend-halo-secondary-limit", 1)
282 .define(SReg::FloatCVar, "rend-halo-dim-near", 10)
283 .define(SReg::FloatCVar, "rend-halo-dim-far", 100)
284 .define(SReg::FloatCVar, "rend-halo-zmag-div", 62)
285
286 .define(SReg::FloatCVar, "rend-glow", .8f)
287 .define(SReg::IntCVar, "rend-glow-height", 100)
288 .define(SReg::FloatCVar, "rend-glow-scale", 3)
289 .define(SReg::IntCVar, "rend-glow-wall", 1)
290
291 .define(SReg::ConfigVariable, "render.fx.resize.factor")
292
293 .define(SReg::IntCVar, "rend-bloom", 1)
294 .define(SReg::FloatCVar, "rend-bloom-intensity", .65f)
295 .define(SReg::FloatCVar, "rend-bloom-threshold", .35f)
296 .define(SReg::FloatCVar, "rend-bloom-dispersion", 1)
297
298 .define(SReg::IntCVar, "rend-fakeradio", 1)
299 .define(SReg::FloatCVar, "rend-fakeradio-darkness", 1.2f)
300 .define(SReg::IntCVar, "rend-shadow", 1)
301 .define(SReg::FloatCVar, "rend-shadow-darkness", 1.2f)
302 .define(SReg::IntCVar, "rend-shadow-far", 1000)
303 .define(SReg::IntCVar, "rend-shadow-radius-max", 80)
304
305 .define(SReg::IntCVar, "rend-tex-shiny", 1)
306 .define(SReg::IntCVar, "rend-tex-mipmap", 5)
307 .define(SReg::IntCVar, "rend-tex-quality", TEXQ_BEST)
308 .define(SReg::IntCVar, "rend-tex-anim-smooth", 1)
309 .define(SReg::IntCVar, "rend-tex-filter-smart", 0)
310 .define(SReg::IntCVar, "rend-tex-filter-sprite", 1)
311 .define(SReg::IntCVar, "rend-tex-filter-mag", 1)
312 .define(SReg::IntCVar, "rend-tex-filter-ui", 1)
313 .define(SReg::IntCVar, "rend-tex-filter-anisotropic", -1)
314 .define(SReg::IntCVar, "rend-tex-detail", 1)
315 .define(SReg::FloatCVar, "rend-tex-detail-scale", 4)
316 .define(SReg::FloatCVar, "rend-tex-detail-strength", .5f)
317
318 .define(SReg::IntCVar, "rend-mobj-smooth-move", 2)
319 .define(SReg::IntCVar, "rend-mobj-smooth-turn", 1)
320
321 .define(SReg::IntCVar, "rend-model", 1)
322 .define(SReg::IntCVar, "rend-model-inter", 1)
323 .define(SReg::IntCVar, "rend-model-distance", 1500)
324 .define(SReg::FloatCVar, "rend-model-lod", 256)
325 .define(SReg::FloatCVar, "rend-model-lights", 4)
326
327 .define(SReg::IntCVar, "rend-sprite-mode", 0)
328 .define(SReg::IntCVar, "rend-sprite-blend", 1)
329 .define(SReg::IntCVar, "rend-sprite-lights", 4)
330 .define(SReg::IntCVar, "rend-sprite-align", 0)
331 .define(SReg::IntCVar, "rend-sprite-noz", 0)
332
333 .define(SReg::IntCVar, "rend-particle", 1)
334 .define(SReg::IntCVar, "rend-particle-max", 0)
335 .define(SReg::FloatCVar, "rend-particle-rate", 1)
336 .define(SReg::FloatCVar, "rend-particle-diffuse", 4)
337 .define(SReg::IntCVar, "rend-particle-visible-near", 0)
338
339 .define(SReg::FloatCVar, "rend-sky-distance", 1600);
340 }
341
342 //~Impl()
343 //{
344 //App::packageLoader().audienceForLoad() -= this;
345 //App::packageLoader().audienceForUnload() -= this;
346 //}
347
348 void packageLoaded(String const &packageId)
349 {
350 FS::FoundFiles found;
351 App::packageLoader().package(packageId).findPartialPath("shaders.dei", found);
352 DENG2_FOR_EACH(FS::FoundFiles, i, found)
353 {
354 // Load new shaders.
355 loadShaders(**i);
356 }
357 }
358
359 void aboutToUnloadPackage(String const &packageId)
360 {
361 ClientApp::shaders().removeAllFromPackage(packageId);
362 }
363
364 /*
365 * Reads all shader definitions and sets up a Bank where the actual
366 * compiled shaders are stored once they're needed.
367 *
368 * @todo This should be reworked to support unloading packages, and
369 * loading of new shaders from any newly loaded packages. -jk
370 */
371 // void loadAllShaders()
372 // {
373 // // Load all the shader program definitions.
374 // FS::FoundFiles found;
375 // App::findInPackages("shaders.dei", found);
376 // DENG2_FOR_EACH(FS::FoundFiles, i, found)
377 // {
378 // loadShaders(**i);
379 // }
380 // }
381
382 void loadShaders(File const &defs)
383 {
384 LOG_MSG("Loading shader definitions from %s") << defs.description();
385 ClientApp::shaders().addFromInfo(defs);
386 }
387
388 /**
389 * Reads the renderer's image definitions and sets up a Bank for caching them
390 * when they're needed.
391 */
392 void loadImages()
393 {
394 //Folder const &renderPack = App::fileSystem().find<Folder>("renderer.pack");
395 //images.addFromInfo(renderPack.locate<File>("images.dei"));
396 }
397 };
398
RenderSystem()399 RenderSystem::RenderSystem() : d(new Impl(this))
400 {}
401
glInit()402 void RenderSystem::glInit()
403 {
404 // Shader defines.
405 {
406 DictionaryValue defines;
407 defines.add(new TextValue("DGL_BATCH_MAX"),
408 new NumberValue(DGL_BatchMaxSize()));
409 shaders().setPreprocessorDefines(defines);
410 }
411
412 d->models.glInit();
413 }
414
glDeinit()415 void RenderSystem::glDeinit()
416 {
417 d->models.glDeinit();
418 d->environment.glDeinit();
419 }
420
shaders()421 GLShaderBank &RenderSystem::shaders()
422 {
423 return BaseGuiApp::shaders();
424 }
425
images()426 ImageBank &RenderSystem::images()
427 {
428 return d->images;
429 }
430
uMapTime() const431 GLUniform const &RenderSystem::uMapTime() const
432 {
433 return d->uMapTime;
434 }
435
uProjectionMatrix() const436 GLUniform &RenderSystem::uProjectionMatrix() const
437 {
438 return d->uProjectionMatrix;
439 }
440
uViewMatrix() const441 GLUniform &RenderSystem::uViewMatrix() const
442 {
443 return d->uViewMatrix;
444 }
445
environment()446 render::Environment &RenderSystem::environment()
447 {
448 return d->environment;
449 }
450
modelRenderer()451 ModelRenderer &RenderSystem::modelRenderer()
452 {
453 return d->models;
454 }
455
sky()456 SkyDrawable &RenderSystem::sky()
457 {
458 return d->sky;
459 }
460
timeChanged(Clock const &)461 void RenderSystem::timeChanged(Clock const &)
462 {
463 // Update the current map time for shaders.
464 d->uMapTime = ClientApp::world().time();
465 }
466
settings()467 ConfigProfiles &RenderSystem::settings()
468 {
469 return d->settings;
470 }
471
appearanceSettings()472 ConfigProfiles &RenderSystem::appearanceSettings()
473 {
474 return d->appearanceSettings;
475 }
476
angleClipper() const477 AngleClipper &RenderSystem::angleClipper() const
478 {
479 return d->clipper;
480 }
481
buffer()482 Store &RenderSystem::buffer()
483 {
484 return d->buffer;
485 }
486
clearDrawLists()487 void RenderSystem::clearDrawLists()
488 {
489 d->drawLists.clear();
490
491 // Clear the global vertex buffer, also.
492 d->buffer.clear();
493 }
494
drawLists()495 DrawLists &RenderSystem::drawLists()
496 {
497 return d->drawLists;
498 }
499
worldSystemMapChanged(world::Map &)500 void RenderSystem::worldSystemMapChanged(world::Map &)
501 {
502 d->projector.init();
503 d->vlights.init();
504 }
505
beginFrame()506 void RenderSystem::beginFrame()
507 {
508 // Clear the draw lists ready for new geometry.
509 d->drawLists.reset();
510 d->buffer.rewind(); // Start reallocating storage from the global vertex buffer.
511
512 // Clear the clipper - we're drawing from a new point of view.
513 d->clipper.clearRanges();
514
515 // Recycle view dependent list data.
516 d->projector.reset();
517 d->vlights.reset();
518
519 R_BeginFrame();
520 }
521
findSurfaceProjectionList(duint * listIdx,bool sortByLuma)522 ProjectionList &RenderSystem::findSurfaceProjectionList(duint *listIdx, bool sortByLuma)
523 {
524 return d->projector.findOrCreateList(listIdx, sortByLuma);
525 }
526
forAllSurfaceProjections(duint listIdx,std::function<LoopResult (ProjectedTextureData const &)> func) const527 LoopResult RenderSystem::forAllSurfaceProjections(duint listIdx, std::function<LoopResult (ProjectedTextureData const &)> func) const
528 {
529 if(ProjectionList *list = d->projector.tryFindList(listIdx))
530 {
531 for(ProjectionList::Node *node = list->head; node; node = node->next)
532 {
533 if(auto result = func(node->projection))
534 return result;
535 }
536 }
537 return LoopContinue;
538 }
539
findVectorLightList(duint * listIdx)540 VectorLightList &RenderSystem::findVectorLightList(duint *listIdx)
541 {
542 return d->vlights.findOrCreateList(listIdx);
543 }
544
forAllVectorLights(duint listIdx,std::function<LoopResult (VectorLightData const &)> func)545 LoopResult RenderSystem::forAllVectorLights(duint listIdx, std::function<LoopResult (VectorLightData const &)> func)
546 {
547 if(VectorLightList *list = d->vlights.tryFindList(listIdx))
548 {
549 for(VectorLightList::Node *node = list->head; node; node = node->next)
550 {
551 if(auto result = func(node->vlight))
552 return result;
553 }
554 }
555 return LoopContinue;
556 }
557
consoleRegister()558 void RenderSystem::consoleRegister()
559 {
560 Viewports_Register();
561 Rend_Register();
562 H_Register();
563 }
564