KiCad PCB EDA Suite
vrml1_material.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) 2016 Cirilo Bernardo <[email protected]>
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.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 #include <iostream>
27 #include <sstream>
28 #include <wx/log.h>
29 
30 #include "vrml1_base.h"
31 #include "vrml1_material.h"
32 #include "plugins/3dapi/ifsg_all.h"
33 
34 
35 WRL1MATERIAL::WRL1MATERIAL( NAMEREGISTER* aDictionary ) : WRL1NODE( aDictionary )
36 {
37  colors[0] = nullptr;
38  colors[1] = nullptr;
40 }
41 
42 
44  WRL1NODE( aDictionary )
45 {
46  colors[0] = nullptr;
47  colors[1] = nullptr;
49  m_Parent = aParent;
50 
51  if( nullptr != m_Parent )
52  m_Parent->AddChildNode( this );
53 }
54 
55 
57 {
58  wxLogTrace( traceVrmlPlugin, " * [INFO] Destroying Material node." );
59 
60  // destroy any orphaned color nodes
61  for( int i = 0; i < 2; ++i )
62  {
63  if( nullptr != colors[i] )
64  {
65  if( nullptr == S3D::GetSGNodeParent( colors[i] ) )
67 
68  wxLogTrace( traceVrmlPlugin, wxT( " * [INFO] destroyed SGCOLOR #%d" ), i );
69  }
70  }
71 }
72 
73 
75 {
76  // this node may not own or reference any other node
77  wxCHECK_MSG( false, false, wxT( "AddRefNode is not applicable." ) );
78 }
79 
80 
82 {
83  // this node may not own or reference any other node
84  wxCHECK_MSG( false, false, wxT( "AddChildNode is not applicable." ) );
85 }
86 
87 
88 bool WRL1MATERIAL::Read( WRLPROC& proc, WRL1BASE* aTopNode )
89 {
90  wxCHECK_MSG( aTopNode, false, wxT( "aTopNode is nullptr." ) );
91 
92  char tok = proc.Peek();
93 
94  if( proc.eof() )
95  {
96  wxLogTrace( traceVrmlPlugin, wxT( "%s:%s:%d\n"
97  " * [INFO] bad file format; unexpected eof %s." ),
98  __FILE__, __FUNCTION__, __LINE__, proc.GetFilePosition() );
99 
100  return false;
101  }
102 
103  if( '{' != tok )
104  {
105  wxLogTrace( traceVrmlPlugin,
106  wxT( "%s:%s:%d\n"
107  " * [INFO] bad file format; expecting '{' but got '%s' %s" ),
108  __FILE__, __FUNCTION__, __LINE__, tok, proc.GetFilePosition() );
109 
110  return false;
111  }
112 
113  proc.Pop();
114  std::string glob;
115 
116  while( true )
117  {
118  if( proc.Peek() == '}' )
119  {
120  proc.Pop();
121  break;
122  }
123 
124  if( !proc.ReadName( glob ) )
125  {
126  wxLogTrace( traceVrmlPlugin, wxT( "%s:%s:%d\n"
127  "%s" ),
128  __FILE__, __FUNCTION__, __LINE__, proc.GetError() );
129 
130  return false;
131  }
132 
133  // expecting one of:
134  // ambientColor
135  // diffuseColor
136  // emissiveColor
137  // shininess
138  // specularColor
139  // transparency
140 
141  if( !glob.compare( "specularColor" ) )
142  {
143  if( !proc.ReadMFVec3f( specularColor ) )
144  {
145  wxLogTrace( traceVrmlPlugin,
146  wxT( "%s:%s:%d\n"
147  " * [INFO] invalid specularColor %s\n"
148  " * [INFO] file: '%s'\n"
149  "%s" ),
150  __FILE__, __FUNCTION__, __LINE__, proc.GetFilePosition(),
151  proc.GetFileName(), proc.GetError() );
152 
153  return false;
154  }
155  }
156  else if( !glob.compare( "diffuseColor" ) )
157  {
158  if( !proc.ReadMFVec3f( diffuseColor ) )
159  {
160  wxLogTrace( traceVrmlPlugin, wxT( "%s:%s:%d\n"
161  " * [INFO] invalid diffuseColor %s\n"
162  " * [INFO] file: '%s'\n"
163  " * [INFO] error: '%s'." ),
164  __FILE__, __FUNCTION__, __LINE__, proc.GetFilePosition(),
165  proc.GetFileName(), proc.GetError() );
166 
167  return false;
168  }
169  }
170  else if( !glob.compare( "emissiveColor" ) )
171  {
172  if( !proc.ReadMFVec3f( emissiveColor ) )
173  {
174  wxLogTrace( traceVrmlPlugin, wxT( "%s:%s:%d\n"
175  " * [INFO] invalid emissiveColor %s\n"
176  " * [INFO] file: '%s'\n"
177  "%s" ),
178  __FILE__, __FUNCTION__, __LINE__, proc.GetFilePosition(),
179  proc.GetFileName(), proc.GetError() );
180 
181  return false;
182  }
183  }
184  else if( !glob.compare( "shininess" ) )
185  {
186  if( !proc.ReadMFFloat( shininess ) )
187  {
188  wxLogTrace( traceVrmlPlugin,
189  wxT( "%s:%s:%d\n"
190  " * [INFO] invalid shininess %s\n"
191  " * [INFO] file: '%s'\n"
192  " * [INFO] error: '%s'." ),
193  __FILE__, __FUNCTION__, __LINE__, proc.GetFileName(),
194  proc.GetFileName(), proc.GetError() );
195 
196  return false;
197  }
198  }
199  else if( !glob.compare( "transparency" ) )
200  {
201  if( !proc.ReadMFFloat( transparency ) )
202  {
203  wxLogTrace( traceVrmlPlugin,
204  wxT( "%s:%s:%d\n"
205  " * [INFO] invalid transparency %s\n"
206  " * [INFO] file: '%s'\n"
207  " * [INFO] error: '%s'." ),
208  __FILE__, __FUNCTION__, __LINE__, proc.GetFilePosition(),
209  proc.GetFileName(), proc.GetError() );
210 
211  return false;
212  }
213  }
214  else if( !glob.compare( "ambientColor" ) )
215  {
216  if( !proc.ReadMFVec3f( ambientColor ) )
217  {
218  wxLogTrace( traceVrmlPlugin,
219  wxT( "%s:%s:%d\n"
220  " * [INFO] invalid ambientColor %s\n"
221  " * [INFO] file: '%s'\n"
222  " * [INFO] error: '%s'." ),
223  __FILE__, __FUNCTION__, __LINE__, proc.GetFilePosition(),
224  proc.GetFileName(), proc.GetError() );
225 
226  return false;
227  }
228  }
229  else
230  {
231  wxLogTrace( traceVrmlPlugin, wxT( "%s:%s:%d\n"
232  "* [INFO] bad Material %s.\n"
233  "* [INFO] file: '%s'." ),
234  __FILE__, __FUNCTION__, __LINE__, proc.GetFilePosition(),
235  proc.GetFileName() );
236 
237  return false;
238  }
239  } // while( true ) -- reading contents of Material{}
240 
241  return true;
242 }
243 
244 
246 {
247  wxCHECK_MSG( sp, nullptr, wxT( "Bad model: no base data given." ) );
248 
249  sp->mat = this;
250 
251  return nullptr;
252 }
253 
254 
256 {
257  ++aIndex;
258 
259  // invalid indices result in the default colors
260  if( aIndex != 0 && aIndex != 1 )
261  aIndex = 0;
262 
263  if( nullptr != colors[ aIndex ] )
264  return colors[ aIndex ];
265 
266  IFSG_APPEARANCE app( true );
267 
268  float red, green, blue, val;
269 
270  if( aIndex == 0 || transparency.empty() )
271  val = 0.0;
272  else
273  val = transparency[0];
274 
275  checkRange( val );
276  app.SetTransparency( val );
277 
278  if( aIndex == 0 || shininess.empty() )
279  val = 0.2f;
280  else
281  val = shininess[0];
282 
283  checkRange( val );
284  app.SetShininess( val );
285 
286  if( aIndex ==0 || ambientColor.empty() )
287  {
288  red = 0.2f;
289  green = 0.2f;
290  blue = 0.2f;
291  }
292  else
293  {
294  red = ambientColor[0].x;
295  green = ambientColor[0].y;
296  blue = ambientColor[0].z;
297  }
298 
299  checkRange( red );
300  checkRange( green );
301  checkRange( blue );
302  app.SetAmbient( red, green, blue );
303 
304  if( aIndex == 0 || diffuseColor.empty() )
305  {
306  red = 0.8f;
307  green = 0.8f;
308  blue = 0.8f;
309  }
310  else
311  {
312  red = diffuseColor[0].x;
313  green = diffuseColor[0].y;
314  blue = diffuseColor[0].z;
315  }
316 
317  checkRange( red );
318  checkRange( green );
319  checkRange( blue );
320  app.SetDiffuse( red, green, blue );
321 
322  if( aIndex > (int)emissiveColor.size() )
323  {
324  red = 0.0;
325  green = 0.0;
326  blue = 0.0;
327  }
328  else
329  {
330  red = emissiveColor[0].x;
331  green = emissiveColor[0].y;
332  blue = emissiveColor[0].z;
333  }
334 
335  checkRange( red );
336  checkRange( green );
337  checkRange( blue );
338  app.SetEmissive( red, green, blue );
339 
340  if( aIndex > (int)specularColor.size() )
341  {
342  red = 0.0;
343  green = 0.0;
344  blue = 0.0;
345  }
346  else
347  {
348  red = specularColor[0].x;
349  green = specularColor[0].y;
350  blue = specularColor[0].z;
351  }
352 
353  checkRange( red );
354  checkRange( green );
355  checkRange( blue );
356  app.SetSpecular( red, green, blue );
357 
358  colors[aIndex] = app.GetRawPtr();
359 
360  return colors[aIndex];
361 }
362 
363 
364 void WRL1MATERIAL::GetColor( SGCOLOR* aColor, int aIndex )
365 {
366  if( nullptr == aColor )
367  return;
368 
369  // Calculate the color based on the given index using the formula:
370  // color = ( emission + ambient + diffuse + shininess * specular ) / N
371  // where N = number of non-zero components or 1 (if all zero)
372  // If the index exceeds the number of items in a list, use the LAST
373  // item rather than the default; this behavior caters to some bad
374  // models.
375 
376  WRLVEC3F rgb;
377  float dRed, dBlue, dGreen;
378  float eRed, eBlue, eGreen;
379  float aRed, aBlue, aGreen;
380  float sRed, sBlue, sGreen;
381  float shiny;
382 
383  if( aIndex < 0 || ( aIndex >= (int)diffuseColor.size() ) )
384  {
385  if( !diffuseColor.empty() )
386  {
387  rgb = diffuseColor.back();
388  dRed = rgb.x;
389  dGreen = rgb.y;
390  dBlue = rgb.z;
391  }
392  else
393  {
394  dRed = 0.8f;
395  dGreen = 0.8f;
396  dBlue = 0.8f;
397  }
398  }
399  else
400  {
401  rgb = diffuseColor[aIndex];
402  dRed = rgb.x;
403  dGreen = rgb.y;
404  dBlue = rgb.z;
405  }
406 
407  if( aIndex < 0 || ( aIndex >= (int)emissiveColor.size() ) )
408  {
409  if( !emissiveColor.empty() )
410  {
411  rgb = emissiveColor.back();
412  eRed = rgb.x;
413  eGreen = rgb.y;
414  eBlue = rgb.z;
415  }
416  else
417  {
418  eRed = 0.0f;
419  eGreen = 0.0f;
420  eBlue = 0.0f;
421  }
422  }
423  else
424  {
425  rgb = emissiveColor[aIndex];
426  eRed = rgb.x;
427  eGreen = rgb.y;
428  eBlue = rgb.z;
429  }
430 
431  if( aIndex < 0 || ( aIndex >= (int)ambientColor.size() ) )
432  {
433  if( !ambientColor.empty() )
434  {
435  rgb = ambientColor.back();
436  aRed = rgb.x;
437  aGreen = rgb.y;
438  aBlue = rgb.z;
439  }
440  else
441  {
442  aRed = 0.2f;
443  aGreen = 0.2f;
444  aBlue = 0.2f;
445  }
446  }
447  else
448  {
449  rgb = ambientColor[aIndex];
450  aRed = rgb.x;
451  aGreen = rgb.y;
452  aBlue = rgb.z;
453  }
454 
455  if( aIndex < 0 || ( aIndex >= (int)specularColor.size() ) )
456  {
457  if( !specularColor.empty() )
458  {
459  rgb = specularColor.back();
460  sRed = rgb.x;
461  sGreen = rgb.y;
462  sBlue = rgb.z;
463  }
464  else
465  {
466  sRed = 0.2f;
467  sGreen = 0.2f;
468  sBlue = 0.2f;
469  }
470  }
471  else
472  {
473  rgb = specularColor[aIndex];
474  sRed = rgb.x;
475  sGreen = rgb.y;
476  sBlue = rgb.z;
477  }
478 
479  if( aIndex < 0 || ( aIndex >= (int)shininess.size() ) )
480  {
481  if( !shininess.empty() )
482  shiny = shininess.back();
483  else
484  shiny = 0.2f;
485  }
486  else
487  {
488  shiny = shininess[aIndex];
489  }
490 
491  checkRange( aRed );
492  checkRange( aGreen );
493  checkRange( aBlue );
494  checkRange( eRed );
495  checkRange( eGreen );
496  checkRange( eBlue );
497  checkRange( dRed );
498  checkRange( dGreen );
499  checkRange( dBlue );
500  checkRange( sRed );
501  checkRange( sGreen );
502  checkRange( sBlue );
503 
504  int n = 0;
505 
506  if( aRed + aGreen + aBlue > 0.01f )
507  ++n;
508 
509  if( eRed + eGreen + eBlue > 0.01f )
510  ++n;
511 
512  if( dRed + dGreen + dBlue > 0.01f )
513  ++n;
514 
515  if( ( sRed + sGreen + sBlue ) * shiny > 0.01f )
516  ++n;
517 
518  if( 0 == n )
519  ++n;
520 
521  float red, green, blue;
522 
523  red = (eRed + aRed + dRed + sRed * shiny) / n;
524  green = (eGreen + aGreen + dGreen + sGreen * shiny) / n;
525  blue = (eBlue + aBlue + dBlue + sBlue * shiny) / n;
526  checkRange( red );
527  checkRange( green );
528  checkRange( blue );
529  aColor->SetColor( red, green, blue );
530 }
531 
532 
533 void WRL1MATERIAL::checkRange( float& aValue )
534 {
535  if( aValue < 0.0 )
536  aValue = 0.0;
537  else if( aValue > 1.0 )
538  aValue = 1.0;
539 }
540 
541 
543 {
544  if( nullptr == aColor )
545  return;
546 
547  if( aColor == colors[0] )
548  {
549  if( nullptr == S3D::GetSGNodeParent( aColor ) )
550  {
551  colors[0] = nullptr;
552  S3D::DestroyNode( aColor );
553  }
554 
555  return;
556  }
557 
558  if( aColor == colors[1] && nullptr == S3D::GetSGNodeParent( aColor ) )
559  {
560  colors[1] = nullptr;
561  S3D::DestroyNode( aColor );
562  }
563 }
bool ReadMFFloat(std::vector< float > &aMFFloat)
Definition: wrlproc.cpp:1393
virtual ~WRL1MATERIAL()
bool ReadMFVec3f(std::vector< WRLVEC3F > &aMFVec3f)
Definition: wrlproc.cpp:1839
Represent the top node of a VRML1 model.
Definition: vrml1_base.h:45
std::vector< WRLVEC3F > emissiveColor
SGNODE * colors[2]
void Pop(void)
Definition: wrlproc.cpp:2035
glm::vec3 WRLVEC3F
Definition: wrltypes.h:188
bool Read(WRLPROC &proc, WRL1BASE *aTopNode) override
bool SetTransparency(float aTransparency) noexcept
bool SetDiffuse(float aRVal, float aGVal, float aBVal)
SGLIB_API SGNODE * GetSGNodeParent(SGNODE *aNode)
Definition: ifsg_api.cpp:492
bool SetColor(float aRedVal, float aGreenVal, float aBlueVal)
Definition: sg_base.cpp:85
The base class of all Scene Graph nodes.
Definition: sg_node.h:74
collects header files for all SG* wrappers and the API
SGNODE * GetRawPtr(void) noexcept
Function GetRawPtr() returns the raw internal SGNODE pointer.
Definition: ifsg_node.cpp:65
void checkRange(float &aValue)
WRL1MATERIAL(NAMEREGISTER *aDictionary)
SGLIB_API void DestroyNode(SGNODE *aNode) noexcept
Function DestroyNode deletes the given SG* class node.
Definition: ifsg_api.cpp:148
bool SetAmbient(float aRVal, float aGVal, float aBVal)
The base class of all VRML1 nodes.
Definition: vrml1_node.h:116
std::vector< WRLVEC3F > ambientColor
SGNODE * TranslateToSG(SGNODE *aParent, WRL1STATUS *sp) override
Produce a representation of the data using the intermediate scenegraph structures of the kicad_3dsg l...
std::vector< float > shininess
std::string GetFilePosition() const
Definition: wrlproc.cpp:1982
bool SetShininess(float aShininess) noexcept
char Peek(void)
Definition: wrlproc.cpp:2007
std::string GetFileName(void)
Definition: wrlproc.cpp:1995
const wxChar *const traceVrmlPlugin
Flag to enable VRML plugin trace output.
Definition: vrml.cpp:63
virtual bool AddChildNode(WRL1NODE *aNode)
Definition: vrml1_node.cpp:376
SGNODE * GetAppearance(int aIndex)
Return an SGAPPEARANCE node representing the appearance for an IndexedFaceSet.
bool SetEmissive(float aRVal, float aGVal, float aBVal)
std::vector< float > transparency
bool SetSpecular(float aRVal, float aGVal, float aBVal)
WRL1NODES m_Type
Definition: vrml1_node.h:227
bool ReadName(std::string &aName)
Definition: wrlproc.cpp:289
void GetColor(SGCOLOR *aColor, int aIndex)
Compute an SGCOLOR representing the appearance of a vertex or face.
std::vector< WRLVEC3F > specularColor
WRL1NODE * m_Parent
Definition: vrml1_node.h:226
std::string GetError(void)
Definition: wrlproc.cpp:1960
bool AddChildNode(WRL1NODE *aNode) override
bool eof(void)
Definition: wrlproc.cpp:1954
bool AddRefNode(WRL1NODE *aNode) override
void Reclaim(SGNODE *aColor)
Destroy the given color node if it does not have a parent.
std::vector< WRLVEC3F > diffuseColor
WRL1MATERIAL * mat
Definition: vrml1_node.h:88