1#include <Engine/Rendering/Renderer.hpp>
3#include <Core/Asset/FileData.hpp>
4#include <Core/Geometry/MeshPrimitives.hpp>
5#include <Core/Utils/Log.hpp>
6#include <Engine/Data/Material.hpp>
7#include <Engine/Data/Mesh.hpp>
8#include <Engine/Data/ShaderConfigFactory.hpp>
9#include <Engine/Data/ShaderProgram.hpp>
10#include <Engine/Data/ShaderProgramManager.hpp>
11#include <Engine/Data/Texture.hpp>
12#include <Engine/Data/TextureManager.hpp>
13#include <Engine/Data/ViewingParameters.hpp>
14#include <Engine/OpenGL.hpp>
15#include <Engine/RadiumEngine.hpp>
16#include <Engine/Rendering/RenderObject.hpp>
17#include <Engine/Rendering/RenderObjectManager.hpp>
18#include <Engine/Scene/LightManager.hpp>
21#include <Engine/Scene/GeometryComponent.hpp>
23#include <globjects/Framebuffer.h>
25#include <globjects/Texture.h>
34using namespace Core::Utils;
37const GLenum buffers[] = { GL_COLOR_ATTACHMENT0,
44 GL_COLOR_ATTACHMENT7 };
48 m_quadMesh { nullptr },
49 m_depthTexture { nullptr },
50 m_fancyTexture { nullptr },
51 m_pickingFbo { nullptr },
52 m_pickingTexture { nullptr } {}
54Renderer::~Renderer() =
default;
58 auto resourcesRootDir { RadiumEngine::getInstance()->getResourcesDir() };
73 resourcesRootDir +
"Shaders/2DShaders/Basic2D.vert.glsl",
74 resourcesRootDir +
"Shaders/2DShaders/DrawScreen.frag.glsl" } );
77 resourcesRootDir +
"Shaders/2DShaders/Basic2D.vert.glsl",
78 resourcesRootDir +
"Shaders/2DShaders/DrawScreenI.frag.glsl" } );
81 resourcesRootDir +
"Shaders/2DShaders/Basic2D.vert.glsl",
82 resourcesRootDir +
"Shaders/2DShaders/CircleBrush.frag.glsl" } );
84 { {
"DisplayDepthBuffer" },
85 resourcesRootDir +
"Shaders/2DShaders/Basic2D.vert.glsl",
86 resourcesRootDir +
"Shaders/2DShaders//DepthDisplay.frag.glsl" } );
89 pickingPointsConfig.
addShader( Data::ShaderType_VERTEX,
90 resourcesRootDir +
"Shaders/Picking/Picking.vert.glsl" );
91 pickingPointsConfig.
addShader( Data::ShaderType_GEOMETRY,
92 resourcesRootDir +
"Shaders/Picking/PickingPoints.geom.glsl" );
93 pickingPointsConfig.
addShader( Data::ShaderType_FRAGMENT,
94 resourcesRootDir +
"Shaders/Picking/Picking.frag.glsl" );
98 CORE_ASSERT( pickingShader,
"Picking Shader is required for points" );
99 m_pickingShaders[Data::Displayable::PKM_POINTS] = *pickingShader;
103 pickingLinesConfig.
addShader( Data::ShaderType_VERTEX,
104 resourcesRootDir +
"Shaders/Picking/Picking.vert.glsl" );
105 pickingLinesConfig.
addShader( Data::ShaderType_GEOMETRY,
106 resourcesRootDir +
"Shaders/Picking/PickingLines.geom.glsl" );
107 pickingLinesConfig.
addShader( Data::ShaderType_FRAGMENT,
108 resourcesRootDir +
"Shaders/Picking/Picking.frag.glsl" );
112 CORE_ASSERT( pickingShader,
"Picking Shader is required for lines" );
113 m_pickingShaders[Data::Displayable::PKM_LINES] = *pickingShader;
117 pickingLinesAdjacencyConfig.
addShader( Data::ShaderType_VERTEX,
118 resourcesRootDir +
"Shaders/Picking/Picking.vert.glsl" );
119 pickingLinesAdjacencyConfig.
addShader( Data::ShaderType_GEOMETRY,
121 "Shaders/Picking/PickingLinesAdjacency.geom.glsl" );
122 pickingLinesAdjacencyConfig.
addShader( Data::ShaderType_FRAGMENT,
123 resourcesRootDir +
"Shaders/Picking/Picking.frag.glsl" );
128 CORE_ASSERT( pickingShader,
"Picking Shader is required for lines adjacency" );
129 m_pickingShaders[Data::Displayable::PKM_LINE_ADJ] = *pickingShader;
133 pickingTrianglesConfig.
addShader( Data::ShaderType_VERTEX,
134 resourcesRootDir +
"Shaders/Picking/Picking.vert.glsl" );
135 pickingTrianglesConfig.
addShader( Data::ShaderType_GEOMETRY,
137 "Shaders/Picking/PickingTriangles.geom.glsl" );
138 pickingTrianglesConfig.
addShader( Data::ShaderType_FRAGMENT,
139 resourcesRootDir +
"Shaders/Picking/Picking.frag.glsl" );
143 CORE_ASSERT( pickingShader,
"Picking Shader is required for triangles" );
144 m_pickingShaders[Data::Displayable::PKM_TRI] = *pickingShader;
148 texparams.image.width = m_width;
149 texparams.image.height = m_height;
150 texparams.image.target = GL_TEXTURE_2D;
151 texparams.sampler.
minFilter = GL_NEAREST;
152 texparams.sampler.
magFilter = GL_NEAREST;
154 texparams.name =
"Depth";
156 texparams.image.format = GL_DEPTH_COMPONENT;
157 texparams.image.type = GL_UNSIGNED_INT;
160 m_pickingFbo = std::make_unique<globjects::Framebuffer>();
161 texparams.name =
"Picking";
163 texparams.image.format = GL_RGBA_INTEGER;
164 texparams.image.type = GL_INT;
165 m_pickingTexture = std::make_unique<Data::Texture>( texparams );
168 texparams.name =
"Final image";
170 texparams.image.format = GL_RGBA;
171 texparams.image.type = GL_SCALAR;
178 Core::Geometry::TriangleMesh mesh =
179 Core::Geometry::makeZNormalQuad( Core::Vector2( -1.f, 1.f ) );
181 auto qm = std::make_unique<Data::Mesh>(
"quad" );
184 m_quadMesh->updateGL();
188 resize( m_width, m_height );
193 CORE_ASSERT( RadiumEngine::getInstance() !=
nullptr,
"Engine is not initialized." );
195 if ( query.m_screenCoords.x() < 0 || query.m_screenCoords.x() > m_width - 1 ||
196 query.m_screenCoords.y() < 0 || query.m_screenCoords.y() > m_height - 1 ) {
204 CORE_UNUSED( renderLock );
207 saveExternalFBOInternal();
210 feedRenderQueuesInternal( renderData );
211 updateRenderObjectsInternal( renderData );
214 m_pickingFbo->bind();
219 GL_ASSERT( glDepthMask( GL_TRUE ) );
220 GL_ASSERT( glColorMask( 1, 1, 1, 1 ) );
221 GL_ASSERT( glDrawBuffers( 1, buffers ) );
223 float clearDepth = 1.0;
224 int clearColor[] = { -1, -1, -1, -1 };
226 GL_ASSERT( glClearBufferiv( GL_COLOR, 0, clearColor ) );
227 GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
229 splitRenderQueuesForPicking( renderData );
232 GL_ASSERT( glEnable( GL_DEPTH_TEST ) );
233 GL_ASSERT( glDepthFunc( GL_LESS ) );
235 renderForPicking( renderData, m_pickingShaders, m_fancyRenderObjectsPicking );
238 m_pickingFbo->readPixels( { {
static_cast<int>( query.m_screenCoords.x() ),
239 static_cast<int>( query.m_screenCoords.y() ),
245 result.setDepth( depth );
251 GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
252 renderForPicking( renderData, m_pickingShaders, m_debugRenderObjectsPicking );
254 GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
255 renderForPicking( renderData, m_pickingShaders, m_xrayRenderObjectsPicking );
261 GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
262 for ( uint i = 0; i < m_pickingShaders.size(); ++i ) {
263 m_pickingShaders[i]->bind();
264 m_pickingShaders[i]->setUniform(
"transform.proj", renderData.projMatrix );
265 m_pickingShaders[i]->setUniform(
"transform.view", renderData.viewMatrix );
267 for (
const auto& ro : m_uiRenderObjectsPicking[i] ) {
268 if ( ro->isVisible() && ro->isPickable() ) {
269 m_pickingShaders[i]->setUniform(
"objectId", ro->getIndex().getValue() );
271 Core::Matrix4 M = ro->getTransformAsMatrix();
272 Core::Matrix4 MV = renderData.viewMatrix * M;
273 Scalar d = MV.block<3, 1>( 0, 3 ).norm();
275 Core::Matrix4 S = Core::Matrix4::Identity();
276 S( 0, 0 ) = S( 1, 1 ) = S( 2, 2 ) = d;
279 Core::Matrix4 N = M.inverse().transpose();
281 m_pickingShaders[i]->setUniform(
"transform.model", M );
282 m_pickingShaders[i]->setUniform(
"transform.worldNormal", N );
285 ro->getMesh()->render( m_pickingShaders[i] );
295 m_pickingFbo->readPixels( GL_COLOR_ATTACHMENT0,
296 { {
static_cast<int>( query.m_screenCoords.x() ),
297 static_cast<int>( query.m_screenCoords.y() ),
303 result.setRoIdx( pick[0] );
304 result.
addIndex( { pick[2], pick[1], pick[3] } );
305 result.setMode( query.m_mode );
306 m_pickingFbo->unbind();
310 restoreExternalFBOInternal();
316 if ( !m_initialized )
return;
318 CORE_ASSERT( RadiumEngine::getInstance() !=
nullptr,
"Engine is not initialized." );
321 CORE_UNUSED( renderLock );
326 saveExternalFBOInternal();
332 feedRenderQueuesInternal( data );
339 updateRenderObjectsInternal( data );
345 m_pickingResults.clear();
346 if ( !m_pickingQueries.empty() ) { doPicking( data ); }
347 m_lastFramePickingQueries = m_pickingQueries;
348 m_pickingQueries.clear();
367 drawScreenInternal();
374 notifyRenderObjectsRenderingInternal();
377void Renderer::saveExternalFBOInternal() {
378 RadiumEngine::getInstance()->pushFboAndViewport();
380 glViewport( 0, 0,
int( m_width ),
int( m_height ) );
385 for (
auto& ro : m_fancyRenderObjects ) {
388 for (
auto& ro : m_xrayRenderObjects ) {
391 for (
auto& ro : m_debugRenderObjects ) {
394 for (
auto& ro : m_uiRenderObjects ) {
399void Renderer::feedRenderQueuesInternal(
const Data::ViewingParameters& ) {
400 m_fancyRenderObjects.clear();
401 m_debugRenderObjects.clear();
402 m_uiRenderObjects.clear();
403 m_xrayRenderObjects.clear();
410 for (
auto it = m_fancyRenderObjects.begin(); it != m_fancyRenderObjects.end(); ) {
411 if ( ( *it )->isXRay() ) {
412 m_xrayRenderObjects.push_back( *it );
413 it = m_fancyRenderObjects.erase( it );
418 for (
auto it = m_debugRenderObjects.begin(); it != m_debugRenderObjects.end(); ) {
419 if ( ( *it )->isXRay() ) {
420 m_xrayRenderObjects.push_back( *it );
421 it = m_debugRenderObjects.erase( it );
426 for (
auto it = m_uiRenderObjects.begin(); it != m_uiRenderObjects.end(); ) {
427 if ( ( *it )->isXRay() ) {
428 m_xrayRenderObjects.push_back( *it );
429 it = m_uiRenderObjects.erase( it );
439 for (
auto& q : renderQueuePicking ) {
444 for (
auto& roPtr : renderQueue ) {
445 auto mode = roPtr->getMesh()->pickingRenderMode();
446 if ( mode != Data::Displayable::NO_PICKING )
447 renderQueuePicking[size_t( mode )].push_back( roPtr );
451void Renderer::splitRenderQueuesForPicking(
const Data::ViewingParameters& ) {
452 splitRQ( m_fancyRenderObjects, m_fancyRenderObjectsPicking );
453 splitRQ( m_debugRenderObjects, m_debugRenderObjectsPicking );
454 splitRQ( m_uiRenderObjects, m_uiRenderObjectsPicking );
455 splitRQ( m_xrayRenderObjects, m_xrayRenderObjectsPicking );
459void Renderer::renderForPicking(
460 const Data::ViewingParameters& renderData,
463 for ( uint i = 0; i < pickingShaders.
size(); ++i ) {
464 pickingShaders[i]->bind();
465 pickingShaders[i]->setUniform(
"transform.proj", renderData.projMatrix );
466 pickingShaders[i]->setUniform(
"transform.view", renderData.viewMatrix );
467 for (
const auto& ro : renderQueuePicking[i] ) {
468 if ( ro->isVisible() && ro->isPickable() ) {
469 pickingShaders[i]->setUniform(
"objectId", ro->getIndex().getValue() );
470 Core::Matrix4 M = ro->getTransformAsMatrix();
471 Core::Matrix4 N = M.inverse().transpose();
472 pickingShaders[i]->setUniform(
"transform.model", M );
473 pickingShaders[i]->setUniform(
"transform.worldNormal", N );
475 auto pointCloud =
dynamic_cast<Scene::PointCloudComponent*
>( ro->getComponent() );
477 pickingShaders[i]->setUniform(
"pointCloudSplatRadius",
478 pointCloud->getSplatSize() / 2.f );
481 ro->getMesh()->render( pickingShaders[i] );
487void Renderer::doPicking(
const Data::ViewingParameters& renderData ) {
488 m_pickingResults.reserve( m_pickingQueries.size() );
490 m_pickingFbo->bind();
491 preparePicking( renderData );
494 GL_ASSERT( glReadBuffer( GL_COLOR_ATTACHMENT0 ) );
497 for (
const PickingQuery& query : m_pickingQueries ) {
498 PickingResult result;
502 if ( query.m_screenCoords.x() < 0 || query.m_screenCoords.x() > m_width - 1 ||
503 query.m_screenCoords.y() < 0 || query.m_screenCoords.y() > m_height - 1 ) {
505 m_pickingResults.push_back( {} );
508 GL_ASSERT( glReadPixels( query.m_screenCoords.x(),
509 query.m_screenCoords.y(),
515 result.setRoIdx( pick[0] );
516 result.addIndex( { pick[2], pick[1], pick[3] } );
522 for (
auto i = -m_brushRadius; i <= m_brushRadius; i += 3 ) {
524 for (
auto j = -h; j <= +h; j += 3 ) {
525 const int x = query.m_screenCoords.x() + i;
526 const int y = query.m_screenCoords.y() - j;
528 if ( x < 0 || x >
int( m_width ) - 1 || y < 0 || y >
int( m_height ) - 1 ) {
531 GL_ASSERT( glReadPixels( x, y, 1, 1, GL_RGBA_INTEGER, GL_INT, pick ) );
532 resultPerRO[pick[0]].setRoIdx( pick[0] );
533 resultPerRO[pick[0]].addIndex( { pick[2], pick[1], pick[3] } );
542 return a.second.getIndices().size() < b.second.getIndices().size();
544 result = itr->second;
546 result.setMode( query.m_mode );
547 m_pickingResults.push_back( result );
550 m_pickingFbo->unbind();
552void Renderer::preparePicking(
const Data::ViewingParameters& renderData ) {
554 GL_ASSERT( glDepthMask( GL_TRUE ) );
555 GL_ASSERT( glColorMask( 1, 1, 1, 1 ) );
556 GL_ASSERT( glDrawBuffers( 1, buffers ) );
558 float clearDepth = 1.0;
559 int clearColor[] = { -1, -1, -1, -1 };
561 GL_ASSERT( glClearBufferiv( GL_COLOR, 0, clearColor ) );
562 GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
564 splitRenderQueuesForPicking( renderData );
567 GL_ASSERT( glEnable( GL_DEPTH_TEST ) );
568 GL_ASSERT( glDepthFunc( GL_LESS ) );
570 renderForPicking( renderData, m_pickingShaders, m_fancyRenderObjectsPicking );
574 GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
575 renderForPicking( renderData, m_pickingShaders, m_debugRenderObjectsPicking );
578 GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
579 renderForPicking( renderData, m_pickingShaders, m_xrayRenderObjectsPicking );
585 GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
586 for ( uint i = 0; i < m_pickingShaders.size(); ++i ) {
587 m_pickingShaders[i]->bind();
588 m_pickingShaders[i]->setUniform(
"transform.proj", renderData.projMatrix );
589 m_pickingShaders[i]->setUniform(
"transform.view", renderData.viewMatrix );
591 for (
const auto& ro : m_uiRenderObjectsPicking[i] ) {
592 if ( ro->isVisible() && ro->isPickable() ) {
593 m_pickingShaders[i]->setUniform(
"objectId", ro->getIndex().getValue() );
595 Core::Matrix4 M = ro->getTransformAsMatrix();
596 Core::Matrix4 MV = renderData.viewMatrix * M;
597 Scalar d = MV.block<3, 1>( 0, 3 ).norm();
599 Core::Matrix4 S = Core::Matrix4::Identity();
600 S( 0, 0 ) = S( 1, 1 ) = S( 2, 2 ) = d;
603 Core::Matrix4 N = M.inverse().transpose();
605 m_pickingShaders[i]->setUniform(
"transform.model", M );
606 m_pickingShaders[i]->setUniform(
"transform.worldNormal", N );
609 ro->getMesh()->render( m_pickingShaders[i] );
615void Renderer::restoreExternalFBOInternal() {
616 RadiumEngine::getInstance()->popFboAndViewport();
619void Renderer::drawScreenInternal() {
621 restoreExternalFBOInternal();
624 GL_ASSERT( glDepthFunc( GL_ALWAYS ) );
634 m_quadMesh->render( shader );
636 GL_ASSERT( glDepthFunc( GL_LESS ) );
639 if ( m_brushRadius > 0 ) {
640 GL_ASSERT( glDisable( GL_BLEND ) );
641 GL_ASSERT( glDisable( GL_DEPTH_TEST ) );
644 shader->setUniform(
"mousePosition", m_mousePosition );
645 shader->setUniform(
"brushRadius", m_brushRadius );
646 shader->setUniform(
"dim", Core::Vector2( m_width, m_height ) );
647 m_quadMesh->render( shader );
648 GL_ASSERT( glEnable( GL_DEPTH_TEST ) );
649 GL_ASSERT( glEnable( GL_BLEND ) );
653void Renderer::notifyRenderObjectsRenderingInternal() {
654 for (
auto& ro : m_fancyRenderObjects ) {
655 ro->hasBeenRenderedOnce();
658 for (
auto& ro : m_debugRenderObjects ) {
659 ro->hasBeenRenderedOnce();
662 for (
auto& ro : m_xrayRenderObjects ) {
663 ro->hasBeenRenderedOnce();
666 for (
auto& ro : m_uiRenderObjects ) {
667 ro->hasBeenRenderedOnce();
675 if ( ( w != 0 && h != 0 ) && ( !m_initialized || ( w != m_width || h != m_height ) ) ) {
679 m_pickingTexture->resize( m_width, m_height );
682 m_pickingFbo->bind();
683 m_pickingFbo->attachTexture( GL_DEPTH_ATTACHMENT,
m_depthTexture->getGpuTexture() );
684 m_pickingFbo->attachTexture( GL_COLOR_ATTACHMENT0, m_pickingTexture->getGpuTexture() );
685 if ( m_pickingFbo->checkStatus() != GL_FRAMEBUFFER_COMPLETE ) {
686 LOG( logERROR ) <<
"File " << __FILE__ <<
"(" << __LINE__ <<
") Picking FBO Error "
687 << m_pickingFbo->checkStatus();
689 m_pickingFbo->unbind();
692 m_initialized =
true;
725 GL_ASSERT( glGetTexImage( GL_TEXTURE_2D, 0, GL_RGBA, GL_SCALAR, pixels.get() ) );
730 for ( uint j = 0; j < tex->
getHeight(); ++j ) {
731 for ( uint i = 0; i < tex->
getWidth(); ++i ) {
732 auto in = 4 * ( j * tex->
getWidth() + i );
736 writtenPixels[ou + 0] =
737 (uchar)std::clamp(
float( pixels[in + 0] * 255.f ),
float( 0 ),
float( 255 ) );
738 writtenPixels[ou + 1] =
739 (uchar)std::clamp(
float( pixels[in + 1] * 255.f ),
float( 0 ),
float( 255 ) );
740 writtenPixels[ou + 2] =
741 (uchar)std::clamp(
float( pixels[in + 2] * 255.f ),
float( 0 ),
float( 255 ) );
742 writtenPixels[ou + 3] =
743 (uchar)std::clamp(
float( pixels[in + 3] * 255.f ),
float( 0 ),
float( 255 ) );
748 return writtenPixels;
753 m->addLight( light );
766 if ( renderObjects.
size() > 0 ) {
767 for (
auto& ro : renderObjects ) {
T back_inserter(T... args)
void addShader(ShaderType type, const std::string &name)
Core::Utils::optional< const Data::ShaderProgram * > addShaderProgram(const Data::ShaderConfiguration &config)
const Data::ShaderProgram * getShaderProgram(const std::string &id)
void reloadAllShaderPrograms()
Represent a Texture of the engine.
void bind(int unit=-1)
Bind the texture to GPU texture unit to enable its use in a shader. Need active OpenGL context.
const TextureParameters & getParameters() const
get read access to texture parameters
Result of a PickingQuery.
void addIndex(const std::tuple< int, int, int > &idx)
Add new ids to the result.
Data::ShaderProgramManager * m_shaderProgramManager
void render(const Data::ViewingParameters &renderData)
Tell the renderer it needs to render. This method does the following steps :
void initialize(uint width, uint height)
Initialize renderer.
PickingResult doPickingNow(const PickingQuery &query, const Data::ViewingParameters &renderData)
virtual void displayTexture(const std::string &texName)
Change the texture that is displayed on screen. Set m_displayedIsDepth to true if depth linearization...
virtual void debugInternal(const Data::ViewingParameters &renderData)=0
Add the debug layer with useful informations.
void resize(uint width, uint height)
Resize the viewport and all the screen textures, fbos. This function must be overrided as soon as som...
bool hasLight() const
Tell if the renderer has an usable light.
virtual void addLight(const Scene::Light *light)
std::vector< Ra::Engine::Scene::LightManager * > m_lightmanagers
virtual void postProcessInternal(const Data::ViewingParameters &renderData)=0
Do all post processing stuff. If you override this method, be careful to fill.
Data::Texture * getDisplayTexture() const
virtual bool buildRenderTechnique(RenderObject *ro) const =0
int buildAllRenderTechniques() const
std::unique_ptr< Data::Texture > m_fancyTexture
Final color texture : might be attached to the main framebuffer.
Data::Texture * m_displayedTexture
The texture that will be displayed on screen. If no call to.
virtual void reloadShaders()
virtual std::vector< std::string > getAvailableTextures() const
Return the names of renderer available textures.
virtual std::unique_ptr< uchar[]> grabFrame(size_t &w, size_t &h) const
@ C_VERTEX
Picks all vertices of a mesh within a screen space circle.
RenderObjectManager * m_renderObjectManager
virtual void initializeInternal()=0
initializeInternal Initialize the renderer dependant resources.
virtual void uiInternal(const Data::ViewingParameters &renderData)=0
Draw the UI data.
std::map< std::string, Data::Texture * > m_secondaryTextures
Textures exposed in the texture section box to be displayed.
std::unique_ptr< Data::Texture > m_depthTexture
Depth texture : might be attached to the main framebuffer.
virtual void updateStepInternal(const Data::ViewingParameters &renderData)=0
virtual void resizeInternal()=0
virtual void renderInternal(const Data::ViewingParameters &renderData)=0
All the scene rendering magics basically happens here.
T emplace_back(T... args)
void addConfiguration(const ShaderConfiguration &config)
@ UI
"Ui" render objects are drawn on top of everything else and view independant
@ Debug
"Debug" render objects are drawn like any other object (not lit though)
@ Geometry
"Geometry" render objects are those loaded using Radium::IO and generated by GeometrySystem
hepler function to manage enum as underlying types in VariableSet
GLenum internalFormat
OpenGL internal format (WARNING, for Integer textures, must be GL_XXX_INTEGER)
GLenum minFilter
OpenGL minification filter ( GL_LINEAR or GL_NEAREST or GL_XXX_MIPMAP_YYY )
GLenum magFilter
OpenGL magnification filter ( GL_LINEAR or GL_NEAREST )
Describes the sampler and image of a texture.
the set of viewing parameters extracted from the camera and given to the renderer