Tutorials: 3D Plugin Class

This section contains a description of two very simple plugins of the PLUGIN_3D class and walks the user through the setup and building of the code.

Basic 3D Plugin

This tutorial walks the user through the development of a very basic 3D plugin named “PLUGIN_3D_DEMO1”. The purpose of this tutorial is only to demonstrate the construction of a very basic 3D plugin which does nothing other than provide a few filter strings which permit the KiCad user to filter file names while browsing for 3D models. The code demonstrated here is the absolute minimum requirement for any 3D plugin and can be used as a template for creating more advanced plugins.

In order to build the demo project we require the following:

  • CMake

  • KiCad plugin headers

  • KiCad Scene Graph library kicad_3dsg

To automatically detect the KiCad headers and library we shall use a CMake FindPackage script; the script supplied in this tutorial should work on Linux and Windows if the relevant header files are installed to ${KICAD_ROOT_DIR}/kicad and the KiCad Scene Graph library is installed in ${KICAD_ROOT_DIR}/lib.

To start let’s create a project directory and the FindPackage script:

  1. mkdir demo && cd demo
  2. export DEMO_ROOT=${PWD}
  3. mkdir CMakeModules && cd CMakeModules
  4. cat > FindKICAD.cmake << _EOF
  5. find_path( KICAD_INCLUDE_DIR kicad/plugins/kicad_plugin.h
  6. PATHS ${KICAD_ROOT_DIR}/include $ENV{KICAD_ROOT_DIR}/include
  7. DOC "Kicad plugins header path."
  8. )
  9. if( NOT ${KICAD_INCLUDE_DIR} STREQUAL "KICAD_INCLUDE_DIR-NOTFOUND" )
  10. # attempt to extract the version information from sg_version.h
  11. find_file( KICAD_SGVERSION sg_version.h
  12. PATHS ${KICAD_INCLUDE_DIR}
  13. PATH_SUFFIXES kicad/plugins/3dapi
  14. NO_DEFAULT_PATH )
  15. if( NOT ${KICAD_SGVERSION} STREQUAL "KICAD_SGVERSION-NOTFOUND" )
  16. # extract the "#define KICADSG_VERSION*" lines
  17. file( STRINGS ${KICAD_SGVERSION} _version REGEX "^#define.*KICADSG_VERSION.*" )
  18. foreach( SVAR ${_version} )
  19. string( REGEX MATCH KICADSG_VERSION_[M,A,J,O,R,I,N,P,T,C,H,E,V,I,S]* _VARNAME ${SVAR} )
  20. string( REGEX MATCH [0-9]+ _VALUE ${SVAR} )
  21. if( NOT ${_VARNAME} STREQUAL "" AND NOT ${_VALUE} STREQUAL "" )
  22. set( _${_VARNAME} ${_VALUE} )
  23. endif()
  24. endforeach()
  25. #ensure that NOT SG3D_VERSION* will evaluate to '0'
  26. if( NOT _KICADSG_VERSION_MAJOR )
  27. set( _KICADSG_VERSION_MAJOR 0 )
  28. endif()
  29. if( NOT _KICADSG_VERSION_MINOR )
  30. set( _KICADSG_VERSION_MINOR 0 )
  31. endif()
  32. if( NOT _KICADSG_VERSION_PATCH )
  33. set( _KICADSG_VERSION_PATCH 0 )
  34. endif()
  35. if( NOT _KICADSG_VERSION_REVISION )
  36. set( _KICADSG_VERSION_REVISION 0 )
  37. endif()
  38. set( KICAD_VERSION ${_KICADSG_VERSION_MAJOR}.${_KICADSG_VERSION_MINOR}.${_KICADSG_VERSION_PATCH}.${_KICADSG_VERSION_REVISION} )
  39. unset( KICAD_SGVERSION CACHE )
  40. endif()
  41. endif()
  42. find_library( KICAD_LIBRARY
  43. NAMES kicad_3dsg
  44. PATHS
  45. ${KICAD_ROOT_DIR}/lib $ENV{KICAD_ROOT_DIR}/lib
  46. ${KICAD_ROOT_DIR}/bin $ENV{KICAD_ROOT_DIR}/bin
  47. DOC "Kicad scenegraph library path."
  48. )
  49. include( FindPackageHandleStandardArgs )
  50. FIND_PACKAGE_HANDLE_STANDARD_ARGS( KICAD
  51. REQUIRED_VARS
  52. KICAD_INCLUDE_DIR
  53. KICAD_LIBRARY
  54. KICAD_VERSION
  55. VERSION_VAR KICAD_VERSION )
  56. mark_as_advanced( KICAD_INCLUDE_DIR )
  57. set( KICAD_VERSION_MAJOR ${_KICADSG_VERSION_MAJOR} CACHE INTERNAL "" )
  58. set( KICAD_VERSION_MINOR ${_KICADSG_VERSION_MINOR} CACHE INTERNAL "" )
  59. set( KICAD_VERSION_PATCH ${_KICADSG_VERSION_PATCH} CACHE INTERNAL "" )
  60. set( KICAD_VERSION_TWEAK ${_KICADSG_VERSION_REVISION} CACHE INTERNAL "" )
  61. _EOF

Kicad and its plugin headers must be installed; if they are installed to a user directory or under /opt on Linux, or you are using Windows, you will need to set the KICAD_ROOT_DIR environment variable to point to the directory containing the KiCad include and lib directories. For OS X the FindPackage script presented here may require some adjustments.

To configure and build the tutorial code we will use CMake and create a CMakeLists.txt script file:

  1. cd ${DEMO_ROOT}
  2. cat > CMakeLists.txt << _EOF
  3. # declare the name of the project
  4. project( PLUGIN_DEMO )
  5. # check that we have a version of CMake with all required features
  6. cmake_minimum_required( VERSION 2.8.12 FATAL_ERROR )
  7. # inform CMake of where to find the FindKICAD script
  8. set( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules )
  9. # attempt to discover the installed kicad headers and library
  10. # and set the variables:
  11. # KICAD_INCLUDE_DIR
  12. # KICAD_LIBRARY
  13. find_package( KICAD 1.0 REQUIRED )
  14. # add the kicad include directory to the compiler's search path
  15. include_directories( ${KICAD_INCLUDE_DIR}/kicad )
  16. # create a plugin named s3d_plugin_demo1
  17. add_library( s3d_plugin_demo1 MODULE
  18. src/s3d_plugin_demo1.cpp
  19. )
  20. _EOF

The first demo project is very basic; it consists of a single file with no external link dependencies other than the compiler defaults. We start by creating a source directory:

  1. cd ${DEMO_ROOT}
  2. mkdir src && cd src
  3. export DEMO_SRC=${PWD}

Now we create the plugin source itself:

s3d_plugin_demo1.cpp

  1. #include <iostream>
  2. // the 3d_plugin.h header defines the functions required of 3D plugins
  3. #include "plugins/3d/3d_plugin.h"
  4. // define the version information of this plugin; do not confuse this
  5. // with the Plugin Class version which is defined in 3d_plugin.h
  6. #define PLUGIN_3D_DEMO1_MAJOR 1
  7. #define PLUGIN_3D_DEMO1_MINOR 0
  8. #define PLUGIN_3D_DEMO1_PATCH 0
  9. #define PLUGIN_3D_DEMO1_REVNO 0
  10. // implement the function which provides users with this plugin's name
  11. const char* GetKicadPluginName( void )
  12. {
  13. return "PLUGIN_3D_DEMO1";
  14. }
  15. // implement the function which provides users with this plugin's version
  16. void GetPluginVersion( unsigned char* Major, unsigned char* Minor,
  17. unsigned char* Patch, unsigned char* Revision )
  18. {
  19. if( Major )
  20. *Major = PLUGIN_3D_DEMO1_MAJOR;
  21. if( Minor )
  22. *Minor = PLUGIN_3D_DEMO1_MINOR;
  23. if( Patch )
  24. *Patch = PLUGIN_3D_DEMO1_PATCH;
  25. if( Revision )
  26. *Revision = PLUGIN_3D_DEMO1_REVNO;
  27. return;
  28. }
  29. // number of extensions supported; on *NIX systems the extensions are
  30. // provided twice - once in lower case and once in upper case letters
  31. #ifdef _WIN32
  32. #define NEXTS 7
  33. #else
  34. #define NEXTS 14
  35. #endif
  36. // number of filter sets supported
  37. #define NFILS 5
  38. // define the extension strings and filter strings which this
  39. // plugin will supply to the user
  40. static char ext0[] = "wrl";
  41. static char ext1[] = "x3d";
  42. static char ext2[] = "emn";
  43. static char ext3[] = "iges";
  44. static char ext4[] = "igs";
  45. static char ext5[] = "stp";
  46. static char ext6[] = "step";
  47. #ifdef _WIN32
  48. static char fil0[] = "VRML 1.0/2.0 (*.wrl)|*.wrl";
  49. static char fil1[] = "X3D (*.x3d)|*.x3d";
  50. static char fil2[] = "IDF 2.0/3.0 (*.emn)|*.emn";
  51. static char fil3[] = "IGESv5.3 (*.igs;*.iges)|*.igs;*.iges";
  52. static char fil4[] = "STEP (*.stp;*.step)|*.stp;*.step";
  53. #else
  54. static char ext7[] = "WRL";
  55. static char ext8[] = "X3D";
  56. static char ext9[] = "EMN";
  57. static char ext10[] = "IGES";
  58. static char ext11[] = "IGS";
  59. static char ext12[] = "STP";
  60. static char ext13[] = "STEP";
  61. static char fil0[] = "VRML 1.0/2.0 (*.wrl;*.WRL)|*.wrl;*.WRL";
  62. static char fil1[] = "X3D (*.x3d;*.X3D)|*.x3d;*.X3D";
  63. static char fil2[] = "IDF 2.0/3.0 (*.emn;*.EMN)|*.emn;*.EMN";
  64. static char fil3[] = "IGESv5.3 (*.igs;*.iges;*.IGS;*.IGES)|*.igs;*.iges;*.IGS;*.IGES";
  65. static char fil4[] = "STEP (*.stp;*.step;*.STP;*.STEP)|*.stp;*.step;*.STP;*.STEP";
  66. #endif
  67. // instantiate a convenient data structure for accessing the
  68. // lists of extension and filter strings
  69. static struct FILE_DATA
  70. {
  71. char const* extensions[NEXTS];
  72. char const* filters[NFILS];
  73. FILE_DATA()
  74. {
  75. extensions[0] = ext0;
  76. extensions[1] = ext1;
  77. extensions[2] = ext2;
  78. extensions[3] = ext3;
  79. extensions[4] = ext4;
  80. extensions[5] = ext5;
  81. extensions[6] = ext6;
  82. filters[0] = fil0;
  83. filters[1] = fil1;
  84. filters[2] = fil2;
  85. filters[3] = fil3;
  86. filters[4] = fil4;
  87. #ifndef _WIN32
  88. extensions[7] = ext7;
  89. extensions[8] = ext8;
  90. extensions[9] = ext9;
  91. extensions[10] = ext10;
  92. extensions[11] = ext11;
  93. extensions[12] = ext12;
  94. extensions[13] = ext13;
  95. #endif
  96. return;
  97. }
  98. } file_data;
  99. // return the number of extensions supported by this plugin
  100. int GetNExtensions( void )
  101. {
  102. return NEXTS;
  103. }
  104. // return the indexed extension string
  105. char const* GetModelExtension( int aIndex )
  106. {
  107. if( aIndex < 0 || aIndex >= NEXTS )
  108. return NULL;
  109. return file_data.extensions[aIndex];
  110. }
  111. // return the number of filter strings provided by this plugin
  112. int GetNFilters( void )
  113. {
  114. return NFILS;
  115. }
  116. // return the indexed filter string
  117. char const* GetFileFilter( int aIndex )
  118. {
  119. if( aIndex < 0 || aIndex >= NFILS )
  120. return NULL;
  121. return file_data.filters[aIndex];
  122. }
  123. // return false since this plugin does not provide visualization data
  124. bool CanRender( void )
  125. {
  126. return false;
  127. }
  128. // return NULL since this plugin does not provide visualization data
  129. SCENEGRAPH* Load( char const* aFileName )
  130. {
  131. // this dummy plugin does not support rendering of any models
  132. return NULL;
  133. }

This source file meets all the minimum requirements to implement a 3D plugin. The plugin does not produce any data for rendering models but it can provide KiCad with a list of supported model file extensions and file extension filters to enhance the 3D model file selection dialog. Within KiCad the extension strings are used to select the plugins which may be used to load a specified model; for example, if the plugin is wrl then KiCad will invoke each plugin which claims to support the extension wrl until a plugin returns visualization data. The file filters provided by each plugin are passed to the 3D file selector dialog to improve the browsing UI.

To build the plugin:

  1. cd ${DEMO_ROOT}
  2. # export KICAD_ROOT_DIR if necessary
  3. mkdir build && cd build
  4. cmake .. && make

The plugin will be built but not installed; you must copy the plugin file to KiCad’s plugin directory if you wish to load the plugin.

Advanced 3D Plugin

This tutorial walks the user through the development of a 3D plugin named “PLUGIN_3D_DEMO2”. The purpose of this tutorial is to demonstrate the construction of a very basic scene graph which the KiCad previewer can render. The plugin claims to handle files of type txt. Although the file must exist in order for the cache manager to invoke the plugin, the file contents are not processed by this plugin; instead, the plugin simply creates a scene graph containing a pair of tetrahedra. This tutorial assumes that the first tutorial had been completed and that the CMakeLists.txt and FindKICAD.cmake script files have been created.

Place the new source file in the same directory as the previous tutorial’s source file and we will extend the previous tutorial’s CMakeLists.txt file to build this tutorial. Since this plugin will create a scene graph for KiCad we need to link to KiCad’s scene graph library kicad_3dsg. KiCad’s Scene Graph Library provides a set of classes which can be used to build the Scene Graph Object; the Scene Graph Object is an intermediate data visualization format used by the 3D Cache Manager. All plugins which support model visualization must translate the model data into a scene graph via this library.

The first step is to extend CMakeLists.txt to build this tutorial project:

  1. cd ${DEMO_ROOT}
  2. cat >> CMakeLists.txt << _EOF
  3. add_library( s3d_plugin_demo2 MODULE
  4. src/s3d_plugin_demo2.cpp
  5. )
  6. target_link_libraries( s3d_plugin_demo2 ${KICAD_LIBRARY} )
  7. _EOF

Now we change to the source directory and create the source file:

  1. cd ${DEMO_SRC}

s3d_plugin_demo2.cpp

  1. #include <cmath>
  2. // 3D Plugin Class declarations
  3. #include "plugins/3d/3d_plugin.h"
  4. // interface to KiCad Scene Graph Library
  5. #include "plugins/3dapi/ifsg_all.h"
  6. // version information for this plugin
  7. #define PLUGIN_3D_DEMO2_MAJOR 1
  8. #define PLUGIN_3D_DEMO2_MINOR 0
  9. #define PLUGIN_3D_DEMO2_PATCH 0
  10. #define PLUGIN_3D_DEMO2_REVNO 0
  11. // provide the name of this plugin
  12. const char* GetKicadPluginName( void )
  13. {
  14. return "PLUGIN_3D_DEMO2";
  15. }
  16. // provide the version of this plugin
  17. void GetPluginVersion( unsigned char* Major, unsigned char* Minor,
  18. unsigned char* Patch, unsigned char* Revision )
  19. {
  20. if( Major )
  21. *Major = PLUGIN_3D_DEMO2_MAJOR;
  22. if( Minor )
  23. *Minor = PLUGIN_3D_DEMO2_MINOR;
  24. if( Patch )
  25. *Patch = PLUGIN_3D_DEMO2_PATCH;
  26. if( Revision )
  27. *Revision = PLUGIN_3D_DEMO2_REVNO;
  28. return;
  29. }
  30. // number of extensions supported
  31. #ifdef _WIN32
  32. #define NEXTS 1
  33. #else
  34. #define NEXTS 2
  35. #endif
  36. // number of filter sets supported
  37. #define NFILS 1
  38. static char ext0[] = "txt";
  39. #ifdef _WIN32
  40. static char fil0[] = "demo (*.txt)|*.txt";
  41. #else
  42. static char ext1[] = "TXT";
  43. static char fil0[] = "demo (*.txt;*.TXT)|*.txt;*.TXT";
  44. #endif
  45. static struct FILE_DATA
  46. {
  47. char const* extensions[NEXTS];
  48. char const* filters[NFILS];
  49. FILE_DATA()
  50. {
  51. extensions[0] = ext0;
  52. filters[0] = fil0;
  53. #ifndef _WIN32
  54. extensions[1] = ext1;
  55. #endif
  56. return;
  57. }
  58. } file_data;
  59. int GetNExtensions( void )
  60. {
  61. return NEXTS;
  62. }
  63. char const* GetModelExtension( int aIndex )
  64. {
  65. if( aIndex < 0 || aIndex >= NEXTS )
  66. return NULL;
  67. return file_data.extensions[aIndex];
  68. }
  69. int GetNFilters( void )
  70. {
  71. return NFILS;
  72. }
  73. char const* GetFileFilter( int aIndex )
  74. {
  75. if( aIndex < 0 || aIndex >= NFILS )
  76. return NULL;
  77. return file_data.filters[aIndex];
  78. }
  79. // return true since this plugin can provide visualization data
  80. bool CanRender( void )
  81. {
  82. return true;
  83. }
  84. // create the visualization data
  85. SCENEGRAPH* Load( char const* aFileName )
  86. {
  87. // For this demonstration we create a tetrahedron (tx1) consisting
  88. // of a SCENEGRAPH (VRML Transform) which in turn contains 4
  89. // SGSHAPE (VRML Shape) objects representing each of the sides of
  90. // the tetrahedron. Each Shape is associated with a color (SGAPPEARANCE)
  91. // and a SGFACESET (VRML Geometry->indexedFaceSet). Each SGFACESET is
  92. // associated with a vertex list (SGCOORDS), a per-vertex normals
  93. // list (SGNORMALS), and a coordinate index (SGCOORDINDEX). One shape
  94. // is used to represent each face so that we may use per-vertex-per-face
  95. // normals.
  96. //
  97. // The tetrahedron in turn is a child of a top level SCENEGRAPH (tx0)
  98. // which has a second SCENEGRAPH child (tx2) which is a transformation
  99. // of the tetrahedron tx1 (rotation + translation). This demonstrates
  100. // the reuse of components within the scene graph hierarchy.
  101. // define the vertices of the tetrahedron
  102. // face 1: 0, 3, 1
  103. // face 2: 0, 2, 3
  104. // face 3: 1, 3, 2
  105. // face 4: 0, 1, 2
  106. double SQ2 = sqrt( 0.5 );
  107. SGPOINT vert[4];
  108. vert[0] = SGPOINT( 1.0, 0.0, -SQ2 );
  109. vert[1] = SGPOINT( -1.0, 0.0, -SQ2 );
  110. vert[2] = SGPOINT( 0.0, 1.0, SQ2 );
  111. vert[3] = SGPOINT( 0.0, -1.0, SQ2 );
  112. // create the top level transform; this will hold all other
  113. // scenegraph objects; a transform may hold other transforms and
  114. // shapes
  115. IFSG_TRANSFORM* tx0 = new IFSG_TRANSFORM( true );
  116. // create the transform which will house the shapes
  117. IFSG_TRANSFORM* tx1 = new IFSG_TRANSFORM( tx0->GetRawPtr() );
  118. // add a shape which we will use to define one face of the tetrahedron;
  119. // shapes hold facesets and appearances
  120. IFSG_SHAPE* shape = new IFSG_SHAPE( *tx1 );
  121. // add a faceset; these contain coordinate lists, coordinate indices,
  122. // vertex lists, vertex indices, and may also contain color lists and
  123. // their indices.
  124. IFSG_FACESET* face = new IFSG_FACESET( *shape );
  125. IFSG_COORDS* cp = new IFSG_COORDS( *face );
  126. cp->AddCoord( vert[0] );
  127. cp->AddCoord( vert[3] );
  128. cp->AddCoord( vert[1] );
  129. // coordinate indices - note: enforce triangles;
  130. // in real plugins where it is not necessarily possible
  131. // to determine which side a triangle is visible from,
  132. // 2 point orders must be specified for each triangle
  133. IFSG_COORDINDEX* coordIdx = new IFSG_COORDINDEX( *face );
  134. coordIdx->AddIndex( 0 );
  135. coordIdx->AddIndex( 1 );
  136. coordIdx->AddIndex( 2 );
  137. // create an appearance; appearances are owned by shapes
  138. // magenta
  139. IFSG_APPEARANCE* material = new IFSG_APPEARANCE( *shape);
  140. material->SetSpecular( 0.1, 0.0, 0.1 );
  141. material->SetDiffuse( 0.8, 0.0, 0.8 );
  142. material->SetAmbient( 0.2, 0.2, 0.2 );
  143. material->SetShininess( 0.2 );
  144. // normals
  145. IFSG_NORMALS* np = new IFSG_NORMALS( *face );
  146. SGVECTOR nval = S3D::CalcTriNorm( vert[0], vert[3], vert[1] );
  147. np->AddNormal( nval );
  148. np->AddNormal( nval );
  149. np->AddNormal( nval );
  150. //
  151. // Shape2
  152. // Note: we reuse the IFSG* wrappers to create and manipulate new
  153. // data structures.
  154. //
  155. shape->NewNode( *tx1 );
  156. face->NewNode( *shape );
  157. coordIdx->NewNode( *face );
  158. cp->NewNode( *face );
  159. np->NewNode( *face );
  160. // vertices
  161. cp->AddCoord( vert[0] );
  162. cp->AddCoord( vert[2] );
  163. cp->AddCoord( vert[3] );
  164. // indices
  165. coordIdx->AddIndex( 0 );
  166. coordIdx->AddIndex( 1 );
  167. coordIdx->AddIndex( 2 );
  168. // normals
  169. nval = S3D::CalcTriNorm( vert[0], vert[2], vert[3] );
  170. np->AddNormal( nval );
  171. np->AddNormal( nval );
  172. np->AddNormal( nval );
  173. // color (red)
  174. material->NewNode( *shape );
  175. material->SetSpecular( 0.2, 0.0, 0.0 );
  176. material->SetDiffuse( 0.9, 0.0, 0.0 );
  177. material->SetAmbient( 0.2, 0.2, 0.2 );
  178. material->SetShininess( 0.1 );
  179. //
  180. // Shape3
  181. //
  182. shape->NewNode( *tx1 );
  183. face->NewNode( *shape );
  184. coordIdx->NewNode( *face );
  185. cp->NewNode( *face );
  186. np->NewNode( *face );
  187. // vertices
  188. cp->AddCoord( vert[1] );
  189. cp->AddCoord( vert[3] );
  190. cp->AddCoord( vert[2] );
  191. // indices
  192. coordIdx->AddIndex( 0 );
  193. coordIdx->AddIndex( 1 );
  194. coordIdx->AddIndex( 2 );
  195. // normals
  196. nval = S3D::CalcTriNorm( vert[1], vert[3], vert[2] );
  197. np->AddNormal( nval );
  198. np->AddNormal( nval );
  199. np->AddNormal( nval );
  200. // color (green)
  201. material->NewNode( *shape );
  202. material->SetSpecular( 0.0, 0.1, 0.0 );
  203. material->SetDiffuse( 0.0, 0.9, 0.0 );
  204. material->SetAmbient( 0.2, 0.2, 0.2 );
  205. material->SetShininess( 0.1 );
  206. //
  207. // Shape4
  208. //
  209. shape->NewNode( *tx1 );
  210. face->NewNode( *shape );
  211. coordIdx->NewNode( *face );
  212. cp->NewNode( *face );
  213. np->NewNode( *face );
  214. // vertices
  215. cp->AddCoord( vert[0] );
  216. cp->AddCoord( vert[1] );
  217. cp->AddCoord( vert[2] );
  218. // indices
  219. coordIdx->AddIndex( 0 );
  220. coordIdx->AddIndex( 1 );
  221. coordIdx->AddIndex( 2 );
  222. // normals
  223. nval = S3D::CalcTriNorm( vert[0], vert[1], vert[2] );
  224. np->AddNormal( nval );
  225. np->AddNormal( nval );
  226. np->AddNormal( nval );
  227. // color (blue)
  228. material->NewNode( *shape );
  229. material->SetSpecular( 0.0, 0.0, 0.1 );
  230. material->SetDiffuse( 0.0, 0.0, 0.9 );
  231. material->SetAmbient( 0.2, 0.2, 0.2 );
  232. material->SetShininess( 0.1 );
  233. // create a copy of the entire tetrahedron shifted Z+2 and rotated 2/3PI
  234. IFSG_TRANSFORM* tx2 = new IFSG_TRANSFORM( tx0->GetRawPtr() );
  235. tx2->AddRefNode( *tx1 );
  236. tx2->SetTranslation( SGPOINT( 0, 0, 2 ) );
  237. tx2->SetRotation( SGVECTOR( 0, 0, 1 ), M_PI*2.0/3.0 );
  238. SGNODE* data = tx0->GetRawPtr();
  239. // delete the wrappers
  240. delete shape;
  241. delete face;
  242. delete coordIdx;
  243. delete material;
  244. delete cp;
  245. delete np;
  246. delete tx0;
  247. delete tx1;
  248. delete tx2;
  249. return (SCENEGRAPH*)data;
  250. }