Panda3D
 All Classes Functions Variables Enumerations
softToEggConverter.cxx
1 // Filename: softToEggConverter.cxx
2 // Created by: masad (25Sep03)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 
16 #include "softToEggConverter.h"
17 #include "config_softegg.h"
18 #include "softEggGroupUserData.h"
19 
20 #include "eggData.h"
21 #include "eggGroup.h"
22 #include "eggTable.h"
23 #include "eggVertex.h"
24 #include "eggComment.h"
25 #include "eggVertexPool.h"
26 #include "eggNurbsSurface.h"
27 #include "eggNurbsCurve.h"
28 #include "eggPolygon.h"
29 #include "eggPrimitive.h"
30 #include "eggTexture.h"
31 #include "eggTextureCollection.h"
32 #include "eggXfmSAnim.h"
33 #include "eggSAnimData.h"
34 #include "string_utils.h"
35 #include "dcast.h"
36 
38 
39 const int TEX_PER_MAT = 1;
40 
41 ////////////////////////////////////////////////////////////////////
42 // Function: SoftToEggConverter::Constructor
43 // Access: Public
44 // Description:
45 ////////////////////////////////////////////////////////////////////
46 SoftToEggConverter::
47 SoftToEggConverter(const string &program_name) :
48  _program_name(program_name)
49 {
50  _from_selection = false;
51  _polygon_output = false;
52  _polygon_tolerance = 0.01;
53  /*
54  _respect_maya_double_sided = maya_default_double_sided;
55  _always_show_vertex_color = maya_default_vertex_color;
56  */
57  _transform_type = TT_model;
58 
59  database_name = NULL;
60  scene_name = NULL;
61  model_name = NULL;
62  animFileName = NULL;
63  eggFileName = NULL;
64  tex_path = NULL;
65  eggGroupName = NULL;
66  tex_filename = NULL;
67  search_prefix = NULL;
68  result = SI_SUCCESS;
69 
70  // skeleton = new EggGroup();
71  foundRoot = FALSE;
72  // animRoot = NULL;
73  // morphRoot = NULL;
74  geom_as_joint = 0;
75  make_anim = 0;
76  make_nurbs = 0;
77  make_poly = 0;
78  make_soft = 0;
79  make_morph = 1;
80  make_duv = 1;
81  make_dart = TRUE;
82  has_morph = 0;
83  make_pose = 0;
84  // animData.is_z_up = FALSE;
85  nurbs_step = 1;
86  anim_start = -1000;
87  anim_end = -1000;
88  anim_rate = 24;
89  pose_frame = -1;
90  verbose = 0;
91  flatten = 0;
92  shift_textures = 0;
93  ignore_tex_offsets = 0;
94  use_prefix = 0;
95 }
96 
97 ////////////////////////////////////////////////////////////////////
98 
99 // Function: SoftToEggConverter::Copy Constructor
100 // Access: Public
101 // Description:
102 ////////////////////////////////////////////////////////////////////
103 SoftToEggConverter::
104 SoftToEggConverter(const SoftToEggConverter &copy) :
105  _from_selection(copy._from_selection),
106  /*
107  _maya(copy._maya),
108  */
109  _polygon_output(copy._polygon_output),
110  _polygon_tolerance(copy._polygon_tolerance),
111  /*
112  _respect_maya_double_sided(copy._respect_maya_double_sided),
113  _always_show_vertex_color(copy._always_show_vertex_color),
114  */
115  _transform_type(copy._transform_type)
116 {
117 }
118 
119 ////////////////////////////////////////////////////////////////////
120 // Function: SoftToEggConverter::Destructor
121 // Access: Public, Virtual
122 // Description:
123 ////////////////////////////////////////////////////////////////////
124 SoftToEggConverter::
125 ~SoftToEggConverter() {
126  /*
127  close_api();
128  */
129 }
130 ////////////////////////////////////////////////////////////////////
131 // Function: Help
132 // Access: Public
133 // Description: Displays the "what is this program" message, along
134 // with the usage message. Should be overridden in base
135 // classes to describe the current program.
136 ////////////////////////////////////////////////////////////////////
139 {
140  softegg_cat.info() <<
141  "soft2egg takes a SoftImage scene or model\n"
142  "and outputs its contents as an egg file\n";
143 
144  Usage();
145 }
146 
147 ////////////////////////////////////////////////////////////////////
148 // Function: Usage
149 // Access: Public
150 // Description: Displays the usage message.
151 ////////////////////////////////////////////////////////////////////
153 Usage() {
154  softegg_cat.info()
155  << "\nUsage:\n"
156  // << _commandName << " [opts] (must specify -m or -s)\n\n"
157  << "soft" << " [opts] (must specify -m or -s)\n\n"
158  << "Options:\n";
159 
160  ShowOpts();
161  softegg_cat.info() << "\n";
162 }
163 
164 ////////////////////////////////////////////////////////////////////
165 // Function: ShowOpts
166 // Access: Public
167 // Description: Displays the valid options. Should be extended in
168 // base classes to show additional options relevant to
169 // the current program.
170 ////////////////////////////////////////////////////////////////////
173 {
174  softegg_cat.info() <<
175  " -r <path> - Used to provide soft with the resource\n"
176  " Defaults to '/ful/ufs/soft371_mips2/3D/rsrc'.\n"
177  " -d <path> - Database path.\n"
178  " -s <scene> - Indicates that a scene will be converted.\n"
179  " -m <model> - Indicates that a model will be converted.\n"
180  " -t <path> - Specify path to place converted textures.\n"
181  " -T <name> - Specify filename for texture map listing.\n"
182  " -S <step> - Specify step for nurbs surface triangulation.\n"
183  " -M <name> - Specify model output filename. Defaults to scene name.\n"
184  " -A <name> - Specify anim output filename. Defaults to scene name.\n"
185  " -N <name> - Specify egg group name.\n"
186  " -k - Enable soft assignment for geometry.\n"
187  " -n - Specify egg NURBS representation instead of poly's.\n"
188  " -p - Specify egg polygon output for geometry.\n"
189  " -P <frame> - Specify frame number for static pose.\n"
190  " -b <frame> - Specify starting frame for animation (default = first).\n"
191  " -e <frame> - Specify ending frame for animation (default = last).\n"
192  " -f <fps> - Specify frame rate for animation playback.\n"
193  " -a - Compile animation tables if animation present.\n"
194  " -F - Ignore hierarchy and build a completely flat skeleton.\n"
195  " -v <level> - Set debug level.\n"
196  " -x - Shift NURBS parameters to preserve Alias textures.\n"
197  " -i - Ignore Soft texture uv offsets.\n"
198  " -u - Use Soft prefix in model names.\n"
199  " -c - Cancel morph conversion.\n"
200  " -C - Cancel duv conversion.\n"
201  " -D - Don't make the output model a character.\n"
202  " -o <prefix>- Convert only models with given prefix.\n";
203 
204  // EggBase::ShowOpts();
205 }
206 
207 ////////////////////////////////////////////////////////////////////
208 // Function: DoGetopts
209 // Access: Public
210 // Description: Calls getopt() to parse the command-line switches.
211 // Calls HandleGetopts() to interpret each switch.
212 // Returns true if the parsing was successful; false if
213 // there was an error. Adjusts argc and argv to remove
214 // the switches from the parameter list.
215 ////////////////////////////////////////////////////////////////////
217 DoGetopts(int &argc, char **&argv) {
218  bool okflag = true;
219  int i = 0;
220  softegg_cat.info() << "argc " << argc << "\n";
221  if (argc <2) {
222  Usage();
223  okflag = false;
224  }
225  while( i < argc ) {
226  strcat(_commandLine, argv[i]);
227  strcat(_commandLine, " ");
228  ++i;
229  }
230  softegg_cat.info() << endl << _commandLine << endl;
231 
232  i = 1;
233  while ((i < argc) && (argv[i][0] == '-') && okflag) {
234  softegg_cat.info() << "arg " << i << " is " << argv[i] << "\n";
235  okflag = HandleGetopts(i, argc, argv);
236  }
237  return okflag;
238 }
239 
240 ////////////////////////////////////////////////////////////////////
241 // Function: HandleGetopts
242 // Access: Public
243 // Description: increment idx based on what kind of option parsed
244 // Supported options are as follows:
245 // r:d:s:m:t:P:b:e:f:T:S:M:A:N:v:o:FhknpaxiucCD
246 ////////////////////////////////////////////////////////////////////
248 HandleGetopts(int &idx, int argc, char **argv)
249 {
250  bool okflag = true;
251 
252  char flag = argv[idx][1]; // skip the '-' from option
253 
254  switch (flag)
255  {
256  case 'r': // Set the resource path for soft.
257  if ( strcmp( argv[idx+1], "" ) ) {
258  // Get the path.
259  rsrc_path = argv[idx+1];
260  softegg_cat.info() << "using rsrc path " << rsrc_path << "\n";
261  }
262  ++idx;
263  break;
264 
265  case 'd': // Set the database path.
266  if ( strcmp( argv[idx+1], "" ) ) {
267  // Get the path.
268  database_name = argv[idx+1];
269  softegg_cat.info() << "using database " << database_name << "\n";
270  }
271  ++idx;
272  break;
273 
274  case 's': // Check if its a scene.
275  if ( strcmp( argv[idx+1], "" ) ) {
276  // Get scene name.
277  scene_name = argv[idx+1];
278  softegg_cat.info() << "loading scene " << scene_name << "\n";
279  }
280  ++idx;
281  break;
282 
283  case 'm': // Check if its a model.
284  if ( strcmp( argv[idx+1], "" ) ) {
285  // Get model name.
286  model_name = argv[idx+1];
287  softegg_cat.info() << "loading model " << model_name << endl;
288  }
289  ++idx;
290  break;
291 
292  case 't': // Get converted texture path.
293  if ( strcmp( argv[idx+1], "" ) ) {
294  // Get tex path name.
295  tex_path = argv[idx+1];
296  softegg_cat.info() << "texture path: " << tex_path << endl;
297  }
298  ++idx;
299  break;
300 
301  case 'T': // Specify texture list filename.
302  if ( strcmp( argv[idx+1], "") ) {
303  // Get the name.
304  tex_filename = argv[idx+1];
305  softegg_cat.info() << "creating texture list file: " << tex_filename << endl;
306  }
307  ++idx;
308  break;
309 
310  case 'S': // Set NURBS step.
311  if ( strcmp( argv[idx+1], "" ) ) {
312  nurbs_step = atoi(argv[idx+1]);
313  softegg_cat.info() << "NURBS step: " << nurbs_step << endl;
314  }
315  ++idx;
316  break;
317 
318  case 'M': // Set model output file name.
319  if ( strcmp( argv[idx+1], "" ) ) {
320  eggFileName = argv[idx+1];
321  softegg_cat.info() << "Model output filename: " << eggFileName << endl;
322  }
323  ++idx;
324  break;
325 
326  case 'A': // Set anim output file name.
327  if ( strcmp( argv[idx+1], "" ) ) {
328  animFileName = argv[idx+1];
329  softegg_cat.info() << "Anim output filename: " << animFileName << endl;
330  }
331  ++idx;
332  break;
333 
334  case 'N': // Set egg model name.
335  if ( strcmp( argv[idx+1], "" ) ) {
336  eggGroupName = argv[idx+1];
337  softegg_cat.info() << "Egg group name: " << eggGroupName << endl;
338  }
339  ++idx;
340  break;
341 
342  case 'o': // Set search_prefix.
343  if ( strcmp( argv[idx+1], "" ) ) {
344  search_prefix = argv[idx+1];
345  softegg_cat.info() << "Only converting models with prefix: " << search_prefix << endl;
346  }
347  ++idx;
348  break;
349 
350  case 'h': // print help message
351  Help();
352  exit(1);
353  break;
354 
355  case 'c': // Cancel morph animation conversion
356  make_morph = FALSE;
357  softegg_cat.info() << "canceling morph conversion\n";
358  break;
359 
360  case 'C': // Cancel uv animation conversion
361  make_duv = FALSE;
362  softegg_cat.info() << "canceling uv animation conversion\n";
363  break;
364 
365  case 'D': // Omit the Dart flag
366  make_dart = FALSE;
367  softegg_cat.info() << "making a non-character model\n";
368  break;
369 
370  case 'k': // Enable soft skinning
371  //make_soft = TRUE;
372  //fprintf( outStream, "enabling soft skinning\n" );
373  softegg_cat.info() << "-k flag no longer necessary\n";
374  break;
375 
376  case 'n': // Generate egg NURBS output
377  make_nurbs = TRUE;
378  softegg_cat.info() << "outputting egg NURBS info\n";
379  break;
380 
381  case 'p': // Generate egg polygon output
382  make_poly = TRUE;
383  softegg_cat.info() << "outputting egg polygon info\n";
384  break;
385 
386  case 'P': // Generate static pose from given frame
387  if ( strcmp( argv[idx+1], "" ) ) {
388  make_pose = TRUE;
389  pose_frame = atoi(argv[idx+1]);
390  softegg_cat.info() << "generating static pose from frame " << pose_frame << endl;
391  }
392  ++idx;
393  break;
394 
395  case 'a': // Compile animation tables.
396  make_anim = TRUE;
397  softegg_cat.info() << "attempting to compile anim tables\n";
398  break;
399 
400  case 'F': // Build a flat skeleton.
401  flatten = TRUE;
402  softegg_cat.info() << "building a flat skeleton!!!\n";
403  break;
404 
405  case 'x': // Shift NURBS parameters to preserve Alias textures.
406  shift_textures = TRUE;
407  softegg_cat.info() << "shifting NURBS parameters...\n";
408  break;
409 
410  case 'i': // Ignore Soft uv texture offsets
411  ignore_tex_offsets = TRUE;
412  softegg_cat.info() << "ignoring texture offsets...\n";
413  break;
414 
415  case 'u': // Use Soft prefix in model names
416  use_prefix = TRUE;
417  softegg_cat.info() << "using prefix in model names...\n";
418  break;
419 
420 
421  case 'v': // print debug messages.
422  if ( strcmp( argv[idx+1], "" ) ) {
423  verbose = atoi(argv[idx+1]);
424  softegg_cat.info() << "using debug level " << verbose << endl;
425  }
426  ++idx;
427  break;
428 
429  case 'b': // Set animation start frame.
430  anim_start = atoi(argv[idx]+2);
431  softegg_cat.info() << "animation starting at frame: " << anim_start << endl;
432  break;
433 
434  case 'e': /// Set animation end frame.
435  anim_end = atoi(argv[idx]+2);
436  softegg_cat.info() << "animation ending at frame: " << anim_end << endl;
437  break;
438 
439  case 'f': /// Set animation frame rate.
440  if ( strcmp( argv[idx+1], "" ) ) {
441  anim_rate = atoi(argv[idx+1]);
442  softegg_cat.info() << "animation frame rate: " << anim_rate << endl;
443  }
444  ++idx;
445  break;
446 
447  default:
448  softegg_cat.info() << flag << " flag not supported\n";
449  okflag = false;
450  }
451  idx++;
452  return (okflag);
453 }
454 
455 ////////////////////////////////////////////////////////////////////
456 // Function: SoftToEggConverter::make_copy
457 // Access: Public, Virtual
458 // Description: Allocates and returns a new copy of the converter.
459 ////////////////////////////////////////////////////////////////////
462  return new SoftToEggConverter(*this);
463 }
464 
465 ////////////////////////////////////////////////////////////////////
466 // Function: SoftToEggConverter::get_name
467 // Access: Public, Virtual
468 // Description: Returns the English name of the file type this
469 // converter supports.
470 ////////////////////////////////////////////////////////////////////
472 get_name() const {
473  return "Soft";
474 }
475 
476 ////////////////////////////////////////////////////////////////////
477 // Function: SoftToEggConverter::get_extension
478 // Access: Public, Virtual
479 // Description: Returns the common extension of the file type this
480 // converter supports.
481 ////////////////////////////////////////////////////////////////////
483 get_extension() const {
484  return "mb";
485 }
486 
487 ////////////////////////////////////////////////////////////////////
488 // Function: SoftToEggConverter::get_name
489 // Access: Public, Virtual
490 // Description: Returns the English name of the file type this
491 // converter supports.
492 ////////////////////////////////////////////////////////////////////
494 find_node(string name) {
495  return _tree.get_node(name);
496 }
497 
498 ////////////////////////////////////////////////////////////////////
499 // Function: GetTextureName
500 // Access: Public
501 // Description: Given a texture element, return texture name
502 // with given tex_path
503 ////////////////////////////////////////////////////////////////////
505 GetTextureName( SAA_Scene *scene, SAA_Elem *texture ) {
506  char *fileName = new char[_MAX_PATH];
507  char tempName[_MAX_PATH];
508  SAA_texture2DGetPicName( scene, texture, _MAX_PATH, tempName );
509 
510  if (tex_path) {
511  // softegg_cat.spam() << "tempName :" << tempName << endl;
512  strcpy(fileName, tex_path);
513 
514  // do some processing on the name string
515  char *tmpName = NULL;
516  tmpName = strrchr(tempName, '/');
517  if (tmpName)
518  tmpName++;
519  else
520  tmpName = tempName;
521 
522  // softegg_cat.spam() << "tmpName : " << tmpName << endl;
523  strcat(fileName, "/");
524  strcat(fileName, tmpName);
525  }
526  else {
527  strcpy(fileName, tempName);
528  }
529 
530  strcat(fileName, ".pic");
531  // softegg_cat.spam() << "fileName : " << fileName << endl;
532 
533  return fileName;
534 }
535 
536 ////////////////////////////////////////////////////////////////////
537 // Function: SoftToEggConverter::convert_file
538 // Access: Public, Virtual
539 // Description: Handles the reading of the input file and converting
540 // it to egg. Returns true if successful, false
541 // otherwise.
542 //
543 // This is designed to be as generic as possible,
544 // generally in support of run-time loading.
545 // Also see convert_soft().
546 ////////////////////////////////////////////////////////////////////
548 convert_file(const Filename &filename) {
549  if (!open_api()) {
550  softegg_cat.error()
551  << "Soft is not available.\n";
552  return false;
553  }
554  if (_character_name.empty()) {
555  _character_name = filename.get_basename_wo_extension();
556  }
557  return convert_soft(false);
558 }
559 
560 ////////////////////////////////////////////////////////////////////
561 // Function: SoftToEggConverter::convert_soft
562 // Access: Public
563 // Description: Fills up the egg_data structure according to the
564 // global soft model data. Returns true if successful,
565 // false if there is an error. If from_selection is
566 // true, the converted geometry is based on that which
567 // is selected; otherwise, it is the entire Soft scene.
568 ////////////////////////////////////////////////////////////////////
570 convert_soft(bool from_selection) {
571  bool all_ok = true;
572 
573  _from_selection = from_selection;
574  _textures.clear();
575 
576  PT(EggData) egg_data = new EggData;
577  set_egg_data(egg_data);
578  softegg_cat.spam() << "eggData " << get_egg_data() << "\n";
579 
580  // append the command line
581  softegg_cat.info() << _commandLine << endl;
582  get_egg_data()->insert(get_egg_data()->begin(), new EggComment("", _commandLine));
583 
584  if (_egg_data->get_coordinate_system() != CS_default) {
585  softegg_cat.spam() << "coordinate system is not default\n";
586  exit(1);
587  }
588 
589  _tree._use_prefix = use_prefix;
590  _tree._search_prefix = search_prefix;
591  all_ok = _tree.build_complete_hierarchy(scene, database);
592 
593  // Lets see if we have gotten the hierarchy right
594  //_tree.print_hierarchy();
595  //exit(1);
596 
597  char *root_name = _tree.GetRootName( eggFileName );
598 
599  softegg_cat.debug() << "main group name: " << root_name << endl;
600  if (root_name)
601  _character_name = root_name;
602 
603  if (make_poly || make_nurbs) {
604  // Specify that the texture names should be relative to the output
605  // file.
606  Filename output_filename(eggFileName);
607  _path_replace->_path_store = PS_relative;
608  _path_replace->_path_directory = output_filename.get_dirname();
609 
610  if (!convert_char_model()) {
611  all_ok = false;
612  }
613 
614  // generate soft skinning assignments if desired
615  if (!make_soft_skin()) {
616  all_ok = false;
617  }
618 
619  // sometimes you need to hard assign some vertices
620  if (!cleanup_soft_skin()) {
621  all_ok = false;
622  }
623 
624  // reparent_decals(get_egg_data());
625  softegg_cat.info() << "Converted Softimage file\n";
626 
627  // write out the egg model file
628  _egg_data->write_egg(output_filename);
629  softegg_cat.info() << "Wrote Egg file " << output_filename << endl;
630  }
631  if (make_anim) {
632  if (!convert_char_chan()) {
633  all_ok = false;
634  }
635 
636  // reparent_decals(get_egg_data());
637  softegg_cat.info() << "Converted Softimage file\n";
638 
639  // write out the egg model file
640  _egg_data->write_egg(Filename(animFileName));
641  softegg_cat.info() << "Wrote Anim file " << animFileName << endl;
642  }
643  return all_ok;
644 }
645 
646 ////////////////////////////////////////////////////////////////////
647 // Function: SoftToEggConverter::open_api
648 // Access: Public
649 // Description: Attempts to open the Soft API if it was not already
650 // open, and returns true if successful, or false if
651 // there is an error.
652 ////////////////////////////////////////////////////////////////////
655  if ((scene_name == NULL && model_name == NULL) || database_name == NULL) {
656  Usage();
657  exit( 1 );
658  }
659  if ((result = SAA_Init(rsrc_path, FALSE)) != SI_SUCCESS) {
660  softegg_cat.info() << "Error: Couldn't get resource path!\n";
661  exit( 1 );
662  }
663  // cout << "got past init" << endl;
664  if ((result = SAA_databaseLoad(database_name, &database)) != SI_SUCCESS) {
665  softegg_cat.info() << "Error: Couldn't load database!\n";
666  exit( 1 );
667  }
668  // cout << "got past database load" << endl;
669  if ((result = SAA_sceneGetCurrent(&scene)) != SI_SUCCESS) {
670  softegg_cat.info() << "Error: Couldn't get current scene!\n";
671  exit( 1 );
672  }
673  // cout << "got past get current" << endl;
674  if ((result = SAA_sceneLoad( &database, scene_name, &scene )) != SI_SUCCESS) {
675  softegg_cat.info() << "Error: Couldn't load scene " << scene_name << "!\n";
676  exit( 1 );
677  }
678  // cout << "got past scene load" << endl;
679  if ( SAA_updatelistGet( &scene ) == SI_SUCCESS ) {
680  PN_stdfloat time;
681 
682  softegg_cat.info() << "setting Scene to frame " << pose_frame << "...\n";
683  //SAA_sceneSetPlayCtrlCurrentFrame( &scene, pose_frame );
684  SAA_frame2Seconds( &scene, pose_frame, &time );
685  SAA_updatelistEvalScene( &scene, time );
686  if ( make_pose )
687  SAA_sceneFreeze(&scene);
688  }
689 
690  // if no egg filename specified, make up a name
691  if ( eggFileName == NULL ) {
692  string madeName;
693  string tempName(scene_name);
694  string::size_type end = tempName.find(".dsc");
695  if (end != string::npos) {
696  madeName.assign(tempName.substr(0,end));
697  if ( make_nurbs )
698  madeName.insert(madeName.size(), "-nurb");
699  madeName.insert(madeName.size(), ".egg" );
700  }
701  eggFileName = new char[madeName.size()+1];
702  strcpy(eggFileName, madeName.c_str());
703 
704  // if no anim filename specified, make up a name
705  if ( animFileName == NULL ) {
706  madeName.assign(tempName.substr(0,end));
707  madeName.insert(madeName.size(), "-chan.egg");
708  animFileName = new char[strlen(scene_name)+ 10];
709  strcpy(animFileName, madeName.c_str());
710  }
711  }
712 
713  return true;
714 }
715 
716 ////////////////////////////////////////////////////////////////////
717 // Function: SoftToEggConverter::close_api
718 // Access: Public
719 // Description: Closes the Soft API, if it was previously opened.
720 // Caution! Soft appears to call exit() when its API is
721 // closed.
722 ////////////////////////////////////////////////////////////////////
725  // don't know yet
726 }
727 
728 ////////////////////////////////////////////////////////////////////
729 // Function: SoftToEggConverter::convert_char_model
730 // Access: Private
731 // Description: Converts the file as an animatable character
732 // model, with joints and vertex membership.
733 ////////////////////////////////////////////////////////////////////
734 bool SoftToEggConverter::
735 convert_char_model() {
736  softegg_cat.spam() << "character name " << _character_name << "\n";
737  EggGroup *char_node = new EggGroup(eggGroupName);
738  get_egg_data()->add_child(char_node);
739  char_node->set_dart_type(EggGroup::DT_default);
740 
741  return convert_hierarchy(char_node);
742 }
743 
744 ////////////////////////////////////////////////////////////////////
745 // Function: SoftToEggConverter::find_morph_table
746 // Access: Public
747 // Description: Given a tablename, it either creates a new
748 // eggSAnimData structure (if doesn't exist) or
749 // locates it.
750 ////////////////////////////////////////////////////////////////////
752 find_morph_table(char *name) {
753  EggSAnimData *anim = NULL;
754  MorphTable::iterator mt;
755  for (mt = _morph_table.begin(); mt != _morph_table.end(); ++mt) {
756  anim = (*mt);
757  if (!strcmp(anim->get_name().c_str(), name))
758  return anim;
759  }
760 
761  // create an entry
762  anim = new EggSAnimData(name);
763  anim->set_fps(_tree._fps);
764  _morph_table.push_back(anim);
765  morph_node->add_child(anim);
766  return anim;
767 }
768 
769 ////////////////////////////////////////////////////////////////////
770 // Function: SoftToEggConverter::convert_char_chan
771 // Access: Private
772 // Description: Converts the animation as a series of tables to apply
773 // to the character model, as retrieved earlier via
774 // AC_model.
775 ////////////////////////////////////////////////////////////////////
776 bool SoftToEggConverter::
777 convert_char_chan() {
778  int start_frame = -1;
779  int end_frame = -1;
780  int frame_inc, frame;
781  double output_frame_rate = anim_rate;
782 
783  PN_stdfloat time;
784 
785  EggTable *root_table_node = new EggTable();
786  get_egg_data()->add_child(root_table_node);
787  EggTable *bundle_node = new EggTable(eggGroupName);
788  bundle_node->set_table_type(EggTable::TT_bundle);
789  root_table_node->add_child(bundle_node);
790  EggTable *skeleton_node = new EggTable("<skeleton>");
791  bundle_node->add_child(skeleton_node);
792 
793  morph_node = new EggTable("morph");
794 
795  // Set the frame rate before we start asking for anim tables to be
796  // created.
797  SAA_sceneGetPlayCtrlStartFrame(&scene, &start_frame);
798  SAA_sceneGetPlayCtrlEndFrame(&scene, &end_frame);
799  SAA_sceneGetPlayCtrlFrameStep( &scene, &frame_inc );
800  if (frame_inc != 1) // Hmmm...some files gave me frame_inc of 0, that can't be good
801  frame_inc = 1;
802 
803  softegg_cat.info() << "animation start frame: " << start_frame << " end frame: " << end_frame << endl;
804  softegg_cat.info() << "animation frame inc: " << frame_inc << endl;
805 
806  _tree._fps = output_frame_rate / frame_inc;
807  // _tree.clear_egg(get_egg_data(), NULL, root_node);
808  _tree.clear_egg(get_egg_data(), NULL, skeleton_node);
809 
810  // Now we can get the animation data by walking through all of the
811  // frames, one at a time, and getting the joint angles at each
812  // frame.
813 
814  // This is just a temporary EggGroup to receive the transform for
815  // each joint each frame.
816  PT(EggGroup) tgroup = new EggGroup;
817 
818  int num_nodes = _tree.get_num_nodes();
819  int i;
820 
821  // MTime frame(start_frame, MTime::uiUnit());
822  // MTime frame_stop(end_frame, MTime::uiUnit());
823  // start at first frame and go to last
824  if (make_pose) {
825  start_frame = pose_frame;
826  end_frame = pose_frame;
827  }
828  if (anim_start > 0)
829  start_frame = anim_start;
830  if (anim_end > 0)
831  end_frame = anim_end;
832  for ( frame = start_frame; frame <= end_frame; frame += frame_inc) {
833  SAA_frame2Seconds( &scene, frame, &time );
834  // softegg_cat.spam() << "got time " << time << endl;
835  if (!make_pose) {
836  SAA_updatelistEvalScene( &scene, time );
837  }
838  softegg_cat.spam() << "\n> animating frame " << frame << endl;
839 
840  // if (softegg_cat.is_debug()) {
841  // softegg_cat.debug(false)
842  softegg_cat.info() << "frame " << time << "\n";
843  //} else {
844  // We have to write to cerr instead of softegg_cat to allow
845  // flushing without writing a newline.
846  // cerr << "." << flush;
847  // }
848  // MGlobal::viewFrame(frame);
849 
850  for (i = 0; i < num_nodes; i++) {
851  SoftNodeDesc *node_desc = _tree.get_node(i);
852 
853  if (node_desc->is_partial(search_prefix)) {
854  softegg_cat.debug() << endl;
855  continue;
856  }
857  if (make_morph) {
858  node_desc->make_morph_table(time);
859  }
860  if (node_desc->is_joint()) {
861  softegg_cat.spam() << "-----joint " << node_desc->get_name() << "\n";
862  EggXfmSAnim *anim = _tree.get_egg_anim(node_desc);
863  // following function fills in the anim structure
864  node_desc->get_joint_transform(&scene, tgroup, anim, TRUE);
865  }
866  }
867 
868  // frame += frame_inc;
869  }
870 
871  if (has_morph)
872  bundle_node->add_child(morph_node);
873 
874  // Now optimize all of the tables we just filled up, for no real
875  // good reason, except that it makes the resulting egg file a little
876  // easier to read.
877  for (i = 0; i < num_nodes; i++) {
878  SoftNodeDesc *node_desc = _tree.get_node(i);
879  if (node_desc->is_partial(search_prefix))
880  continue;
881 
882  if (node_desc->is_joint()) {
883  _tree.get_egg_anim(node_desc)->optimize();
884  }
885  }
886 
887  softegg_cat.info(false)
888  << "\n";
889 
890  return true;
891 }
892 
893 ////////////////////////////////////////////////////////////////////
894 // Function: SoftToEggConverter::convert_hierarchy
895 // Access: Private
896 // Description: Generates egg structures for each node in the Soft
897 // hierarchy.
898 ////////////////////////////////////////////////////////////////////
899 bool SoftToEggConverter::
900 convert_hierarchy(EggGroupNode *egg_root) {
901  int num_nodes = _tree.get_num_nodes();
902 
903  _tree.clear_egg(get_egg_data(), egg_root, NULL);
904  softegg_cat.spam() << "num_nodes = " << num_nodes << endl;
905  for (int i = 0; i < num_nodes; i++) {
906  if (!process_model_node(_tree.get_node(i))) {
907  return false;
908  }
909  softegg_cat.debug() << i << endl;
910  }
911  return true;
912 }
913 
914 ////////////////////////////////////////////////////////////////////
915 // Function: SoftToEggConverter::process_model_node
916 // Access: Private
917 // Description: Converts the indicated Soft node (given a MDagPath,
918 // similar in concept to Panda's NodePath) to the
919 // corresponding Egg structure. Returns true if
920 // successful, false if an error was encountered.
921 ////////////////////////////////////////////////////////////////////
922 bool SoftToEggConverter::
923 process_model_node(SoftNodeDesc *node_desc) {
924  EggGroup *egg_group = NULL;
925  const char *name = NULL;
926  char *fullname = NULL;
927  SAA_ModelType type;
928 
929  name = node_desc->get_name().c_str();
930  softegg_cat.debug() << "element name <" << name << ">\n";
931 
932  if (node_desc->is_junk()) {
933  softegg_cat.spam() << "no processing, it is junk\n";
934  return true;
935  }
936 
937  // split
938  if (node_desc->is_partial(search_prefix)) {
939  softegg_cat.debug() << endl;
940  return true;
941  }
942  else
943  softegg_cat.debug() << endl << name << ":being processed" << endl;
944 
945  egg_group = _tree.get_egg_group(node_desc);
946 
947  // find out what type of node we're dealing with
948  SAA_modelGetType( &scene, node_desc->get_model(), &type );
949 
950  softegg_cat.debug() << "encountered ";
951  switch(type){
952  case SAA_MNILL:
953  softegg_cat.debug() << "null\n";
954  break;
955  case SAA_MPTCH:
956  softegg_cat.debug() << "patch\n";
957  break;
958  case SAA_MFACE:
959  softegg_cat.debug() << "face\n";
960  //break;
961  case SAA_MSMSH:
962  softegg_cat.debug() << "mesh\n";
963  node_desc->get_transform(&scene, egg_group, TRUE);
964  make_polyset(node_desc, egg_group, type);
965  break;
966  case SAA_MJNT:
967  softegg_cat.debug() << "joint";
968  softegg_cat.debug() << " joint type " << node_desc->is_joint() << endl;
969  break;
970  case SAA_MSPLN:
971  softegg_cat.debug() << "spline\n";
972  break;
973  case SAA_MMETA:
974  softegg_cat.debug() << "meta element\n";
975  break;
976  case SAA_MBALL:
977  softegg_cat.debug() << "meta ball\n";
978  break;
979  case SAA_MNCRV:
980  softegg_cat.debug() << "nurbs curve\n";
981  break;
982  case SAA_MNSRF:
983  softegg_cat.debug() << "nurbs surf\n";
984  node_desc->get_transform(&scene, egg_group, TRUE);
985  make_nurb_surface(node_desc, egg_group, type);
986  break;
987  default:
988  softegg_cat.debug() << "unknown type: " << type << "\n";
989  }
990 
991  if (node_desc->is_joint())
992  node_desc->get_transform(&scene, egg_group, FALSE);
993 
994  return true;
995 }
996 
997 ////////////////////////////////////////////////////////////////////
998 // Function: SoftToEggConverter::make_polyset
999 // Access: Private
1000 // Description: Converts the indicated Soft polyset to a bunch of
1001 // EggPolygons and parents them to the indicated egg
1002 // group.
1003 ////////////////////////////////////////////////////////////////////
1004 void SoftToEggConverter::
1005 make_polyset(SoftNodeDesc *node_desc, EggGroup *egg_group, SAA_ModelType type) {
1006  int id = 0;
1007  int i, idx;
1008  int numShapes;
1009  SAA_Boolean valid;
1010  SAA_Boolean visible;
1011  PN_stdfloat *uCoords = NULL;
1012  PN_stdfloat *vCoords = NULL;
1013  string name = node_desc->get_name();
1014 
1015  SAA_modelGetNodeVisibility( &scene, node_desc->get_model(), &visible );
1016  softegg_cat.spam() << "model visibility: " << visible << endl;
1017 
1018  ///////////////////////////////////////////////////////////////////////
1019  // Only create egg polygon data if: the node is visible, and its not
1020  // a NULL or a Joint, and we're outputing polys (or if we are outputing
1021  // NURBS and the model is a poly mesh or a face)
1022  ///////////////////////////////////////////////////////////////////////
1023  if ( visible &&
1024  (type != SAA_MNILL) &&
1025  (type != SAA_MJNT) &&
1026  ((make_poly ||
1027  (make_nurbs && ((type == SAA_MSMSH) || (type == SAA_MFACE )) )) ||
1028  (!make_poly && !make_nurbs && make_duv &&
1029  ((type == SAA_MSMSH) || (type == SAA_MFACE )) ))
1030  )
1031  {
1032  // Get the number of key shapes
1033  SAA_modelGetNbShapes( &scene, node_desc->get_model(), &numShapes );
1034  softegg_cat.spam() << "process_model_node: num shapes: " << numShapes << endl;
1035 
1036  // load all node data from soft for this node_desc
1037  node_desc->load_poly_model(&scene, type);
1038 
1039  string vpool_name = name + ".verts";
1040  EggVertexPool *vpool = new EggVertexPool(vpool_name);
1041  vpool->set_highest_index(0);
1042 
1043  // add the vertices in the _tree._root node, so that
1044  // they will be written out first in egg file. This
1045  // solves a problem of soft-skinning trying to access
1046  // vertex pool before it is defined.
1047 
1048  _tree.get_egg_root()->insert(_tree.get_egg_root()->begin(), vpool);
1049 
1050  // We will need to transform all vertices from world coordinate
1051  // space into the vertex space appropriate to this node. Usually,
1052  // this is the same thing as world coordinate space, and this matrix
1053  // will be identity; but if the node is under an instance
1054  // (particularly, for instance, a billboard) then the vertex space
1055  // will be different from world space.
1056  LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
1057 
1058  // Asad: change from soft2egg.c. Here I am trying to get one triangles vertices not all
1059  for (idx=0; idx<node_desc->numTri; ++idx) {
1060  EggPolygon *egg_poly = new EggPolygon;
1061  egg_group->add_child(egg_poly);
1062 
1063  softegg_cat.spam() << "processing polygon " << idx << endl;
1064 
1065  // Is this a double sided polygon? meaning check for back face flag
1066  char *modelNoteStr = _tree.GetModelNoteInfo( &scene, node_desc->get_model() );
1067  if ( modelNoteStr != NULL ) {
1068  if ( strstr( modelNoteStr, "bface" ) != NULL )
1069  egg_poly->set_bface_flag(TRUE);
1070  }
1071 
1072  // read each triangle's control vertices into array
1073  SAA_SubElem cvertices[3];
1074  SAA_triangleGetCtrlVertices( &scene, node_desc->get_model(), node_desc->gtype, id, 1, node_desc->triangles+idx, cvertices );
1075 
1076  // read control vertices in this triangle
1077  SAA_DVector cvertPos[3];
1078  SAA_ctrlVertexGetPositions( &scene, node_desc->get_model(), 3, cvertices, cvertPos);
1079 
1080  // read indices of each vertices in this triangle
1081  int indices[3];
1082  indices[0] = indices[1] = indices[2] = 0;
1083  SAA_ctrlVertexGetIndices( &scene, node_desc->get_model(), 3, cvertices, indices );
1084 
1085  // read each control vertex's normals into an array
1086  SAA_DVector normals[3];
1087  SAA_ctrlVertexGetNormals( &scene, node_desc->get_model(), 3, cvertices, normals );
1088  for (i=0; i<3; ++i)
1089  softegg_cat.spam() << "normals[" << i <<"] = " << normals[i].x << " " << normals[i].y
1090  << " " << normals[i].z << " " << normals[i].w << "\n";
1091 
1092  // allocate arrays for u & v coords
1093  if (node_desc->textures) {
1094  if (node_desc->numTexLoc && node_desc->numTexTri[idx]) {
1095  // allocate arrays for u & v coords
1096  // I think there are one texture per triangle hence we need only 3 corrdinates
1097  uCoords = new PN_stdfloat[3];
1098  vCoords = new PN_stdfloat[3];
1099 
1100  // read the u & v coords into the arrays
1101  if ( uCoords != NULL && vCoords != NULL) {
1102  for ( i = 0; i < 3; i++ )
1103  uCoords[i] = vCoords[i] = 0.0f;
1104 
1105  // TODO: investigate the coord_cnt parameter...
1106  SAA_ctrlVertexGetUVTxtCoords( &scene, node_desc->get_model(), 3, cvertices,
1107  3, uCoords, vCoords );
1108  }
1109  else
1110  softegg_cat.info() << "Not enough Memory for texture coords...\n";
1111 
1112 #if 1
1113  for ( i=0; i<3; i++ )
1114  softegg_cat.spam() << "texcoords[" << i << "] = ( " << uCoords[i] << " , " << vCoords[i] <<" )\n";
1115 #endif
1116  }
1117  else if (node_desc->numTexGlb) {
1118  // allocate arrays for u & v coords
1119  uCoords = new PN_stdfloat[node_desc->numTexGlb*3];
1120  vCoords = new PN_stdfloat[node_desc->numTexGlb*3];
1121 
1122  for ( i = 0; i < node_desc->numTexGlb*3; i++ ) {
1123  uCoords[i] = vCoords[i] = 0.0f;
1124  }
1125 
1126  // read the u & v coords into the arrays
1127  if ( uCoords != NULL && vCoords != NULL) {
1128  SAA_triCtrlVertexGetGlobalUVTxtCoords( &scene, node_desc->get_model(), 3, cvertices,
1129  node_desc->numTexGlb, node_desc->textures, uCoords, vCoords );
1130  }
1131  else
1132  softegg_cat.info() << "Not enough Memory for texture coords...\n";
1133  }
1134  }
1135 
1136  for ( i=0; i < 3; i++ ) {
1137  EggVertex vert;
1138 
1139  // There are some conversions needed from local matrix to global coords
1140  SAA_DVector local = cvertPos[i];
1141  SAA_DVector global = {0};
1142 
1143  _VCT_X_MAT( global, local, node_desc->matrix );
1144 
1145  softegg_cat.spam() << "indices[" << i << "] = " << indices[i] << "\n";
1146  softegg_cat.spam() << "cvert[" << i << "] = " << cvertPos[i].x << " " << cvertPos[i].y
1147  << " " << cvertPos[i].z << " " << cvertPos[i].w << "\n";
1148  softegg_cat.spam() << " global cvert[" << i << "] = " << global.x << " " << global.y
1149  << " " << global.z << " " << global.w << "\n";
1150 
1151  // LPoint3d p3d(cvertPos[i].x, cvertPos[i].y, cvertPos[i].z);
1152  LPoint3d p3d(global.x, global.y, global.z);
1153  p3d = p3d * vertex_frame_inv;
1154  vert.set_pos(p3d);
1155 
1156  local = normals[i];
1157  _VCT_X_MAT( global, local, node_desc->matrix );
1158 
1159  softegg_cat.spam() << "normals[" << i <<"] = " << normals[i].x << " " << normals[i].y
1160  << " " << normals[i].z << " " << normals[i].w << "\n";
1161  softegg_cat.spam() << " global normals[" << i <<"] = " << global.x << " " << global.y
1162  << " " << global.z << " " << global.w << "\n";
1163 
1164  LVector3d n3d(global.x, global.y, global.z);
1165  n3d = n3d * vertex_frame_inv;
1166  vert.set_normal(n3d);
1167 
1168  // if texture present set the texture coordinates
1169  if (node_desc->textures) {
1170  PN_stdfloat u, v;
1171 
1172  if (uCoords && vCoords) {
1173  u = uCoords[i];
1174  v = 1.0f - vCoords[i];
1175  softegg_cat.spam() << "texcoords[" << i << "] = " << u << " "
1176  << v << endl;
1177 
1178  vert.set_uv(LTexCoordd(u, v));
1179  //vert.set_uv(LTexCoordd(uCoords[i], vCoords[i]));
1180  }
1181  }
1182  vert.set_external_index(indices[i]);
1183  egg_poly->add_vertex(vpool->create_unique_vertex(vert));
1184 
1185  // check to see if material is present
1186  PN_stdfloat r,g,b,a;
1187  SAA_elementIsValid( &scene, &node_desc->materials[idx], &valid );
1188  // material present - get the color
1189  if ( valid ) {
1190  SAA_materialGetDiffuse( &scene, &node_desc->materials[idx], &r, &g, &b );
1191  SAA_materialGetTransparency( &scene, &node_desc->materials[idx], &a );
1192  egg_poly->set_color(LColor(r, g, b, 1.0f - a));
1193  softegg_cat.spam() << "color r = " << r << " g = " << g << " b = " << b << " a = " << 1.0f - a << "\n";
1194  }
1195  else { // no material - default to white
1196  egg_poly->set_color(LColor(1.0, 1.0, 1.0, 1.0));
1197  softegg_cat.spam() << "default color\n";
1198  }
1199 
1200  /*
1201  // keep a one to one copy in this node's vpool
1202  EggVertex *t_vert = new EggVertex(vert);
1203  if (!t_vert) {
1204  softegg_cat.spam() << "out of memeory " << endl;
1205  nassertv(t_vert != NULL);
1206  }
1207  node_desc->get_vpool()->add_vertex(t_vert, indices[i]);
1208  */
1209 
1210  softegg_cat.spam() << "\n";
1211  }
1212 
1213  // Now apply the shader.
1214  if (node_desc->textures != NULL) {
1215  if (node_desc->numTexLoc && node_desc->numTexTri[idx]) {
1216  if (!strstr(node_desc->texNameArray[idx], "noIcon"))
1217  set_shader_attributes(node_desc, *egg_poly, idx);
1218  else
1219  softegg_cat.spam() << "texname :" << node_desc->texNameArray[idx] << endl;
1220  }
1221  else {
1222  if (!strstr(node_desc->texNameArray[0], "noIcon"))
1223  set_shader_attributes(node_desc, *egg_poly, 0);
1224  else
1225  softegg_cat.spam() << "texname :" << node_desc->texNameArray[0] << endl;
1226  }
1227  }
1228  }
1229  // if model has key shapes, generate vertex offsets
1230  if ( numShapes > 0 && make_morph )
1231  node_desc->make_vertex_offsets( numShapes);
1232  }
1233 }
1234 
1235 ////////////////////////////////////////////////////////////////////
1236 // Function: SoftToEggConverter::make_nurb_surface
1237 // Access: Private
1238 // Description: Converts the indicated Soft nurbs set to a bunch of
1239 // EggPolygons and parents them to the indicated egg
1240 // group.
1241 ////////////////////////////////////////////////////////////////////
1242 void SoftToEggConverter::
1243 make_nurb_surface(SoftNodeDesc *node_desc, EggGroup *egg_group, SAA_ModelType type) {
1244  int id = 0;
1245  int i, j, k;
1246  int numShapes;
1247  SAA_Boolean valid;
1248  SAA_Boolean visible;
1249  PN_stdfloat *uCoords = NULL;
1250  PN_stdfloat *vCoords = NULL;
1251  string name = node_desc->get_name();
1252 
1253  SAA_modelGetNodeVisibility( &scene, node_desc->get_model(), &visible );
1254  softegg_cat.spam() << "model visibility: " << visible << endl;
1255  softegg_cat.spam() << "nurbs!!!surface!!!" << endl;
1256 
1257  ///////////////////////////////////////
1258  // check to see if its a nurbs surface
1259  ///////////////////////////////////////
1260  if ( (type == SAA_MNSRF) && ( visible ) && (( make_nurbs )
1261  || ( !make_nurbs && !make_poly && make_duv )) )
1262  {
1263  // Get the number of key shapes
1264  SAA_modelGetNbShapes( &scene, node_desc->get_model(), &numShapes );
1265  softegg_cat.spam() << "process_model_node: num shapes: " << numShapes << endl;
1266 
1267  // load all node data from soft for this node_desc
1268  node_desc->load_nurbs_model(&scene, type);
1269 
1270  string vpool_name = name + ".verts";
1271  EggVertexPool *vpool = new EggVertexPool(vpool_name);
1272  vpool->set_highest_index(0);
1273 
1274  // add the vertices in the _tree._egg_root node, so that
1275  // they will be written out first in egg file. This
1276  // solves a problem of soft-skinning trying to access
1277  // vertex pool before it is defined.
1278 
1279  //_tree.get_egg_root()->add_child(vpool);
1280  _tree.get_egg_root()->insert(_tree.get_egg_root()->begin(), vpool);
1281 
1282  //egg_group->add_child(vpool);
1283 
1284  /*
1285  // create a copy of vpool in node_desc which will be used later
1286  // for soft_skinning
1287  node_desc->create_vpool(vpool_name);
1288  */
1289 
1290  int uRows, vRows;
1291  int uKnots, vKnots;
1292  int uExtra, vExtra;
1293  int uDegree, vDegree;
1294  int uCurves, vCurves;
1295 
1296  vector <double> Knots;
1297 
1298  EggNurbsSurface *eggNurbs = new EggNurbsSurface( name );
1299 
1300  // create nurbs representation of surface
1301  SAA_nurbsSurfaceGetDegree( &scene, node_desc->get_model(), &uDegree, &vDegree );
1302  softegg_cat.spam() << "nurbs degree: " << uDegree << " u, " << vDegree << " v\n";
1303 
1304  SAA_nurbsSurfaceGetNbKnots( &scene, node_desc->get_model(), &uKnots, &vKnots );
1305  softegg_cat.spam() << "nurbs knots: " << uKnots << " u, " << vKnots << " v\n";
1306 
1307  SAA_Boolean uClosed = FALSE;
1308  SAA_Boolean vClosed = FALSE;
1309 
1310  SAA_nurbsSurfaceGetClosed( &scene, node_desc->get_model(), &uClosed, &vClosed);
1311 
1312  uExtra = vExtra = 2;
1313  if ( uClosed ) {
1314  softegg_cat.spam() << "nurbs is closed in u...\n";
1315  uExtra += 4;
1316  }
1317  if ( vClosed ) {
1318  softegg_cat.spam() << "nurbs is closed in v...\n";
1319  vExtra += 4;
1320  }
1321  eggNurbs->setup(uDegree+1, vDegree+1,
1322  uKnots + uExtra, vKnots + vExtra);
1323 
1324  softegg_cat.spam() << "from eggNurbs: num u knots " << eggNurbs->get_num_u_knots() << endl;
1325  softegg_cat.spam() << "from eggNurbs: num v knots " << eggNurbs->get_num_v_knots() << endl;
1326  softegg_cat.spam() << "from eggNurbs: num u cvs " << eggNurbs->get_num_u_cvs() << endl;
1327  softegg_cat.spam() << "from eggNurbs: num v cvs " << eggNurbs->get_num_v_cvs() << endl;
1328 
1329  SAA_nurbsSurfaceGetNbVertices( &scene, node_desc->get_model(), &uRows, &vRows );
1330  softegg_cat.spam() << "nurbs vertices: " << uRows << " u, " << vRows << " v\n";
1331 
1332  SAA_nurbsSurfaceGetNbCurves( &scene, node_desc->get_model(), &uCurves, &vCurves );
1333  softegg_cat.spam() << "nurbs curves: " << uCurves << " u, " << vCurves << " v\n";
1334 
1335  if ( shift_textures ) {
1336  if ( uClosed )
1337  // shift starting point on NURBS surface for correct textures
1338  SAA_nurbsSurfaceShiftParameterization( &scene, node_desc->get_model(), -2, 0 );
1339 
1340  if ( vClosed )
1341  // shift starting point on NURBS surface for correct textures
1342  SAA_nurbsSurfaceShiftParameterization( &scene, node_desc->get_model(), 0, -2 );
1343  }
1344 
1345  SAA_nurbsSurfaceSetStep( &scene, node_desc->get_model(), nurbs_step, nurbs_step );
1346 
1347  // Is this a double sided polygon? meaning check for back face flag
1348  char *modelNoteStr = _tree.GetModelNoteInfo( &scene, node_desc->get_model() );
1349  if ( modelNoteStr != NULL ) {
1350  if ( strstr( modelNoteStr, "bface" ) != NULL ) {
1351  eggNurbs->set_bface_flag(TRUE);
1352  softegg_cat.spam() << "Set backface flag\n";
1353  }
1354  }
1355 
1356  double *uKnotArray = new double[uKnots];
1357  double *vKnotArray = new double[vKnots];
1358  result = SAA_nurbsSurfaceGetKnots( &scene, node_desc->get_model(), node_desc->gtype, 0,
1359  uKnots, vKnots, uKnotArray, vKnotArray );
1360 
1361  if (result != SI_SUCCESS) {
1362  softegg_cat.spam() << "Couldn't get knots\n";
1363  exit(1);
1364  }
1365 
1366  // Lets prepare the softimage knots and then assign to eggKnots
1367  add_knots( Knots, uKnotArray, uKnots, uClosed, uDegree );
1368  softegg_cat.spam() << "u knots: ";
1369  for (i = 0; i < (int)Knots.size(); i++) {
1370  softegg_cat.spam() << Knots[i] << " ";
1371  eggNurbs->set_u_knot(i, Knots[i]);
1372  }
1373  softegg_cat.spam() << endl;
1374 
1375  Knots.resize(0);
1376  add_knots( Knots, vKnotArray, vKnots, vClosed, vDegree );
1377  softegg_cat.spam() << "v knots: ";
1378  for (i = 0; i < (int)Knots.size(); i++) {
1379  softegg_cat.spam() << Knots[i] << " ";
1380  eggNurbs->set_v_knot(i, Knots[i]);
1381  }
1382  softegg_cat.spam() << endl;
1383 
1384  // lets get the number of vertices from softimage
1385  int numVert;
1386  SAA_modelGetNbVertices( &scene, node_desc->get_model(), &numVert );
1387 
1388  softegg_cat.spam() << numVert << " CV's\n";
1389 
1390  // get the CV's
1391  SAA_DVector *vertices = NULL;
1392  vertices = new SAA_DVector[numVert];
1393 
1394  SAA_modelGetVertices( &scene, node_desc->get_model(), node_desc->gtype, 0, numVert, vertices );
1395 
1396  LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
1397 
1398  // create the buffer for EggVertices
1399  EggVertex *verts = new EggVertex[numVert];
1400 
1401  softegg_cat.spam() << endl << eggNurbs->get_num_cvs() << endl << endl;
1402 
1403  //for ( i = 0; i<eggNurbs->get_num_cvs(); i++ ) {
1404  for ( k = 0; k<numVert; k++ ) {
1405  SAA_DVector global;
1406 
1407  /*
1408  int ui = eggNurbs->get_u_index(i);
1409  int vi = eggNurbs->get_v_index(i);
1410 
1411  int k = vRows * ui + vi;
1412 
1413  softegg_cat.spam() << i << ": ui " << ui << ", vi " << vi << ", k " << k << endl;
1414 
1415  softegg_cat.spam() << "original cv[" << k << "] = "
1416  << vertices[k].x << " " << vertices[k].y << " "
1417  << vertices[k].z << " " << vertices[k].w << endl;
1418  */
1419 
1420  // convert to global coords
1421  _VCT_X_MAT( global, vertices[k], node_desc->matrix );
1422 
1423  //preserve original weight
1424  global.w = vertices[k].w;
1425 
1426  // normalize coords to weight
1427  global.x *= global.w;
1428  global.y *= global.w;
1429  global.z *= global.w;
1430 
1431  /*
1432  softegg_cat.spam() << "global cv[" << k << "] = "
1433  << global.x << " " << global.y << " "
1434  << global.x << " " << global.w << endl;
1435  */
1436 
1437  LPoint4d p4d(global.x, global.y, global.z, global.w);
1438  p4d = p4d * vertex_frame_inv;
1439  verts[k].set_pos(p4d);
1440 
1441  // check to see if material is present
1442  if (node_desc->numNurbMats) {
1443  PN_stdfloat r,g,b,a;
1444  SAA_elementIsValid( &scene, &node_desc->materials[0], &valid );
1445  // material present - get the color
1446  if ( valid ) {
1447  SAA_materialGetDiffuse( &scene, &node_desc->materials[0], &r, &g, &b );
1448  SAA_materialGetTransparency( &scene, &node_desc->materials[0], &a );
1449  verts[k].set_color(LColor(r, g, b, 1.0f - a));
1450  //softegg_cat.spam() << "color r = " << r << " g = " << g << " b = " << b << " a = " << a << "\n";
1451  }
1452  else { // no material - default to white
1453  verts[k].set_color(LColor(1.0, 1.0, 1.0, 1.0));
1454  softegg_cat.spam() << "default color\n";
1455  }
1456  }
1457  vpool->add_vertex(verts+k, k);
1458  eggNurbs->add_vertex(vpool->get_vertex(k));
1459 
1460  if ( uClosed ) {
1461  // add first uDegree verts to end of row
1462  if ( (k % uRows) == ( uRows - 1) ) {
1463  for ( i = 0; i < uDegree; i++ ) {
1464  // add vref's to NURBS info
1465  eggNurbs->add_vertex( vpool->get_vertex(i+((k/uRows)*uRows)) );
1466  }
1467  }
1468  }
1469  }
1470 
1471  // check to see if the NURB is closed in v
1472  if ( vClosed && !uClosed ) {
1473  // add first vDegree rows of verts to end of list
1474  for ( int i = 0; i < vDegree*uRows; i++ )
1475  eggNurbs->add_vertex( vpool->get_vertex(i) );
1476  }
1477  // check to see if the NURB is closed in u and v
1478  else if ( vClosed && uClosed ) {
1479  // add the first (degree) v verts and a few
1480  // extra - for good measure
1481  for ( i = 0; i < vDegree; i++ ) {
1482  // add first vDegree rows of verts to end of list
1483  for ( j = 0; j < uRows; j++ )
1484  eggNurbs->add_vertex( vpool->get_vertex(j+(i*uRows)) );
1485 
1486  // if u is closed to we have added uDegree
1487  // verts onto the ends of the rows - add them here too
1488  for ( k = 0; k < uDegree; k++ )
1489  eggNurbs->add_vertex( vpool->get_vertex(k+(i*uRows)+((k/uRows)*uRows)) );
1490  }
1491  }
1492 
1493  // We add the NURBS to the group down here, after all of the vpools
1494  // for the trim curves have been added.
1495  egg_group->add_child(eggNurbs);
1496 
1497  // Now apply the shader.
1498  if (node_desc->textures != NULL) {
1499  if (!strstr(node_desc->texNameArray[0], "noIcon"))
1500  set_shader_attributes(node_desc, *eggNurbs, 0);
1501  else
1502  softegg_cat.spam() << "texname :" << node_desc->texNameArray[0] << endl;
1503  }
1504 
1505  // if model has key shapes, generate vertex offsets
1506  if ( numShapes > 0 && make_morph )
1507  node_desc->make_vertex_offsets( numShapes);
1508  }
1509 }
1510 
1511 ////////////////////////////////////////////////////////////////////
1512 // Function: add_knots
1513 // Access: Public
1514 // Description: Given a parametric surface, and its knots, create
1515 // the appropriate egg structure by filling in Soft's
1516 // implicit knots and assigning the rest to eggKnots.
1517 ////////////////////////////////////////////////////////////////////
1518 void SoftToEggConverter::
1519 add_knots( vector <double> &eggKnots, double *knots, int numKnots, SAA_Boolean closed, int degree ) {
1520 
1521  int k = 0;
1522  double lastKnot = knots[0];
1523  double *newKnots;
1524 
1525  // add initial implicit knot(s)
1526  if ( closed ) {
1527  int i = 0;
1528  newKnots = new double[degree];
1529 
1530  // need to add (degree) number of knots
1531  for ( k = numKnots - 1; k >= numKnots - degree; k-- ) {
1532  // we have to know these in order to calculate
1533  // next knot value so hold them in temp array
1534  newKnots[i] = lastKnot - (knots[k] - knots[k-1]);
1535  lastKnot = newKnots[i];
1536  i++;
1537  }
1538  for ( k = degree - 1; k >= 0; k-- ) {
1539  eggKnots.push_back( newKnots[k] );
1540  softegg_cat.spam() << "knots[" << k << "] = " << newKnots[k] << endl;
1541  }
1542  }
1543  else {
1544  eggKnots.push_back( knots[k] );
1545  softegg_cat.spam() << "knots[" << k << "] = " << knots[k] << endl;
1546  }
1547 
1548  // add the regular complement of knots
1549  for (k = 0; k < numKnots; k++) {
1550  eggKnots.push_back( knots[k] );
1551  softegg_cat.spam() << "knots[" << k+1 << "] = " << knots[k] << endl;
1552  }
1553 
1554  lastKnot = knots[numKnots-1];
1555 
1556  // add trailing implicit knots
1557  if ( closed ) {
1558  // need to add (degree) number of knots
1559  for ( k = 1; k <= degree; k++ ) {
1560  eggKnots.push_back( lastKnot + (knots[k] - knots[k-1]) );
1561  softegg_cat.spam() << "knots[" << k << "] = " << lastKnot + (knots[k] - knots[k-1]) << endl;
1562  lastKnot = lastKnot + (knots[k] - knots[k-1]);
1563  }
1564  }
1565  else {
1566  eggKnots.push_back( knots[k-1] );
1567  softegg_cat.spam() << "knots[" << k+1 << "] = " << knots[k-1] << endl;
1568  }
1569 }
1570 
1571 ////////////////////////////////////////////////////////////////////
1572 // Function: FindClosestTriVert
1573 // Access: Public
1574 // Description: Given an egg vertex pool, map each vertex therein to
1575 // a vertex within an array of SAA model vertices of
1576 // size numVert. Mapping is done by closest proximity.
1577 ////////////////////////////////////////////////////////////////////
1579 FindClosestTriVert( EggVertexPool *vpool, SAA_DVector *vertices, int numVert ) {
1580  int i,j;
1581  int *vertMap = NULL;
1582  int vpoolSize = (int)vpool->size();
1583  PN_stdfloat closestDist;
1584  PN_stdfloat thisDist;
1585  int closest;
1586 
1587  vertMap = new int[vpoolSize];
1588  i = 0;
1590  for (vi = vpool->begin(); vi != vpool->end(); ++vi, ++i) {
1591  EggVertex *vert = (*vi);
1592  softegg_cat.spam() << "vert external index = " << vert->get_external_index() << endl;
1593  // softegg_cat.spam() << "found vert " << vert << endl;
1594  // softegg_cat.spam() << "vert [" << i << "] " << vpool->get_vertex(i+1);
1595  LPoint3d p3d = vert->get_pos3();
1596 
1597  // find closest model vertex
1598  for ( j = 0; j < numVert; j++ ) {
1599  // calculate distance
1600  thisDist = sqrtf(
1601  powf( p3d[0] - vertices[j].x , 2 ) +
1602  powf( p3d[1] - vertices[j].y , 2 ) +
1603  powf( p3d[2] - vertices[j].z , 2 ) );
1604 
1605  // remember this if its the closest so far
1606  if ( !j || ( thisDist < closestDist ) ) {
1607  closest = j;
1608  closestDist = thisDist;
1609  }
1610  }
1611 
1612  vertMap[i] = closest;
1613  softegg_cat.spam() << "mapping v " << i << " of " << vpoolSize-1 << ":( "
1614  << p3d[0] << " "
1615  << p3d[1] << " "
1616  << p3d[2] << ")\n";
1617 
1618  softegg_cat.spam() << " to cv " << closest << " of " << numVert-1 << ":( "
1619  << vertices[closest].x << " "
1620  << vertices[closest].y << " "
1621  << vertices[closest].z << " )\tdelta = " << closestDist << endl;
1622  }
1623  return vertMap;
1624 }
1625 
1626 ////////////////////////////////////////////////////////////////////
1627 // Function: SoftToEggConverter::make_soft_skin
1628 // Access: Private
1629 // Description: make soft skin assignments to the mesh
1630 // finally call cleanup_soft_skin to clean it up
1631 ////////////////////////////////////////////////////////////////////
1632 bool SoftToEggConverter::
1633 make_soft_skin() {
1634  int num_nodes = _tree.get_num_nodes();
1635  SoftNodeDesc *node_desc;
1636  SAA_Boolean isSkeleton;
1637 
1638  softegg_cat.spam() << endl << "----------------------------------------------------------------" << endl;
1639 
1640  for (int i = 0; i < num_nodes; i++) {
1641  node_desc = _tree.get_node(i);
1642  SAA_modelIsSkeleton( &scene, node_desc->get_model(), &isSkeleton );
1643 
1644  softegg_cat.spam() << "??checking node " << node_desc->get_name() << " isSkel " << isSkeleton << " isJoint " << node_desc->is_joint() << endl;
1645  if (isSkeleton && node_desc->is_joint()) {
1646 
1647  if (node_desc->is_partial(search_prefix))
1648  continue;
1649 
1650  // Now that we've added all the polygons (and created all the
1651  // vertices), go back through the vertex pool and set up the
1652  // appropriate joint membership for each of the vertices.
1653 
1654  // check for envelops
1655  int numEnv;
1656  SAA_ModelType type;
1657  SAA_Elem *envelopes;
1658  SAA_Elem *model = node_desc->get_model();
1659  EggGroup *joint = NULL;
1660  EggVertexPool *vpool;
1661 
1662  SAA_skeletonGetNbEnvelopes( &scene, model, &numEnv );
1663  if ( numEnv == 0 ) {
1664  softegg_cat.spam() << "no soft skinning for joint " << node_desc->get_name() << endl;
1665  continue;
1666  }
1667 
1668  // it's got envelopes - must be soft skinned
1669  softegg_cat.spam() << endl << "found skeleton part( " << node_desc->get_name() << ")!\n";
1670  softegg_cat.spam() << "numEnv = " << numEnv << endl;
1671  // allocate envelope array
1672  envelopes = new SAA_Elem[numEnv];
1673  if ( envelopes == NULL ) {
1674  softegg_cat.info() << "Out Of Memory" << endl;
1675  exit(1);
1676  }
1677  int thisEnv;
1678  SAA_EnvType envType;
1679  bool hasEnvVertices = 0;
1680 
1681  SAA_skeletonGetEnvelopes( &scene, model, numEnv, envelopes );
1682  for ( thisEnv = 0; thisEnv < numEnv; thisEnv++ ) {
1683  softegg_cat.spam() << "env[" << thisEnv << "]: ";
1684  SAA_envelopeGetType( &scene, &envelopes[thisEnv], &envType );
1685 
1686  if ( envType == SAA_ENVTYPE_NONE ) {
1687  softegg_cat.spam() << "envType = none\n";
1688  }
1689  else if ( envType == SAA_ENVTYPE_FLXLCL ) {
1690  softegg_cat.spam() << "envType = flexible, local\n";
1691  hasEnvVertices = 1;
1692  }
1693  else if ( envType == SAA_ENVTYPE_FLXGLB ) {
1694  softegg_cat.spam() << "envType = flexible, global\n";
1695  hasEnvVertices = 1;
1696  }
1697  else if ( envType == SAA_ENVTYPE_RGDGLB ) {
1698  softegg_cat.spam() << "envType = rigid, global\n";
1699  hasEnvVertices = 1;
1700  }
1701  else {
1702  softegg_cat.spam() << "envType = unknown\n";
1703  }
1704 
1705  }
1706  if ( !hasEnvVertices )
1707  continue;
1708 
1709  SAA_SubElem *envVertices = NULL;
1710  int *numEnvVertices;
1711  int i,j,k;
1712 
1713  numEnvVertices = new int[numEnv];
1714 
1715  if ( numEnvVertices != NULL ) {
1716  SAA_envelopeGetNbCtrlVertices( &scene, model, numEnv, envelopes, numEnvVertices );
1717  int totalEnvVertices = 0;
1718  for( i = 0; i < numEnv; i++ ) {
1719  totalEnvVertices += numEnvVertices[i];
1720  softegg_cat.spam() << "numEnvVertices[" << i << "] = " << numEnvVertices[i] << endl;
1721  }
1722  softegg_cat.spam() << "total env verts = " << totalEnvVertices << endl;
1723  if ( totalEnvVertices == 0 )
1724  continue;
1725 
1726  envVertices = new SAA_SubElem[totalEnvVertices];
1727  if ( envVertices != NULL ) {
1728  result = SAA_envelopeGetCtrlVertices( &scene, model,
1729  numEnv, envelopes, numEnvVertices, envVertices);
1730  if (result != SI_SUCCESS) {
1731  softegg_cat.spam() << "error: GetCtrlVertices\n";
1732  exit(1);
1733  }
1734  // loop through for each envelope
1735  for ( i = 0; i < numEnv; i++ ) {
1736  PN_stdfloat *weights = NULL;
1737  int vertArrayOffset = 0;
1738  softegg_cat.spam() << "envelope[" << i << "]: ";
1739  weights = new PN_stdfloat[numEnvVertices[i]];
1740  if ( weights ) {
1741  char *envName;
1742  int *vpoolMap = NULL;
1743  for ( j = 0; j < i; j++ )
1744  vertArrayOffset += numEnvVertices[j];
1745  softegg_cat.spam() << "envVertArray offset = " << vertArrayOffset;
1746 
1747  /*
1748  if (vertArrayOffset == totalEnvVertices) {
1749  softegg_cat.spam() << endl; vpoolMap = FindClosestTriVert( vpool, globalModelVertices, modelNumVert );
1750 
1751  break;
1752  }
1753  */
1754 
1755  // get the weights of the envelope vertices
1756  result = SAA_ctrlVertexGetEnvelopeWeights( &scene, model, &envelopes[i],
1757  numEnvVertices[i],
1758  &envVertices[vertArrayOffset], weights );
1759 
1760  // Get the name of the envelope model
1761  if ( use_prefix ) {
1762  // Get the FULL name of the envelope
1763  envName = _tree.GetFullName( &scene, &envelopes[i] );
1764  }
1765  else {
1766  // Get the name of the envelope
1767  envName = _tree.GetName( &scene, &envelopes[i] );
1768  }
1769 
1770  softegg_cat.spam() << " envelop name is [" << envName << "]" << endl;
1771 
1772  if (result != SI_SUCCESS) {
1773  softegg_cat.spam() << "warning: this envelop doesn't have any weights\n";
1774  continue;
1775  }
1776 
1777  result = SAA_modelGetType( &scene, &envelopes[i], &type );
1778  if (result != SI_SUCCESS) {
1779  softegg_cat.debug() << "choked on get type\n";
1780  exit(1);
1781  }
1782 
1783  softegg_cat.spam() << "envelope model type ";
1784  if ( type == SAA_MSMSH )
1785  softegg_cat.spam() << "MESH\n";
1786  else if ( type == SAA_MNSRF )
1787  softegg_cat.spam() << "NURBS\n";
1788  else
1789  softegg_cat.spam() << "OTHER\n";
1790 
1791  int *envVtxIndices = NULL;
1792  envVtxIndices = new int[numEnvVertices[i]];
1793 
1794  // Get the envelope vertex indices
1795  result = SAA_ctrlVertexGetIndices( &scene, &envelopes[i], numEnvVertices[i],
1796  &envVertices[vertArrayOffset], envVtxIndices );
1797 
1798  if (result != SI_SUCCESS) {
1799  softegg_cat.debug() << "error: choked on get indices\n";
1800  exit(1);
1801  }
1802 
1803  // find out how many vertices the model has
1804  int modelNumVert;
1805 
1806  SAA_modelGetNbVertices( &scene, &envelopes[i], &modelNumVert );
1807 
1808  SAA_DVector *modelVertices = NULL;
1809  modelVertices = new SAA_DVector[modelNumVert];
1810 
1811  // get the model vertices
1812  SAA_modelGetVertices( &scene, &envelopes[i],
1813  SAA_GEOM_ORIGINAL, 0, modelNumVert,
1814  modelVertices );
1815 
1816  // create array of global model coords
1817  SAA_DVector *globalModelVertices = NULL;
1818  globalModelVertices = new SAA_DVector[modelNumVert];
1819  PN_stdfloat matrix[4][4];
1820 
1821  // tranform local model vert coords to global
1822 
1823  // first get the global matrix
1824  SAA_modelGetMatrix( &scene, &envelopes[i], SAA_COORDSYS_GLOBAL, matrix );
1825 
1826  // populate array of global model verts
1827  for ( j = 0; j < modelNumVert; j++ ) {
1828  _VCT_X_MAT( globalModelVertices[j],
1829  modelVertices[j], matrix );
1830  }
1831 
1832  // Get the vpool
1833  string s_name = envName;
1834  SoftNodeDesc *mesh_node = find_node(s_name);
1835  if (!mesh_node) {
1836  softegg_cat.debug() << "error: node " << s_name << " not found in tree\n";
1837  exit(1);
1838  }
1839  string vpool_name = s_name + ".verts";
1840  EggNode *t = _tree.get_egg_root()->find_child(vpool_name);
1841  if (t)
1842  DCAST_INTO_R(vpool, t, NULL);
1843 
1844  // find the mapping of the vertices that match this envelop
1845  if (vpool) {
1846  softegg_cat.spam() << "found vpool of size " << vpool->size() << endl;
1847  if ( !make_nurbs || (type == SAA_MSMSH) ) {
1848  vpoolMap = FindClosestTriVert( vpool, globalModelVertices, modelNumVert );
1849  }
1850  }
1851  else {
1852  softegg_cat.debug() << "warning: vpool " << vpool_name << " not found\n";
1853  continue; // could be because of not visible
1854  }
1855 
1856  joint = node_desc->get_egg_group();
1857  // for every envelope vertex
1858  for (j = 0; j < numEnvVertices[i]; j++) {
1859  double scaledWeight = weights[j]/ 100.0f;
1860 
1861  // make sure its in legal range
1862  if (( envVtxIndices[j] < modelNumVert )
1863  && ( envVtxIndices[j] >= 0 )) {
1864  if ( (type == SAA_MNSRF) && make_nurbs ) {
1865  // assign all referenced control vertices
1866  EggVertex *vert = vpool->get_vertex(envVtxIndices[j]);
1867  if (!vert) {
1868  softegg_cat.debug() << "possible error: index " << envVtxIndices[j] << ": vert is " << vert << endl;
1869  continue;
1870  }
1871  joint->ref_vertex( vert, scaledWeight );
1872  softegg_cat.spam() << j << ": adding vref to cv " << envVtxIndices[j]
1873  << " with weight " << scaledWeight << endl;
1874 
1875  /*
1876  envPool->Vertex(envVtxIndices[j])->AddJoint( joint, scaledWeight );
1877  // set flag to show this vertex has
1878  // been assigned
1879  envPool->Vertex(envVtxIndices[j])->multipleJoints = 1;
1880  */
1881  }
1882  else {
1883  //assign all the tri verts associated
1884  // with this control vertex to joint
1885  softegg_cat.spam() << j << "--trying to find " << envVtxIndices[j] << endl;
1886  for ( k = 0; k < (int)vpool->size(); k++ ) {
1887  if ( vpoolMap[k] == envVtxIndices[j] ) {
1888  EggVertex *vert = vpool->get_vertex(k+1);
1889  // EggVertex *vert = mesh_node->get_vpool()->get_vertex(vpoolMap[k]+1);
1890  if (!vert) {
1891  softegg_cat.debug() << "possible error: index " << k+1 << ": vert is " << vert << endl;
1892  break;
1893  }
1894 
1895  joint->ref_vertex(vert, scaledWeight);
1896  softegg_cat.spam() << j << ": adding vref from cv " << envVtxIndices[j]
1897  << " to vert " << k+1 << " with weight " << scaledWeight
1898  << "(vpool)\n";
1899  /*
1900  envPool->Vertex(k)->AddJoint( joint, scaledWeight );
1901  // set flag to show this vertex has
1902  // been assigned
1903  envPool->Vertex(k)->multipleJoints = 1;
1904  */
1905  }
1906  }
1907  }
1908  }
1909  }
1910  }
1911  }
1912  }
1913  }
1914  }
1915  }
1916  return true;
1917 }
1918 ////////////////////////////////////////////////////////////////////
1919 // Function: cleanup_soft_skin
1920 // Access: Public
1921 // Description: Given a model, make sure all its vertices have been
1922 // soft assigned. If not hard assign to the last
1923 // joint we saw.
1924 ////////////////////////////////////////////////////////////////////
1925 bool SoftToEggConverter::
1926 cleanup_soft_skin()
1927 {
1928  int num_nodes = _tree.get_num_nodes();
1929  SoftNodeDesc *node_desc;
1930 
1931  softegg_cat.spam() << endl << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl;
1932 
1933  for (int i = 0; i < num_nodes; i++) {
1934  node_desc = _tree.get_node(i);
1935  if (node_desc->is_partial(search_prefix))
1936  continue;
1937 
1938  SAA_Elem *model = node_desc->get_model();
1939  EggGroup *joint = NULL;
1940  EggVertexPool *vpool = NULL;
1941  SAA_ModelType type;
1942 
1943  // find out what type of node we're dealing with
1944 
1945  SAA_modelGetType( &scene, model, &type );
1946 
1947  softegg_cat.debug() << "Cleaning up model------- " << node_desc->get_name() << endl;
1948 
1949  // this step is weird - I think I want it here but it seems
1950  // to break some models. Files like props-props_wh_cookietime.3-0 in
1951  // /ful/rnd/pub/vrml/chip/chips_adventure/char/zone1/rooms/warehouse_final
1952  // need to do the "if (skel)" bit.
1953 
1954  //find the vpool for this model
1955  string vpool_name = node_desc->get_name() + ".verts";
1956  EggNode *t = _tree.get_egg_root()->find_child(vpool_name);
1957  if (t)
1958  DCAST_INTO_R(vpool, t, NULL);
1959 
1960  if (!vpool) {
1961  //softegg_cat.spam() << "couldn't find vpool " << vpool_name << endl;
1962  continue;
1963  }
1964 
1965  int numVerts = (int)vpool->size();
1966  softegg_cat.spam() << "found vpool " << vpool_name << " w/ " << numVerts << " verts\n";
1967 
1968  // if this node is a joint, then these vertices belong
1969  // to this joint
1970  if (node_desc->is_joint())
1971  joint = node_desc->get_egg_group();
1972  else {
1973  // find the closest _parentJoint
1974  SoftNodeDesc *parentJ = node_desc;
1975  while( parentJ && !parentJ->_parentJoint) {
1976  if ( parentJ->_parent) {
1977  SAA_Boolean isSkeleton;
1978  //softegg_cat.spam() << " checking parent " << parentJ->_parent->get_name() << endl;
1979  if (parentJ->_parent->has_model())
1980  SAA_modelIsSkeleton( &scene, parentJ->_parent->get_model(), &isSkeleton );
1981 
1982  if (isSkeleton) {
1983  joint = parentJ->_parent->get_egg_group();
1984  softegg_cat.spam() << "parent to " << parentJ->_parent->get_name() << endl;
1985  break;
1986  }
1987 
1988  parentJ = parentJ->_parent;
1989  }
1990  else
1991  break;
1992  }
1993  if (!joint && (!parentJ || !parentJ->_parentJoint)) {
1994  softegg_cat.spam() << node_desc->get_name() << " has no _parentJoint?!" << endl;
1995  continue;
1996  }
1997 
1998  if (!joint) {
1999  softegg_cat.spam() << "parent joint to " << parentJ->_parentJoint->get_name() << endl;
2000  joint = parentJ->_parentJoint->get_egg_group();
2001  }
2002  }
2004  double membership = 1.0f;
2005  for ( vi = vpool->begin(); vi != vpool->end(); ++vi) {
2006  EggVertex *vert = (*vi);
2007 
2008  // if this vertex has not been soft assigned, then hard assign it to the parentJoint
2009  if ( vert->gref_size() == 0 ) {
2010 
2011  softegg_cat.spam() << "vert " << vert->get_external_index() << " not assigned!\n";
2012 
2013  // hard skin this vertex
2014  joint->ref_vertex( vert, 1.0f );
2015  }
2016  }
2017  }
2018  return true;
2019 }
2020 
2021 ////////////////////////////////////////////////////////////////////
2022 // Function: SoftShader::set_shader_attributes
2023 // Access: Private
2024 // Description: Applies the known shader attributes to the indicated
2025 // egg primitive.
2026 ////////////////////////////////////////////////////////////////////
2027 void SoftToEggConverter::
2028 set_shader_attributes(SoftNodeDesc *node_desc, EggPrimitive &primitive, int idx) {
2029  char *texName = node_desc->texNameArray[idx];
2030  EggTexture tex(texName, "");
2031 
2032  Filename filename = Filename::from_os_specific(texName);
2033  Filename fullpath = _path_replace->match_path(filename, get_model_path());
2034  tex.set_filename(_path_replace->store_path(fullpath));
2035  tex.set_fullpath(fullpath);
2036  // tex.set_format(EggTexture::F_rgb);
2037  apply_texture_properties(tex, node_desc->uRepeat[idx], node_desc->vRepeat[idx]);
2038 
2039  EggTexture *new_tex = _textures.create_unique_texture(tex, ~EggTexture::E_tref_name);
2040  primitive.set_texture(new_tex);
2041 }
2042 
2043 ////////////////////////////////////////////////////////////////////
2044 // Function: SoftShader::apply_texture_properties
2045 // Access: Private
2046 // Description: Applies all the appropriate texture properties to the
2047 // EggTexture object, including wrap modes and texture
2048 // matrix.
2049 ////////////////////////////////////////////////////////////////////
2050 void SoftToEggConverter::
2051 apply_texture_properties(EggTexture &tex, int uRepeat, int vRepeat) {
2052  // Let's mipmap all textures by default.
2053  tex.set_minfilter(EggTexture::FT_linear_mipmap_linear);
2054  tex.set_magfilter(EggTexture::FT_linear);
2055 
2056  EggTexture::WrapMode wrap_u = uRepeat > 0 ? EggTexture::WM_repeat : EggTexture::WM_clamp;
2057  EggTexture::WrapMode wrap_v = vRepeat > 0 ? EggTexture::WM_repeat : EggTexture::WM_clamp;
2058 
2059  tex.set_wrap_u(wrap_u);
2060  tex.set_wrap_v(wrap_v);
2061  /*
2062  LMatrix3d mat = color_def.compute_texture_matrix();
2063  if (!mat.almost_equal(LMatrix3d::ident_mat())) {
2064  tex.set_transform(mat);
2065  }
2066  */
2067 }
2068 #if 0
2069 ////////////////////////////////////////////////////////////////////
2070 // Function: SoftShader::compare_texture_properties
2071 // Access: Private
2072 // Description: Compares the texture properties already on the
2073 // texture (presumably set by a previous call to
2074 // apply_texture_properties()) and returns false if they
2075 // differ from that specified by the indicated color_def
2076 // object, or true if they match.
2077 ////////////////////////////////////////////////////////////////////
2078 bool SoftToEggConverter::
2079 compare_texture_properties(EggTexture &tex,
2080  const SoftShaderColorDef &color_def) {
2081  bool okflag = true;
2082 
2083  EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp;
2084  EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp;
2085 
2086  if (wrap_u != tex.determine_wrap_u()) {
2087  // Choose the more general of the two.
2088  if (wrap_u == EggTexture::WM_repeat) {
2089  tex.set_wrap_u(wrap_u);
2090  }
2091  okflag = false;
2092  }
2093  if (wrap_v != tex.determine_wrap_v()) {
2094  if (wrap_v == EggTexture::WM_repeat) {
2095  tex.set_wrap_v(wrap_v);
2096  }
2097  okflag = false;
2098  }
2099 
2100  LMatrix3d mat = color_def.compute_texture_matrix();
2101  if (!mat.almost_equal(tex.get_transform())) {
2102  okflag = false;
2103  }
2104 
2105  return okflag;
2106 }
2107 #endif
2108 ////////////////////////////////////////////////////////////////////
2109 // Function: SoftShader::reparent_decals
2110 // Access: Private
2111 // Description: Recursively walks the egg hierarchy, reparenting
2112 // "decal" type nodes below their corresponding
2113 // "decalbase" type nodes, and setting the flags.
2114 //
2115 // Returns true on success, false if some nodes were
2116 // incorrect.
2117 ////////////////////////////////////////////////////////////////////
2118 bool SoftToEggConverter::
2119 reparent_decals(EggGroupNode *egg_parent) {
2120  bool okflag = true;
2121 
2122  // First, walk through all children of this node, looking for the
2123  // one decal base, if any.
2124  EggGroup *decal_base = (EggGroup *)NULL;
2125  pvector<EggGroup *> decal_children;
2126 
2127  EggGroupNode::iterator ci;
2128  for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
2129  EggNode *child = (*ci);
2130  if (child->is_of_type(EggGroup::get_class_type())) {
2131  EggGroup *child_group = DCAST(EggGroup, child);
2132  if (child_group->has_object_type("decalbase")) {
2133  if (decal_base != (EggNode *)NULL) {
2134  softegg_cat.error()
2135  << "Two children of " << egg_parent->get_name()
2136  << " both have decalbase set: " << decal_base->get_name()
2137  << " and " << child_group->get_name() << "\n";
2138  okflag = false;
2139  }
2140  child_group->remove_object_type("decalbase");
2141  decal_base = child_group;
2142 
2143  } else if (child_group->has_object_type("decal")) {
2144  child_group->remove_object_type("decal");
2145  decal_children.push_back(child_group);
2146  }
2147  }
2148  }
2149 
2150  if (decal_base == (EggGroup *)NULL) {
2151  if (!decal_children.empty()) {
2152  softegg_cat.warning()
2153  << decal_children.front()->get_name()
2154  << " has decal, but no sibling node has decalbase.\n";
2155  }
2156 
2157  } else {
2158  if (decal_children.empty()) {
2159  softegg_cat.warning()
2160  << decal_base->get_name()
2161  << " has decalbase, but no sibling nodes have decal.\n";
2162 
2163  } else {
2164  // All the decal children get moved to be a child of decal base.
2165  // This usually will not affect the vertex positions, but it
2166  // could if the decal base has a transform and the decal child
2167  // is an instance node. So don't do that.
2169  for (di = decal_children.begin(); di != decal_children.end(); ++di) {
2170  EggGroup *child_group = (*di);
2171  decal_base->add_child(child_group);
2172  }
2173 
2174  // Also set the decal state on the base.
2175  decal_base->set_decal_flag(true);
2176  }
2177  }
2178 
2179  // Now recurse on each of the child nodes.
2180  for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
2181  EggNode *child = (*ci);
2182  if (child->is_of_type(EggGroupNode::get_class_type())) {
2183  EggGroupNode *child_group = DCAST(EggGroupNode, child);
2184  if (!reparent_decals(child_group)) {
2185  okflag = false;
2186  }
2187  }
2188  }
2189 
2190  return okflag;
2191 }
2192 
2193 ////////////////////////////////////////////////////////////////////
2194 // Function: SoftShader::string_transform_type
2195 // Access: Public, Static
2196 // Description: Returns the TransformType value corresponding to the
2197 // indicated string, or TT_invalid.
2198 ////////////////////////////////////////////////////////////////////
2199 SoftToEggConverter::TransformType SoftToEggConverter::
2200 string_transform_type(const string &arg) {
2201  if (cmp_nocase(arg, "all") == 0) {
2202  return TT_all;
2203  } else if (cmp_nocase(arg, "model") == 0) {
2204  return TT_model;
2205  } else if (cmp_nocase(arg, "dcs") == 0) {
2206  return TT_dcs;
2207  } else if (cmp_nocase(arg, "none") == 0) {
2208  return TT_none;
2209  } else {
2210  return TT_invalid;
2211  }
2212 }
2213 
2214 /////////////////////////////////////////////////////////////////////////
2215 // Function: init_soft2egg
2216 // Access:
2217 // Description: Invokes the softToEggConverter class
2218 /////////////////////////////////////////////////////////////////////////
2219 extern "C" int init_soft2egg (int argc, char **argv)
2220 {
2221  stec._commandName = argv[0];
2222  stec.rsrc_path = "c:\\Softimage\\SOFT3D_3.9.2\\3D\\rsrc";
2223  if (stec.DoGetopts(argc, argv)) {
2224 
2225  // create a Filename object and convert the file
2226  Filename softFile(argv[1]);
2227  stec.convert_file(softFile);
2228  }
2229 
2230  return 0;
2231 }
2232 //
2233 //
2234 //
A base class for any of a number of kinds of geometry primitives: polygons, point lights...
Definition: eggPrimitive.h:51
bool has_model() const
Returns true if a Soft dag path has been associated with this node, false otherwise.
int get_num_nodes() const
Returns the total number of nodes in the hierarchy, not counting the root node.
This is an iterator adaptor that converts any iterator that returns a pair (e.g.
bool open_api()
Attempts to open the Soft API if it was not already open, and returns true if successful, or false if there is an error.
void set_highest_index(int highest_index)
Artificially changes the &quot;highest index number&quot;, so that a newly created vertex will begin at this nu...
EggVertex * get_vertex(int index) const
Returns the vertex in the pool with the indicated index number, or NULL if no vertices have that inde...
EggTexture * create_unique_texture(const EggTexture &copy, int eq)
Creates a new texture if there is not already one equivalent (according to eq, see EggTexture::is_equ...
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:4716
void get_joint_transform(SAA_Scene *scene, EggGroup *egg_group, EggXfmSAnim *anim, bool global)
Extracts the transform on the indicated Soft node, as appropriate for a joint in an animated characte...
WrapMode determine_wrap_v() const
Determines the appropriate wrap in the V direction.
Definition: eggTexture.I:166
virtual bool convert_file(const Filename &filename)
Handles the reading of the input file and converting it to egg.
int get_num_u_knots() const
Returns the number of knots in the U direction.
void close_api()
Closes the Soft API, if it was previously opened.
EggVertex * add_vertex(EggVertex *vertex, int index=-1)
Adds the indicated vertex to the pool.
void set_pos(double pos)
Sets the vertex position.
Definition: eggVertex.I:54
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:51
This is a four-component point in space.
Definition: lpoint4.h:443
A comment that appears in an egg file within a &lt;Comment&gt; entry.
Definition: eggComment.h:27
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:33
void set_u_knot(int k, double value)
Resets the value of the indicated knot as indicated.
char * GetModelNoteInfo(SAA_Scene *, SAA_Elem *)
Given an element, return a string containing the contents of its MODEL NOTE entry.
virtual string get_name() const
Returns the English name of the file type this converter supports.
char * GetName(SAA_Scene *scene, SAA_Elem *element)
Given an element, return a copy of the element&#39;s name WITHOUT prefix.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
Definition: eggVertex.I:160
const LMatrix4d & get_vertex_frame_inv() const
Returns the inverse of the matrix returned by get_vertex_frame().
Definition: eggNode.I:167
void clear()
Removes all textures from the collection.
void set_texture(EggTexture *texture)
Replaces the current list of textures with the indicated texture.
Definition: eggPrimitive.I:139
EggData * get_egg_data()
Returns the EggData structure.
EggNode * find_child(const string &name) const
Returns the child of this node whose name is the indicated string, or NULL if there is no child of th...
virtual string get_extension() const
Returns the common extension of the file type this converter supports.
Corresponding to an &lt;S$Anim&gt; entry, this stores a single column of numbers, for instance for a morph ...
Definition: eggSAnimData.h:28
void setup(int u_order, int v_order, int num_u_knots, int num_v_knots)
Prepares a new surface definition with the indicated order and number of knots in each dimension...
This is a two-component point in space.
Definition: lpoint2.h:411
void Help()
Displays the &quot;what is this program&quot; message, along with the usage message.
string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:424
void set_external_index(int external_index)
Sets a special index number that is associated with the EggVertex (but is not written to the egg file...
Definition: eggVertex.I:365
This is the primary interface into all the egg data, and the root of the egg file structure...
Definition: eggData.h:41
bool HandleGetopts(int &idx, int argc, char **argv)
increment idx based on what kind of option parsed Supported options are as follows: r:d:s:m:t:P:b:e:f...
int get_external_index() const
Returns the number set by set_external_index().
Definition: eggVertex.I:376
bool remove_object_type(const string &object_type)
Removes the first instance of the indicated object type from the group if it is present.
Definition: eggGroup.cxx:176
char * GetFullName(SAA_Scene *scene, SAA_Elem *element)
Given an element, return a copy of the element&#39;s name complete with prefix.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
char * GetTextureName(SAA_Scene *scene, SAA_Elem *texture)
Given a texture element, return texture name with given tex_path.
SAA_Elem * get_model() const
Returns the SAA_Elem * associated with this node.
void clear_egg(EggData *egg_data, EggGroupNode *egg_root, EggGroupNode *skeleton_node)
Removes all of the references to generated egg structures from the tree, and prepares the tree for ge...
EggSAnimData * find_morph_table(char *name)
Given a tablename, it either creates a new eggSAnimData structure (if doesn&#39;t exist) or locates it...
void set_bface_flag(bool flag)
Sets the backfacing flag of the polygon.
Definition: eggPrimitive.I:289
The main glue of the egg hierarchy, this corresponds to the &lt;Group&gt;, &lt;Instance&gt;, and &lt;Joint&gt; type nod...
Definition: eggGroup.h:36
void set_v_knot(int k, double value)
Resets the value of the indicated knot as indicated.
void ShowOpts()
Displays the valid options.
This is a 3-by-3 transform matrix.
Definition: lmatrix.h:4375
char * GetRootName(const char *)
Given a string, return a copy of the string up to the first occurence of &#39;-&#39;.
iterator end() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
EggGroup * get_egg_group(SoftNodeDesc *node_desc)
Returns the EggGroupNode corresponding to the group or joint for the indicated node.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
bool is_joint() const
Returns true if the node should be treated as a joint by the converter.
bool is_partial(char *search_prefix)
check to see if this is a selected branch we want to descend - this will prevent creating geometry fo...
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
Definition: eggVertex.h:41
WrapMode determine_wrap_u() const
Determines the appropriate wrap in the U direction.
Definition: eggTexture.I:132
int * FindClosestTriVert(EggVertexPool *vpool, SAA_DVector *vertices, int numVert)
Given an egg vertex pool, map each vertex therein to a vertex within an array of SAA model vertices o...
bool build_complete_hierarchy(SAA_Scene &scene, SAA_Database &database)
Walks through the complete Soft hierarchy and builds up the corresponding tree.
This corresponds to an &lt;Xfm$Anim_S$&gt; entry, which is a collection of up to nine &lt;S$Anim&gt; entries that...
Definition: eggXfmSAnim.h:33
void make_vertex_offsets(int numShapes)
Given a scene, a model , the vertices of its original shape and its name find the difference between ...
Describes a single instance of a node aka element in the Soft scene graph, relating it to the corresp...
Definition: softNodeDesc.h:46
void set_egg_data(EggData *egg_data)
Sets the egg data that will be filled in when convert_file() is called.
int get_num_cvs() const
Returns the total number of control vertices that should* be defined for the surface.
void optimize()
Optimizes the table by collapsing redundant sub-tables.
Definition: eggXfmSAnim.cxx:75
SoftNodeDesc * get_node(int n) const
Returns the nth node in the hierarchy, in an arbitrary ordering.
A single polygon.
Definition: eggPolygon.h:26
bool is_junk() const
Returns true if the node should be treated as a junk by the converter.
bool has_object_type(const string &object_type) const
Returns true if the indicated object type has been added to the group, or false otherwise.
Definition: eggGroup.cxx:157
size_type size() const
Returns the number of vertices in the pool.
This corresponds to a.
Definition: eggTable.h:31
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
void load_nurbs_model(SAA_Scene *scene, SAA_ModelType type)
Converts the indicated Soft polyset to a bunch of EggPolygons and parents them to the indicated egg g...
This class supervises the construction of an EggData structure from a single Softimage file...
virtual SomethingToEggConverter * make_copy()
Allocates and returns a new copy of the converter.
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:746
string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:460
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:531
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
bool almost_equal(const LMatrix3d &other, double threshold) const
Returns true if two matrices are memberwise equal within a specified tolerance.
Definition: lmatrix.cxx:1598
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:38
void Usage()
Displays the usage message.
A parametric NURBS surface.
void ref_vertex(EggVertex *vert, double membership=1.0)
Adds the vertex to the set of those referenced by the group, at the indicated membership level...
Definition: eggGroup.cxx:678
void get_transform(SAA_Scene *scene, EggGroup *egg_group, bool global)
Extracts the transform on the indicated Soft node, and applies it to the corresponding Egg node...
bool DoGetopts(int &argc, char **&argv)
Calls getopt() to parse the command-line switches.
SoftNodeDesc * find_node(string name)
Returns the English name of the file type this converter supports.
int get_num_u_cvs() const
Returns the number of control vertices that should be present in the U direction. ...
EggXfmSAnim * get_egg_anim(SoftNodeDesc *node_desc)
Returns the anim table corresponding to the joint for the indicated node.
GroupRef::size_type gref_size() const
Returns the number of elements between gref_begin() and gref_end().
Definition: eggVertex.cxx:819
int get_num_v_knots() const
Returns the number of knots in the V direction.
bool convert_soft(bool from_selection)
Fills up the egg_data structure according to the global soft model data.
void set_uv(const LTexCoordd &texCoord)
Replaces the unnamed UV coordinate pair on the vertex with the indicated value.
Definition: eggVertex.I:239
This is a base class for a family of converter classes that manage a conversion from some file type t...
EggVertex * create_unique_vertex(const EggVertex &copy)
Creates a new vertex in the pool that is a copy of the indicated one and returns it.
int get_num_v_cvs() const
Returns the number of control vertices that should be present in the V direction. ...
A collection of vertices.
Definition: eggVertexPool.h:46
EggVertex * add_vertex(EggVertex *vertex)
Adds the indicated vertex to the end of the primitive&#39;s list of vertices, and returns it...
void load_poly_model(SAA_Scene *scene, SAA_ModelType type)
Converts the indicated Soft polyset to a bunch of EggPolygons and parents them to the indicated egg g...
static TransformType string_transform_type(const string &arg)
Returns the TransformType value corresponding to the indicated string, or TT_invalid.
void make_morph_table(PN_stdfloat time)
Given a scene, a model, a name and a frame time, determine what type of shape interpolation is used a...
iterator begin() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).
Definition: filename.cxx:332