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