//################################################################################################## // // Custom Visualization Core library // Copyright (C) 2011-2012 Ceetron AS // // This library is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This library is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at <> // for more details. // //################################################################################################## #include "cvfBase.h" #include "cvfRenderQueueBuilder.h" #include "cvfRenderQueue.h" #include "cvfPartRenderHintCollection.h" #include "cvfPart.h" #include "cvfDrawable.h" #include "cvfEffect.h" #include "cvfShaderProgram.h" #include "cvfOpenGLContext.h" #include "cvfOpenGLResourceManager.h" #include "cvfBufferObjectManaged.h" #include "cvfCamera.h" #include "cvfRenderStateTextureBindings.h" #ifndef CVF_OPENGL_ES #include "cvfRenderState_FF.h" #endif namespace cvf { //================================================================================================== /// /// \class cvf::RenderQueueBuilder /// \ingroup Viewing /// /// Populates a render queue based on a part collection. All items in the part collection will /// result in a corresponding RenderItem. /// //================================================================================================== //-------------------------------------------------------------------------------------------------- /// Constructor. Sets up the object with the part collection to use in populateRenderQueue() //-------------------------------------------------------------------------------------------------- RenderQueueBuilder::RenderQueueBuilder(PartRenderHintCollection* srcPartCollection, OpenGLContext* oglContext, const Camera* camera) { CVF_ASSERT(srcPartCollection); CVF_ASSERT(oglContext); CVF_ASSERT(camera); m_srcPartCollection = srcPartCollection; m_oglContext = oglContext; m_camera = camera; m_requireDistance = false; m_requirePixelArea = false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RenderQueueBuilder::setFixedEffect(Effect* effect) { m_fixedEffect = effect; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RenderQueueBuilder::setRequireDistance(bool requireDistance) { m_requireDistance = requireDistance; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RenderQueueBuilder::setRequirePixelArea(bool requirePixelArea) { m_requirePixelArea = requirePixelArea; } //-------------------------------------------------------------------------------------------------- /// Populate the render queue based on the input parts /// /// \param renderQueue The render queue that should be populated /// /// Currently this function will: /// - Populate the RenderQueue with RenderItem objects /// - Link all referenced GLShaderProgram objects /// /// Future tasks (?): /// - Do LOD evaluation to determine LOD level for drawables and effects /// - Perform VBO and display list setup /// /// To be determined: /// - What should happen to parts that end up having no effect or no drawable? /// - Currently, parts ending up with \b no drawable are \b not added to the queue. /// - Parts with a drawable, but without any effect or with illegal effects (shader program /// that fails to link) will get added to the queue //-------------------------------------------------------------------------------------------------- void RenderQueueBuilder::populateRenderQueue(RenderQueue* renderQueue) { CVF_ASSERT(renderQueue); size_t srcPartCount = m_srcPartCollection->count(); renderQueue->removeAll(); renderQueue->hintNumEntriesToAdd(srcPartCount); // Extract raw pointer to any fixed effect Effect* fixedEffect = m_fixedEffect.p(); OpenGLResourceManager* resourceManager = m_oglContext->resourceManager(); resourceManager->evictOrphanedOpenGLResources(m_oglContext.p()); // Does OpenGL support buffer objects? const bool oglSupportsBufferObjects = BufferObjectManaged::supportedOpenGL(m_oglContext.p()); size_t i; for (i = 0; i < srcPartCount; i++) { Part* part = m_srcPartCollection->part(i); CVF_ASSERT(part); // TODO Must do LOD evaluation for geometry lod const int drawableLod = 0; Drawable* drawable = part->drawable(drawableLod); CVF_ASSERT(drawable); Effect* effect = fixedEffect; if (!effect) { // TODO Must do LOD evaluation for effect lod const int effectLod = 0; effect = part->effect(effectLod); } CVF_ASSERT(effect); bool canAddToQueue = true; // TODO rework this when we get uniforms in place ShaderProgram* shaderProg = effect->shaderProgram(); if (shaderProg) { // For now, don't add parts where program fails to compile if (!shaderProg->linkProgram(m_oglContext.p())) { // TODO Should we do any error reporting here? // For now we just don't add the effect to the render queue canAddToQueue = false; } } // Create and upload data to buffer objects (VBOs) // Whether anything actually gets uploaded is controlled by the drawable itself if (oglSupportsBufferObjects) { drawable->createUploadBufferObjectsGPU(m_oglContext.p()); } // Create/Setup the textures RenderStateTextureBindings* textureBindings = static_cast(effect->renderStateOfType(RenderState::TEXTURE_BINDINGS)); if (textureBindings && canAddToQueue) { textureBindings->setupTextures(m_oglContext.p()); } #ifndef CVF_OPENGL_ES RenderStateTextureMapping_FF* textureMapping = static_cast(effect->renderStateOfType(RenderState::TEXTURE_MAPPING_FF)); if (textureMapping && canAddToQueue) { textureMapping->setupTexture(m_oglContext.p()); } #endif if (canAddToQueue) { float distance = -1.0f; float pixelArea = -1.0f; // Check if we need distance and pixel area. If it is provided by the PartRenderHint we use that. // If not, we compute it here if it is required PartRenderHint* hint = m_srcPartCollection->renderHint(i); if (hint) { distance = hint->centerDistance(); pixelArea = hint->projectedAreaPixels(); } if (m_requireDistance && (distance == -1.0f)) { const BoundingBox& bb = part->boundingBox(); CVF_ASSERT(bb.isValid()); distance = static_cast((m_camera->position() - bb.center()).length()); } if (m_requirePixelArea && (pixelArea == -1.0f)) { const BoundingBox& bb = part->boundingBox(); CVF_ASSERT(bb.isValid()); pixelArea = static_cast(m_camera->computeProjectedBoundingSpherePixelArea(bb.center(), bb.radius())); } renderQueue->add(part, drawable, effect, pixelArea, distance); } } } } // namespace cvf