Panda3D
 All Classes Functions Variables Enumerations
assimpLoader.cxx
1 // Filename: assimpLoader.cxx
2 // Created by: rdb (29Mar11)
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 "assimpLoader.h"
16 
17 #include "geomNode.h"
18 #include "luse.h"
19 #include "geomVertexWriter.h"
20 #include "geomPoints.h"
21 #include "geomLines.h"
22 #include "geomTriangles.h"
23 #include "pnmFileTypeRegistry.h"
24 #include "pnmImage.h"
25 #include "materialAttrib.h"
26 #include "textureAttrib.h"
27 #include "cullFaceAttrib.h"
28 #include "lightNode.h"
29 #include "ambientLight.h"
30 #include "directionalLight.h"
31 #include "spotlight.h"
32 #include "pointLight.h"
33 #include "look_at.h"
34 #include "texturePool.h"
35 
36 #include "pandaIOSystem.h"
37 #include "pandaLogger.h"
38 
39 #include "aiPostProcess.h"
40 
41 ////////////////////////////////////////////////////////////////////
42 // Function: AssimpLoader::Constructor
43 // Access: Public
44 // Description:
45 ////////////////////////////////////////////////////////////////////
46 AssimpLoader::
47 AssimpLoader() :
48  _error (false),
49  _geoms (NULL) {
50 
52  _importer.SetIOHandler(new PandaIOSystem);
53 }
54 
55 ////////////////////////////////////////////////////////////////////
56 // Function: AssimpLoader::Destructor
57 // Access: Public
58 // Description:
59 ////////////////////////////////////////////////////////////////////
60 AssimpLoader::
61 ~AssimpLoader() {
62  _importer.FreeScene();
63 }
64 
65 ////////////////////////////////////////////////////////////////////
66 // Function: AssimpLoader::get_extensions
67 // Access: Public
68 // Description: Returns a space-separated list of extensions that
69 // Assimp can load, without the leading dots.
70 ////////////////////////////////////////////////////////////////////
71 void AssimpLoader::
72 get_extensions(string &ext) const {
73  aiString aexts;
74  _importer.GetExtensionList(aexts);
75 
76  // The format is like: *.mdc;*.mdl;*.mesh.xml;*.mot
77  char *sub = strtok(aexts.data, ";");
78  while (sub != NULL) {
79  ext += sub + 2;
80  sub = strtok(NULL, ";");
81 
82  if (sub != NULL) {
83  ext += ' ';
84  }
85  }
86 }
87 
88 ////////////////////////////////////////////////////////////////////
89 // Function: AssimpLoader::read
90 // Access: Public
91 // Description: Reads from the indicated file.
92 ////////////////////////////////////////////////////////////////////
93 bool AssimpLoader::
94 read(const Filename &filename) {
95  _filename = filename;
96 
97  // I really don't know why we need to flip the winding order,
98  // but otherwise the models I tested with are showing inside out.
99  _scene = _importer.ReadFile(_filename.c_str(), aiProcess_Triangulate | aiProcess_GenUVCoords | aiProcess_FlipWindingOrder);
100  if (_scene == NULL) {
101  _error = true;
102  return false;
103  }
104 
105  _error = false;
106  return true;
107 }
108 
109 ////////////////////////////////////////////////////////////////////
110 // Function: AssimpLoader::build_graph
111 // Access: Public
112 // Description: Converts scene graph structures into a Panda3D
113 // scene graph, with _root being the root node.
114 ////////////////////////////////////////////////////////////////////
115 void AssimpLoader::
117  nassertv(_scene != NULL); // read() must be called first
118  nassertv(!_error); // and have succeeded
119 
120  // Protect the import process
121  MutexHolder holder(_lock);
122 
123  _root = new ModelRoot(_filename.get_basename());
124 
125  // Import all of the embedded textures first.
126  _textures = new PT(Texture)[_scene->mNumTextures];
127  for (size_t i = 0; i < _scene->mNumTextures; ++i) {
128  load_texture(i);
129  }
130 
131  // Then the materials.
132  _mat_states = new CPT(RenderState)[_scene->mNumMaterials];
133  for (size_t i = 0; i < _scene->mNumMaterials; ++i) {
134  load_material(i);
135  }
136 
137  // And then the meshes.
138  _geoms = new PT(Geom)[_scene->mNumMeshes];
139  _geom_matindices = new unsigned int[_scene->mNumMeshes];
140  for (size_t i = 0; i < _scene->mNumMeshes; ++i) {
141  load_mesh(i);
142  }
143 
144  // And now the node structure.
145  if (_scene->mRootNode != NULL) {
146  load_node(*_scene->mRootNode, _root);
147  }
148 
149  // And lastly, the lights.
150  for (size_t i = 0; i < _scene->mNumLights; ++i) {
151  load_light(*_scene->mLights[i]);
152  }
153 
154  delete[] _textures;
155  delete[] _mat_states;
156  delete[] _geoms;
157  delete[] _geom_matindices;
158 }
159 
160 ////////////////////////////////////////////////////////////////////
161 // Function: AssimpLoader::load_texture
162 // Access: Private
163 // Description: Converts an aiTexture into a Texture.
164 ////////////////////////////////////////////////////////////////////
165 void AssimpLoader::
166 load_texture(size_t index) {
167  const aiTexture &tex = *_scene->mTextures[index];
168 
169  PT(Texture) ptex = new Texture;
170 
171  if (tex.mHeight == 0) {
172  // Compressed texture.
173  assimp_cat.debug()
174  << "Reading embedded compressed texture with format " << tex.achFormatHint << " and size " << tex.mWidth << "\n";
175  stringstream str;
176  str.write((char*) tex.pcData, tex.mWidth);
177 
178  if (strncmp(tex.achFormatHint, "dds", 3) == 0) {
179  ptex->read_dds(str);
180 
181  } else {
183  PNMFileType *ftype;
184  PNMImage img;
185 
186  // Work around a bug in Assimp, it sometimes writes jp instead of jpg
187  if (strncmp(tex.achFormatHint, "jp\0", 3) == 0) {
188  ftype = reg->get_type_from_extension("jpg");
189  } else {
190  ftype = reg->get_type_from_extension(tex.achFormatHint);
191  }
192 
193  if (img.read(str, "", ftype)) {
194  ptex->load(img);
195  } else {
196  ptex = NULL;
197  }
198  }
199  } else {
200  assimp_cat.debug()
201  << "Reading embedded raw texture with size " << tex.mWidth << "x" << tex.mHeight << "\n";
202 
203  ptex->setup_2d_texture(tex.mWidth, tex.mHeight, Texture::T_unsigned_byte, Texture::F_rgba);
204  PTA_uchar data = ptex->modify_ram_image();
205 
206  size_t p = 0;
207  for (size_t i = 0; i < tex.mWidth * tex.mHeight; ++i) {
208  const aiTexel &texel = tex.pcData[i];
209  data[p++] = texel.b;
210  data[p++] = texel.g;
211  data[p++] = texel.r;
212  data[p++] = texel.a;
213  }
214  }
215 
216  //ostringstream path;
217  //path << "/tmp/" << index << ".png";
218  //ptex->write(path.str());
219 
220  _textures[index] = ptex;
221 
222 }
223 
224 ////////////////////////////////////////////////////////////////////
225 // Function: AssimpLoader::load_texture_stage
226 // Access: Private
227 // Description: Converts an aiMaterial into a RenderState.
228 ////////////////////////////////////////////////////////////////////
229 void AssimpLoader::
230 load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, CPT(TextureAttrib) &tattr) {
231  aiString path;
232  aiTextureMapping mapping;
233  unsigned int uvindex;
234  PN_stdfloat blend;
235  aiTextureOp op;
236  aiTextureMapMode mapmode;
237 
238  for (size_t i = 0; i < mat.GetTextureCount(ttype); ++i) {
239  mat.GetTexture(ttype, i, &path, &mapping, NULL, &blend, &op, &mapmode);
240 
241  if (AI_SUCCESS != mat.Get(AI_MATKEY_UVWSRC(ttype, i), uvindex)) {
242  // If there's no texture coordinate set for this texture,
243  // assume that it's the same as the index on the stack.
244  //TODO: if there's only one set on the mesh,
245  // force everything to use just the first stage.
246  uvindex = i;
247  }
248 
249  stringstream str;
250  str << uvindex;
251  PT(TextureStage) stage = new TextureStage(str.str());
252  if (uvindex > 0) {
253  stage->set_texcoord_name(InternalName::get_texcoord_name(str.str()));
254  }
255  PT(Texture) ptex = NULL;
256 
257  // I'm not sure if this is the right way to handle it, as
258  // I couldn't find much information on embedded textures.
259  if (path.data[0] == '*') {
260  long num = strtol(path.data + 1, NULL, 10);
261  ptex = _textures[num];
262 
263  } else if (path.length > 0) {
264  Filename fn = Filename::from_os_specific(string(path.data, path.length));
265 
266  // Try to find the file by moving up twice in the hierarchy.
268  Filename dir (_filename);
269  _filename.make_canonical();
270  dir = _filename.get_dirname();
271 
272  // Quake 3 BSP doesn't specify an extension for textures.
273  if (vfs->is_regular_file(Filename(dir, fn))) {
274  fn = Filename(dir, fn);
275  } else if (vfs->is_regular_file(Filename(dir, fn + ".tga"))) {
276  fn = Filename(dir, fn + ".tga");
277  } else if (vfs->is_regular_file(Filename(dir, fn + ".jpg"))) {
278  fn = Filename(dir, fn + ".jpg");
279  } else {
280  dir = _filename.get_dirname();
281  if (vfs->is_regular_file(Filename(dir, fn))) {
282  fn = Filename(dir, fn);
283  } else if (vfs->is_regular_file(Filename(dir, fn + ".tga"))) {
284  fn = Filename(dir, fn + ".tga");
285  } else if (vfs->is_regular_file(Filename(dir, fn + ".jpg"))) {
286  fn = Filename(dir, fn + ".jpg");
287  }
288  }
289 
290  ptex = TexturePool::load_texture(fn);
291  }
292 
293  if (ptex != NULL) {
294  tattr = DCAST(TextureAttrib, tattr->add_on_stage(stage, ptex));
295  }
296  }
297 }
298 
299 ////////////////////////////////////////////////////////////////////
300 // Function: AssimpLoader::load_material
301 // Access: Private
302 // Description: Converts an aiMaterial into a RenderState.
303 ////////////////////////////////////////////////////////////////////
304 void AssimpLoader::
305 load_material(size_t index) {
306  const aiMaterial &mat = *_scene->mMaterials[index];
307 
308  CPT(RenderState) state = RenderState::make_empty();
309 
310  aiColor3D col;
311  bool have;
312  int ival;
313  PN_stdfloat fval;
314 
315  // XXX a lot of this is untested.
316 
317  // First do the material attribute.
318  PT(Material) pmat = new Material;
319  have = false;
320  if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_DIFFUSE, col)) {
321  pmat->set_diffuse(LColor(col.r, col.g, col.b, 1));
322  have = true;
323  }
324  if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_SPECULAR, col)) {
325  if (AI_SUCCESS == mat.Get(AI_MATKEY_SHININESS_STRENGTH, fval)) {
326  pmat->set_specular(LColor(col.r * fval, col.g * fval, col.b * fval, 1));
327  } else {
328  pmat->set_specular(LColor(col.r, col.g, col.b, 1));
329  }
330  have = true;
331  }
332  if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_AMBIENT, col)) {
333  pmat->set_specular(LColor(col.r, col.g, col.b, 1));
334  have = true;
335  }
336  if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_EMISSIVE, col)) {
337  pmat->set_emission(LColor(col.r, col.g, col.b, 1));
338  have = true;
339  }
340  if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_TRANSPARENT, col)) {
341  //FIXME: ???
342  }
343  if (AI_SUCCESS == mat.Get(AI_MATKEY_SHININESS, fval)) {
344  pmat->set_shininess(fval);
345  have = true;
346  }
347  if (have) {
348  state = state->add_attrib(MaterialAttrib::make(pmat));
349  }
350 
351  // Wireframe.
352  if (AI_SUCCESS == mat.Get(AI_MATKEY_ENABLE_WIREFRAME, ival)) {
353  if (ival) {
354  state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_wireframe));
355  } else {
356  state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_filled));
357  }
358  }
359 
360  // Backface culling. Not sure if this is also supposed to
361  // set the twoside flag in the material, I'm guessing not.
362  if (AI_SUCCESS == mat.Get(AI_MATKEY_TWOSIDED, ival)) {
363  if (ival) {
364  state = state->add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_none));
365  } else {
366  state = state->add_attrib(CullFaceAttrib::make_default());
367  }
368  }
369 
370  // And let's not forget the textures!
371  CPT(TextureAttrib) tattr = DCAST(TextureAttrib, TextureAttrib::make());
372  load_texture_stage(mat, aiTextureType_DIFFUSE, tattr);
373  load_texture_stage(mat, aiTextureType_LIGHTMAP, tattr);
374  if (tattr->get_num_on_stages() > 0) {
375  state = state->add_attrib(tattr);
376  }
377 
378  _mat_states[index] = state;
379 }
380 
381 ////////////////////////////////////////////////////////////////////
382 // Function: AssimpLoader::load_mesh
383 // Access: Private
384 // Description: Converts an aiMesh into a Geom.
385 ////////////////////////////////////////////////////////////////////
386 void AssimpLoader::
387 load_mesh(size_t index) {
388  const aiMesh &mesh = *_scene->mMeshes[index];
389 
390  // Create the vertex format.
391  PT(GeomVertexArrayFormat) aformat = new GeomVertexArrayFormat;
392  aformat->add_column(InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point);
393  if (mesh.HasNormals()) {
394  aformat->add_column(InternalName::get_normal(), 3, Geom::NT_stdfloat, Geom::C_vector);
395  }
396  if (mesh.HasVertexColors(0)) {
397  aformat->add_column(InternalName::get_color(), 4, Geom::NT_stdfloat, Geom::C_color);
398  }
399  unsigned int num_uvs = mesh.GetNumUVChannels();
400  if (num_uvs > 0) {
401  // UV sets are named texcoord, texcoord.1, texcoord.2...
402  aformat->add_column(InternalName::get_texcoord(), 3, Geom::NT_stdfloat, Geom::C_texcoord);
403  for (unsigned int u = 1; u < num_uvs; ++u) {
404  ostringstream out;
405  out << u;
406  aformat->add_column(InternalName::get_texcoord_name(out.str()), 3, Geom::NT_stdfloat, Geom::C_texcoord);
407  }
408  }
409  //TODO: if there is only one UV set, hackily iterate over the texture stages and clear the texcoord name things
410 
411  PT(GeomVertexFormat) format = new GeomVertexFormat;
412  format->add_array(aformat);
413 
414  // Create the GeomVertexData.
415  string name (mesh.mName.data, mesh.mName.length);
416  PT(GeomVertexData) vdata = new GeomVertexData(name, GeomVertexFormat::register_format(format), Geom::UH_static);
417  vdata->unclean_set_num_rows(mesh.mNumVertices);
418 
419  // Read out the vertices.
420  GeomVertexWriter vertex (vdata, InternalName::get_vertex());
421  for (size_t i = 0; i < mesh.mNumVertices; ++i) {
422  const aiVector3D &vec = mesh.mVertices[i];
423  vertex.add_data3(vec.x, vec.y, vec.z);
424  }
425 
426  // Now the normals, if any.
427  if (mesh.HasNormals()) {
428  GeomVertexWriter normal (vdata, InternalName::get_normal());
429  for (size_t i = 0; i < mesh.mNumVertices; ++i) {
430  const aiVector3D &vec = mesh.mNormals[i];
431  normal.add_data3(vec.x, vec.y, vec.z);
432  }
433  }
434 
435  // Vertex colors, if any. We only import the first set.
436  if (mesh.HasVertexColors(0)) {
437  GeomVertexWriter color (vdata, InternalName::get_color());
438  for (size_t i = 0; i < mesh.mNumVertices; ++i) {
439  const aiColor4D &col = mesh.mColors[0][i];
440  color.add_data4(col.r, col.g, col.b, col.a);
441  }
442  }
443 
444  // Now the texture coordinates.
445  if (num_uvs > 0) {
446  // UV sets are named texcoord, texcoord.1, texcoord.2...
447  GeomVertexWriter texcoord0 (vdata, InternalName::get_texcoord());
448  for (size_t i = 0; i < mesh.mNumVertices; ++i) {
449  const aiVector3D &vec = mesh.mTextureCoords[0][i];
450  texcoord0.add_data3(vec.x, vec.y, vec.z);
451  }
452  for (unsigned int u = 1; u < num_uvs; ++u) {
453  ostringstream out;
454  out << u;
455  GeomVertexWriter texcoord (vdata, InternalName::get_texcoord_name(out.str()));
456  for (size_t i = 0; i < mesh.mNumVertices; ++i) {
457  const aiVector3D &vec = mesh.mTextureCoords[u][i];
458  texcoord.add_data3(vec.x, vec.y, vec.z);
459  }
460  }
461  }
462 
463  // Now read out the primitives.
464  // Keep in mind that we called ReadFile with the aiProcess_Triangulate
465  // flag earlier, so we don't have to worry about polygons.
466  PT(GeomPoints) points = new GeomPoints(Geom::UH_static);
467  PT(GeomLines) lines = new GeomLines(Geom::UH_static);
468  PT(GeomTriangles) triangles = new GeomTriangles(Geom::UH_static);
469 
470  // Now add the vertex indices.
471  for (size_t i = 0; i < mesh.mNumFaces; ++i) {
472  const aiFace &face = mesh.mFaces[i];
473 
474  if (face.mNumIndices == 0) {
475  // It happens, strangely enough.
476  continue;
477  } else if (face.mNumIndices == 1) {
478  points->add_vertex(face.mIndices[0]);
479  points->close_primitive();
480  } else if (face.mNumIndices == 2) {
481  lines->add_vertices(face.mIndices[0], face.mIndices[1]);
482  lines->close_primitive();
483  } else if (face.mNumIndices == 3) {
484  triangles->add_vertices(face.mIndices[0], face.mIndices[1], face.mIndices[2]);
485  triangles->close_primitive();
486  } else {
487  nassertd(false) continue;
488  }
489  }
490 
491  // Create a geom and add the primitives to it.
492  PT(Geom) geom = new Geom(vdata);
493  if (points->get_num_primitives() > 0) {
494  geom->add_primitive(points);
495  }
496  if (lines->get_num_primitives() > 0) {
497  geom->add_primitive(lines);
498  }
499  if (triangles->get_num_primitives() > 0) {
500  geom->add_primitive(triangles);
501  }
502 
503  _geoms[index] = geom;
504  _geom_matindices[index] = mesh.mMaterialIndex;
505 }
506 
507 ////////////////////////////////////////////////////////////////////
508 // Function: AssimpLoader::load_node
509 // Access: Private
510 // Description: Converts an aiNode into a PandaNode.
511 ////////////////////////////////////////////////////////////////////
512 void AssimpLoader::
513 load_node(const aiNode &node, PandaNode *parent) {
514  PT(PandaNode) pnode;
515 
516  // Create the node and give it a name.
517  string name (node.mName.data, node.mName.length);
518  if (node.mNumMeshes > 0) {
519  pnode = new GeomNode(name);
520  } else {
521  pnode = new PandaNode(name);
522  }
523  parent->add_child(pnode);
524 
525  // Load in the transformation matrix.
526  const aiMatrix4x4 &t = node.mTransformation;
527  if (!t.IsIdentity()) {
528  LMatrix4 mat(t.a1, t.b1, t.c1, t.d1,
529  t.a2, t.b2, t.c2, t.d2,
530  t.a3, t.b3, t.c3, t.d3,
531  t.a4, t.b4, t.c4, t.d4);
532  pnode->set_transform(TransformState::make_mat(mat));
533  }
534 
535  for (size_t i = 0; i < node.mNumChildren; ++i) {
536  load_node(*node.mChildren[i], pnode);
537  }
538 
539  if (node.mNumMeshes > 0) {
540  // Remember, we created this as GeomNode earlier.
541  PT(GeomNode) gnode = DCAST(GeomNode, pnode);
542  size_t meshIndex;
543 
544  // If there's only mesh, don't bother using a per-geom state.
545  if (node.mNumMeshes == 1) {
546  meshIndex = node.mMeshes[0];
547  gnode->add_geom(_geoms[meshIndex]);
548  gnode->set_state(_mat_states[_geom_matindices[meshIndex]]);
549 
550  } else {
551  for (size_t i = 0; i < node.mNumMeshes; ++i) {
552  meshIndex = node.mMeshes[i];
553  gnode->add_geom(_geoms[node.mMeshes[i]],
554  _mat_states[_geom_matindices[meshIndex]]);
555  }
556  }
557  }
558 }
559 
560 ////////////////////////////////////////////////////////////////////
561 // Function: AssimpLoader::load_light
562 // Access: Private
563 // Description: Converts an aiLight into a LightNode.
564 ////////////////////////////////////////////////////////////////////
565 void AssimpLoader::
566 load_light(const aiLight &light) {
567  string name (light.mName.data, light.mName.length);
568  assimp_cat.debug() << "Found light '" << name << "'\n";
569 
570  aiColor3D col;
571  aiVector3D vec;
572 
573  PT(LightNode) lnode;
574 
575  switch (light.mType) {
576  case aiLightSource_DIRECTIONAL: {
577  PT(DirectionalLight) dlight = new DirectionalLight(name);
578  lnode = DCAST(LightNode, dlight);
579 
580  col = light.mColorSpecular;
581  dlight->set_specular_color(LColor(col.r, col.g, col.b, 1));
582 
583  vec = light.mPosition;
584  dlight->set_point(LPoint3(vec.x, vec.y, vec.z));
585 
586  vec = light.mDirection;
587  dlight->set_direction(LVector3(vec.x, vec.y, vec.z));
588  break; }
589 
590  case aiLightSource_POINT: {
591  PT(PointLight) plight = new PointLight(name);
592  lnode = DCAST(LightNode, plight);
593 
594  col = light.mColorSpecular;
595  plight->set_specular_color(LColor(col.r, col.g, col.b, 1));
596 
597  vec = light.mPosition;
598  plight->set_point(LPoint3(vec.x, vec.y, vec.z));
599 
600  plight->set_attenuation(LVecBase3(light.mAttenuationConstant,
601  light.mAttenuationLinear,
602  light.mAttenuationQuadratic));
603  break; }
604 
605  case aiLightSource_SPOT: {
606  PT(Spotlight) plight = new Spotlight(name);
607  lnode = DCAST(LightNode, plight);
608 
609  col = light.mColorSpecular;
610  plight->set_specular_color(LColor(col.r, col.g, col.b, 1));
611 
612  plight->set_attenuation(LVecBase3(light.mAttenuationConstant,
613  light.mAttenuationLinear,
614  light.mAttenuationQuadratic));
615 
616  plight->get_lens()->set_fov(light.mAngleOuterCone);
617  //TODO: translate mAngleInnerCone to an exponent, somehow
618 
619  // This *should* be about right.
620  vec = light.mDirection;
621  LPoint3 pos (light.mPosition.x, light.mPosition.y, light.mPosition.z);
622  LQuaternion quat;
623  ::look_at(quat, LPoint3(vec.x, vec.y, vec.z), LVector3::up());
624  plight->set_transform(TransformState::make_pos_quat_scale(pos, quat, LVecBase3(1, 1, 1)));
625  break; }
626 
627  default:
628  assimp_cat.warning() << "Light '" << name << "' has an unknown type!\n";
629  return;
630  }
631 
632  // If there's an ambient color, add it as ambient light.
633  LVecBase4 ambient (col.r, col.g, col.b, 0);
634  if (ambient != LVecBase4::zero()) {
635  PT(AmbientLight) alight = new AmbientLight(name);
636  col = light.mColorAmbient;
637  alight->set_color(ambient);
638  _root->add_child(alight);
639  }
640 
641  _root->add_child(lnode);
642  col = light.mColorDiffuse;
643  lnode->set_color(LColor(col.r, col.g, col.b, 1));
644 }
A node of this type is created automatically at the root of each model file that is loaded...
Definition: modelRoot.h:31
bool read(const Filename &filename)
Reads from the indicated file.
A light shining from infinitely far away in a particular direction, like sunlight.
PNMFileType * get_type_from_extension(const string &filename) const
Tries to determine what the PNMFileType is likely to be for a particular image file based on its exte...
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
The abstract interface to all kinds of lights.
Definition: light.h:42
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
void set_diffuse(const LColor &color)
Specifies the diffuse color setting of the material.
Definition: material.cxx:80
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:68
Defines a series of disconnected points.
Definition: geomPoints.h:25
A derivative of Light and of PandaNode.
Definition: lightNode.h:29
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 build_graph()
Converts scene graph structures into a Panda3D scene graph, with _root being the root node...
bool read(const Filename &filename, PNMFileType *type=NULL, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:245
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:75
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:35
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
bool make_canonical()
Converts this filename to a canonical name by replacing the directory part with the fully-qualified d...
Definition: filename.cxx:1072
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
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:29
string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:424
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Definition: textureAttrib.h:34
void get_extensions(string &ext) const
Returns a space-separated list of extensions that Assimp can load, without the leading dots...
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
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
A container for geometry primitives.
Definition: geom.h:58
A light originating from a single point in space, and shining in a particular direction, with a cone-shaped falloff.
Definition: spotlight.h:37
static Texture * load_texture(const Filename &filename, int primary_file_num_channels=0, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads the given filename up into a texture, if it has not already been loaded, and returns the new te...
Definition: texturePool.I:58
bool is_regular_file(const Filename &filename) const
Convenience function; returns true if the named file exists and is a regular file.
Defines the way an object appears in the presence of lighting.
Definition: material.h:34
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
Defines a series of disconnected line segments.
Definition: geomLines.h:25
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
This class maintains the set of all known PNMFileTypes in the universe.
string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:436
This is the base quaternion class.
Definition: lquaternion.h:96
Defines a series of disconnected triangles.
Definition: geomTriangles.h:25
void set_texcoord_name(InternalName *name)
Indicate which set of UV&#39;s this texture stage will use.
Definition: textureStage.I:125
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
Defines the properties of a named stage of the multitexture pipeline.
Definition: textureStage.h:38
static void set_default()
Makes sure there&#39;s a global PandaLogger object and makes sure that it is Assimp&#39;s default logger...
Definition: pandaLogger.cxx:28
Custom implementation of Assimp::IOSystem.
Definition: pandaIOSystem.h:27
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
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).
Definition: filename.cxx:332