教程:3D插件类

本节包含对 PLUGIN_3D 类的两个非常简单的插件的描述,并引导用户完成代码的设置和构建。

基本的 3D 插件

本教程将引导用户开发一个名为 “PLUGIN_3D_DEMO1” 的非常基本的 3D 插件。 本教程的目的只是为了演示一个非常基本的 3D 插件的构造,除了提供一些允许 KiCad 用户在浏览 3D 模型时过滤文件名的过滤字符串之外什么都不做。 这里演示的代码是任何 3D 插件的绝对最低要求,可以用作创建更高级插件的模板。

为了构建演示项目,我们需要以下内容:

  • CMake

  • KiCad 插件头

  • KiCad 场景图库 ‘kicad_3dsg’

要自动检测 KiCad 标头和库,我们将使用 CMake FindPackage 脚本; 如果相关的头文件安装到 ‘${KICAD_ROOT_DIR}/kicad’ 并且 KiCad Scene Graph 库安装在 ‘${KICAD_ROOT_DIR}/lib’ 中,则本教程中提供的脚本应该适用于 Linux 和 Windows。

要开始,让我们创建一个项目目录和 FindPackage 脚本:

  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. # 尝试从 sg_version.h 中提取版本信息
  11. # attempt to extract the version information from sg_version.h
  12. find_file( KICAD_SGVERSION sg_version.h
  13. PATHS ${KICAD_INCLUDE_DIR}
  14. PATH_SUFFIXES kicad/plugins/3dapi
  15. NO_DEFAULT_PATH )
  16. if( NOT ${KICAD_SGVERSION} STREQUAL "KICAD_SGVERSION-NOTFOUND" )
  17. # 提取 “#define KICADSG_VERSION*” 行
  18. # extract the "#define KICADSG_VERSION*" lines
  19. file( STRINGS ${KICAD_SGVERSION} _version REGEX "^#define.*KICADSG_VERSION.*" )
  20. foreach( SVAR ${_version} )
  21. string( REGEX MATCH KICADSG_VERSION_[M,A,J,O,R,I,N,P,T,C,H,E,V,I,S]* _VARNAME ${SVAR} )
  22. string( REGEX MATCH [0-9]+ _VALUE ${SVAR} )
  23. if( NOT ${_VARNAME} STREQUAL "" AND NOT ${_VALUE} STREQUAL "" )
  24. set( _${_VARNAME} ${_VALUE} )
  25. endif()
  26. endforeach()
  27. # 确保 NOT SG3D_VERSION* 的计算结果为 “0”
  28. # ensure that NOT SG3D_VERSION* will evaluate to '0'
  29. if( NOT _KICADSG_VERSION_MAJOR )
  30. set( _KICADSG_VERSION_MAJOR 0 )
  31. endif()
  32. if( NOT _KICADSG_VERSION_MINOR )
  33. set( _KICADSG_VERSION_MINOR 0 )
  34. endif()
  35. if( NOT _KICADSG_VERSION_PATCH )
  36. set( _KICADSG_VERSION_PATCH 0 )
  37. endif()
  38. if( NOT _KICADSG_VERSION_REVISION )
  39. set( _KICADSG_VERSION_REVISION 0 )
  40. endif()
  41. set( KICAD_VERSION ${_KICADSG_VERSION_MAJOR}.${_KICADSG_VERSION_MINOR}.${_KICADSG_VERSION_PATCH}.${_KICADSG_VERSION_REVISION} )
  42. unset( KICAD_SGVERSION CACHE )
  43. endif()
  44. endif()
  45. find_library( KICAD_LIBRARY
  46. NAMES kicad_3dsg
  47. PATHS
  48. ${KICAD_ROOT_DIR}/lib $ENV{KICAD_ROOT_DIR}/lib
  49. ${KICAD_ROOT_DIR}/bin $ENV{KICAD_ROOT_DIR}/bin
  50. DOC "Kicad scenegraph library path."
  51. )
  52. include( FindPackageHandleStandardArgs )
  53. FIND_PACKAGE_HANDLE_STANDARD_ARGS( KICAD
  54. REQUIRED_VARS
  55. KICAD_INCLUDE_DIR
  56. KICAD_LIBRARY
  57. KICAD_VERSION
  58. VERSION_VAR KICAD_VERSION )
  59. mark_as_advanced( KICAD_INCLUDE_DIR )
  60. set( KICAD_VERSION_MAJOR ${_KICADSG_VERSION_MAJOR} CACHE INTERNAL "" )
  61. set( KICAD_VERSION_MINOR ${_KICADSG_VERSION_MINOR} CACHE INTERNAL "" )
  62. set( KICAD_VERSION_PATCH ${_KICADSG_VERSION_PATCH} CACHE INTERNAL "" )
  63. set( KICAD_VERSION_TWEAK ${_KICADSG_VERSION_REVISION} CACHE INTERNAL "" )
  64. _EOF

必须安装 Kicad 及其插件标头; 如果将它们安装到用户目录或 Linux 上的 ‘/opt’ 下,或者您使用的是 Windows,则需要将 ‘KICAD_ROOT_DIR’ 环境变量设置为指向包含 KiCad ‘include’ 和 ‘lib’ 目录的目录。 对于 OS X,此处显示的 FindPackage 脚本可能需要进行一些调整。

要配置和构建教程代码,我们将使用 CMake 并创建一个 ‘CMakeLists.txt’ 脚本文件:

  1. cd ${DEMO_ROOT}
  2. cat > CMakeLists.txt << _EOF
  3. # 声明项目名称
  4. # declare the name of the project
  5. project( PLUGIN_DEMO )
  6. # 检查我们是否有具有所有必需功能的 CMake 版本
  7. # check that we have a version of CMake with all required features
  8. cmake_minimum_required( VERSION 2.8.12 FATAL_ERROR )
  9. # 通知 CMake 何处可以找到 FindKICAD 脚本
  10. # inform CMake of where to find the FindKICAD script
  11. set( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules )
  12. # 尝试发现已安装的 kicad 标头和库
  13. # attempt to discover the installed kicad headers and library
  14. # 并设置变量: (and set the variables:)
  15. # KICAD_INCLUDE_DIR
  16. # KICAD_LIBRARY
  17. find_package( KICAD 1.0 REQUIRED )
  18. # 将 kicad include 目录添加到编译器的搜索路径中
  19. # add the kicad include directory to the compiler's search path
  20. include_directories( ${KICAD_INCLUDE_DIR}/kicad )
  21. # 创建名为 s3D_plugin_demo1 的插件
  22. # create a plugin named s3d_plugin_demo1
  23. add_library( s3d_plugin_demo1 MODULE
  24. src/s3d_plugin_demo1.cpp
  25. )
  26. _EOF

第一个演示项目非常基础; 它由一个文件组成,除了编译器默认值之外没有外部链接依赖项。 我们首先创建一个源目录:

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

现在我们自己创建插件源:

s3d_plugin_demo1.cpp

  1. #include <iostream>
  2. // 3d_plugin.h 标头定义了 3D 插件所需的功能
  3. // the 3d_plugin.h header defines the functions required of 3D plugins
  4. #include "plugins/3d/3d_plugin.h"
  5. // 定义这个插件的版本信息,不要混淆
  6. // define the version information of this plugin; do not confuse this
  7. // 使用在 3d_plugin.h 中定义的插件类版本
  8. // with the Plugin Class version which is defined in 3d_plugin.h
  9. #define PLUGIN_3D_DEMO1_MAJOR 1
  10. #define PLUGIN_3D_DEMO1_MINOR 0
  11. #define PLUGIN_3D_DEMO1_PATCH 0
  12. #define PLUGIN_3D_DEMO1_REVNO 0
  13. // 实现为用户提供此插件名称的函数
  14. // implement the function which provides users with this plugin's name
  15. const char* GetKicadPluginName( void )
  16. {
  17. return "PLUGIN_3D_DEMO1";
  18. }
  19. // 实现为用户提供此插件版本的功能
  20. // implement the function which provides users with this plugin's version
  21. void GetPluginVersion( unsigned char* Major, unsigned char* Minor,
  22. unsigned char* Patch, unsigned char* Revision )
  23. {
  24. if( Major )
  25. *Major = PLUGIN_3D_DEMO1_MAJOR;
  26. if( Minor )
  27. *Minor = PLUGIN_3D_DEMO1_MINOR;
  28. if( Patch )
  29. *Patch = PLUGIN_3D_DEMO1_PATCH;
  30. if( Revision )
  31. *Revision = PLUGIN_3D_DEMO1_REVNO;
  32. return;
  33. }
  34. // 支持的扩展名数量;在 *NIX 系统上,扩展名为
  35. // number of extensions supported; on *NIX systems the extensions are
  36. // 提供两次-一次小写,一次大写
  37. // provided twice - once in lower case and once in upper case letters
  38. #ifdef _WIN32
  39. #define NEXTS 7
  40. #else
  41. #define NEXTS 14
  42. #endif
  43. // 支持的筛选器集数量
  44. // number of filter sets supported
  45. #define NFILS 5
  46. // 定义此扩展字符串和筛选器字符串
  47. // define the extension strings and filter strings which this
  48. // 插件将提供给用户
  49. // plugin will supply to the user
  50. static char ext0[] = "wrl";
  51. static char ext1[] = "x3d";
  52. static char ext2[] = "emn";
  53. static char ext3[] = "iges";
  54. static char ext4[] = "igs";
  55. static char ext5[] = "stp";
  56. static char ext6[] = "step";
  57. #ifdef _WIN32
  58. static char fil0[] = "VRML 1.0/2.0 (*.wrl)|*.wrl";
  59. static char fil1[] = "X3D (*.x3d)|*.x3d";
  60. static char fil2[] = "IDF 2.0/3.0 (*.emn)|*.emn";
  61. static char fil3[] = "IGESv5.3 (*.igs;*.iges)|*.igs;*.iges";
  62. static char fil4[] = "STEP (*.stp;*.step)|*.stp;*.step";
  63. #else
  64. static char ext7[] = "WRL";
  65. static char ext8[] = "X3D";
  66. static char ext9[] = "EMN";
  67. static char ext10[] = "IGES";
  68. static char ext11[] = "IGS";
  69. static char ext12[] = "STP";
  70. static char ext13[] = "STEP";
  71. static char fil0[] = "VRML 1.0/2.0 (*.wrl;*.WRL)|*.wrl;*.WRL";
  72. static char fil1[] = "X3D (*.x3d;*.X3D)|*.x3d;*.X3D";
  73. static char fil2[] = "IDF 2.0/3.0 (*.emn;*.EMN)|*.emn;*.EMN";
  74. static char fil3[] = "IGESv5.3 (*.igs;*.iges;*.IGS;*.IGES)|*.igs;*.iges;*.IGS;*.IGES";
  75. static char fil4[] = "STEP (*.stp;*.step;*.STP;*.STEP)|*.stp;*.step;*.STP;*.STEP";
  76. #endif
  77. // 实例化一个方便的数据结构来访问
  78. // instantiate a convenient data structure for accessing the
  79. // 扩展和筛选字符串列表
  80. // lists of extension and filter strings
  81. static struct FILE_DATA
  82. {
  83. char const* extensions[NEXTS];
  84. char const* filters[NFILS];
  85. FILE_DATA()
  86. {
  87. extensions[0] = ext0;
  88. extensions[1] = ext1;
  89. extensions[2] = ext2;
  90. extensions[3] = ext3;
  91. extensions[4] = ext4;
  92. extensions[5] = ext5;
  93. extensions[6] = ext6;
  94. filters[0] = fil0;
  95. filters[1] = fil1;
  96. filters[2] = fil2;
  97. filters[3] = fil3;
  98. filters[4] = fil4;
  99. #ifndef _WIN32
  100. extensions[7] = ext7;
  101. extensions[8] = ext8;
  102. extensions[9] = ext9;
  103. extensions[10] = ext10;
  104. extensions[11] = ext11;
  105. extensions[12] = ext12;
  106. extensions[13] = ext13;
  107. #endif
  108. return;
  109. }
  110. } file_data;
  111. // 返回该插件支持的扩展数量
  112. // return the number of extensions supported by this plugin
  113. int GetNExtensions( void )
  114. {
  115. return NEXTS;
  116. }
  117. // 返回索引的扩展字符串
  118. // return the indexed extension string
  119. char const* GetModelExtension( int aIndex )
  120. {
  121. if( aIndex < 0 || aIndex >= NEXTS )
  122. return NULL;
  123. return file_data.extensions[aIndex];
  124. }
  125. // 返回该插件提供的筛选字符串数量
  126. // return the number of filter strings provided by this plugin
  127. int GetNFilters( void )
  128. {
  129. return NFILS;
  130. }
  131. // 返回索引筛选字符串
  132. // return the indexed filter string
  133. char const* GetFileFilter( int aIndex )
  134. {
  135. if( aIndex < 0 || aIndex >= NFILS )
  136. return NULL;
  137. return file_data.filters[aIndex];
  138. }
  139. // 返回 false,因为此插件不提供可视化数据
  140. // return false since this plugin does not provide visualization data
  141. bool CanRender( void )
  142. {
  143. return false;
  144. }
  145. // 返回 null,因为此插件不提供可视化数据
  146. // return NULL since this plugin does not provide visualization data
  147. SCENEGRAPH* Load( char const* aFileName )
  148. {
  149. // 此伪插件不支持渲染任何模型
  150. // this dummy plugin does not support rendering of any models
  151. return NULL;
  152. }

此源文件满足实现 3D 插件的所有最低要求。 该插件不会为渲染模型生成任何数据,但它可以为 KiCad 提供支持的模型文件扩展名和文件扩展名过滤器列表,以增强 3D 模型文件选择对话框。 在 KiCad 中,扩展字符串用于选择可用于加载指定模型的插件; 例如,如果插件是 ‘wrl’,那么 KiCad 将调用声称支持扩展 ‘wrl’ 的每个插件,直到插件返回可视化数据。 每个插件提供的文件过滤器将传递到 3D 文件选择器对话框,以改善浏览 UI。

要构建插件:

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

该插件将被构建但未安装; 如果要加载插件,必须将插件文件复制到 KiCad 的插件目录。

高级 3D 插件

本教程将引导用户开发名为 “PLUGIN_3D_DEMO2” 的 3D 插件。 本教程的目的是演示 KiCad 预览器可以渲染的非常基本的场景图的构造。 该插件声称处理 ‘txt’ 类型的文件。 虽然文件必须存在,以便缓存管理器调用插件,但此插件不处理文件内容; 相反,插件只是创建一个包含一对四面体的场景图。 本教程假定第一个教程已完成,并且已创建 CMakeLists.txt 和 FindKICAD.cmake 脚本文件。

将新的源文件放在与上一个教程的源文件相同的目录中,我们将扩展上一个教程的 CMakeLists.txt 文件来构建本教程。 由于这个插件会为 KiCad 创建一个场景图,我们需要链接到 KiCad 的场景图库 ‘kicad_3dsg’。 KiCad 的场景图库提供了一组可用于构建场景图对象的类; 场景图对象是 3D 缓存管理器使用的中间数据可视化格式。 所有支持模型可视化的插件都必须通过此库将模型数据转换为场景图。

第一步是扩展 ‘CMakeLists.txt’ 来构建这个教程项目:

  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

现在我们切换到源目录并创建源文件:

  1. cd ${DEMO_SRC}

s3d_plugin_demo2.cpp

  1. #include <cmath>
  2. // 3D 插件类声明
  3. // 3D Plugin Class declarations
  4. #include "plugins/3d/3d_plugin.h"
  5. // KiCad 场景图形库接口
  6. // interface to KiCad Scene Graph Library
  7. #include "plugins/3dapi/ifsg_all.h"
  8. // 该插件的版本信息
  9. // version information for this plugin
  10. #define PLUGIN_3D_DEMO2_MAJOR 1
  11. #define PLUGIN_3D_DEMO2_MINOR 0
  12. #define PLUGIN_3D_DEMO2_PATCH 0
  13. #define PLUGIN_3D_DEMO2_REVNO 0
  14. // 提供此插件的名称
  15. // provide the name of this plugin
  16. const char* GetKicadPluginName( void )
  17. {
  18. return "PLUGIN_3D_DEMO2";
  19. }
  20. // 提供此插件的版本
  21. // provide the version of this plugin
  22. void GetPluginVersion( unsigned char* Major, unsigned char* Minor,
  23. unsigned char* Patch, unsigned char* Revision )
  24. {
  25. if( Major )
  26. *Major = PLUGIN_3D_DEMO2_MAJOR;
  27. if( Minor )
  28. *Minor = PLUGIN_3D_DEMO2_MINOR;
  29. if( Patch )
  30. *Patch = PLUGIN_3D_DEMO2_PATCH;
  31. if( Revision )
  32. *Revision = PLUGIN_3D_DEMO2_REVNO;
  33. return;
  34. }
  35. // 支持的扩展数量
  36. // number of extensions supported
  37. #ifdef _WIN32
  38. #define NEXTS 1
  39. #else
  40. #define NEXTS 2
  41. #endif
  42. // 支持的筛选器集数量
  43. // number of filter sets supported
  44. #define NFILS 1
  45. static char ext0[] = "txt";
  46. #ifdef _WIN32
  47. static char fil0[] = "demo (*.txt)|*.txt";
  48. #else
  49. static char ext1[] = "TXT";
  50. static char fil0[] = "demo (*.txt;*.TXT)|*.txt;*.TXT";
  51. #endif
  52. static struct FILE_DATA
  53. {
  54. char const* extensions[NEXTS];
  55. char const* filters[NFILS];
  56. FILE_DATA()
  57. {
  58. extensions[0] = ext0;
  59. filters[0] = fil0;
  60. #ifndef _WIN32
  61. extensions[1] = ext1;
  62. #endif
  63. return;
  64. }
  65. } file_data;
  66. int GetNExtensions( void )
  67. {
  68. return NEXTS;
  69. }
  70. char const* GetModelExtension( int aIndex )
  71. {
  72. if( aIndex < 0 || aIndex >= NEXTS )
  73. return NULL;
  74. return file_data.extensions[aIndex];
  75. }
  76. int GetNFilters( void )
  77. {
  78. return NFILS;
  79. }
  80. char const* GetFileFilter( int aIndex )
  81. {
  82. if( aIndex < 0 || aIndex >= NFILS )
  83. return NULL;
  84. return file_data.filters[aIndex];
  85. }
  86. // 返回 true,因为此插件可以提供可视化数据
  87. // return true since this plugin can provide visualization data
  88. bool CanRender( void )
  89. {
  90. return true;
  91. }
  92. // 创建可视化数据
  93. // create the visualization data
  94. SCENEGRAPH* Load( char const* aFileName )
  95. {
  96. // 对于本演示,我们创建一个四面体( TX1),包括
  97. // For this demonstration we create a tetrahedron (tx1) consisting
  98. // SCENEGRAPH (VRML 变换),它依次包含 4
  99. // of a SCENEGRAPH (VRML Transform) which in turn contains 4
  100. // SGSHAPE (VRML Shape) 对象表示
  101. // SGSHAPE (VRML Shape) objects representing each of the sides of
  102. // 四面体。每个形状都与一种颜色关联 (SGAPPEARANCE)
  103. // the tetrahedron. Each Shape is associated with a color (SGAPPEARANCE)
  104. // 和一个 SGFACESET (VRML Geometry->indexedFaceSet)。每个 SGFACESET 是
  105. // and a SGFACESET (VRML Geometry->indexedFaceSet). Each SGFACESET is
  106. // 与顶点列表 (SGCOORDS) 关联,每顶点法线
  107. // associated with a vertex list (SGCOORDS), a per-vertex normals
  108. // list(SGNORMALS) 和坐标索引 (SGCOORDINDEX)。一个形状
  109. // list (SGNORMALS), and a coordinate index (SGCOORDINDEX). One shape
  110. // 用于表示每个面,以便我们可以使用逐顶点逐面
  111. // is used to represent each face so that we may use per-vertex-per-face
  112. // 法线。
  113. // normals.
  114. //
  115. // 四面体又是顶级 SCENEGRAPH(Tx0) 的子级
  116. // The tetrahedron in turn is a child of a top level SCENEGRAPH (tx0)
  117. // 它有第二个 SCENEGRAPH子(TX2),这是一个转换
  118. // which has a second SCENEGRAPH child (tx2) which is a transformation
  119. // 四面体 TX1 (旋转+平移)。这表明
  120. // of the tetrahedron tx1 (rotation + translation). This demonstrates
  121. // 场景图层次结构中组件的重用。
  122. // the reuse of components within the scene graph hierarchy.
  123. // 定义四面体的顶点
  124. // define the vertices of the tetrahedron
  125. // 面 1:0,3,1
  126. // face 1: 0, 3, 1
  127. // 面 2: 0, 2, 3
  128. // face 2: 0, 2, 3
  129. // 面 3: 1, 3, 2
  130. // face 3: 1, 3, 2
  131. // 面 4: 0, 1, 2
  132. // face 4: 0, 1, 2
  133. double SQ2 = sqrt( 0.5 );
  134. SGPOINT vert[4];
  135. vert[0] = SGPOINT( 1.0, 0.0, -SQ2 );
  136. vert[1] = SGPOINT( -1.0, 0.0, -SQ2 );
  137. vert[2] = SGPOINT( 0.0, 1.0, SQ2 );
  138. vert[3] = SGPOINT( 0.0, -1.0, SQ2 );
  139. // 创建顶级转换;这将保存所有其他
  140. // create the top level transform; this will hold all other
  141. // Scenegraph 对象;转换可能包含其他转换和
  142. // scenegraph objects; a transform may hold other transforms and
  143. // 形状
  144. // shapes
  145. IFSG_TRANSFORM* tx0 = new IFSG_TRANSFORM( true );
  146. // 创建将容纳形状的转换
  147. // create the transform which will house the shapes
  148. IFSG_TRANSFORM* tx1 = new IFSG_TRANSFORM( tx0->GetRawPtr() );
  149. // 添加一个形状,我们将使用该形状定义四面体的一个面;
  150. // add a shape which we will use to define one face of the tetrahedron;
  151. // 形状保存 facesets 和外观
  152. // shapes hold facesets and appearances
  153. IFSG_SHAPE* shape = new IFSG_SHAPE( *tx1 );
  154. // 添加 faceset;这些包含坐标列表,坐标索引,
  155. // add a faceset; these contain coordinate lists, coordinate indices,
  156. // 顶点列表、顶点索引,还可能包含颜色列表和
  157. // vertex lists, vertex indices, and may also contain color lists and
  158. // 它们的索引。
  159. // their indices.
  160. IFSG_FACESET* face = new IFSG_FACESET( *shape );
  161. IFSG_COORDS* cp = new IFSG_COORDS( *face );
  162. cp->AddCoord( vert[0] );
  163. cp->AddCoord( vert[3] );
  164. cp->AddCoord( vert[1] );
  165. // 坐标索引-注意:强制三角形;
  166. // coordinate indices - note: enforce triangles;
  167. // 在不一定可能的实际插件中
  168. // in real plugins where it is not necessarily possible
  169. // 若要确定三角形从哪一侧可见,请执行以下操作:
  170. // to determine which side a triangle is visible from,
  171. // 2 必须为每个三角形指定点序
  172. // 2 point orders must be specified for each triangle
  173. IFSG_COORDINDEX* coordIdx = new IFSG_COORDINDEX( *face );
  174. coordIdx->AddIndex( 0 );
  175. coordIdx->AddIndex( 1 );
  176. coordIdx->AddIndex( 2 );
  177. // 创建外观;外观归形状所有
  178. // create an appearance; appearances are owned by shapes
  179. // 文件名将更改为
  180. // magenta
  181. IFSG_APPEARANCE* material = new IFSG_APPEARANCE( *shape);
  182. material->SetSpecular( 0.1, 0.0, 0.1 );
  183. material->SetDiffuse( 0.8, 0.0, 0.8 );
  184. material->SetAmbient( 0.2, 0.2, 0.2 );
  185. material->SetShininess( 0.2 );
  186. // 法线
  187. // normals
  188. IFSG_NORMALS* np = new IFSG_NORMALS( *face );
  189. SGVECTOR nval = S3D::CalcTriNorm( vert[0], vert[3], vert[1] );
  190. np->AddNormal( nval );
  191. np->AddNormal( nval );
  192. np->AddNormal( nval );
  193. //
  194. // 形状 2
  195. // Shape2
  196. // 注意:我们重用 IFSG* 包装器来创建和操作新的
  197. // Note: we reuse the IFSG* wrappers to create and manipulate new
  198. // 数据结构。
  199. // data structures.
  200. //
  201. shape->NewNode( *tx1 );
  202. face->NewNode( *shape );
  203. coordIdx->NewNode( *face );
  204. cp->NewNode( *face );
  205. np->NewNode( *face );
  206. // 顶点
  207. // vertices
  208. cp->AddCoord( vert[0] );
  209. cp->AddCoord( vert[2] );
  210. cp->AddCoord( vert[3] );
  211. // 索引
  212. // indices
  213. coordIdx->AddIndex( 0 );
  214. coordIdx->AddIndex( 1 );
  215. coordIdx->AddIndex( 2 );
  216. // 法线
  217. // normals
  218. nval = S3D::CalcTriNorm( vert[0], vert[2], vert[3] );
  219. np->AddNormal( nval );
  220. np->AddNormal( nval );
  221. np->AddNormal( nval );
  222. // 颜色 (红色)
  223. // color (red)
  224. material->NewNode( *shape );
  225. material->SetSpecular( 0.2, 0.0, 0.0 );
  226. material->SetDiffuse( 0.9, 0.0, 0.0 );
  227. material->SetAmbient( 0.2, 0.2, 0.2 );
  228. material->SetShininess( 0.1 );
  229. //
  230. // 形状 3
  231. // Shape3
  232. //
  233. shape->NewNode( *tx1 );
  234. face->NewNode( *shape );
  235. coordIdx->NewNode( *face );
  236. cp->NewNode( *face );
  237. np->NewNode( *face );
  238. // 顶点
  239. // vertices
  240. cp->AddCoord( vert[1] );
  241. cp->AddCoord( vert[3] );
  242. cp->AddCoord( vert[2] );
  243. // 索引
  244. // indices
  245. coordIdx->AddIndex( 0 );
  246. coordIdx->AddIndex( 1 );
  247. coordIdx->AddIndex( 2 );
  248. // 法线
  249. // normals
  250. nval = S3D::CalcTriNorm( vert[1], vert[3], vert[2] );
  251. np->AddNormal( nval );
  252. np->AddNormal( nval );
  253. np->AddNormal( nval );
  254. // 颜色 (绿色)
  255. // color (green)
  256. material->NewNode( *shape );
  257. material->SetSpecular( 0.0, 0.1, 0.0 );
  258. material->SetDiffuse( 0.0, 0.9, 0.0 );
  259. material->SetAmbient( 0.2, 0.2, 0.2 );
  260. material->SetShininess( 0.1 );
  261. //
  262. // 形状 4
  263. // Shape4
  264. //
  265. shape->NewNode( *tx1 );
  266. face->NewNode( *shape );
  267. coordIdx->NewNode( *face );
  268. cp->NewNode( *face );
  269. np->NewNode( *face );
  270. // 顶点
  271. // vertices
  272. cp->AddCoord( vert[0] );
  273. cp->AddCoord( vert[1] );
  274. cp->AddCoord( vert[2] );
  275. // 索引
  276. // indices
  277. coordIdx->AddIndex( 0 );
  278. coordIdx->AddIndex( 1 );
  279. coordIdx->AddIndex( 2 );
  280. // 法线
  281. // normals
  282. nval = S3D::CalcTriNorm( vert[0], vert[1], vert[2] );
  283. np->AddNormal( nval );
  284. np->AddNormal( nval );
  285. np->AddNormal( nval );
  286. // 颜色 (蓝色)
  287. // color (blue)
  288. material->NewNode( *shape );
  289. material->SetSpecular( 0.0, 0.0, 0.1 );
  290. material->SetDiffuse( 0.0, 0.0, 0.9 );
  291. material->SetAmbient( 0.2, 0.2, 0.2 );
  292. material->SetShininess( 0.1 );
  293. // 创建整个四面体移位 Z+2 并旋转 2/3PI 的副本
  294. // create a copy of the entire tetrahedron shifted Z+2 and rotated 2/3PI
  295. IFSG_TRANSFORM* tx2 = new IFSG_TRANSFORM( tx0->GetRawPtr() );
  296. tx2->AddRefNode( *tx1 );
  297. tx2->SetTranslation( SGPOINT( 0, 0, 2 ) );
  298. tx2->SetRotation( SGVECTOR( 0, 0, 1 ), M_PI*2.0/3.0 );
  299. SGNODE* data = tx0->GetRawPtr();
  300. // 删除包装器
  301. // delete the wrappers
  302. delete shape;
  303. delete face;
  304. delete coordIdx;
  305. delete material;
  306. delete cp;
  307. delete np;
  308. delete tx0;
  309. delete tx1;
  310. delete tx2;
  311. return (SCENEGRAPH*)data;
  312. }