// // Urho3D Engine // Copyright (c) 2008-2011 Lasse Öörni // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // #include "Precompiled.h" #include "Camera.h" #include "CoreEvents.h" #include "DebugRenderer.h" #include "Geometry.h" #include "Graphics.h" #include "GraphicsEvents.h" #include "GraphicsImpl.h" #include "IndexBuffer.h" #include "Light.h" #include "Log.h" #include "Material.h" #include "OcclusionBuffer.h" #include "Octree.h" #include "Profiler.h" #include "Renderer.h" #include "ResourceCache.h" #include "Scene.h" #include "Shader.h" #include "ShaderVariation.h" #include "Technique.h" #include "Texture2D.h" #include "TextureCube.h" #include "VertexBuffer.h" #include "View.h" #include "XMLFile.h" #include "Zone.h" #include "DebugNew.h" static const float dirLightVertexData[] = { -1, 1, 0, 1, 1, 0, 1, -1, 0, -1, -1, 0, }; static const unsigned short dirLightIndexData[] = { 0, 1, 2, 2, 3, 0, }; static const float pointLightVertexData[] = { -0.423169f, -1.000000f, 0.423169f, -0.423169f, -1.000000f, -0.423169f, 0.423169f, -1.000000f, -0.423169f, 0.423169f, -1.000000f, 0.423169f, 0.423169f, 1.000000f, -0.423169f, -0.423169f, 1.000000f, -0.423169f, -0.423169f, 1.000000f, 0.423169f, 0.423169f, 1.000000f, 0.423169f, -1.000000f, 0.423169f, -0.423169f, -1.000000f, -0.423169f, -0.423169f, -1.000000f, -0.423169f, 0.423169f, -1.000000f, 0.423169f, 0.423169f, 0.423169f, 0.423169f, -1.000000f, 0.423169f, -0.423169f, -1.000000f, -0.423169f, -0.423169f, -1.000000f, -0.423169f, 0.423169f, -1.000000f, 1.000000f, 0.423169f, 0.423169f, 1.000000f, -0.423169f, 0.423169f, 1.000000f, -0.423169f, -0.423169f, 1.000000f, 0.423169f, -0.423169f, 0.423169f, -0.423169f, 1.000000f, 0.423169f, 0.423169f, 1.000000f, -0.423169f, 0.423169f, 1.000000f, -0.423169f, -0.423169f, 1.000000f }; static const unsigned short pointLightIndexData[] = { 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23, 0, 10, 9, 0, 9, 1, 13, 2, 1, 13, 1, 14, 23, 0, 3, 23, 3, 20, 17, 3, 2, 17, 2, 18, 21, 7, 6, 21, 6, 22, 7, 16, 19, 7, 19, 4, 5, 8, 11, 5, 11, 6, 4, 12, 15, 4, 15, 5, 22, 11, 10, 22, 10, 23, 8, 15, 14, 8, 14, 9, 12, 19, 18, 12, 18, 13, 16, 21, 20, 16, 20, 17, 0, 23, 10, 1, 9, 14, 2, 13, 18, 3, 17, 20, 6, 11, 22, 5, 15, 8, 4, 19, 12, 7, 21, 16 }; static const float spotLightVertexData[] = { 0.00001f, 0.00001f, 0.00001f, 0.00001f, -0.00001f, 0.00001f, -0.00001f, -0.00001f, 0.00001f, -0.00001f, 0.00001f, 0.00001f, 1.00000f, 1.00000f, 0.99999f, 1.00000f, -1.00000f, 0.99999f, -1.00000f, -1.00000f, 0.99999f, -1.00000f, 1.00000f, 0.99999f, }; static const unsigned short spotLightIndexData[] = { 3, 0, 1, 3, 1, 2, 0, 4, 5, 0, 5, 1, 3, 7, 4, 3, 4, 0, 7, 3, 2, 7, 2, 6, 6, 2, 1, 6, 1, 5, 7, 5, 4, 7, 6, 5 }; static const String shadowVariations[] = { // No specific hardware shadow compare variation on OpenGL, it is always supported #ifdef USE_OPENGL "LQ", "LQ", "", "" #else "", "LQHW", "", "HW" #endif }; static const String hwVariations[] = { "", "HW" }; static const String geometryVSVariations[] = { "", "Skinned", "Instanced", "Billboard" }; static const String lightVSVariations[] = { "Dir", "Spot", "Point", "DirSpec", "SpotSpec", "PointSpec", "DirShadow", "SpotShadow", "PointShadow", "DirSpecShadow", "SpotSpecShadow", "PointSpecShadow" }; static const String vertexLightVSVariations[] = { "", "1VL", "2VL", "3VL", "4VL", "5VL", "6VL" }; static const String deferredLightVSVariations[] = { "", "Dir", "Ortho", "OrthoDir" }; static const String lightPSVariations[] = { "Dir", "Spot", "Point", "PointMask", "DirSpec", "SpotSpec", "PointSpec", "PointMaskSpec", "DirShadow", "SpotShadow", "PointShadow", "PointMaskShadow", "DirSpecShadow", "SpotSpecShadow", "PointSpecShadow", "PointMaskSpecShadow" }; static const unsigned INSTANCING_BUFFER_MASK = MASK_INSTANCEMATRIX1 | MASK_INSTANCEMATRIX2 | MASK_INSTANCEMATRIX3; static const unsigned MAX_BUFFER_AGE = 2000; OBJECTTYPESTATIC(Renderer); Renderer::Renderer(Context* context) : Object(context), defaultZone_(new Zone(context)), numViews_(0), numShadowCameras_(0), numOcclusionBuffers_(0), textureAnisotropy_(4), textureFilterMode_(FILTER_TRILINEAR), textureQuality_(QUALITY_HIGH), materialQuality_(QUALITY_HIGH), shadowMapSize_(1024), shadowQuality_(SHADOWQUALITY_HIGH_16BIT), maxShadowMaps_(1), maxShadowCascades_(MAX_CASCADE_SPLITS), maxInstanceTriangles_(500), maxOccluderTriangles_(5000), occlusionBufferSize_(256), occluderSizeThreshold_(0.1f), shadersChangedFrameNumber_(M_MAX_UNSIGNED), lightPrepass_(false), specularLighting_(true), drawShadows_(true), reuseShadowMaps_(true), dynamicInstancing_(true), shadersDirty_(true), initialized_(false) { SubscribeToEvent(E_SCREENMODE, HANDLER(Renderer, HandleScreenMode)); SubscribeToEvent(E_GRAPHICSFEATURES, HANDLER(Renderer, HandleGraphicsFeatures)); SubscribeToEvent(E_RENDERUPDATE, HANDLER(Renderer, HandleRenderUpdate)); // Try to initialize right now, but skip if screen mode is not yet set Initialize(); SetNumViewports(1); } Renderer::~Renderer() { } void Renderer::SetNumViewports(unsigned num) { viewports_.Resize(num); for (unsigned i = 0; i < viewports_.Size(); ++i) { if (!viewports_[i]) viewports_[i] = new Viewport(); } } bool Renderer::SetViewport(unsigned index, Viewport* viewport) { if (index >= viewports_.Size()) { LOGERROR("Viewport index out of bounds"); return false; } if (!viewport) { LOGERROR("Null viewport"); return false; } viewports_[index] = viewport; return true; } void Renderer::SetLightPrepass(bool enable) { if (!initialized_) { LOGERROR("Can not switch light pre-pass rendering before setting initial screen mode"); return; } if (!graphics_->GetLightPrepassSupport()) enable = false; if (enable != lightPrepass_) { // Light prepass is incompatible with hardware multisampling, so set new screen mode with 1x sampling if in use if (graphics_->GetMultiSample() > 1) { graphics_->SetMode(graphics_->GetWidth(), graphics_->GetHeight(), graphics_->GetFullscreen(), graphics_->GetVSync(), graphics_->GetTripleBuffer(), 1); } lightPrepass_ = enable; shadersDirty_ = true; } } void Renderer::SetSpecularLighting(bool enable) { specularLighting_ = enable; } void Renderer::SetTextureAnisotropy(int level) { textureAnisotropy_ = Max(level, 1); } void Renderer::SetTextureFilterMode(TextureFilterMode mode) { textureFilterMode_ = mode; } void Renderer::SetTextureQuality(int quality) { quality = Clamp(quality, QUALITY_LOW, QUALITY_HIGH); if (quality != textureQuality_) { textureQuality_ = quality; ReloadTextures(); } } void Renderer::SetMaterialQuality(int quality) { materialQuality_ = Clamp(quality, QUALITY_LOW, QUALITY_MAX); shadersDirty_ = true; ResetViews(); } void Renderer::SetDrawShadows(bool enable) { if (!graphics_ || !graphics_->GetShadowMapFormat()) return; drawShadows_ = enable; if (!drawShadows_) ResetShadowMaps(); } void Renderer::SetShadowMapSize(int size) { if (!graphics_) return; size = NextPowerOfTwo(Max(size, SHADOW_MIN_PIXELS)); if (size != shadowMapSize_) { shadowMapSize_ = size; ResetShadowMaps(); } } void Renderer::SetShadowQuality(int quality) { if (!graphics_) return; quality &= SHADOWQUALITY_HIGH_24BIT; // If no hardware PCF, do not allow to select one-sample quality if (!graphics_->GetHardwareShadowSupport()) quality |= SHADOWQUALITY_HIGH_16BIT; if (!graphics_->GetHiresShadowSupport()) quality &= SHADOWQUALITY_HIGH_16BIT; if (quality != shadowQuality_) { shadowQuality_ = quality; shadersDirty_ = true; ResetShadowMaps(); } } void Renderer::SetReuseShadowMaps(bool enable) { if (enable == reuseShadowMaps_) return; reuseShadowMaps_ = enable; } void Renderer::SetMaxShadowMaps(int shadowMaps) { if (shadowMaps < 1) return; maxShadowMaps_ = shadowMaps; for (HashMap > >::Iterator i = shadowMaps_.Begin(); i != shadowMaps_.End(); ++i) { if ((int)i->second_.Size() > maxShadowMaps_) i->second_.Resize(maxShadowMaps_); } } void Renderer::SetMaxShadowCascades(int cascades) { cascades = Clamp(cascades, 1, MAX_CASCADE_SPLITS); if (cascades != maxShadowCascades_) { maxShadowCascades_ = cascades; ResetShadowMaps(); } } void Renderer::SetDynamicInstancing(bool enable) { if (!instancingBuffer_) enable = false; dynamicInstancing_ = enable; } void Renderer::SetMaxInstanceTriangles(int triangles) { maxInstanceTriangles_ = Max(triangles, 0); } void Renderer::SetMaxOccluderTriangles(int triangles) { maxOccluderTriangles_ = Max(triangles, 0); } void Renderer::SetOcclusionBufferSize(int size) { occlusionBufferSize_ = Max(size, 1); occlusionBuffers_.Clear(); } void Renderer::SetOccluderSizeThreshold(float screenSize) { occluderSizeThreshold_ = Max(screenSize, 0.0f); } Viewport* Renderer::GetViewport(unsigned index) const { return index < viewports_.Size() ? viewports_[index] : (Viewport*)0; } ShaderVariation* Renderer::GetVertexShader(const String& name, bool checkExists) const { return GetShader(name, vsFormat_, checkExists); } ShaderVariation* Renderer::GetPixelShader(const String& name, bool checkExists) const { return GetShader(name, psFormat_, checkExists); } unsigned Renderer::GetNumGeometries(bool allViews) const { unsigned numGeometries = 0; unsigned lastView = allViews ? numViews_ : 1; for (unsigned i = 0; i < lastView; ++i) numGeometries += views_[i]->GetGeometries().Size(); return numGeometries; } unsigned Renderer::GetNumLights(bool allViews) const { unsigned numLights = 0; unsigned lastView = allViews ? numViews_ : 1; for (unsigned i = 0; i < lastView; ++i) numLights += views_[i]->GetLights().Size(); return numLights; } unsigned Renderer::GetNumShadowMaps(bool allViews) const { unsigned numShadowMaps = 0; unsigned lastView = allViews ? numViews_ : 1; for (unsigned i = 0; i < lastView; ++i) { const List& lightQueues = views_[i]->GetLightQueues(); for (List::ConstIterator i = lightQueues.Begin(); i != lightQueues.End(); ++i) { if (i->shadowMap_) ++numShadowMaps; } } return numShadowMaps; } unsigned Renderer::GetNumOccluders(bool allViews) const { unsigned numOccluders = 0; unsigned lastView = allViews ? numViews_ : 1; for (unsigned i = 0; i < lastView; ++i) numOccluders += views_[i]->GetOccluders().Size(); return numOccluders; } void Renderer::Update(float timeStep) { PROFILE(UpdateViews); numViews_ = 0; // If device lost, do not perform update. This is because any dynamic vertex/index buffer updates happen already here, // and if the device is lost, the updates queue up, causing memory use to rise constantly if (!graphics_ || !graphics_->IsInitialized() || graphics_->IsDeviceLost()) return; // Set up the frameinfo structure for this frame frame_.frameNumber_ = GetSubsystem