// // 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 "OctreeQuery.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[] = { // Use slightly clamped Z-range so that shadowed point light splits line up nicely 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 "LQ", "LQHW", "", "HW" #endif }; static const String fallbackVariations[] = { "", "FB" }; static const String linearVariations[] = { "", "Linear" }; static const String depthVariations[] = { "", "Depth" }; static const String geometryVSVariations[] = { "", "Skinned", "Instanced", "Billboard" }; static const String lightVSVariations[] = { "", "Spot", "Shadow", "SpotShadow" }; static const String deferredLightVSVariations[] = { "", "Dir", "Ortho", "OrthoDir" }; static const String lightPSVariations[] = { "Dir", "DirSpec", "Spot", "SpotSpec", "Point", "PointSpec", "PointMask", "PointMaskSpec", "DirShadow", "DirShadowSpec", "SpotShadow", "SpotShadowSpec", "PointShadow", "PointShadowSpec", "PointMaskShadow", "PointMaskShadowSpec", "OrthoDir", "OrthoDirSpec", "OrthoSpot", "OrthoSpotSpec", "OrthoPoint", "OrthoPointSpec", "OrthoPointMask", "OrthoPointMaskSpec", "OrthoDirShadow", "OrthoDirShadowSpec", "OrthoSpotShadow", "OrthoSpotShadowSpec", "OrthoPointShadow", "OrthoPointShadowSpec", "OrthoPointMaskShadow", "OrthoPointMaskShadowSpec" }; static const unsigned INSTANCING_BUFFER_MASK = MASK_INSTANCEMATRIX1 | MASK_INSTANCEMATRIX2 | MASK_INSTANCEMATRIX3; static const Viewport noViewport; OBJECTTYPESTATIC(Renderer); Renderer::Renderer(Context* context) : Object(context), defaultZone_(new Zone(context)), numViews_(0), numShadowCameras_(0), numSplitLights_(0), numTempNodes_(0), specularLighting_(true), drawShadows_(true), textureAnisotropy_(4), textureFilterMode_(FILTER_TRILINEAR), textureQuality_(QUALITY_HIGH), materialQuality_(QUALITY_HIGH), shadowMapSize_(1024), shadowQuality_(SHADOWQUALITY_HIGH_16BIT), reuseShadowMaps_(true), dynamicInstancing_(true), minInstanceGroupSize_(4), maxInstanceTriangles_(500), maxOccluderTriangles_(5000), occlusionBufferSize_(256), occluderSizeThreshold_(0.1f), shadersChangedFrameNumber_(M_MAX_UNSIGNED), shadersDirty_(true), initialized_(false) { SubscribeToEvent(E_SCREENMODE, HANDLER(Renderer, HandleScreenMode)); SubscribeToEvent(E_RENDERUPDATE, HANDLER(Renderer, HandleRenderUpdate)); // Default to one of each shadow map resolution for (unsigned i = 0; i < NUM_SHADOWMAP_RESOLUTIONS; ++i) shadowMaps_[i].Resize(1); // 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, const Viewport& viewport) { if (index >= viewports_.Size()) { LOGERROR("Viewport index out of bounds"); return; } viewports_[index] = viewport; } void Renderer::SetSpecularLighting(bool enable) { specularLighting_ = enable; } void Renderer::SetDrawShadows(bool enable) { if (!graphics_) return; drawShadows_ = enable; if (!CreateShadowMaps()) drawShadows_ = false; } 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::SetShadowMapSize(int size) { if (!graphics_) return; shadowMapSize_ = Max(size, SHADOW_MIN_PIXELS); if (!CreateShadowMaps()) { shadowMapSize_ = 1024; if (!CreateShadowMaps()) drawShadows_ = false; } } void Renderer::SetShadowQuality(int quality) { if (!graphics_) return; quality &= SHADOWQUALITY_HIGH_24BIT; if (!graphics_->GetHiresShadowSupport()) quality &= SHADOWQUALITY_HIGH_16BIT; if (quality != shadowQuality_) { shadowQuality_ = quality; shadersDirty_ = true; if (!CreateShadowMaps()) drawShadows_ = false; } } void Renderer::SetReuseShadowMaps(bool enable) { if (enable == reuseShadowMaps_) return; reuseShadowMaps_ = enable; if (reuseShadowMaps_) { for (unsigned i = 0; i < NUM_SHADOWMAP_RESOLUTIONS; ++i) shadowMaps_[i].Resize(1); } if (!CreateShadowMaps()) drawShadows_ = false; shadersDirty_ = true; } void Renderer::SetNumShadowMaps(unsigned full, unsigned half, unsigned quarter) { shadowMaps_[0].Resize(full ? full : 1); shadowMaps_[1].Resize(half ? half : 1); shadowMaps_[2].Resize(quarter ? quarter : 1); if (!CreateShadowMaps()) drawShadows_ = false; } void Renderer::SetDynamicInstancing(bool enable) { if (!instancingBuffer_) enable = false; dynamicInstancing_ = enable; } void Renderer::SetMinInstanceGroupSize(int size) { minInstanceGroupSize_ = Max(size, 2); } 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); } const Viewport& Renderer::GetViewport(unsigned index) const { return index < viewports_.Size() ? viewports_[index] : noViewport; } 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 nulights_ = 0; unsigned lastView = allViews ? numViews_ : 1; for (unsigned i = 0; i < lastView; ++i) nulights_ += views_[i]->GetLights().Size(); return nulights_; } 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 (unsigned j = 0; j < lightQueues.Size(); ++j) { Light* light = lightQueues[j].light_; if (light && light->GetShadowMap()) ++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; } unsigned Renderer::GetNumShadowOccluders(bool allViews) const { unsigned numShadowOccluders = 0; unsigned lastView = allViews ? numViews_ : 1; for (unsigned i = 0; i < lastView; ++i) numShadowOccluders += views_[i]->GetShadowOccluders().Size(); return numShadowOccluders; } const OcclusionBuffer* Renderer::GetOcclusionBuffer(float aspectRatio, bool halfResolution) { // Return an occlusion buffer for debug output purposes. Do not allocate new int width = occlusionBufferSize_; int height = (int)(occlusionBufferSize_ / aspectRatio); if (halfResolution) { width >>= 1; height >>= 1; } int searchKey = (width << 16) | height; HashMap >::Iterator i = occlusionBuffers_.Find(searchKey); if (i != occlusionBuffers_.End()) return i->second_; else return 0; } 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; // Advance frame number & time, set up the frameinfo structure, and reset views & stats frame_.frameNumber_ = GetSubsystem