Panda3D
Loading...
Searching...
No Matches
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
51using std::endl;
52using std::string;
53
54/**
55 *
56 */
57DAEToEggConverter::
58DAEToEggConverter() {
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 */
70DAEToEggConverter::
71DAEToEggConverter(const DAEToEggConverter &copy) :
73{
74}
75
76/**
77 *
78 */
79DAEToEggConverter::
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 */
93
94
95/**
96 * Returns the English name of the file type this converter supports.
97 */
99get_name() const {
100 return "COLLADA";
101}
102
103/**
104 * Returns the common extension of the file type this converter supports.
105 */
107get_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 */
116convert_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
345void DAEToEggConverter::
346process_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.
372void DAEToEggConverter::
373process_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
420void DAEToEggConverter::
421process_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.
468void DAEToEggConverter::
469process_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
688void DAEToEggConverter::
689process_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
704void DAEToEggConverter::
705process_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
726void DAEToEggConverter::
727process_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
770void DAEToEggConverter::
771process_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
797LMatrix4d DAEToEggConverter::
798convert_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
806void DAEToEggConverter::
807apply_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}
This class supervises the construction of an EggData structure from a DAE file.
virtual SomethingToEggConverter * make_copy()
Allocates and returns a new copy of the converter.
virtual std::string get_name() const
Returns the English name of the file type this converter supports.
virtual bool convert_file(const Filename &filename)
Handles the reading of the input file and converting it to egg.
virtual DistanceUnit get_input_units()
This may be called after convert_file() has been called and returned true, indicating a successful co...
virtual std::string get_extension() const
Returns the common extension of the file type this converter supports.
Class representing an animated character.
void bind_joints(JointMap &joint_map)
Binds the joints to the character.
void influence_vertex(int index, EggVertex *vertex)
Adds the influences for the given vertex.
void build_table(EggTable *parent, FCDSceneNode *node, const pset< float > &keys)
Processes a joint node and its transforms.
void collect_keys(pset< float > &keys)
Collects all animation keys of animations applied to this character.
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.
This class is seperated from the converter file because otherwise it would get too big and needlessly...
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...
void apply_to_group(const std::string semantic, const PT(EggGroup) to, bool invert_transparency=false)
Applies the colorblend stuff to the given EggGroup.
void apply_to_primitive(const std::string semantic, const PT(EggPrimitive) to)
Applies the stuff to the given EggPrimitive.
A base class for nodes in the hierarchy that are not leaf nodes.
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition eggGroup.h:34
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
A line segment, or a series of connected line segments, defined by a <Line> entry.
Definition eggLine.h:25
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.
A single point, or a collection of points as defined by a single <PointLight> entry.
Definition eggPoint.h:25
A single polygon.
Definition eggPolygon.h:24
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Corresponding to an entry, this stores a single column of numbers, for instance for a morph target,...
This corresponds to a.
Definition eggTable.h:27
void add_scale3d(const LVecBase3d &scale)
Appends a possibly non-uniform scale to the current transform.
void add_matrix4(const LMatrix4d &mat)
Appends an arbitrary 4x4 matrix to the current transform.
void add_translate3d(const LVector3d &translate)
Appends a 3-d translation operation to the current transform.
void add_rotate3d(double angle, const LVector3d &axis)
Appends a 3-d rotation about an arbitrary axis to the current transform.
A connected fan of triangles.
A connected strip of triangles.
A collection of vertices.
The set of UV's that may or may not be assigned to a vertex.
Definition eggVertexUV.h:29
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition eggVertex.h:39
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
This is a base class for a family of converter classes that manage a conversion from some file type t...
AnimationConvert get_animation_convert() const
Returns how source animation will be converted into egg structures.
void clear_error()
Resets the error flag to the no-error state.
This is our own Panda specialization on the default STL set.
Definition pset.h:49
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DistanceUnit
This enumerated type lists all the kinds of units we're likely to come across in model conversion pro...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.