KiCad PCB EDA Suite
vrml.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
5  * Copyright (C) 2020-2021 KiCad Developers, see CHANGELOG.TXT for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 /*
26  * Description:
27  * This plugin implements the legacy KiCad VRML1/VRML2 and X3D parsers
28  * The plugin will invoke a VRML1 or VRML2 parser depending on the
29  * identifying information in the file header:
30  *
31  * #VRML V1.0 ASCII
32  * #VRML V2.0 utf8
33  */
34 
35 #include "plugins/3d/3d_plugin.h"
36 #include "plugins/3dapi/ifsg_all.h"
37 #include "richio.h"
38 #include "vrml1_base.h"
39 #include "vrml2_base.h"
40 #include "wrlproc.h"
41 #include "x3d.h"
42 #include <clocale>
43 #include <wx/filename.h>
44 #include <wx/stdpaths.h>
45 #include <wx/string.h>
46 #include <wx/wfstream.h>
47 #include <wx/log.h>
48 
49 #include <decompress.hpp>
50 
51 
52 #define PLUGIN_VRML_MAJOR 1
53 #define PLUGIN_VRML_MINOR 3
54 #define PLUGIN_VRML_PATCH 2
55 #define PLUGIN_VRML_REVNO 2
56 
57 
63 const wxChar* const traceVrmlPlugin = wxT( "KICAD_VRML_PLUGIN" );
64 
65 
66 const char* GetKicadPluginName( void )
67 {
68  return "PLUGIN_3D_VRML";
69 }
70 
71 
72 void GetPluginVersion( unsigned char* Major, unsigned char* Minor, unsigned char* Patch,
73  unsigned char* Revision )
74 {
75  if( Major )
76  *Major = PLUGIN_VRML_MAJOR;
77 
78  if( Minor )
79  *Minor = PLUGIN_VRML_MINOR;
80 
81  if( Patch )
82  *Patch = PLUGIN_VRML_PATCH;
83 
84  if( Revision )
85  *Revision = PLUGIN_VRML_REVNO;
86 
87  return;
88 }
89 
90 
91 static struct FILE_DATA
92 {
93  std::vector<std::string> extensions;
94  std::vector<std::string> filters;
95 
97  {
98 
99 #ifdef _WIN32
100  extensions = { "wrl", "wrz", "x3d" };
101  filters = { "VRML 1.0/2.0 (*.wrl;*.wrz)|*.wrl;*.wrz",
102  "X3D (*.x3d)|*.x3d" };
103 #else
104  extensions = { "wrl", "WRL", "wrz", "WRZ", "x3d", "X3D" };
105  filters = { "VRML 1.0/2.0 (*.wrl;*.WRL;*.wrz;*.WRZ)|*.wrl;*.WRL;*.wrz;*.WRZ",
106  "X3D (*.x3d;*.X3D)|*.x3d;*.X3D" };
107 #endif
108  }
109 
110 } file_data;
111 
112 
113 int GetNExtensions( void )
114 {
115  return file_data.extensions.size();
116 }
117 
118 
119 char const* GetModelExtension( int aIndex )
120 {
121  if( aIndex < 0 || aIndex >= int( file_data.extensions.size() ) )
122  return nullptr;
123 
124  return file_data.extensions[aIndex].c_str();
125 }
126 
127 
128 int GetNFilters( void )
129 {
130  return file_data.filters.size();
131 }
132 
133 
134 char const* GetFileFilter( int aIndex )
135 {
136  if( aIndex < 0 || aIndex >= int( file_data.filters.size() ) )
137  return nullptr;
138 
139  return file_data.filters[aIndex].c_str();
140 }
141 
142 
143 bool CanRender( void )
144 {
145  // this plugin supports rendering of IDF component outlines
146  return true;
147 }
148 
149 
150 class LOCALESWITCH
151 {
152 public:
154  {
155  m_locale = setlocale( LC_NUMERIC, nullptr );
156  setlocale( LC_NUMERIC, "C" );
157  }
158 
160  {
161  setlocale( LC_NUMERIC, m_locale.c_str() );
162  }
163 
164 private:
165  // Store the user locale name, to restore this locale later, in dtor
166  std::string m_locale;
167 };
168 
169 
170 SCENEGRAPH* LoadVRML( const wxString& aFileName, bool useInline )
171 {
172  FILE_LINE_READER* modelFile = nullptr;
173  SCENEGRAPH* scene = nullptr;
174  wxString filename = aFileName;
175  wxFileName tmpfilename;
176 
177  if( aFileName.Upper().EndsWith( "WRZ" ) )
178  {
179  wxFFileInputStream ifile( aFileName );
180  tmpfilename = wxFileName( aFileName );
181 
182  tmpfilename.SetPath( wxStandardPaths::Get().GetTempDir() );
183  tmpfilename.SetExt( "WRL" );
184 
185  wxFileOffset size = ifile.GetLength();
186 
187  if( size == wxInvalidOffset )
188  return nullptr;
189 
190  {
191  wxFFileOutputStream ofile( tmpfilename.GetFullPath() );
192 
193  if( !ofile.IsOk() )
194  return nullptr;
195 
196  char *buffer = new char[size];
197 
198  ifile.Read( buffer, size);
199  std::string expanded;
200 
201  try
202  {
203  expanded = gzip::decompress( buffer, size );
204  }
205  catch(...)
206  {
207  delete[] buffer;
208  return nullptr;
209  }
210 
211  delete[] buffer;
212 
213  ofile.Write( expanded.data(), expanded.size() );
214  ofile.Close();
215  }
216 
217  filename = tmpfilename.GetFullPath();
218  }
219 
220  try
221  {
222  // set the max char limit to 8MB; if a VRML file contains
223  // longer lines then perhaps it shouldn't be used
224  modelFile = new FILE_LINE_READER( filename, 0, 8388608 );
225  }
226  catch( IO_ERROR & )
227  {
228  wxLogError( wxS( " * " ) + _( "[INFO] load failed: input line too long\n" ) );
229  return nullptr;
230  }
231 
232 
233  // VRML file processor
234  WRLPROC proc( modelFile );
235 
236  // Cleanup our temporary file
237  if( tmpfilename.IsOk() )
238  wxRemoveFile( tmpfilename.GetFullPath() );
239 
240  if( proc.GetVRMLType() == WRLVERSION::VRML_V1 )
241  {
242  wxLogTrace( traceVrmlPlugin, " * [INFO] Processing VRML 1.0 file" );
243 
244  WRL1BASE* bp = new WRL1BASE;
245 
246  if( !bp->Read( proc ) )
247  {
248  wxLogTrace( traceVrmlPlugin, " * [INFO] load failed" );
249  }
250  else
251  {
252  wxLogTrace( traceVrmlPlugin, " * [INFO] load completed" );
253 
254  scene = (SCENEGRAPH*)bp->TranslateToSG( nullptr, nullptr );
255  }
256 
257  delete bp;
258  }
259  else
260  {
261  wxLogTrace( traceVrmlPlugin, " * [INFO] Processing VRML 2.0 file" );
262 
263  WRL2BASE* bp = new WRL2BASE;
264 
265  // allow Inline{} files to be included
266  bp->SetEnableInline( true );
267 
268  if( !bp->Read( proc ) )
269  {
270  wxLogTrace( traceVrmlPlugin, " * [INFO] load failed" );
271  }
272  else
273  {
274  wxLogTrace( traceVrmlPlugin, " * [INFO] load completed" );
275 
276  // for now we recalculate all normals per-vertex per-face
277  scene = (SCENEGRAPH*)bp->TranslateToSG( nullptr );
278  }
279 
280  delete bp;
281  }
282 
283  if( nullptr != modelFile )
284  delete modelFile;
285 
286  // DEBUG: WRITE OUT VRML2 FILE TO CONFIRM STRUCTURE
287 #if ( defined( DEBUG_VRML1 ) && DEBUG_VRML1 > 3 ) \
288  || ( defined( DEBUG_VRML2 ) && DEBUG_VRML2 > 3 )
289  if( scene )
290  {
291  wxFileName fn( wxString::FromUTF8Unchecked( aFileName ) );
292  wxString output;
293 
294  if( proc.GetVRMLType() == VRML_V1 )
295  output = wxT( "_vrml1-" );
296  else
297  output = wxT( "_vrml2-" );
298 
299  output.append( fn.GetName() );
300  output.append( wxT(".wrl") );
301  S3D::WriteVRML( output.ToUTF8(), true, (SGNODE*)(scene), true, true );
302  }
303 #endif
304 
305  return scene;
306 }
307 
308 
309 SCENEGRAPH* LoadX3D( const wxString& aFileName )
310 {
311  SCENEGRAPH* scene = nullptr;
312  X3DPARSER model;
313  scene = model.Load( aFileName );
314 
315  return scene;
316 }
317 
318 
319 SCENEGRAPH* Load( char const* aFileName )
320 {
321  if( nullptr == aFileName )
322  return nullptr;
323 
324  wxString fname = wxString::FromUTF8Unchecked( aFileName );
325 
326  if( !wxFileName::FileExists( fname ) )
327  return nullptr;
328 
329  LOCALESWITCH switcher;
330 
331  SCENEGRAPH* scene = nullptr;
332  wxString ext = wxFileName( fname ).GetExt();
333 
334  if( ext == "x3d" || ext == "X3D" )
335  scene = LoadX3D( fname );
336  else
337  scene = LoadVRML( fname, true );
338 
339  return scene;
340 }
int GetNExtensions(void)
Function GetNExtensions.
Definition: vrml.cpp:113
Represent the top node of a VRML1 model.
Definition: vrml1_base.h:45
defines the basic input class for VRML
SCENEGRAPH * Load(char const *aFileName)
reads a model file and creates a generic display structure
Definition: vrml.cpp:319
int GetNFilters(void)
Function GetNFilters.
Definition: vrml.cpp:128
#define PLUGIN_VRML_MAJOR
Definition: vrml.cpp:52
WRLVERSION GetVRMLType(void)
Definition: wrlproc.cpp:230
bool Read(WRLPROC &proc)
Definition: vrml2_base.cpp:163
bool Read(WRLPROC &proc)
Definition: vrml1_base.cpp:77
const char * GetKicadPluginName(void)
Function GetKicadPluginName returns the name of the plugin instance; for example IDFv3.
Definition: vrml.cpp:66
The base class of all Scene Graph nodes.
Definition: sg_node.h:74
collects header files for all SG* wrappers and the API
FILE_DATA()
Definition: vrml.cpp:96
describes the runtime-loadable interface to support loading and parsing of 3D models.
~LOCALESWITCH()
Definition: vrml.cpp:159
#define PLUGIN_VRML_PATCH
Definition: vrml.cpp:54
SCENEGRAPH * LoadX3D(const wxString &aFileName)
Definition: vrml.cpp:309
SGLIB_API bool WriteVRML(const char *filename, bool overwrite, SGNODE *aTopNode, bool reuse, bool renameNodes)
Function WriteVRML writes out the given node and its subnodes to a VRML2 file.
Definition: ifsg_api.cpp:76
Definition: x3d.h:36
static struct FILE_DATA file_data
void SetEnableInline(bool enable)
Definition: vrml2_base.cpp:85
char const * extensions[NEXTS]
A LINE_READER that reads from an open file.
Definition: richio.h:172
char const * GetModelExtension(int aIndex)
Function GetModelExtension.
Definition: vrml.cpp:119
SCENEGRAPH * LoadVRML(const wxString &aFileName, bool useInline)
Definition: vrml.cpp:170
SGNODE * TranslateToSG(SGNODE *aParent, WRL1STATUS *sp) override
Produce a representation of the data using the intermediate scenegraph structures of the kicad_3dsg l...
Definition: vrml1_base.cpp:529
#define PLUGIN_VRML_MINOR
Definition: vrml.cpp:53
#define _(s)
LOCALESWITCH()
Definition: vrml.cpp:153
The top node of a VRML2 model.
Definition: vrml2_base.h:59
const wxChar *const traceVrmlPlugin
Flag to enable VRML plugin trace output.
Definition: vrml.cpp:63
std::string m_locale
Definition: vrml.cpp:166
#define PLUGIN_VRML_REVNO
Definition: vrml.cpp:55
SCENEGRAPH * Load(const wxString &aFileName)
Definition: x3d.cpp:46
char const * filters[NFILS]
char const * GetFileFilter(int aIndex)
Function GetFileFilter.
Definition: vrml.cpp:134
void GetPluginVersion(unsigned char *Major, unsigned char *Minor, unsigned char *Patch, unsigned char *Revision)
Function GetPluginVersion retrieves the version of the instantiated plugin for informational purposes...
Definition: vrml.cpp:72
Define the basic data set required to represent a 3D model.
Definition: scenegraph.h:44
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
bool CanRender(void)
Function CanRender.
Definition: vrml.cpp:143
SGNODE * TranslateToSG(SGNODE *aParent) override
Produce a representation of the data using the intermediate scenegraph structures of the kicad_3dsg l...
Definition: vrml2_base.cpp:911