Panda3D
 All Classes Functions Variables Enumerations
colladaLoader.cxx
1 // Filename: colladaLoader.cxx
2 // Created by: Xidram (21Dec10)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "colladaLoader.h"
16 #include "virtualFileSystem.h"
17 #include "luse.h"
18 #include "string_utils.h"
19 #include "geomNode.h"
20 #include "geomVertexWriter.h"
21 #include "geomTriangles.h"
22 #include "lightNode.h"
23 #include "lightAttrib.h"
24 #include "ambientLight.h"
25 #include "directionalLight.h"
26 #include "pointLight.h"
27 #include "spotlight.h"
28 
29 #include "colladaBindMaterial.h"
30 #include "colladaPrimitive.h"
31 
32 // Collada DOM includes. No other includes beyond this point.
33 #include "pre_collada_include.h"
34 #include <dom/domCOLLADA.h>
35 #include <dom/domNode.h>
36 #include <dom/domVisual_scene.h>
37 #include <dom/domTranslate.h>
38 #include <dom/domRotate.h>
39 #include <dom/domMatrix.h>
40 
41 #if PANDA_COLLADA_VERSION >= 15
42 #include <dom/domInstance_with_extra.h>
43 #else
44 #include <dom/domInstanceWithExtra.h>
45 #define domInstance_with_extra domInstanceWithExtra
46 #define domTargetable_floatRef domTargetableFloatRef
47 #endif
48 
49 #define TOSTRING(x) (x == NULL ? "" : x)
50 
51 ////////////////////////////////////////////////////////////////////
52 // Function: ColladaLoader::Constructor
53 // Description:
54 ////////////////////////////////////////////////////////////////////
55 ColladaLoader::
56 ColladaLoader() :
57  _record (NULL),
58  _cs (CS_default),
59  _error (false),
60  _root (NULL),
61  _collada (NULL) {
62 
63  _dae = new DAE;
64 }
65 
66 ////////////////////////////////////////////////////////////////////
67 // Function: ColladaLoader::Destructor
68 // Description:
69 ////////////////////////////////////////////////////////////////////
70 ColladaLoader::
71 ~ColladaLoader() {
72  delete _dae;
73 }
74 
75 ////////////////////////////////////////////////////////////////////
76 // Function: ColladaLoader::read
77 // Description: Reads from the indicated file.
78 ////////////////////////////////////////////////////////////////////
79 bool ColladaLoader::
80 read(const Filename &filename) {
81  _filename = filename;
82 
83  string data;
85 
86  if (!vfs->read_file(_filename, data, true)) {
87  collada_cat.error()
88  << "Error reading " << _filename << "\n";
89  _error = true;
90  return false;
91  }
92 
93  _collada = _dae->openFromMemory(_filename.to_os_specific(), data.c_str());
94  _error = (_collada == NULL);
95  return !_error;
96 }
97 
98 ////////////////////////////////////////////////////////////////////
99 // Function: ColladaLoader::build_graph
100 // Description: Converts scene graph structures into a Panda3D
101 // scene graph, with _root being the root node.
102 ////////////////////////////////////////////////////////////////////
103 void ColladaLoader::
105  nassertv(_collada); // read() must be called first
106  nassertv(!_error); // and have succeeded
107 
108  _root = new ModelRoot(_filename.get_basename());
109 
110  domCOLLADA::domScene* scene = _collada->getScene();
111  domInstance_with_extra* inst = scene->getInstance_visual_scene();
112  domVisual_scene* vscene = daeSafeCast<domVisual_scene> (inst->getUrl().getElement());
113  if (vscene) {
114  load_visual_scene(*vscene, _root);
115  }
116 }
117 
118 ////////////////////////////////////////////////////////////////////
119 // Function: ColladaLoader::load_visual_scene
120 // Description: Loads a visual scene structure.
121 ////////////////////////////////////////////////////////////////////
122 void ColladaLoader::
123 load_visual_scene(domVisual_scene& scene, PandaNode *parent) {
124  // If we already loaded it before, instantiate the stored node.
125  if (scene.getUserData() != NULL) {
126  parent->add_child((PandaNode *) scene.getUserData());
127  return;
128  }
129 
130  PT(PandaNode) pnode = new PandaNode(TOSTRING(scene.getName()));
131  scene.setUserData((void *) pnode);
132  parent->add_child(pnode);
133 
134  // Load in any tags.
135  domExtra_Array &extras = scene.getExtra_array();
136  for (size_t i = 0; i < extras.getCount(); ++i) {
137  load_tags(*extras[i], pnode);
138  }
139 
140  // Now load in the child nodes.
141  domNode_Array &nodes = scene.getNode_array();
142  for (size_t i = 0; i < nodes.getCount(); ++i) {
143  load_node(*nodes[i], pnode);
144  }
145 
146  // Apply any lights we've encountered to the visual scene.
147  if (_lights.size() > 0) {
148  CPT(LightAttrib) lattr = DCAST(LightAttrib, LightAttrib::make());
149  pvector<LightNode*>::iterator it;
150  for (it = _lights.begin(); it != _lights.end(); ++it) {
151  lattr = DCAST(LightAttrib, lattr->add_on_light(*it));
152  }
153  pnode->set_state(RenderState::make(lattr));
154 
155  _lights.clear();
156  }
157 }
158 
159 ////////////////////////////////////////////////////////////////////
160 // Function: ColladaLoader::load_node
161 // Description: Loads a COLLADA <node>.
162 ////////////////////////////////////////////////////////////////////
163 void ColladaLoader::
164 load_node(domNode& node, PandaNode *parent) {
165  // If we already loaded it before, instantiate the stored node.
166  if (node.getUserData() != NULL) {
167  parent->add_child((PandaNode *) node.getUserData());
168  return;
169  }
170 
171  // Create the node.
172  PT(PandaNode) pnode;
173  pnode = new PandaNode(TOSTRING(node.getName()));
174  node.setUserData((void *) pnode);
175  parent->add_child(pnode);
176 
177  // Apply the transformation elements in reverse order.
178  LMatrix4f transform (LMatrix4f::ident_mat());
179 
180  daeElementRefArray &elements = node.getContents();
181  for (size_t i = elements.getCount(); i > 0; --i) {
182  daeElementRef &elem = elements[i - 1];
183 
184  switch (elem->getElementType()) {
185  case COLLADA_TYPE::LOOKAT: {
186  // Didn't test this, but *should* be right.
187  domFloat3x3 &l = (daeSafeCast<domLookat>(elem))->getValue();
188  LPoint3f eye (l[0], l[1], l[2]);
189  LVector3f up (l[6], l[7], l[8]);
190  LVector3f forward = LPoint3f(l[3], l[4], l[5]) - eye;
191  forward.normalize();
192  LVector3f side = forward.cross(up);
193  side.normalize();
194  up = side.cross(forward);
196  mat.set_col(0, side);
197  mat.set_col(1, up);
198  mat.set_col(2, -forward);
199  transform *= mat;
200  transform *= LMatrix4f::translate_mat(-eye);
201  break;
202  }
203  case COLLADA_TYPE::MATRIX: {
204  domFloat4x4 &m = (daeSafeCast<domMatrix>(elem))->getValue();
205  transform *= LMatrix4f(
206  m[0], m[4], m[ 8], m[12],
207  m[1], m[5], m[ 9], m[13],
208  m[2], m[6], m[10], m[14],
209  m[3], m[7], m[11], m[15]);
210  break;
211  }
212  case COLLADA_TYPE::ROTATE: {
213  domFloat4 &r = (daeSafeCast<domRotate>(elem))->getValue();
214  transform *= LMatrix4f::rotate_mat(r[3], LVecBase3f(r[0], r[1], r[2]));
215  break;
216  }
217  case COLLADA_TYPE::SCALE: {
218  domFloat3 &s = (daeSafeCast<domScale>(elem))->getValue();
219  transform *= LMatrix4f::scale_mat(s[0], s[1], s[2]);
220  break;
221  }
222  case COLLADA_TYPE::SKEW:
223  //FIXME: implement skew
224  collada_cat.error() << "<skew> not supported yet\n";
225  break;
226  case COLLADA_TYPE::TRANSLATE: {
227  domFloat3 &t = (daeSafeCast<domTranslate>(elem))->getValue();
228  transform *= LMatrix4f::translate_mat(t[0], t[1], t[2]);
229  break;
230  }
231  }
232  }
233  //TODO: convert coordinate systems
234  //transform *= LMatrix4f::convert_mat(XXX, _cs);
235 
236  // If there's a transform, set it.
237  if (transform != LMatrix4f::ident_mat()) {
238  pnode->set_transform(TransformState::make_mat(transform));
239  }
240 
241  // See if this node instantiates any cameras.
242  domInstance_camera_Array &caminst = node.getInstance_camera_array();
243  for (size_t i = 0; i < caminst.getCount(); ++i) {
244  domCamera* target = daeSafeCast<domCamera> (caminst[i]->getUrl().getElement());
245  load_camera(*target, pnode);
246  }
247 
248  // See if this node instantiates any controllers.
249  domInstance_controller_Array &ctrlinst = node.getInstance_controller_array();
250  for (size_t i = 0; i < ctrlinst.getCount(); ++i) {
251  domController* target = daeSafeCast<domController> (ctrlinst[i]->getUrl().getElement());
252  //TODO: implement controllers. For now, let's just read the geometry
253  if (target->getSkin() != NULL) {
254  domGeometry* geom = daeSafeCast<domGeometry> (target->getSkin()->getSource().getElement());
255  //TODO
256  //load_geometry(*geom, ctrlinst[i]->getBind_material(), pnode);
257  }
258  }
259 
260  // See if this node instantiates any geoms.
261  domInstance_geometry_Array &ginst = node.getInstance_geometry_array();
262  for (size_t i = 0; i < ginst.getCount(); ++i) {
263  load_instance_geometry(*ginst[i], pnode);
264  }
265 
266  // See if this node instantiates any lights.
267  domInstance_light_Array &linst = node.getInstance_light_array();
268  for (size_t i = 0; i < linst.getCount(); ++i) {
269  domLight* target = daeSafeCast<domLight> (linst[i]->getUrl().getElement());
270  load_light(*target, pnode);
271  }
272 
273  // And instantiate any <instance_nodes> elements.
274  domInstance_node_Array &ninst = node.getInstance_node_array();
275  for (size_t i = 0; i < ninst.getCount(); ++i) {
276  domNode* target = daeSafeCast<domNode> (ninst[i]->getUrl().getElement());
277  load_node(*target, pnode);
278  }
279 
280  // Now load in the child nodes.
281  domNode_Array &nodes = node.getNode_array();
282  for (size_t i = 0; i < nodes.getCount(); ++i) {
283  load_node(*nodes[i], pnode);
284  }
285 
286  // Load in any tags.
287  domExtra_Array &extras = node.getExtra_array();
288  for (size_t i = 0; i < extras.getCount(); ++i) {
289  load_tags(*extras[i], pnode);
290  //TODO: load SI_Visibility under XSI profile
291  //TODO: support OpenSceneGraph's switch nodes
292  }
293 }
294 
295 ////////////////////////////////////////////////////////////////////
296 // Function: ColladaLoader::load_tags
297 // Description: Loads tags specified in an <extra> element.
298 ////////////////////////////////////////////////////////////////////
299 void ColladaLoader::
300 load_tags(domExtra &extra, PandaNode *node) {
301  domTechnique_Array &techniques = extra.getTechnique_array();
302 
303  for (size_t t = 0; t < techniques.getCount(); ++t) {
304  if (cmp_nocase(techniques[t]->getProfile(), "PANDA3D") == 0) {
305  const daeElementRefArray &children = techniques[t]->getChildren();
306 
307  for (size_t c = 0; c < children.getCount(); ++c) {
308  daeElement &child = *children[c];
309 
310  if (cmp_nocase(child.getElementName(), "tag") == 0) {
311  const string &name = child.getAttribute("name");
312  if (name.size() > 0) {
313  node->set_tag(name, child.getCharData());
314  } else {
315  collada_cat.warning() << "Ignoring <tag> without name attribute\n";
316  }
317  } else if (cmp_nocase(child.getElementName(), "param") == 0) {
318  collada_cat.error() <<
319  "Unknown <param> attribute in PANDA3D technique. "
320  "Did you mean to use <tag> instead?\n";
321  }
322  }
323  }
324  }
325 }
326 
327 ////////////////////////////////////////////////////////////////////
328 // Function: ColladaLoader::load_camera
329 // Description: Loads a COLLADA <camera> as a Camera object.
330 ////////////////////////////////////////////////////////////////////
331 void ColladaLoader::
332 load_camera(domCamera &cam, PandaNode *parent) {
333  // If we already loaded it before, instantiate the stored node.
334  if (cam.getUserData() != NULL) {
335  parent->add_child((PandaNode *) cam.getUserData());
336  return;
337  }
338 
339  //TODO
340 }
341 
342 ////////////////////////////////////////////////////////////////////
343 // Function: ColladaLoader::load_instance_geometry
344 // Description: Loads a COLLADA <instance_geometry> as a GeomNode
345 // object.
346 ////////////////////////////////////////////////////////////////////
347 void ColladaLoader::
348 load_instance_geometry(domInstance_geometry &inst, PandaNode *parent) {
349  // If we already loaded it before, instantiate the stored node.
350  if (inst.getUserData() != NULL) {
351  parent->add_child((PandaNode *) inst.getUserData());
352  return;
353  }
354 
355  domGeometry* geom = daeSafeCast<domGeometry> (inst.getUrl().getElement());
356  nassertv(geom != NULL);
357 
358  // Create the node.
359  PT(GeomNode) gnode = new GeomNode(TOSTRING(geom->getName()));
360  inst.setUserData((void *) gnode);
361  parent->add_child(gnode);
362 
363  domBind_materialRef bind_mat = inst.getBind_material();
365  if (bind_mat != NULL) {
366  cbm.load_bind_material(*bind_mat);
367  }
368 
369  load_geometry(*geom, gnode, cbm);
370 
371  // Load in any tags.
372  domExtra_Array &extras = geom->getExtra_array();
373  for (size_t i = 0; i < extras.getCount(); ++i) {
374  load_tags(*extras[i], gnode);
375  }
376 }
377 
378 ////////////////////////////////////////////////////////////////////
379 // Function: ColladaLoader::load_geometry
380 // Description: Loads a COLLADA <geometry> and adds the primitives
381 // to the given GeomNode object.
382 ////////////////////////////////////////////////////////////////////
383 void ColladaLoader::
384 load_geometry(domGeometry &geom, GeomNode *gnode, ColladaBindMaterial &bind_mat) {
385  domMesh* mesh = geom.getMesh();
386  if (mesh == NULL) {
387  //TODO: support non-mesh geometry.
388  return;
389  }
390 
391  //TODO: support other than just triangles.
392  domLines_Array &lines_array = mesh->getLines_array();
393  for (size_t i = 0; i < lines_array.getCount(); ++i) {
394  PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*lines_array[i]);
395  if (prim != NULL) {
396  gnode->add_geom(prim->get_geom());
397  }
398  }
399 
400  domLinestrips_Array &linestrips_array = mesh->getLinestrips_array();
401  for (size_t i = 0; i < linestrips_array.getCount(); ++i) {
402  PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*linestrips_array[i]);
403  if (prim != NULL) {
404  gnode->add_geom(prim->get_geom());
405  }
406  }
407 
408  domPolygons_Array &polygons_array = mesh->getPolygons_array();
409  for (size_t i = 0; i < polygons_array.getCount(); ++i) {
410  PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*polygons_array[i]);
411  if (prim != NULL) {
412  gnode->add_geom(prim->get_geom());
413  }
414  }
415 
416  domPolylist_Array &polylist_array = mesh->getPolylist_array();
417  for (size_t i = 0; i < polylist_array.getCount(); ++i) {
418  PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*polylist_array[i]);
419  if (prim != NULL) {
420  gnode->add_geom(prim->get_geom());
421  }
422  }
423 
424  domTriangles_Array &triangles_array = mesh->getTriangles_array();
425  for (size_t i = 0; i < triangles_array.getCount(); ++i) {
426  PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*triangles_array[i]);
427  if (prim != NULL) {
428  gnode->add_geom(prim->get_geom());
429  }
430  }
431 
432  domTrifans_Array &trifans_array = mesh->getTrifans_array();
433  for (size_t i = 0; i < trifans_array.getCount(); ++i) {
434  PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*trifans_array[i]);
435  if (prim != NULL) {
436  gnode->add_geom(prim->get_geom());
437  }
438  }
439 
440  domTristrips_Array &tristrips_array = mesh->getTristrips_array();
441  for (size_t i = 0; i < tristrips_array.getCount(); ++i) {
442  PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*tristrips_array[i]);
443  if (prim != NULL) {
444  gnode->add_geom(prim->get_geom());
445  }
446  }
447 }
448 
449 ////////////////////////////////////////////////////////////////////
450 // Function: ColladaLoader::load_light
451 // Description: Loads a COLLADA <light> as a LightNode object.
452 ////////////////////////////////////////////////////////////////////
453 void ColladaLoader::
454 load_light(domLight &light, PandaNode *parent) {
455  // If we already loaded it before, instantiate the stored node.
456  if (light.getUserData() != NULL) {
457  parent->add_child((PandaNode *) light.getUserData());
458  return;
459  }
460 
461  PT(LightNode) lnode;
462  domLight::domTechnique_common &tc = *light.getTechnique_common();
463 
464  // Check for an ambient light.
465  domLight::domTechnique_common::domAmbientRef ambient = tc.getAmbient();
466  if (ambient != NULL) {
467  PT(AmbientLight) alight = new AmbientLight(TOSTRING(light.getName()));
468  lnode = DCAST(LightNode, alight);
469 
470  domFloat3 &color = ambient->getColor()->getValue();
471  alight->set_color(LColor(color[0], color[1], color[2], 1.0));
472  }
473 
474  // Check for a directional light.
475  domLight::domTechnique_common::domDirectionalRef directional = tc.getDirectional();
476  if (directional != NULL) {
477  PT(DirectionalLight) dlight = new DirectionalLight(TOSTRING(light.getName()));
478  lnode = DCAST(LightNode, dlight);
479 
480  domFloat3 &color = directional->getColor()->getValue();
481  dlight->set_color(LColor(color[0], color[1], color[2], 1.0));
482  dlight->set_direction(LVector3f(0, 0, -1));
483  }
484 
485  // Check for a point light.
486  domLight::domTechnique_common::domPointRef point = tc.getPoint();
487  if (point != NULL) {
488  PT(PointLight) plight = new PointLight(TOSTRING(light.getName()));
489  lnode = DCAST(LightNode, plight);
490 
491  domFloat3 &color = point->getColor()->getValue();
492  plight->set_color(LColor(color[0], color[1], color[2], 1.0));
493 
494  LVecBase3f atten (1.0f, 0.0f, 0.0f);
495  domTargetable_floatRef fval = point->getConstant_attenuation();
496  if (fval != NULL) {
497  atten[0] = fval->getValue();
498  }
499  fval = point->getLinear_attenuation();
500  if (fval != NULL) {
501  atten[1] = fval->getValue();
502  }
503  fval = point->getQuadratic_attenuation();
504  if (fval != NULL) {
505  atten[2] = fval->getValue();
506  }
507 
508  plight->set_attenuation(atten);
509  }
510 
511  // Check for a spot light.
512  domLight::domTechnique_common::domSpotRef spot = tc.getSpot();
513  if (spot != NULL) {
514  PT(Spotlight) slight = new Spotlight(TOSTRING(light.getName()));
515  lnode = DCAST(LightNode, slight);
516 
517  domFloat3 &color = spot->getColor()->getValue();
518  slight->set_color(LColor(color[0], color[1], color[2], 1.0));
519 
520  LVecBase3f atten (1.0f, 0.0f, 0.0f);
521  domTargetable_floatRef fval = spot->getConstant_attenuation();
522  if (fval != NULL) {
523  atten[0] = fval->getValue();
524  }
525  fval = spot->getLinear_attenuation();
526  if (fval != NULL) {
527  atten[1] = fval->getValue();
528  }
529  fval = spot->getQuadratic_attenuation();
530  if (fval != NULL) {
531  atten[2] = fval->getValue();
532  }
533 
534  slight->set_attenuation(atten);
535 
536  fval = spot->getFalloff_angle();
537  if (fval != NULL) {
538  slight->get_lens()->set_fov(fval->getValue());
539  } else {
540  slight->get_lens()->set_fov(180.0f);
541  }
542 
543  fval = spot->getFalloff_exponent();
544  if (fval != NULL) {
545  slight->set_exponent(fval->getValue());
546  } else {
547  slight->set_exponent(0.0f);
548  }
549  }
550 
551  if (lnode == NULL) {
552  return;
553  }
554  parent->add_child(lnode);
555  _lights.push_back(lnode);
556  light.setUserData((void*) lnode);
557 
558  // Load in any tags.
559  domExtra_Array &extras = light.getExtra_array();
560  for (size_t i = 0; i < extras.getCount(); ++i) {
561  load_tags(*extras[i], lnode);
562  }
563 }
A node of this type is created automatically at the root of each model file that is loaded...
Definition: modelRoot.h:31
A light shining from infinitely far away in a particular direction, like sunlight.
void set_tag(const string &key, const string &value, Thread *current_thread=Thread::get_current_thread())
Associates a user-defined value with a user-defined key which is stored on the node.
Definition: pandaNode.cxx:1403
static const LMatrix4f & ident_mat()
Returns an identity matrix.
Definition: lmatrix.h:903
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
static LMatrix4f scale_mat(const LVecBase3f &scale)
Returns a matrix that applies the indicated scale in each of the three axes.
Definition: lmatrix.h:2456
static LMatrix4f translate_mat(const LVecBase3f &trans)
Returns a matrix that applies the indicated translation.
Definition: lmatrix.h:2397
A derivative of Light and of PandaNode.
Definition: lightNode.h:29
static LMatrix4f rotate_mat(float angle, const LVecBase3f &axis, CoordinateSystem cs=CS_default)
Returns a matrix that rotates by the given angle in degrees counterclockwise about the indicated vect...
Definition: lmatrix.h:2425
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
void set_col(int col, const LVecBase4d &v)
Replaces the indicated column of the matrix.
Definition: lmatrix.h:5469
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
string read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns the entire contents of the indicated file as a string.
A light source that seems to illuminate all points in space at once.
Definition: ambientLight.h:29
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
void build_graph()
Converts scene graph structures into a Panda3D scene graph, with _root being the root node...
void load_bind_material(domBind_material &bind_mat)
Loads a bind_material object.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
A light originating from a single point in space, and shining in a particular direction, with a cone-shaped falloff.
Definition: spotlight.h:37
string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1196
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:436
Class that deals with COLLADA primitive structures, such as &lt;triangles&gt; and &lt;polylist&gt;.
void add_child(PandaNode *child_node, int sort=0, Thread *current_thread=Thread::get_current_thread())
Adds a new child to the node.
Definition: pandaNode.cxx:654
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:782
Class that deals with binding materials to COLLADA geometry.
A light originating from a single point in space, and shining in all directions.
Definition: pointLight.h:27
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:37
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition: geomNode.cxx:642
bool read(const Filename &filename)
Reads from the indicated file.
Indicates which set of lights should be considered &quot;on&quot; to illuminate geometry at this level and belo...
Definition: lightAttrib.h:33