{"id":175,"date":"2023-02-21T16:20:19","date_gmt":"2023-02-21T16:20:19","guid":{"rendered":"https:\/\/oiv10wp.athena-dev.net\/?page_id=175"},"modified":"2023-12-13T16:04:36","modified_gmt":"2023-12-13T16:04:36","slug":"manage-custom-rendering-nodes","status":"publish","type":"page","link":"https:\/\/developer.openinventor.com\/index.php\/general-documentation\/manage-custom-rendering-nodes\/","title":{"rendered":"Manage custom rendering nodes"},"content":{"rendered":"<p>Introduction<\/p>\n<ul>\n<li><a href=\"#OIVRenderEngine\">Open Inventor render engine<\/a><\/li>\n<li><a href=\"#CustomNodes\">Custom nodes<\/a><\/li>\n<li><a href=\"#PureOIVCustomNodes\">Pure Open Inventor custom nodes<\/a><\/li>\n<li><a href=\"#PureOGLCustomNodes\"><em>OpenGL<\/em> custom nodes<\/a><\/li>\n<li><a href=\"#Guidelines\">Guidelines<\/a><\/li>\n<li><a href=\"#CommonPrinciples\">Common principles<\/a><\/li>\n<li><a href=\"#OGLRenderModes\">OpenGL render modes<\/a><\/li>\n<li><a href=\"#Examples\">Examples of <em>OpenGL<\/em> Open Inventor custom node<\/a>\n<ul>\n<li><a href=\"#CompatProfile\"><em>OpenGL<\/em> compatibility profile<\/a><\/li>\n<li><a href=\"#CoreProfile\"><em>OpenGL<\/em> core profile<\/a><\/li>\n<li><a href=\"#Callback\">SoGLCallback<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Appendices<\/p>\n<ul>\n<li><a href=\"#AppendixA\">Appendix A: A Open Inventor state against <em>OpenGL<\/em> state<\/a><\/li>\n<li><a href=\"#AppendixB\">Appendix B: OpenGL compatibility custom node source code example<\/a><\/li>\n<li><a href=\"#AppendixC\">Appendix C: OpenGL core custom node source code example<\/a><\/li>\n<li><a href=\"#AppendixD\">Appendix D: SoGLCallback source code example<\/a><\/li>\n<\/ul>\n<hr \/>\n<h3>Introduction<\/h3>\n<p style=\"text-align: justify;\"><a name=\"OIVRenderEngine\"><\/a><strong>Open Inventor render engine<br \/>\n<\/strong>Starting with Open Inventor 10, all graphics API calls are executed by a render engine, a software layer between hardware and\u00a0Open Inventor public API (see <a href=\"#Figure1_1\">Figure 1.1<\/a>). This\u00a0render engine has changed the product design, as\u00a0the 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\u00a0previous versions.<\/p>\n<p><a name=\"Figure1_1\"><\/a><\/p>\n<p class=\"rtecenter\"><img decoding=\"async\" src=\"https:\/\/developer.openinventor.com\/wp-content\/uploads\/2023\/12\/architecture2.png\" alt=\"\" \/><\/p>\n<p>&nbsp;<\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\"><strong>Figure 1.1:<\/strong> 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.<\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\">As shown in <a href=\"#Figure1_2\">Figure 1.2<\/a>, during a\u00a0<strong><em><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_g_l_render_action.html\" target=\"blank\" rel=\"noopener noreferrer\">SoGLRenderAction<\/a><\/em><\/strong>, the scene graph traversal generates\u00a0a set of render engine <em>commands<\/em> 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\u00a0the 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.<\/p>\n<p><a name=\"Figure1_2\"><\/a><\/p>\n<p class=\"rtecenter\"><img decoding=\"async\" src=\"https:\/\/developer.openinventor.com\/wp-content\/uploads\/2023\/12\/renderengine2.png\" alt=\"\" \/><\/p>\n<p>&nbsp;<\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\"><strong>Figure 1.2: <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_g_l_render_action.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoGLRenderAction<\/a><\/strong><\/em><\/strong> 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.<\/p>\n<hr \/>\n<p style=\"text-align: justify;\"><a name=\"CustomNodes\"><\/a><strong>Custom Nodes<br \/>\n<\/strong>Introduced with Open Inventor 10, each node contains a <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_sb_render_engine_mode.html\" target=\"_blank\" rel=\"noopener noreferrer\">SbRenderEngineMode<\/a><\/strong><\/em> static class member which defines how a node is executed by the Open Inventor render engine. The render mode, of type <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_node.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoNode<\/a><\/strong><\/em>::RenderMode, should be defined with one of the following values:<\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><strong>OIV_OPENINVENTOR_RENDERING<\/strong>, for custom nodes that use only Open Inventor API (see Section 2).<\/li>\n<li><strong>OIV_OPENGL_COMPATIBILITY_RENDERING<\/strong>, for custom nodes that work in OpenGL compatibility profile.<\/li>\n<li><strong>OIV_OPENGL_CORE_RENDERING<\/strong>, for custom nodes that work in OpenGL core profile.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Please refer to Chapters <a href=\"#PureOIVCustomNodes\">Pure Open Inventor custom nodes<\/a> and <a href=\"#PureOGLCustomNodes\"><em>OpenGL<\/em> custom nodes<\/a> for details of what the choice of a render mode involves.<\/p>\n<p class=\"rtejustify\">\u00a0 \u00a0 \u00a0 \u00a0 Accessible via the <strong>getClassRenderEngineMode<\/strong> static function, the render mode must be initialized during the static class initialization (i.e., <strong>initClass<\/strong> function) such as shown in <strong>Listing 1.1. <\/strong><\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">void<br \/>\nCustomNode<\/span>::initClass()<br \/>\n{<br \/>\ngetClassRenderEngineMode().setRenderMode(OIV_OPENINVENTOR_RENDERING);<br \/>\nSO_NODE_INIT_CLASS(<span style=\"color: #0000ff;\">CustomNode<\/span>, <span style=\"color: #ff0000;\">\"CustomNode\"<\/span>, SoNode );<br \/>\n}<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong><a name=\"Listing1_1\"><\/a>Listing 1.1<\/strong>: Class initialization function for a custom node, named <em><span style=\"color: #0000ff;\">CustomNode<\/span><\/em> inherited from <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_node.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoNode<\/a><\/strong><\/em> which uses only Open Inventor API.<\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\">According to the architecture of Open Inventor 10 (see <a href=\"#OIVRenderEngine\">Open Inventor render engine<\/a>), 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\u00a0to <strong>OIV_OPENINVENTOR_RENDERING<\/strong> indirectly benefit from the capability of Open Inventor nodes to generate render engine commands.<\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\">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 <a href=\"#Figure1_1\">Figure 1.1<\/a>). This\u00a0means 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 <strong>OIV_OPENGL_COMPATIBILITY_RENDERING<\/strong> or <strong>OIV_OPENGL_CORE_RENDERING<\/strong>, 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.<\/p>\n<hr \/>\n<p style=\"text-align: justify;\"><strong><a name=\"PureOIVCustomNodes\"><\/a>Pure Open Inventor custom nodes<\/strong><br \/>\nA 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 <a href=\"#isting1_1\">Listing 1.1.<\/a><\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\">The recommended usage is to build a scene graph that corresponds to the expected rendering result. This scene graph will be traversed during a <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_action.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoAction<\/a><\/strong><\/em> using the <em>forwardTraversal<\/em> function, for instance during the render action:<\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNode::GLRender(SoGLRenderAction* action)<br \/>\n{<br \/>\n<span style=\"color: #007c0c;\">\/\/ supposed m_sceneGraph is a group, previously initialized<\/span><br \/>\naction-&gt;forwardTraversal(m_sceneGraph);<br \/>\n}<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"rtejustify\">One of the main advantages of using the <em>forwardTraversal<\/em> function instead of calling the <em>GLRender<\/em> fonction of the embedded scene graph is that paths are preserved. Paths (i.e., <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_path.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoPath<\/a><\/strong><\/em>) 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 <em>GLRendere<\/em> function of a node may lead to unexpected results.<\/p>\n<p>WARNING: Do not render nodes in the internal scene graph by calling their GLRender() method. Custom nodes MUST call action-&gt;forwardTraversal() to render nodes in the internal scene graph. Calling GLRender() in this situation is likely to cause a crash.<\/p>\n<hr \/>\n<h3><a name=\"PureOGLCustomNodes\"><\/a><em>OpenGL<\/em> custom nodes<\/h3>\n<h4><a name=\"Guidelines\"><\/a>Guidelines<\/h4>\n<p><a name=\"CommonPrinciples\"><\/a><strong>Common principles<\/strong><\/p>\n<p>A custom node that makes OpenGL calls must set its render mode to<\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><strong>OIV_OPENGL_COMPATIBILITY_RENDERING<\/strong> or,<\/li>\n<li><strong>OIV_OPENGL_CORE_RENDERING<\/strong><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p class=\"rtejustify\" style=\"text-align: justify;\">for using OpenGL calls. <strong>These calls are allowed only in the custom node class implementation of the virtual <em><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_node.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoNode<\/a><\/em>::GLRender function. The effect of OpenGL calls outside this function is undefined<\/strong>. The <em>GLRender<\/em> function of such nodes is not executed during the <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_g_l_render_action.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoGLRenderAction<\/a><\/strong><\/em> traversal; its execution is postponed later for an execution by the Open Inventor render engine, as explained in Section <a href=\"#CustomNodes\">Custom nodes<\/a>. This\u00a0imposes some guidelines and limitations when you consider using <em>OpenGL<\/em> API.<\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\">\u00a0 \u00a0 \u00a0Mixing OpenGL and Open Inventor API calls is not allowed. This\u00a0means that the <em>GLRender<\/em> function of an <em>OpenGL<\/em> custom node must contain only <em>OpenGL<\/em> calls. However, Open Inventor base types (e.g., <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_sb_vec3f.html\" target=\"_blank\" rel=\"noopener noreferrer\">SbVec3f<\/a><\/strong><\/em>, <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_sb_color.html\" target=\"_blank\" rel=\"noopener noreferrer\">SbColor<\/a><\/strong><\/em>) can be used as well as queries of Open Inventor state elements. The Open Inventor state (i.e., <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_state.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoState<\/a><\/strong><\/em>) is available via the <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_g_l_render_action.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoGLRenderAction<\/a><\/strong><\/em> 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 href=\"#OIVStateAgainstOGLState\">A Open Inventor state against <em>OpenGL<\/em> state<\/a> 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 <strong><em>glGet<\/em><\/strong> functions.<\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\">Note:<br \/>\nDo NOT use SoLazyElement to get information about the materials in the traversal state list.\u00a0 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.<\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\">\u00a0 \u00a0 \u00a0The 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, <strong>modification of the Open Inventor state is only valid within the GLRender function but has no effect on the rendering state<\/strong>. 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 <em>GLRender<\/em> function. So, a change of state has no effect outside the node, both for Open Inventor and <em>OpenGL<\/em> states. Note that the ability to run <em>OpenGL <\/em>calls has a cost, and the execution of such nodes may be time consuming.<\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\">\u00a0 \u00a0 \u00a0<em><strong>OpenGL<\/strong><\/em> version 4.1 is the minimum requirement for Open Inventor 10. All functionnalities defined by this version of <em>OpenGL<\/em> are available in an <em>OpenGL<\/em> custom node. Extensions and features from higher versions are available if the hardware used for your application supports them.<\/p>\n<p><a name=\"OGLRenderModes\"><\/a><strong>OpenGL render modes<br \/>\n<\/strong>Depending on the render mode, the setup of an <em>OpenGL <\/em>custom node should fulfill\u00a0<em>OpenGL <\/em>specification for compatibility and core profiles. The specifications for the two possible render modes are described in the following.<\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\"><strong>OIV_OPENGL_COMPATIBILITY_RENDERING<\/strong><br \/>\nIn this compatibility mode, the behavior for <em>OpenGL <\/em>is the one defined by <em>OpenGL <\/em>specification\u00a0version 4.1 compatibility profile. In this mode, <strong>Open Inventor initializes fixed-function vertex transformations, and fixed-function vertex lighting, and color properties according the Open Inventor state<\/strong>. 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.<\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\">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 <em>OpenGL <\/em>to Open Inventor.<\/p>\n<p class=\"rtejustify\" style=\"text-align: justify;\"><strong>OIV_OPENGL_CORE_RENDERING<\/strong><br \/>\nThe 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, <strong>a user-defined shader is mandatory when using this render mode<\/strong>. The Open Inventor shader API covers all the fixed-function <em>OpenGL<\/em> state supported by Open Inventor.<\/p>\n<p>Note that only core profile <em>OpenGL <\/em>calls can be used here. This mode is mandatory if you plan to use <em>OpenGL <\/em>custom nodes on Mac OS.<\/p>\n<hr \/>\n<h3><a name=\"Examples\"><\/a>Examples of <em>OpenGL<\/em> Open Inventor custom node<\/h3>\n<p><a name=\"CompatProfile\"><\/a><strong>OpenGL compatibility profile<br \/>\n<\/strong>The header file of an <em>OpenGL<\/em> compatibility custom node is shown in <a href=\"#Listing_3_1\">Listing 3.1<\/a>. In this example, a node, inherited from <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_shape.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoShape<\/a><\/strong><\/em>, is defined with appropriate function definitions. This header is similar to the header of a classic custom node, except for the <em>OpenGL<\/em> types defined in private members.<\/p>\n<p><a name=\"Listing_3_1\"><\/a><\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">#pragma once<\/span><br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/nodes\/SoShape.h&gt;<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/sys\/SoGL.h&gt;<\/span><span style=\"color: #007c0c;\">\/**<\/span><br \/>\n<span style=\"color: #007c0c;\">* @brief Custom OpenGL (OpenGL 2) node<\/span><br \/>\n<span style=\"color: #007c0c;\">*\/<\/span><br \/>\n<span style=\"color: #0000ff;\">class<\/span> CustomNodeOpenGLCompatibility : <span style=\"color: #0000ff;\">public<\/span> SoShape<br \/>\n{<br \/>\nSO_NODE_HEADER(CustomNodeOpenGLCompatibility );<span style=\"color: #0000ff;\">public :<\/span><br \/>\n<span style=\"color: #007c0c;\">\/**<\/span><br \/>\n<span style=\"color: #007c0c;\">* Default constructor<\/span><br \/>\n<span style=\"color: #007c0c;\">*\/<\/span><br \/>\nCustomNodeOpenGLCompatibility ();<span style=\"color: #0000ff;\">protected :<\/span><br \/>\n<span style=\"color: #007c0c;\">\/\/ destructor<\/span><br \/>\n<span style=\"color: #0000ff;\">virtual<\/span> ~ CustomNodeOpenGLCompatibility ();<span style=\"color: #007c0c;\">\/\/ not implemented here<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> generatePrimitives(SoAction* <span style=\"color: #007c0c;\">\/*action*\/<\/span>) {}<span style=\"color: #007c0c;\">\/\/ create OpenGL resources<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> initialize();<span style=\"color: #007c0c;\">\/\/ destroy OpenGL resources<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> destroy();<span style=\"color: #0000ff;\">SoEXTENDER public :<\/span><span style=\"color: #007c0c;\">\/\/ Rendering stuff<\/span><br \/>\n<span style=\"color: #0000ff;\">virtual void<\/span> GLRender ( SoGLRenderAction* action );<span style=\"color: #007c0c;\">\/\/ BBox computation<\/span><br \/>\n<span style=\"color: #0000ff;\">virtual void<\/span> computeBBox(SoAction* action , SbBox3f &amp;box , SbVec3f&amp; center);<span style=\"color: #0000ff;\">SoINTERNAL public :<\/span><\/p>\n<p><span style=\"color: #0000ff;\">static void<\/span> initClass ();<br \/>\n<span style=\"color: #0000ff;\">static void<\/span> exitClass ();<\/p>\n<p><span style=\"color: #0000ff;\">private :<\/span><br \/>\n<span style=\"color: #0000ff;\">bool<\/span> m_initialized ;<br \/>\nGLuint m_vertexBuffer ;<br \/>\nGLuint m_colorBuffer ;<br \/>\nGLuint m_normalBuffer ;<br \/>\n};<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing 3.1<\/strong>: Header file for <em>OpenGL<\/em> compatibility custom node.<\/p>\n<p>&nbsp;<\/p>\n<p class=\"rtejustify\">\u00a0 \u00a0 \u00a0 \u00a0The implementation first defines the render mode during the class initialization as shown in <a href=\"#Listing_3_2\">Listing 3.2<\/a>.<\/p>\n<p><a name=\"Listing_3_2\"><\/a><\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCompatibility::initClass()<br \/>\n{<br \/>\ngetClassRenderEngineMode().setRenderMode(SbRenderEngineMode::<br \/>\nOIV_OPENGL_COMPATIBILITY_RENDERING );<br \/>\nSO__NODE_INIT_CLASS(CustomNodeOpenGLCompatibility,<br \/>\n<span style=\"color: #ff0000;\">\"CustomNodeOpenGLCompatibility\"<\/span>, SoShape );<br \/>\n} <\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing 3.2<\/strong>: Class initialization function for <em>OpenGL<\/em> compatibility custom node.<\/p>\n<p class=\"rtejustify\">The GLRender function of this node uses OpenGL functions as shown in <a href=\"#Listing_3_3\">Listing 3.3<\/a>. Note that, here, the state from the <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_g_l_render_action.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoGLRenderAction<\/a><\/strong><\/em> is queried for conditionnal rendering based on lighting properties.<\/p>\n<p><a name=\"Listing_3_3\"><\/a><\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCompatibility::GLRender(SoGLRenderAction* action)<br \/>\n{<br \/>\n<span style=\"color: #007c0c;\">\/\/ create resources<\/span><br \/>\ninitialize();<\/span><\/span>glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);<br \/>\nglEnableClientState(GL_VERTEX_ARRAY);<br \/>\nglVertexPointer(3, GL_FLOAT, 0, 0);SoState* state = action-&gt;getState();<br \/>\nSoLightModelElement::Model model = SoLightModelElement::get(state);<span style=\"color: #0000ff;\">if<\/span> (model == SoLightModelElement::PHONG || model == SoLightModelElement::PER_VERTEX_PHONG)<br \/>\n{<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);<br \/>\nglColorMaterial(GL_FRONT_AND_BACK , GL_DIFFUSE);<br \/>\nglEnable(GL_COLOR_MATERIAL);<br \/>\nglEnableClientState(GL_COLOR_ARRAY);<br \/>\nglColorPointer(3, GL_FLOAT , 0, 0);glBindBuffer(GL_ARRAY_BUFFER, m_normalBuffer);<br \/>\nglEnableClientState(GL_NORMAL_ARRAY);<br \/>\nglNormalPointer(GL_FLOAT , 0, 0);<br \/>\n}<br \/>\nglDrawArrays ( GL_TRIANGLES , 0, numVertices );<span style=\"color: #0000ff;\">if<\/span> (model == SoLightModelElement::PHONG || model == SoLightModelElement::PER_VERTEX_PHONG)<br \/>\n{<br \/>\nglDisable(GL_COLOR_MATERIAL);<br \/>\nglDisableClientState(GL_NORMAL_ARRAY);<br \/>\nglDisableClientState(GL_COLOR_ARRAY);<br \/>\n}<br \/>\nglDisableClientState(GL_VERTEX_ARRAY);<br \/>\n}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing 3.3<\/strong>: GLRender function for <em>OpenGL<\/em> compatibility custom node.<\/p>\n<p class=\"rtejustify\">The complete source code for this node is available in <a href=\"#AppendixB\">Appendix B<\/a>.<\/p>\n<p><a name=\"CoreProfile\"><\/a><strong><em>OpenGL<\/em> core profile<br \/>\n<\/strong>The header file of an <em>OpenGL<\/em> core custom node is described in <a href=\"#Listing_3_4\">Listing 3.4<\/a>. 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 <em>OpenGL<\/em> types defined in private members.<\/p>\n<p><a name=\"Listing_3_4\"><\/a><\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">#pragma once<\/span><br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/nodes\/SoShaderProgram.h&gt;<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/nodes\/SoShape.h&gt;<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/sys\/SoGL.h&gt;<\/span><span style=\"color: #007c0c;\">\/**<\/span><br \/>\n<span style=\"color: #007c0c;\">* @brief Custom OpenGL (OpenGL 4 Core) node<\/span><br \/>\n<span style=\"color: #007c0c;\">*\/<\/span><br \/>\n<span style=\"color: #0000ff;\">class<\/span> CustomNodeOpenGLCore : <span style=\"color: #0000ff;\">public<\/span> SoShape<br \/>\n{<br \/>\nSO_NODE_HEADER(CustomNodeOpenGLCore);<span style=\"color: #0000ff;\">public :<\/span><br \/>\n<span style=\"color: #007c0c;\">\/**<\/span><br \/>\n<span style=\"color: #007c0c;\">* Default constructor<\/span><br \/>\n<span style=\"color: #007c0c;\">*\/<\/span><br \/>\nCustomNodeOpenGLCore();<span style=\"color: #0000ff;\">protected :<\/span><br \/>\n<span style=\"color: #007c0c;\">\/\/ destructor<\/span><br \/>\n<span style=\"color: #0000ff;\">virtual<\/span> ~CustomNodeOpenGLCore();<span style=\"color: #007c0c;\">\/\/ not implemented here<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> generatePrimitives(SoAction* <span style=\"color: #007c0c;\">\/*action*\/<\/span>) {}<span style=\"color: #007c0c;\">\/\/ create OpenGL resources<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> initialize();<span style=\"color: #007c0c;\">\/\/ destroy OpenGL resources<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> destroy();<span style=\"color: #0000ff;\">SoEXTENDER public :<\/span><span style=\"color: #007c0c;\">\/\/ Rendering stuff<\/span><br \/>\n<span style=\"color: #0000ff;\">virtual void<\/span> GLRender (SoGLRenderAction* action);<span style=\"color: #007c0c;\">\/\/ BBox computation<\/span><br \/>\n<span style=\"color: #0000ff;\">virtual void<\/span> computeBBox(SoAction* action , SbBox3f &amp;box , SbVec3f&amp; center);<span style=\"color: #0000ff;\">SoINTERNAL public :<\/span><\/p>\n<p><span style=\"color: #0000ff;\">static void<\/span> initClass();<br \/>\n<span style=\"color: #0000ff;\">static void<\/span> exitClass();<\/p>\n<p><span style=\"color: #0000ff;\">private :<\/span><br \/>\n<span style=\"color: #0000ff;\">bool<\/span> m_initialized;<br \/>\nGLuint m_vertexBuffer;<br \/>\nGLuint m_colorBuffer;<br \/>\nGLuint m_normalBuffer;<br \/>\n};<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing 3.4<\/strong>: Header file for <em>OpenGL<\/em> core custom node.<\/p>\n<p class=\"rtejustify\">The implementation first defines the render mode during the class initialization as shown in <a href=\"#Listing_3_5\">Listing 3.5<\/a>.<\/p>\n<p><a name=\"Listing_3_5\"><\/a><\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCore::initClass()<br \/>\n{<br \/>\ngetClassRenderEngineMode().setRenderMode(SbRenderEngineMode::<br \/>\nOIV_OPENGL_CORE_RENDERING);<br \/>\nSO__NODE_INIT_CLASS(CustomNodeOpenGLCore,<br \/>\n<span style=\"color: #ff0000;\">\"CustomNodeOpenGLCore\"<\/span>, SoShape );<br \/>\n} <\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing 3.5<\/strong>: Class initialization function for <em>OpenGL<\/em> core custom node.<\/p>\n<p class=\"rtejustify\">The <em>GLRender<\/em> function of this node uses <em>OpenGL<\/em> functions as shown in <a href=\"#Listing_3_6\">Listing 3.6<\/a>. Here, the functions to manage attributes are part of the core profile instead of the legacy ones in the compatibility examples (see <a href=\"#Listing_3_3\">Listing 3.3<\/a>).<\/p>\n<p><a name=\"Listing_3_6\"><\/a><\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCore::GLRender(SoGLRenderAction* action)<br \/>\n{<br \/>\n<span style=\"color: #007c0c;\">\/\/ create resources<\/span><br \/>\ninitialize();<\/span><\/span>glEnableClientState(0);<br \/>\nglEnableClientState(1);<br \/>\nglEnableClientState(2);<span style=\"color: #007c0c;\">\/\/ vertex positions<\/span><br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer );<br \/>\nglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);<span style=\"color: #007c0c;\">\/\/ vertex colors<\/span><br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);<br \/>\nglVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);<span style=\"color: #007c0c;\">\/\/ vertex normals<\/span><br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_normalBuffer);<br \/>\nglVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);glDrawArrays(GL_TRIANGLES, 0, numVertices);glDisableClientState(2);<br \/>\nglDisableClientState(1);<br \/>\nglDisableClientState(0);<br \/>\n}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing 3.6<\/strong>: GLRender function for <em>OpenGL<\/em> core custom node.<\/p>\n<p class=\"rtejustify\">Note that this kind of node needs a shader program during rendering. Therefore an <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_shader_program.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoShaderProgram<\/a><\/strong><\/em> node must lie in the scene graph before this node. The vertex shader header can specify the layout location of the attributes (<em>i.e.<\/em>, positions, colors and normals) as shown in <a href=\"#Listing_3_7\">Listing 3.7<\/a> to avoid link issues with the underlying GLSL program.<\/p>\n<p><a name=\"Listing_3_7\"><\/a><\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">layout<\/span>(location = 0) in vec3 position; <span style=\"color: #0000ff;\">layout<\/span>(location = 1) in vec3 color; <span style=\"color: #0000ff;\">layout<\/span>(location = 2) in vec3 normal; <\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing 3.7<\/strong>: GLSL vertex shader attribute declarations for <em>OpenGL<\/em> core custom node example.<\/p>\n<p class=\"rtejustify\">The complete C++ and shader source code for this node is available in <a href=\"#AppendixC\">Appendix C<\/a>.<\/p>\n<p style=\"text-align: justify;\"><a name=\"Callback\"><\/a><strong>SoGLCallback<br \/>\n<\/strong>The <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_g_l_callback.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoGLCallback<\/a><\/strong><\/em> class provides another way of making OpenGL calls, using an interface similar to <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_callback.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoCallback<\/a><\/strong><\/em>. However, it has a significant limitation: the type of the render mode must be <strong>OIV_OPENGL_COMPATIBILITY_RENDERING<\/strong>. The prototype of the callback function is the same as <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_callback.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoCallback's <\/a><\/strong><\/em>(<a href=\"#Listing_3_8\">see Listing 3.8<\/a>). The main difference between <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_g_l_callback.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoGLCallback<\/a><\/strong><\/em> and <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_callback.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoCallback<\/a><\/strong><\/em>\u00a0classes is that <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_g_l_render_action.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoGLRenderAction<\/a>\u00a0<\/strong><\/em>is the only action that can traverse an\u00a0<em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_callback.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoCallback<\/a>\u00a0<\/strong><\/em>object<em><strong>.<\/strong><\/em><\/p>\n<p><a name=\"Listing_3_8\"><\/a><\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeGLCallback::callback(<span style=\"color: #0000ff;\">void<\/span>* data, SoAction* action)<br \/>\n{<br \/>\nCustomNodeGLCallback* cb = <span style=\"color: #0000ff;\">static_cast<\/span>&lt;CustomNodeGLCallback*&gt;(data);<br \/>\n<span style=\"color: #007c0c;\">\/\/ create resources<\/span><br \/>\ncb-&gt;initialize();<\/span><\/span>glBindBuffer(GL_ARRAY_BUFFER, cb-&gt;m_vertexBuffer);<br \/>\nglEnableClientState(GL_VERTEX_ARRAY);<br \/>\nglVertexPointer(3, GL_FLOAT, 0, 0);SoState* state = action-&gt;getState();<br \/>\nSoLightModelElement::Model model = SoLightModelElement::get(state);<span style=\"color: #0000ff;\">if<\/span> (model == SoLightModelElement::PHONG || model == SoLightModelElement::PER_VERTEX_PHONG)<br \/>\n{<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, cb-&gt;m_colorBuffer);<br \/>\nglColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);<br \/>\nglEnable(GL_COLOR_MATERIAL);<br \/>\nglEnableClientState(GL_COLOR_ARRAY);<br \/>\nglColorPointer(3, GL_FLOAT, 0, 0);glBindBuffer(GL_ARRAY_BUFFER, cb-&gt;m_normalBuffer);<br \/>\nglEnableClientState(GL_NORMAL_ARRAY);<br \/>\nglNormalPointer(GL_FLOAT, 0, 0);<br \/>\n}glDrawArrays(GL_TRIANGLES, 0, numVertices);<span style=\"color: #0000ff;\">if<\/span> (model == SoLightModelElement::PHONG || model == SoLightModelElement::PER_VERTEX_PHONG)<br \/>\n{<br \/>\nglDisable(GL_COLOR_MATERIAL);<br \/>\nglDisableClientState(GL_NORMAL_ARRAY);<br \/>\nglDisableClientState(GL_COLOR_ARRAY);<br \/>\n}<br \/>\nglDisableClientState(GL_VERTEX_ARRAY);<br \/>\n}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing 3.8<\/strong>: Callback function for <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_g_l_callback.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoGLCallback<\/a><\/strong><\/em>.<\/p>\n<p class=\"rtejustify\">Using a custom node instead of an <em><strong><a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_so_g_l_callback.html\" target=\"_blank\" rel=\"noopener noreferrer\">SoGLCallback<\/a><\/strong><\/em>\u00a0 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.<\/p>\n<hr \/>\n<h3>Appendices<\/h3>\n<p><a name=\"AppendixA\"><\/a><strong>Appendix A: A Open Inventor state against OpenGL state<\/strong><\/p>\n<table style=\"height: 838px; width: 713px;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"vertical-align: top;\"><strong>Categories<\/strong><\/td>\n<td style=\"vertical-align: top;\"><strong>Inventor elements<\/strong><\/td>\n<td style=\"vertical-align: top;\"><strong><em>OpenGL<\/em> related functions<\/strong><\/td>\n<\/tr>\n<tr>\n<td style=\"vertical-align: top;\"><strong>Rasterizer<\/strong><\/td>\n<td style=\"vertical-align: top;\">SoDrawStyleElement<br \/>\nSoShapeHintsElementSoLineWidthElement<br \/>\nSoPointSizeElement<br \/>\nSoPolygonOffsetElement<br \/>\nSoLinePatternElement<\/td>\n<td style=\"vertical-align: top;\">glPolygonMode<br \/>\nglCullFace<br \/>\nglFrontFace<br \/>\nglLineWidth<br \/>\nglPointSize<br \/>\nglPolygonOffset<br \/>\nglLineStipple<\/td>\n<\/tr>\n<tr>\n<td style=\"vertical-align: top;\"><strong>Viewport and scissor<\/strong><\/td>\n<td style=\"vertical-align: top;\">SoUpdateAreaElement<br \/>\nSoViewportRegionElement<\/td>\n<td style=\"vertical-align: top;\">glScissor<br \/>\nglViewport<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 164.667px;\"><strong>Blending and color<\/strong><\/td>\n<td style=\"vertical-align: top; width: 254.2px;\">SoBlendElement<\/p>\n<p>SoColorMaskElement<br \/>\nSoLogicOperationElement<\/td>\n<td style=\"height: 60px; vertical-align: top; width: 280.9px;\">glBlendEquation<br \/>\nglBlendFunc<br \/>\nglBlendColor<br \/>\nglColorMask<br \/>\nglLogicOp<\/td>\n<\/tr>\n<tr>\n<td style=\"vertical-align: top;\"><strong>Depth\/stencil buffer<\/strong><\/td>\n<td style=\"vertical-align: top;\">SoDepthBufferElement<\/p>\n<p>SoStencilBufferElement<\/td>\n<td style=\"height: 100px; vertical-align: top;\">glDepthMask<br \/>\nglDepthFunc<br \/>\nglStencilOpSeparate<br \/>\nglStencilMaskSeparate<br \/>\nglStencilMaskSeparate<\/td>\n<\/tr>\n<tr>\n<td style=\"vertical-align: top;\"><strong>Transform<\/strong><\/td>\n<td style=\"vertical-align: top;\">SoProjectionMatrixElement<br \/>\nSoViewMatrixElement<br \/>\nSoModelMatrixElement<br \/>\nSoTextureMatrixElementSo*MatrixElement<\/td>\n<td style=\"vertical-align: top;\">glMatrixMode -&gt; GL PROJECTION<br \/>\nglMatrixMode -&gt; GL MODELVIEW<br \/>\nglMatrixMode -&gt; GL MODELVIEW<br \/>\nglActiveTexture<br \/>\nglMatrixMode -&gt; GL TEXTURE<br \/>\nglLoadMatrixf<\/td>\n<\/tr>\n<tr>\n<td style=\"vertical-align: top;\"><strong>Material &amp; Light<\/strong><\/td>\n<td style=\"vertical-align: top;\">SoMaterialElement<\/p>\n<p>SoLightModelElement<\/p>\n<p>SoLightElement<br \/>\nSoEnvironmentElement<\/td>\n<td style=\"vertical-align: top;\">glShadeModel<br \/>\nglMaterial<br \/>\nglLight<br \/>\nglLightModel<br \/>\nglLightModel<br \/>\nglLightModel (-&gt; ambient)<\/td>\n<\/tr>\n<tr>\n<td style=\"vertical-align: top;\"><strong>Texture<\/strong><\/td>\n<td style=\"vertical-align: top;\">SoTextureImageElement<\/p>\n<p>SoTexGenElement<\/td>\n<td style=\"vertical-align: top;\">glActiveTexture<br \/>\nglTexEnv<br \/>\nglBindTexture<br \/>\nglGenerateMipmap<br \/>\nglActiveTexture<br \/>\nglTexGen<\/td>\n<\/tr>\n<tr>\n<td style=\"vertical-align: top;\"><strong>Misc<\/strong><\/td>\n<td style=\"vertical-align: top;\">SoClipPlaneElement<br \/>\nSoAlphaTestElement<\/td>\n<td style=\"vertical-align: top;\">glAlphaFunc<br \/>\nglClipPlane<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Table A.2<\/strong>: <em>OpenGL<\/em> shader state for <strong>OIV_OPENGL_COMPATIBILITY_RENDERING<\/strong> using legacy <em>OpenGL<\/em> functions.<\/p>\n<p><a name=\"AppendixB\"><\/a><strong>Appendix B: OpenGL compatibility custom node source code example<\/strong><\/p>\n<p><strong>Listing B.1<\/strong>: Header fi\fle for <em>OpenGL<\/em> compatibility custom node.<\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">#pragma once<\/span><br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/nodes\/SoShape.h&gt;<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/sys\/SoGL.h&gt;<\/span><span style=\"color: #007c0c;\">\/**<\/span><br \/>\n<span style=\"color: #007c0c;\">* @brief Custom OpenGL (OpenGL 2) node<\/span><br \/>\n<span style=\"color: #007c0c;\">*\/<\/span><br \/>\n<span style=\"color: #0000ff;\">class<\/span> CustomNodeOpenGLCompatibility : <span style=\"color: #0000ff;\">public<\/span> SoShape<br \/>\n{<br \/>\nSO_NODE_HEADER(CustomNodeOpenGLCompatibility );<span style=\"color: #0000ff;\">public :<\/span><br \/>\n<span style=\"color: #007c0c;\">\/**<\/span><br \/>\n<span style=\"color: #007c0c;\">* Default constructor<\/span><br \/>\n<span style=\"color: #007c0c;\">*\/<\/span><br \/>\nCustomNodeOpenGLCompatibility ();<span style=\"color: #0000ff;\">protected :<\/span><br \/>\n<span style=\"color: #007c0c;\">\/\/ destructor<\/span><br \/>\n<span style=\"color: #0000ff;\">virtual<\/span> ~ CustomNodeOpenGLCompatibility ();<span style=\"color: #007c0c;\">\/\/ not implemented here<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> generatePrimitives(SoAction* <span style=\"color: #007c0c;\">\/*action*\/<\/span>) {}<span style=\"color: #007c0c;\">\/\/ create OpenGL resources<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> initialize();<span style=\"color: #007c0c;\">\/\/ destroy OpenGL resources<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> destroy();<span style=\"color: #0000ff;\">SoEXTENDER public :<\/span><span style=\"color: #007c0c;\">\/\/ Rendering stuff<\/span><br \/>\n<span style=\"color: #0000ff;\">virtual void<\/span> GLRender ( SoGLRenderAction * action );<span style=\"color: #007c0c;\">\/\/ BBox computation<\/span><br \/>\n<span style=\"color: #0000ff;\">virtual void<\/span> computeBBox(SoAction* action , SbBox3f &amp;box , SbVec3f&amp; center);<span style=\"color: #0000ff;\">SoINTERNAL public :<\/span><\/p>\n<p><span style=\"color: #0000ff;\">static void<\/span> initClass ();<br \/>\n<span style=\"color: #0000ff;\">static void<\/span> exitClass ();<\/p>\n<p><span style=\"color: #0000ff;\">private :<\/span><br \/>\n<span style=\"color: #0000ff;\">bool<\/span> m_initialized ;<br \/>\nGLuint m_vertexBuffer ;<br \/>\nGLuint m_colorBuffer ;<br \/>\nGLuint m_normalBuffer ;<br \/>\n};<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing B.2<\/strong>: Implementation \ffile for <em>OpenGL<\/em> compatibility custom node.<\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">#include<\/span> \"CustomNodeOpenGLCompatibility.h\"<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/elements\/SoLightModelElement.h&gt;<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/actions\/SoGLRenderAction.h&gt;<\/span><span style=\"color: #007c0c;\">\/\/ external stuff for data access<\/span><br \/>\n<span style=\"color: #0000ff;\">extern uint32_t<\/span> numVertices;<br \/>\n<span style=\"color: #0000ff;\">extern float<\/span> vertex [];<br \/>\n<span style=\"color: #0000ff;\">extern float<\/span> normal [];<br \/>\n<span style=\"color: #0000ff;\">extern float<\/span> color [];<br \/>\nSO_NODE_SOURCE(CustomNodeOpenGLCompatibility);CustomNodeOpenGLCompatibility::CustomNodeOpenGLCompatibility()<br \/>\n: m_initialized (<span style=\"color: #0000ff;\">false<\/span>)<br \/>\n{<br \/>\nSO_NODE_CONSTRUCTOR(CustomNodeOpenGLCompatibility);<br \/>\n}CustomNodeOpenGLCompatibility::~CustomNodeOpenGLCompatibility()<br \/>\n{<br \/>\ndestroy();<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCompatibility::initClass()<br \/>\n{<br \/>\ngetClassRenderEngineMode().setRenderMode(<br \/>\nSbRenderEngineMode::OIV_OPENGL_COMPATIBILITY_RENDERING);<br \/>\nSO__NODE_INIT_CLASS (CustomNodeOpenGLCompatibility,<br \/>\n<span style=\"color: #ff0000;\">\"CustomNodeOpenGLCompatibility\"<\/span>, SoShape);<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCompatibility::exitClass()<br \/>\n{<br \/>\nSO__NODE_EXIT_CLASS (CustomNodeOpenGLCompatibility);<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCompatibility::initialize()<br \/>\n{<br \/>\n<span style=\"color: #0000ff;\">if<\/span> (m_initialized)<br \/>\n{<br \/>\n<span style=\"color: #0000ff;\">return<\/span>;<br \/>\n}<br \/>\nglGenBuffers(1, &amp;m_vertexBuffer);<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);<br \/>\nglBufferData(GL_ARRAY_BUFFER, numVertices * <span style=\"color: #0000ff;\">sizeof<\/span>(<span style=\"color: #0000ff;\">float<\/span>) * 3, &amp;vertex [0], GL_STATIC_DRAW);<br \/>\nglGenBuffers(1, &amp;m_colorBuffer);<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);<br \/>\nglBufferData(GL_ARRAY_BUFFER, numVertices * <span style=\"color: #0000ff;\">sizeof<\/span>(<span style=\"color: #0000ff;\">float<\/span>) * 3, &amp;color [0], GL_STATIC_DRAW);<br \/>\nglGenBuffers(1, &amp;m_normalBuffer);<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_normalBuffer);<br \/>\nglBufferData(GL_ARRAY_BUFFER, numVertices * <span style=\"color: #0000ff;\">sizeof<\/span>(<span style=\"color: #0000ff;\">float<\/span>) * 3, &amp;normal [0], GL_STATIC_DRAW);<br \/>\nm_initialized = <span style=\"color: #0000ff;\">true<\/span>;<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCompatibility::destroy ()<br \/>\n{<br \/>\n<span style=\"color: #0000ff;\">if<\/span> (! m_initialized)<br \/>\n<span style=\"color: #0000ff;\">return<\/span> ;<br \/>\nglDeleteBuffers(1, &amp;m_vertexBuffer);<br \/>\nglDeleteBuffers(1, &amp;m_normalBuffer);<br \/>\nglDeleteBuffers(1, &amp;m_colorBuffer);<br \/>\nm_initialized = <span style=\"color: #0000ff;\">false<\/span>;<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCompatibility::GLRender(SoGLRenderAction* action)<br \/>\n{<br \/>\n<span style=\"color: #007c0c;\">\/\/ create resources<\/span><br \/>\ninitialize();<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);<br \/>\nglEnableClientState(GL_VERTEX_ARRAY);<br \/>\nglVertexPointer(3, GL_FLOAT, 0, 0);<br \/>\nSoState* state = action-&gt;getState();<br \/>\nSoLightModelElement::Model model = SoLightModelElement::get(state);<br \/>\n<span style=\"color: #0000ff;\">if<\/span> (model == SoLightModelElement::PHONG || model == SoLightModelElement :: PER_VERTEX_PHONG)<br \/>\n{<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);<br \/>\nglColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);<br \/>\nglEnable(GL_COLOR_MATERIAL);<br \/>\nglEnableClientState(GL_COLOR_ARRAY);<br \/>\nglColorPointer(3, GL_FLOAT, 0, 0);<br \/>\nglBindBuffer(GL_ARRAY_BUFFER , m_normalBuffer);<br \/>\nglEnableClientState(GL_NORMAL_ARRAY);<br \/>\nglNormalPointer(GL_FLOAT, 0, 0);<br \/>\n}<br \/>\nglDrawArrays(GL_TRIANGLES , 0, numVertices);<br \/>\n<span style=\"color: #0000ff;\">if<\/span> (model == SoLightModelElement :: PHONG || model == SoLightModelElement::PER_VERTEX_PHONG)<br \/>\n{<br \/>\nglDisable(GL_COLOR_MATERIAL);<br \/>\nglDisableClientState(GL_NORMAL_ARRAY);<br \/>\nglDisableClientState(GL_COLOR_ARRAY);<br \/>\n}<br \/>\nglDisableClientState(GL_VERTEX_ARRAY);<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCompatibility::computeBBox(SoAction* action, SbBox3f &amp;box, SbVec3f \u00a2er)<br \/>\n{<br \/>\nSbBox3f bbox ;<br \/>\n<span style=\"color: #0000ff;\">float<\/span>* vert=vertex ;<br \/>\n<span style=\"color: #0000ff;\">for<\/span> (uint32_t i = 0; i &lt; numVertices; ++i)<br \/>\n{<br \/>\nbbox.extendBy(SbVec3f(vert[0], vert[1], vert [2]));<br \/>\nvert += 3;<br \/>\n}<br \/>\nbox = bbox ;<br \/>\ncenter = box.getCenter();<br \/>\n}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><a name=\"AppendixC\"><\/a><strong>Appendix C: OpenGL core custom node source code example<\/strong><\/p>\n<p><strong>Listing C.1<\/strong>: Header file for <em>OpenGL<\/em> core custom node.<\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">#pragma<\/span> once<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/nodes\/SoShaderProgram.h&gt;<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/nodes\/SoShape.h&gt;<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/sys\/SoGL.h&gt;<\/span><span style=\"color: #007c0c;\">\/**<\/span><br \/>\n<span style=\"color: #007c0c;\">* @brief Custom OpenGL (OpenGL 4 core) node<\/span><br \/>\n<span style=\"color: #007c0c;\">*\/<\/span><br \/>\n<span style=\"color: #0000ff;\">class<\/span> CustomNodeOpenGLCore : <span style=\"color: #0000ff;\">public<\/span> SoShape<br \/>\n{<br \/>\nSO_NODE_HEADER(CustomNodeOpenGLCore);<br \/>\n<span style=\"color: #0000ff;\">public<\/span>:<br \/>\n<span style=\"color: #007c0c;\">\/**<\/span><br \/>\n<span style=\"color: #007c0c;\">* Default constructor.<\/span><br \/>\n<span style=\"color: #007c0c;\">*\/<\/span><br \/>\nCustomNodeOpenGLCore();<span style=\"color: #0000ff;\">protected<\/span>:<br \/>\n<span style=\"color: #007c0c;\">\/\/ destructor<\/span><br \/>\n<span style=\"color: #0000ff;\">virtual<\/span> ~CustomNodeOpenGLCore();<br \/>\n<span style=\"color: #007c0c;\">\/\/ not implemented here<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> generatePrimitives(SoAction* <span style=\"color: #007c0c;\">\/* action *\/<\/span>){}<br \/>\n<span style=\"color: #007c0c;\">\/\/ create OpenGL resources<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> initialize();<br \/>\n<span style=\"color: #007c0c;\">\/\/ destroy OpenGL resources<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> destroy();SoEXTENDER <span style=\"color: #0000ff;\">public<\/span>:<br \/>\n<span style=\"color: #007c0c;\">\/\/ Rendering stuff<\/span><br \/>\n<span style=\"color: #0000ff;\">virtual void<\/span> GLRender(SoGLRenderAction* action);<br \/>\n<span style=\"color: #007c0c;\">\/\/ BBox computation<\/span><br \/>\n<span style=\"color: #0000ff;\">virtual void<\/span> computeBBox(SoAction* action, SbBox3f &amp;box, SbVec3f&amp; center);SoINTERNAL <span style=\"color: #0000ff;\">public<\/span>:<br \/>\nstatic <span style=\"color: #0000ff;\">void<\/span> initClass();<br \/>\nstatic <span style=\"color: #0000ff;\">void<\/span> exitClass();<span style=\"color: #0000ff;\">private<\/span>:<br \/>\n<span style=\"color: #0000ff;\">bool<\/span> m_initialized;<br \/>\nGLuint m_vertexBuffer;<br \/>\nGLuint m_colorBuffer;<br \/>\nGLuint m_normalBuffer;<br \/>\n};<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing C.2<\/strong>: Implementation file for <em>OpenGL<\/em> core custom node.<\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">#include<\/span> <span style=\"color: #ff0000;\">\"CustomNodeOpenGLCore.h\"<\/span><br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/elements\/SoLightModelElement.h&gt;<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/actions\/SoGLRenderAction.h&gt;<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/sys\/SoGL.h&gt;<\/span><span style=\"color: #007c0c;\">\/\/ external stuff for data access<\/span><br \/>\n<span style=\"color: #0000ff;\">extern uint32_t<\/span> numVertices;<br \/>\n<span style=\"color: #0000ff;\">extern float<\/span> vertex [];<br \/>\n<span style=\"color: #0000ff;\">extern float<\/span> normal [];<br \/>\n<span style=\"color: #0000ff;\">extern float<\/span> color [];<br \/>\nSO_NODE_SOURCE(CustomNodeOpenGLCore);CustomNodeOpenGLCore::CustomNodeOpenGLCore()<br \/>\n: m_initialized (<span style=\"color: #0000ff;\">false<\/span>)<br \/>\n{<br \/>\nSO_NODE_CONSTRUCTOR(CustomNodeOpenGLCore);<br \/>\n}CustomNodeOpenGLCore::~CustomNodeOpenGLCore()<br \/>\n{<br \/>\ndestroy();<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCore::initClass()<br \/>\n{<br \/>\ngetClassRenderEngineMode().setRenderMode(<br \/>\nSbRenderEngineMode::OIV_OPENGL_CORE_RENDERING);<br \/>\nSO__NODE_INIT_CLASS (CustomNodeOpenGLCore,<br \/>\n<span style=\"color: #ff0000;\">\"CustomNodeOpenGLCore\"<\/span>, SoShape);<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCore::exitClass()<br \/>\n{<br \/>\nSO__NODE_EXIT_CLASS (CustomNodeOpenGLCore);<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCore::initialize()<br \/>\n{<br \/>\n<span style=\"color: #0000ff;\">if<\/span> (m_initialized)<br \/>\n{<br \/>\n<span style=\"color: #0000ff;\">return<\/span>;<br \/>\n}<br \/>\nglGenBuffers(1, &amp;m_vertexBuffer);<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);<br \/>\nglBufferData(GL_ARRAY_BUFFER, numVertices * <span style=\"color: #0000ff;\">sizeof<\/span>(<span style=\"color: #0000ff;\">float<\/span>) * 3, &amp;vertex [0], GL_STATIC_DRAW);<br \/>\nglGenBuffers(1, &amp;m_colorBuffer);<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);<br \/>\nglBufferData(GL_ARRAY_BUFFER, numVertices * <span style=\"color: #0000ff;\">sizeof<\/span>(<span style=\"color: #0000ff;\">float<\/span>) * 3, &amp;color [0], GL_STATIC_DRAW);<br \/>\nglGenBuffers(1, &amp;m_normalBuffer);<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_normalBuffer);<br \/>\nglBufferData(GL_ARRAY_BUFFER, numVertices * <span style=\"color: #0000ff;\">sizeof<\/span>(<span style=\"color: #0000ff;\">float<\/span>) * 3, &amp;normal [0], GL_STATIC_DRAW);<br \/>\nm_initialized = <span style=\"color: #0000ff;\">true<\/span>;<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCore::destroy ()<br \/>\n{<br \/>\n<span style=\"color: #0000ff;\">if<\/span> (! m_initialized)<br \/>\n<span style=\"color: #0000ff;\">return<\/span> ;<br \/>\nglDeleteBuffers(1, &amp;m_vertexBuffer);<br \/>\nglDeleteBuffers(1, &amp;m_normalBuffer);<br \/>\nglDeleteBuffers(1, &amp;m_colorBuffer);<br \/>\nm_initialized = <span style=\"color: #0000ff;\">false<\/span>;<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCore::GLRender(SoGLRenderAction* action)<br \/>\n{<br \/>\n<span style=\"color: #007c0c;\">\/\/ create resources<\/span><br \/>\ninitialize();<br \/>\nglEnableClientState (0);<br \/>\nglEnableClientState (1);<br \/>\nglEnableClientState (2);<span style=\"color: #007c0c;\">\/\/ vertex positions<\/span><br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); \u00a0\u00a0\u00a0\u00a0glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);<br \/>\n<span style=\"color: #007c0c;\">\/\/ vertex colors<\/span><br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); \u00a0\u00a0\u00a0\u00a0glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);<br \/>\n<span style=\"color: #007c0c;\">\/\/ vertex normals<\/span><br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_normalBuffer); \u00a0\u00a0\u00a0\u00a0glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);<br \/>\nglDrawArrays(GL_TRIANGLES, 0, numVertices);glDisableClientState(2);<br \/>\nglDisableClientState(1);<br \/>\nglDisableClientState(0);<br \/>\n}<\/p>\n<p><span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeOpenGLCore::computeBBox(SoAction* action, SbBox3f &amp;box, SbVec3f \u00a2er)<br \/>\n{<br \/>\nSbBox3f bbox ;<br \/>\n<span style=\"color: #0000ff;\">float<\/span>* vert=vertex ;<br \/>\n<span style=\"color: #0000ff;\">for<\/span> (uint32_t i = 0; i &lt; numVertices; ++i)<br \/>\n{<br \/>\nbbox.extendBy(SbVec3f(vert[0], vert[1], vert [2]));<br \/>\nvert += 3;<br \/>\n}<br \/>\nbox = bbox ;<br \/>\ncenter = box.getCenter();<br \/>\n}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing C.3<\/strong>: GLSL vertex shader for <em>OpenGL<\/em> core custom node.<\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">#<\/span> version 410 core<\/span><\/p>\n<p><span style=\"color: #007c0c;\">\/\/! oiv_include &lt;Inventor\/oivShaderState.h&gt;<\/span><\/p>\n<p><span style=\"color: #0000ff;\">layout<\/span>(location = 0) in vec3 position;<br \/>\n<span style=\"color: #0000ff;\">layout<\/span>(location = 1) in vec3 color;<br \/>\n<span style=\"color: #0000ff;\">layout<\/span>(location = 2) in vec3 normal;<\/p>\n<p>out vec3 normal;<br \/>\nout vec4 color;<\/p>\n<p><span style=\"color: #0000ff;\">void<\/span><br \/>\nmain ()<br \/>\n{<br \/>\nnormal = OivNormalMatrix() * normal;<br \/>\ncolor = vec4(color, 1.0);<br \/>\ngl_Position = OivModelViewProjectionMatrix() * vec4(position, 1.0);<br \/>\n}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing C.4<\/strong>: GLSL fragmant shader for <em>OpenGL<\/em> core custom node.<\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">#<\/span> version 410 core<\/span><\/p>\n<p><span style=\"color: #007c0c;\">\/\/! oiv_include &lt;Inventor\/oivShaderState.h&gt;<\/span><\/p>\n<p>in vec3 normal;<br \/>\nin vec4 color;<\/p>\n<p><span style=\"color: #0000ff;\">void<\/span><br \/>\nmain ()<br \/>\n{<br \/>\nvec3 l = normalize(OivLightSourcePosition (0).xyz);<br \/>\nvec3 n = normalize(normal);<br \/>\ngl_FragColor = max(0.0, dot(n, l)) * color;<br \/>\ngl_FragColor.a = 1.0;<br \/>\n}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><a name=\"AppendixD\"><\/a><strong>Appendix D: SoGLCallback source code example<\/strong><\/p>\n<p><strong>Listing D.1<\/strong>: Header file for <em>OpenGL<\/em>SoGLCallback.<\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">#pragma<\/span> once<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/actions\/SoAction.h&gt;<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/sys\/SoGL.h&gt;<\/span><span style=\"color: #007c0c;\">\/**<\/span><br \/>\n<span style=\"color: #007c0c;\">* @brief Custom OpenGL (OpenGL 4 core) node<\/span><br \/>\n<span style=\"color: #007c0c;\">*\/<\/span><br \/>\n<span style=\"color: #0000ff;\">class<\/span> CustomNodeGLCallback<br \/>\n{<br \/>\n<span style=\"color: #0000ff;\">public<\/span>:<br \/>\n<span style=\"color: #007c0c;\">\/**<\/span><br \/>\n<span style=\"color: #007c0c;\">* Default constructor.<\/span><br \/>\n<span style=\"color: #007c0c;\">*\/<\/span><br \/>\nCustomNodeGLCallback();<span style=\"color: #0000ff;\">public<\/span>:<br \/>\n<span style=\"color: #007c0c;\">\/**<\/span><br \/>\n<span style=\"color: #007c0c;\">* @brief Callback for SoGLCallback<\/span><br \/>\n<span style=\"color: #007c0c;\">*\/<\/span><br \/>\n<span style=\"color: #0000ff;\">static void<\/span> callback(void* data, SoAction* action );<span style=\"color: #0000ff;\">protected<\/span>:<span style=\"color: #007c0c;\">\/\/ Destructor<\/span><br \/>\n~CustomNodeGLCallback();<br \/>\n<span style=\"color: #007c0c;\">\/\/ Create resources<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> initialiaze();<br \/>\n<span style=\"color: #007c0c;\">\/\/ destroy OpenGL resources<\/span><br \/>\n<span style=\"color: #0000ff;\">void<\/span> destroy();<span style=\"color: #0000ff;\">private<\/span>:<br \/>\n<span style=\"color: #0000ff;\">bool<\/span> m_initialized;<br \/>\nGLuint m_vertexBuffer;<br \/>\nGLuint m_colorBuffer;<br \/>\nGLuint m_normalBuffer;<br \/>\n};<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing D.2<\/strong>: Implementation file for <em>OpenGL<\/em>SoGLCallback.<\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\"><span style=\"color: #0000ff;\">#include<\/span> <span style=\"color: #ff0000;\">\"CustomNodeGLCallback.h\"<\/span><br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/elements\/SoLightModelElement.h&gt;<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/actions\/SoGLRenderAction.h&gt;<br \/>\n<span style=\"color: #0000ff;\">#include<\/span> &lt;Inventor\/sys\/SoGL.h&gt;<\/span><span style=\"color: #007c0c;\">\/\/ external stuff for bunny data access<\/span><br \/>\n<span style=\"color: #0000ff;\">extern uint32_t<\/span> numVertices;<br \/>\n<span style=\"color: #0000ff;\">extern float<\/span> vertex [];<br \/>\n<span style=\"color: #0000ff;\">extern float<\/span> normal [];<br \/>\n<span style=\"color: #0000ff;\">extern float<\/span> color [];CustomNodeGLCallback::CustomNodeGLCallback()<br \/>\n: m_initialized (<span style=\"color: #0000ff;\">false<\/span>)<br \/>\n{}CustomNodeGLCallback::~CustomNodeGLCallback()<br \/>\n{<br \/>\ndestroy();<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeGLCallback::initialize()<br \/>\n{<br \/>\n<span style=\"color: #0000ff;\">if<\/span> (m_initialized)<br \/>\n{<br \/>\nreturn;<br \/>\n}glGenBuffers(1, &amp;m_vertexBuffer);<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);<br \/>\nglBufferData(GL_ARRAY_BUFFER, numVertices * <span style=\"color: #0000ff;\">sizeof<\/span>(<span style=\"color: #0000ff;\">float<\/span>) * 3, &amp;vertex[0], GL_STATIC_DRAW);glGenBuffers(1, &amp;m_colorBuffer);<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);<br \/>\nglBufferData(GL_ARRAY_BUFFER, numVertices * <span style=\"color: #0000ff;\">sizeof<\/span>(<span style=\"color: #0000ff;\">float<\/span>) * 3, &amp;color[0], GL_STATIC_DRAW);glGenBuffers(1, &amp;m_normalBuffer);<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, m_normalBuffer);<br \/>\nglBufferData(GL_ARRAY_BUFFER, numVertices * <span style=\"color: #0000ff;\">sizeof<\/span>(<span style=\"color: #0000ff;\">float<\/span>) * 3, &amp;normal[0], GL_STATIC_DRAW);<br \/>\nm_initialized = true;<br \/>\n}<span style=\"color: #0000ff;\">void<\/span><br \/>\nCustomNodeGLCallback::callback(<span style=\"color: #0000ff;\">void<\/span>* data , SoAction* action)<br \/>\n{<br \/>\nCustomNodeGLCallback* cb = <span style=\"color: #0000ff;\">static_cast<\/span>&lt;CustomNodeGLCallback*&gt;(data);<span style=\"color: #007c0c;\">\/\/ create resources<\/span><br \/>\ncb-&gt;initialize();<\/p>\n<p>glBindBuffer(GL_ARRAY_BUFFER, cb-&gt;m_vertexBuffer);<br \/>\nglEnableClientState(GL_VERTEX_ARRAY);<br \/>\nglVertexPointer(3, GL_FLOAT, 0, 0);<\/p>\n<p>SoState* state = action-&gt;getState();<br \/>\nSoLightModelElement::Model model = SoLightModelElement::get(state);<\/p>\n<p><span style=\"color: #0000ff;\">if<\/span> (model == SoLightModelElement::PHONG || model == SoLightModelElement::PER_VERTEX_PHONG)<br \/>\n{<br \/>\nglBindBuffer(GL_ARRAY_BUFFER, cb-&gt;m_colorBuffer);<br \/>\nglColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);<br \/>\nglEnable(GL_COLOR_MATERIAL);<br \/>\nglEnableClientState(GL_COLOR_ARRAY);<br \/>\nglColorPointer(3, GL_FLOAT, 0, 0);<\/p>\n<p>glBindBuffer(GL_ARRAY_BUFFER, cb-&gt;m_normalBuffer);<br \/>\nglEnableClientState(GL_NORMAL_ARRAY);<br \/>\nglNormalPointer(GL_FLOAT, 0, 0);<br \/>\n}<\/p>\n<p>glDrawArrays(GL_TRIANGLES, 0, numVertices);<\/p>\n<p><span style=\"color: #0000ff;\">if<\/span> (model == SoLightModelElement::PHONG || model == SoLightModelElement::PER_VERTEX_PHONG)<br \/>\n{<br \/>\nglDisable(GL_COLOR_MATERIAL);<br \/>\nglDisableClientState(GL_NORMAL_ARRAY);<br \/>\nglDisableClientState(GL_COLOR_ARRAY);<br \/>\n}<\/p>\n<p>glDisableClientState(GL_VERTEX_ARRAY);<br \/>\n}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Listing D.3<\/strong>: Usage for SoGLCallback.<\/p>\n<table style=\"width: 100%;\" border=\"0\">\n<tbody>\n<tr>\n<td style=\"background-color: #fdfbef; text-align: left; vertical-align: top;\"><span style=\"font-family: courier new,courier,monospace;\">CustomNodeGLCallback* callback = <span style=\"color: #0000ff;\">new<\/span> CustomNodeGLCallback;<br \/>\nSoGLCallback* glcb = <span style=\"color: #0000ff;\">new<\/span> SoGLCallback;<br \/>\nglcb-&gt;setCallback(&amp;CustomNodeGLCallback::callback, callback);<br \/>\n<span style=\"color: #007c0c;\">\/\/ add glcb to scene graph<\/span> <\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Open Inventor render engine Custom nodes Pure Open Inventor custom nodes OpenGL custom nodes Guidelines Common principles OpenGL render&hellip;<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":63,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-175","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/pages\/175","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/comments?post=175"}],"version-history":[{"count":35,"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/pages\/175\/revisions"}],"predecessor-version":[{"id":529,"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/pages\/175\/revisions\/529"}],"up":[{"embeddable":true,"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/pages\/63"}],"wp:attachment":[{"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/media?parent=175"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}