1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #include "../Precompiled.h"
24 
25 #include "../Core/Profiler.h"
26 #include "../Core/WorkQueue.h"
27 #include "../Graphics/Camera.h"
28 #include "../Graphics/DebugRenderer.h"
29 #include "../Graphics/Geometry.h"
30 #include "../Graphics/Graphics.h"
31 #include "../Graphics/GraphicsEvents.h"
32 #include "../Graphics/GraphicsImpl.h"
33 #include "../Graphics/Material.h"
34 #include "../Graphics/OcclusionBuffer.h"
35 #include "../Graphics/Octree.h"
36 #include "../Graphics/Renderer.h"
37 #include "../Graphics/RenderPath.h"
38 #include "../Graphics/ShaderVariation.h"
39 #include "../Graphics/Skybox.h"
40 #include "../Graphics/Technique.h"
41 #include "../Graphics/Texture2D.h"
42 #include "../Graphics/Texture2DArray.h"
43 #include "../Graphics/Texture3D.h"
44 #include "../Graphics/TextureCube.h"
45 #include "../Graphics/VertexBuffer.h"
46 #include "../Graphics/View.h"
47 #include "../IO/FileSystem.h"
48 #include "../IO/Log.h"
49 #include "../Resource/ResourceCache.h"
50 #include "../Scene/Scene.h"
51 #include "../UI/UI.h"
52 
53 #include "../DebugNew.h"
54 
55 namespace Urho3D
56 {
57 
58 static const Vector3* directions[] =
59 {
60     &Vector3::RIGHT,
61     &Vector3::LEFT,
62     &Vector3::UP,
63     &Vector3::DOWN,
64     &Vector3::FORWARD,
65     &Vector3::BACK
66 };
67 
68 /// %Frustum octree query for shadowcasters.
69 class ShadowCasterOctreeQuery : public FrustumOctreeQuery
70 {
71 public:
72     /// Construct with frustum and query parameters.
ShadowCasterOctreeQuery(PODVector<Drawable * > & result,const Frustum & frustum,unsigned char drawableFlags=DRAWABLE_ANY,unsigned viewMask=DEFAULT_VIEWMASK)73     ShadowCasterOctreeQuery(PODVector<Drawable*>& result, const Frustum& frustum, unsigned char drawableFlags = DRAWABLE_ANY,
74         unsigned viewMask = DEFAULT_VIEWMASK) :
75         FrustumOctreeQuery(result, frustum, drawableFlags, viewMask)
76     {
77     }
78 
79     /// Intersection test for drawables.
TestDrawables(Drawable ** start,Drawable ** end,bool inside)80     virtual void TestDrawables(Drawable** start, Drawable** end, bool inside)
81     {
82         while (start != end)
83         {
84             Drawable* drawable = *start++;
85 
86             if (drawable->GetCastShadows() && (drawable->GetDrawableFlags() & drawableFlags_) &&
87                 (drawable->GetViewMask() & viewMask_))
88             {
89                 if (inside || frustum_.IsInsideFast(drawable->GetWorldBoundingBox()))
90                     result_.Push(drawable);
91             }
92         }
93     }
94 };
95 
96 /// %Frustum octree query for zones and occluders.
97 class ZoneOccluderOctreeQuery : public FrustumOctreeQuery
98 {
99 public:
100     /// Construct with frustum and query parameters.
ZoneOccluderOctreeQuery(PODVector<Drawable * > & result,const Frustum & frustum,unsigned char drawableFlags=DRAWABLE_ANY,unsigned viewMask=DEFAULT_VIEWMASK)101     ZoneOccluderOctreeQuery(PODVector<Drawable*>& result, const Frustum& frustum, unsigned char drawableFlags = DRAWABLE_ANY,
102         unsigned viewMask = DEFAULT_VIEWMASK) :
103         FrustumOctreeQuery(result, frustum, drawableFlags, viewMask)
104     {
105     }
106 
107     /// Intersection test for drawables.
TestDrawables(Drawable ** start,Drawable ** end,bool inside)108     virtual void TestDrawables(Drawable** start, Drawable** end, bool inside)
109     {
110         while (start != end)
111         {
112             Drawable* drawable = *start++;
113             unsigned char flags = drawable->GetDrawableFlags();
114 
115             if ((flags == DRAWABLE_ZONE || (flags == DRAWABLE_GEOMETRY && drawable->IsOccluder())) &&
116                 (drawable->GetViewMask() & viewMask_))
117             {
118                 if (inside || frustum_.IsInsideFast(drawable->GetWorldBoundingBox()))
119                     result_.Push(drawable);
120             }
121         }
122     }
123 };
124 
125 /// %Frustum octree query with occlusion.
126 class OccludedFrustumOctreeQuery : public FrustumOctreeQuery
127 {
128 public:
129     /// Construct with frustum, occlusion buffer and query parameters.
OccludedFrustumOctreeQuery(PODVector<Drawable * > & result,const Frustum & frustum,OcclusionBuffer * buffer,unsigned char drawableFlags=DRAWABLE_ANY,unsigned viewMask=DEFAULT_VIEWMASK)130     OccludedFrustumOctreeQuery(PODVector<Drawable*>& result, const Frustum& frustum, OcclusionBuffer* buffer,
131         unsigned char drawableFlags = DRAWABLE_ANY, unsigned viewMask = DEFAULT_VIEWMASK) :
132         FrustumOctreeQuery(result, frustum, drawableFlags, viewMask),
133         buffer_(buffer)
134     {
135     }
136 
137     /// Intersection test for an octant.
TestOctant(const BoundingBox & box,bool inside)138     virtual Intersection TestOctant(const BoundingBox& box, bool inside)
139     {
140         if (inside)
141             return buffer_->IsVisible(box) ? INSIDE : OUTSIDE;
142         else
143         {
144             Intersection result = frustum_.IsInside(box);
145             if (result != OUTSIDE && !buffer_->IsVisible(box))
146                 result = OUTSIDE;
147             return result;
148         }
149     }
150 
151     /// Intersection test for drawables. Note: drawable occlusion is performed later in worker threads.
TestDrawables(Drawable ** start,Drawable ** end,bool inside)152     virtual void TestDrawables(Drawable** start, Drawable** end, bool inside)
153     {
154         while (start != end)
155         {
156             Drawable* drawable = *start++;
157 
158             if ((drawable->GetDrawableFlags() & drawableFlags_) && (drawable->GetViewMask() & viewMask_))
159             {
160                 if (inside || frustum_.IsInsideFast(drawable->GetWorldBoundingBox()))
161                     result_.Push(drawable);
162             }
163         }
164     }
165 
166     /// Occlusion buffer.
167     OcclusionBuffer* buffer_;
168 };
169 
CheckVisibilityWork(const WorkItem * item,unsigned threadIndex)170 void CheckVisibilityWork(const WorkItem* item, unsigned threadIndex)
171 {
172     View* view = reinterpret_cast<View*>(item->aux_);
173     Drawable** start = reinterpret_cast<Drawable**>(item->start_);
174     Drawable** end = reinterpret_cast<Drawable**>(item->end_);
175     OcclusionBuffer* buffer = view->occlusionBuffer_;
176     const Matrix3x4& viewMatrix = view->cullCamera_->GetView();
177     Vector3 viewZ = Vector3(viewMatrix.m20_, viewMatrix.m21_, viewMatrix.m22_);
178     Vector3 absViewZ = viewZ.Abs();
179     unsigned cameraViewMask = view->cullCamera_->GetViewMask();
180     bool cameraZoneOverride = view->cameraZoneOverride_;
181     PerThreadSceneResult& result = view->sceneResults_[threadIndex];
182 
183     while (start != end)
184     {
185         Drawable* drawable = *start++;
186 
187         if (!buffer || !drawable->IsOccludee() || buffer->IsVisible(drawable->GetWorldBoundingBox()))
188         {
189             drawable->UpdateBatches(view->frame_);
190             // If draw distance non-zero, update and check it
191             float maxDistance = drawable->GetDrawDistance();
192             if (maxDistance > 0.0f)
193             {
194                 if (drawable->GetDistance() > maxDistance)
195                     continue;
196             }
197 
198             drawable->MarkInView(view->frame_);
199 
200             // For geometries, find zone, clear lights and calculate view space Z range
201             if (drawable->GetDrawableFlags() & DRAWABLE_GEOMETRY)
202             {
203                 Zone* drawableZone = drawable->GetZone();
204                 if (!cameraZoneOverride &&
205                     (drawable->IsZoneDirty() || !drawableZone || (drawableZone->GetViewMask() & cameraViewMask) == 0))
206                     view->FindZone(drawable);
207 
208                 const BoundingBox& geomBox = drawable->GetWorldBoundingBox();
209                 Vector3 center = geomBox.Center();
210                 Vector3 edge = geomBox.Size() * 0.5f;
211 
212                 // Do not add "infinite" objects like skybox to prevent shadow map focusing behaving erroneously
213                 if (edge.LengthSquared() < M_LARGE_VALUE * M_LARGE_VALUE)
214                 {
215                     float viewCenterZ = viewZ.DotProduct(center) + viewMatrix.m23_;
216                     float viewEdgeZ = absViewZ.DotProduct(edge);
217                     float minZ = viewCenterZ - viewEdgeZ;
218                     float maxZ = viewCenterZ + viewEdgeZ;
219                     drawable->SetMinMaxZ(viewCenterZ - viewEdgeZ, viewCenterZ + viewEdgeZ);
220                     result.minZ_ = Min(result.minZ_, minZ);
221                     result.maxZ_ = Max(result.maxZ_, maxZ);
222                 }
223                 else
224                     drawable->SetMinMaxZ(M_LARGE_VALUE, M_LARGE_VALUE);
225 
226                 result.geometries_.Push(drawable);
227             }
228             else if (drawable->GetDrawableFlags() & DRAWABLE_LIGHT)
229             {
230                 Light* light = static_cast<Light*>(drawable);
231                 // Skip lights with zero brightness or black color
232                 if (!light->GetEffectiveColor().Equals(Color::BLACK))
233                     result.lights_.Push(light);
234             }
235         }
236     }
237 }
238 
ProcessLightWork(const WorkItem * item,unsigned threadIndex)239 void ProcessLightWork(const WorkItem* item, unsigned threadIndex)
240 {
241     View* view = reinterpret_cast<View*>(item->aux_);
242     LightQueryResult* query = reinterpret_cast<LightQueryResult*>(item->start_);
243 
244     view->ProcessLight(*query, threadIndex);
245 }
246 
UpdateDrawableGeometriesWork(const WorkItem * item,unsigned threadIndex)247 void UpdateDrawableGeometriesWork(const WorkItem* item, unsigned threadIndex)
248 {
249     const FrameInfo& frame = *(reinterpret_cast<FrameInfo*>(item->aux_));
250     Drawable** start = reinterpret_cast<Drawable**>(item->start_);
251     Drawable** end = reinterpret_cast<Drawable**>(item->end_);
252 
253     while (start != end)
254     {
255         Drawable* drawable = *start++;
256         // We may leave null pointer holes in the queue if a drawable is found out to require a main thread update
257         if (drawable)
258             drawable->UpdateGeometry(frame);
259     }
260 }
261 
SortBatchQueueFrontToBackWork(const WorkItem * item,unsigned threadIndex)262 void SortBatchQueueFrontToBackWork(const WorkItem* item, unsigned threadIndex)
263 {
264     BatchQueue* queue = reinterpret_cast<BatchQueue*>(item->start_);
265 
266     queue->SortFrontToBack();
267 }
268 
SortBatchQueueBackToFrontWork(const WorkItem * item,unsigned threadIndex)269 void SortBatchQueueBackToFrontWork(const WorkItem* item, unsigned threadIndex)
270 {
271     BatchQueue* queue = reinterpret_cast<BatchQueue*>(item->start_);
272 
273     queue->SortBackToFront();
274 }
275 
SortLightQueueWork(const WorkItem * item,unsigned threadIndex)276 void SortLightQueueWork(const WorkItem* item, unsigned threadIndex)
277 {
278     LightBatchQueue* start = reinterpret_cast<LightBatchQueue*>(item->start_);
279     start->litBaseBatches_.SortFrontToBack();
280     start->litBatches_.SortFrontToBack();
281 }
282 
SortShadowQueueWork(const WorkItem * item,unsigned threadIndex)283 void SortShadowQueueWork(const WorkItem* item, unsigned threadIndex)
284 {
285     LightBatchQueue* start = reinterpret_cast<LightBatchQueue*>(item->start_);
286     for (unsigned i = 0; i < start->shadowSplits_.Size(); ++i)
287         start->shadowSplits_[i].shadowBatches_.SortFrontToBack();
288 }
289 
290 StringHash ParseTextureTypeXml(ResourceCache* cache, String filename);
291 
View(Context * context)292 View::View(Context* context) :
293     Object(context),
294     graphics_(GetSubsystem<Graphics>()),
295     renderer_(GetSubsystem<Renderer>()),
296     scene_(0),
297     octree_(0),
298     cullCamera_(0),
299     camera_(0),
300     cameraZone_(0),
301     farClipZone_(0),
302     occlusionBuffer_(0),
303     renderTarget_(0),
304     substituteRenderTarget_(0),
305     passCommand_(0)
306 {
307     // Create octree query and scene results vector for each thread
308     unsigned numThreads = GetSubsystem<WorkQueue>()->GetNumThreads() + 1; // Worker threads + main thread
309     tempDrawables_.Resize(numThreads);
310     sceneResults_.Resize(numThreads);
311     frame_.camera_ = 0;
312 }
313 
~View()314 View::~View()
315 {
316 }
317 
Define(RenderSurface * renderTarget,Viewport * viewport)318 bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
319 {
320     sourceView_ = 0;
321     renderPath_ = viewport->GetRenderPath();
322     if (!renderPath_)
323         return false;
324 
325     renderTarget_ = renderTarget;
326     drawDebug_ = viewport->GetDrawDebug();
327 
328     // Validate the rect and calculate size. If zero rect, use whole rendertarget size
329     int rtWidth = renderTarget ? renderTarget->GetWidth() : graphics_->GetWidth();
330     int rtHeight = renderTarget ? renderTarget->GetHeight() : graphics_->GetHeight();
331     const IntRect& rect = viewport->GetRect();
332 
333     if (rect != IntRect::ZERO)
334     {
335         viewRect_.left_ = Clamp(rect.left_, 0, rtWidth - 1);
336         viewRect_.top_ = Clamp(rect.top_, 0, rtHeight - 1);
337         viewRect_.right_ = Clamp(rect.right_, viewRect_.left_ + 1, rtWidth);
338         viewRect_.bottom_ = Clamp(rect.bottom_, viewRect_.top_ + 1, rtHeight);
339     }
340     else
341         viewRect_ = IntRect(0, 0, rtWidth, rtHeight);
342 
343     viewSize_ = viewRect_.Size();
344     rtSize_ = IntVector2(rtWidth, rtHeight);
345 
346     // On OpenGL flip the viewport if rendering to a texture for consistent UV addressing with Direct3D9
347 #ifdef URHO3D_OPENGL
348     if (renderTarget_)
349     {
350         viewRect_.bottom_ = rtHeight - viewRect_.top_;
351         viewRect_.top_ = viewRect_.bottom_ - viewSize_.y_;
352     }
353 #endif
354 
355     scene_ = viewport->GetScene();
356     cullCamera_ = viewport->GetCullCamera();
357     camera_ = viewport->GetCamera();
358     if (!cullCamera_)
359         cullCamera_ = camera_;
360     else
361     {
362         // If view specifies a culling camera (view preparation sharing), check if already prepared
363         sourceView_ = renderer_->GetPreparedView(cullCamera_);
364         if (sourceView_ && sourceView_->scene_ == scene_ && sourceView_->renderPath_ == renderPath_)
365         {
366             // Copy properties needed later in rendering
367             deferred_ = sourceView_->deferred_;
368             deferredAmbient_ = sourceView_->deferredAmbient_;
369             useLitBase_ = sourceView_->useLitBase_;
370             hasScenePasses_ = sourceView_->hasScenePasses_;
371             noStencil_ = sourceView_->noStencil_;
372             lightVolumeCommand_ = sourceView_->lightVolumeCommand_;
373             forwardLightsCommand_ = sourceView_->forwardLightsCommand_;
374             octree_ = sourceView_->octree_;
375             return true;
376         }
377         else
378         {
379             // Mismatch in scene or renderpath, fall back to unique view preparation
380             sourceView_ = 0;
381         }
382     }
383 
384     // Set default passes
385     gBufferPassIndex_ = M_MAX_UNSIGNED;
386     basePassIndex_ = Technique::GetPassIndex("base");
387     alphaPassIndex_ = Technique::GetPassIndex("alpha");
388     lightPassIndex_ = Technique::GetPassIndex("light");
389     litBasePassIndex_ = Technique::GetPassIndex("litbase");
390     litAlphaPassIndex_ = Technique::GetPassIndex("litalpha");
391 
392     deferred_ = false;
393     deferredAmbient_ = false;
394     useLitBase_ = false;
395     hasScenePasses_ = false;
396     noStencil_ = false;
397     lightVolumeCommand_ = 0;
398     forwardLightsCommand_ = 0;
399 
400     scenePasses_.Clear();
401     geometriesUpdated_ = false;
402 
403 #ifdef URHO3D_OPENGL
404 #ifdef GL_ES_VERSION_2_0
405     // On OpenGL ES we assume a stencil is not available or would not give a good performance, and disable light stencil
406     // optimizations in any case
407     noStencil_ = true;
408 #else
409     for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
410     {
411         const RenderPathCommand& command = renderPath_->commands_[i];
412         if (!command.enabled_)
413             continue;
414         if (command.depthStencilName_.Length())
415         {
416             // Using a readable depth texture will disable light stencil optimizations on OpenGL, as for compatibility reasons
417             // we are using a depth format without stencil channel
418             noStencil_ = true;
419             break;
420         }
421     }
422 #endif
423 #endif
424 
425     // Make sure that all necessary batch queues exist
426     for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
427     {
428         RenderPathCommand& command = renderPath_->commands_[i];
429         if (!command.enabled_)
430             continue;
431 
432         if (command.type_ == CMD_SCENEPASS)
433         {
434             hasScenePasses_ = true;
435 
436             ScenePassInfo info;
437             info.passIndex_ = command.passIndex_ = Technique::GetPassIndex(command.pass_);
438             info.allowInstancing_ = command.sortMode_ != SORT_BACKTOFRONT;
439             info.markToStencil_ = !noStencil_ && command.markToStencil_;
440             info.vertexLights_ = command.vertexLights_;
441 
442             // Check scenepass metadata for defining custom passes which interact with lighting
443             if (!command.metadata_.Empty())
444             {
445                 if (command.metadata_ == "gbuffer")
446                     gBufferPassIndex_ = command.passIndex_;
447                 else if (command.metadata_ == "base" && command.pass_ != "base")
448                 {
449                     basePassIndex_ = command.passIndex_;
450                     litBasePassIndex_ = Technique::GetPassIndex("lit" + command.pass_);
451                 }
452                 else if (command.metadata_ == "alpha" && command.pass_ != "alpha")
453                 {
454                     alphaPassIndex_ = command.passIndex_;
455                     litAlphaPassIndex_ = Technique::GetPassIndex("lit" + command.pass_);
456                 }
457             }
458 
459             HashMap<unsigned, BatchQueue>::Iterator j = batchQueues_.Find(info.passIndex_);
460             if (j == batchQueues_.End())
461                 j = batchQueues_.Insert(Pair<unsigned, BatchQueue>(info.passIndex_, BatchQueue()));
462             info.batchQueue_ = &j->second_;
463             SetQueueShaderDefines(*info.batchQueue_, command);
464 
465             scenePasses_.Push(info);
466         }
467         // Allow a custom forward light pass
468         else if (command.type_ == CMD_FORWARDLIGHTS && !command.pass_.Empty())
469             lightPassIndex_ = command.passIndex_ = Technique::GetPassIndex(command.pass_);
470     }
471 
472     octree_ = 0;
473     // Get default zone first in case we do not have zones defined
474     cameraZone_ = farClipZone_ = renderer_->GetDefaultZone();
475 
476     if (hasScenePasses_)
477     {
478         if (!scene_ || !cullCamera_ || !cullCamera_->IsEnabledEffective())
479             return false;
480 
481         // If scene is loading scene content asynchronously, it is incomplete and should not be rendered
482         if (scene_->IsAsyncLoading() && scene_->GetAsyncLoadMode() > LOAD_RESOURCES_ONLY)
483             return false;
484 
485         octree_ = scene_->GetComponent<Octree>();
486         if (!octree_)
487             return false;
488 
489         // Do not accept view if camera projection is illegal
490         // (there is a possibility of crash if occlusion is used and it can not clip properly)
491         if (!cullCamera_->IsProjectionValid())
492             return false;
493     }
494 
495     // Go through commands to check for deferred rendering and other flags
496     for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
497     {
498         const RenderPathCommand& command = renderPath_->commands_[i];
499         if (!command.enabled_)
500             continue;
501 
502         // Check if ambient pass and G-buffer rendering happens at the same time
503         if (command.type_ == CMD_SCENEPASS && command.outputs_.Size() > 1)
504         {
505             if (CheckViewportWrite(command))
506                 deferredAmbient_ = true;
507         }
508         else if (command.type_ == CMD_LIGHTVOLUMES)
509         {
510             lightVolumeCommand_ = &command;
511             deferred_ = true;
512         }
513         else if (command.type_ == CMD_FORWARDLIGHTS)
514         {
515             forwardLightsCommand_ = &command;
516             useLitBase_ = command.useLitBase_;
517         }
518     }
519 
520     drawShadows_ = renderer_->GetDrawShadows();
521     materialQuality_ = renderer_->GetMaterialQuality();
522     maxOccluderTriangles_ = renderer_->GetMaxOccluderTriangles();
523     minInstances_ = renderer_->GetMinInstances();
524 
525     // Set possible quality overrides from the camera
526     // Note that the culling camera is used here (its settings are authoritative) while the render camera
527     // will be just used for the final view & projection matrices
528     unsigned viewOverrideFlags = cullCamera_ ? cullCamera_->GetViewOverrideFlags() : VO_NONE;
529     if (viewOverrideFlags & VO_LOW_MATERIAL_QUALITY)
530         materialQuality_ = QUALITY_LOW;
531     if (viewOverrideFlags & VO_DISABLE_SHADOWS)
532         drawShadows_ = false;
533     if (viewOverrideFlags & VO_DISABLE_OCCLUSION)
534         maxOccluderTriangles_ = 0;
535 
536     // Occlusion buffer has constant width. If resulting height would be too large due to aspect ratio, disable occlusion
537     if (viewSize_.y_ > viewSize_.x_ * 4)
538         maxOccluderTriangles_ = 0;
539 
540     return true;
541 }
542 
Update(const FrameInfo & frame)543 void View::Update(const FrameInfo& frame)
544 {
545     // No need to update if using another prepared view
546     if (sourceView_)
547         return;
548 
549     frame_.camera_ = cullCamera_;
550     frame_.timeStep_ = frame.timeStep_;
551     frame_.frameNumber_ = frame.frameNumber_;
552     frame_.viewSize_ = viewSize_;
553 
554     using namespace BeginViewUpdate;
555 
556     SendViewEvent(E_BEGINVIEWUPDATE);
557 
558     int maxSortedInstances = renderer_->GetMaxSortedInstances();
559 
560     // Clear buffers, geometry, light, occluder & batch list
561     renderTargets_.Clear();
562     geometries_.Clear();
563     lights_.Clear();
564     zones_.Clear();
565     occluders_.Clear();
566     activeOccluders_ = 0;
567     vertexLightQueues_.Clear();
568     for (HashMap<unsigned, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
569         i->second_.Clear(maxSortedInstances);
570 
571     if (hasScenePasses_ && (!cullCamera_ || !octree_))
572     {
573         SendViewEvent(E_ENDVIEWUPDATE);
574         return;
575     }
576 
577     // Set automatic aspect ratio if required
578     if (cullCamera_ && cullCamera_->GetAutoAspectRatio())
579         cullCamera_->SetAspectRatioInternal((float)frame_.viewSize_.x_ / (float)frame_.viewSize_.y_);
580 
581     GetDrawables();
582     GetBatches();
583     renderer_->StorePreparedView(this, cullCamera_);
584 
585     SendViewEvent(E_ENDVIEWUPDATE);
586 }
587 
Render()588 void View::Render()
589 {
590     SendViewEvent(E_BEGINVIEWRENDER);
591 
592     if (hasScenePasses_ && (!octree_ || !camera_))
593     {
594         SendViewEvent(E_ENDVIEWRENDER);
595         return;
596     }
597 
598     UpdateGeometries();
599 
600     // Allocate screen buffers as necessary
601     AllocateScreenBuffers();
602     SendViewEvent(E_VIEWBUFFERSREADY);
603 
604     // Forget parameter sources from the previous view
605     graphics_->ClearParameterSources();
606 
607     if (renderer_->GetDynamicInstancing() && graphics_->GetInstancingSupport())
608         PrepareInstancingBuffer();
609 
610     // It is possible, though not recommended, that the same camera is used for multiple main views. Set automatic aspect ratio
611     // to ensure correct projection will be used
612     if (camera_ && camera_->GetAutoAspectRatio())
613         camera_->SetAspectRatioInternal((float)(viewSize_.x_) / (float)(viewSize_.y_));
614 
615     // Bind the face selection and indirection cube maps for point light shadows
616 #ifndef GL_ES_VERSION_2_0
617     if (renderer_->GetDrawShadows())
618     {
619         graphics_->SetTexture(TU_FACESELECT, renderer_->GetFaceSelectCubeMap());
620         graphics_->SetTexture(TU_INDIRECTION, renderer_->GetIndirectionCubeMap());
621     }
622 #endif
623 
624     if (renderTarget_)
625     {
626         // On OpenGL, flip the projection if rendering to a texture so that the texture can be addressed in the same way
627         // as a render texture produced on Direct3D9
628 #ifdef URHO3D_OPENGL
629         if (camera_)
630             camera_->SetFlipVertical(true);
631 #endif
632     }
633 
634     // Render
635     ExecuteRenderPathCommands();
636 
637     // Reset state after commands
638     graphics_->SetFillMode(FILL_SOLID);
639     graphics_->SetLineAntiAlias(false);
640     graphics_->SetClipPlane(false);
641     graphics_->SetColorWrite(true);
642     graphics_->SetDepthBias(0.0f, 0.0f);
643     graphics_->SetScissorTest(false);
644     graphics_->SetStencilTest(false);
645 
646     // Draw the associated debug geometry now if enabled
647     if (drawDebug_ && octree_ && camera_)
648     {
649         DebugRenderer* debug = octree_->GetComponent<DebugRenderer>();
650         if (debug && debug->IsEnabledEffective() && debug->HasContent())
651         {
652             // If used resolve from backbuffer, blit first to the backbuffer to ensure correct depth buffer on OpenGL
653             // Otherwise use the last rendertarget and blit after debug geometry
654             if (usedResolve_ && currentRenderTarget_ != renderTarget_)
655             {
656                 BlitFramebuffer(currentRenderTarget_->GetParentTexture(), renderTarget_, false);
657                 currentRenderTarget_ = renderTarget_;
658                 lastCustomDepthSurface_ = 0;
659             }
660 
661             graphics_->SetRenderTarget(0, currentRenderTarget_);
662             for (unsigned i = 1; i < MAX_RENDERTARGETS; ++i)
663                 graphics_->SetRenderTarget(i, (RenderSurface*)0);
664 
665             // If a custom depth surface was used, use it also for debug rendering
666             graphics_->SetDepthStencil(lastCustomDepthSurface_ ? lastCustomDepthSurface_ : GetDepthStencil(currentRenderTarget_));
667 
668             IntVector2 rtSizeNow = graphics_->GetRenderTargetDimensions();
669             IntRect viewport = (currentRenderTarget_ == renderTarget_) ? viewRect_ : IntRect(0, 0, rtSizeNow.x_,
670                 rtSizeNow.y_);
671             graphics_->SetViewport(viewport);
672 
673             debug->SetView(camera_);
674             debug->Render();
675         }
676     }
677 
678 #ifdef URHO3D_OPENGL
679     if (camera_)
680         camera_->SetFlipVertical(false);
681 #endif
682 
683     // Run framebuffer blitting if necessary. If scene was resolved from backbuffer, do not touch depth
684     // (backbuffer should contain proper depth already)
685     if (currentRenderTarget_ != renderTarget_)
686         BlitFramebuffer(currentRenderTarget_->GetParentTexture(), renderTarget_, !usedResolve_);
687 
688     SendViewEvent(E_ENDVIEWRENDER);
689 }
690 
GetGraphics() const691 Graphics* View::GetGraphics() const
692 {
693     return graphics_;
694 }
695 
GetRenderer() const696 Renderer* View::GetRenderer() const
697 {
698     return renderer_;
699 }
700 
GetSourceView() const701 View* View::GetSourceView() const
702 {
703     return sourceView_;
704 }
705 
SetGlobalShaderParameters()706 void View::SetGlobalShaderParameters()
707 {
708     graphics_->SetShaderParameter(VSP_DELTATIME, frame_.timeStep_);
709     graphics_->SetShaderParameter(PSP_DELTATIME, frame_.timeStep_);
710 
711     if (scene_)
712     {
713         float elapsedTime = scene_->GetElapsedTime();
714         graphics_->SetShaderParameter(VSP_ELAPSEDTIME, elapsedTime);
715         graphics_->SetShaderParameter(PSP_ELAPSEDTIME, elapsedTime);
716     }
717 
718     SendViewEvent(E_VIEWGLOBALSHADERPARAMETERS);
719 }
720 
SetCameraShaderParameters(Camera * camera)721 void View::SetCameraShaderParameters(Camera* camera)
722 {
723     if (!camera)
724         return;
725 
726     Matrix3x4 cameraEffectiveTransform = camera->GetEffectiveWorldTransform();
727 
728     graphics_->SetShaderParameter(VSP_CAMERAPOS, cameraEffectiveTransform.Translation());
729     graphics_->SetShaderParameter(VSP_VIEWINV, cameraEffectiveTransform);
730     graphics_->SetShaderParameter(VSP_VIEW, camera->GetView());
731     graphics_->SetShaderParameter(PSP_CAMERAPOS, cameraEffectiveTransform.Translation());
732 
733     float nearClip = camera->GetNearClip();
734     float farClip = camera->GetFarClip();
735     graphics_->SetShaderParameter(VSP_NEARCLIP, nearClip);
736     graphics_->SetShaderParameter(VSP_FARCLIP, farClip);
737     graphics_->SetShaderParameter(PSP_NEARCLIP, nearClip);
738     graphics_->SetShaderParameter(PSP_FARCLIP, farClip);
739 
740     Vector4 depthMode = Vector4::ZERO;
741     if (camera->IsOrthographic())
742     {
743         depthMode.x_ = 1.0f;
744 #ifdef URHO3D_OPENGL
745         depthMode.z_ = 0.5f;
746         depthMode.w_ = 0.5f;
747 #else
748         depthMode.z_ = 1.0f;
749 #endif
750     }
751     else
752         depthMode.w_ = 1.0f / camera->GetFarClip();
753 
754     graphics_->SetShaderParameter(VSP_DEPTHMODE, depthMode);
755 
756     Vector4 depthReconstruct
757         (farClip / (farClip - nearClip), -nearClip / (farClip - nearClip), camera->IsOrthographic() ? 1.0f : 0.0f,
758             camera->IsOrthographic() ? 0.0f : 1.0f);
759     graphics_->SetShaderParameter(PSP_DEPTHRECONSTRUCT, depthReconstruct);
760 
761     Vector3 nearVector, farVector;
762     camera->GetFrustumSize(nearVector, farVector);
763     graphics_->SetShaderParameter(VSP_FRUSTUMSIZE, farVector);
764 
765     Matrix4 projection = camera->GetGPUProjection();
766 #ifdef URHO3D_OPENGL
767     // Add constant depth bias manually to the projection matrix due to glPolygonOffset() inconsistency
768     float constantBias = 2.0f * graphics_->GetDepthConstantBias();
769     projection.m22_ += projection.m32_ * constantBias;
770     projection.m23_ += projection.m33_ * constantBias;
771 #endif
772 
773     graphics_->SetShaderParameter(VSP_VIEWPROJ, projection * camera->GetView());
774 
775     // If in a scene pass and the command defines shader parameters, set them now
776     if (passCommand_)
777         SetCommandShaderParameters(*passCommand_);
778 }
779 
SetCommandShaderParameters(const RenderPathCommand & command)780 void View::SetCommandShaderParameters(const RenderPathCommand& command)
781 {
782     const HashMap<StringHash, Variant>& parameters = command.shaderParameters_;
783     for (HashMap<StringHash, Variant>::ConstIterator k = parameters.Begin(); k != parameters.End(); ++k)
784         graphics_->SetShaderParameter(k->first_, k->second_);
785 }
786 
SetGBufferShaderParameters(const IntVector2 & texSize,const IntRect & viewRect)787 void View::SetGBufferShaderParameters(const IntVector2& texSize, const IntRect& viewRect)
788 {
789     float texWidth = (float)texSize.x_;
790     float texHeight = (float)texSize.y_;
791     float widthRange = 0.5f * viewRect.Width() / texWidth;
792     float heightRange = 0.5f * viewRect.Height() / texHeight;
793 
794 #ifdef URHO3D_OPENGL
795     Vector4 bufferUVOffset(((float)viewRect.left_) / texWidth + widthRange,
796         1.0f - (((float)viewRect.top_) / texHeight + heightRange), widthRange, heightRange);
797 #else
798     const Vector2& pixelUVOffset = Graphics::GetPixelUVOffset();
799     Vector4 bufferUVOffset((pixelUVOffset.x_ + (float)viewRect.left_) / texWidth + widthRange,
800         (pixelUVOffset.y_ + (float)viewRect.top_) / texHeight + heightRange, widthRange, heightRange);
801 #endif
802     graphics_->SetShaderParameter(VSP_GBUFFEROFFSETS, bufferUVOffset);
803 
804     float invSizeX = 1.0f / texWidth;
805     float invSizeY = 1.0f / texHeight;
806     graphics_->SetShaderParameter(PSP_GBUFFERINVSIZE, Vector2(invSizeX, invSizeY));
807 }
808 
GetDrawables()809 void View::GetDrawables()
810 {
811     if (!octree_ || !cullCamera_)
812         return;
813 
814     URHO3D_PROFILE(GetDrawables);
815 
816     WorkQueue* queue = GetSubsystem<WorkQueue>();
817     PODVector<Drawable*>& tempDrawables = tempDrawables_[0];
818 
819     // Get zones and occluders first
820     {
821         ZoneOccluderOctreeQuery
822             query(tempDrawables, cullCamera_->GetFrustum(), DRAWABLE_GEOMETRY | DRAWABLE_ZONE, cullCamera_->GetViewMask());
823         octree_->GetDrawables(query);
824     }
825 
826     highestZonePriority_ = M_MIN_INT;
827     int bestPriority = M_MIN_INT;
828     Node* cameraNode = cullCamera_->GetNode();
829     Vector3 cameraPos = cameraNode->GetWorldPosition();
830 
831     for (PODVector<Drawable*>::ConstIterator i = tempDrawables.Begin(); i != tempDrawables.End(); ++i)
832     {
833         Drawable* drawable = *i;
834         unsigned char flags = drawable->GetDrawableFlags();
835 
836         if (flags & DRAWABLE_ZONE)
837         {
838             Zone* zone = static_cast<Zone*>(drawable);
839             zones_.Push(zone);
840             int priority = zone->GetPriority();
841             if (priority > highestZonePriority_)
842                 highestZonePriority_ = priority;
843             if (priority > bestPriority && zone->IsInside(cameraPos))
844             {
845                 cameraZone_ = zone;
846                 bestPriority = priority;
847             }
848         }
849         else
850             occluders_.Push(drawable);
851     }
852 
853     // Determine the zone at far clip distance. If not found, or camera zone has override mode, use camera zone
854     cameraZoneOverride_ = cameraZone_->GetOverride();
855     if (!cameraZoneOverride_)
856     {
857         Vector3 farClipPos = cameraPos + cameraNode->GetWorldDirection() * Vector3(0.0f, 0.0f, cullCamera_->GetFarClip());
858         bestPriority = M_MIN_INT;
859 
860         for (PODVector<Zone*>::Iterator i = zones_.Begin(); i != zones_.End(); ++i)
861         {
862             int priority = (*i)->GetPriority();
863             if (priority > bestPriority && (*i)->IsInside(farClipPos))
864             {
865                 farClipZone_ = *i;
866                 bestPriority = priority;
867             }
868         }
869     }
870     if (farClipZone_ == renderer_->GetDefaultZone())
871         farClipZone_ = cameraZone_;
872 
873     // If occlusion in use, get & render the occluders
874     occlusionBuffer_ = 0;
875     if (maxOccluderTriangles_ > 0)
876     {
877         UpdateOccluders(occluders_, cullCamera_);
878         if (occluders_.Size())
879         {
880             URHO3D_PROFILE(DrawOcclusion);
881 
882             occlusionBuffer_ = renderer_->GetOcclusionBuffer(cullCamera_);
883             DrawOccluders(occlusionBuffer_, occluders_);
884         }
885     }
886     else
887         occluders_.Clear();
888 
889     // Get lights and geometries. Coarse occlusion for octants is used at this point
890     if (occlusionBuffer_)
891     {
892         OccludedFrustumOctreeQuery query
893             (tempDrawables, cullCamera_->GetFrustum(), occlusionBuffer_, DRAWABLE_GEOMETRY | DRAWABLE_LIGHT, cullCamera_->GetViewMask());
894         octree_->GetDrawables(query);
895     }
896     else
897     {
898         FrustumOctreeQuery query(tempDrawables, cullCamera_->GetFrustum(), DRAWABLE_GEOMETRY | DRAWABLE_LIGHT, cullCamera_->GetViewMask());
899         octree_->GetDrawables(query);
900     }
901 
902     // Check drawable occlusion, find zones for moved drawables and collect geometries & lights in worker threads
903     {
904         for (unsigned i = 0; i < sceneResults_.Size(); ++i)
905         {
906             PerThreadSceneResult& result = sceneResults_[i];
907 
908             result.geometries_.Clear();
909             result.lights_.Clear();
910             result.minZ_ = M_INFINITY;
911             result.maxZ_ = 0.0f;
912         }
913 
914         int numWorkItems = queue->GetNumThreads() + 1; // Worker threads + main thread
915         int drawablesPerItem = tempDrawables.Size() / numWorkItems;
916 
917         PODVector<Drawable*>::Iterator start = tempDrawables.Begin();
918         // Create a work item for each thread
919         for (int i = 0; i < numWorkItems; ++i)
920         {
921             SharedPtr<WorkItem> item = queue->GetFreeItem();
922             item->priority_ = M_MAX_UNSIGNED;
923             item->workFunction_ = CheckVisibilityWork;
924             item->aux_ = this;
925 
926             PODVector<Drawable*>::Iterator end = tempDrawables.End();
927             if (i < numWorkItems - 1 && end - start > drawablesPerItem)
928                 end = start + drawablesPerItem;
929 
930             item->start_ = &(*start);
931             item->end_ = &(*end);
932             queue->AddWorkItem(item);
933 
934             start = end;
935         }
936 
937         queue->Complete(M_MAX_UNSIGNED);
938     }
939 
940     // Combine lights, geometries & scene Z range from the threads
941     geometries_.Clear();
942     lights_.Clear();
943     minZ_ = M_INFINITY;
944     maxZ_ = 0.0f;
945 
946     if (sceneResults_.Size() > 1)
947     {
948         for (unsigned i = 0; i < sceneResults_.Size(); ++i)
949         {
950             PerThreadSceneResult& result = sceneResults_[i];
951             geometries_.Push(result.geometries_);
952             lights_.Push(result.lights_);
953             minZ_ = Min(minZ_, result.minZ_);
954             maxZ_ = Max(maxZ_, result.maxZ_);
955         }
956     }
957     else
958     {
959         // If just 1 thread, copy the results directly
960         PerThreadSceneResult& result = sceneResults_[0];
961         minZ_ = result.minZ_;
962         maxZ_ = result.maxZ_;
963         Swap(geometries_, result.geometries_);
964         Swap(lights_, result.lights_);
965     }
966 
967     if (minZ_ == M_INFINITY)
968         minZ_ = 0.0f;
969 
970     // Sort the lights to brightest/closest first, and per-vertex lights first so that per-vertex base pass can be evaluated first
971     for (unsigned i = 0; i < lights_.Size(); ++i)
972     {
973         Light* light = lights_[i];
974         light->SetIntensitySortValue(cullCamera_->GetDistance(light->GetNode()->GetWorldPosition()));
975         light->SetLightQueue(0);
976     }
977 
978     Sort(lights_.Begin(), lights_.End(), CompareLights);
979 }
980 
GetBatches()981 void View::GetBatches()
982 {
983     if (!octree_ || !cullCamera_)
984         return;
985 
986     nonThreadedGeometries_.Clear();
987     threadedGeometries_.Clear();
988 
989     ProcessLights();
990     GetLightBatches();
991     GetBaseBatches();
992 }
993 
ProcessLights()994 void View::ProcessLights()
995 {
996     // Process lit geometries and shadow casters for each light
997     URHO3D_PROFILE(ProcessLights);
998 
999     WorkQueue* queue = GetSubsystem<WorkQueue>();
1000     lightQueryResults_.Resize(lights_.Size());
1001 
1002     for (unsigned i = 0; i < lightQueryResults_.Size(); ++i)
1003     {
1004         SharedPtr<WorkItem> item = queue->GetFreeItem();
1005         item->priority_ = M_MAX_UNSIGNED;
1006         item->workFunction_ = ProcessLightWork;
1007         item->aux_ = this;
1008 
1009         LightQueryResult& query = lightQueryResults_[i];
1010         query.light_ = lights_[i];
1011 
1012         item->start_ = &query;
1013         queue->AddWorkItem(item);
1014     }
1015 
1016     // Ensure all lights have been processed before proceeding
1017     queue->Complete(M_MAX_UNSIGNED);
1018 }
1019 
GetLightBatches()1020 void View::GetLightBatches()
1021 {
1022     BatchQueue* alphaQueue = batchQueues_.Contains(alphaPassIndex_) ? &batchQueues_[alphaPassIndex_] : (BatchQueue*)0;
1023 
1024     // Build light queues and lit batches
1025     {
1026         URHO3D_PROFILE(GetLightBatches);
1027 
1028         // Preallocate light queues: per-pixel lights which have lit geometries
1029         unsigned numLightQueues = 0;
1030         unsigned usedLightQueues = 0;
1031         for (Vector<LightQueryResult>::ConstIterator i = lightQueryResults_.Begin(); i != lightQueryResults_.End(); ++i)
1032         {
1033             if (!i->light_->GetPerVertex() && i->litGeometries_.Size())
1034                 ++numLightQueues;
1035         }
1036 
1037         lightQueues_.Resize(numLightQueues);
1038         maxLightsDrawables_.Clear();
1039         unsigned maxSortedInstances = (unsigned)renderer_->GetMaxSortedInstances();
1040 
1041         for (Vector<LightQueryResult>::Iterator i = lightQueryResults_.Begin(); i != lightQueryResults_.End(); ++i)
1042         {
1043             LightQueryResult& query = *i;
1044 
1045             // If light has no affected geometries, no need to process further
1046             if (query.litGeometries_.Empty())
1047                 continue;
1048 
1049             Light* light = query.light_;
1050 
1051             // Per-pixel light
1052             if (!light->GetPerVertex())
1053             {
1054                 unsigned shadowSplits = query.numSplits_;
1055 
1056                 // Initialize light queue and store it to the light so that it can be found later
1057                 LightBatchQueue& lightQueue = lightQueues_[usedLightQueues++];
1058                 light->SetLightQueue(&lightQueue);
1059                 lightQueue.light_ = light;
1060                 lightQueue.negative_ = light->IsNegative();
1061                 lightQueue.shadowMap_ = 0;
1062                 lightQueue.litBaseBatches_.Clear(maxSortedInstances);
1063                 lightQueue.litBatches_.Clear(maxSortedInstances);
1064                 if (forwardLightsCommand_)
1065                 {
1066                     SetQueueShaderDefines(lightQueue.litBaseBatches_, *forwardLightsCommand_);
1067                     SetQueueShaderDefines(lightQueue.litBatches_, *forwardLightsCommand_);
1068                 }
1069                 else
1070                 {
1071                     lightQueue.litBaseBatches_.hasExtraDefines_ = false;
1072                     lightQueue.litBatches_.hasExtraDefines_ = false;
1073                 }
1074                 lightQueue.volumeBatches_.Clear();
1075 
1076                 // Allocate shadow map now
1077                 if (shadowSplits > 0)
1078                 {
1079                     lightQueue.shadowMap_ = renderer_->GetShadowMap(light, cullCamera_, (unsigned)viewSize_.x_, (unsigned)viewSize_.y_);
1080                     // If did not manage to get a shadow map, convert the light to unshadowed
1081                     if (!lightQueue.shadowMap_)
1082                         shadowSplits = 0;
1083                 }
1084 
1085                 // Setup shadow batch queues
1086                 lightQueue.shadowSplits_.Resize(shadowSplits);
1087                 for (unsigned j = 0; j < shadowSplits; ++j)
1088                 {
1089                     ShadowBatchQueue& shadowQueue = lightQueue.shadowSplits_[j];
1090                     Camera* shadowCamera = query.shadowCameras_[j];
1091                     shadowQueue.shadowCamera_ = shadowCamera;
1092                     shadowQueue.nearSplit_ = query.shadowNearSplits_[j];
1093                     shadowQueue.farSplit_ = query.shadowFarSplits_[j];
1094                     shadowQueue.shadowBatches_.Clear(maxSortedInstances);
1095 
1096                     // Setup the shadow split viewport and finalize shadow camera parameters
1097                     shadowQueue.shadowViewport_ = GetShadowMapViewport(light, j, lightQueue.shadowMap_);
1098                     FinalizeShadowCamera(shadowCamera, light, shadowQueue.shadowViewport_, query.shadowCasterBox_[j]);
1099 
1100                     // Loop through shadow casters
1101                     for (PODVector<Drawable*>::ConstIterator k = query.shadowCasters_.Begin() + query.shadowCasterBegin_[j];
1102                          k < query.shadowCasters_.Begin() + query.shadowCasterEnd_[j]; ++k)
1103                     {
1104                         Drawable* drawable = *k;
1105                         // If drawable is not in actual view frustum, mark it in view here and check its geometry update type
1106                         if (!drawable->IsInView(frame_, true))
1107                         {
1108                             drawable->MarkInView(frame_.frameNumber_);
1109                             UpdateGeometryType type = drawable->GetUpdateGeometryType();
1110                             if (type == UPDATE_MAIN_THREAD)
1111                                 nonThreadedGeometries_.Push(drawable);
1112                             else if (type == UPDATE_WORKER_THREAD)
1113                                 threadedGeometries_.Push(drawable);
1114                         }
1115 
1116                         const Vector<SourceBatch>& batches = drawable->GetBatches();
1117 
1118                         for (unsigned l = 0; l < batches.Size(); ++l)
1119                         {
1120                             const SourceBatch& srcBatch = batches[l];
1121 
1122                             Technique* tech = GetTechnique(drawable, srcBatch.material_);
1123                             if (!srcBatch.geometry_ || !srcBatch.numWorldTransforms_ || !tech)
1124                                 continue;
1125 
1126                             Pass* pass = tech->GetSupportedPass(Technique::shadowPassIndex);
1127                             // Skip if material has no shadow pass
1128                             if (!pass)
1129                                 continue;
1130 
1131                             Batch destBatch(srcBatch);
1132                             destBatch.pass_ = pass;
1133                             destBatch.zone_ = 0;
1134 
1135                             AddBatchToQueue(shadowQueue.shadowBatches_, destBatch, tech);
1136                         }
1137                     }
1138                 }
1139 
1140                 // Process lit geometries
1141                 for (PODVector<Drawable*>::ConstIterator j = query.litGeometries_.Begin(); j != query.litGeometries_.End(); ++j)
1142                 {
1143                     Drawable* drawable = *j;
1144                     drawable->AddLight(light);
1145 
1146                     // If drawable limits maximum lights, only record the light, and check maximum count / build batches later
1147                     if (!drawable->GetMaxLights())
1148                         GetLitBatches(drawable, lightQueue, alphaQueue);
1149                     else
1150                         maxLightsDrawables_.Insert(drawable);
1151                 }
1152 
1153                 // In deferred modes, store the light volume batch now. Since light mask 8 lowest bits are output to the stencil,
1154                 // lights that have all zeroes in the low 8 bits can be skipped; they would not affect geometry anyway
1155                 if (deferred_ && (light->GetLightMask() & 0xff) != 0)
1156                 {
1157                     Batch volumeBatch;
1158                     volumeBatch.geometry_ = renderer_->GetLightGeometry(light);
1159                     volumeBatch.geometryType_ = GEOM_STATIC;
1160                     volumeBatch.worldTransform_ = &light->GetVolumeTransform(cullCamera_);
1161                     volumeBatch.numWorldTransforms_ = 1;
1162                     volumeBatch.lightQueue_ = &lightQueue;
1163                     volumeBatch.distance_ = light->GetDistance();
1164                     volumeBatch.material_ = 0;
1165                     volumeBatch.pass_ = 0;
1166                     volumeBatch.zone_ = 0;
1167                     renderer_->SetLightVolumeBatchShaders(volumeBatch, cullCamera_, lightVolumeCommand_->vertexShaderName_,
1168                         lightVolumeCommand_->pixelShaderName_, lightVolumeCommand_->vertexShaderDefines_,
1169                         lightVolumeCommand_->pixelShaderDefines_);
1170                     lightQueue.volumeBatches_.Push(volumeBatch);
1171                 }
1172             }
1173             // Per-vertex light
1174             else
1175             {
1176                 // Add the vertex light to lit drawables. It will be processed later during base pass batch generation
1177                 for (PODVector<Drawable*>::ConstIterator j = query.litGeometries_.Begin(); j != query.litGeometries_.End(); ++j)
1178                 {
1179                     Drawable* drawable = *j;
1180                     drawable->AddVertexLight(light);
1181                 }
1182             }
1183         }
1184     }
1185 
1186     // Process drawables with limited per-pixel light count
1187     if (maxLightsDrawables_.Size())
1188     {
1189         URHO3D_PROFILE(GetMaxLightsBatches);
1190 
1191         for (HashSet<Drawable*>::Iterator i = maxLightsDrawables_.Begin(); i != maxLightsDrawables_.End(); ++i)
1192         {
1193             Drawable* drawable = *i;
1194             drawable->LimitLights();
1195             const PODVector<Light*>& lights = drawable->GetLights();
1196 
1197             for (unsigned i = 0; i < lights.Size(); ++i)
1198             {
1199                 Light* light = lights[i];
1200                 // Find the correct light queue again
1201                 LightBatchQueue* queue = light->GetLightQueue();
1202                 if (queue)
1203                     GetLitBatches(drawable, *queue, alphaQueue);
1204             }
1205         }
1206     }
1207 }
1208 
GetBaseBatches()1209 void View::GetBaseBatches()
1210 {
1211     URHO3D_PROFILE(GetBaseBatches);
1212 
1213     for (PODVector<Drawable*>::ConstIterator i = geometries_.Begin(); i != geometries_.End(); ++i)
1214     {
1215         Drawable* drawable = *i;
1216         UpdateGeometryType type = drawable->GetUpdateGeometryType();
1217         if (type == UPDATE_MAIN_THREAD)
1218             nonThreadedGeometries_.Push(drawable);
1219         else if (type == UPDATE_WORKER_THREAD)
1220             threadedGeometries_.Push(drawable);
1221 
1222         const Vector<SourceBatch>& batches = drawable->GetBatches();
1223         bool vertexLightsProcessed = false;
1224 
1225         for (unsigned j = 0; j < batches.Size(); ++j)
1226         {
1227             const SourceBatch& srcBatch = batches[j];
1228 
1229             // Check here if the material refers to a rendertarget texture with camera(s) attached
1230             // Only check this for backbuffer views (null rendertarget)
1231             if (srcBatch.material_ && srcBatch.material_->GetAuxViewFrameNumber() != frame_.frameNumber_ && !renderTarget_)
1232                 CheckMaterialForAuxView(srcBatch.material_);
1233 
1234             Technique* tech = GetTechnique(drawable, srcBatch.material_);
1235             if (!srcBatch.geometry_ || !srcBatch.numWorldTransforms_ || !tech)
1236                 continue;
1237 
1238             // Check each of the scene passes
1239             for (unsigned k = 0; k < scenePasses_.Size(); ++k)
1240             {
1241                 ScenePassInfo& info = scenePasses_[k];
1242                 // Skip forward base pass if the corresponding litbase pass already exists
1243                 if (info.passIndex_ == basePassIndex_ && j < 32 && drawable->HasBasePass(j))
1244                     continue;
1245 
1246                 Pass* pass = tech->GetSupportedPass(info.passIndex_);
1247                 if (!pass)
1248                     continue;
1249 
1250                 Batch destBatch(srcBatch);
1251                 destBatch.pass_ = pass;
1252                 destBatch.zone_ = GetZone(drawable);
1253                 destBatch.isBase_ = true;
1254                 destBatch.lightMask_ = (unsigned char)GetLightMask(drawable);
1255 
1256                 if (info.vertexLights_)
1257                 {
1258                     const PODVector<Light*>& drawableVertexLights = drawable->GetVertexLights();
1259                     if (drawableVertexLights.Size() && !vertexLightsProcessed)
1260                     {
1261                         // Limit vertex lights. If this is a deferred opaque batch, remove converted per-pixel lights,
1262                         // as they will be rendered as light volumes in any case, and drawing them also as vertex lights
1263                         // would result in double lighting
1264                         drawable->LimitVertexLights(deferred_ && destBatch.pass_->GetBlendMode() == BLEND_REPLACE);
1265                         vertexLightsProcessed = true;
1266                     }
1267 
1268                     if (drawableVertexLights.Size())
1269                     {
1270                         // Find a vertex light queue. If not found, create new
1271                         unsigned long long hash = GetVertexLightQueueHash(drawableVertexLights);
1272                         HashMap<unsigned long long, LightBatchQueue>::Iterator i = vertexLightQueues_.Find(hash);
1273                         if (i == vertexLightQueues_.End())
1274                         {
1275                             i = vertexLightQueues_.Insert(MakePair(hash, LightBatchQueue()));
1276                             i->second_.light_ = 0;
1277                             i->second_.shadowMap_ = 0;
1278                             i->second_.vertexLights_ = drawableVertexLights;
1279                         }
1280 
1281                         destBatch.lightQueue_ = &(i->second_);
1282                     }
1283                 }
1284                 else
1285                     destBatch.lightQueue_ = 0;
1286 
1287                 bool allowInstancing = info.allowInstancing_;
1288                 if (allowInstancing && info.markToStencil_ && destBatch.lightMask_ != (destBatch.zone_->GetLightMask() & 0xff))
1289                     allowInstancing = false;
1290 
1291                 AddBatchToQueue(*info.batchQueue_, destBatch, tech, allowInstancing);
1292             }
1293         }
1294     }
1295 }
1296 
UpdateGeometries()1297 void View::UpdateGeometries()
1298 {
1299     // Update geometries in the source view if necessary (prepare order may differ from render order)
1300     if (sourceView_ && !sourceView_->geometriesUpdated_)
1301     {
1302         sourceView_->UpdateGeometries();
1303         return;
1304     }
1305 
1306     URHO3D_PROFILE(SortAndUpdateGeometry);
1307 
1308     WorkQueue* queue = GetSubsystem<WorkQueue>();
1309 
1310     // Sort batches
1311     {
1312         for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
1313         {
1314             const RenderPathCommand& command = renderPath_->commands_[i];
1315             if (!IsNecessary(command))
1316                 continue;
1317 
1318             if (command.type_ == CMD_SCENEPASS)
1319             {
1320                 SharedPtr<WorkItem> item = queue->GetFreeItem();
1321                 item->priority_ = M_MAX_UNSIGNED;
1322                 item->workFunction_ =
1323                     command.sortMode_ == SORT_FRONTTOBACK ? SortBatchQueueFrontToBackWork : SortBatchQueueBackToFrontWork;
1324                 item->start_ = &batchQueues_[command.passIndex_];
1325                 queue->AddWorkItem(item);
1326             }
1327         }
1328 
1329         for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
1330         {
1331             SharedPtr<WorkItem> lightItem = queue->GetFreeItem();
1332             lightItem->priority_ = M_MAX_UNSIGNED;
1333             lightItem->workFunction_ = SortLightQueueWork;
1334             lightItem->start_ = &(*i);
1335             queue->AddWorkItem(lightItem);
1336 
1337             if (i->shadowSplits_.Size())
1338             {
1339                 SharedPtr<WorkItem> shadowItem = queue->GetFreeItem();
1340                 shadowItem->priority_ = M_MAX_UNSIGNED;
1341                 shadowItem->workFunction_ = SortShadowQueueWork;
1342                 shadowItem->start_ = &(*i);
1343                 queue->AddWorkItem(shadowItem);
1344             }
1345         }
1346     }
1347 
1348     // Update geometries. Split into threaded and non-threaded updates.
1349     {
1350         if (threadedGeometries_.Size())
1351         {
1352             // In special cases (context loss, multi-view) a drawable may theoretically first have reported a threaded update, but will actually
1353             // require a main thread update. Check these cases first and move as applicable. The threaded work routine will tolerate the null
1354             // pointer holes that we leave to the threaded update queue.
1355             for (PODVector<Drawable*>::Iterator i = threadedGeometries_.Begin(); i != threadedGeometries_.End(); ++i)
1356             {
1357                 if ((*i)->GetUpdateGeometryType() == UPDATE_MAIN_THREAD)
1358                 {
1359                     nonThreadedGeometries_.Push(*i);
1360                     *i = 0;
1361                 }
1362             }
1363 
1364             int numWorkItems = queue->GetNumThreads() + 1; // Worker threads + main thread
1365             int drawablesPerItem = threadedGeometries_.Size() / numWorkItems;
1366 
1367             PODVector<Drawable*>::Iterator start = threadedGeometries_.Begin();
1368             for (int i = 0; i < numWorkItems; ++i)
1369             {
1370                 PODVector<Drawable*>::Iterator end = threadedGeometries_.End();
1371                 if (i < numWorkItems - 1 && end - start > drawablesPerItem)
1372                     end = start + drawablesPerItem;
1373 
1374                 SharedPtr<WorkItem> item = queue->GetFreeItem();
1375                 item->priority_ = M_MAX_UNSIGNED;
1376                 item->workFunction_ = UpdateDrawableGeometriesWork;
1377                 item->aux_ = const_cast<FrameInfo*>(&frame_);
1378                 item->start_ = &(*start);
1379                 item->end_ = &(*end);
1380                 queue->AddWorkItem(item);
1381 
1382                 start = end;
1383             }
1384         }
1385 
1386         // While the work queue is processed, update non-threaded geometries
1387         for (PODVector<Drawable*>::ConstIterator i = nonThreadedGeometries_.Begin(); i != nonThreadedGeometries_.End(); ++i)
1388             (*i)->UpdateGeometry(frame_);
1389     }
1390 
1391     // Finally ensure all threaded work has completed
1392     queue->Complete(M_MAX_UNSIGNED);
1393     geometriesUpdated_ = true;
1394 }
1395 
GetLitBatches(Drawable * drawable,LightBatchQueue & lightQueue,BatchQueue * alphaQueue)1396 void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue, BatchQueue* alphaQueue)
1397 {
1398     Light* light = lightQueue.light_;
1399     Zone* zone = GetZone(drawable);
1400     const Vector<SourceBatch>& batches = drawable->GetBatches();
1401 
1402     bool allowLitBase =
1403         useLitBase_ && !lightQueue.negative_ && light == drawable->GetFirstLight() && drawable->GetVertexLights().Empty() &&
1404         !zone->GetAmbientGradient();
1405 
1406     for (unsigned i = 0; i < batches.Size(); ++i)
1407     {
1408         const SourceBatch& srcBatch = batches[i];
1409 
1410         Technique* tech = GetTechnique(drawable, srcBatch.material_);
1411         if (!srcBatch.geometry_ || !srcBatch.numWorldTransforms_ || !tech)
1412             continue;
1413 
1414         // Do not create pixel lit forward passes for materials that render into the G-buffer
1415         if (gBufferPassIndex_ != M_MAX_UNSIGNED && tech->HasPass(gBufferPassIndex_))
1416             continue;
1417 
1418         Batch destBatch(srcBatch);
1419         bool isLitAlpha = false;
1420 
1421         // Check for lit base pass. Because it uses the replace blend mode, it must be ensured to be the first light
1422         // Also vertex lighting or ambient gradient require the non-lit base pass, so skip in those cases
1423         if (i < 32 && allowLitBase)
1424         {
1425             destBatch.pass_ = tech->GetSupportedPass(litBasePassIndex_);
1426             if (destBatch.pass_)
1427             {
1428                 destBatch.isBase_ = true;
1429                 drawable->SetBasePass(i);
1430             }
1431             else
1432                 destBatch.pass_ = tech->GetSupportedPass(lightPassIndex_);
1433         }
1434         else
1435             destBatch.pass_ = tech->GetSupportedPass(lightPassIndex_);
1436 
1437         // If no lit pass, check for lit alpha
1438         if (!destBatch.pass_)
1439         {
1440             destBatch.pass_ = tech->GetSupportedPass(litAlphaPassIndex_);
1441             isLitAlpha = true;
1442         }
1443 
1444         // Skip if material does not receive light at all
1445         if (!destBatch.pass_)
1446             continue;
1447 
1448         destBatch.lightQueue_ = &lightQueue;
1449         destBatch.zone_ = zone;
1450 
1451         if (!isLitAlpha)
1452         {
1453             if (destBatch.isBase_)
1454                 AddBatchToQueue(lightQueue.litBaseBatches_, destBatch, tech);
1455             else
1456                 AddBatchToQueue(lightQueue.litBatches_, destBatch, tech);
1457         }
1458         else if (alphaQueue)
1459         {
1460             // Transparent batches can not be instanced, and shadows on transparencies can only be rendered if shadow maps are
1461             // not reused
1462             AddBatchToQueue(*alphaQueue, destBatch, tech, false, !renderer_->GetReuseShadowMaps());
1463         }
1464     }
1465 }
1466 
ExecuteRenderPathCommands()1467 void View::ExecuteRenderPathCommands()
1468 {
1469     View* actualView = sourceView_ ? sourceView_ : this;
1470 
1471     // If not reusing shadowmaps, render all of them first
1472     if (!renderer_->GetReuseShadowMaps() && renderer_->GetDrawShadows() && !actualView->lightQueues_.Empty())
1473     {
1474         URHO3D_PROFILE(RenderShadowMaps);
1475 
1476         for (Vector<LightBatchQueue>::Iterator i = actualView->lightQueues_.Begin(); i != actualView->lightQueues_.End(); ++i)
1477         {
1478             if (NeedRenderShadowMap(*i))
1479                 RenderShadowMap(*i);
1480         }
1481     }
1482 
1483     {
1484         URHO3D_PROFILE(ExecuteRenderPath);
1485 
1486         // Set for safety in case of empty renderpath
1487         currentRenderTarget_ = substituteRenderTarget_ ? substituteRenderTarget_ : renderTarget_;
1488         currentViewportTexture_ = 0;
1489         passCommand_ = 0;
1490 
1491         bool viewportModified = false;
1492         bool isPingponging = false;
1493         usedResolve_ = false;
1494 
1495         unsigned lastCommandIndex = 0;
1496         for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
1497         {
1498             RenderPathCommand& command = renderPath_->commands_[i];
1499             if (actualView->IsNecessary(command))
1500                 lastCommandIndex = i;
1501         }
1502 
1503         for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
1504         {
1505             RenderPathCommand& command = renderPath_->commands_[i];
1506             if (!actualView->IsNecessary(command))
1507                 continue;
1508 
1509             bool viewportRead = actualView->CheckViewportRead(command);
1510             bool viewportWrite = actualView->CheckViewportWrite(command);
1511             bool beginPingpong = actualView->CheckPingpong(i);
1512 
1513             // Has the viewport been modified and will be read as a texture by the current command?
1514             if (viewportRead && viewportModified)
1515             {
1516                 // Start pingponging without a blit if already rendering to the substitute render target
1517                 if (currentRenderTarget_ && currentRenderTarget_ == substituteRenderTarget_ && beginPingpong)
1518                     isPingponging = true;
1519 
1520                 // If not using pingponging, simply resolve/copy to the first viewport texture
1521                 if (!isPingponging)
1522                 {
1523                     if (!currentRenderTarget_)
1524                     {
1525                         graphics_->ResolveToTexture(dynamic_cast<Texture2D*>(viewportTextures_[0]), viewRect_);
1526                         currentViewportTexture_ = viewportTextures_[0];
1527                         viewportModified = false;
1528                         usedResolve_ = true;
1529                     }
1530                     else
1531                     {
1532                         if (viewportWrite)
1533                         {
1534                             BlitFramebuffer(currentRenderTarget_->GetParentTexture(),
1535                                 GetRenderSurfaceFromTexture(viewportTextures_[0]), false);
1536                             currentViewportTexture_ = viewportTextures_[0];
1537                             viewportModified = false;
1538                         }
1539                         else
1540                         {
1541                             // If the current render target is already a texture, and we are not writing to it, can read that
1542                             // texture directly instead of blitting. However keep the viewport dirty flag in case a later command
1543                             // will do both read and write, and then we need to blit / resolve
1544                             currentViewportTexture_ = currentRenderTarget_->GetParentTexture();
1545                         }
1546                     }
1547                 }
1548                 else
1549                 {
1550                     // Swap the pingpong double buffer sides. Texture 0 will be read next
1551                     viewportTextures_[1] = viewportTextures_[0];
1552                     viewportTextures_[0] = currentRenderTarget_->GetParentTexture();
1553                     currentViewportTexture_ = viewportTextures_[0];
1554                     viewportModified = false;
1555                 }
1556             }
1557 
1558             if (beginPingpong)
1559                 isPingponging = true;
1560 
1561             // Determine viewport write target
1562             if (viewportWrite)
1563             {
1564                 if (isPingponging)
1565                 {
1566                     currentRenderTarget_ = GetRenderSurfaceFromTexture(viewportTextures_[1]);
1567                     // If the render path ends into a quad, it can be redirected to the final render target
1568                     // However, on OpenGL we can not reliably do this in case the final target is the backbuffer, and we want to
1569                     // render depth buffer sensitive debug geometry afterward (backbuffer and textures can not share depth)
1570 #ifndef URHO3D_OPENGL
1571                     if (i == lastCommandIndex && command.type_ == CMD_QUAD)
1572 #else
1573                     if (i == lastCommandIndex && command.type_ == CMD_QUAD && renderTarget_)
1574 #endif
1575                         currentRenderTarget_ = renderTarget_;
1576                 }
1577                 else
1578                     currentRenderTarget_ = substituteRenderTarget_ ? substituteRenderTarget_ : renderTarget_;
1579             }
1580 
1581             switch (command.type_)
1582             {
1583             case CMD_CLEAR:
1584                 {
1585                     URHO3D_PROFILE(ClearRenderTarget);
1586 
1587                     Color clearColor = command.clearColor_;
1588                     if (command.useFogColor_)
1589                         clearColor = actualView->farClipZone_->GetFogColor();
1590 
1591                     SetRenderTargets(command);
1592                     graphics_->Clear(command.clearFlags_, clearColor, command.clearDepth_, command.clearStencil_);
1593                 }
1594                 break;
1595 
1596             case CMD_SCENEPASS:
1597                 {
1598                     BatchQueue& queue = actualView->batchQueues_[command.passIndex_];
1599                     if (!queue.IsEmpty())
1600                     {
1601                         URHO3D_PROFILE(RenderScenePass);
1602 
1603                         SetRenderTargets(command);
1604                         bool allowDepthWrite = SetTextures(command);
1605                         graphics_->SetClipPlane(camera_->GetUseClipping(), camera_->GetClipPlane(), camera_->GetView(),
1606                             camera_->GetGPUProjection());
1607 
1608                         if (command.shaderParameters_.Size())
1609                         {
1610                             // If pass defines shader parameters, reset parameter sources now to ensure they all will be set
1611                             // (will be set after camera shader parameters)
1612                             graphics_->ClearParameterSources();
1613                             passCommand_ = &command;
1614                         }
1615 
1616                         queue.Draw(this, camera_, command.markToStencil_, false, allowDepthWrite);
1617 
1618                         passCommand_ = 0;
1619                     }
1620                 }
1621                 break;
1622 
1623             case CMD_QUAD:
1624                 {
1625                     URHO3D_PROFILE(RenderQuad);
1626 
1627                     SetRenderTargets(command);
1628                     SetTextures(command);
1629                     RenderQuad(command);
1630                 }
1631                 break;
1632 
1633             case CMD_FORWARDLIGHTS:
1634                 // Render shadow maps + opaque objects' additive lighting
1635                 if (!actualView->lightQueues_.Empty())
1636                 {
1637                     URHO3D_PROFILE(RenderLights);
1638 
1639                     SetRenderTargets(command);
1640 
1641                     for (Vector<LightBatchQueue>::Iterator i = actualView->lightQueues_.Begin(); i != actualView->lightQueues_.End(); ++i)
1642                     {
1643                         // If reusing shadowmaps, render each of them before the lit batches
1644                         if (renderer_->GetReuseShadowMaps() && NeedRenderShadowMap(*i))
1645                         {
1646                             RenderShadowMap(*i);
1647                             SetRenderTargets(command);
1648                         }
1649 
1650                         bool allowDepthWrite = SetTextures(command);
1651                         graphics_->SetClipPlane(camera_->GetUseClipping(), camera_->GetClipPlane(), camera_->GetView(),
1652                             camera_->GetGPUProjection());
1653 
1654                         if (command.shaderParameters_.Size())
1655                         {
1656                             graphics_->ClearParameterSources();
1657                             passCommand_ = &command;
1658                         }
1659 
1660                         // Draw base (replace blend) batches first
1661                         i->litBaseBatches_.Draw(this, camera_, false, false, allowDepthWrite);
1662 
1663                         // Then, if there are additive passes, optimize the light and draw them
1664                         if (!i->litBatches_.IsEmpty())
1665                         {
1666                             renderer_->OptimizeLightByScissor(i->light_, camera_);
1667                             if (!noStencil_)
1668                                 renderer_->OptimizeLightByStencil(i->light_, camera_);
1669                             i->litBatches_.Draw(this, camera_, false, true, allowDepthWrite);
1670                         }
1671 
1672                         passCommand_ = 0;
1673                     }
1674 
1675                     graphics_->SetScissorTest(false);
1676                     graphics_->SetStencilTest(false);
1677                 }
1678                 break;
1679 
1680             case CMD_LIGHTVOLUMES:
1681                 // Render shadow maps + light volumes
1682                 if (!actualView->lightQueues_.Empty())
1683                 {
1684                     URHO3D_PROFILE(RenderLightVolumes);
1685 
1686                     SetRenderTargets(command);
1687                     for (Vector<LightBatchQueue>::Iterator i = actualView->lightQueues_.Begin(); i != actualView->lightQueues_.End(); ++i)
1688                     {
1689                         // If reusing shadowmaps, render each of them before the lit batches
1690                         if (renderer_->GetReuseShadowMaps() && NeedRenderShadowMap(*i))
1691                         {
1692                             RenderShadowMap(*i);
1693                             SetRenderTargets(command);
1694                         }
1695 
1696                         SetTextures(command);
1697 
1698                         if (command.shaderParameters_.Size())
1699                         {
1700                             graphics_->ClearParameterSources();
1701                             passCommand_ = &command;
1702                         }
1703 
1704                         for (unsigned j = 0; j < i->volumeBatches_.Size(); ++j)
1705                         {
1706                             SetupLightVolumeBatch(i->volumeBatches_[j]);
1707                             i->volumeBatches_[j].Draw(this, camera_, false);
1708                         }
1709 
1710                         passCommand_ = 0;
1711                     }
1712 
1713                     graphics_->SetScissorTest(false);
1714                     graphics_->SetStencilTest(false);
1715                 }
1716                 break;
1717 
1718             case CMD_RENDERUI:
1719                 {
1720                     SetRenderTargets(command);
1721                     GetSubsystem<UI>()->Render(false);
1722                 }
1723                 break;
1724 
1725             case CMD_SENDEVENT:
1726                 {
1727                     using namespace RenderPathEvent;
1728 
1729                     VariantMap& eventData = GetEventDataMap();
1730                     eventData[P_NAME] = command.eventName_;
1731                     renderer_->SendEvent(E_RENDERPATHEVENT, eventData);
1732                 }
1733                 break;
1734 
1735             default:
1736                 break;
1737             }
1738 
1739             // If current command output to the viewport, mark it modified
1740             if (viewportWrite)
1741                 viewportModified = true;
1742         }
1743     }
1744 }
1745 
SetRenderTargets(RenderPathCommand & command)1746 void View::SetRenderTargets(RenderPathCommand& command)
1747 {
1748     unsigned index = 0;
1749     bool useColorWrite = true;
1750     bool useCustomDepth = false;
1751     bool useViewportOutput = false;
1752 
1753     while (index < command.outputs_.Size())
1754     {
1755         if (!command.outputs_[index].first_.Compare("viewport", false))
1756         {
1757             graphics_->SetRenderTarget(index, currentRenderTarget_);
1758             useViewportOutput = true;
1759         }
1760         else
1761         {
1762             Texture* texture = FindNamedTexture(command.outputs_[index].first_, true, false);
1763 
1764             // Check for depth only rendering (by specifying a depth texture as the sole output)
1765             if (!index && command.outputs_.Size() == 1 && texture && (texture->GetFormat() == Graphics::GetReadableDepthFormat() ||
1766                                                                       texture->GetFormat() == Graphics::GetDepthStencilFormat()))
1767             {
1768                 useColorWrite = false;
1769                 useCustomDepth = true;
1770 #if !defined(URHO3D_OPENGL) && !defined(URHO3D_D3D11)
1771                 // On D3D9 actual depth-only rendering is illegal, we need a color rendertarget
1772                 if (!depthOnlyDummyTexture_)
1773                 {
1774                     depthOnlyDummyTexture_ = renderer_->GetScreenBuffer(texture->GetWidth(), texture->GetHeight(),
1775                         graphics_->GetDummyColorFormat(), texture->GetMultiSample(), texture->GetAutoResolve(), false, false, false);
1776                 }
1777 #endif
1778                 graphics_->SetRenderTarget(0, GetRenderSurfaceFromTexture(depthOnlyDummyTexture_));
1779                 graphics_->SetDepthStencil(GetRenderSurfaceFromTexture(texture));
1780             }
1781             else
1782                 graphics_->SetRenderTarget(index, GetRenderSurfaceFromTexture(texture, command.outputs_[index].second_));
1783         }
1784 
1785         ++index;
1786     }
1787 
1788     while (index < MAX_RENDERTARGETS)
1789     {
1790         graphics_->SetRenderTarget(index, (RenderSurface*)0);
1791         ++index;
1792     }
1793 
1794     if (command.depthStencilName_.Length())
1795     {
1796         Texture* depthTexture = FindNamedTexture(command.depthStencilName_, true, false);
1797         if (depthTexture)
1798         {
1799             useCustomDepth = true;
1800             lastCustomDepthSurface_ = GetRenderSurfaceFromTexture(depthTexture);
1801             graphics_->SetDepthStencil(lastCustomDepthSurface_);
1802         }
1803     }
1804 
1805     // When rendering to the final destination rendertarget, use the actual viewport. Otherwise texture rendertargets should use
1806     // their full size as the viewport
1807     IntVector2 rtSizeNow = graphics_->GetRenderTargetDimensions();
1808     IntRect viewport = (useViewportOutput && currentRenderTarget_ == renderTarget_) ? viewRect_ : IntRect(0, 0, rtSizeNow.x_,
1809         rtSizeNow.y_);
1810 
1811     if (!useCustomDepth)
1812         graphics_->SetDepthStencil(GetDepthStencil(graphics_->GetRenderTarget(0)));
1813     graphics_->SetViewport(viewport);
1814     graphics_->SetColorWrite(useColorWrite);
1815 }
1816 
SetTextures(RenderPathCommand & command)1817 bool View::SetTextures(RenderPathCommand& command)
1818 {
1819     bool allowDepthWrite = true;
1820 
1821     for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
1822     {
1823         if (command.textureNames_[i].Empty())
1824             continue;
1825 
1826         // Bind the rendered output
1827         if (!command.textureNames_[i].Compare("viewport", false))
1828         {
1829             graphics_->SetTexture(i, currentViewportTexture_);
1830             continue;
1831         }
1832 
1833 #ifdef DESKTOP_GRAPHICS
1834         Texture* texture = FindNamedTexture(command.textureNames_[i], false, i == TU_VOLUMEMAP);
1835 #else
1836         Texture* texture = FindNamedTexture(command.textureNames_[i], false, false);
1837 #endif
1838 
1839         if (texture)
1840         {
1841             graphics_->SetTexture(i, texture);
1842             // Check if the current depth stencil is being sampled
1843             if (graphics_->GetDepthStencil() && texture == graphics_->GetDepthStencil()->GetParentTexture())
1844                 allowDepthWrite = false;
1845         }
1846         else
1847         {
1848             // If requesting a texture fails, clear the texture name to prevent redundant attempts
1849             command.textureNames_[i] = String::EMPTY;
1850         }
1851     }
1852 
1853     return allowDepthWrite;
1854 }
1855 
RenderQuad(RenderPathCommand & command)1856 void View::RenderQuad(RenderPathCommand& command)
1857 {
1858     if (command.vertexShaderName_.Empty() || command.pixelShaderName_.Empty())
1859         return;
1860 
1861     // If shader can not be found, clear it from the command to prevent redundant attempts
1862     ShaderVariation* vs = graphics_->GetShader(VS, command.vertexShaderName_, command.vertexShaderDefines_);
1863     if (!vs)
1864         command.vertexShaderName_ = String::EMPTY;
1865     ShaderVariation* ps = graphics_->GetShader(PS, command.pixelShaderName_, command.pixelShaderDefines_);
1866     if (!ps)
1867         command.pixelShaderName_ = String::EMPTY;
1868 
1869     // Set shaders & shader parameters and textures
1870     graphics_->SetShaders(vs, ps);
1871 
1872     SetGlobalShaderParameters();
1873     SetCameraShaderParameters(camera_);
1874 
1875     // During renderpath commands the G-Buffer or viewport texture is assumed to always be viewport-sized
1876     IntRect viewport = graphics_->GetViewport();
1877     IntVector2 viewSize = IntVector2(viewport.Width(), viewport.Height());
1878     SetGBufferShaderParameters(viewSize, IntRect(0, 0, viewSize.x_, viewSize.y_));
1879 
1880     // Set per-rendertarget inverse size / offset shader parameters as necessary
1881     for (unsigned i = 0; i < renderPath_->renderTargets_.Size(); ++i)
1882     {
1883         const RenderTargetInfo& rtInfo = renderPath_->renderTargets_[i];
1884         if (!rtInfo.enabled_)
1885             continue;
1886 
1887         StringHash nameHash(rtInfo.name_);
1888         if (!renderTargets_.Contains(nameHash))
1889             continue;
1890 
1891         String invSizeName = rtInfo.name_ + "InvSize";
1892         String offsetsName = rtInfo.name_ + "Offsets";
1893         float width = (float)renderTargets_[nameHash]->GetWidth();
1894         float height = (float)renderTargets_[nameHash]->GetHeight();
1895 
1896         const Vector2& pixelUVOffset = Graphics::GetPixelUVOffset();
1897         graphics_->SetShaderParameter(invSizeName, Vector2(1.0f / width, 1.0f / height));
1898         graphics_->SetShaderParameter(offsetsName, Vector2(pixelUVOffset.x_ / width, pixelUVOffset.y_ / height));
1899     }
1900 
1901     // Set command's shader parameters last to allow them to override any of the above
1902     SetCommandShaderParameters(command);
1903 
1904     graphics_->SetBlendMode(command.blendMode_);
1905     graphics_->SetDepthTest(CMP_ALWAYS);
1906     graphics_->SetDepthWrite(false);
1907     graphics_->SetFillMode(FILL_SOLID);
1908     graphics_->SetLineAntiAlias(false);
1909     graphics_->SetClipPlane(false);
1910     graphics_->SetScissorTest(false);
1911     graphics_->SetStencilTest(false);
1912 
1913     DrawFullscreenQuad(false);
1914 }
1915 
IsNecessary(const RenderPathCommand & command)1916 bool View::IsNecessary(const RenderPathCommand& command)
1917 {
1918     return command.enabled_ && command.outputs_.Size() &&
1919            (command.type_ != CMD_SCENEPASS || !batchQueues_[command.passIndex_].IsEmpty());
1920 }
1921 
CheckViewportRead(const RenderPathCommand & command)1922 bool View::CheckViewportRead(const RenderPathCommand& command)
1923 {
1924     for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
1925     {
1926         if (!command.textureNames_[i].Empty() && !command.textureNames_[i].Compare("viewport", false))
1927             return true;
1928     }
1929 
1930     return false;
1931 }
1932 
CheckViewportWrite(const RenderPathCommand & command)1933 bool View::CheckViewportWrite(const RenderPathCommand& command)
1934 {
1935     for (unsigned i = 0; i < command.outputs_.Size(); ++i)
1936     {
1937         if (!command.outputs_[i].first_.Compare("viewport", false))
1938             return true;
1939     }
1940 
1941     return false;
1942 }
1943 
1944 
CheckPingpong(unsigned index)1945 bool View::CheckPingpong(unsigned index)
1946 {
1947     // Current command must be a viewport-reading & writing quad to begin the pingpong chain
1948     RenderPathCommand& current = renderPath_->commands_[index];
1949     if (current.type_ != CMD_QUAD || !CheckViewportRead(current) || !CheckViewportWrite(current))
1950         return false;
1951 
1952     // If there are commands other than quads that target the viewport, we must keep rendering to the final target and resolving
1953     // to a viewport texture when necessary instead of pingponging, as a scene pass is not guaranteed to fill the entire viewport
1954     for (unsigned i = index + 1; i < renderPath_->commands_.Size(); ++i)
1955     {
1956         RenderPathCommand& command = renderPath_->commands_[i];
1957         if (!IsNecessary(command))
1958             continue;
1959         if (CheckViewportWrite(command))
1960         {
1961             if (command.type_ != CMD_QUAD)
1962                 return false;
1963         }
1964     }
1965 
1966     return true;
1967 }
1968 
AllocateScreenBuffers()1969 void View::AllocateScreenBuffers()
1970 {
1971     View* actualView = sourceView_ ? sourceView_ : this;
1972 
1973     bool hasScenePassToRTs = false;
1974     bool hasCustomDepth = false;
1975     bool hasViewportRead = false;
1976     bool hasPingpong = false;
1977     bool needSubstitute = false;
1978     unsigned numViewportTextures = 0;
1979     depthOnlyDummyTexture_ = 0;
1980     lastCustomDepthSurface_ = 0;
1981 
1982     // Check for commands with special meaning: has custom depth, renders a scene pass to other than the destination viewport,
1983     // read the viewport, or pingpong between viewport textures. These may trigger the need to substitute the destination RT
1984     for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
1985     {
1986         const RenderPathCommand& command = renderPath_->commands_[i];
1987         if (!actualView->IsNecessary(command))
1988             continue;
1989         if (!hasViewportRead && CheckViewportRead(command))
1990             hasViewportRead = true;
1991         if (!hasPingpong && CheckPingpong(i))
1992             hasPingpong = true;
1993         if (command.depthStencilName_.Length())
1994             hasCustomDepth = true;
1995         if (!hasScenePassToRTs && command.type_ == CMD_SCENEPASS)
1996         {
1997             for (unsigned j = 0; j < command.outputs_.Size(); ++j)
1998             {
1999                 if (command.outputs_[j].first_.Compare("viewport", false))
2000                 {
2001                     hasScenePassToRTs = true;
2002                     break;
2003                 }
2004             }
2005         }
2006     }
2007 
2008 #ifdef URHO3D_OPENGL
2009     // Due to FBO limitations, in OpenGL deferred modes need to render to texture first and then blit to the backbuffer
2010     // Also, if rendering to a texture with full deferred rendering, it must be RGBA to comply with the rest of the buffers,
2011     // unless using OpenGL 3
2012     if (((deferred_ || hasScenePassToRTs) && !renderTarget_) || (!Graphics::GetGL3Support() && deferredAmbient_ && renderTarget_
2013         && renderTarget_->GetParentTexture()->GetFormat() != Graphics::GetRGBAFormat()))
2014             needSubstitute = true;
2015     // Also need substitute if rendering to backbuffer using a custom (readable) depth buffer
2016     if (!renderTarget_ && hasCustomDepth)
2017         needSubstitute = true;
2018 #endif
2019     // If backbuffer is antialiased when using deferred rendering, need to reserve a buffer
2020     if (deferred_ && !renderTarget_ && graphics_->GetMultiSample() > 1)
2021         needSubstitute = true;
2022     // If viewport is smaller than whole texture/backbuffer in deferred rendering, need to reserve a buffer, as the G-buffer
2023     // textures will be sized equal to the viewport
2024     if (viewSize_.x_ < rtSize_.x_ || viewSize_.y_ < rtSize_.y_)
2025     {
2026         if (deferred_ || hasScenePassToRTs || hasCustomDepth)
2027             needSubstitute = true;
2028     }
2029 
2030     // Follow final rendertarget format, or use RGB to match the backbuffer format
2031     unsigned format = renderTarget_ ? renderTarget_->GetParentTexture()->GetFormat() : Graphics::GetRGBFormat();
2032 
2033     // If HDR rendering is enabled use RGBA16f and reserve a buffer
2034     if (renderer_->GetHDRRendering())
2035     {
2036         format = Graphics::GetRGBAFloat16Format();
2037         needSubstitute = true;
2038     }
2039 
2040 #ifdef URHO3D_OPENGL
2041     // On OpenGL 2 ensure that all MRT buffers are RGBA in deferred rendering
2042     if (deferred_ && !renderer_->GetHDRRendering() && !Graphics::GetGL3Support())
2043         format = Graphics::GetRGBAFormat();
2044 #endif
2045 
2046     if (hasViewportRead)
2047     {
2048         ++numViewportTextures;
2049 
2050         // If OpenGL ES, use substitute target to avoid resolve from the backbuffer, which may be slow. However if multisampling
2051         // is specified, there is no choice
2052 #ifdef GL_ES_VERSION_2_0
2053         if (!renderTarget_ && graphics_->GetMultiSample() < 2)
2054             needSubstitute = true;
2055 #endif
2056 
2057         // If we have viewport read and target is a cube map, must allocate a substitute target instead as BlitFramebuffer()
2058         // does not support reading a cube map
2059         if (renderTarget_ && renderTarget_->GetParentTexture()->GetType() == TextureCube::GetTypeStatic())
2060             needSubstitute = true;
2061 
2062         // If rendering to a texture, but the viewport is less than the whole texture, use a substitute to ensure
2063         // postprocessing shaders will never read outside the viewport
2064         if (renderTarget_ && (viewSize_.x_ < renderTarget_->GetWidth() || viewSize_.y_ < renderTarget_->GetHeight()))
2065             needSubstitute = true;
2066 
2067         if (hasPingpong && !needSubstitute)
2068             ++numViewportTextures;
2069     }
2070 
2071     // Allocate screen buffers. Enable filtering in case the quad commands need that
2072     // Follow the sRGB mode of the destination render target
2073     bool sRGB = renderTarget_ ? renderTarget_->GetParentTexture()->GetSRGB() : graphics_->GetSRGB();
2074     int multiSample = renderTarget_ ? renderTarget_->GetMultiSample() : graphics_->GetMultiSample();
2075     bool autoResolve = renderTarget_ ? renderTarget_->GetAutoResolve() : true;
2076     substituteRenderTarget_ = needSubstitute ? GetRenderSurfaceFromTexture(renderer_->GetScreenBuffer(viewSize_.x_, viewSize_.y_,
2077         format, multiSample, autoResolve, false, true, sRGB)) : (RenderSurface*)0;
2078     for (unsigned i = 0; i < MAX_VIEWPORT_TEXTURES; ++i)
2079     {
2080         viewportTextures_[i] = i < numViewportTextures ? renderer_->GetScreenBuffer(viewSize_.x_, viewSize_.y_, format, multiSample,
2081             autoResolve, false, true, sRGB) : (Texture*)0;
2082     }
2083     // If using a substitute render target and pingponging, the substitute can act as the second viewport texture
2084     if (numViewportTextures == 1 && substituteRenderTarget_)
2085         viewportTextures_[1] = substituteRenderTarget_->GetParentTexture();
2086 
2087     // Allocate extra render targets defined by the render path
2088     for (unsigned i = 0; i < renderPath_->renderTargets_.Size(); ++i)
2089     {
2090         const RenderTargetInfo& rtInfo = renderPath_->renderTargets_[i];
2091         if (!rtInfo.enabled_)
2092             continue;
2093 
2094         float width = rtInfo.size_.x_;
2095         float height = rtInfo.size_.y_;
2096 
2097         if (rtInfo.sizeMode_ == SIZE_VIEWPORTDIVISOR)
2098         {
2099             width = (float)viewSize_.x_ / Max(width, M_EPSILON);
2100             height = (float)viewSize_.y_ / Max(height, M_EPSILON);
2101         }
2102         else if (rtInfo.sizeMode_ == SIZE_VIEWPORTMULTIPLIER)
2103         {
2104             width = (float)viewSize_.x_ * width;
2105             height = (float)viewSize_.y_ * height;
2106         }
2107 
2108         int intWidth = (int)(width + 0.5f);
2109         int intHeight = (int)(height + 0.5f);
2110 
2111         // If the rendertarget is persistent, key it with a hash derived from the RT name and the view's pointer
2112         renderTargets_[rtInfo.name_] =
2113             renderer_->GetScreenBuffer(intWidth, intHeight, rtInfo.format_, rtInfo.multiSample_, rtInfo.autoResolve_,
2114                 rtInfo.cubemap_, rtInfo.filtered_, rtInfo.sRGB_, rtInfo.persistent_ ? StringHash(rtInfo.name_).Value()
2115                 + (unsigned)(size_t)this : 0);
2116     }
2117 }
2118 
BlitFramebuffer(Texture * source,RenderSurface * destination,bool depthWrite)2119 void View::BlitFramebuffer(Texture* source, RenderSurface* destination, bool depthWrite)
2120 {
2121     if (!source)
2122         return;
2123 
2124     URHO3D_PROFILE(BlitFramebuffer);
2125 
2126     // If blitting to the destination rendertarget, use the actual viewport. Intermediate textures on the other hand
2127     // are always viewport-sized
2128     IntVector2 srcSize(source->GetWidth(), source->GetHeight());
2129     IntVector2 destSize = destination ? IntVector2(destination->GetWidth(), destination->GetHeight()) : IntVector2(
2130         graphics_->GetWidth(), graphics_->GetHeight());
2131 
2132     IntRect srcRect = (GetRenderSurfaceFromTexture(source) == renderTarget_) ? viewRect_ : IntRect(0, 0, srcSize.x_, srcSize.y_);
2133     IntRect destRect = (destination == renderTarget_) ? viewRect_ : IntRect(0, 0, destSize.x_, destSize.y_);
2134 
2135     graphics_->SetBlendMode(BLEND_REPLACE);
2136     graphics_->SetDepthTest(CMP_ALWAYS);
2137     graphics_->SetDepthWrite(depthWrite);
2138     graphics_->SetFillMode(FILL_SOLID);
2139     graphics_->SetLineAntiAlias(false);
2140     graphics_->SetClipPlane(false);
2141     graphics_->SetScissorTest(false);
2142     graphics_->SetStencilTest(false);
2143     graphics_->SetRenderTarget(0, destination);
2144     for (unsigned i = 1; i < MAX_RENDERTARGETS; ++i)
2145         graphics_->SetRenderTarget(i, (RenderSurface*)0);
2146     graphics_->SetDepthStencil(GetDepthStencil(destination));
2147     graphics_->SetViewport(destRect);
2148 
2149     static const String shaderName("CopyFramebuffer");
2150     graphics_->SetShaders(graphics_->GetShader(VS, shaderName), graphics_->GetShader(PS, shaderName));
2151 
2152     SetGBufferShaderParameters(srcSize, srcRect);
2153 
2154     graphics_->SetTexture(TU_DIFFUSE, source);
2155     DrawFullscreenQuad(true);
2156 }
2157 
DrawFullscreenQuad(bool setIdentityProjection)2158 void View::DrawFullscreenQuad(bool setIdentityProjection)
2159 {
2160     Geometry* geometry = renderer_->GetQuadGeometry();
2161 
2162     // If no camera, no choice but to use identity projection
2163     if (!camera_)
2164         setIdentityProjection = true;
2165 
2166     if (setIdentityProjection)
2167     {
2168         Matrix3x4 model = Matrix3x4::IDENTITY;
2169         Matrix4 projection = Matrix4::IDENTITY;
2170 #ifdef URHO3D_OPENGL
2171         if (camera_ && camera_->GetFlipVertical())
2172             projection.m11_ = -1.0f;
2173         model.m23_ = 0.0f;
2174 #else
2175         model.m23_ = 0.5f;
2176 #endif
2177 
2178         graphics_->SetShaderParameter(VSP_MODEL, model);
2179         graphics_->SetShaderParameter(VSP_VIEWPROJ, projection);
2180     }
2181     else
2182         graphics_->SetShaderParameter(VSP_MODEL, Light::GetFullscreenQuadTransform(camera_));
2183 
2184     graphics_->SetCullMode(CULL_NONE);
2185     graphics_->ClearTransformSources();
2186 
2187     geometry->Draw(graphics_);
2188 }
2189 
UpdateOccluders(PODVector<Drawable * > & occluders,Camera * camera)2190 void View::UpdateOccluders(PODVector<Drawable*>& occluders, Camera* camera)
2191 {
2192     float occluderSizeThreshold_ = renderer_->GetOccluderSizeThreshold();
2193     float halfViewSize = camera->GetHalfViewSize();
2194     float invOrthoSize = 1.0f / camera->GetOrthoSize();
2195 
2196     for (PODVector<Drawable*>::Iterator i = occluders.Begin(); i != occluders.End();)
2197     {
2198         Drawable* occluder = *i;
2199         bool erase = false;
2200 
2201         if (!occluder->IsInView(frame_, true))
2202             occluder->UpdateBatches(frame_);
2203 
2204         // Check occluder's draw distance (in main camera view)
2205         float maxDistance = occluder->GetDrawDistance();
2206         if (maxDistance <= 0.0f || occluder->GetDistance() <= maxDistance)
2207         {
2208             // Check that occluder is big enough on the screen
2209             const BoundingBox& box = occluder->GetWorldBoundingBox();
2210             float diagonal = box.Size().Length();
2211             float compare;
2212             if (!camera->IsOrthographic())
2213             {
2214                 // Occluders which are near the camera are more useful then occluders at the end of the camera's draw distance
2215                 float cameraMaxDistanceFraction = occluder->GetDistance() / camera->GetFarClip();
2216                 compare = diagonal * halfViewSize / (occluder->GetDistance() * cameraMaxDistanceFraction);
2217 
2218                 // Give higher priority to occluders which the camera is inside their AABB
2219                 const Vector3& cameraPos = camera->GetNode() ? camera->GetNode()->GetWorldPosition() : Vector3::ZERO;
2220                 if (box.IsInside(cameraPos))
2221                     compare *= diagonal;    // size^2
2222             }
2223             else
2224                 compare = diagonal * invOrthoSize;
2225 
2226             if (compare < occluderSizeThreshold_)
2227                 erase = true;
2228             else
2229             {
2230                 // Best occluders have big triangles (low density)
2231                 float density = occluder->GetNumOccluderTriangles() / diagonal;
2232                 // Lower value is higher priority
2233                 occluder->SetSortValue(density / compare);
2234             }
2235         }
2236         else
2237             erase = true;
2238 
2239         if (erase)
2240             i = occluders.Erase(i);
2241         else
2242             ++i;
2243     }
2244 
2245     // Sort occluders so that if triangle budget is exceeded, best occluders have been drawn
2246     if (occluders.Size())
2247         Sort(occluders.Begin(), occluders.End(), CompareDrawables);
2248 }
2249 
DrawOccluders(OcclusionBuffer * buffer,const PODVector<Drawable * > & occluders)2250 void View::DrawOccluders(OcclusionBuffer* buffer, const PODVector<Drawable*>& occluders)
2251 {
2252     buffer->SetMaxTriangles((unsigned)maxOccluderTriangles_);
2253     buffer->Clear();
2254 
2255     if (!buffer->IsThreaded())
2256     {
2257         // If not threaded, draw occluders one by one and test the next occluder against already rasterized depth
2258         for (unsigned i = 0; i < occluders.Size(); ++i)
2259         {
2260             Drawable* occluder = occluders[i];
2261             if (i > 0)
2262             {
2263                 // For subsequent occluders, do a test against the pixel-level occlusion buffer to see if rendering is necessary
2264                 if (!buffer->IsVisible(occluder->GetWorldBoundingBox()))
2265                     continue;
2266             }
2267 
2268             // Check for running out of triangles
2269             ++activeOccluders_;
2270             bool success = occluder->DrawOcclusion(buffer);
2271             // Draw triangles submitted by this occluder
2272             buffer->DrawTriangles();
2273             if (!success)
2274                 break;
2275         }
2276     }
2277     else
2278     {
2279         // In threaded mode submit all triangles first, then render (cannot test in this case)
2280         for (unsigned i = 0; i < occluders.Size(); ++i)
2281         {
2282             // Check for running out of triangles
2283             ++activeOccluders_;
2284             if (!occluders[i]->DrawOcclusion(buffer))
2285                 break;
2286         }
2287 
2288         buffer->DrawTriangles();
2289     }
2290 
2291     // Finally build the depth mip levels
2292     buffer->BuildDepthHierarchy();
2293 }
2294 
ProcessLight(LightQueryResult & query,unsigned threadIndex)2295 void View::ProcessLight(LightQueryResult& query, unsigned threadIndex)
2296 {
2297     Light* light = query.light_;
2298     LightType type = light->GetLightType();
2299     unsigned lightMask = light->GetLightMask();
2300     const Frustum& frustum = cullCamera_->GetFrustum();
2301 
2302     // Check if light should be shadowed
2303     bool isShadowed = drawShadows_ && light->GetCastShadows() && !light->GetPerVertex() && light->GetShadowIntensity() < 1.0f;
2304     // If shadow distance non-zero, check it
2305     if (isShadowed && light->GetShadowDistance() > 0.0f && light->GetDistance() > light->GetShadowDistance())
2306         isShadowed = false;
2307     // OpenGL ES can not support point light shadows
2308 #ifdef GL_ES_VERSION_2_0
2309     if (isShadowed && type == LIGHT_POINT)
2310         isShadowed = false;
2311 #endif
2312     // Get lit geometries. They must match the light mask and be inside the main camera frustum to be considered
2313     PODVector<Drawable*>& tempDrawables = tempDrawables_[threadIndex];
2314     query.litGeometries_.Clear();
2315 
2316     switch (type)
2317     {
2318     case LIGHT_DIRECTIONAL:
2319         for (unsigned i = 0; i < geometries_.Size(); ++i)
2320         {
2321             if (GetLightMask(geometries_[i]) & lightMask)
2322                 query.litGeometries_.Push(geometries_[i]);
2323         }
2324         break;
2325 
2326     case LIGHT_SPOT:
2327         {
2328             FrustumOctreeQuery octreeQuery(tempDrawables, light->GetFrustum(), DRAWABLE_GEOMETRY,
2329                 cullCamera_->GetViewMask());
2330             octree_->GetDrawables(octreeQuery);
2331             for (unsigned i = 0; i < tempDrawables.Size(); ++i)
2332             {
2333                 if (tempDrawables[i]->IsInView(frame_) && (GetLightMask(tempDrawables[i]) & lightMask))
2334                     query.litGeometries_.Push(tempDrawables[i]);
2335             }
2336         }
2337         break;
2338 
2339     case LIGHT_POINT:
2340         {
2341             SphereOctreeQuery octreeQuery(tempDrawables, Sphere(light->GetNode()->GetWorldPosition(), light->GetRange()),
2342                 DRAWABLE_GEOMETRY, cullCamera_->GetViewMask());
2343             octree_->GetDrawables(octreeQuery);
2344             for (unsigned i = 0; i < tempDrawables.Size(); ++i)
2345             {
2346                 if (tempDrawables[i]->IsInView(frame_) && (GetLightMask(tempDrawables[i]) & lightMask))
2347                     query.litGeometries_.Push(tempDrawables[i]);
2348             }
2349         }
2350         break;
2351     }
2352 
2353     // If no lit geometries or not shadowed, no need to process shadow cameras
2354     if (query.litGeometries_.Empty() || !isShadowed)
2355     {
2356         query.numSplits_ = 0;
2357         return;
2358     }
2359 
2360     // Determine number of shadow cameras and setup their initial positions
2361     SetupShadowCameras(query);
2362 
2363     // Process each split for shadow casters
2364     query.shadowCasters_.Clear();
2365     for (unsigned i = 0; i < query.numSplits_; ++i)
2366     {
2367         Camera* shadowCamera = query.shadowCameras_[i];
2368         const Frustum& shadowCameraFrustum = shadowCamera->GetFrustum();
2369         query.shadowCasterBegin_[i] = query.shadowCasterEnd_[i] = query.shadowCasters_.Size();
2370 
2371         // For point light check that the face is visible: if not, can skip the split
2372         if (type == LIGHT_POINT && frustum.IsInsideFast(BoundingBox(shadowCameraFrustum)) == OUTSIDE)
2373             continue;
2374 
2375         // For directional light check that the split is inside the visible scene: if not, can skip the split
2376         if (type == LIGHT_DIRECTIONAL)
2377         {
2378             if (minZ_ > query.shadowFarSplits_[i])
2379                 continue;
2380             if (maxZ_ < query.shadowNearSplits_[i])
2381                 continue;
2382 
2383             // Reuse lit geometry query for all except directional lights
2384             ShadowCasterOctreeQuery query(tempDrawables, shadowCameraFrustum, DRAWABLE_GEOMETRY, cullCamera_->GetViewMask());
2385             octree_->GetDrawables(query);
2386         }
2387 
2388         // Check which shadow casters actually contribute to the shadowing
2389         ProcessShadowCasters(query, tempDrawables, i);
2390     }
2391 
2392     // If no shadow casters, the light can be rendered unshadowed. At this point we have not allocated a shadow map yet, so the
2393     // only cost has been the shadow camera setup & queries
2394     if (query.shadowCasters_.Empty())
2395         query.numSplits_ = 0;
2396 }
2397 
ProcessShadowCasters(LightQueryResult & query,const PODVector<Drawable * > & drawables,unsigned splitIndex)2398 void View::ProcessShadowCasters(LightQueryResult& query, const PODVector<Drawable*>& drawables, unsigned splitIndex)
2399 {
2400     Light* light = query.light_;
2401     unsigned lightMask = light->GetLightMask();
2402 
2403     Camera* shadowCamera = query.shadowCameras_[splitIndex];
2404     const Frustum& shadowCameraFrustum = shadowCamera->GetFrustum();
2405     const Matrix3x4& lightView = shadowCamera->GetView();
2406     const Matrix4& lightProj = shadowCamera->GetProjection();
2407     LightType type = light->GetLightType();
2408 
2409     query.shadowCasterBox_[splitIndex].Clear();
2410 
2411     // Transform scene frustum into shadow camera's view space for shadow caster visibility check. For point & spot lights,
2412     // we can use the whole scene frustum. For directional lights, use the intersection of the scene frustum and the split
2413     // frustum, so that shadow casters do not get rendered into unnecessary splits
2414     Frustum lightViewFrustum;
2415     if (type != LIGHT_DIRECTIONAL)
2416         lightViewFrustum = cullCamera_->GetSplitFrustum(minZ_, maxZ_).Transformed(lightView);
2417     else
2418         lightViewFrustum = cullCamera_->GetSplitFrustum(Max(minZ_, query.shadowNearSplits_[splitIndex]),
2419             Min(maxZ_, query.shadowFarSplits_[splitIndex])).Transformed(lightView);
2420 
2421     BoundingBox lightViewFrustumBox(lightViewFrustum);
2422 
2423     // Check for degenerate split frustum: in that case there is no need to get shadow casters
2424     if (lightViewFrustum.vertices_[0] == lightViewFrustum.vertices_[4])
2425         return;
2426 
2427     BoundingBox lightViewBox;
2428     BoundingBox lightProjBox;
2429 
2430     for (PODVector<Drawable*>::ConstIterator i = drawables.Begin(); i != drawables.End(); ++i)
2431     {
2432         Drawable* drawable = *i;
2433         // In case this is a point or spot light query result reused for optimization, we may have non-shadowcasters included.
2434         // Check for that first
2435         if (!drawable->GetCastShadows())
2436             continue;
2437         // Check shadow mask
2438         if (!(GetShadowMask(drawable) & lightMask))
2439             continue;
2440         // For point light, check that this drawable is inside the split shadow camera frustum
2441         if (type == LIGHT_POINT && shadowCameraFrustum.IsInsideFast(drawable->GetWorldBoundingBox()) == OUTSIDE)
2442             continue;
2443 
2444         // Check shadow distance
2445         // Note: as lights are processed threaded, it is possible a drawable's UpdateBatches() function is called several
2446         // times. However, this should not cause problems as no scene modification happens at this point.
2447         if (!drawable->IsInView(frame_, true))
2448             drawable->UpdateBatches(frame_);
2449         float maxShadowDistance = drawable->GetShadowDistance();
2450         float drawDistance = drawable->GetDrawDistance();
2451         if (drawDistance > 0.0f && (maxShadowDistance <= 0.0f || drawDistance < maxShadowDistance))
2452             maxShadowDistance = drawDistance;
2453         if (maxShadowDistance > 0.0f && drawable->GetDistance() > maxShadowDistance)
2454             continue;
2455 
2456         // Project shadow caster bounding box to light view space for visibility check
2457         lightViewBox = drawable->GetWorldBoundingBox().Transformed(lightView);
2458 
2459         if (IsShadowCasterVisible(drawable, lightViewBox, shadowCamera, lightView, lightViewFrustum, lightViewFrustumBox))
2460         {
2461             // Merge to shadow caster bounding box (only needed for focused spot lights) and add to the list
2462             if (type == LIGHT_SPOT && light->GetShadowFocus().focus_)
2463             {
2464                 lightProjBox = lightViewBox.Projected(lightProj);
2465                 query.shadowCasterBox_[splitIndex].Merge(lightProjBox);
2466             }
2467             query.shadowCasters_.Push(drawable);
2468         }
2469     }
2470 
2471     query.shadowCasterEnd_[splitIndex] = query.shadowCasters_.Size();
2472 }
2473 
IsShadowCasterVisible(Drawable * drawable,BoundingBox lightViewBox,Camera * shadowCamera,const Matrix3x4 & lightView,const Frustum & lightViewFrustum,const BoundingBox & lightViewFrustumBox)2474 bool View::IsShadowCasterVisible(Drawable* drawable, BoundingBox lightViewBox, Camera* shadowCamera, const Matrix3x4& lightView,
2475     const Frustum& lightViewFrustum, const BoundingBox& lightViewFrustumBox)
2476 {
2477     if (shadowCamera->IsOrthographic())
2478     {
2479         // Extrude the light space bounding box up to the far edge of the frustum's light space bounding box
2480         lightViewBox.max_.z_ = Max(lightViewBox.max_.z_, lightViewFrustumBox.max_.z_);
2481         return lightViewFrustum.IsInsideFast(lightViewBox) != OUTSIDE;
2482     }
2483     else
2484     {
2485         // If light is not directional, can do a simple check: if object is visible, its shadow is too
2486         if (drawable->IsInView(frame_))
2487             return true;
2488 
2489         // For perspective lights, extrusion direction depends on the position of the shadow caster
2490         Vector3 center = lightViewBox.Center();
2491         Ray extrusionRay(center, center);
2492 
2493         float extrusionDistance = shadowCamera->GetFarClip();
2494         float originalDistance = Clamp(center.Length(), M_EPSILON, extrusionDistance);
2495 
2496         // Because of the perspective, the bounding box must also grow when it is extruded to the distance
2497         float sizeFactor = extrusionDistance / originalDistance;
2498 
2499         // Calculate the endpoint box and merge it to the original. Because it's axis-aligned, it will be larger
2500         // than necessary, so the test will be conservative
2501         Vector3 newCenter = extrusionDistance * extrusionRay.direction_;
2502         Vector3 newHalfSize = lightViewBox.Size() * sizeFactor * 0.5f;
2503         BoundingBox extrudedBox(newCenter - newHalfSize, newCenter + newHalfSize);
2504         lightViewBox.Merge(extrudedBox);
2505 
2506         return lightViewFrustum.IsInsideFast(lightViewBox) != OUTSIDE;
2507     }
2508 }
2509 
GetShadowMapViewport(Light * light,unsigned splitIndex,Texture2D * shadowMap)2510 IntRect View::GetShadowMapViewport(Light* light, unsigned splitIndex, Texture2D* shadowMap)
2511 {
2512     unsigned width = (unsigned)shadowMap->GetWidth();
2513     unsigned height = (unsigned)shadowMap->GetHeight();
2514 
2515     switch (light->GetLightType())
2516     {
2517     case LIGHT_DIRECTIONAL:
2518         {
2519             int numSplits = light->GetNumShadowSplits();
2520             if (numSplits == 1)
2521                 return IntRect(0, 0, width, height);
2522             else if (numSplits == 2)
2523                 return IntRect(splitIndex * width / 2, 0, (splitIndex + 1) * width / 2, height);
2524             else
2525                 return IntRect((splitIndex & 1) * width / 2, (splitIndex / 2) * height / 2, ((splitIndex & 1) + 1) * width / 2,
2526                     (splitIndex / 2 + 1) * height / 2);
2527         }
2528 
2529     case LIGHT_SPOT:
2530         return IntRect(0, 0, width, height);
2531 
2532     case LIGHT_POINT:
2533         return IntRect((splitIndex & 1) * width / 2, (splitIndex / 2) * height / 3, ((splitIndex & 1) + 1) * width / 2,
2534             (splitIndex / 2 + 1) * height / 3);
2535     }
2536 
2537     return IntRect();
2538 }
2539 
SetupShadowCameras(LightQueryResult & query)2540 void View::SetupShadowCameras(LightQueryResult& query)
2541 {
2542     Light* light = query.light_;
2543 
2544     int splits = 0;
2545 
2546     switch (light->GetLightType())
2547     {
2548     case LIGHT_DIRECTIONAL:
2549         {
2550             const CascadeParameters& cascade = light->GetShadowCascade();
2551 
2552             float nearSplit = cullCamera_->GetNearClip();
2553             float farSplit;
2554             int numSplits = light->GetNumShadowSplits();
2555 
2556             while (splits < numSplits)
2557             {
2558                 // If split is completely beyond camera far clip, we are done
2559                 if (nearSplit > cullCamera_->GetFarClip())
2560                     break;
2561 
2562                 farSplit = Min(cullCamera_->GetFarClip(), cascade.splits_[splits]);
2563                 if (farSplit <= nearSplit)
2564                     break;
2565 
2566                 // Setup the shadow camera for the split
2567                 Camera* shadowCamera = renderer_->GetShadowCamera();
2568                 query.shadowCameras_[splits] = shadowCamera;
2569                 query.shadowNearSplits_[splits] = nearSplit;
2570                 query.shadowFarSplits_[splits] = farSplit;
2571                 SetupDirLightShadowCamera(shadowCamera, light, nearSplit, farSplit);
2572 
2573                 nearSplit = farSplit;
2574                 ++splits;
2575             }
2576         }
2577         break;
2578 
2579     case LIGHT_SPOT:
2580         {
2581             Camera* shadowCamera = renderer_->GetShadowCamera();
2582             query.shadowCameras_[0] = shadowCamera;
2583             Node* cameraNode = shadowCamera->GetNode();
2584             Node* lightNode = light->GetNode();
2585 
2586             cameraNode->SetTransform(lightNode->GetWorldPosition(), lightNode->GetWorldRotation());
2587             shadowCamera->SetNearClip(light->GetShadowNearFarRatio() * light->GetRange());
2588             shadowCamera->SetFarClip(light->GetRange());
2589             shadowCamera->SetFov(light->GetFov());
2590             shadowCamera->SetAspectRatio(light->GetAspectRatio());
2591 
2592             splits = 1;
2593         }
2594         break;
2595 
2596     case LIGHT_POINT:
2597         {
2598             for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
2599             {
2600                 Camera* shadowCamera = renderer_->GetShadowCamera();
2601                 query.shadowCameras_[i] = shadowCamera;
2602                 Node* cameraNode = shadowCamera->GetNode();
2603 
2604                 // When making a shadowed point light, align the splits along X, Y and Z axes regardless of light rotation
2605                 cameraNode->SetPosition(light->GetNode()->GetWorldPosition());
2606                 cameraNode->SetDirection(*directions[i]);
2607                 shadowCamera->SetNearClip(light->GetShadowNearFarRatio() * light->GetRange());
2608                 shadowCamera->SetFarClip(light->GetRange());
2609                 shadowCamera->SetFov(90.0f);
2610                 shadowCamera->SetAspectRatio(1.0f);
2611             }
2612 
2613             splits = MAX_CUBEMAP_FACES;
2614         }
2615         break;
2616     }
2617 
2618     query.numSplits_ = (unsigned)splits;
2619 }
2620 
SetupDirLightShadowCamera(Camera * shadowCamera,Light * light,float nearSplit,float farSplit)2621 void View::SetupDirLightShadowCamera(Camera* shadowCamera, Light* light, float nearSplit, float farSplit)
2622 {
2623     Node* shadowCameraNode = shadowCamera->GetNode();
2624     Node* lightNode = light->GetNode();
2625     float extrusionDistance = Min(cullCamera_->GetFarClip(), light->GetShadowMaxExtrusion());
2626     const FocusParameters& parameters = light->GetShadowFocus();
2627 
2628     // Calculate initial position & rotation
2629     Vector3 pos = cullCamera_->GetNode()->GetWorldPosition() - extrusionDistance * lightNode->GetWorldDirection();
2630     shadowCameraNode->SetTransform(pos, lightNode->GetWorldRotation());
2631 
2632     // Calculate main camera shadowed frustum in light's view space
2633     farSplit = Min(farSplit, cullCamera_->GetFarClip());
2634     // Use the scene Z bounds to limit frustum size if applicable
2635     if (parameters.focus_)
2636     {
2637         nearSplit = Max(minZ_, nearSplit);
2638         farSplit = Min(maxZ_, farSplit);
2639     }
2640 
2641     Frustum splitFrustum = cullCamera_->GetSplitFrustum(nearSplit, farSplit);
2642     Polyhedron frustumVolume;
2643     frustumVolume.Define(splitFrustum);
2644     // If focusing enabled, clip the frustum volume by the combined bounding box of the lit geometries within the frustum
2645     if (parameters.focus_)
2646     {
2647         BoundingBox litGeometriesBox;
2648         unsigned lightMask = light->GetLightMask();
2649 
2650         for (unsigned i = 0; i < geometries_.Size(); ++i)
2651         {
2652             Drawable* drawable = geometries_[i];
2653             if (drawable->GetMinZ() <= farSplit && drawable->GetMaxZ() >= nearSplit &&
2654                 (GetLightMask(drawable) & lightMask))
2655                 litGeometriesBox.Merge(drawable->GetWorldBoundingBox());
2656         }
2657 
2658         if (litGeometriesBox.Defined())
2659         {
2660             frustumVolume.Clip(litGeometriesBox);
2661             // If volume became empty, restore it to avoid zero size
2662             if (frustumVolume.Empty())
2663                 frustumVolume.Define(splitFrustum);
2664         }
2665     }
2666 
2667     // Transform frustum volume to light space
2668     const Matrix3x4& lightView = shadowCamera->GetView();
2669     frustumVolume.Transform(lightView);
2670 
2671     // Fit the frustum volume inside a bounding box. If uniform size, use a sphere instead
2672     BoundingBox shadowBox;
2673     if (!parameters.nonUniform_)
2674         shadowBox.Define(Sphere(frustumVolume));
2675     else
2676         shadowBox.Define(frustumVolume);
2677 
2678     shadowCamera->SetOrthographic(true);
2679     shadowCamera->SetAspectRatio(1.0f);
2680     shadowCamera->SetNearClip(0.0f);
2681     shadowCamera->SetFarClip(shadowBox.max_.z_);
2682 
2683     // Center shadow camera on the bounding box. Can not snap to texels yet as the shadow map viewport is unknown
2684     QuantizeDirLightShadowCamera(shadowCamera, light, IntRect(0, 0, 0, 0), shadowBox);
2685 }
2686 
FinalizeShadowCamera(Camera * shadowCamera,Light * light,const IntRect & shadowViewport,const BoundingBox & shadowCasterBox)2687 void View::FinalizeShadowCamera(Camera* shadowCamera, Light* light, const IntRect& shadowViewport,
2688     const BoundingBox& shadowCasterBox)
2689 {
2690     const FocusParameters& parameters = light->GetShadowFocus();
2691     float shadowMapWidth = (float)(shadowViewport.Width());
2692     LightType type = light->GetLightType();
2693 
2694     if (type == LIGHT_DIRECTIONAL)
2695     {
2696         BoundingBox shadowBox;
2697         shadowBox.max_.y_ = shadowCamera->GetOrthoSize() * 0.5f;
2698         shadowBox.max_.x_ = shadowCamera->GetAspectRatio() * shadowBox.max_.y_;
2699         shadowBox.min_.y_ = -shadowBox.max_.y_;
2700         shadowBox.min_.x_ = -shadowBox.max_.x_;
2701 
2702         // Requantize and snap to shadow map texels
2703         QuantizeDirLightShadowCamera(shadowCamera, light, shadowViewport, shadowBox);
2704     }
2705 
2706     if (type == LIGHT_SPOT && parameters.focus_)
2707     {
2708         float viewSizeX = Max(Abs(shadowCasterBox.min_.x_), Abs(shadowCasterBox.max_.x_));
2709         float viewSizeY = Max(Abs(shadowCasterBox.min_.y_), Abs(shadowCasterBox.max_.y_));
2710         float viewSize = Max(viewSizeX, viewSizeY);
2711         // Scale the quantization parameters, because view size is in projection space (-1.0 - 1.0)
2712         float invOrthoSize = 1.0f / shadowCamera->GetOrthoSize();
2713         float quantize = parameters.quantize_ * invOrthoSize;
2714         float minView = parameters.minView_ * invOrthoSize;
2715 
2716         viewSize = Max(ceilf(viewSize / quantize) * quantize, minView);
2717         if (viewSize < 1.0f)
2718             shadowCamera->SetZoom(1.0f / viewSize);
2719     }
2720 
2721     // Perform a finalization step for all lights: ensure zoom out of 2 pixels to eliminate border filtering issues
2722     // For point lights use 4 pixels, as they must not cross sides of the virtual cube map (maximum 3x3 PCF)
2723     if (shadowCamera->GetZoom() >= 1.0f)
2724     {
2725         if (light->GetLightType() != LIGHT_POINT)
2726             shadowCamera->SetZoom(shadowCamera->GetZoom() * ((shadowMapWidth - 2.0f) / shadowMapWidth));
2727         else
2728         {
2729 #ifdef URHO3D_OPENGL
2730             shadowCamera->SetZoom(shadowCamera->GetZoom() * ((shadowMapWidth - 3.0f) / shadowMapWidth));
2731 #else
2732             shadowCamera->SetZoom(shadowCamera->GetZoom() * ((shadowMapWidth - 4.0f) / shadowMapWidth));
2733 #endif
2734         }
2735     }
2736 }
2737 
QuantizeDirLightShadowCamera(Camera * shadowCamera,Light * light,const IntRect & shadowViewport,const BoundingBox & viewBox)2738 void View::QuantizeDirLightShadowCamera(Camera* shadowCamera, Light* light, const IntRect& shadowViewport,
2739     const BoundingBox& viewBox)
2740 {
2741     Node* shadowCameraNode = shadowCamera->GetNode();
2742     const FocusParameters& parameters = light->GetShadowFocus();
2743     float shadowMapWidth = (float)(shadowViewport.Width());
2744 
2745     float minX = viewBox.min_.x_;
2746     float minY = viewBox.min_.y_;
2747     float maxX = viewBox.max_.x_;
2748     float maxY = viewBox.max_.y_;
2749 
2750     Vector2 center((minX + maxX) * 0.5f, (minY + maxY) * 0.5f);
2751     Vector2 viewSize(maxX - minX, maxY - minY);
2752 
2753     // Quantize size to reduce swimming
2754     // Note: if size is uniform and there is no focusing, quantization is unnecessary
2755     if (parameters.nonUniform_)
2756     {
2757         viewSize.x_ = ceilf(sqrtf(viewSize.x_ / parameters.quantize_));
2758         viewSize.y_ = ceilf(sqrtf(viewSize.y_ / parameters.quantize_));
2759         viewSize.x_ = Max(viewSize.x_ * viewSize.x_ * parameters.quantize_, parameters.minView_);
2760         viewSize.y_ = Max(viewSize.y_ * viewSize.y_ * parameters.quantize_, parameters.minView_);
2761     }
2762     else if (parameters.focus_)
2763     {
2764         viewSize.x_ = Max(viewSize.x_, viewSize.y_);
2765         viewSize.x_ = ceilf(sqrtf(viewSize.x_ / parameters.quantize_));
2766         viewSize.x_ = Max(viewSize.x_ * viewSize.x_ * parameters.quantize_, parameters.minView_);
2767         viewSize.y_ = viewSize.x_;
2768     }
2769 
2770     shadowCamera->SetOrthoSize(viewSize);
2771 
2772     // Center shadow camera to the view space bounding box
2773     Quaternion rot(shadowCameraNode->GetWorldRotation());
2774     Vector3 adjust(center.x_, center.y_, 0.0f);
2775     shadowCameraNode->Translate(rot * adjust, TS_WORLD);
2776 
2777     // If the shadow map viewport is known, snap to whole texels
2778     if (shadowMapWidth > 0.0f)
2779     {
2780         Vector3 viewPos(rot.Inverse() * shadowCameraNode->GetWorldPosition());
2781         // Take into account that shadow map border will not be used
2782         float invActualSize = 1.0f / (shadowMapWidth - 2.0f);
2783         Vector2 texelSize(viewSize.x_ * invActualSize, viewSize.y_ * invActualSize);
2784         Vector3 snap(-fmodf(viewPos.x_, texelSize.x_), -fmodf(viewPos.y_, texelSize.y_), 0.0f);
2785         shadowCameraNode->Translate(rot * snap, TS_WORLD);
2786     }
2787 }
2788 
FindZone(Drawable * drawable)2789 void View::FindZone(Drawable* drawable)
2790 {
2791     Vector3 center = drawable->GetWorldBoundingBox().Center();
2792     int bestPriority = M_MIN_INT;
2793     Zone* newZone = 0;
2794 
2795     // If bounding box center is in view, the zone assignment is conclusive also for next frames. Otherwise it is temporary
2796     // (possibly incorrect) and must be re-evaluated on the next frame
2797     bool temporary = !cullCamera_->GetFrustum().IsInside(center);
2798 
2799     // First check if the current zone remains a conclusive result
2800     Zone* lastZone = drawable->GetZone();
2801 
2802     if (lastZone && (lastZone->GetViewMask() & cullCamera_->GetViewMask()) && lastZone->GetPriority() >= highestZonePriority_ &&
2803         (drawable->GetZoneMask() & lastZone->GetZoneMask()) && lastZone->IsInside(center))
2804         newZone = lastZone;
2805     else
2806     {
2807         for (PODVector<Zone*>::Iterator i = zones_.Begin(); i != zones_.End(); ++i)
2808         {
2809             Zone* zone = *i;
2810             int priority = zone->GetPriority();
2811             if (priority > bestPriority && (drawable->GetZoneMask() & zone->GetZoneMask()) && zone->IsInside(center))
2812             {
2813                 newZone = zone;
2814                 bestPriority = priority;
2815             }
2816         }
2817     }
2818 
2819     drawable->SetZone(newZone, temporary);
2820 }
2821 
GetTechnique(Drawable * drawable,Material * material)2822 Technique* View::GetTechnique(Drawable* drawable, Material* material)
2823 {
2824     if (!material)
2825         return renderer_->GetDefaultMaterial()->GetTechniques()[0].technique_;
2826 
2827     const Vector<TechniqueEntry>& techniques = material->GetTechniques();
2828     // If only one technique, no choice
2829     if (techniques.Size() == 1)
2830         return techniques[0].technique_;
2831     else
2832     {
2833         float lodDistance = drawable->GetLodDistance();
2834 
2835         // Check for suitable technique. Techniques should be ordered like this:
2836         // Most distant & highest quality
2837         // Most distant & lowest quality
2838         // Second most distant & highest quality
2839         // ...
2840         for (unsigned i = 0; i < techniques.Size(); ++i)
2841         {
2842             const TechniqueEntry& entry = techniques[i];
2843             Technique* tech = entry.technique_;
2844 
2845             if (!tech || (!tech->IsSupported()) || materialQuality_ < entry.qualityLevel_)
2846                 continue;
2847             if (lodDistance >= entry.lodDistance_)
2848                 return tech;
2849         }
2850 
2851         // If no suitable technique found, fallback to the last
2852         return techniques.Size() ? techniques.Back().technique_ : (Technique*)0;
2853     }
2854 }
2855 
CheckMaterialForAuxView(Material * material)2856 void View::CheckMaterialForAuxView(Material* material)
2857 {
2858     const HashMap<TextureUnit, SharedPtr<Texture> >& textures = material->GetTextures();
2859 
2860     for (HashMap<TextureUnit, SharedPtr<Texture> >::ConstIterator i = textures.Begin(); i != textures.End(); ++i)
2861     {
2862         Texture* texture = i->second_.Get();
2863         if (texture && texture->GetUsage() == TEXTURE_RENDERTARGET)
2864         {
2865             // Have to check cube & 2D textures separately
2866             if (texture->GetType() == Texture2D::GetTypeStatic())
2867             {
2868                 Texture2D* tex2D = static_cast<Texture2D*>(texture);
2869                 RenderSurface* target = tex2D->GetRenderSurface();
2870                 if (target && target->GetUpdateMode() == SURFACE_UPDATEVISIBLE)
2871                     target->QueueUpdate();
2872             }
2873             else if (texture->GetType() == TextureCube::GetTypeStatic())
2874             {
2875                 TextureCube* texCube = static_cast<TextureCube*>(texture);
2876                 for (unsigned j = 0; j < MAX_CUBEMAP_FACES; ++j)
2877                 {
2878                     RenderSurface* target = texCube->GetRenderSurface((CubeMapFace)j);
2879                     if (target && target->GetUpdateMode() == SURFACE_UPDATEVISIBLE)
2880                         target->QueueUpdate();
2881                 }
2882             }
2883         }
2884     }
2885 
2886     // Flag as processed so we can early-out next time we come across this material on the same frame
2887     material->MarkForAuxView(frame_.frameNumber_);
2888 }
2889 
SetQueueShaderDefines(BatchQueue & queue,const RenderPathCommand & command)2890 void View::SetQueueShaderDefines(BatchQueue& queue, const RenderPathCommand& command)
2891 {
2892     String vsDefines = command.vertexShaderDefines_.Trimmed();
2893     String psDefines = command.pixelShaderDefines_.Trimmed();
2894     if (vsDefines.Length() || psDefines.Length())
2895     {
2896         queue.hasExtraDefines_ = true;
2897         queue.vsExtraDefines_ = vsDefines;
2898         queue.psExtraDefines_ = psDefines;
2899         queue.vsExtraDefinesHash_ = StringHash(vsDefines);
2900         queue.psExtraDefinesHash_ = StringHash(psDefines);
2901     }
2902     else
2903         queue.hasExtraDefines_ = false;
2904 }
2905 
AddBatchToQueue(BatchQueue & queue,Batch & batch,Technique * tech,bool allowInstancing,bool allowShadows)2906 void View::AddBatchToQueue(BatchQueue& queue, Batch& batch, Technique* tech, bool allowInstancing, bool allowShadows)
2907 {
2908     if (!batch.material_)
2909         batch.material_ = renderer_->GetDefaultMaterial();
2910 
2911     // Convert to instanced if possible
2912     if (allowInstancing && batch.geometryType_ == GEOM_STATIC && batch.geometry_->GetIndexBuffer())
2913         batch.geometryType_ = GEOM_INSTANCED;
2914 
2915     if (batch.geometryType_ == GEOM_INSTANCED)
2916     {
2917         BatchGroupKey key(batch);
2918 
2919         HashMap<BatchGroupKey, BatchGroup>::Iterator i = queue.batchGroups_.Find(key);
2920         if (i == queue.batchGroups_.End())
2921         {
2922             // Create a new group based on the batch
2923             // In case the group remains below the instancing limit, do not enable instancing shaders yet
2924             BatchGroup newGroup(batch);
2925             newGroup.geometryType_ = GEOM_STATIC;
2926             renderer_->SetBatchShaders(newGroup, tech, allowShadows, queue);
2927             newGroup.CalculateSortKey();
2928             i = queue.batchGroups_.Insert(MakePair(key, newGroup));
2929         }
2930 
2931         int oldSize = i->second_.instances_.Size();
2932         i->second_.AddTransforms(batch);
2933         // Convert to using instancing shaders when the instancing limit is reached
2934         if (oldSize < minInstances_ && (int)i->second_.instances_.Size() >= minInstances_)
2935         {
2936             i->second_.geometryType_ = GEOM_INSTANCED;
2937             renderer_->SetBatchShaders(i->second_, tech, allowShadows, queue);
2938             i->second_.CalculateSortKey();
2939         }
2940     }
2941     else
2942     {
2943         renderer_->SetBatchShaders(batch, tech, allowShadows, queue);
2944         batch.CalculateSortKey();
2945 
2946         // If batch is static with multiple world transforms and cannot instance, we must push copies of the batch individually
2947         if (batch.geometryType_ == GEOM_STATIC && batch.numWorldTransforms_ > 1)
2948         {
2949             unsigned numTransforms = batch.numWorldTransforms_;
2950             batch.numWorldTransforms_ = 1;
2951             for (unsigned i = 0; i < numTransforms; ++i)
2952             {
2953                 // Move the transform pointer to generate copies of the batch which only refer to 1 world transform
2954                 queue.batches_.Push(batch);
2955                 ++batch.worldTransform_;
2956             }
2957         }
2958         else
2959             queue.batches_.Push(batch);
2960     }
2961 }
2962 
PrepareInstancingBuffer()2963 void View::PrepareInstancingBuffer()
2964 {
2965     // Prepare instancing buffer from the source view
2966     /// \todo If rendering the same view several times back-to-back, would not need to refill the buffer
2967     if (sourceView_)
2968     {
2969         sourceView_->PrepareInstancingBuffer();
2970         return;
2971     }
2972 
2973     URHO3D_PROFILE(PrepareInstancingBuffer);
2974 
2975     unsigned totalInstances = 0;
2976 
2977     for (HashMap<unsigned, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
2978         totalInstances += i->second_.GetNumInstances();
2979 
2980     for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
2981     {
2982         for (unsigned j = 0; j < i->shadowSplits_.Size(); ++j)
2983             totalInstances += i->shadowSplits_[j].shadowBatches_.GetNumInstances();
2984         totalInstances += i->litBaseBatches_.GetNumInstances();
2985         totalInstances += i->litBatches_.GetNumInstances();
2986     }
2987 
2988     if (!totalInstances || !renderer_->ResizeInstancingBuffer(totalInstances))
2989         return;
2990 
2991     VertexBuffer* instancingBuffer = renderer_->GetInstancingBuffer();
2992     unsigned freeIndex = 0;
2993     void* dest = instancingBuffer->Lock(0, totalInstances, true);
2994     if (!dest)
2995         return;
2996 
2997     const unsigned stride = instancingBuffer->GetVertexSize();
2998     for (HashMap<unsigned, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
2999         i->second_.SetInstancingData(dest, stride, freeIndex);
3000 
3001     for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
3002     {
3003         for (unsigned j = 0; j < i->shadowSplits_.Size(); ++j)
3004             i->shadowSplits_[j].shadowBatches_.SetInstancingData(dest, stride, freeIndex);
3005         i->litBaseBatches_.SetInstancingData(dest, stride, freeIndex);
3006         i->litBatches_.SetInstancingData(dest, stride, freeIndex);
3007     }
3008 
3009     instancingBuffer->Unlock();
3010 }
3011 
SetupLightVolumeBatch(Batch & batch)3012 void View::SetupLightVolumeBatch(Batch& batch)
3013 {
3014     Light* light = batch.lightQueue_->light_;
3015     LightType type = light->GetLightType();
3016     Vector3 cameraPos = camera_->GetNode()->GetWorldPosition();
3017     float lightDist;
3018 
3019     graphics_->SetBlendMode(light->IsNegative() ? BLEND_SUBTRACT : BLEND_ADD);
3020     graphics_->SetDepthBias(0.0f, 0.0f);
3021     graphics_->SetDepthWrite(false);
3022     graphics_->SetFillMode(FILL_SOLID);
3023     graphics_->SetLineAntiAlias(false);
3024     graphics_->SetClipPlane(false);
3025 
3026     if (type != LIGHT_DIRECTIONAL)
3027     {
3028         if (type == LIGHT_POINT)
3029             lightDist = Sphere(light->GetNode()->GetWorldPosition(), light->GetRange() * 1.25f).Distance(cameraPos);
3030         else
3031             lightDist = light->GetFrustum().Distance(cameraPos);
3032 
3033         // Draw front faces if not inside light volume
3034         if (lightDist < camera_->GetNearClip() * 2.0f)
3035         {
3036             renderer_->SetCullMode(CULL_CW, camera_);
3037             graphics_->SetDepthTest(CMP_GREATER);
3038         }
3039         else
3040         {
3041             renderer_->SetCullMode(CULL_CCW, camera_);
3042             graphics_->SetDepthTest(CMP_LESSEQUAL);
3043         }
3044     }
3045     else
3046     {
3047         // In case the same camera is used for multiple views with differing aspect ratios (not recommended)
3048         // refresh the directional light's model transform before rendering
3049         light->GetVolumeTransform(camera_);
3050         graphics_->SetCullMode(CULL_NONE);
3051         graphics_->SetDepthTest(CMP_ALWAYS);
3052     }
3053 
3054     graphics_->SetScissorTest(false);
3055     if (!noStencil_)
3056         graphics_->SetStencilTest(true, CMP_NOTEQUAL, OP_KEEP, OP_KEEP, OP_KEEP, 0, light->GetLightMask());
3057     else
3058         graphics_->SetStencilTest(false);
3059 }
3060 
NeedRenderShadowMap(const LightBatchQueue & queue)3061 bool View::NeedRenderShadowMap(const LightBatchQueue& queue)
3062 {
3063     // Must have a shadow map, and either forward or deferred lit batches
3064     return queue.shadowMap_ && (!queue.litBatches_.IsEmpty() || !queue.litBaseBatches_.IsEmpty() ||
3065         !queue.volumeBatches_.Empty());
3066 }
3067 
RenderShadowMap(const LightBatchQueue & queue)3068 void View::RenderShadowMap(const LightBatchQueue& queue)
3069 {
3070     URHO3D_PROFILE(RenderShadowMap);
3071 
3072     Texture2D* shadowMap = queue.shadowMap_;
3073     graphics_->SetTexture(TU_SHADOWMAP, 0);
3074 
3075     graphics_->SetFillMode(FILL_SOLID);
3076     graphics_->SetClipPlane(false);
3077     graphics_->SetStencilTest(false);
3078 
3079     // Set shadow depth bias
3080     BiasParameters parameters = queue.light_->GetShadowBias();
3081 
3082     // The shadow map is a depth stencil texture
3083     if (shadowMap->GetUsage() == TEXTURE_DEPTHSTENCIL)
3084     {
3085         graphics_->SetColorWrite(false);
3086         graphics_->SetDepthStencil(shadowMap);
3087         graphics_->SetRenderTarget(0, shadowMap->GetRenderSurface()->GetLinkedRenderTarget());
3088         // Disable other render targets
3089         for (unsigned i = 1; i < MAX_RENDERTARGETS; ++i)
3090             graphics_->SetRenderTarget(i, (RenderSurface*) 0);
3091         graphics_->SetViewport(IntRect(0, 0, shadowMap->GetWidth(), shadowMap->GetHeight()));
3092         graphics_->Clear(CLEAR_DEPTH);
3093     }
3094     else // if the shadow map is a color rendertarget
3095     {
3096         graphics_->SetColorWrite(true);
3097         graphics_->SetRenderTarget(0, shadowMap);
3098         // Disable other render targets
3099         for (unsigned i = 1; i < MAX_RENDERTARGETS; ++i)
3100             graphics_->SetRenderTarget(i, (RenderSurface*) 0);
3101         graphics_->SetDepthStencil(renderer_->GetDepthStencil(shadowMap->GetWidth(), shadowMap->GetHeight(),
3102             shadowMap->GetMultiSample(), shadowMap->GetAutoResolve()));
3103         graphics_->SetViewport(IntRect(0, 0, shadowMap->GetWidth(), shadowMap->GetHeight()));
3104         graphics_->Clear(CLEAR_DEPTH | CLEAR_COLOR, Color::WHITE);
3105 
3106         parameters = BiasParameters(0.0f, 0.0f);
3107     }
3108 
3109     // Render each of the splits
3110     for (unsigned i = 0; i < queue.shadowSplits_.Size(); ++i)
3111     {
3112         const ShadowBatchQueue& shadowQueue = queue.shadowSplits_[i];
3113 
3114         float multiplier = 1.0f;
3115         // For directional light cascade splits, adjust depth bias according to the far clip ratio of the splits
3116         if (i > 0 && queue.light_->GetLightType() == LIGHT_DIRECTIONAL)
3117         {
3118             multiplier =
3119                 Max(shadowQueue.shadowCamera_->GetFarClip() / queue.shadowSplits_[0].shadowCamera_->GetFarClip(), 1.0f);
3120             multiplier = 1.0f + (multiplier - 1.0f) * queue.light_->GetShadowCascade().biasAutoAdjust_;
3121             // Quantize multiplier to prevent creation of too many rasterizer states on D3D11
3122             multiplier = (int)(multiplier * 10.0f) / 10.0f;
3123         }
3124 
3125         // Perform further modification of depth bias on OpenGL ES, as shadow calculations' precision is limited
3126         float addition = 0.0f;
3127 #ifdef GL_ES_VERSION_2_0
3128         multiplier *= renderer_->GetMobileShadowBiasMul();
3129         addition = renderer_->GetMobileShadowBiasAdd();
3130 #endif
3131 
3132         graphics_->SetDepthBias(multiplier * parameters.constantBias_ + addition, multiplier * parameters.slopeScaledBias_);
3133 
3134         if (!shadowQueue.shadowBatches_.IsEmpty())
3135         {
3136             graphics_->SetViewport(shadowQueue.shadowViewport_);
3137             shadowQueue.shadowBatches_.Draw(this, shadowQueue.shadowCamera_, false, false, true);
3138         }
3139     }
3140 
3141     // Scale filter blur amount to shadow map viewport size so that different shadow map resolutions don't behave differently
3142     float blurScale = queue.shadowSplits_[0].shadowViewport_.Width() / 1024.0f;
3143     renderer_->ApplyShadowMapFilter(this, shadowMap, blurScale);
3144 
3145     // reset some parameters
3146     graphics_->SetColorWrite(true);
3147     graphics_->SetDepthBias(0.0f, 0.0f);
3148 }
3149 
GetDepthStencil(RenderSurface * renderTarget)3150 RenderSurface* View::GetDepthStencil(RenderSurface* renderTarget)
3151 {
3152     // If using the backbuffer, return the backbuffer depth-stencil
3153     if (!renderTarget)
3154         return 0;
3155     // Then check for linked depth-stencil
3156     RenderSurface* depthStencil = renderTarget->GetLinkedDepthStencil();
3157     // Finally get one from Renderer
3158     if (!depthStencil)
3159         depthStencil = renderer_->GetDepthStencil(renderTarget->GetWidth(), renderTarget->GetHeight(),
3160             renderTarget->GetMultiSample(), renderTarget->GetAutoResolve());
3161     return depthStencil;
3162 }
3163 
GetRenderSurfaceFromTexture(Texture * texture,CubeMapFace face)3164 RenderSurface* View::GetRenderSurfaceFromTexture(Texture* texture, CubeMapFace face)
3165 {
3166     if (!texture)
3167         return 0;
3168 
3169     if (texture->GetType() == Texture2D::GetTypeStatic())
3170         return static_cast<Texture2D*>(texture)->GetRenderSurface();
3171     else if (texture->GetType() == TextureCube::GetTypeStatic())
3172         return static_cast<TextureCube*>(texture)->GetRenderSurface(face);
3173     else
3174         return 0;
3175 }
3176 
SendViewEvent(StringHash eventType)3177 void View::SendViewEvent(StringHash eventType)
3178 {
3179     using namespace BeginViewRender;
3180 
3181     VariantMap& eventData = GetEventDataMap();
3182 
3183     eventData[P_VIEW] = this;
3184     eventData[P_SURFACE] = renderTarget_;
3185     eventData[P_TEXTURE] = (renderTarget_ ? renderTarget_->GetParentTexture() : 0);
3186     eventData[P_SCENE] = scene_;
3187     eventData[P_CAMERA] = cullCamera_;
3188 
3189     renderer_->SendEvent(eventType, eventData);
3190 }
3191 
FindNamedTexture(const String & name,bool isRenderTarget,bool isVolumeMap)3192 Texture* View::FindNamedTexture(const String& name, bool isRenderTarget, bool isVolumeMap)
3193 {
3194     // Check rendertargets first
3195     StringHash nameHash(name);
3196     if (renderTargets_.Contains(nameHash))
3197         return renderTargets_[nameHash];
3198 
3199     // Then the resource system
3200     ResourceCache* cache = GetSubsystem<ResourceCache>();
3201 
3202     // Check existing resources first. This does not load resources, so we can afford to guess the resource type wrong
3203     // without having to rely on the file extension
3204     Texture* texture = cache->GetExistingResource<Texture2D>(name);
3205     if (!texture)
3206         texture = cache->GetExistingResource<TextureCube>(name);
3207     if (!texture)
3208         texture = cache->GetExistingResource<Texture3D>(name);
3209     if (!texture)
3210         texture = cache->GetExistingResource<Texture2DArray>(name);
3211     if (texture)
3212         return texture;
3213 
3214     // If not a rendertarget (which will never be loaded from a file), finally also try to load the texture
3215     // This will log an error if not found; the texture binding will be cleared in that case to not constantly spam the log
3216     if (!isRenderTarget)
3217     {
3218         if (GetExtension(name) == ".xml")
3219         {
3220             // Assume 3D textures are only bound to the volume map unit, otherwise it's a cube texture
3221 #ifdef DESKTOP_GRAPHICS
3222             StringHash type = ParseTextureTypeXml(cache, name);
3223             if (!type && isVolumeMap)
3224                 type = Texture3D::GetTypeStatic();
3225 
3226             if (type == Texture3D::GetTypeStatic())
3227                 return cache->GetResource<Texture3D>(name);
3228             else if (type == Texture2DArray::GetTypeStatic())
3229                 return cache->GetResource<Texture2DArray>(name);
3230             else
3231 #endif
3232                 return cache->GetResource<TextureCube>(name);
3233         }
3234         else
3235             return cache->GetResource<Texture2D>(name);
3236     }
3237 
3238     return 0;
3239 }
3240 
3241 }
3242