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  */
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  */
106 string DAEToEggConverter::
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  */
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 }
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Definition: eggPrimitive.h:47
The set of UV's that may or may not be assigned to a vertex.
Definition: eggVertexUV.h:29
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_rotate3d(double angle, const LVector3d &axis)
Appends a 3-d rotation about an arbitrary axis to the current transform.
virtual std::string get_name() const
Returns the English name of the file type this converter supports.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A line segment, or a series of connected line segments, defined by a <Line> entry.
Definition: eggLine.h:25
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:46
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A connected fan of triangles.
Corresponding to an <S$Anim> entry, this stores a single column of numbers, for instance for a morph ...
Definition: eggSAnimData.h:25
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool convert_file(const Filename &filename)
Handles the reading of the input file and converting it to egg.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A single point, or a collection of points as defined by a single <PointLight> entry.
Definition: eggPoint.h:25
Class representing an animated character.
Definition: daeCharacter.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class is seperated from the converter file because otherwise it would get too big and needlessly...
Definition: daeMaterials.h:38
DistanceUnit
This enumerated type lists all the kinds of units we're likely to come across in model conversion pro...
Definition: distanceUnit.h:23
This class supervises the construction of an EggData structure from a DAE file.
void apply_to_primitive(const std::string semantic, const PT(EggPrimitive) to)
Applies the stuff to the given EggPrimitive.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
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:39
void influence_vertex(int index, EggVertex *vertex)
Adds the influences for the given vertex.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition: eggVertex.h:39
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
AnimationConvert get_animation_convert() const
Returns how source animation will be converted into egg structures.
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
A parametric NURBS curve.
Definition: eggNurbsCurve.h:26
A single polygon.
Definition: eggPolygon.h:24
void add_translate3d(const LVector3d &translate)
Appends a 3-d translation operation to the current transform.
This corresponds to a.
Definition: eggTable.h:27
virtual std::string get_extension() const
Returns the common extension of the file type this converter supports.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
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...
This is our own Panda specialization on the default STL set.
Definition: pset.h:49
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
void build_table(EggTable *parent, FCDSceneNode *node, const pset< float > &keys)
Processes a joint node and its transforms.
A connected strip of triangles.
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
void add_matrix4(const LMatrix4d &mat)
Appends an arbitrary 4x4 matrix to the current transform.
Definition: eggTransform.I:132
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a base class for a family of converter classes that manage a conversion from some file type t...
void collect_keys(pset< float > &keys)
Collects all animation keys of animations applied to this character.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void apply_to_group(const std::string semantic, const PT(EggGroup) to, bool invert_transparency=false)
Applies the colorblend stuff to the given EggGroup.
A collection of vertices.
Definition: eggVertexPool.h:41
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.