// // Copyright (c) 2008-2013 the Urho3D project. // // 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 "Log.h" #include "Material.h" #include "OcclusionBuffer.h" #include "Octree.h" #include "Profiler.h" #include "Renderer.h" #include "RenderPath.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" namespace Urho3D { 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 char* shadowVariations[] = { // No specific hardware shadow compare variation on OpenGL, it is always supported #ifdef USE_OPENGL "LQ", "LQ", "", "" #else "", "LQHW", "", "HW" #endif }; static const char* geometryVSVariations[] = { "", "Skinned", "Instanced", "Billboard" }; static const char* lightVSVariations[] = { "Dir", "Spot", "Point", "DirSpec", "SpotSpec", "PointSpec", "DirShadow", "SpotShadow", "PointShadow", "DirSpecShadow", "SpotSpecShadow", "PointSpecShadow" }; static const char* vertexLightVSVariations[] = { "", "1VL", "2VL", "3VL", "4VL", "5VL", "6VL" }; static const char* deferredLightVSVariations[] = { "", "Dir", "Ortho", "OrthoDir" }; static const char* 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 = 1000; Renderer::Renderer(Context* context) : Object(context), defaultZone_(new Zone(context)), quadDirLight_(new Light(context)), textureAnisotropy_(4), textureFilterMode_(FILTER_TRILINEAR), textureQuality_(QUALITY_HIGH), materialQuality_(QUALITY_HIGH), shadowMapSize_(1024), shadowQuality_(SHADOWQUALITY_HIGH_16BIT), maxShadowMaps_(1), maxShadowCascades_(MAX_CASCADE_SPLITS), minInstances_(2), maxInstanceTriangles_(500), maxSortedInstances_(1000), maxOccluderTriangles_(5000), occlusionBufferSize_(256), occluderSizeThreshold_(0.025f), numViews_(0), numOcclusionBuffers_(0), numShadowCameras_(0), shadersChangedFrameNumber_(M_MAX_UNSIGNED), specularLighting_(true), drawShadows_(true), reuseShadowMaps_(true), dynamicInstancing_(true), shadersDirty_(true), initialized_(false) { #ifndef USE_OPENGL shaderPath_ = "Shaders/HLSL/"; #else shaderPath_ = "Shaders/GLSL/"; #endif SubscribeToEvent(E_SCREENMODE, HANDLER(Renderer, HandleScreenMode)); SubscribeToEvent(E_GRAPHICSFEATURES, HANDLER(Renderer, HandleGraphicsFeatures)); quadDirLight_->SetLightType(LIGHT_DIRECTIONAL); // Try to initialize right now, but skip if screen mode is not yet set Initialize(); } Renderer::~Renderer() { } void Renderer::SetNumViewports(unsigned num) { viewports_.Resize(num); } void Renderer::SetViewport(unsigned index, Viewport* viewport) { if (index >= viewports_.Size()) viewports_.Resize(index + 1); viewports_[index] = viewport; } void Renderer::SetDefaultRenderPath(RenderPath* renderPath) { if (renderPath) defaultRenderPath_ = renderPath; } void Renderer::SetDefaultRenderPath(XMLFile* xmlFile) { SharedPtr newRenderPath(new RenderPath()); if (newRenderPath->Load(xmlFile)) defaultRenderPath_ = newRenderPath; } 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_->GetHiresShadowMapFormat()) 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::SetMinInstances(int instances) { minInstances_ = Max(instances, 2); } void Renderer::SetMaxInstanceTriangles(int triangles) { maxInstanceTriangles_ = Max(triangles, 0); } void Renderer::SetMaxSortedInstances(int instances) { maxSortedInstances_ = Max(instances, 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); } void Renderer::ReloadShaders() { shadersDirty_ = true; } Viewport* Renderer::GetViewport(unsigned index) const { return index < viewports_.Size() ? viewports_[index] : (Viewport*)0; } RenderPath* Renderer::GetDefaultRenderPath() const { return defaultRenderPath_; } ShaderVariation* Renderer::GetVertexShader(const String& name, bool checkExists) const { return GetShader(VS, name, checkExists); } ShaderVariation* Renderer::GetPixelShader(const String& name, bool checkExists) const { return GetShader(PS, name, 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 Vector& lightQueues = views_[i]->GetLightQueues(); for (Vector::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