Introduction
- Open Inventor render engine
- Custom nodes
- Pure Open Inventor custom nodes
- OpenGL custom nodes
- Guidelines
- Common principles
- OpenGL render modes
- Examples of OpenGL Open Inventor custom node
Appendices
- Appendix A: A Open Inventor state against OpenGL state
- Appendix B: OpenGL compatibility custom node source code example
- Appendix C: OpenGL core custom node source code example
- Appendix D: SoGLCallback source code example
Introduction
Open Inventor render engine
Starting with Open Inventor 10, all graphics API calls are executed by a render engine, a software layer between hardware and Open Inventor public API (see Figure 1.1). This render engine has changed the product design, as the Open Inventor public API is now independent of any graphics API. This paradigm has many advantages, including, maintenance, optimizations and future graphics API support. On the other hand, new concepts introduced in Open Inventor 10 involve changes in user code, especially for custom nodes which require strict guidelines comparing to previous versions.
Figure 1.1: Open Inventor simplifed architecture. Open Inventor public API is the higher layer which represents the user API. The render engine is the intermediate layer between Open Inventor and graphics API which contains a specialized backend layer for communication with graphics API, here the OpenGL API.
As shown in Figure 1.2, during a SoGLRenderAction, the scene graph traversal generates a set of render engine commands which represent a description of the scene (materials, shapes, and so on). Note that during the scene graph traversal, there is no call to any low-level graphics API. In the same way, render engine commands have no dependencies on the scene graph API. When the scene graph traversal is finished, the generated commands are executed by the render engine, which makes the necessary low-level rendering calls.
Figure 1.2: SoGLRenderAction flow chart. First, the function apply is called on a scene graph which leads to a scene graph traversal. During the traversal, a set of commands is setup that represents an abstraction of the rendering execution for the render engine. Finally, commands are submitted to the render engine in order to be transformed into rendering calls.
Custom Nodes
Introduced with Open Inventor 10, each node contains a SbRenderEngineMode static class member which defines how a node is executed by the Open Inventor render engine. The render mode, of type SoNode::RenderMode, should be defined with one of the following values:
-
-
- OIV_OPENINVENTOR_RENDERING, for custom nodes that use only Open Inventor API (see Section 2).
- OIV_OPENGL_COMPATIBILITY_RENDERING, for custom nodes that work in OpenGL compatibility profile.
- OIV_OPENGL_CORE_RENDERING, for custom nodes that work in OpenGL core profile.
-
Please refer to Chapters Pure Open Inventor custom nodes and OpenGL custom nodes for details of what the choice of a render mode involves.
Accessible via the getClassRenderEngineMode static function, the render mode must be initialized during the static class initialization (i.e., initClass function) such as shown in Listing 1.1.
void CustomNode::initClass() { getClassRenderEngineMode().setRenderMode(OIV_OPENINVENTOR_RENDERING); SO_NODE_INIT_CLASS(CustomNode, "CustomNode", SoNode ); } |
Listing 1.1: Class initialization function for a custom node, named CustomNode inherited from SoNode which uses only Open Inventor API.
According to the architecture of Open Inventor 10 (see Open Inventor render engine), these rendering modes are mandatory to know how to execute a node. Actually, nodes from the Open Inventor public API generate render engine commands at shape level during scene graph traversal. Custom nodes with a render mode set to OIV_OPENINVENTOR_RENDERING indirectly benefit from the capability of Open Inventor nodes to generate render engine commands.
However, custom nodes that need to call low-level graphics API functions directly must be threated differently. Indeed, Open Inventor public API and graphics API do not work on the same layer (see Figure 1.1). This means that these custom nodes must be executed at the render engine level in order to communicate with graphics API. Thus, when render mode is set to OIV_OPENGL_COMPATIBILITY_RENDERING or OIV_OPENGL_CORE_RENDERING, custom nodes are executed at the render engine level in order to access to graphics API functions. The most important consequence of this behavior is that Open Inventor and low-level graphics API calls cannot be mixed in the same custom node since these APIs do not work on the same layer. Implementation details, limitations and guidelines for all kind of custom nodes are defined in the remainder of this documentation.
Pure Open Inventor custom nodes
A pure Open Inventor custom node implements its custom actions -- including rendering -- using a set of other nodes. It presents an abstraction that hides the traversal of those nodes from the user. The initialization of a custom node is shown in Listing 1.1.
The recommended usage is to build a scene graph that corresponds to the expected rendering result. This scene graph will be traversed during a SoAction using the forwardTraversal function, for instance during the render action:
void CustomNode::GLRender(SoGLRenderAction* action) { // supposed m_sceneGraph is a group, previously initialized action->forwardTraversal(m_sceneGraph); } |
One of the main advantages of using the forwardTraversal function instead of calling the GLRender fonction of the embedded scene graph is that paths are preserved. Paths (i.e., SoPath) are a key point for Open Inventor scene graph management (e.g., picking, cache and so on) and thus, it's mandatory to use this function for traversing a scene graph. Calling the GLRendere function of a node may lead to unexpected results.
WARNING: Do not render nodes in the internal scene graph by calling their GLRender() method. Custom nodes MUST call action->forwardTraversal() to render nodes in the internal scene graph. Calling GLRender() in this situation is likely to cause a crash.
OpenGL custom nodes
Guidelines
A custom node that makes OpenGL calls must set its render mode to
-
-
- OIV_OPENGL_COMPATIBILITY_RENDERING or,
- OIV_OPENGL_CORE_RENDERING
-
for using OpenGL calls. These calls are allowed only in the custom node class implementation of the virtual SoNode::GLRender function. The effect of OpenGL calls outside this function is undefined. The GLRender function of such nodes is not executed during the SoGLRenderAction traversal; its execution is postponed later for an execution by the Open Inventor render engine, as explained in Section Custom nodes. This imposes some guidelines and limitations when you consider using OpenGL API.
Mixing OpenGL and Open Inventor API calls is not allowed. This means that the GLRender function of an OpenGL custom node must contain only OpenGL calls. However, Open Inventor base types (e.g., SbVec3f, SbColor) can be used as well as queries of Open Inventor state elements. The Open Inventor state (i.e., SoState) is available via the SoGLRenderAction and can be queried to determine the current graphics configuration. During the execution of such node, the OpenGL state is initialized according to the Open Inventor state (see Section A Open Inventor state against OpenGL state for more details). This allows one to determine the rendering state bye querying the Open Inventor state instead of the OpenGL state, which would require using the expensive glGet functions.
Note:
Do NOT use SoLazyElement to get information about the materials in the traversal state list. SoLazyElement is deprecated in Open Inventor 10 and the values returned are not reliable. Custom nodes should be modified to use the new SoMaterialElement, which has similar methods to get, for example, the number of diffuse colors in the state list.
The execution of a custom node is self-contained. As a result, this kind of nodes is executed using its own render action and thus, its own Open Inventor state. In this way, modification of the Open Inventor state is only valid within the GLRender function but has no effect on the rendering state. The OpenGL state is set up according to the Open Inventor state before the traversal of this node and restored after the execution of the GLRender function. So, a change of state has no effect outside the node, both for Open Inventor and OpenGL states. Note that the ability to run OpenGL calls has a cost, and the execution of such nodes may be time consuming.
OpenGL version 4.1 is the minimum requirement for Open Inventor 10. All functionnalities defined by this version of OpenGL are available in an OpenGL custom node. Extensions and features from higher versions are available if the hardware used for your application supports them.
OpenGL render modes
Depending on the render mode, the setup of an OpenGL custom node should fulfill OpenGL specification for compatibility and core profiles. The specifications for the two possible render modes are described in the following.
OIV_OPENGL_COMPATIBILITY_RENDERING
In this compatibility mode, the behavior for OpenGL is the one defined by OpenGL specification version 4.1 compatibility profile. In this mode, Open Inventor initializes fixed-function vertex transformations, and fixed-function vertex lighting, and color properties according the Open Inventor state. In this way, no shader is requiered for rendering but, if a user-defined GLSL shader is set on the state, built-in language variables are accessible. Note that the Open Inventor shader API is also available. It is recommended to use this API to be independent from the render mode.
This compatibility mode is time-consuming since both fixed-function properties and Open Inventor shader API are initialized. However, this rendering mode facilitates porting an existing application from OpenGL to Open Inventor.
OIV_OPENGL_CORE_RENDERING
The OpenGL core rendering mode behavior is described by OpenGL specification version 4.1 core profile. This mode is qualified as a full shader pipeline since fixed-function helpers are no longer available. Therefore, a user-defined shader is mandatory when using this render mode. The Open Inventor shader API covers all the fixed-function OpenGL state supported by Open Inventor.
Note that only core profile OpenGL calls can be used here. This mode is mandatory if you plan to use OpenGL custom nodes on Mac OS.
Examples of OpenGL Open Inventor custom node
OpenGL compatibility profile
The header file of an OpenGL compatibility custom node is shown in Listing 3.1. In this example, a node, inherited from SoShape, is defined with appropriate function definitions. This header is similar to the header of a classic custom node, except for the OpenGL types defined in private members.
#pragma once #include <Inventor/nodes/SoShape.h> #include <Inventor/sys/SoGL.h>/** * @brief Custom OpenGL (OpenGL 2) node */ class CustomNodeOpenGLCompatibility : public SoShape { SO_NODE_HEADER(CustomNodeOpenGLCompatibility );public : /** * Default constructor */ CustomNodeOpenGLCompatibility ();protected : // destructor virtual ~ CustomNodeOpenGLCompatibility ();// not implemented here void generatePrimitives(SoAction* /*action*/) {}// create OpenGL resources void initialize();// destroy OpenGL resources void destroy();SoEXTENDER public :// Rendering stuff virtual void GLRender ( SoGLRenderAction* action );// BBox computation virtual void computeBBox(SoAction* action , SbBox3f &box , SbVec3f& center);SoINTERNAL public : static void initClass (); private : |
Listing 3.1: Header file for OpenGL compatibility custom node.
The implementation first defines the render mode during the class initialization as shown in Listing 3.2.
void CustomNodeOpenGLCompatibility::initClass() { getClassRenderEngineMode().setRenderMode(SbRenderEngineMode:: OIV_OPENGL_COMPATIBILITY_RENDERING ); SO__NODE_INIT_CLASS(CustomNodeOpenGLCompatibility, "CustomNodeOpenGLCompatibility", SoShape ); } |
Listing 3.2: Class initialization function for OpenGL compatibility custom node.
The GLRender function of this node uses OpenGL functions as shown in Listing 3.3. Note that, here, the state from the SoGLRenderAction is queried for conditionnal rendering based on lighting properties.
void CustomNodeOpenGLCompatibility::GLRender(SoGLRenderAction* action) { // create resources initialize();glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, 0);SoState* state = action->getState(); SoLightModelElement::Model model = SoLightModelElement::get(state);if (model == SoLightModelElement::PHONG || model == SoLightModelElement::PER_VERTEX_PHONG) { glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); glColorMaterial(GL_FRONT_AND_BACK , GL_DIFFUSE); glEnable(GL_COLOR_MATERIAL); glEnableClientState(GL_COLOR_ARRAY); glColorPointer(3, GL_FLOAT , 0, 0);glBindBuffer(GL_ARRAY_BUFFER, m_normalBuffer); glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer(GL_FLOAT , 0, 0); } glDrawArrays ( GL_TRIANGLES , 0, numVertices );if (model == SoLightModelElement::PHONG || model == SoLightModelElement::PER_VERTEX_PHONG) { glDisable(GL_COLOR_MATERIAL); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); } glDisableClientState(GL_VERTEX_ARRAY); } |
Listing 3.3: GLRender function for OpenGL compatibility custom node.
The complete source code for this node is available in Appendix B.
OpenGL core profile
The header file of an OpenGL core custom node is described in Listing 3.4. In this example, a node, inherited from SoShape, is defined with appropriate function definitions. This header is similar to the header of a classic custom node, except for the OpenGL types defined in private members.
#pragma once #include <Inventor/nodes/SoShaderProgram.h> #include <Inventor/nodes/SoShape.h> #include <Inventor/sys/SoGL.h>/** * @brief Custom OpenGL (OpenGL 4 Core) node */ class CustomNodeOpenGLCore : public SoShape { SO_NODE_HEADER(CustomNodeOpenGLCore);public : /** * Default constructor */ CustomNodeOpenGLCore();protected : // destructor virtual ~CustomNodeOpenGLCore();// not implemented here void generatePrimitives(SoAction* /*action*/) {}// create OpenGL resources void initialize();// destroy OpenGL resources void destroy();SoEXTENDER public :// Rendering stuff virtual void GLRender (SoGLRenderAction* action);// BBox computation virtual void computeBBox(SoAction* action , SbBox3f &box , SbVec3f& center);SoINTERNAL public : static void initClass(); private : |
Listing 3.4: Header file for OpenGL core custom node.
The implementation first defines the render mode during the class initialization as shown in Listing 3.5.
void CustomNodeOpenGLCore::initClass() { getClassRenderEngineMode().setRenderMode(SbRenderEngineMode:: OIV_OPENGL_CORE_RENDERING); SO__NODE_INIT_CLASS(CustomNodeOpenGLCore, "CustomNodeOpenGLCore", SoShape ); } |
Listing 3.5: Class initialization function for OpenGL core custom node.
The GLRender function of this node uses OpenGL functions as shown in Listing 3.6. Here, the functions to manage attributes are part of the core profile instead of the legacy ones in the compatibility examples (see Listing 3.3).
void CustomNodeOpenGLCore::GLRender(SoGLRenderAction* action) { // create resources initialize();glEnableClientState(0); glEnableClientState(1); glEnableClientState(2);// vertex positions glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer ); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);// vertex colors glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);// vertex normals glBindBuffer(GL_ARRAY_BUFFER, m_normalBuffer); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);glDrawArrays(GL_TRIANGLES, 0, numVertices);glDisableClientState(2); glDisableClientState(1); glDisableClientState(0); } |
Listing 3.6: GLRender function for OpenGL core custom node.
Note that this kind of node needs a shader program during rendering. Therefore an SoShaderProgram node must lie in the scene graph before this node. The vertex shader header can specify the layout location of the attributes (i.e., positions, colors and normals) as shown in Listing 3.7 to avoid link issues with the underlying GLSL program.
layout(location = 0) in vec3 position; layout(location = 1) in vec3 color; layout(location = 2) in vec3 normal; |
Listing 3.7: GLSL vertex shader attribute declarations for OpenGL core custom node example.
The complete C++ and shader source code for this node is available in Appendix C.
SoGLCallback
The SoGLCallback class provides another way of making OpenGL calls, using an interface similar to SoCallback. However, it has a significant limitation: the type of the render mode must be OIV_OPENGL_COMPATIBILITY_RENDERING. The prototype of the callback function is the same as SoCallback's (see Listing 3.8). The main difference between SoGLCallback and SoCallback classes is that SoGLRenderAction is the only action that can traverse an SoCallback object.
void CustomNodeGLCallback::callback(void* data, SoAction* action) { CustomNodeGLCallback* cb = static_cast<CustomNodeGLCallback*>(data); // create resources cb->initialize();glBindBuffer(GL_ARRAY_BUFFER, cb->m_vertexBuffer); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, 0);SoState* state = action->getState(); SoLightModelElement::Model model = SoLightModelElement::get(state);if (model == SoLightModelElement::PHONG || model == SoLightModelElement::PER_VERTEX_PHONG) { glBindBuffer(GL_ARRAY_BUFFER, cb->m_colorBuffer); glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); glEnable(GL_COLOR_MATERIAL); glEnableClientState(GL_COLOR_ARRAY); glColorPointer(3, GL_FLOAT, 0, 0);glBindBuffer(GL_ARRAY_BUFFER, cb->m_normalBuffer); glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer(GL_FLOAT, 0, 0); }glDrawArrays(GL_TRIANGLES, 0, numVertices);if (model == SoLightModelElement::PHONG || model == SoLightModelElement::PER_VERTEX_PHONG) { glDisable(GL_COLOR_MATERIAL); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); } glDisableClientState(GL_VERTEX_ARRAY); } |
Listing 3.8: Callback function for SoGLCallback.
Using a custom node instead of an SoGLCallback is recommended in new code. This gives better control of all node behavior in addition to rendering, such as bounding box computation, ray picking, etc. The complete source code is available in Appendix D.
Appendices
Appendix A: A Open Inventor state against OpenGL state
Categories | Inventor elements | OpenGL related functions |
Rasterizer | SoDrawStyleElement SoShapeHintsElementSoLineWidthElement SoPointSizeElement SoPolygonOffsetElement SoLinePatternElement |
glPolygonMode glCullFace glFrontFace glLineWidth glPointSize glPolygonOffset glLineStipple |
Viewport and scissor | SoUpdateAreaElement SoViewportRegionElement |
glScissor glViewport |
Blending and color | SoBlendElement
SoColorMaskElement |
glBlendEquation glBlendFunc glBlendColor glColorMask glLogicOp |
Depth/stencil buffer | SoDepthBufferElement
SoStencilBufferElement |
glDepthMask glDepthFunc glStencilOpSeparate glStencilMaskSeparate glStencilMaskSeparate |
Transform | SoProjectionMatrixElement SoViewMatrixElement SoModelMatrixElement SoTextureMatrixElementSo*MatrixElement |
glMatrixMode -> GL PROJECTION glMatrixMode -> GL MODELVIEW glMatrixMode -> GL MODELVIEW glActiveTexture glMatrixMode -> GL TEXTURE glLoadMatrixf |
Material & Light | SoMaterialElement
SoLightModelElement SoLightElement |
glShadeModel glMaterial glLight glLightModel glLightModel glLightModel (-> ambient) |
Texture | SoTextureImageElement
SoTexGenElement |
glActiveTexture glTexEnv glBindTexture glGenerateMipmap glActiveTexture glTexGen |
Misc | SoClipPlaneElement SoAlphaTestElement |
glAlphaFunc glClipPlane |
Table A.2: OpenGL shader state for OIV_OPENGL_COMPATIBILITY_RENDERING using legacy OpenGL functions.
Appendix B: OpenGL compatibility custom node source code example
Listing B.1: Header file for OpenGL compatibility custom node.
#pragma once #include <Inventor/nodes/SoShape.h> #include <Inventor/sys/SoGL.h>/** * @brief Custom OpenGL (OpenGL 2) node */ class CustomNodeOpenGLCompatibility : public SoShape { SO_NODE_HEADER(CustomNodeOpenGLCompatibility );public : /** * Default constructor */ CustomNodeOpenGLCompatibility ();protected : // destructor virtual ~ CustomNodeOpenGLCompatibility ();// not implemented here void generatePrimitives(SoAction* /*action*/) {}// create OpenGL resources void initialize();// destroy OpenGL resources void destroy();SoEXTENDER public :// Rendering stuff virtual void GLRender ( SoGLRenderAction * action );// BBox computation virtual void computeBBox(SoAction* action , SbBox3f &box , SbVec3f& center);SoINTERNAL public : static void initClass (); private : |
Listing B.2: Implementation file for OpenGL compatibility custom node.
#include "CustomNodeOpenGLCompatibility.h" #include <Inventor/elements/SoLightModelElement.h> #include <Inventor/actions/SoGLRenderAction.h>// external stuff for data access extern uint32_t numVertices; extern float vertex []; extern float normal []; extern float color []; SO_NODE_SOURCE(CustomNodeOpenGLCompatibility);CustomNodeOpenGLCompatibility::CustomNodeOpenGLCompatibility() : m_initialized (false) { SO_NODE_CONSTRUCTOR(CustomNodeOpenGLCompatibility); }CustomNodeOpenGLCompatibility::~CustomNodeOpenGLCompatibility() { destroy(); }void CustomNodeOpenGLCompatibility::initClass() { getClassRenderEngineMode().setRenderMode( SbRenderEngineMode::OIV_OPENGL_COMPATIBILITY_RENDERING); SO__NODE_INIT_CLASS (CustomNodeOpenGLCompatibility, "CustomNodeOpenGLCompatibility", SoShape); }void CustomNodeOpenGLCompatibility::exitClass() { SO__NODE_EXIT_CLASS (CustomNodeOpenGLCompatibility); }void CustomNodeOpenGLCompatibility::initialize() { if (m_initialized) { return; } glGenBuffers(1, &m_vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(float) * 3, &vertex [0], GL_STATIC_DRAW); glGenBuffers(1, &m_colorBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(float) * 3, &color [0], GL_STATIC_DRAW); glGenBuffers(1, &m_normalBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_normalBuffer); glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(float) * 3, &normal [0], GL_STATIC_DRAW); m_initialized = true; }void CustomNodeOpenGLCompatibility::destroy () { if (! m_initialized) return ; glDeleteBuffers(1, &m_vertexBuffer); glDeleteBuffers(1, &m_normalBuffer); glDeleteBuffers(1, &m_colorBuffer); m_initialized = false; }void CustomNodeOpenGLCompatibility::GLRender(SoGLRenderAction* action) { // create resources initialize(); glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, 0); SoState* state = action->getState(); SoLightModelElement::Model model = SoLightModelElement::get(state); if (model == SoLightModelElement::PHONG || model == SoLightModelElement :: PER_VERTEX_PHONG) { glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); glEnable(GL_COLOR_MATERIAL); glEnableClientState(GL_COLOR_ARRAY); glColorPointer(3, GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER , m_normalBuffer); glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer(GL_FLOAT, 0, 0); } glDrawArrays(GL_TRIANGLES , 0, numVertices); if (model == SoLightModelElement :: PHONG || model == SoLightModelElement::PER_VERTEX_PHONG) { glDisable(GL_COLOR_MATERIAL); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); } glDisableClientState(GL_VERTEX_ARRAY); }void CustomNodeOpenGLCompatibility::computeBBox(SoAction* action, SbBox3f &box, SbVec3f ¢er) { SbBox3f bbox ; float* vert=vertex ; for (uint32_t i = 0; i < numVertices; ++i) { bbox.extendBy(SbVec3f(vert[0], vert[1], vert [2])); vert += 3; } box = bbox ; center = box.getCenter(); } |
Appendix C: OpenGL core custom node source code example
Listing C.1: Header file for OpenGL core custom node.
#pragma once #include <Inventor/nodes/SoShaderProgram.h> #include <Inventor/nodes/SoShape.h> #include <Inventor/sys/SoGL.h>/** * @brief Custom OpenGL (OpenGL 4 core) node */ class CustomNodeOpenGLCore : public SoShape { SO_NODE_HEADER(CustomNodeOpenGLCore); public: /** * Default constructor. */ CustomNodeOpenGLCore();protected: // destructor virtual ~CustomNodeOpenGLCore(); // not implemented here void generatePrimitives(SoAction* /* action */){} // create OpenGL resources void initialize(); // destroy OpenGL resources void destroy();SoEXTENDER public: // Rendering stuff virtual void GLRender(SoGLRenderAction* action); // BBox computation virtual void computeBBox(SoAction* action, SbBox3f &box, SbVec3f& center);SoINTERNAL public: static void initClass(); static void exitClass();private: bool m_initialized; GLuint m_vertexBuffer; GLuint m_colorBuffer; GLuint m_normalBuffer; }; |
Listing C.2: Implementation file for OpenGL core custom node.
#include "CustomNodeOpenGLCore.h" #include <Inventor/elements/SoLightModelElement.h> #include <Inventor/actions/SoGLRenderAction.h> #include <Inventor/sys/SoGL.h>// external stuff for data access extern uint32_t numVertices; extern float vertex []; extern float normal []; extern float color []; SO_NODE_SOURCE(CustomNodeOpenGLCore);CustomNodeOpenGLCore::CustomNodeOpenGLCore() : m_initialized (false) { SO_NODE_CONSTRUCTOR(CustomNodeOpenGLCore); }CustomNodeOpenGLCore::~CustomNodeOpenGLCore() { destroy(); }void CustomNodeOpenGLCore::initClass() { getClassRenderEngineMode().setRenderMode( SbRenderEngineMode::OIV_OPENGL_CORE_RENDERING); SO__NODE_INIT_CLASS (CustomNodeOpenGLCore, "CustomNodeOpenGLCore", SoShape); }void CustomNodeOpenGLCore::exitClass() { SO__NODE_EXIT_CLASS (CustomNodeOpenGLCore); }void CustomNodeOpenGLCore::initialize() { if (m_initialized) { return; } glGenBuffers(1, &m_vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(float) * 3, &vertex [0], GL_STATIC_DRAW); glGenBuffers(1, &m_colorBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(float) * 3, &color [0], GL_STATIC_DRAW); glGenBuffers(1, &m_normalBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_normalBuffer); glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(float) * 3, &normal [0], GL_STATIC_DRAW); m_initialized = true; }void CustomNodeOpenGLCore::destroy () { if (! m_initialized) return ; glDeleteBuffers(1, &m_vertexBuffer); glDeleteBuffers(1, &m_normalBuffer); glDeleteBuffers(1, &m_colorBuffer); m_initialized = false; }void CustomNodeOpenGLCore::GLRender(SoGLRenderAction* action) { // create resources initialize(); glEnableClientState (0); glEnableClientState (1); glEnableClientState (2);// vertex positions glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // vertex colors glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0); // vertex normals glBindBuffer(GL_ARRAY_BUFFER, m_normalBuffer); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0); glDrawArrays(GL_TRIANGLES, 0, numVertices);glDisableClientState(2); glDisableClientState(1); glDisableClientState(0); } void |
Listing C.3: GLSL vertex shader for OpenGL core custom node.
# version 410 core
//! oiv_include <Inventor/oivShaderState.h> layout(location = 0) in vec3 position; out vec3 normal; void |
Listing C.4: GLSL fragmant shader for OpenGL core custom node.
# version 410 core
//! oiv_include <Inventor/oivShaderState.h> in vec3 normal; void |
Appendix D: SoGLCallback source code example
Listing D.1: Header file for OpenGLSoGLCallback.
#pragma once #include <Inventor/actions/SoAction.h> #include <Inventor/sys/SoGL.h>/** * @brief Custom OpenGL (OpenGL 4 core) node */ class CustomNodeGLCallback { public: /** * Default constructor. */ CustomNodeGLCallback();public: /** * @brief Callback for SoGLCallback */ static void callback(void* data, SoAction* action );protected:// Destructor ~CustomNodeGLCallback(); // Create resources void initialiaze(); // destroy OpenGL resources void destroy();private: bool m_initialized; GLuint m_vertexBuffer; GLuint m_colorBuffer; GLuint m_normalBuffer; }; |
Listing D.2: Implementation file for OpenGLSoGLCallback.
#include "CustomNodeGLCallback.h" #include <Inventor/elements/SoLightModelElement.h> #include <Inventor/actions/SoGLRenderAction.h> #include <Inventor/sys/SoGL.h>// external stuff for bunny data access extern uint32_t numVertices; extern float vertex []; extern float normal []; extern float color [];CustomNodeGLCallback::CustomNodeGLCallback() : m_initialized (false) {}CustomNodeGLCallback::~CustomNodeGLCallback() { destroy(); }void CustomNodeGLCallback::initialize() { if (m_initialized) { return; }glGenBuffers(1, &m_vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(float) * 3, &vertex[0], GL_STATIC_DRAW);glGenBuffers(1, &m_colorBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(float) * 3, &color[0], GL_STATIC_DRAW);glGenBuffers(1, &m_normalBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_normalBuffer); glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(float) * 3, &normal[0], GL_STATIC_DRAW); m_initialized = true; }void CustomNodeGLCallback::callback(void* data , SoAction* action) { CustomNodeGLCallback* cb = static_cast<CustomNodeGLCallback*>(data);// create resources cb->initialize(); glBindBuffer(GL_ARRAY_BUFFER, cb->m_vertexBuffer); SoState* state = action->getState(); if (model == SoLightModelElement::PHONG || model == SoLightModelElement::PER_VERTEX_PHONG) glBindBuffer(GL_ARRAY_BUFFER, cb->m_normalBuffer); glDrawArrays(GL_TRIANGLES, 0, numVertices); if (model == SoLightModelElement::PHONG || model == SoLightModelElement::PER_VERTEX_PHONG) glDisableClientState(GL_VERTEX_ARRAY); |
Listing D.3: Usage for SoGLCallback.
CustomNodeGLCallback* callback = new CustomNodeGLCallback; SoGLCallback* glcb = new SoGLCallback; glcb->setCallback(&CustomNodeGLCallback::callback, callback); // add glcb to scene graph |