Commit 1d87613e authored by Jan Möbius's avatar Jan Möbius

Reorganize and document the shader renderer

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@16073 383ad7c9-94d9-4d36-a494-682f7c89f535
parent ba6273b2
#include <ACG/GL/gl.hh>
#include <ACG/ShaderUtils/GLSLShader.hh>
#include "Renderer.hh"
#include <ACG/GL/ShaderCache.hh>
#include <ACG/GL/VertexDeclaration.hh>
using namespace ACG;
// =================================================
void Renderer::addRenderObject(RenderObject* _renderObject)
{
// do some more checks for error detection
if (!_renderObject->vertexDecl)
std::cout << "error: missing vertex declaration" << std::endl;
else
{
renderObjects_.push_back(*_renderObject);
RenderObject* p = &renderObjects_.back();
if (!p->shaderDesc.numLights)
p->shaderDesc.numLights = numLights_;
else if (p->shaderDesc.numLights < 0 || p->shaderDesc.numLights >= SG_MAX_SHADER_LIGHTS)
p->shaderDesc.numLights = 0;
p->internalFlags_ = 0;
// precompile shader
ShaderCache::getInstance()->getProgram(&p->shaderDesc);
}
}
void Renderer::collectRenderObjects( GLState* _glState, SceneGraph::DrawModes::DrawMode _drawMode, SceneGraph::BaseNode* _sceneGraphRoot )
{
// collect light sources
collectLightNodes(_sceneGraphRoot);
// flush render objects
// clear() may actually free memory, resulting in capacity = 0
renderObjects_.resize(0);
// default material needed
ACG::SceneGraph::Material defMat;
defMat.baseColor(Vec4f(0.0f, 0.0f, 0.0f, 1.0f));
defMat.ambientColor(Vec4f(0.2f, 0.2f, 0.2f, 1.0f));
defMat.diffuseColor(Vec4f(0.6f, 0.6f, 0.6f, 1.0f));
defMat.specularColor(Vec4f(0.0f, 0.0f, 0.0f, 1.0f));
defMat.shininess(1.0f);
// defMat.alphaValue(1.0f);
// collect renderables
traverseRenderableNodes(_glState, _drawMode, _sceneGraphRoot, &defMat);
}
void Renderer::traverseRenderableNodes( GLState* _glState, SceneGraph::DrawModes::DrawMode _drawMode, SceneGraph::BaseNode* _node, const SceneGraph::Material* _mat )
{
if (_node)
{
SceneGraph::BaseNode::StatusMode status(_node->status());
bool process_children(status != SceneGraph::BaseNode::HideChildren);
SceneGraph::DrawModes::DrawMode nodeDM = _node->drawMode();
if (nodeDM == SceneGraph::DrawModes::DEFAULT)
nodeDM = _drawMode;
// If the subtree is hidden, ignore this node and its children while rendering
if (status != SceneGraph::BaseNode::HideSubtree)
{
if ( _node->status() != SceneGraph::BaseNode::HideNode )
_node->enter(*_glState, _drawMode);
// fetch material (Node itself can be a material node, so we have to
// set that in front of the nodes own rendering
SceneGraph::MaterialNode* matNode = dynamic_cast<SceneGraph::MaterialNode*>(_node);
if (matNode)
_mat = &matNode->material();
if (_node->status() != SceneGraph::BaseNode::HideNode)
_node->getRenderObjects(this, *_glState, nodeDM, _mat);
if (process_children)
{
SceneGraph::BaseNode::ChildIter cIt, cEnd(_node->childrenEnd());
// Process all children which are not second pass
for (cIt = _node->childrenBegin(); cIt != cEnd; ++cIt)
if (~(*cIt)->traverseMode() & SceneGraph::BaseNode::SecondPass)
traverseRenderableNodes( _glState, _drawMode, *cIt, _mat);
// Process all children which are second pass
for (cIt = _node->childrenBegin(); cIt != cEnd; ++cIt)
if ((*cIt)->traverseMode() & SceneGraph::BaseNode::SecondPass)
traverseRenderableNodes( _glState, _drawMode, *cIt, _mat);
}
if (_node->status() != SceneGraph::BaseNode::HideNode )
_node->leave(*_glState, nodeDM);
}
}
}
void Renderer::traverseLightNodes( ACG::SceneGraph::BaseNode* _node )
{
if (_node && numLights_ < SG_MAX_SHADER_LIGHTS)
{
BaseNode::StatusMode status(_node->status());
bool process_children(status != BaseNode::HideChildren);
// If the subtree is hidden, ignore this node and its children while rendering
if (status != BaseNode::HideSubtree)
{
if (_node->status() != BaseNode::HideNode)
{
ACG::SceneGraph::LightNode* lnode = dynamic_cast<ACG::SceneGraph::LightNode*>(_node);
if (lnode)
{
ACG::SceneGraph::LightSource light;
// lnode->getLightSource(&light);
lnode->getLightSourceViewSpace(&light);
// --------------------------
// add light to renderer
if (light.directional())
lightTypes_[numLights_] = ACG::SG_LIGHT_DIRECTIONAL;
else if (light.spotCutoff() > 179.5f)
lightTypes_[numLights_] = ACG::SG_LIGHT_POINT;
else
lightTypes_[numLights_] = ACG::SG_LIGHT_SPOT;
lights_[numLights_] = light;
++numLights_;
}
}
if (process_children)
{
BaseNode::ChildIter cIt, cEnd(_node->childrenEnd());
// Process all children which are not second pass
for (cIt = _node->childrenBegin(); cIt != cEnd; ++cIt)
if (~(*cIt)->traverseMode() & BaseNode::SecondPass)
traverseLightNodes(*cIt);
// Process all children which are second pass
for (cIt = _node->childrenBegin(); cIt != cEnd; ++cIt)
if ((*cIt)->traverseMode() & BaseNode::SecondPass)
traverseLightNodes(*cIt);
}
}
}
}
void Renderer::collectLightNodes( ACG::SceneGraph::BaseNode* _node )
{
numLights_ = 0;
traverseLightNodes(_node);
}
......@@ -27,126 +27,11 @@ Renderer::Renderer()
Renderer::~Renderer()
{
}
void Renderer::initializePlugin()
{
ACG::ShaderProgGenerator::setShaderDir(OpenFlipper::Options::shaderDirStr());
}
void Renderer::addRenderObject(RenderObject* _renderObject)
{
// do some more checks for error detection
if (!_renderObject->vertexDecl)
std::cout << "error: missing vertex declaration" << std::endl;
else
{
renderObjects_.push_back(*_renderObject);
RenderObject* p = &renderObjects_.back();
if (!p->shaderDesc.numLights)
p->shaderDesc.numLights = numLights_;
else if (p->shaderDesc.numLights < 0 || p->shaderDesc.numLights >= SG_MAX_SHADER_LIGHTS)
p->shaderDesc.numLights = 0;
p->internalFlags_ = 0;
// precompile shader
ShaderCache::getInstance()->getProgram(&p->shaderDesc);
}
}
void Renderer::collectRenderObjects( GLState* _glState, SceneGraph::DrawModes::DrawMode _drawMode, SceneGraph::BaseNode* _sceneGraphRoot )
{
// collect light sources
collectLightNodes(_sceneGraphRoot);
// flush render objects
// clear() may actually free memory, resulting in capacity = 0
renderObjects_.resize(0);
// default material needed
ACG::SceneGraph::Material defMat;
defMat.baseColor(Vec4f(0.0f, 0.0f, 0.0f, 1.0f));
defMat.ambientColor(Vec4f(0.2f, 0.2f, 0.2f, 1.0f));
defMat.diffuseColor(Vec4f(0.6f, 0.6f, 0.6f, 1.0f));
defMat.specularColor(Vec4f(0.0f, 0.0f, 0.0f, 1.0f));
defMat.shininess(1.0f);
// defMat.alphaValue(1.0f);
// collect renderables
traverseRenderableNodes(_glState, _drawMode, _sceneGraphRoot, &defMat);
}
void Renderer::traverseRenderableNodes( GLState* _glState, SceneGraph::DrawModes::DrawMode _drawMode, SceneGraph::BaseNode* _node, const SceneGraph::Material* _mat )
{
if (_node)
{
SceneGraph::BaseNode::StatusMode status(_node->status());
bool process_children(status != SceneGraph::BaseNode::HideChildren);
SceneGraph::DrawModes::DrawMode nodeDM = _node->drawMode();
if (nodeDM == SceneGraph::DrawModes::DEFAULT)
nodeDM = _drawMode;
// If the subtree is hidden, ignore this node and its children while rendering
if (status != SceneGraph::BaseNode::HideSubtree)
{
if ( _node->status() != SceneGraph::BaseNode::HideNode )
_node->enter(*_glState, _drawMode);
// fetch material (Node itself can be a material node, so we have to
// set that in front of the nodes own rendering
SceneGraph::MaterialNode* matNode = dynamic_cast<SceneGraph::MaterialNode*>(_node);
if (matNode)
_mat = &matNode->material();
if (_node->status() != SceneGraph::BaseNode::HideNode)
_node->getRenderObjects(this, *_glState, nodeDM, _mat);
if (process_children)
{
SceneGraph::BaseNode::ChildIter cIt, cEnd(_node->childrenEnd());
// Process all children which are not second pass
for (cIt = _node->childrenBegin(); cIt != cEnd; ++cIt)
if (~(*cIt)->traverseMode() & SceneGraph::BaseNode::SecondPass)
traverseRenderableNodes( _glState, _drawMode, *cIt, _mat);
// Process all children which are second pass
for (cIt = _node->childrenBegin(); cIt != cEnd; ++cIt)
if ((*cIt)->traverseMode() & SceneGraph::BaseNode::SecondPass)
traverseRenderableNodes( _glState, _drawMode, *cIt, _mat);
}
if (_node->status() != SceneGraph::BaseNode::HideNode )
_node->leave(*_glState, nodeDM);
}
}
}
int Renderer::cmpPriority(const void* _a, const void* _b)
{
const RenderObject* a = *(const RenderObject**)_a;
......@@ -155,47 +40,49 @@ int Renderer::cmpPriority(const void* _a, const void* _b)
return a->priority - b->priority;
}
void Renderer::render(ACG::GLState* _glState, Viewer::ViewerProperties& _properties)
{
// First, all render objects get collected.
collectRenderObjects(_glState, _properties.drawMode(), PluginFunctions::getSceneGraphRootNode());
// Check if there is anything to render
if (renderObjects_.empty())
return;
const size_t numRenderObjects = getNumRenderObjects();
// ==========================================================
// Sort renderable objects based on their priority
// ==========================================================
const size_t numRenderObjects = renderObjects_.size();
// sort for priority
RenderObject** sortedObjects = new RenderObject*[numRenderObjects];
// init sorted objects
for (size_t i = 0; i < numRenderObjects; ++i)
{
sortedObjects[i] = &renderObjects_[i];
}
qsort(sortedObjects, numRenderObjects, sizeof(RenderObject*), cmpPriority);
// dumpRenderObjectsToText("../../dump_ro.txt", sortedObjects);
// ==========================================================
// Now we render them
// ==========================================================
// render
// ---------------------------
// Initialize the render state
// ---------------------------
// gl cleanup
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_INDEX_ARRAY);
// size of a rendered point is set in vertex-shader via gl_PointSize
#ifndef __APPLE__
glEnable(GL_PROGRAM_POINT_SIZE_ARB);
#endif
#ifndef __APPLE__
glEnable(GL_PROGRAM_POINT_SIZE_ARB);
#endif
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
......@@ -207,11 +94,15 @@ void Renderer::render(ACG::GLState* _glState, Viewer::ViewerProperties& _propert
glClearColor(clearColor[0], clearColor[1], clearColor[2], 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// ---------------------------
// Render objects one by one
// ---------------------------
for (size_t i = 0; i < numRenderObjects; ++i)
renderObject(sortedObjects[i]);
// restore common opengl state
// ----------------------------------------
// Restore common opengl state
// ----------------------------------------
// log window remains hidden otherwise
glBindFramebuffer(GL_FRAMEBUFFER, 0);
......@@ -225,9 +116,6 @@ void Renderer::render(ACG::GLState* _glState, Viewer::ViewerProperties& _propert
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
delete [] sortedObjects;
}
......@@ -245,7 +133,6 @@ void Renderer::bindObjectVBO(ACG::RenderObject* _obj,
glBindBufferARB(GL_ARRAY_BUFFER_ARB, _obj->vertexBuffer);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, _obj->indexBuffer);
// activate vertex declaration
_obj->vertexDecl->activateShaderPipeline(_prog);
}
......@@ -409,7 +296,6 @@ void Renderer::renderObject(ACG::RenderObject* _obj,
// select shader from cache
GLSL::Program* prog = _prog ? _prog : ShaderCache::getInstance()->getProgram(&_obj->shaderDesc);
bindObjectVBO(_obj, prog);
// ---------------------------------------
......@@ -435,91 +321,6 @@ void Renderer::renderObject(ACG::RenderObject* _obj,
}
void Renderer::traverseLightNodes( ACG::SceneGraph::BaseNode* _node )
{
if (_node && numLights_ < SG_MAX_SHADER_LIGHTS)
{
BaseNode::StatusMode status(_node->status());
bool process_children(status != BaseNode::HideChildren);
// If the subtree is hidden, ignore this node and its children while rendering
if (status != BaseNode::HideSubtree)
{
if (_node->status() != BaseNode::HideNode)
{
ACG::SceneGraph::LightNode* lnode = dynamic_cast<ACG::SceneGraph::LightNode*>(_node);
if (lnode)
{
ACG::SceneGraph::LightSource light;
// lnode->getLightSource(&light);
lnode->getLightSourceViewSpace(&light);
// --------------------------
// add light to renderer
if (light.directional())
lightTypes_[numLights_] = ACG::SG_LIGHT_DIRECTIONAL;
else if (light.spotCutoff() > 179.5f)
lightTypes_[numLights_] = ACG::SG_LIGHT_POINT;
else
lightTypes_[numLights_] = ACG::SG_LIGHT_SPOT;
lights_[numLights_] = light;
++numLights_;
}
}
if (process_children)
{
BaseNode::ChildIter cIt, cEnd(_node->childrenEnd());
// Process all children which are not second pass
for (cIt = _node->childrenBegin(); cIt != cEnd; ++cIt)
if (~(*cIt)->traverseMode() & BaseNode::SecondPass)
traverseLightNodes(*cIt);
// Process all children which are second pass
for (cIt = _node->childrenBegin(); cIt != cEnd; ++cIt)
if ((*cIt)->traverseMode() & BaseNode::SecondPass)
traverseLightNodes(*cIt);
}
}
}
}
void Renderer::collectLightNodes( ACG::SceneGraph::BaseNode* _node )
{
numLights_ = 0;
traverseLightNodes(_node);
}
int Renderer::getNumRenderObjects() const
{
return renderObjects_.size();
}
QString Renderer::checkOpenGL()
{
return QString("");
......@@ -531,7 +332,9 @@ void Renderer::dumpRenderObjectsToText(const char* _fileName, RenderObject** _so
if (fileOut.open(QFile::WriteOnly | QFile::Truncate))
{
QTextStream outStrm(&fileOut);
for (int i = 0; i < getNumRenderObjects(); ++i)
const int numRenderObjects = renderObjects_.size();
for (int i = 0; i < numRenderObjects; ++i)
{
if (_sortedList)
outStrm << "\n" << _sortedList[i]->toString();
......
......@@ -60,6 +60,11 @@
#include <map>
/** \brief A simple renderer Example for the shader pipeline
*
* It has to derive from ACG::IRenderer as the nodes will will pass back the generated render objects
* via this objects addRenderObject() function.
*/
class Renderer : public QObject, BaseInterface, RenderInterface, LoggingInterface, ACG::IRenderer
{
Q_OBJECT
......@@ -94,110 +99,165 @@ private slots:
void render(ACG::GLState* _glState, Viewer::ViewerProperties& _properties);
QString rendererName() {return QString("Alpha_Version_ShaderPipeline");}
void supportedDrawModes(ACG::SceneGraph::DrawModes::DrawMode& _mode) {_mode = ACG::SceneGraph::DrawModes::DEFAULT;}
QString checkOpenGL();
//=========================================================================
// Callback for the scenegraph nodes
//=========================================================================
public:
/** \brief callback for the nodes, which send new render objects via this function.
*
* AddRenderObject is typically called by a scenegraph nodes during the collection of renderable
* objects.
*
* A renderobject is a collection of opengl states, buffers and parameters, that correspond to exactly one draw call.
*/
virtual void addRenderObject(ACG::RenderObject* _renderObject);
/**
Traverses the scenegraph and calls the getRenderObject function of each node.
Each node can then add multiple renderobjects via addRenderObject to this renderer.
Also collects all light sources in the scenegraph.
//=========================================================================
// Render object collection
//=========================================================================
private:
The currently active list of renderobjects is invalidated too.
*/
/** \brief Traverse the scenegraph to collect render information
*
* Traverses the scenegraph and calls the getRenderObject function of each node.
* Each node can then add multiple renderobjects via addRenderObject to this renderer.
*
* Also collects all light sources in the scenegraph.
* The currently active list of renderobjects is invalidated too.
*/
void collectRenderObjects(ACG::GLState* _glState, ACG::SceneGraph::DrawModes::DrawMode _drawMode, ACG::SceneGraph::BaseNode* _sceneGraphRoot);
/**
addRenderObject is typically called by a scenegraph node.
A renderobject is a collection of opengl states, buffers and parameters,
that correspond to exactly one draw call.
*/
virtual void addRenderObject(ACG::RenderObject* _renderObject);
/** \brief Scene graph traversal for render object collection
*
* Calls getRenderObjects on each node of the scenegraph recursively.
*/
void traverseRenderableNodes( ACG::GLState* _glState,
ACG::SceneGraph::DrawModes::DrawMode _drawMode,
ACG::SceneGraph::BaseNode* _node,
const ACG::SceneGraph::Material* _mat);
protected:
//=========================================================================
// Light node collection
//=========================================================================
private:
/**
compare function for qsort
*/
static int cmpPriority(const void*, const void*);
/** \brief Find all light sources in the scene
*
* Find all light nodes in the scene and save them for the next render() call.
*
* The lights are collected in lights_ while the corresponding type ends up in lightTypes_
*/
virtual void collectLightNodes(ACG::SceneGraph::BaseNode* _node);
/** \brief Light Node traverser
*
* Finds all Light Nodes in the scenegraph and stores them for later use. This is a recursive function.
*/
void traverseLightNodes(ACG::SceneGraph::BaseNode* _node);
/** Find all light nodes in the scene and save them for the next render() call.
Implemention is not possible here: SceneGraph::LightSource is not defined yet
*/
virtual void collectLightNodes(ACG::SceneGraph::BaseNode* _node);
//=========================================================================
// Sorting
//=========================================================================
private:
/** Calls getRenderObjects on each node of the scenegraph recursively.
/** \brief Compare priority of render objects
*
* compare function for qsort. This is required to compare render objects based
* on their prioerity and render them in the right order
*/
void traverseRenderableNodes(ACG::GLState* _glState, ACG::SceneGraph::DrawModes::DrawMode _drawMode, ACG::SceneGraph::BaseNode* _node, const ACG::SceneGraph::Material* _mat);
static int cmpPriority(const void*, const void*);
//=========================================================================
// Rendering
//=========================================================================
private:
/**
Bind vertex, index buffer and vertex format of a render object.
*/
/** \brief Binding VBOs (First state function)
*
* This is the first function called by renderObject().
*
* It binds vertex, index buffer and vertex format of a
* render object.