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>
63 _error_handler =
nullptr;
64 _invert_transparency =
false;
80 ~DAEToEggConverter() {
81 if (_error_handler !=
nullptr) {
82 delete _error_handler;
120 if (_error_handler ==
nullptr) {
121 _error_handler =
new FUErrorSimpleHandler;
125 if (_egg_data->get_coordinate_system() == CS_default) {
126 _egg_data->set_coordinate_system(CS_yup_right);
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;
138 if (_document->GetAsset() !=
nullptr) {
139 FCDocumentTools::StandardizeUpAxisAndLength(_document);
145 string model_name = _character_name;
147 FCDSceneNode* visual_scene = _document->GetVisualSceneInstance();
148 if (visual_scene !=
nullptr) {
149 if (model_name.empty()) {
151 model_name = FROM_FSTRING(visual_scene->GetName());
153 scene_group =
new EggGroup(model_name);
154 _egg_data->add_child(scene_group);
156 for (
size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
157 process_node(scene_group, visual_scene->GetChild(ch));
161 <<
"No visual scene instance found in COLLADA document.\n";
167 Characters::iterator it;
169 for (it = _characters.begin(); it != _characters.end(); ++it) {
174 const FCDGeometryMesh *mesh = character->_skin_mesh;
176 if (mesh !=
nullptr) {
178 daeegg_cat.spam() <<
"Processing mesh for controller\n";
179 process_mesh(character->_node_group, mesh, materials, character);
185 for (
size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
186 character->
adjust_joints(visual_scene->GetChild(ch), _joints, LMatrix4d::ident_mat());
189 if (scene_group !=
nullptr) {
192 _egg_data->remove_child(scene_group);
194 scene_group->set_dart_type(EggGroup::DT_default);
200 _table->set_table_type(EggTable::TT_table);
201 _egg_data->add_child(_table);
204 bundle->set_table_type(EggTable::TT_bundle);
205 _table->add_child(bundle);
208 skeleton->set_table_type(EggTable::TT_table);
209 bundle->add_child(skeleton);
213 Characters::iterator it;
215 for (it = _characters.begin(); it != _characters.end(); ++it) {
225 if (_frame_inc != 0.0) {
229 if (_end_frame != _start_frame) {
230 start = _start_frame;
234 start = *keys.begin();
235 end = *keys.rbegin();
239 for (
float t = start; t <= end; t += _frame_inc) {
246 if (_end_frame != 0.0) {
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());
257 if (_start_frame != 0.0) {
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);
272 if (ki != keys.end()) {
276 for (++ki; ki != keys.end(); ++ki) {
277 if (diff != 0 && !IS_THRESHOLD_EQUAL((*ki - last), diff, 0.001)) {
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";
292 for (
size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
293 character->
build_table(skeleton, visual_scene->GetChild(ch), keys);
299 SAFE_DELETE(visual_scene);
300 SAFE_DELETE(_document);
313 if (IS_NEARLY_EQUAL(_unit_meters, 0.001)) {
314 return DU_millimeters;
316 if (IS_NEARLY_EQUAL(_unit_meters, 0.01)) {
317 return DU_centimeters;
319 if (IS_NEARLY_EQUAL(_unit_meters, 1.0)) {
322 if (IS_NEARLY_EQUAL(_unit_meters, 1000.0)) {
323 return DU_kilometers;
325 if (IS_NEARLY_EQUAL(_unit_meters, 3.0 * 12.0 * 0.0254)) {
328 if (IS_NEARLY_EQUAL(_unit_meters, 12.0 * 0.0254)) {
331 if (IS_NEARLY_EQUAL(_unit_meters, 0.0254)) {
334 if (IS_NEARLY_EQUAL(_unit_meters, 1852.0)) {
335 return DU_nautical_miles;
337 if (IS_NEARLY_EQUAL(_unit_meters, 5280.0 * 12.0 * 0.0254)) {
338 return DU_statute_miles;
345 void DAEToEggConverter::
347 const FCDAsset *asset = _document->GetAsset();
348 if (_document->GetAsset() ==
nullptr) {
352 _unit_name = FROM_FSTRING(asset->GetUnitName());
353 _unit_meters = asset->GetUnitConversionFactor();
356 FMVector3 up_axis = asset->GetUpAxis();
358 if (up_axis == FMVector3(0, 1, 0)) {
359 _egg_data->set_coordinate_system(CS_yup_right);
361 }
else if (up_axis == FMVector3(0, 0, 1)) {
362 _egg_data->set_coordinate_system(CS_zup_right);
365 _egg_data->set_coordinate_system(CS_invalid);
366 daeegg_cat.warning() <<
"Unrecognized coordinate system!\n";
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;
380 process_extra(node_group, node->GetExtra());
384 if (node->IsJoint()) {
385 string sid = FROM_FSTRING(node->GetSubId());
386 node_group->set_group_type(EggGroup::GT_joint);
388 if (!_joints.insert(DaeCharacter::JointMap::value_type(sid,
391 <<
"Joint with sid " << sid <<
" occurs more than once!\n";
396 for (
size_t tr = node->GetTransformCount(); tr > 0; --tr) {
397 apply_transform(node_group, node->GetTransform(tr - 1));
402 for (
size_t in = 0; in < node->GetInstanceCount(); ++in) {
403 process_instance(node_group, node->GetInstance(in));
407 for (
size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
408 process_node(DCAST(
EggGroupNode, node_group), node->GetChild(ch));
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);
420 void DAEToEggConverter::
421 process_instance(
EggGroup *parent,
const FCDEntityInstance* instance) {
422 nassertv(instance !=
nullptr);
423 nassertv(instance->GetEntity() !=
nullptr);
425 switch (instance->GetType()) {
426 case FCDEntityInstance::GEOMETRY:
429 const FCDGeometry* geometry = (
const FCDGeometry*) instance->GetEntity();
430 assert(geometry !=
nullptr);
431 if (geometry->IsMesh()) {
433 process_mesh(parent, geometry->GetMesh(),
new DaeMaterials((
const FCDGeometryInstance*) instance));
435 if (geometry->IsSpline()) {
436 process_spline(parent, FROM_FSTRING(geometry->GetName()),
const_cast<FCDGeometrySpline*
> (geometry->GetSpline()));
442 case FCDEntityInstance::CONTROLLER:
445 process_controller(parent, (
const FCDControllerInstance*) instance);
448 case FCDEntityInstance::MATERIAL:
452 case FCDEntityInstance::SIMPLE:
455 const FCDEntity* entity = instance->GetEntity();
456 if (entity->GetType() != FCDEntity::SCENE_NODE) {
457 daeegg_cat.warning() <<
"Unsupported entity type found" << endl;
463 daeegg_cat.warning() <<
"Unsupported instance type found" << endl;
468 void DAEToEggConverter::
469 process_mesh(
EggGroup *parent,
const FCDGeometryMesh* mesh,
472 nassertv(mesh !=
nullptr);
473 daeegg_cat.debug() <<
"Processing mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << endl;
479 mesh_group->add_child(mesh_pool);
482 if (mesh->GetSourceCount() == 0) {
483 daeegg_cat.debug() <<
"Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) <<
" has no sources" << endl;
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;
493 daeegg_cat.spam() <<
"Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) <<
" has " << mesh->GetPolygonsCount() <<
" polygon groups" << endl;
494 if (mesh->GetPolygonsCount() == 0)
return;
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());
507 if (materials !=
nullptr && (!polygons->GetMaterialSemantic().empty()) && mesh->GetPolygonsCount() > 1) {
513 primitiveholder = mesh_group;
515 primitive_holders[gr] = primitiveholder;
517 if (materials !=
nullptr) {
518 materials->
apply_to_group(material_semantic, primitiveholder, _invert_transparency);
521 const FCDGeometryPolygonsInput* pinput = polygons->FindInput(FUDaeGeometryInput::POSITION);
522 assert(pinput !=
nullptr);
523 const uint32* indices = pinput->GetIndices();
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();
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();
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();
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();
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();
551 if (materials !=
nullptr && tcinput !=
nullptr) {
552 if (daeegg_cat.is_debug()) {
554 <<
"Assigning texcoord set " << tcinput->GetSet()
555 <<
" to semantic '" << material_semantic <<
"'\n";
558 FUDaeGeometryInput::TEXCOORD, tcinput->GetSet());
561 if (materials !=
nullptr && binput !=
nullptr) {
562 if (daeegg_cat.is_debug()) {
564 <<
"Assigning texbinormal set " << binput->GetSet()
565 <<
" to semantic '" << material_semantic <<
"'\n";
568 FUDaeGeometryInput::TEXBINORMAL, binput->GetSet());
571 if (materials !=
nullptr && tinput !=
nullptr) {
572 if (daeegg_cat.is_debug()) {
574 <<
"Assigning textangent set " << tinput->GetSet()
575 <<
" to semantic '" << material_semantic <<
"'\n";
578 FUDaeGeometryInput::TEXTANGENT, tinput->GetSet());
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]));
586 if (character !=
nullptr) {
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]));
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]));
604 vertex->set_uvw(tcsetname, LPoint3d(data[0], data[1], data[2]));
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));
614 data = &csource->GetData()[cindices[ix]*4];
615 vertex->set_color(LColor(data[0], data[1], data[2], data[3]));
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) {
627 uv_obj->set_binormal(LVecBase3d(data[0], data[1], data[2]));
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) {
636 uv_obj->set_tangent(LVecBase3d(data[0], data[1], data[2]));
643 for (
size_t gr = 0; gr < mesh->GetPolygonsCount(); ++gr) {
644 const FCDGeometryPolygons* polygons = mesh->GetPolygons(gr);
647 for (
size_t fa = 0; fa < polygons->GetFaceVertexCountCount(); ++fa) {
650 switch (polygons->GetPrimitiveType()) {
651 case FCDGeometryPolygons::LINES:
654 case FCDGeometryPolygons::POLYGONS:
657 case FCDGeometryPolygons::TRIANGLE_FANS:
660 case FCDGeometryPolygons::TRIANGLE_STRIPS:
663 case FCDGeometryPolygons::POINTS:
666 case FCDGeometryPolygons::LINE_STRIPS:
667 daeegg_cat.warning() <<
"Linestrips not yet supported!" << endl;
670 daeegg_cat.warning() <<
"Unsupported primitive type found!" << endl;
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);
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));
682 offset += polygons->GetFaceVertexCount(fa);
685 delete[] primitive_holders;
688 void DAEToEggConverter::
689 process_spline(
EggGroup *parent,
const string group_name, FCDGeometrySpline* geometry_spline) {
690 assert(geometry_spline !=
nullptr);
694 if (geometry_spline->GetType() != FUDaeSplineType::NURBS) {
695 daeegg_cat.warning() <<
"Only NURBS curves are supported (yet)!" << endl;
698 for (
size_t sp = 0; sp < geometry_spline->GetSplineCount(); ++sp) {
699 process_spline(result, geometry_spline->GetSpline(sp));
704 void DAEToEggConverter::
705 process_spline(
EggGroup *parent,
const FCDSpline* spline) {
706 assert(spline !=
nullptr);
707 nassertv(spline->GetSplineType() == FUDaeSplineType::NURBS);
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);
718 for (
size_t cv = 0; cv < spline->GetCVCount(); ++cv) {
720 c_vtx->set_pos(TO_VEC3(*spline->GetCV(cv)));
722 nurbs_curve->add_vertex(c_vtx);
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);
734 const FCDGeometryMesh *mesh = controller->GetBaseGeometry()->GetMesh();
735 if (mesh !=
nullptr) {
737 daeegg_cat.spam() <<
"Processing mesh for controller\n";
738 process_mesh(parent, mesh, materials);
743 _characters.push_back(character);
746 if (controller->IsMorph()) {
747 assert(controller !=
nullptr);
748 const FCDMorphController* morph_controller = controller->GetMorphController();
749 assert(morph_controller !=
nullptr);
751 bundle->set_table_type(EggTable::TT_bundle);
753 morph->set_table_type(EggTable::TT_table);
754 bundle->add_child(morph);
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);
760 if (morph_target->IsAnimated()) {
763 target->add_data(morph_target->GetWeight());
765 morph->add_child(target);
770 void DAEToEggConverter::
771 process_extra(
EggGroup *group,
const FCDExtra* extra) {
772 if (extra ==
nullptr) {
775 nassertv(group !=
nullptr);
777 const FCDEType* etype = extra->GetDefaultType();
778 if (etype ==
nullptr) {
782 const FCDENode* enode = (
const FCDENode*) etype->FindTechnique(
"PANDA3D");
783 if (enode ==
nullptr) {
788 enode->FindChildrenNodes(
"param", tags);
789 for (FCDENodeList::iterator it = tags.begin(); it != tags.end(); ++it) {
790 const FCDEAttribute* attr = (*it)->FindAttribute(
"sid");
792 group->
set_tag(FROM_FSTRING(attr->GetValue()), (*it)->GetContent());
797 LMatrix4d DAEToEggConverter::
798 convert_matrix(
const FMMatrix44 &matrix) {
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]);
806 void DAEToEggConverter::
807 apply_transform(
EggGroup *to,
const FCDTransform* from) {
808 assert(from !=
nullptr);
809 assert(to !=
nullptr);
812 switch (from->GetType()) {
813 case FCDTransform::TRANSLATION:
815 const FCDTTranslation *trans = (
const FCDTTranslation *)from;
820 case FCDTransform::ROTATION:
822 const FCDTRotation *rot = (
const FCDTRotation *)from;
823 to->
add_rotate3d(rot->GetAngle(), TO_VEC3(rot->GetAxis()));
827 case FCDTransform::SCALE:
829 const FCDTScale *scale = (
const FCDTScale *)from;