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