Panda3D
 All Classes Functions Variables Enumerations
daeToEggConverter.cxx
1 // Filename: daeToEggConverter.cxx
2 // Created by: pro-rsoft (08May08)
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 "daeToEggConverter.h"
16 #include "fcollada_utils.h"
17 #include "config_daeegg.h"
18 #include "daeCharacter.h"
19 #include "dcast.h"
20 #include "string_utils.h"
21 #include "eggData.h"
22 #include "eggPrimitive.h"
23 #include "eggLine.h"
24 #include "eggPolygon.h"
25 #include "eggTriangleFan.h"
26 #include "eggTriangleStrip.h"
27 #include "eggPoint.h"
28 #include "eggXfmSAnim.h"
29 #include "eggSAnimData.h"
30 #include "pt_EggVertex.h"
31 
32 #include "FCDocument/FCDAsset.h"
33 #include "FCDocument/FCDocumentTools.h"
34 #include "FCDocument/FCDSceneNode.h"
35 #include "FCDocument/FCDSceneNodeTools.h"
36 #include "FCDocument/FCDGeometry.h"
37 #include "FCDocument/FCDGeometryInstance.h"
38 #include "FCDocument/FCDGeometryPolygons.h"
39 #include "FCDocument/FCDGeometrySource.h"
40 #include "FCDocument/FCDSkinController.h"
41 #include "FCDocument/FCDController.h"
42 #include "FCDocument/FCDControllerInstance.h"
43 #include "FCDocument/FCDMorphController.h"
44 #include "FCDocument/FCDMaterialInstance.h"
45 #include "FCDocument/FCDExtra.h"
46 #include "FCDocument/FCDEffect.h"
47 #include "FCDocument/FCDEffectStandard.h"
48 #if FCOLLADA_VERSION >= 0x00030005
49  #include "FCDocument/FCDGeometryPolygonsInput.h"
50 #endif
51 
52 ////////////////////////////////////////////////////////////////////
53 // Function: DAEToEggConverter::Constructor
54 // Access: Public
55 // Description:
56 ////////////////////////////////////////////////////////////////////
57 DAEToEggConverter::
58 DAEToEggConverter() {
59  _unit_name = "meter";
60  _unit_meters = 1.0;
61  _document = NULL;
62  _table = NULL;
63  _error_handler = NULL;
64  _invert_transparency = false;
65 }
66 
67 ////////////////////////////////////////////////////////////////////
68 // Function: DAEToEggConverter::Copy Constructor
69 // Access: Public
70 // Description:
71 ////////////////////////////////////////////////////////////////////
72 DAEToEggConverter::
73 DAEToEggConverter(const DAEToEggConverter &copy) :
75 {
76 }
77 
78 ////////////////////////////////////////////////////////////////////
79 // Function: DAEToEggConverter::Destructor
80 // Access: Public
81 // Description:
82 ////////////////////////////////////////////////////////////////////
83 DAEToEggConverter::
84 ~DAEToEggConverter() {
85  if (_error_handler != NULL) {
86  delete _error_handler;
87  }
88 }
89 
90 ////////////////////////////////////////////////////////////////////
91 // Function: DAEToEggConverter::make_copy
92 // Access: Public, Virtual
93 // Description: Allocates and returns a new copy of the converter.
94 ////////////////////////////////////////////////////////////////////
97  return new DAEToEggConverter(*this);
98 }
99 
100 
101 ////////////////////////////////////////////////////////////////////
102 // Function: DAEToEggConverter::get_name
103 // Access: Public, Virtual
104 // Description: Returns the English name of the file type this
105 // converter supports.
106 ////////////////////////////////////////////////////////////////////
107 string DAEToEggConverter::
108 get_name() const {
109  return "COLLADA";
110 }
111 
112 ////////////////////////////////////////////////////////////////////
113 // Function: DAEToEggConverter::get_extension
114 // Access: Public, Virtual
115 // Description: Returns the common extension of the file type this
116 // converter supports.
117 ////////////////////////////////////////////////////////////////////
118 string DAEToEggConverter::
119 get_extension() const {
120  return "dae";
121 }
122 
123 ////////////////////////////////////////////////////////////////////
124 // Function: DAEToEggConverter::convert_file
125 // Access: Public, Virtual
126 // Description: Handles the reading of the input file and converting
127 // it to egg. Returns true if successful, false
128 // otherwise.
129 ////////////////////////////////////////////////////////////////////
131 convert_file(const Filename &filename) {
132  // Reset stuff
133  clear_error();
134  _joints.clear();
135  if (_error_handler == NULL) {
136  _error_handler = new FUErrorSimpleHandler;
137  }
138 
139  // The default coordinate system is Y-up
140  if (_egg_data->get_coordinate_system() == CS_default) {
141  _egg_data->set_coordinate_system(CS_yup_right);
142  }
143 
144  // Read the file
145  FCollada::Initialize();
146  _document = FCollada::LoadDocument(filename.to_os_specific().c_str());
147  if (_document == NULL) {
148  daeegg_cat.error() << "Failed to load document: " << _error_handler->GetErrorString() << endl;
149  FCollada::Release();
150  return false;
151  }
152  // Make sure the file uses consistent coordinate system and length
153  if (_document->GetAsset() != NULL) {
154  FCDocumentTools::StandardizeUpAxisAndLength(_document);
155  }
156 
157  // Process the scene
158  process_asset();
159  PT(EggGroup) scene_group;
160  string model_name = _character_name;
161 
162  FCDSceneNode* visual_scene = _document->GetVisualSceneInstance();
163  if (visual_scene != NULL) {
164  if (model_name.empty()) {
165  // By lack of anything better...
166  model_name = FROM_FSTRING(visual_scene->GetName());
167  }
168  scene_group = new EggGroup(model_name);
169  _egg_data->add_child(scene_group);
170 
171  for (size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
172  process_node(scene_group, visual_scene->GetChild(ch));
173  }
174  } else {
175  daeegg_cat.warning()
176  << "No visual scene instance found in COLLADA document.\n";
177  }
178 
179  // Now process the characters. This depends on information from collected
180  // joints, which is why it's done in a second step.
181  if (get_animation_convert() != AC_none) {
182  Characters::iterator it;
183  DaeCharacter *character;
184  for (it = _characters.begin(); it != _characters.end(); ++it) {
185  character = *it;
186  if (get_animation_convert() != AC_chan) {
187  character->bind_joints(_joints);
188 
189  const FCDGeometryMesh *mesh = character->_skin_mesh;
190 
191  if (mesh != NULL) {
192  PT(DaeMaterials) materials = new DaeMaterials(character->_instance);
193  daeegg_cat.spam() << "Processing mesh for controller\n";
194  process_mesh(character->_node_group, mesh, materials, character);
195  }
196  }
197  }
198 
199  // Put the joints in bind pose.
200  for (size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
201  character->adjust_joints(visual_scene->GetChild(ch), _joints, LMatrix4d::ident_mat());
202  }
203 
204  if (scene_group != NULL) {
205  // Mark the scene as character.
206  if (get_animation_convert() == AC_chan) {
207  _egg_data->remove_child(scene_group);
208  } else {
209  scene_group->set_dart_type(EggGroup::DT_default);
210  }
211  }
212 
213  if (get_animation_convert() != AC_model) {
214  _table = new EggTable();
215  _table->set_table_type(EggTable::TT_table);
216  _egg_data->add_child(_table);
217 
218  PT(EggTable) bundle = new EggTable(model_name);
219  bundle->set_table_type(EggTable::TT_bundle);
220  _table->add_child(bundle);
221 
222  PT(EggTable) skeleton = new EggTable("<skeleton>");
223  skeleton->set_table_type(EggTable::TT_table);
224  bundle->add_child(skeleton);
225 
226  pset<float> keys;
227 
228  Characters::iterator it;
229  DaeCharacter *character;
230  for (it = _characters.begin(); it != _characters.end(); ++it) {
231  character = *it;
232 
233  // Collect key frame timings.
234  if (get_animation_convert() == AC_both ||
235  get_animation_convert() == AC_chan) {
236  character->collect_keys(keys);
237  }
238  }
239 
240  if (_frame_inc != 0.0) {
241  // A frame increment was given, this means that we have to sample the
242  // animation.
243  float start, end;
244  if (_end_frame != _start_frame) {
245  start = _start_frame;
246  end = _end_frame;
247  } else {
248  // No range was given. Infer the frame range from the keys.
249  start = *keys.begin();
250  end = *keys.rbegin();
251  }
252  keys.clear();
253 
254  for (float t = start; t <= end; t += _frame_inc) {
255  keys.insert(t);
256  }
257  } else {
258  // No sampling parameters given; not necessarily a failure, since the
259  // animation may already be sampled. We use the key frames as animation
260  // frames.
261  if (_end_frame != 0.0) {
262  // An end frame was given, chop off all keys after that.
263  float end = _end_frame;
265  for (ki = keys.begin(); ki != keys.end(); ++ki) {
266  if (*ki > end && !IS_THRESHOLD_EQUAL(*ki, end, 0.001)) {
267  keys.erase(ki, keys.end());
268  break;
269  }
270  }
271  }
272  if (_start_frame != 0.0) {
273  // A start frame was given, chop off all keys before that.
274  float start = _start_frame;
276  for (ki = keys.begin(); ki != keys.end(); ++ki) {
277  if (*ki > start && !IS_THRESHOLD_EQUAL(*ki, start, 0.001)) {
278  keys.erase(keys.begin(), ki);
279  break;
280  }
281  }
282  }
283 
284  // Check that this does indeed look like a sampled animation; if not,
285  // issue an appropriate warning.
286  pset<float>::const_iterator ki = keys.begin();
287  if (ki != keys.end()) {
288  float last = *ki;
289  float diff = 0;
290 
291  for (++ki; ki != keys.end(); ++ki) {
292  if (diff != 0 && !IS_THRESHOLD_EQUAL((*ki - last), diff, 0.001)) {
293  daeegg_cat.error()
294  << "This does not appear to be a sampled animation.\n"
295  << "Specify the -sf, -ef and -if options to indicate how the "
296  << "animations should be sampled.\n";
297  break;
298  }
299  diff = (*ki - last);
300  last = *ki;
301  }
302  }
303  }
304 
305  // It doesn't really matter which character we grab for this as
306  // it'll iterate over the whole graph right now anyway.
307  for (size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
308  character->build_table(skeleton, visual_scene->GetChild(ch), keys);
309  }
310  }
311  }
312 
313  // Clean up and return
314  SAFE_DELETE(visual_scene);
315  SAFE_DELETE(_document);
316  FCollada::Release();
317  return true;
318 }
319 
320 ////////////////////////////////////////////////////////////////////
321 // Function: DAEToEggConverter::get_input_units
322 // Access: Public, Virtual
323 // Description: This may be called after convert_file() has been
324 // called and returned true, indicating a successful
325 // conversion. It will return the distance units
326 // represented by the converted egg file, if known, or
327 // DU_invalid if not known.
328 ////////////////////////////////////////////////////////////////////
329 DistanceUnit DAEToEggConverter::
331  if (IS_NEARLY_EQUAL(_unit_meters, 0.001)) {
332  return DU_millimeters;
333  }
334  if (IS_NEARLY_EQUAL(_unit_meters, 0.01)) {
335  return DU_centimeters;
336  }
337  if (IS_NEARLY_EQUAL(_unit_meters, 1.0)) {
338  return DU_meters;
339  }
340  if (IS_NEARLY_EQUAL(_unit_meters, 1000.0)) {
341  return DU_kilometers;
342  }
343  if (IS_NEARLY_EQUAL(_unit_meters, 3.0 * 12.0 * 0.0254)) {
344  return DU_yards;
345  }
346  if (IS_NEARLY_EQUAL(_unit_meters, 12.0 * 0.0254)) {
347  return DU_feet;
348  }
349  if (IS_NEARLY_EQUAL(_unit_meters, 0.0254)) {
350  return DU_inches;
351  }
352  if (IS_NEARLY_EQUAL(_unit_meters, 1852.0)) {
353  return DU_nautical_miles;
354  }
355  if (IS_NEARLY_EQUAL(_unit_meters, 5280.0 * 12.0 * 0.0254)) {
356  return DU_statute_miles;
357  }
358 
359  // Whatever.
360  return DU_invalid;
361 }
362 
363 void DAEToEggConverter::
364 process_asset() {
365  const FCDAsset *asset = _document->GetAsset();
366  if (_document->GetAsset() == NULL) {
367  return;
368  }
369 
370  _unit_name = FROM_FSTRING(asset->GetUnitName());
371  _unit_meters = asset->GetUnitConversionFactor();
372 
373  // Read out the coordinate system
374  FMVector3 up_axis = asset->GetUpAxis();
375 
376  if (up_axis == FMVector3(0, 1, 0)) {
377  _egg_data->set_coordinate_system(CS_yup_right);
378 
379  } else if (up_axis == FMVector3(0, 0, 1)) {
380  _egg_data->set_coordinate_system(CS_zup_right);
381 
382  } else {
383  _egg_data->set_coordinate_system(CS_invalid);
384  daeegg_cat.warning() << "Unrecognized coordinate system!\n";
385  }
386 }
387 
388 // Process the node. If forced is true, it will even process it if its known to be a skeleton root.
389 void DAEToEggConverter::
390 process_node(EggGroupNode *parent, const FCDSceneNode* node, bool forced) {
391  nassertv(node != NULL);
392  string node_id = FROM_FSTRING(node->GetDaeId());
393  daeegg_cat.spam() << "Processing node with ID '" << node_id << "'" << endl;
394 
395  // Create an egg group for this node
396  PT(EggGroup) node_group = new EggGroup(FROM_FSTRING(node->GetDaeId()));
397  process_extra(node_group, node->GetExtra());
398  parent->add_child(node_group);
399 
400  // Check if its a joint
401  if (node->IsJoint()) {
402  string sid = FROM_FSTRING(node->GetSubId());
403  node_group->set_group_type(EggGroup::GT_joint);
404 
405  if (!_joints.insert(DaeCharacter::JointMap::value_type(sid,
406  DaeCharacter::Joint(node_group, node))).second) {
407  daeegg_cat.error()
408  << "Joint with sid " << sid << " occurs more than once!\n";
409  }
410  }
411 
412  // Loop through the transforms and apply them (in reverse order)
413  for (size_t tr = node->GetTransformCount(); tr > 0; --tr) {
414  apply_transform(node_group, node->GetTransform(tr - 1));
415  }
416  //node_group->set_transform3d(convert_matrix(node->ToMatrix()));
417 
418  // Loop through the instances and process them
419  for (size_t in = 0; in < node->GetInstanceCount(); ++in) {
420  process_instance(node_group, node->GetInstance(in));
421  }
422 
423  // Loop through the children and recursively process them
424  for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
425  process_node(DCAST(EggGroupNode, node_group), node->GetChild(ch));
426  }
427 
428  // Loop through any possible scene node instances and process those, too.
429  for (size_t in = 0; in < node->GetInstanceCount(); ++in) {
430  const FCDEntity *entity = node->GetInstance(in)->GetEntity();
431  if (entity && entity->GetType() == FCDEntity::SCENE_NODE) {
432  process_node(node_group, (const FCDSceneNode*) entity);
433  }
434  }
435 }
436 
437 void DAEToEggConverter::
438 process_instance(EggGroup *parent, const FCDEntityInstance* instance) {
439  nassertv(instance != NULL);
440  nassertv(instance->GetEntity() != NULL);
441  // Check what kind of instance this is
442  switch (instance->GetType()) {
443  case FCDEntityInstance::GEOMETRY:
444  {
445  if (get_animation_convert() != AC_chan) {
446  const FCDGeometry* geometry = (const FCDGeometry*) instance->GetEntity();
447  assert(geometry != NULL);
448  if (geometry->IsMesh()) {
449  // Now, handle the mesh.
450  process_mesh(parent, geometry->GetMesh(), new DaeMaterials((const FCDGeometryInstance*) instance));
451  }
452  if (geometry->IsSpline()) {
453  process_spline(parent, FROM_FSTRING(geometry->GetName()), const_cast<FCDGeometrySpline*> (geometry->GetSpline()));
454  }
455  }
456  }
457  break;
458 
459  case FCDEntityInstance::CONTROLLER:
460  // Add the dart tag and process the controller instance
461  //parent->set_dart_type(EggGroup::DT_default);
462  process_controller(parent, (const FCDControllerInstance*) instance);
463  break;
464 
465  case FCDEntityInstance::MATERIAL:
466  // We don't process this directly, handled per-geometry instead.
467  break;
468 
469  case FCDEntityInstance::SIMPLE:
470  {
471  // Grab the entity and check its type.
472  const FCDEntity* entity = instance->GetEntity();
473  if (entity->GetType() != FCDEntity::SCENE_NODE) {
474  daeegg_cat.warning() << "Unsupported entity type found" << endl;
475  }
476  }
477  break;
478 
479  default:
480  daeegg_cat.warning() << "Unsupported instance type found" << endl;
481  }
482 }
483 
484 // Processes the given mesh.
485 void DAEToEggConverter::
486 process_mesh(EggGroup *parent, const FCDGeometryMesh* mesh,
487  DaeMaterials *materials, DaeCharacter *character) {
488 
489  nassertv(mesh != NULL);
490  daeegg_cat.debug() << "Processing mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << endl;
491 
492  // Create the egg stuff to hold this mesh
493  PT(EggGroup) mesh_group = new EggGroup(FROM_FSTRING(mesh->GetDaeId()));
494  parent->add_child(mesh_group);
495  PT(EggVertexPool) mesh_pool = new EggVertexPool(FROM_FSTRING(mesh->GetDaeId()));
496  mesh_group->add_child(mesh_pool);
497 
498  // First retrieve the vertex source
499  if (mesh->GetSourceCount() == 0) {
500  daeegg_cat.debug() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has no sources" << endl;
501  return;
502  }
503  const FCDGeometrySource* vsource = mesh->FindSourceByType(FUDaeGeometryInput::POSITION);
504  if (vsource == NULL) {
505  daeegg_cat.debug() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has no source for POSITION data" << endl;
506  return;
507  }
508 
509  // Loop through the polygon groups and add them
510  daeegg_cat.spam() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has " << mesh->GetPolygonsCount() << " polygon groups" << endl;
511  if (mesh->GetPolygonsCount() == 0) return;
512 
513  // This is an array of pointers, I know. But since they are refcounted, I don't have a better idea.
514  PT(EggGroup) *primitive_holders = new PT(EggGroup) [mesh->GetPolygonsCount()];
515  for (size_t gr = 0; gr < mesh->GetPolygonsCount(); ++gr) {
516  const FCDGeometryPolygons* polygons = mesh->GetPolygons(gr);
517  string material_semantic = FROM_FSTRING(polygons->GetMaterialSemantic());
518 
519  // Stores which group holds the primitives.
520  PT(EggGroup) primitiveholder;
521  // If we have materials, make a group for each material. Then, apply the material's per-group stuff.
522  if (materials != NULL && (!polygons->GetMaterialSemantic().empty()) && mesh->GetPolygonsCount() > 1) {
523  //primitiveholder = new EggGroup(FROM_FSTRING(mesh->GetDaeId()) + "." + material_semantic);
524  primitiveholder = new EggGroup;
525  mesh_group->add_child(primitiveholder);
526  } else {
527  primitiveholder = mesh_group;
528  }
529  primitive_holders[gr] = primitiveholder;
530  // Apply the per-group data of the materials, if we have it.
531  if (materials != NULL) {
532  materials->apply_to_group(material_semantic, primitiveholder, _invert_transparency);
533  }
534  // Find the position sources
535  const FCDGeometryPolygonsInput* pinput = polygons->FindInput(FUDaeGeometryInput::POSITION);
536  assert(pinput != NULL);
537  const uint32* indices = pinput->GetIndices();
538  // Find the normal sources
539  const FCDGeometrySource* nsource = mesh->FindSourceByType(FUDaeGeometryInput::NORMAL);
540  const FCDGeometryPolygonsInput* ninput = polygons->FindInput(FUDaeGeometryInput::NORMAL);
541  const uint32* nindices;
542  if (ninput != NULL) nindices = ninput->GetIndices();
543  // Find texcoord sources
544  const FCDGeometrySource* tcsource = mesh->FindSourceByType(FUDaeGeometryInput::TEXCOORD);
545  const FCDGeometryPolygonsInput* tcinput = polygons->FindInput(FUDaeGeometryInput::TEXCOORD);
546  const uint32* tcindices;
547  if (tcinput != NULL) tcindices = tcinput->GetIndices();
548  // Find vcolor sources
549  const FCDGeometrySource* csource = mesh->FindSourceByType(FUDaeGeometryInput::COLOR);
550  const FCDGeometryPolygonsInput* cinput = polygons->FindInput(FUDaeGeometryInput::COLOR);
551  const uint32* cindices;
552  if (cinput != NULL) cindices = cinput->GetIndices();
553  // Find binormal sources
554  const FCDGeometrySource* bsource = mesh->FindSourceByType(FUDaeGeometryInput::TEXBINORMAL);
555  const FCDGeometryPolygonsInput* binput = polygons->FindInput(FUDaeGeometryInput::TEXBINORMAL);
556  const uint32* bindices;
557  if (binput != NULL) bindices = binput->GetIndices();
558  // Find tangent sources
559  const FCDGeometrySource* tsource = mesh->FindSourceByType(FUDaeGeometryInput::TEXTANGENT);
560  const FCDGeometryPolygonsInput* tinput = polygons->FindInput(FUDaeGeometryInput::TEXTANGENT);
561  const uint32* tindices;
562  if (tinput != NULL) tindices = tinput->GetIndices();
563  // Get a name for potential coordinate sets
564  string tcsetname;
565  if (materials != NULL && tcinput != NULL) {
566  if (daeegg_cat.is_debug()) {
567  daeegg_cat.debug()
568  << "Assigning texcoord set " << tcinput->GetSet()
569  << " to semantic '" << material_semantic << "'\n";
570  }
571  tcsetname = materials->get_uvset_name(material_semantic,
572  FUDaeGeometryInput::TEXCOORD, tcinput->GetSet());
573  }
574  string tbsetname;
575  if (materials != NULL && binput != NULL) {
576  if (daeegg_cat.is_debug()) {
577  daeegg_cat.debug()
578  << "Assigning texbinormal set " << binput->GetSet()
579  << " to semantic '" << material_semantic << "'\n";
580  }
581  tbsetname = materials->get_uvset_name(material_semantic,
582  FUDaeGeometryInput::TEXBINORMAL, binput->GetSet());
583  }
584  string ttsetname;
585  if (materials != NULL && tinput != NULL) {
586  if (daeegg_cat.is_debug()) {
587  daeegg_cat.debug()
588  << "Assigning textangent set " << tinput->GetSet()
589  << " to semantic '" << material_semantic << "'\n";
590  }
591  ttsetname = materials->get_uvset_name(material_semantic,
592  FUDaeGeometryInput::TEXTANGENT, tinput->GetSet());
593  }
594  // Loop through the indices and add the vertices.
595  for (size_t ix = 0; ix < pinput->GetIndexCount(); ++ix) {
596  PT_EggVertex vertex = mesh_pool->make_new_vertex();
597  const float* data = &vsource->GetData()[indices[ix]*3];
598  vertex->set_pos(LPoint3d(data[0], data[1], data[2]));
599 
600  if (character != NULL) {
601  // If this is skinned geometry, add the vertex influences.
602  character->influence_vertex(indices[ix], vertex);
603  }
604 
605  // Process the normal
606  if (nsource != NULL && ninput != NULL) {
607  assert(nsource->GetStride() == 3);
608  data = &nsource->GetData()[nindices[ix]*3];
609  vertex->set_normal(LVecBase3d(data[0], data[1], data[2]));
610  }
611  // Process the texcoords
612  if (tcsource != NULL && tcinput != NULL) {
613  assert(tcsource->GetStride() == 2 || tcsource->GetStride() == 3);
614  data = &tcsource->GetData()[tcindices[ix]*tcsource->GetStride()];
615  if (tcsource->GetStride() == 2) {
616  vertex->set_uv(tcsetname, LPoint2d(data[0], data[1]));
617  } else {
618  vertex->set_uvw(tcsetname, LPoint3d(data[0], data[1], data[2]));
619  }
620  }
621  // Process the color
622  if (csource != NULL && cinput != NULL) {
623  assert(csource->GetStride() == 3 || csource->GetStride() == 4);
624  if (csource->GetStride() == 3) {
625  data = &csource->GetData()[cindices[ix]*3];
626  vertex->set_color(LColor(data[0], data[1], data[2], 1.0f));
627  } else {
628  data = &csource->GetData()[cindices[ix]*4];
629  vertex->set_color(LColor(data[0], data[1], data[2], data[3]));
630  }
631  }
632  // Possibly add a UV object
633  if ((bsource != NULL && binput != NULL) || (tsource != NULL && tinput != NULL)) {
634  if (bsource != NULL && binput != NULL) {
635  assert(bsource->GetStride() == 3);
636  data = &bsource->GetData()[bindices[ix]*3];
637  PT(EggVertexUV) uv_obj = vertex->modify_uv_obj(tbsetname);
638  if (uv_obj == NULL) {
639  uv_obj = new EggVertexUV(tbsetname, LTexCoordd());
640  }
641  uv_obj->set_binormal(LVecBase3d(data[0], data[1], data[2]));
642  }
643  if (tsource != NULL && tinput != NULL) {
644  assert(tsource->GetStride() == 3);
645  data = &tsource->GetData()[tindices[ix]*3];
646  PT(EggVertexUV) uv_obj = vertex->modify_uv_obj(ttsetname);
647  if (uv_obj == NULL) {
648  uv_obj = new EggVertexUV(ttsetname, LTexCoordd());
649  }
650  uv_obj->set_tangent(LVecBase3d(data[0], data[1], data[2]));
651  }
652  }
653  vertex->transform(parent->get_node_to_vertex());
654  }
655  }
656  // Loop again for the polygons
657  for (size_t gr = 0; gr < mesh->GetPolygonsCount(); ++gr) {
658  const FCDGeometryPolygons* polygons = mesh->GetPolygons(gr);
659  // Now loop through the faces
660  uint32 offset = 0;
661  for (size_t fa = 0; fa < polygons->GetFaceVertexCountCount(); ++fa) {
662  PT(EggPrimitive) primitive = NULL;
663  // Create a primitive that matches the fcollada type
664  switch (polygons->GetPrimitiveType()) {
665  case FCDGeometryPolygons::LINES:
666  primitive = new EggLine();
667  break;
668  case FCDGeometryPolygons::POLYGONS:
669  primitive = new EggPolygon();
670  break;
671  case FCDGeometryPolygons::TRIANGLE_FANS:
672  primitive = new EggTriangleFan();
673  break;
674  case FCDGeometryPolygons::TRIANGLE_STRIPS:
675  primitive = new EggTriangleStrip();
676  break;
677  case FCDGeometryPolygons::POINTS:
678  primitive = new EggPoint();
679  break;
680  case FCDGeometryPolygons::LINE_STRIPS:
681  daeegg_cat.warning() << "Linestrips not yet supported!" << endl;
682  break;
683  default:
684  daeegg_cat.warning() << "Unsupported primitive type found!" << endl;
685  }
686  if (primitive != NULL) {
687  primitive_holders[gr]->add_child(primitive);
688  if (materials != NULL) {
689  materials->apply_to_primitive(FROM_FSTRING(polygons->GetMaterialSemantic()), primitive);
690  }
691  for (size_t ve = 0; ve < polygons->GetFaceVertexCount(fa); ++ve) {
692  assert(mesh_pool->has_vertex(ve + polygons->GetFaceVertexOffset() + offset));
693  primitive->add_vertex(mesh_pool->get_vertex(ve + polygons->GetFaceVertexOffset() + offset));
694  }
695  }
696  offset += polygons->GetFaceVertexCount(fa);
697  }
698  }
699  delete[] primitive_holders;
700 }
701 
702 void DAEToEggConverter::
703 process_spline(EggGroup *parent, const string group_name, FCDGeometrySpline* geometry_spline) {
704  assert(geometry_spline != NULL);
705  PT(EggGroup) result = new EggGroup(group_name);
706  parent->add_child(result);
707  //TODO: if its not a nurbs, make it convert between the types
708  if (geometry_spline->GetType() != FUDaeSplineType::NURBS) {
709  daeegg_cat.warning() << "Only NURBS curves are supported (yet)!" << endl;
710  } else {
711  // Loop through the splines
712  for (size_t sp = 0; sp < geometry_spline->GetSplineCount(); ++sp) {
713  process_spline(result, geometry_spline->GetSpline(sp));
714  }
715  }
716 }
717 
718 void DAEToEggConverter::
719 process_spline(EggGroup *parent, const FCDSpline* spline) {
720  assert(spline != NULL);
721  nassertv(spline->GetSplineType() == FUDaeSplineType::NURBS);
722  // Now load in the nurbs curve to the egg library
723  PT(EggNurbsCurve) nurbs_curve = new EggNurbsCurve(FROM_FSTRING(spline->GetName()));
724  parent->add_child(nurbs_curve);
725  //TODO: what value is this?
726  nurbs_curve->setup(0, ((const FCDNURBSSpline*) spline)->GetKnotCount());
727  for (size_t kn = 0; kn < ((const FCDNURBSSpline*) spline)->GetKnotCount(); ++kn) {
728  const float* knot = ((const FCDNURBSSpline*) spline)->GetKnot(kn);
729  assert(knot != NULL);
730  nurbs_curve->set_knot(kn, *knot);
731  }
732  for (size_t cv = 0; cv < spline->GetCVCount(); ++cv) {
733  PT_EggVertex c_vtx = new EggVertex();
734  c_vtx->set_pos(TO_VEC3(*spline->GetCV(cv)));
735  c_vtx->transform(parent->get_node_to_vertex());
736  nurbs_curve->add_vertex(c_vtx);
737  }
738 }
739 
740 void DAEToEggConverter::
741 process_controller(EggGroup *parent, const FCDControllerInstance *instance) {
742  assert(instance != NULL);
743  const FCDController* controller = (const FCDController *)instance->GetEntity();
744  assert(controller != NULL);
745 
746  if (get_animation_convert() == AC_none) {
747  // If we're exporting a static mesh, export the base geometry as-is.
748  const FCDGeometryMesh *mesh = controller->GetBaseGeometry()->GetMesh();
749  if (mesh != NULL) {
750  PT(DaeMaterials) materials = new DaeMaterials(instance);
751  daeegg_cat.spam() << "Processing mesh for controller\n";
752  process_mesh(parent, mesh, materials);
753  }
754  } else {
755  // Add a character for this to the table, the mesh is processed later
756  PT(DaeCharacter) character = new DaeCharacter(parent, instance);
757  _characters.push_back(character);
758  }
759 
760  if (controller->IsMorph()) {
761  assert(controller != NULL);
762  const FCDMorphController* morph_controller = controller->GetMorphController();
763  assert(morph_controller != NULL);
764  PT(EggTable) bundle = new EggTable(parent->get_name());
765  bundle->set_table_type(EggTable::TT_bundle);
766  PT(EggTable) morph = new EggTable("morph");
767  morph->set_table_type(EggTable::TT_table);
768  bundle->add_child(morph);
769  // Loop through the morph targets.
770  for (size_t mt = 0; mt < morph_controller->GetTargetCount(); ++mt) {
771  const FCDMorphTarget* morph_target = morph_controller->GetTarget(mt);
772  assert(morph_target != NULL);
773  PT(EggSAnimData) target = new EggSAnimData(FROM_FSTRING(morph_target->GetGeometry()->GetName()));
774  if (morph_target->IsAnimated()) {
775  //TODO
776  } else {
777  target->add_data(morph_target->GetWeight());
778  }
779  morph->add_child(target);
780  }
781  }
782 }
783 
784 void DAEToEggConverter::
785 process_extra(EggGroup *group, const FCDExtra* extra) {
786  if (extra == NULL) {
787  return;
788  }
789  nassertv(group != NULL);
790 
791  const FCDEType* etype = extra->GetDefaultType();
792  if (etype == NULL) {
793  return;
794  }
795 
796  const FCDENode* enode = (const FCDENode*) etype->FindTechnique("PANDA3D");
797  if (enode == NULL) {
798  return;
799  }
800 
801  FCDENodeList tags;
802  enode->FindChildrenNodes("param", tags);
803  for (FCDENodeList::iterator it = tags.begin(); it != tags.end(); ++it) {
804  const FCDEAttribute* attr = (*it)->FindAttribute("sid");
805  if (attr) {
806  group->set_tag(FROM_FSTRING(attr->GetValue()), (*it)->GetContent());
807  }
808  }
809 }
810 
811 LMatrix4d DAEToEggConverter::
812 convert_matrix(const FMMatrix44 &matrix) {
813  return LMatrix4d(
814  matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3],
815  matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3],
816  matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3],
817  matrix[3][0], matrix[3][1], matrix[3][2], matrix[3][3]);
818 }
819 
820 void DAEToEggConverter::
821 apply_transform(EggGroup *to, const FCDTransform* from) {
822  assert(from != NULL);
823  assert(to != NULL);
824  //to->set_transform3d(convert_matrix(from->ToMatrix()) * to->get_transform3d());
825  switch (from->GetType()) {
826  case FCDTransform::TRANSLATION:
827  {
828  const FCDTTranslation *trans = (const FCDTTranslation *)from;
829  to->add_translate3d(TO_VEC3(trans->GetTranslation()));
830  }
831  break;
832 
833  case FCDTransform::ROTATION:
834  {
835  const FCDTRotation *rot = (const FCDTRotation *)from;
836  to->add_rotate3d(rot->GetAngle(), TO_VEC3(rot->GetAxis()));
837  }
838  break;
839 
840  case FCDTransform::SCALE:
841  {
842  const FCDTScale *scale = (const FCDTScale *)from;
843  to->add_scale3d(TO_VEC3(scale->GetScale()));
844  }
845  break;
846 
847  default:
848  // Either a matrix, or something we can't handle.
849  to->add_matrix4(convert_matrix(from->ToMatrix()));
850  break;
851  }
852 }
A base class for any of a number of kinds of geometry primitives: polygons, point lights...
Definition: eggPrimitive.h:51
The set of UV&#39;s that may or may not be assigned to a vertex.
Definition: eggVertexUV.h:32
void add_rotate3d(double angle, const LVector3d &axis)
Appends a 3-d rotation about an arbitrary axis to the current transform.
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:4716
A line segment, or a series of connected line segments, defined by a &lt;Line&gt; entry.
Definition: eggLine.h:27
void add_data(double value)
Adds a single element to the table.
Definition: eggAnimData.I:116
virtual SomethingToEggConverter * make_copy()
Allocates and returns a new copy of the converter.
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:51
virtual string get_extension() const
Returns the common extension of the file type this converter supports.
A connected fan of triangles.
Corresponding to an &lt;S$Anim&gt; entry, this stores a single column of numbers, for instance for a morph ...
Definition: eggSAnimData.h:28
This is a two-component point in space.
Definition: lpoint2.h:411
const LMatrix4d & get_node_to_vertex() const
Returns the transformation matrix suitable for converting vertices in the coordinate space of the nod...
Definition: eggNode.I:223
virtual bool convert_file(const Filename &filename)
Handles the reading of the input file and converting it to egg.
A single point, or a collection of points as defined by a single &lt;PointLight&gt; entry.
Definition: eggPoint.h:27
Class representing an animated character.
Definition: daeCharacter.h:37
This class is seperated from the converter file because otherwise it would get too big and needlessly...
Definition: daeMaterials.h:41
This class supervises the construction of an EggData structure from a DAE file.
The main glue of the egg hierarchy, this corresponds to the &lt;Group&gt;, &lt;Instance&gt;, and &lt;Joint&gt; type nod...
Definition: eggGroup.h:36
void clear_error()
Resets the error flag to the no-error state.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
void influence_vertex(int index, EggVertex *vertex)
Adds the influences for the given vertex.
static const LMatrix4d & ident_mat()
Returns an identity matrix.
Definition: lmatrix.h:5168
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
Definition: eggVertex.h:41
void bind_joints(JointMap &joint_map)
Binds the joints to the character.
virtual DistanceUnit get_input_units()
This may be called after convert_file() has been called and returned true, indicating a successful co...
void apply_to_group(const string semantic, const PT(EggGroup) to, bool invert_transparency=false)
Applies the colorblend stuff to the given EggGroup.
A parametric NURBS curve.
Definition: eggNurbsCurve.h:28
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:1455
AnimationConvert get_animation_convert() const
Returns how source animation will be converted into egg structures.
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
A single polygon.
Definition: eggPolygon.h:26
void add_translate3d(const LVector3d &translate)
Appends a 3-d translation operation to the current transform.
void set_tag(const string &key, const string &value)
Associates a user-defined value with a user-defined key which is stored on the node.
Definition: eggGroup.I:883
This corresponds to a.
Definition: eggTable.h:31
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:531
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
void add_scale3d(const LVecBase3d &scale)
Appends a possibly non-uniform scale to the current transform.
This is our own Panda specialization on the default STL set.
Definition: pset.h:52
const string get_uvset_name(const string semantic, FUDaeGeometryInput::Semantic input_semantic, int32 input_set)
Returns the semantic of the uvset with the specified input set, or an empty string if the given mater...
void build_table(EggTable *parent, FCDSceneNode *node, const pset< float > &keys)
Processes a joint node and its transforms.
A connected strip of triangles.
void add_matrix4(const LMatrix4d &mat)
Appends an arbitrary 4x4 matrix to the current transform.
Definition: eggTransform.I:149
This is a base class for a family of converter classes that manage a conversion from some file type t...
void apply_to_primitive(const string semantic, const PT(EggPrimitive) to)
Applies the stuff to the given EggPrimitive.
void collect_keys(pset< float > &keys)
Collects all animation keys of animations applied to this character.
A collection of vertices.
Definition: eggVertexPool.h:46
virtual string get_name() const
Returns the English name of the file type this converter supports.
void adjust_joints(FCDSceneNode *node, const JointMap &joint_map, const LMatrix4d &transform=LMatrix4d::ident_mat())
Traverses through the character hierarchy in order to bind the mesh to the character.