1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 /*!
34   \class SoMaterial SoMaterial.h Inventor/nodes/SoMaterial.h
35   \brief The SoMaterial class is a node type for setting up material values for scene geometry.
36 
37   \ingroup nodes
38 
39   After traversing an SoMaterial node, subsequent shape nodes with
40   geometry in the scene graph will use values from the material "pool"
41   of the traversal state set up from nodes of this type.
42 
43   For detailed information on the various components, see the OpenGL
44   color model, presented in the chapter "Colors and Coloring" (chapter
45   2.13 in the OpenGL 1.4 specification).
46 
47   Note that values from a material node will \e replace the previous
48   values from the traversal state, they will \e not accumulate. That's
49   the case even when e.g. material changes are \e implicit in an
50   iv-file, as illustrated by the following example:
51 
52   Also note that support for multiple values in ambientColor,
53   emissiveColor, specularColor and shininess was obsoleted in Open
54   Inventor 2.1. The reason for this design change was performance
55   driven, since it's relatively slow to change the OpenGL material
56   properties. Changing the diffuse color value is fast though, so it's
57   still possible to have multiple diffuseColor and transparency
58   values.
59 
60   \verbatim
61   #Inventor V2.1 ascii
62 
63   Material { ambientColor 1 0 0 }
64   Cone { }
65 
66   Translation { translation 5 0 0 }
67 
68   Material { }
69   Sphere { }
70   \endverbatim
71 
72   (The SoSphere will not "inherit" the SoMaterial::ambientColor from
73   the first SoMaterial node, even though it is not explicitly set in
74   the second material node. The default value of
75   SoMaterial::ambientColor will be used.)
76 
77   Note that nodes imported as part of a VRML V1.0 file has a special
78   case, where the fields SoMaterial::ambientColor,
79   SoMaterial::diffuseColor and SoMaterial::specularColor contains zero
80   values, and SoMaterial::emissiveColor contains one or more
81   values. The values in SoMaterial::emissiveColor should then be
82   treated as precalculated lighting, and the other fields should be
83   ignored.
84 
85   You can detect this case by checking the values of the material
86   elements when the scene graph is traversed using an
87   SoCallbackAction. SoDiffuseColorElement, SoAmbientColorElement, and
88   SoSpecularColorElement will contain one value with a completely
89   black color (0.0f, 0.0f, 0.0f), SoShininessElement will contain one
90   value of 0.0f, and SoEmissiveColorElement will contain one or more
91   values. It is done like this to make rendering work correctly on
92   systems that do not test for this specific case.
93 
94   You should only check for this case when you're traversing a VRML
95   V1.0 file scene graph, of course. See SoNode::getNodeType() for
96   information about how nodes can be tested for whether or not they
97   have been imported or otherwise set up as of VRML1 type versus
98   Inventor type.
99 
100   When the scene graph is rendered using an SoGLRenderAction, the
101   elements will be set differently to optimize rendering.  The
102   SoDiffuseColorElement will be set to the values in
103   SoMaterial::emissiveColor, and the light model will be set to
104   SoLightModel::BASE_COLOR.
105 
106   The SoMaterial::transparency values will always be treated normally.
107 
108   Here is a very simple usage example:
109 
110   \verbatim
111   #Inventor V2.1 ascii
112 
113   Separator {
114      Coordinate3 {
115         point [ 0 0 0, 1 0 0, 1 1 0 ]
116      }
117 
118      Material {
119         diffuseColor [ 1 0 0, 1 1 0, 0 0 1 ]
120      }
121 
122      MaterialBinding {
123         value PER_VERTEX
124      }
125 
126      IndexedFaceSet {
127         coordIndex [ 0, 1, 2, -1 ]
128      }
129   }
130   \endverbatim
131 
132   <b>FILE FORMAT/DEFAULTS:</b>
133   \code
134     Material {
135         ambientColor 0.2 0.2 0.2
136         diffuseColor 0.8 0.8 0.8
137         specularColor 0 0 0
138         emissiveColor 0 0 0
139         shininess 0.2
140         transparency 0
141     }
142   \endcode
143 
144   \sa SoMaterialBinding, SoBaseColor, SoPackedColor
145 */
146 
147 // *************************************************************************
148 
149 // FIXME: should also describe what happens if the number of values in
150 // the fields are not consistent. 20020119 mortene.
151 
152 // *************************************************************************
153 
154 #include <Inventor/nodes/SoMaterial.h>
155 
156 #include <cstdlib>
157 
158 #include <Inventor/C/tidbits.h>
159 #include <Inventor/actions/SoCallbackAction.h>
160 #include <Inventor/actions/SoGLRenderAction.h>
161 #include <Inventor/actions/SoPickAction.h>
162 #include <Inventor/elements/SoOverrideElement.h>
163 #include <Inventor/elements/SoShapeStyleElement.h>
164 #include <Inventor/elements/SoGLLazyElement.h>
165 #include <Inventor/elements/SoAmbientColorElement.h>
166 #include <Inventor/elements/SoDiffuseColorElement.h>
167 #include <Inventor/elements/SoSpecularColorElement.h>
168 #include <Inventor/elements/SoEmissiveColorElement.h>
169 #include <Inventor/elements/SoShininessElement.h>
170 #include <Inventor/elements/SoTransparencyElement.h>
171 #include <Inventor/elements/SoLightModelElement.h>
172 #include <Inventor/elements/SoGLVBOElement.h>
173 #include <Inventor/errors/SoDebugError.h>
174 
175 #include <Inventor/annex/Profiler/SoProfiler.h>
176 #include <Inventor/annex/Profiler/elements/SoProfilerElement.h>
177 #include <Inventor/annex/Profiler/SbProfilingData.h>
178 
179 #ifdef HAVE_CONFIG_H
180 #include "config.h"
181 #endif // HAVE_CONFIG_H
182 
183 #ifdef COIN_THREADSAFE
184 #include <Inventor/threads/SbStorage.h>
185 #endif // COIN_THREADSAFE
186 
187 #include "rendering/SoVBO.h"
188 #include "nodes/SoSubNodeP.h"
189 
190 // *************************************************************************
191 
192 /*!
193   \var SoMFColor SoMaterial::ambientColor
194 
195   Ambient material part color values. Will by default contain a single
196   color value of [0.2, 0.2, 0.2] (ie dark gray).
197 
198   The ambient part of the material is not influenced by any
199   lightsources, and should be thought of conceptually as the constant,
200   but small contribution of light to a scene "seeping in" from
201   everywhere.
202 
203   (Think of the ambient contribution in the context that there's
204   always photons fizzing around everywhere -- even in a black,
205   lightsource-less room, for instance).
206 
207   Only the first value in this field will be used. All other values
208   will be ignored.
209 
210   \sa SoEnvironment::ambientIntensity
211 */
212 /*!
213   \var SoMFColor SoMaterial::diffuseColor
214 
215   Diffuse material part color values. This field is by default
216   initialized to contain a single color value of [0.8, 0.8, 0.8]
217   (light gray).
218 
219   The diffuse part is combined with the light emitted from the scene's
220   light sources.
221 
222   Traditional Open Inventor uses the same override bit for both
223   diffuse color and transparency.  To get around this problem if you
224   need to override one without the other, set the environment
225   variable "COIN_SEPARATE_DIFFUSE_TRANSPARENCY_OVERRIDE".  This is
226   a Coin extension, and  will not work on the other Open Inventor
227   implementations.
228 */
229 /*!
230   \var SoMFColor SoMaterial::specularColor
231 
232   Specular material part color values. Defaults to a single color
233   value of [0, 0, 0] (black).
234 
235   Only the first value in this field will be used. All other values
236   will be ignored.
237 */
238 
239 /*!
240   \var SoMFColor SoMaterial::emissiveColor
241 
242   The color of the light "emitted" by the subsequent geometry,
243   independent of lighting / shading.
244 
245   Defaults to contain a single color value of [0, 0, 0] (black, ie no
246   contribution).
247 
248   Only the first value in this field will be used. All other values will be ignored.
249 */
250 
251 /*!
252   \var SoMFFloat SoMaterial::shininess
253 
254   Shininess values. Decides how the light from light sources are
255   distributed across the geometry surfaces. Valid range is from 0.0
256   (which gives a dim appearance), to 1.0 (glossy-looking surfaces).
257 
258   Defaults to contain a single value of 0.2.
259 
260   Only the first value in this field will be used. All other values
261   will be ignored.
262 */
263 
264 /*!
265   \var SoMFFloat SoMaterial::transparency
266 
267   Transparency values. Valid range is from 0.0 (completely opaque,
268   which is the default) to 1.0 (completely transparent,
269   i.e. invisible).
270 
271   Defaults to contain a single value of 0.0.
272 
273   Traditional Open Inventor uses the same override bit for both
274   transparency and diffuse color.  To get around this problem if you
275   need to override one without the other, set the environment
276   variable "COIN_SEPARATE_DIFFUSE_TRANSPARENCY_OVERRIDE".  This is
277   a Coin extension, and  will not work on the other Open Inventor
278   implementations.
279 */
280 
281 // defines for materialtype
282 #define TYPE_UNKNOWN            0
283 #define TYPE_NORMAL             1
284 #define TYPE_VRML1_ONLYEMISSIVE 2 // special case in vrml1
285 
286 // *************************************************************************
287 
288 #ifndef DOXYGEN_SKIP_THIS
289 
290 class SoMaterialP {
291 public:
SoMaterialP()292   SoMaterialP() :
293 #ifdef COIN_THREADSAFE
294     colorpacker_storage(sizeof(void*), alloc_colorpacker, free_colorpacker),
295 #endif // COIN_THREADSAFE
296     vbo(NULL) { }
~SoMaterialP()297   ~SoMaterialP() { delete this->vbo; }
298 
299   int materialtype;
300   int transparencyflag;
301 
302 #ifdef COIN_THREADSAFE
303   SbStorage colorpacker_storage;
304 #else // COIN_THREADSAFE
305   SoColorPacker single_colorpacker;
306 #endif // COIN_THREADSAFE
307 
getColorPacker(void)308   SoColorPacker * getColorPacker(void) {
309 #ifdef COIN_THREADSAFE
310     SoColorPacker ** cptr = (SoColorPacker**) this->colorpacker_storage.get();
311     return * cptr;
312 #else // COIN_THREADSAFE
313     return &this->single_colorpacker;
314 #endif // COIN_THREADSAFE
315   }
316 
317   SoVBO * vbo;
318 
319 #ifdef COIN_THREADSAFE
320 private:
alloc_colorpacker(void * data)321   static void alloc_colorpacker(void * data) {
322     SoColorPacker ** cptr = (SoColorPacker**) data;
323     *cptr = new SoColorPacker;
324   }
free_colorpacker(void * data)325   static void free_colorpacker(void * data) {
326     SoColorPacker ** cptr = (SoColorPacker**) data;
327     delete *cptr;
328   }
329 #endif // COIN_THREADSAFE
330 };
331 
332 #endif // DOXYGEN_SKIP_THIS
333 
334 #define PRIVATE(obj) ((obj)->pimpl)
335 
336 SO_NODE_SOURCE(SoMaterial);
337 
338 /*!
339   Constructor.
340 */
SoMaterial(void)341 SoMaterial::SoMaterial(void)
342 {
343   SO_NODE_INTERNAL_CONSTRUCTOR(SoMaterial);
344 
345   SO_NODE_ADD_FIELD(ambientColor, (0.2f, 0.2f, 0.2f));
346   SO_NODE_ADD_FIELD(diffuseColor, (0.8f, 0.8f, 0.8f));
347   SO_NODE_ADD_FIELD(specularColor, (0.0f, 0.0f, 0.0f));
348   SO_NODE_ADD_FIELD(emissiveColor, (0.0f, 0.0f, 0.0f));
349   SO_NODE_ADD_FIELD(shininess, (0.2f));
350   SO_NODE_ADD_FIELD(transparency, (0.0f));
351 
352   PRIVATE(this)->materialtype = TYPE_NORMAL;
353   PRIVATE(this)->transparencyflag = FALSE; // we know it's not transparent
354 }
355 
356 /*!
357   Destructor.
358 */
~SoMaterial()359 SoMaterial::~SoMaterial()
360 {
361 }
362 
363 // Doc from superclass.
364 void
initClass(void)365 SoMaterial::initClass(void)
366 {
367   SO_NODE_INTERNAL_INIT_CLASS(SoMaterial, SO_FROM_INVENTOR_1|SoNode::VRML1);
368 
369   SO_ENABLE(SoGLRenderAction, SoGLLazyElement);
370   SO_ENABLE(SoCallbackAction, SoLazyElement);
371 
372   SO_ENABLE(SoCallbackAction, SoAmbientColorElement);
373   SO_ENABLE(SoCallbackAction, SoDiffuseColorElement);
374   SO_ENABLE(SoCallbackAction, SoEmissiveColorElement);
375   SO_ENABLE(SoCallbackAction, SoSpecularColorElement);
376   SO_ENABLE(SoCallbackAction, SoShininessElement);
377   SO_ENABLE(SoCallbackAction, SoTransparencyElement);
378 
379   SO_ENABLE(SoGLRenderAction, SoAmbientColorElement);
380   SO_ENABLE(SoGLRenderAction, SoDiffuseColorElement);
381   SO_ENABLE(SoGLRenderAction, SoEmissiveColorElement);
382   SO_ENABLE(SoGLRenderAction, SoSpecularColorElement);
383   SO_ENABLE(SoGLRenderAction, SoShininessElement);
384   SO_ENABLE(SoGLRenderAction, SoTransparencyElement);
385 }
386 
387 // Doc from superclass.
388 void
GLRender(SoGLRenderAction * action)389 SoMaterial::GLRender(SoGLRenderAction * action)
390 {
391   SoMaterial::doAction(action);
392 }
393 
394 // Doc from superclass.
395 void
doAction(SoAction * action)396 SoMaterial::doAction(SoAction * action)
397 {
398   SbBool istransparent = FALSE;
399 
400   SoState * state = action->getState();
401 
402   if (SoProfiler::isEnabled()) {
403     // register the SoColorPacker memory usage
404     if (state->isElementEnabled(SoProfilerElement::getClassStackIndex())) {
405       const SoColorPacker * packer = PRIVATE(this)->getColorPacker();
406       if (packer) {
407         SoProfilerElement * profilerelt = SoProfilerElement::get(state);
408         assert(profilerelt);
409         SbProfilingData & data = profilerelt->getProfilingData();
410         int entry = data.getIndex(action->getCurPath(), TRUE);
411         assert(entry != -1);
412         size_t mem = data.getNodeFootprint(entry, SbProfilingData::MEMORY_SIZE);
413         data.setNodeFootprint(entry, SbProfilingData::MEMORY_SIZE,
414                               mem + packer->getSize() * sizeof(uint32_t));
415       }
416     }
417   }
418 
419   uint32_t bitmask = 0;
420   uint32_t flags = SoOverrideElement::getFlags(state);
421 #define TEST_OVERRIDE(bit) ((SoOverrideElement::bit & flags) != 0)
422 
423   if (!this->ambientColor.isIgnored() && this->ambientColor.getNum() &&
424       !TEST_OVERRIDE(AMBIENT_COLOR)) {
425     bitmask |= SoLazyElement::AMBIENT_MASK;
426     if (this->isOverride()) {
427       SoOverrideElement::setAmbientColorOverride(state, this, TRUE);
428     }
429   }
430   if (!this->diffuseColor.isIgnored() && this->diffuseColor.getNum() &&
431       !TEST_OVERRIDE(DIFFUSE_COLOR)) {
432     // Note: the override flag bit values for diffuseColor and
433     // transparency are equal (done like that to match SGI/TGS
434     // Inventor behavior), so overriding one will also override the
435     // other.
436     bitmask |= SoLazyElement::DIFFUSE_MASK;
437     if (this->isOverride()) {
438       SoOverrideElement::setDiffuseColorOverride(state, this, TRUE);
439     }
440   }
441   if (!this->emissiveColor.isIgnored() && this->emissiveColor.getNum() &&
442       !TEST_OVERRIDE(EMISSIVE_COLOR)) {
443     bitmask |= SoLazyElement::EMISSIVE_MASK;
444     if (this->isOverride()) {
445       SoOverrideElement::setEmissiveColorOverride(state, this, TRUE);
446     }
447   }
448   if (!this->specularColor.isIgnored() && this->specularColor.getNum() &&
449       !TEST_OVERRIDE(SPECULAR_COLOR)) {
450     bitmask |= SoLazyElement::SPECULAR_MASK;
451     if (this->isOverride()) {
452       SoOverrideElement::setSpecularColorOverride(state, this, TRUE);
453     }
454   }
455   if (!this->shininess.isIgnored() && this->shininess.getNum() &&
456       !TEST_OVERRIDE(SHININESS)) {
457     bitmask |= SoLazyElement::SHININESS_MASK;
458     if (this->isOverride()) {
459       SoOverrideElement::setShininessOverride(state, this, TRUE);
460     }
461   }
462   if (!this->transparency.isIgnored() && this->transparency.getNum() &&
463       !TEST_OVERRIDE(TRANSPARENCY)) {
464     // Note: the override flag bit values for diffuseColor and
465     // transparency are equal (done like that to match SGI/TGS
466     // Inventor behavior), so overriding one will also override the
467     // other.
468     bitmask |= SoLazyElement::TRANSPARENCY_MASK;
469     if (this->isOverride()) {
470       SoOverrideElement::setTransparencyOverride(state, this, TRUE);
471     }
472     // if we don't know if material is transparent, run through all
473     // values and test
474     if (PRIVATE(this)->transparencyflag < 0) {
475       int i, n = this->transparency.getNum();
476       const float * p = this->transparency.getValues(0);
477       for (i = 0; i < n; i++) {
478         if (p[i] > 0.0f) {
479           istransparent = TRUE;
480           break;
481         }
482       }
483       // we now know whether material is transparent or not
484       PRIVATE(this)->transparencyflag = (int) istransparent;
485     }
486     istransparent = (SbBool) PRIVATE(this)->transparencyflag;
487   }
488 #undef TEST_OVERRIDE
489 
490   if (bitmask) {
491     SbColor dummycolor(0.8f, 0.8f, 0.0f);
492     float dummyval = 0.2f;
493     const SbColor * diffuseptr = this->diffuseColor.getValues(0);
494     int numdiffuse = this->diffuseColor.getNum();
495 
496     if (this->getMaterialType() == TYPE_VRML1_ONLYEMISSIVE) {
497       bitmask |= SoLazyElement::DIFFUSE_MASK;
498       bitmask &= ~SoLazyElement::EMISSIVE_MASK;
499       diffuseptr = this->emissiveColor.getValues(0);
500       numdiffuse = this->emissiveColor.getNum();
501       // if only emissive color, turn off lighting and render as diffuse.
502       // this is much faster
503       SoLightModelElement::set(state, this, SoLightModelElement::BASE_COLOR);
504     }
505     else if (this->getNodeType() == SoNode::VRML1) {
506       SoLightModelElement::set(state, this, SoLightModelElement::PHONG);
507     }
508 
509 #if COIN_DEBUG
510     if (bitmask & SoLazyElement::SHININESS_MASK) {
511       static int didwarn = 0;
512       if (!didwarn && (this->shininess[0] < 0.0f || this->shininess[0] > 1.0f)) {
513         SoDebugError::postWarning("SoMaterial::GLRender",
514                                   "Shininess out of range [0-1]. "
515                                   "The shininess value will be clamped."
516                                   "This warning will be printed only once, but there might be more errors. "
517                                   "You should check and fix your code and/or Inventor exporter.");
518 
519         didwarn = 1;
520       }
521     }
522 #endif // COIN_DEBUG
523 
524     const int numtransp = this->transparency.getNum();
525     SoLazyElement::setMaterials(state, this, bitmask,
526                                 PRIVATE(this)->getColorPacker(),
527                                 diffuseptr, numdiffuse,
528                                 this->transparency.getValues(0), numtransp,
529                                 bitmask & SoLazyElement::AMBIENT_MASK ?
530                                 this->ambientColor[0] : dummycolor,
531                                 bitmask & SoLazyElement::EMISSIVE_MASK ?
532                                 this->emissiveColor[0] : dummycolor,
533                                 bitmask & SoLazyElement::SPECULAR_MASK ?
534                                 this->specularColor[0] : dummycolor,
535                                 bitmask & SoLazyElement::SHININESS_MASK ?
536                                 SbClamp(this->shininess[0], 0.0f, 1.0f) : dummyval,
537                                 istransparent);
538     if (state->isElementEnabled(SoGLVBOElement::getClassStackIndex())) {
539       SoBase::staticDataLock();
540       SbBool setvbo = FALSE;
541       if (SoGLVBOElement::shouldCreateVBO(state, numdiffuse)) {
542         setvbo = TRUE;
543         if (PRIVATE(this)->vbo == NULL) {
544           PRIVATE(this)->vbo = new SoVBO(GL_ARRAY_BUFFER, GL_STATIC_DRAW);
545         }
546       }
547       else if (PRIVATE(this)->vbo) {
548         PRIVATE(this)->vbo->setBufferData(NULL, 0, 0);
549       }
550       // don't fill in any data in the VBO. Data will be filled in
551       // using the ColorPacker right before the VBO is used
552       SoBase::staticDataUnlock();
553       if (setvbo) {
554         SoGLVBOElement::setColorVBO(state, PRIVATE(this)->vbo);
555       }
556     }
557   }
558 }
559 
560 // Doc from superclass.
561 void
callback(SoCallbackAction * action)562 SoMaterial::callback(SoCallbackAction * action)
563 {
564   SoMaterial::doAction(action);
565 }
566 
567 void
notify(SoNotList * list)568 SoMaterial::notify(SoNotList *list)
569 {
570   SoField * f = list->getLastField();
571   if (f) PRIVATE(this)->materialtype = TYPE_UNKNOWN;
572   if (f == &this->transparency) {
573     PRIVATE(this)->transparencyflag = -1; // unknown
574   }
575   inherited::notify(list);
576 }
577 
578 //
579 // to test for special vrml1 case. It's not used right now,
580 // but it might be enabled again later. pederb, 2002-09-11
581 //
582 int
getMaterialType(void)583 SoMaterial::getMaterialType(void)
584 {
585   if (this->getNodeType() != SoNode::VRML1) return TYPE_NORMAL;
586   else {
587     if (PRIVATE(this)->materialtype == TYPE_UNKNOWN) {
588       if (!this->diffuseColor.isIgnored() && this->diffuseColor.getNum() == 0 &&
589           !this->ambientColor.isIgnored() && this->ambientColor.getNum() == 0 &&
590           !this->specularColor.isIgnored() && this->specularColor.getNum() == 0 &&
591           !this->emissiveColor.isIgnored() && this->emissiveColor.getNum()) {
592         PRIVATE(this)->materialtype = TYPE_VRML1_ONLYEMISSIVE;
593       }
594       else if (this->emissiveColor.getNum() > this->diffuseColor.getNum()) {
595         PRIVATE(this)->materialtype = TYPE_VRML1_ONLYEMISSIVE;
596       }
597       else {
598         PRIVATE(this)->materialtype = TYPE_NORMAL;
599       }
600     }
601     return PRIVATE(this)->materialtype;
602   }
603 }
604 
605 #undef PRIVATE
606 #undef TYPE_UNKNOWN
607 #undef TYPE_NORMAL
608 #undef TYPE_VRML1_ONLYEMISSIVE
609