Browse Source

Merge branch 'master' into interrogate-overhaul

Conflicts:
	dtool/src/cppparser/cppBison.yxx
	dtool/src/interrogate/interfaceMakerPythonNative.cxx
rdb 10 years ago
parent
commit
e608fa69c7
100 changed files with 4585 additions and 623 deletions
  1. 0 1
      .gitignore
  2. 38 38
      contrib/src/ai/aiBehaviors.cxx
  3. 16 16
      contrib/src/ai/aiBehaviors.h
  4. 21 26
      contrib/src/ai/aiCharacter.cxx
  5. 4 4
      contrib/src/ai/aiCharacter.h
  6. 1 1
      contrib/src/ai/aiNode.cxx
  7. 2 2
      contrib/src/ai/aiNode.h
  8. 2 2
      contrib/src/ai/aiPathFinder.cxx
  9. 1 1
      contrib/src/ai/aiPathFinder.h
  10. 8 8
      contrib/src/ai/arrival.cxx
  11. 3 3
      contrib/src/ai/arrival.h
  12. 4 4
      contrib/src/ai/evade.cxx
  13. 2 2
      contrib/src/ai/evade.h
  14. 7 7
      contrib/src/ai/flee.cxx
  15. 5 5
      contrib/src/ai/flee.h
  16. 1 1
      contrib/src/ai/meshNode.cxx
  17. 2 2
      contrib/src/ai/meshNode.h
  18. 15 15
      contrib/src/ai/obstacleAvoidance.cxx
  19. 1 1
      contrib/src/ai/obstacleAvoidance.h
  20. 3 3
      contrib/src/ai/pathFind.cxx
  21. 2 2
      contrib/src/ai/pathFind.h
  22. 2 2
      contrib/src/ai/pathFollow.cxx
  23. 2 2
      contrib/src/ai/pathFollow.h
  24. 6 6
      contrib/src/ai/pursue.cxx
  25. 2 2
      contrib/src/ai/pursue.h
  26. 5 5
      contrib/src/ai/seek.cxx
  27. 5 5
      contrib/src/ai/seek.h
  28. 18 18
      contrib/src/ai/wander.cxx
  29. 3 3
      contrib/src/ai/wander.h
  30. 2 2
      direct/src/directscripts/Doxyfile.python
  31. 111 50
      direct/src/directscripts/extract_docs.py
  32. 1 1
      direct/src/filter/FilterManager.py
  33. 24 10
      direct/src/gui/DirectDialog.py
  34. 5 3
      direct/src/gui/DirectRadioButton.py
  35. 8 6
      direct/src/showbase/Transitions.py
  36. 8 2
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  37. 7 5
      makepanda/makepanda.py
  38. 1 1
      panda/src/bullet/bulletSoftBodyNode.cxx
  39. 16 19
      panda/src/cocoadisplay/cocoaGraphicsPipe.mm
  40. 7 13
      panda/src/display/config_display.cxx
  41. 0 3
      panda/src/display/config_display.h
  42. 26 21
      panda/src/display/frameBufferProperties.I
  43. 29 33
      panda/src/display/frameBufferProperties.cxx
  44. 1 1
      panda/src/display/frameBufferProperties.h
  45. 3 3
      panda/src/distort/nonlinearImager.cxx
  46. 8 3
      panda/src/doc/eggSyntax.txt
  47. 4 0
      panda/src/egg2pg/eggLoader.cxx
  48. 7 21
      panda/src/glstuff/glCgShaderContext_src.cxx
  49. 0 1
      panda/src/glstuff/glCgShaderContext_src.h
  50. 82 73
      panda/src/glstuff/glGraphicsBuffer_src.cxx
  51. 8 0
      panda/src/glstuff/glShaderContext_src.cxx
  52. 15 22
      panda/src/glxdisplay/glxGraphicsPipe.cxx
  53. 77 72
      panda/src/gobj/texture.cxx
  54. 7 0
      panda/src/gobj/texture.h
  55. 8 8
      panda/src/grutil/pfmVizzer.cxx
  56. 2 0
      panda/src/ode/odeRayGeom.I
  57. 2 0
      panda/src/ode/odeTriMeshData.h
  58. 17 22
      panda/src/osxdisplay/osxGraphicsPipe.cxx
  59. 1 1
      panda/src/pgraph/cullBinManager.h
  60. 7 5
      panda/src/pgraph/pandaNode.I
  61. 175 0
      panda/src/pnmimage/convert_srgb.I
  62. 165 0
      panda/src/pnmimage/convert_srgb.cxx
  63. 59 0
      panda/src/pnmimage/convert_srgb.h
  64. 151 0
      panda/src/pnmimage/convert_srgb_sse2.cxx
  65. 1 0
      panda/src/pnmimage/p3pnmimage_composite1.cxx
  66. 1 1
      panda/src/rocket/rocketRenderInterface.h
  67. 1 1
      panda/src/tinydisplay/zbuffer.h
  68. 16 30
      panda/src/wgldisplay/wglGraphicsPipe.cxx
  69. 1 1
      pandatool/src/daeegg/daeCharacter.cxx
  70. 1 1
      pandatool/src/daeegg/daeCharacter.h
  71. 1 1
      pandatool/src/daeegg/daeToEggConverter.cxx
  72. 7 0
      pandatool/src/daeegg/pre_fcollada_include.h
  73. 399 0
      samples/asteroids/main.py
  74. 39 0
      samples/asteroids/models/plane.egg
  75. BIN
      samples/asteroids/textures/asteroid1.png
  76. BIN
      samples/asteroids/textures/asteroid2.png
  77. BIN
      samples/asteroids/textures/asteroid3.png
  78. BIN
      samples/asteroids/textures/bullet.png
  79. BIN
      samples/asteroids/textures/ship.png
  80. BIN
      samples/asteroids/textures/stars.jpg
  81. 338 0
      samples/ball-in-maze/main.py
  82. BIN
      samples/ball-in-maze/models/ball.egg.pz
  83. BIN
      samples/ball-in-maze/models/iron05.jpg
  84. BIN
      samples/ball-in-maze/models/limba.jpg
  85. BIN
      samples/ball-in-maze/models/maze.egg.pz
  86. 199 0
      samples/boxing-robots/main.py
  87. BIN
      samples/boxing-robots/models/ring.egg.pz
  88. BIN
      samples/boxing-robots/models/robot.egg.pz
  89. BIN
      samples/boxing-robots/models/robot_head_down.egg.pz
  90. BIN
      samples/boxing-robots/models/robot_head_up.egg.pz
  91. BIN
      samples/boxing-robots/models/robot_left_punch.egg.pz
  92. BIN
      samples/boxing-robots/models/robot_right_punch.egg.pz
  93. 188 0
      samples/bump-mapping/main.py
  94. 1671 0
      samples/bump-mapping/models/abstractroom.egg
  95. BIN
      samples/bump-mapping/models/abstractroom.mb
  96. BIN
      samples/bump-mapping/models/brick-c.jpg
  97. BIN
      samples/bump-mapping/models/brick-n.jpg
  98. BIN
      samples/bump-mapping/models/fieldstone-c.jpg
  99. BIN
      samples/bump-mapping/models/fieldstone-n.jpg
  100. 497 0
      samples/bump-mapping/models/icosphere.egg

+ 0 - 1
.gitignore

@@ -1,4 +1,3 @@
 /built_x64
 /built
-/samples
 /thirdparty

+ 38 - 38
contrib/src/ai/aiBehaviors.cxx

@@ -18,7 +18,7 @@
 static const float _PI = 3.14;
 
 AIBehaviors::AIBehaviors() {
-  _steering_force = LVecBase3f(0.0, 0.0, 0.0);
+  _steering_force = LVecBase3(0.0, 0.0, 0.0);
   _behaviors_flags = _behaviors_flags & _none;
   _previous_conflict = false;
   _conflict = false;
@@ -67,7 +67,7 @@ bool AIBehaviors::is_conflict() {
       }
 
       if(is_on(_flee)) {
-        LVecBase3f dirn = _flee_force;
+        LVecBase3 dirn = _flee_force;
         dirn.normalize();
         _flee_force = _steering_force.length() * dirn * _flee_obj->_flee_weight;
       }
@@ -77,7 +77,7 @@ bool AIBehaviors::is_conflict() {
       }
 
       if(is_on(_evade)) {
-        LVecBase3f dirn = _evade_force;
+        LVecBase3 dirn = _evade_force;
         dirn.normalize();
         _evade_force = _steering_force.length() * dirn * _evade_obj->_evade_weight;
       }
@@ -111,9 +111,9 @@ bool AIBehaviors::is_conflict() {
 
 /////////////////////////////////////////////////////////////////////////////////////////////////////
 
-void AIBehaviors::accumulate_force(string force_type, LVecBase3f force) {
+void AIBehaviors::accumulate_force(string force_type, LVecBase3 force) {
 
-  LVecBase3f old_force;
+  LVecBase3 old_force;
 
   if(force_type == "seek") {
     old_force = _seek_force;
@@ -169,8 +169,8 @@ void AIBehaviors::accumulate_force(string force_type, LVecBase3f force) {
 
 //////////////////////////////////////////////////////////////////////////////////////////////
 
-LVecBase3f AIBehaviors::calculate_prioritized() {
-  LVecBase3f force;
+LVecBase3 AIBehaviors::calculate_prioritized() {
+  LVecBase3 force;
 
   if(is_on(_seek)) {
     if(_conflict) {
@@ -299,13 +299,13 @@ LVecBase3f AIBehaviors::calculate_prioritized() {
 
   if(is_on(_arrival)) {
     if(_seek_obj != NULL) {
-      LVecBase3f dirn = _steering_force;
+      LVecBase3 dirn = _steering_force;
       dirn.normalize();
       _steering_force = ((_steering_force.length() - _arrival_force.length()) * dirn);
     }
 
     if(_pursue_obj != NULL) {
-      LVecBase3f dirn = _steering_force;
+      LVecBase3 dirn = _steering_force;
       dirn.normalize();
       _steering_force = ((_steering_force.length() - _arrival_force.length()) * _arrival_obj->_arrival_direction);
     }
@@ -639,7 +639,7 @@ void AIBehaviors::resume_ai(string ai_type) {
 // Function : seek
 // Description : This function activates seek and makes an object of the Seek class.
 //                This is the function we want the user to call for seek to be done.
-//                This function is overloaded to accept a NodePath or an LVecBase3f.
+//                This function is overloaded to accept a NodePath or an LVecBase3.
 
 /////////////////////////////////////////////////////////////////////////////////
 
@@ -648,7 +648,7 @@ void AIBehaviors::seek(NodePath target_object, float seek_wt) {
   turn_on("seek");
 }
 
-void AIBehaviors::seek(LVecBase3f pos, float seek_wt) {
+void AIBehaviors::seek(LVecBase3 pos, float seek_wt) {
   _seek_obj = new Seek(_ai_char, pos, seek_wt);
   turn_on("seek");
 }
@@ -657,7 +657,7 @@ void AIBehaviors::seek(LVecBase3f pos, float seek_wt) {
 //
 // Function : flee
 // Description : This function activates flee_activate and creates an object of the Flee class.
-//                This function is overloaded to accept a NodePath or an LVecBase3f.
+//                This function is overloaded to accept a NodePath or an LVecBase3.
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -668,7 +668,7 @@ void AIBehaviors::flee(NodePath target_object, double panic_distance, double rel
   turn_on("flee_activate");
 }
 
-void AIBehaviors::flee(LVecBase3f pos, double panic_distance, double relax_distance, float flee_wt) {
+void AIBehaviors::flee(LVecBase3 pos, double panic_distance, double relax_distance, float flee_wt) {
   _flee_obj = new Flee(_ai_char, pos, panic_distance, relax_distance, flee_wt);
   _flee_list.insert(_flee_list.end(), *_flee_obj);
 
@@ -769,38 +769,38 @@ void AIBehaviors::flock_activate() {
 
 /////////////////////////////////////////////////////////////////////////////////
 
-LVecBase3f AIBehaviors::do_flock() {
+LVecBase3 AIBehaviors::do_flock() {
 
   //! Initialize variables required to compute the flocking force on the ai char.
   unsigned int neighbor_count = 0;
-  LVecBase3f separation_force = LVecBase3f(0.0, 0.0, 0.0);
-  LVecBase3f alignment_force = LVecBase3f(0.0, 0.0, 0.0);
-  LVecBase3f cohesion_force = LVecBase3f(0.0, 0.0, 0.0);
-  LVecBase3f avg_neighbor_heading = LVecBase3f(0.0, 0.0, 0.0);
-  LVecBase3f total_neighbor_heading = LVecBase3f(0.0, 0.0, 0.0);
-  LVecBase3f avg_center_of_mass = LVecBase3f(0.0, 0.0, 0.0);
-  LVecBase3f total_center_of_mass = LVecBase3f(0.0, 0.0, 0.0);
+  LVecBase3 separation_force = LVecBase3(0.0, 0.0, 0.0);
+  LVecBase3 alignment_force = LVecBase3(0.0, 0.0, 0.0);
+  LVecBase3 cohesion_force = LVecBase3(0.0, 0.0, 0.0);
+  LVecBase3 avg_neighbor_heading = LVecBase3(0.0, 0.0, 0.0);
+  LVecBase3 total_neighbor_heading = LVecBase3(0.0, 0.0, 0.0);
+  LVecBase3 avg_center_of_mass = LVecBase3(0.0, 0.0, 0.0);
+  LVecBase3 total_center_of_mass = LVecBase3(0.0, 0.0, 0.0);
 
   //! Loop through all the other AI units in the flock to check if they are neigbours.
   for(unsigned int i = 0; i < _flock_group->_ai_char_list.size(); i++) {
     if(_flock_group->_ai_char_list[i]->_name != _ai_char->_name) {
 
       //! Using visibilty cone to detect neighbors.
-      LVecBase3f dist_vect = _flock_group->_ai_char_list[i]->_ai_char_np.get_pos() - _ai_char->_ai_char_np.get_pos();
-      LVecBase3f ai_char_heading = _ai_char->get_velocity();
+      LVecBase3 dist_vect = _flock_group->_ai_char_list[i]->_ai_char_np.get_pos() - _ai_char->_ai_char_np.get_pos();
+      LVecBase3 ai_char_heading = _ai_char->get_velocity();
       ai_char_heading.normalize();
 
       //! Check if the current unit is a neighbor.
       if(dist_vect.dot(ai_char_heading) > ((dist_vect.length()) * (ai_char_heading.length()) * cos(_flock_group->_flock_vcone_angle * (_PI / 180)))
         && (dist_vect.length() < _flock_group->_flock_vcone_radius)) {
           //! Separation force calculation.
-          LVecBase3f ai_char_to_units = _ai_char->_ai_char_np.get_pos() - _flock_group->_ai_char_list[i]->_ai_char_np.get_pos();
+          LVecBase3 ai_char_to_units = _ai_char->_ai_char_np.get_pos() - _flock_group->_ai_char_list[i]->_ai_char_np.get_pos();
           float to_units_dist = ai_char_to_units.length();
           ai_char_to_units.normalize();
           separation_force += (ai_char_to_units / to_units_dist);
 
           //! Calculating the total heading and center of mass of all the neighbors.
-          LVecBase3f neighbor_heading = _flock_group->_ai_char_list[i]->get_velocity();
+          LVecBase3 neighbor_heading = _flock_group->_ai_char_list[i]->get_velocity();
           neighbor_heading.normalize();
           total_neighbor_heading += neighbor_heading;
           total_center_of_mass += _flock_group->_ai_char_list[i]->_ai_char_np.get_pos();
@@ -814,7 +814,7 @@ LVecBase3f AIBehaviors::do_flock() {
   if(neighbor_count > 0) {
     //! Alignment force calculation
     avg_neighbor_heading = total_neighbor_heading / neighbor_count;
-    LVector3f ai_char_heading = _ai_char->get_velocity();
+    LVector3 ai_char_heading = _ai_char->get_velocity();
     ai_char_heading.normalize();
     avg_neighbor_heading -= ai_char_heading;
     avg_neighbor_heading.normalize();
@@ -822,7 +822,7 @@ LVecBase3f AIBehaviors::do_flock() {
 
     //! Cohesion force calculation
     avg_center_of_mass = total_center_of_mass / neighbor_count;
-    LVecBase3f cohesion_dir = avg_center_of_mass - _ai_char->_ai_char_np.get_pos();
+    LVecBase3 cohesion_dir = avg_center_of_mass - _ai_char->_ai_char_np.get_pos();
     cohesion_dir.normalize();
     cohesion_force = cohesion_dir * _ai_char->_movt_force;
   }
@@ -830,7 +830,7 @@ LVecBase3f AIBehaviors::do_flock() {
     _flock_done = true;
     turn_off("flock");
     turn_on("flock_activate");
-    return(LVecBase3f(0.0, 0.0, 0.0));
+    return(LVecBase3(0.0, 0.0, 0.0));
   }
 
   //! Calculate the resultant force on the ai character by taking into account the separation, alignment and cohesion
@@ -885,7 +885,7 @@ void AIBehaviors::path_follow(float follow_wt) {
 
 /////////////////////////////////////////////////////////////////////////////////
 
-void AIBehaviors::add_to_path(LVecBase3f pos) {
+void AIBehaviors::add_to_path(LVecBase3 pos) {
   _path_follow_obj->add_to_path(pos);
 }
 
@@ -923,7 +923,7 @@ void AIBehaviors::init_path_find(const char* navmesh_filename) {
 
 ///////////////////////////////////////////////////////////////////////////////////////
 
-void AIBehaviors::path_find_to(LVecBase3f pos, string type) {
+void AIBehaviors::path_find_to(LVecBase3 pos, string type) {
   _path_find_obj->path_find(pos, type);
 }
 
@@ -1331,56 +1331,56 @@ switch(char_to_int(ai_type)) {
               if (is_on(_seek)) {
                 _behaviors_flags ^= _seek;
               }
-              _seek_force = LVecBase3f(0.0f, 0.0f, 0.0f);
+              _seek_force = LVecBase3(0.0f, 0.0f, 0.0f);
               break;
             }
     case 2: {
               if (is_on(_flee)) {
                 _behaviors_flags ^= _flee;
               }
-              _flee_force = LVecBase3f(0.0f, 0.0f, 0.0f);
+              _flee_force = LVecBase3(0.0f, 0.0f, 0.0f);
               break;
             }
     case 3: {
               if(is_on(_pursue)) {
                 _behaviors_flags ^= _pursue;
               }
-              _pursue_force = LVecBase3f(0.0f, 0.0f, 0.0f);
+              _pursue_force = LVecBase3(0.0f, 0.0f, 0.0f);
               break;
             }
     case 4: {
               if(is_on(_evade)) {
                 _behaviors_flags ^= _evade;
               }
-              _evade_force = LVecBase3f(0.0f, 0.0f, 0.0f);
+              _evade_force = LVecBase3(0.0f, 0.0f, 0.0f);
               break;
             }
     case 5: {
               if (is_on(_arrival)) {
                   _behaviors_flags ^= _arrival;
                 }
-                _arrival_force = LVecBase3f(0.0f, 0.0f, 0.0f);
+                _arrival_force = LVecBase3(0.0f, 0.0f, 0.0f);
               break;
             }
     case 6: {
               if(is_on(_flock)) {
                 _behaviors_flags ^= _flock;
               }
-              _flock_force = LVecBase3f(0.0f, 0.0f, 0.0f);
+              _flock_force = LVecBase3(0.0f, 0.0f, 0.0f);
               break;
             }
     case 7: {
               if(is_on(_wander)) {
                 _behaviors_flags ^= _wander;
               }
-              _wander_force = LVecBase3f(0.0f, 0.0f, 0.0f);
+              _wander_force = LVecBase3(0.0f, 0.0f, 0.0f);
               break;
             }
     case 8: {
               if(is_on(_obstacle_avoidance)) {
                 _behaviors_flags ^= _obstacle_avoidance;
               }
-              _obstacle_avoidance_force = LVecBase3f(0.0f, 0.0f, 0.0f);
+              _obstacle_avoidance_force = LVecBase3(0.0f, 0.0f, 0.0f);
               break;
             }
     case 9:{

+ 16 - 16
contrib/src/ai/aiBehaviors.h

@@ -73,41 +73,41 @@ public:
   Flock *_flock_group;
 
   int _behaviors_flags;
-  LVecBase3f _steering_force;
+  LVecBase3 _steering_force;
 
   Seek *_seek_obj;
-  LVecBase3f _seek_force;
+  LVecBase3 _seek_force;
 
   Flee *_flee_obj;
-  LVecBase3f _flee_force;
+  LVecBase3 _flee_force;
 
   //! This list is used if the ai character needs to flee from multiple onjects.
   ListFlee _flee_list;
   ListFlee::iterator _flee_itr;
 
   Pursue *_pursue_obj;
-  LVecBase3f _pursue_force;
+  LVecBase3 _pursue_force;
 
   Evade *_evade_obj;
-  LVecBase3f _evade_force;
+  LVecBase3 _evade_force;
 
   //! This list is used if the ai character needs to evade from multiple onjects.
   ListEvade _evade_list;
   ListEvade::iterator _evade_itr;
 
   Arrival *_arrival_obj;
-  LVecBase3f _arrival_force;
+  LVecBase3 _arrival_force;
 
   //! Since Flock is a collective behavior the variables are declared within the AIBehaviors class.
   float _flock_weight;
-  LVecBase3f _flock_force;
+  LVecBase3 _flock_force;
   bool _flock_done;
 
   Wander * _wander_obj;
-  LVecBase3f _wander_force;
+  LVecBase3 _wander_force;
 
   ObstacleAvoidance *_obstacle_avoidance_obj;
-  LVecBase3f _obstacle_avoidance_force;
+  LVecBase3 _obstacle_avoidance_force;
 
   PathFollow *_path_follow_obj;
 
@@ -127,20 +127,20 @@ public:
 
   bool is_conflict();
 
-  void accumulate_force(string force_type, LVecBase3f force);
-  LVecBase3f calculate_prioritized();
+  void accumulate_force(string force_type, LVecBase3 force);
+  LVecBase3 calculate_prioritized();
 
   void flock_activate();
-  LVecBase3f do_flock();
+  LVecBase3 do_flock();
 
   int char_to_int(string ai_type);
 
 PUBLISHED:
   void seek(NodePath target_object, float seek_wt = 1.0);
-  void seek(LVecBase3f pos, float seek_wt = 1.0);
+  void seek(LVecBase3 pos, float seek_wt = 1.0);
 
   void flee(NodePath target_object, double panic_distance = 10.0, double relax_distance = 10.0, float flee_wt = 1.0);
-  void flee(LVecBase3f pos, double panic_distance = 10.0, double relax_distance = 10.0, float flee_wt = 1.0);
+  void flee(LVecBase3 pos, double panic_distance = 10.0, double relax_distance = 10.0, float flee_wt = 1.0);
 
   void pursue(NodePath target_object, float pursue_wt = 1.0);
 
@@ -155,12 +155,12 @@ PUBLISHED:
   void obstacle_avoidance(float feeler_length = 1.0);
 
   void path_follow(float follow_wt);
-  void add_to_path(LVecBase3f pos);
+  void add_to_path(LVecBase3 pos);
   void start_follow(string type = "normal");
 
   // should have different function names.
   void init_path_find(const char* navmesh_filename);
-  void path_find_to(LVecBase3f pos, string type = "normal");
+  void path_find_to(LVecBase3 pos, string type = "normal");
   void path_find_to(NodePath target, string type = "normal");
   void add_static_obstacle(NodePath obstacle);
   void add_dynamic_obstacle(NodePath obstacle);

+ 21 - 26
contrib/src/ai/aiCharacter.cxx

@@ -23,8 +23,8 @@ AICharacter::AICharacter(string model_name, NodePath model_np, double mass, doub
   _max_force = max_force;
   _movt_force = movt_force;
 
-  _velocity = LVecBase3f(0.0, 0.0, 0.0);
-  _steering_force = LVecBase3f(0.0, 0.0, 0.0);
+  _velocity = LVecBase3(0.0, 0.0, 0.0);
+  _steering_force = LVecBase3(0.0, 0.0, 0.0);
 
   _steering = new AIBehaviors();
   _steering->_ai_char = this;
@@ -43,48 +43,43 @@ AICharacter::~AICharacter() {
 //                This also makes the character  look at the direction of the force.
 
 /////////////////////////////////////////////////////////////////////////////////////////
-
-void AICharacter::update() {
-
-  if(!_steering->is_off(_steering->_none)) {
-
-    LVecBase3f old_pos = _ai_char_np.get_pos();
-
-    LVecBase3f steering_force = _steering->calculate_prioritized();
-
-    LVecBase3f acceleration = steering_force / _mass;
+void AICharacter::
+update() {
+  if (!_steering->is_off(_steering->_none)) {
+    LVecBase3 old_pos = _ai_char_np.get_pos();
+    LVecBase3 steering_force = _steering->calculate_prioritized();
+    LVecBase3 acceleration = steering_force / _mass;
 
     _velocity = acceleration;
 
-    LVecBase3f direction = _steering->_steering_force;
+    LVecBase3 direction = _steering->_steering_force;
     direction.normalize();
 
     _ai_char_np.set_pos(old_pos + _velocity) ;
 
-    if(steering_force.length() > 0) {
+    if (steering_force.length() > 0) {
       _ai_char_np.look_at(old_pos + (direction * 5));
       _ai_char_np.set_h(_ai_char_np.get_h() + 180);
       _ai_char_np.set_p(-_ai_char_np.get_p());
       _ai_char_np.set_r(-_ai_char_np.get_r());
     }
-  }
-  else {
-    _steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0);
-    _steering->_seek_force = LVecBase3f(0.0, 0.0, 0.0);
-    _steering->_flee_force = LVecBase3f(0.0, 0.0, 0.0);
-    _steering->_pursue_force = LVecBase3f(0.0, 0.0, 0.0);
-    _steering->_evade_force = LVecBase3f(0.0, 0.0, 0.0);
-    _steering->_arrival_force = LVecBase3f(0.0, 0.0, 0.0);
-    _steering->_flock_force = LVecBase3f(0.0, 0.0, 0.0);
-    _steering->_wander_force = LVecBase3f(0.0, 0.0, 0.0);
+  } else {
+    _steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
+    _steering->_seek_force = LVecBase3(0.0, 0.0, 0.0);
+    _steering->_flee_force = LVecBase3(0.0, 0.0, 0.0);
+    _steering->_pursue_force = LVecBase3(0.0, 0.0, 0.0);
+    _steering->_evade_force = LVecBase3(0.0, 0.0, 0.0);
+    _steering->_arrival_force = LVecBase3(0.0, 0.0, 0.0);
+    _steering->_flock_force = LVecBase3(0.0, 0.0, 0.0);
+    _steering->_wander_force = LVecBase3(0.0, 0.0, 0.0);
   }
 }
 
-LVecBase3f AICharacter::get_velocity() {
+LVecBase3 AICharacter::get_velocity() {
   return _velocity;
 }
 
-void AICharacter::set_velocity(LVecBase3f velocity) {
+void AICharacter::set_velocity(LVecBase3 velocity) {
   _velocity = velocity;
 }
 

+ 4 - 4
contrib/src/ai/aiCharacter.h

@@ -39,8 +39,8 @@ class EXPCL_PANDAAI AICharacter {
  public:
   double _mass;
   double _max_force;
-  LVecBase3f _velocity;
-  LVecBase3f _steering_force;
+  LVecBase3 _velocity;
+  LVecBase3 _steering_force;
   string _name;
   double _movt_force;
   unsigned int _ai_char_flock_id;
@@ -51,7 +51,7 @@ class EXPCL_PANDAAI AICharacter {
   bool _pf_guide;
 
   void update();
-  void set_velocity(LVecBase3f vel);
+  void set_velocity(LVecBase3 vel);
   void set_char_render(NodePath render);
   NodePath get_char_render();
 
@@ -59,7 +59,7 @@ PUBLISHED:
     double get_mass();
     void set_mass(double m);
 
-    LVecBase3f get_velocity();
+    LVecBase3 get_velocity();
 
     double get_max_force();
     void set_max_force(double max_force);

+ 1 - 1
contrib/src/ai/aiNode.cxx

@@ -14,7 +14,7 @@
 
 #include "aiNode.h"
 
-AINode::AINode(int grid_x, int grid_y, LVecBase3f pos, float w, float l, float h) {
+AINode::AINode(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h) {
   for (int i = 0; i < 8; ++i) {
     _neighbours[i] = NULL;
   }

+ 2 - 2
contrib/src/ai/aiNode.h

@@ -62,7 +62,7 @@ public:
   int _grid_x, _grid_y;
 
   // Position of the node in 3D space.
-  LVecBase3f _position;
+  LVecBase3 _position;
 
   // Dimensions of each face / cell on the mesh.
   // Height is given in case of expansion to a 3d mesh. Currently
@@ -77,7 +77,7 @@ public:
   AINode *_next;
 
 PUBLISHED:
-  AINode(int grid_x, int grid_y, LVecBase3f pos, float w, float l, float h);
+  AINode(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h);
   ~AINode();
 
   bool contains(float x, float y);

+ 2 - 2
contrib/src/ai/aiPathFinder.cxx

@@ -36,7 +36,7 @@ void PathFinder::find_path(Node *src_node, Node *dest_node) {
 
   // Add a dummy node as the first element of the open list with score = -1.
   // Inorder to implement a binary heap the index of the elements should never be 0.
-  Node *_dummy_node = new Node(-1, -1, LVecBase3f(0.0, 0.0, 0.0), 0, 0, 0);
+  Node *_dummy_node = new Node(-1, -1, LVecBase3(0.0, 0.0, 0.0), 0, 0, 0);
   _dummy_node->_status = _dummy_node->open;
   _dummy_node->_score = -1;
   _open_list.push_back(_dummy_node);
@@ -379,7 +379,7 @@ void PathFinder::remove_from_clist(int r, int c) {
 
 /////////////////////////////////////////////////////////////////////////////////////////
 
-Node* find_in_mesh(NavMesh nav_mesh, LVecBase3f pos, int grid_size) {
+Node* find_in_mesh(NavMesh nav_mesh, LVecBase3 pos, int grid_size) {
   int size = grid_size;
   float x = pos[0];
   float y = pos[1];

+ 1 - 1
contrib/src/ai/aiPathFinder.h

@@ -23,7 +23,7 @@
 typedef vector<Node *> NodeArray;
 typedef vector<NodeArray> NavMesh;
 
-Node* find_in_mesh(NavMesh nav_mesh, LVecBase3f pos, int grid_size);
+Node* find_in_mesh(NavMesh nav_mesh, LVecBase3 pos, int grid_size);
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 //

+ 8 - 8
contrib/src/ai/arrival.cxx

@@ -36,8 +36,8 @@ Arrival::~Arrival() {
 
 /////////////////////////////////////////////////////////////////////////////////
 
-LVecBase3f Arrival::do_arrival() {
-  LVecBase3f direction_to_target;
+LVecBase3 Arrival::do_arrival() {
+  LVecBase3 direction_to_target;
   double distance;
 
   if(_arrival_type) {
@@ -52,22 +52,22 @@ LVecBase3f Arrival::do_arrival() {
   _arrival_direction.normalize();
 
   if(int(distance) == 0) {
-    _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0);
-    _ai_char->_steering->_arrival_force = LVecBase3f(0.0, 0.0, 0.0);
+    _ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
+    _ai_char->_steering->_arrival_force = LVecBase3(0.0, 0.0, 0.0);
 
     if(_ai_char->_steering->_seek_obj != NULL) {
       _ai_char->_steering->turn_off("arrival");
       _ai_char->_steering->turn_on("arrival_activate");
     }
     _arrival_done = true;
-    return(LVecBase3f(0.0, 0.0, 0.0));
+    return(LVecBase3(0.0, 0.0, 0.0));
   }
   else {
     _arrival_done = false;
   }
 
   double u = _ai_char->get_velocity().length();
-  LVecBase3f desired_force = ((u * u) / (2 * distance)) * _ai_char->get_mass();
+  LVecBase3 desired_force = ((u * u) / (2 * distance)) * _ai_char->get_mass();
 
   if(_ai_char->_steering->_seek_obj != NULL) {
     return(desired_force);
@@ -85,7 +85,7 @@ LVecBase3f Arrival::do_arrival() {
   }
 
   cout<<"Arrival works only with seek and pursue"<<endl;
-  return(LVecBase3f(0.0, 0.0, 0.0));
+  return(LVecBase3(0.0, 0.0, 0.0));
 }
 
 /////////////////////////////////////////////////////////////////////////////////
@@ -98,7 +98,7 @@ LVecBase3f Arrival::do_arrival() {
 /////////////////////////////////////////////////////////////////////////////////
 
 void Arrival::arrival_activate() {
-  LVecBase3f dirn;
+  LVecBase3 dirn;
   if(_arrival_type) {
     dirn = (_ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _ai_char->get_ai_behaviors()->_pursue_obj->_pursue_target.get_pos(_ai_char->_window_render));
   }

+ 3 - 3
contrib/src/ai/arrival.h

@@ -27,9 +27,9 @@ public:
   AICharacter *_ai_char;
 
   NodePath _arrival_target;
-  LVecBase3f _arrival_target_pos;
+  LVecBase3 _arrival_target_pos;
   double _arrival_distance;
-  LVecBase3f _arrival_direction;
+  LVecBase3 _arrival_direction;
   bool _arrival_done;
 
   // This flag specifies if the arrival behavior is being used with seek or pursue behavior.
@@ -39,7 +39,7 @@ public:
 
   Arrival(AICharacter *ai_ch, double distance = 10.0);
   ~Arrival();
-  LVecBase3f do_arrival();
+  LVecBase3 do_arrival();
   void arrival_activate();
 };
 

+ 4 - 4
contrib/src/ai/evade.cxx

@@ -42,23 +42,23 @@ Evade::~Evade() {
 
 /////////////////////////////////////////////////////////////////////////////////
 
-LVecBase3f Evade::do_evade() {
+LVecBase3 Evade::do_evade() {
   assert(_evade_target && "evade target not assigned");
 
   _evade_direction = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _evade_target.get_pos(_ai_char->_window_render);
   double distance = _evade_direction.length();
 
   _evade_direction.normalize();
-  LVecBase3f desired_force = _evade_direction * _ai_char->_movt_force;
+  LVecBase3 desired_force = _evade_direction * _ai_char->_movt_force;
 
   if(distance > (_evade_distance + _evade_relax_distance)) {
     if((_ai_char->_steering->_behaviors_flags | _ai_char->_steering->_evade) == _ai_char->_steering->_evade) {
-      _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0);
+      _ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
     }
     _ai_char->_steering->turn_off("evade");
     _ai_char->_steering->turn_on("evade_activate");
     _evade_done = true;
-    return(LVecBase3f(0.0, 0.0, 0.0));
+    return(LVecBase3(0.0, 0.0, 0.0));
   }
   else {
       _evade_done = false;

+ 2 - 2
contrib/src/ai/evade.h

@@ -28,7 +28,7 @@ public:
 
   NodePath _evade_target;
   float _evade_weight;
-  LVecBase3f _evade_direction;
+  LVecBase3 _evade_direction;
   double _evade_distance;
   double _evade_relax_distance;
   bool _evade_done;
@@ -38,7 +38,7 @@ public:
                                           double relax_distance, float evade_wt);
 
   ~Evade();
-  LVecBase3f do_evade();
+  LVecBase3 do_evade();
   void evade_activate();
 };
 

+ 7 - 7
contrib/src/ai/flee.cxx

@@ -29,7 +29,7 @@ Flee::Flee(AICharacter *ai_ch, NodePath target_object, double panic_distance,
   _flee_activate_done = false;
 }
 
-Flee::Flee(AICharacter *ai_ch, LVecBase3f pos, double panic_distance,
+Flee::Flee(AICharacter *ai_ch, LVecBase3 pos, double panic_distance,
                                 double relax_distance, float flee_wt){
 
     _ai_char = ai_ch;
@@ -57,10 +57,10 @@ Flee::~Flee() {
 
 /////////////////////////////////////////////////////////////////////////////////
 
-LVecBase3f Flee::do_flee() {
-  LVecBase3f dirn;
+LVecBase3 Flee::do_flee() {
+  LVecBase3 dirn;
   double distance;
-  LVecBase3f desired_force;
+  LVecBase3 desired_force;
 
   dirn = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _flee_present_pos;
   distance = dirn.length();
@@ -68,12 +68,12 @@ LVecBase3f Flee::do_flee() {
 
   if(distance > (_flee_distance + _flee_relax_distance)) {
     if((_ai_char->_steering->_behaviors_flags | _ai_char->_steering->_flee) == _ai_char->_steering->_flee) {
-        _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0);
+        _ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
     }
     _flee_done = true;
     _ai_char->_steering->turn_off("flee");
     _ai_char->_steering->turn_on("flee_activate");
-    return(LVecBase3f(0.0, 0.0, 0.0));
+    return(LVecBase3(0.0, 0.0, 0.0));
   }
   else {
       return(desired_force);
@@ -90,7 +90,7 @@ LVecBase3f Flee::do_flee() {
 /////////////////////////////////////////////////////////////////////////////////
 
 void Flee::flee_activate() {
-  LVecBase3f dirn;
+  LVecBase3 dirn;
   double distance;
 
   _flee_activate_done = false;

+ 5 - 5
contrib/src/ai/flee.h

@@ -26,23 +26,23 @@ class EXPCL_PANDAAI Flee {
 public:
   AICharacter *_ai_char;
 
-  LVecBase3f _flee_position;
+  LVecBase3 _flee_position;
   float _flee_weight;
-  LVecBase3f _flee_direction;
+  LVecBase3 _flee_direction;
   double _flee_distance;
   double _flee_relax_distance;
-  LVecBase3f _flee_present_pos;
+  LVecBase3 _flee_present_pos;
   bool _flee_done;
   bool _flee_activate_done;
 
   Flee(AICharacter *ai_ch, NodePath target_object, double panic_distance = 10.0,
                               double relax_distance = 10.0, float flee_wt = 1.0);
 
-  Flee(AICharacter *ai_ch, LVecBase3f pos, double panic_distance = 10.0,
+  Flee(AICharacter *ai_ch, LVecBase3 pos, double panic_distance = 10.0,
                               double relax_distance = 10.0, float flee_wt = 1.0);
 
   ~Flee();
-  LVecBase3f do_flee();
+  LVecBase3 do_flee();
   void flee_activate();
 };
 

+ 1 - 1
contrib/src/ai/meshNode.cxx

@@ -1,7 +1,7 @@
 
 #include "meshNode.h"
 
-Node::Node(int grid_x, int grid_y, LVecBase3f pos, float w, float l, float h) {
+Node::Node(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h) {
   for(int i = 0; i < 8; ++i) {
     _neighbours[i] = NULL;
   }

+ 2 - 2
contrib/src/ai/meshNode.h

@@ -47,7 +47,7 @@ public:
     int _grid_x, _grid_y;
 
     // Position of the node in 3D space.
-    LVecBase3f _position;
+    LVecBase3 _position;
 
     // Dimensions of each face / cell on the mesh.
     // Height is given in case of expansion to a 3d mesh. Currently not used.
@@ -58,7 +58,7 @@ public:
     // Note: The data in this member is discarded when mesh data is written into navmesh.csv file.
     Node *_next;
 
-    Node(int grid_x, int grid_y, LVecBase3f pos, float w, float l, float h);
+    Node(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h);
     ~Node();
 
     bool contains(float x, float y);

+ 15 - 15
contrib/src/ai/obstacleAvoidance.cxx

@@ -35,15 +35,15 @@ bool ObstacleAvoidance::obstacle_detection() {
   // Calculate the volume of the AICharacter with respect to render
   PT(BoundingVolume) np_bounds = _ai_char->get_node_path().get_bounds();
   CPT(BoundingSphere) np_sphere = np_bounds->as_bounding_sphere();
-  LVecBase3f avoidance(0.0, 0.0, 0.0);
+  LVecBase3 avoidance(0.0, 0.0, 0.0);
   double distance = 0x7fff ;
   double expanded_radius;
-  LVecBase3f to_obstacle;
-  LVecBase3f prev_avoidance;
+  LVecBase3 to_obstacle;
+  LVecBase3 prev_avoidance;
   for(unsigned int i = 0; i < _ai_char->_world->_obstacles.size(); ++i) {
     PT(BoundingVolume) bounds = _ai_char->_world->_obstacles[i].get_bounds();
     CPT(BoundingSphere) bsphere = bounds->as_bounding_sphere();
-    LVecBase3f near_obstacle = _ai_char->_world->_obstacles[i].get_pos() - _ai_char->get_node_path().get_pos();
+    LVecBase3 near_obstacle = _ai_char->_world->_obstacles[i].get_pos() - _ai_char->get_node_path().get_pos();
     // Check if it's the nearest obstacle, If so initialize as the nearest obstacle
     if((near_obstacle.length() < distance) && (_ai_char->_world->_obstacles[i].get_pos() != _ai_char->get_node_path().get_pos())) {
       _nearest_obstacle = _ai_char->_world->_obstacles[i];
@@ -51,13 +51,13 @@ bool ObstacleAvoidance::obstacle_detection() {
       expanded_radius = bsphere->get_radius() + np_sphere->get_radius();
     }
   }
-     LVecBase3f feeler = _feeler * _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward());
+     LVecBase3 feeler = _feeler * _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3::forward());
      feeler.normalize();
      feeler *= (expanded_radius + np_sphere->get_radius()) ;
      to_obstacle = _nearest_obstacle.get_pos() - _ai_char->get_node_path().get_pos();
-     LVector3f line_vector = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward());
-     LVecBase3f project = (to_obstacle.dot(line_vector) * line_vector) / line_vector.length_squared();
-     LVecBase3f perp = project - to_obstacle;
+     LVector3 line_vector = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3::forward());
+     LVecBase3 project = (to_obstacle.dot(line_vector) * line_vector) / line_vector.length_squared();
+     LVecBase3 perp = project - to_obstacle;
      // If the nearest obstacle will collide with our AICharacter then send obstacle detection as true
      if((_nearest_obstacle) && (perp.length() < expanded_radius - np_sphere->get_radius()) && (project.length() < feeler.length())) {
        return true;
@@ -90,27 +90,27 @@ void ObstacleAvoidance::obstacle_avoidance_activate() {
 
 /////////////////////////////////////////////////////////////////////////////////
 
-LVecBase3f ObstacleAvoidance::do_obstacle_avoidance() {
-  LVecBase3f offset = _ai_char->get_node_path().get_pos() - _nearest_obstacle.get_pos();
+LVecBase3 ObstacleAvoidance::do_obstacle_avoidance() {
+  LVecBase3 offset = _ai_char->get_node_path().get_pos() - _nearest_obstacle.get_pos();
   PT(BoundingVolume) bounds =_nearest_obstacle.get_bounds();
   CPT(BoundingSphere) bsphere = bounds->as_bounding_sphere();
   PT(BoundingVolume) np_bounds = _ai_char->get_node_path().get_bounds();
   CPT(BoundingSphere) np_sphere = np_bounds->as_bounding_sphere();
   double distance_needed = offset.length() - bsphere->get_radius() - np_sphere->get_radius();
   if((obstacle_detection())) {
-    LVecBase3f direction = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward());
+    LVecBase3 direction = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3::forward());
     direction.normalize();
     float forward_component = offset.dot(direction);
-    LVecBase3f projection = forward_component * direction;
-    LVecBase3f perpendicular_component = offset - projection;
+    LVecBase3 projection = forward_component * direction;
+    LVecBase3 perpendicular_component = offset - projection;
     double p = perpendicular_component.length();
     perpendicular_component.normalize();
-    LVecBase3f   avoidance = perpendicular_component;
+    LVecBase3   avoidance = perpendicular_component;
     // The more closer the obstacle, the more force it generates
     avoidance = (avoidance * _ai_char->get_max_force() * _ai_char->_movt_force) / (p + 0.01);
     return avoidance;
   }
   _ai_char->_steering->turn_on("obstacle_avoidance_activate");
   _ai_char->_steering->turn_off("obstacle_avoidance");
-  return LVecBase3f(0, 0, 0);
+  return LVecBase3(0, 0, 0);
 }

+ 1 - 1
contrib/src/ai/obstacleAvoidance.h

@@ -30,7 +30,7 @@ class EXPCL_PANDAAI ObstacleAvoidance {
     float _feeler;
 
     ObstacleAvoidance(AICharacter *ai_char, float feeler_length);
-    LVecBase3f do_obstacle_avoidance();
+    LVecBase3 do_obstacle_avoidance();
     ~ObstacleAvoidance();
     void obstacle_avoidance_activate();
     bool obstacle_detection();

+ 3 - 3
contrib/src/ai/pathFind.cxx

@@ -29,7 +29,7 @@ void PathFind::create_nav_mesh(const char* navmesh_filename) {
   // Stage variables.
   int grid_x, grid_y;
   float l, w, h;
-  LVecBase3f position;
+  LVecBase3 position;
 
   // Variable to hold line data read from file.
   string line;
@@ -75,7 +75,7 @@ void PathFind::create_nav_mesh(const char* navmesh_filename) {
         l = atof(fields[4].c_str());
         w = atof(fields[5].c_str());
         h = atof(fields[6].c_str());
-        position = LVecBase3f(atof(fields[7].c_str()), atof(fields[8].c_str()), atof(fields[9].c_str()));
+        position = LVecBase3(atof(fields[7].c_str()), atof(fields[8].c_str()), atof(fields[9].c_str()));
 
         Node *stage_node = new Node(grid_x, grid_y, position, w, l, h);
 
@@ -197,7 +197,7 @@ void PathFind::set_path_find(const char* navmesh_filename) {
 ///////////////////////////////////////////////////////////////////////////////////////
 
 
-void PathFind::path_find(LVecBase3f pos, string type) {
+void PathFind::path_find(LVecBase3 pos, string type) {
   if(type == "addPath") {
     if(_ai_char->_steering->_path_follow_obj) {
       _ai_char->_steering->remove_ai("pathfollow");

+ 2 - 2
contrib/src/ai/pathFind.h

@@ -43,7 +43,7 @@ public:
 
   int _grid_size;
   NodePath _path_find_target;
-  LVecBase3f _prev_position;
+  LVecBase3 _prev_position;
   PT(GeomNode) _parent;
   LineSegs *_pen;
   vector<int> _previous_obstacles;
@@ -62,7 +62,7 @@ public:
   void clear_previous_obstacles();
 
   void set_path_find(const char* navmesh_filename);
-  void path_find(LVecBase3f pos, string type = "normal");
+  void path_find(LVecBase3 pos, string type = "normal");
   void path_find(NodePath target, string type = "normal");
   void add_obstacle_to_mesh(NodePath obstacle);
   void dynamic_avoid(NodePath obstacle);

+ 2 - 2
contrib/src/ai/pathFollow.cxx

@@ -20,7 +20,7 @@ PathFollow::~PathFollow() {
 
 /////////////////////////////////////////////////////////////////////////////////////////
 
-void PathFollow::add_to_path(LVecBase3f pos) {
+void PathFollow::add_to_path(LVecBase3 pos) {
     _path.push_back(pos);
 }
 
@@ -119,7 +119,7 @@ void PathFollow::do_follow() {
 
 bool PathFollow::check_if_possible() {
   Node* src = find_in_mesh(_ai_char->_steering->_path_find_obj->_nav_mesh, _ai_char->_ai_char_np.get_pos(_ai_char->_window_render), _ai_char->_steering->_path_find_obj->_grid_size);
-  LVecBase3f _prev_position = _ai_char->_steering->_path_find_obj->_path_find_target.get_pos(_ai_char->_window_render);
+  LVecBase3 _prev_position = _ai_char->_steering->_path_find_obj->_path_find_target.get_pos(_ai_char->_window_render);
   Node* dst = find_in_mesh(_ai_char->_steering->_path_find_obj->_nav_mesh, _prev_position, _ai_char->_steering->_path_find_obj->_grid_size);
 
   if(src && dst) {

+ 2 - 2
contrib/src/ai/pathFollow.h

@@ -13,7 +13,7 @@ class EXPCL_PANDAAI PathFollow {
 public:
   AICharacter *_ai_char;
   float _follow_weight;
-  vector<LVecBase3f> _path;
+  vector<LVecBase3> _path;
   int _curr_path_waypoint;
   bool _start;
   NodePath _dummy;
@@ -23,7 +23,7 @@ public:
 
   PathFollow(AICharacter *ai_ch, float follow_wt);
   ~PathFollow();
-  void add_to_path(LVecBase3f pos);
+  void add_to_path(LVecBase3 pos);
   void start(string type);
   void do_follow();
   bool check_if_possible();

+ 6 - 6
contrib/src/ai/pursue.cxx

@@ -37,17 +37,17 @@ Pursue::~Pursue() {
 
 /////////////////////////////////////////////////////////////////////////////////
 
-LVecBase3f Pursue::do_pursue() {
+LVecBase3 Pursue::do_pursue() {
   assert(_pursue_target && "pursue target not assigned");
 
-  LVecBase3f present_pos = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render);
+  LVecBase3 present_pos = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render);
   double target_distance = (_pursue_target.get_pos(_ai_char->_window_render) - present_pos).length();
 
   if(int(target_distance) == 0) {
     _pursue_done = true;
-    _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0);
-    _ai_char->_steering->_pursue_force = LVecBase3f(0.0, 0.0, 0.0);
-    return(LVecBase3f(0.0, 0.0, 0.0));
+    _ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
+    _ai_char->_steering->_pursue_force = LVecBase3(0.0, 0.0, 0.0);
+    return(LVecBase3(0.0, 0.0, 0.0));
   }
   else {
     _pursue_done = false;
@@ -56,6 +56,6 @@ LVecBase3f Pursue::do_pursue() {
   _pursue_direction = _pursue_target.get_pos(_ai_char->_window_render) - present_pos;
   _pursue_direction.normalize();
 
-  LVecBase3f desired_force = _pursue_direction * _ai_char->_movt_force;
+  LVecBase3 desired_force = _pursue_direction * _ai_char->_movt_force;
   return(desired_force);
 }

+ 2 - 2
contrib/src/ai/pursue.h

@@ -28,12 +28,12 @@ public:
 
   NodePath _pursue_target;
   float _pursue_weight;
-  LVecBase3f _pursue_direction;
+  LVecBase3 _pursue_direction;
   bool _pursue_done;
 
   Pursue(AICharacter *ai_ch, NodePath target_object, float pursue_wt);
   ~Pursue();
-  LVecBase3f do_pursue();
+  LVecBase3 do_pursue();
 };
 
 #endif

+ 5 - 5
contrib/src/ai/seek.cxx

@@ -27,7 +27,7 @@ Seek::Seek(AICharacter *ai_ch, NodePath target_object, float seek_wt) {
   _seek_done = false;
 }
 
-Seek::Seek(AICharacter *ai_ch, LVecBase3f pos, float seek_wt) {
+Seek::Seek(AICharacter *ai_ch, LVecBase3 pos, float seek_wt) {
       _ai_char = ai_ch;
 
   _seek_position = pos;
@@ -51,16 +51,16 @@ Seek::~Seek() {
 
 /////////////////////////////////////////////////////////////////////////////////
 
-LVecBase3f Seek::do_seek() {
+LVecBase3 Seek::do_seek() {
   double target_distance = (_seek_position - _ai_char->_ai_char_np.get_pos(_ai_char->_window_render)).length();
 
     if(int(target_distance) == 0) {
         _seek_done = true;
-    _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0);
+    _ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
     _ai_char->_steering->turn_off("seek");
-    return(LVecBase3f(0.0, 0.0, 0.0));
+    return(LVecBase3(0.0, 0.0, 0.0));
   }
 
-  LVecBase3f desired_force = _seek_direction * _ai_char->_movt_force;
+  LVecBase3 desired_force = _seek_direction * _ai_char->_movt_force;
   return(desired_force);
 }

+ 5 - 5
contrib/src/ai/seek.h

@@ -26,16 +26,16 @@ class EXPCL_PANDAAI Seek {
 public:
   AICharacter *_ai_char;
 
-  LVecBase3f _seek_position;
+  LVecBase3 _seek_position;
   float _seek_weight;
-  LVecBase3f _seek_direction;
+  LVecBase3 _seek_direction;
   bool _seek_done;
-  LVecBase3f _seek_accum_force;
+  LVecBase3 _seek_accum_force;
 
   Seek(AICharacter *ai_ch, NodePath target_object, float seek_wt = 1.0);
-  Seek(AICharacter *ai_ch, LVecBase3f pos, float seek_wt = 1.0);
+  Seek(AICharacter *ai_ch, LVecBase3 pos, float seek_wt = 1.0);
   ~Seek();
-  LVecBase3f do_seek();
+  LVecBase3 do_seek();
 };
 
 #endif

+ 18 - 18
contrib/src/ai/wander.cxx

@@ -57,23 +57,23 @@ Wander::Wander(AICharacter *ai_ch, double wander_radius,int flag, double aoe, fl
   // default is XY axes
   switch(_flag) {
     case 0: {
-              _wander_target = LVecBase3f(_wander_radius * cos(theta), _wander_radius * sin(theta),0);
+              _wander_target = LVecBase3(_wander_radius * cos(theta), _wander_radius * sin(theta),0);
               break;
             }
     case 1: {
-              _wander_target = LVecBase3f(0, _wander_radius * cos(theta), _wander_radius * sin(theta));
+              _wander_target = LVecBase3(0, _wander_radius * cos(theta), _wander_radius * sin(theta));
               break;
             }
     case 2: {
-              _wander_target = LVecBase3f(_wander_radius * cos(theta), 0,  _wander_radius * sin(theta));
+              _wander_target = LVecBase3(_wander_radius * cos(theta), 0,  _wander_radius * sin(theta));
               break;
             }
     case 3: {
-              _wander_target = LVecBase3f(_wander_radius * sin(theta) * cos(si), _wander_radius * sin(theta) * sin(si), _wander_radius * cos(theta));
+              _wander_target = LVecBase3(_wander_radius * sin(theta) * cos(si), _wander_radius * sin(theta) * sin(si), _wander_radius * cos(theta));
               break;
             }
     default: {
-              _wander_target = LVecBase3f(_wander_radius * cos(theta), _wander_radius * sin(theta),0);
+              _wander_target = LVecBase3(_wander_radius * cos(theta), _wander_radius * sin(theta),0);
               break;
              }
   }
@@ -91,52 +91,52 @@ Wander::~Wander() {
 
 /////////////////////////////////////////////////////////////////////////////////
 
-LVecBase3f Wander::do_wander() {
-  LVecBase3f present_pos = _ai_char->get_node_path().get_pos(_ai_char->get_char_render());
+LVecBase3 Wander::do_wander() {
+  LVecBase3 present_pos = _ai_char->get_node_path().get_pos(_ai_char->get_char_render());
   // Create the random slices to enable random movement of wander for x,y,z respectively
   double time_slice_1 = random_clamped() * 1.5;
   double time_slice_2 = random_clamped() * 1.5;
   double time_slice_3 = random_clamped() * 1.5;
   switch(_flag) {
   case 0: {
-            _wander_target += LVecBase3f(time_slice_1, time_slice_2, 0);
+            _wander_target += LVecBase3(time_slice_1, time_slice_2, 0);
             break;
           }
   case 1: {
-            _wander_target += LVecBase3f(0, time_slice_1, time_slice_2);
+            _wander_target += LVecBase3(0, time_slice_1, time_slice_2);
             break;
           }
   case 2: {
-            _wander_target += LVecBase3f(time_slice_1, 0, time_slice_2);
+            _wander_target += LVecBase3(time_slice_1, 0, time_slice_2);
             break;
           }
   case 3: {
-            _wander_target += LVecBase3f(time_slice_1, time_slice_2, time_slice_3);
+            _wander_target += LVecBase3(time_slice_1, time_slice_2, time_slice_3);
             break;
           }
 
   default: {
-            _wander_target = LVecBase3f(time_slice_1, time_slice_2, 0);
+            _wander_target = LVecBase3(time_slice_1, time_slice_2, 0);
            }
   }
   _wander_target.normalize();
   _wander_target *= _wander_radius;
-  LVecBase3f target = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward());
+  LVecBase3 target = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3::forward());
   target.normalize();
   // Project wander target onto global space
   target = _wander_target + target;
-  LVecBase3f desired_target = present_pos + target;
-  LVecBase3f desired_force = desired_target - _ai_char->get_node_path().get_pos() ;
+  LVecBase3 desired_target = present_pos + target;
+  LVecBase3 desired_force = desired_target - _ai_char->get_node_path().get_pos() ;
   desired_force.normalize();
   desired_force *= _ai_char->_movt_force;
   double distance = (present_pos - _init_pos).length();
   if(_area_of_effect > 0 && distance > _area_of_effect) {
-    LVecBase3f direction = present_pos - _init_pos;
+    LVecBase3 direction = present_pos - _init_pos;
     direction.normalize();
     desired_force =  - direction * _ai_char->_movt_force;
-    LVecBase3f dirn = _ai_char->_steering->_steering_force;
+    LVecBase3 dirn = _ai_char->_steering->_steering_force;
     dirn.normalize();
-    _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0);
+    _ai_char->_steering->_steering_force = LVecBase3(0.0, 0.0, 0.0);
   }
   return desired_force;
 }

+ 3 - 3
contrib/src/ai/wander.h

@@ -24,14 +24,14 @@ class EXPCL_PANDAAI Wander {
   public:
     AICharacter *_ai_char;
     double _wander_radius;
-    LVecBase3f _wander_target;
+    LVecBase3 _wander_target;
     float _wander_weight;
     int _flag;
-    LVecBase3f _init_pos;
+    LVecBase3 _init_pos;
     double _area_of_effect;
 
     Wander(AICharacter *ai_ch, double wander_radius, int flag, double aoe, float wander_weight);
-    LVecBase3f do_wander();
+    LVecBase3 do_wander();
     ~Wander();
 };
 

+ 2 - 2
direct/src/directscripts/Doxyfile.python

@@ -760,13 +760,13 @@ STRIP_CODE_COMMENTS    = YES
 # then for each documented function all documented 
 # functions referencing it will be listed.
 
-REFERENCED_BY_RELATION = YES
+REFERENCED_BY_RELATION = NO
 
 # If the REFERENCES_RELATION tag is set to YES 
 # then for each documented function all documented entities 
 # called/used by that function will be listed.
 
-REFERENCES_RELATION    = YES
+REFERENCES_RELATION    = NO
 
 # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) 
 # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from 

+ 111 - 50
direct/src/directscripts/extract_docs.py

@@ -30,6 +30,7 @@ def comment(code):
             if empty_line:
                 # New paragraph.
                 comment += '\n\n'
+                empty_line = False
             elif comment:
                 comment += '\n'
             comment += '/// ' + line
@@ -56,15 +57,29 @@ def block_comment(code):
 
         line = line.rstrip()
         strline = line.lstrip('/ \t')
-        if reading_desc:
-            newlines.append('/// ' + line[min(indent, len(line) - len(strline)):])
-        else:
-            # A "Description:" text starts the description.
-            if strline.startswith("Description"):
-                strline = strline[11:].lstrip(': \t')
-                indent = len(line) - len(strline)
-                reading_desc = True
-                newlines.append('/// ' + strline)
+
+        if ':' in strline:
+            pre, post = strline.split(':', 1)
+            pre = pre.rstrip()
+            if pre == "Description":
+                strline = post.lstrip()
+            elif pre in ("Class", "Access", "Function", "Created by", "Enum"):
+                continue
+
+        if strline or len(newlines) > 0:
+            newlines.append('/// ' + strline)
+
+        #if reading_desc:
+        #    newlines.append('/// ' + line[min(indent, len(line) - len(strline)):])
+        #else:
+        #    # A "Description:" text starts the description.
+        #    if strline.startswith("Description"):
+        #        strline = strline[11:].lstrip(': \t')
+        #        indent = len(line) - len(strline)
+        #        reading_desc = True
+        #        newlines.append('/// ' + strline)
+        #    else:
+        #        print line
 
     newcode = '\n'.join(newlines)
     if len(newcode) > 0:
@@ -73,6 +88,9 @@ def block_comment(code):
         return ""
 
 def translateFunctionName(name):
+    if name.startswith("__"):
+        return name
+
     new = ""
     for i in name.split("_"):
         if new == "":
@@ -85,32 +103,58 @@ def translateFunctionName(name):
             new += i[0].upper() + i[1:]
     return new
 
-def translated_type_name(type):
+def translateTypeName(name, mangle=True):
+    # Equivalent to C++ classNameFromCppName
+    class_name = ""
+    bad_chars = "!@#$%^&*()<>,.-=+~{}? "
+    next_cap = False
+    first_char = mangle
+
+    for chr in name:
+        if (chr == '_' or chr == ' ') and mangle:
+            next_cap = True
+        elif chr in bad_chars:
+            if not mangle:
+                class_name += '_'
+        elif next_cap or first_char:
+            class_name += chr.upper()
+            next_cap = False
+            first_char = False
+        else:
+            class_name += chr
+
+    return class_name
+
+def translated_type_name(type, scoped=True):
+    while interrogate_type_is_wrapped(type):
+        if interrogate_type_is_const(type):
+            return 'const ' + translated_type_name(interrogate_type_wrapped_type(type))
+        else:
+            type = interrogate_type_wrapped_type(type)
+
+    typename = interrogate_type_name(type)
+    if typename in ("PyObject", "_object"):
+        return "object"
+
     if interrogate_type_is_atomic(type):
         token = interrogate_type_atomic_token(type)
         if token == 7:
             return 'str'
+        else:
+            return typename
 
-    typename = interrogate_type_name(type)
-    typename = typename.replace("< ", "").replace(" >", "")
-    return typename
-
-def translateTypeSpec(name):
-    name = name.strip("* ")
-    name = name.replace("BitMask< unsigned int, 32 >", "BitMask32")
-    name = name.replace("atomic ", "")
-    name = name.replace("< ", "").replace(" >", "")
-    if name == '_object':
-        name = 'object'
-    elif name == '_typeobject':
-        name = 'type'
-    return name
+    typename = translateTypeName(typename)
+
+    if scoped and interrogate_type_is_nested(type):
+        return translated_type_name(interrogate_type_outer_class(type)) + '::' + typename
+    else:
+        return typename
 
 def processElement(handle, element):
     if interrogate_element_has_comment(element):
         print >>handle, comment(interrogate_element_comment(element))
 
-    print >>handle, translateTypeSpec(translated_type_name(interrogate_element_type(element))),
+    print >>handle, translated_type_name(interrogate_element_type(element)),
     print >>handle, interrogate_element_name(element) + ';'
 
 def processFunction(handle, function, isConstructor = False):
@@ -120,11 +164,12 @@ def processFunction(handle, function, isConstructor = False):
             print >>handle, block_comment(interrogate_wrapper_comment(wrapper))
         
         if not isConstructor:
-            if not interrogate_wrapper_number_of_parameters(wrapper) > 0 or not interrogate_wrapper_parameter_is_this(wrapper, 0):
-                print >>handle, "static",
+            if interrogate_function_is_method(function):
+                if not interrogate_wrapper_number_of_parameters(wrapper) > 0 or not interrogate_wrapper_parameter_is_this(wrapper, 0):
+                    print >>handle, "static",
             
             if interrogate_wrapper_has_return_value(wrapper):
-                print >>handle, translateTypeSpec(translated_type_name(interrogate_wrapper_return_type(wrapper))),
+                print >>handle, translated_type_name(interrogate_wrapper_return_type(wrapper)),
             else:
                 pass#print >>handle, "void",
 
@@ -137,7 +182,7 @@ def processFunction(handle, function, isConstructor = False):
             if not interrogate_wrapper_parameter_is_this(wrapper, i_param):
                 if not first:
                     print >>handle, ",",
-                print >>handle, translateTypeSpec(translated_type_name(interrogate_wrapper_parameter_type(wrapper, i_param))),
+                print >>handle, translated_type_name(interrogate_wrapper_parameter_type(wrapper, i_param)),
                 if interrogate_wrapper_parameter_has_name(wrapper, i_param):
                     print >>handle, interrogate_wrapper_parameter_name(wrapper, i_param),
                 first = False
@@ -145,7 +190,7 @@ def processFunction(handle, function, isConstructor = False):
         print >>handle, ");"
 
 def processType(handle, type):
-    typename = translated_type_name(type)
+    typename = translated_type_name(type, scoped=False)
     derivations = [ translated_type_name(interrogate_type_get_derivation(type, n)) for n in range(interrogate_type_number_of_derivations(type)) ]
     
     if interrogate_type_has_comment(type):
@@ -157,7 +202,7 @@ def processType(handle, type):
             docstring = comment(interrogate_type_enum_value_comment(type, i_value))
             if docstring:
                 print >>handle, docstring
-            print >>handle, translateFunctionName(interrogate_type_enum_value_name(type, i_value)), "=", interrogate_type_enum_value(type, i_value), ","
+            print >>handle, interrogate_type_enum_value_name(type, i_value), "=", interrogate_type_enum_value(type, i_value), ","
     else:
         if interrogate_type_is_struct(type):
             classtype = "struct"
@@ -192,38 +237,54 @@ def processType(handle, type):
     
     print >>handle, "};"
 
+def processModule(handle, package):
+    print >>handle, "namespace %s {" % package
+
+    if package != "core":
+        print >>handle, "using namespace core;"
+
+    for i_type in xrange(interrogate_number_of_global_types()):
+        type = interrogate_get_global_type(i_type)
+
+        if interrogate_type_has_module_name(type):
+            module_name = interrogate_type_module_name(type)
+            if "panda3d." + package == module_name:
+                processType(handle, type)
+        else:
+            print "Type %s has no module name" % typename
+
+    for i_func in xrange(interrogate_number_of_global_functions()):
+        func = interrogate_get_global_function(i_func)
+
+        if interrogate_function_has_module_name(func):
+            module_name = interrogate_function_module_name(func)
+            if "panda3d." + package == module_name:
+                processFunction(handle, func)
+        else:
+            print "Type %s has no module name" % typename
+
+    print >>handle, "}"
+
+
 if __name__ == "__main__":
     handle = open("pandadoc.hpp", "w")
     
     print >>handle, comment("Panda3D modules that are implemented in C++.")
-    print >>handle, "namespace panda3d {}"
+    print >>handle, "namespace panda3d {"
     
     # Determine the path to the interrogatedb files
     interrogate_add_search_directory(os.path.join(os.path.dirname(pandac.__file__), "..", "..", "etc"))
     interrogate_add_search_directory(os.path.join(os.path.dirname(pandac.__file__), "input"))
 
     import panda3d.core
+    processModule(handle, "core")
 
     for lib in os.listdir(os.path.dirname(panda3d.__file__)):
         if lib.endswith(('.pyd', '.so')) and not lib.startswith('core.'):
-            __import__('panda3d.' + os.path.splitext(lib)[0])
+            module_name = os.path.splitext(lib)[0]
+            __import__("panda3d." + module_name)
+            processModule(handle, module_name)
 
-    lastpkg = None
-    for i_type in xrange(interrogate_number_of_global_types()):
-        type = interrogate_get_global_type(i_type)
 
-        if interrogate_type_has_module_name(type):
-            package = interrogate_type_module_name(type)
-            if lastpkg != package:
-                if lastpkg is not None:
-                    print >>handle, "}"
-                print >>handle, "namespace %s {" % package
-                lastpkg = package
-
-            processType(handle, type)
-        else:
-            print "Type %s has no module name" % typename
-    
-    if lastpkg is not None:
-        print >>handle, "}"
+    print >>handle, "}"
     handle.close()

+ 1 - 1
direct/src/filter/FilterManager.py

@@ -294,7 +294,7 @@ class FilterManager(DirectObject):
 
         winprops = WindowProperties()
         winprops.setSize(xsize, ysize)
-        props = FrameBufferProperties(self.win.getFbProperties())
+        props = FrameBufferProperties(FrameBufferProperties.getDefault())
         props.setBackBuffers(0)
         props.setRgbColor(1)
         props.setDepthBits(depthbits)

+ 24 - 10
direct/src/gui/DirectDialog.py

@@ -94,8 +94,9 @@ class DirectDialog(DirectFrame):
             ('text',              '',            None),
             ('text_align',        TextNode.ALeft,   None),
             ('text_scale',        0.06,          None),
-            ('image',  DGG.getDefaultDialogGeom(),   None),
-            ('relief',            None,          None),
+            ('image',             None,          None),
+            ('relief',            DGG.RAISED,     None),
+            ('borderWidth',       (0.01, 0.01),  None),
             ('buttonTextList',    [],            DGG.INITOPT),
             ('buttonGeomList',    [],            DGG.INITOPT),
             ('buttonImageList',   [],            DGG.INITOPT),
@@ -210,10 +211,16 @@ class DirectDialog(DirectFrame):
         bounds = self.stateNodePath[0].getTightBounds()
         if image:
             image.reparentTo(self.stateNodePath[0])
-        l = bounds[0][0]
-        r = bounds[1][0]
-        b = bounds[0][2]
-        t = bounds[1][2]
+        if bounds is None:
+            l = 0
+            r = 0
+            b = 0
+            t = 0
+        else:
+            l = bounds[0][0]
+            r = bounds[1][0]
+            b = bounds[0][2]
+            t = bounds[1][2]
         # Center text and geom around origin
         # How far is center of text from origin?
         xOffset = -(l+r)*0.5
@@ -246,10 +253,16 @@ class DirectDialog(DirectFrame):
                 bl = br = bb = bt = 0
                 for button in self.buttonList:
                     bounds = button.stateNodePath[0].getTightBounds()
-                    bl = min(bl, bounds[0][0])
-                    br = max(br, bounds[1][0])
-                    bb = min(bb, bounds[0][2])
-                    bt = max(bt, bounds[1][2])
+                    if bounds is None:
+                        bl = 0
+                        br = 0
+                        bb = 0
+                        bt = 0
+                    else:
+                        bl = min(bl, bounds[0][0])
+                        br = max(br, bounds[1][0])
+                        bb = min(bb, bounds[0][2])
+                        bt = max(bt, bounds[1][2])
                 bl -= bpad[0]
                 br += bpad[0]
                 bb -= bpad[1]
@@ -303,6 +316,7 @@ class DirectDialog(DirectFrame):
         # reduce bottom by pad, button height and 2*button pad
         b = min(b - self['midPad'] - bpad[1] - bHeight - bpad[1], b) - pad[1]
         t = t + self['topPad'] + pad[1]
+        self['frameSize'] = (l, r, b, t)
         self['image_scale'] = (r - l, 1, t - b)
         # Center frame about text and buttons
         self['image_pos'] = ((l+r)*0.5, 0.0, (b+t)*0.5)

+ 5 - 3
direct/src/gui/DirectRadioButton.py

@@ -43,7 +43,7 @@ class DirectRadioButton(DirectButton):
             ('boxGeom', None, None),
             ('boxGeomColor', None, None),
             ('boxGeomScale', 1.0, None),
-            ('boxImage', loader.loadModel('models/gui/radio_button_gui'), None),
+            ('boxImage', None, None),
             ('boxImageScale', 1.0, None),
             ('boxImageColor', VBase4(1, 1, 1, 1), None),
             ('boxRelief', None, None),
@@ -69,9 +69,11 @@ class DirectRadioButton(DirectButton):
         # Call option initialization functions
         self.initialiseoptions(DirectRadioButton)
         # After initialization with X giving it the correct size, put back space
-        if self['boxGeom'] ==  None:
+        if self['boxGeom'] is None:
+            if not 'boxRelief' in kw and self['boxImage'] is None:
+                self.indicator['relief'] = DGG.SUNKEN
             self.indicator['text'] = (' ', '*')
-            self.indicator['text_pos'] = (0, -.5)
+            self.indicator['text_pos'] = (0, -.25)
         else:
             self.indicator['text'] = (' ', ' ')
 

+ 8 - 6
direct/src/showbase/Transitions.py

@@ -59,15 +59,12 @@ class Transitions:
 
         # Reload fade if its already been created
         if self.fade:
-            del self.fade
+            self.fade.destroy()
             self.fade = None
             self.loadFade()
 
     def loadFade(self):
-        if not self.fadeModel:
-            self.fadeModel = loader.loadModel(self.FadeModelName)
-
-        if self.fade == None:
+        if self.fade is None:
             # We create a DirectFrame for the fade polygon, instead of
             # simply loading the polygon model and using it directly,
             # so that it will also obscure mouse events for objects
@@ -80,10 +77,15 @@ class Transitions:
                 image_scale = (4, 2, 2),
                 state = DGG.NORMAL,
                 )
+            if not self.fadeModel:
+                # No fade model was given, so we make this the fade model.
+                self.fade["relief"] = DGG.FLAT
+                self.fade["frameSize"] = (-2, 2, -1, 1)
+                self.fade["frameColor"] = (0, 0, 0, 1)
+                self.fade.setTransparency(TransparencyAttrib.MAlpha)
             self.fade.setBin('unsorted', 0)
             self.fade.setColor(0,0,0,0)
 
-    
     def getFadeInIval(self, t=0.5, finishIval=None):
         """
         Returns an interval without starting it.  This is particularly useful in

+ 8 - 2
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -4625,8 +4625,14 @@ write_function_instance(ostream &out, FunctionRemap *remap,
         format_specifiers += "h";
         parameter_list += ", &" + param_name;
       }
-      expected_params += "int";
-      only_pyobjects = false;
+
+      extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
+      extra_param_check += " && " + param_name + "_long != NULL";
+      pexpr_string = "(" + type->get_local_name(&parser) + ")" +
+                     "PyLong_AsLongLong(" + param_name + "_long)";
+      extra_cleanup += "Py_XDECREF(" + param_name + "_long);";
+      expected_params += "long long";
+      ++num_params;
 
     } else if (TypeManager::is_unsigned_integer(type)) {
       if (args_type == AT_single_arg) {

+ 7 - 5
makepanda/makepanda.py

@@ -949,7 +949,7 @@ def CompileCxx(obj,src,opts):
             if PkgSkip("TOUCHINPUT") == 0:
                 cmd += "/DWINVER=0x601 "
             cmd += "/Fo" + obj + " /nologo /c"
-            if (GetTargetArch() != 'x64' and PkgSkip("SSE2") == 0):
+            if GetTargetArch() != 'x64' and (not PkgSkip("SSE2") or 'SSE2' in opts):
                 cmd += " /arch:SSE2"
             for x in ipath: cmd += " /I" + x
             for (opt,dir) in INCDIRECTORIES:
@@ -1160,7 +1160,7 @@ def CompileCxx(obj,src,opts):
                 if optlevel >= 4 or GetTarget() == "android":
                     cmd += " -fno-rtti"
 
-        if PkgSkip("SSE2") == 0 and not arch.startswith("arm"):
+        if ('SSE2' in opts or not PkgSkip("SSE2")) and not arch.startswith("arm"):
             cmd += " -msse2"
 
         if optlevel >= 3:
@@ -1705,7 +1705,7 @@ def RunGenPyCode(target, inputs, opts):
     if (PkgSkip("PYTHON") != 0):
         return
 
-    cmdstr = sys.executable + " "
+    cmdstr = BracketNameWithQuotes(SDK["PYTHONEXEC"]) + " "
     if sys.version_info >= (2, 6):
         cmdstr += "-B "
 
@@ -1729,7 +1729,7 @@ def RunGenPyCode(target, inputs, opts):
 def FreezePy(target, inputs, opts):
     assert len(inputs) > 0
     # Make sure this function isn't called before genpycode is run.
-    cmdstr = sys.executable + " "
+    cmdstr = BracketNameWithQuotes(SDK["PYTHONEXEC"]) + " "
     if sys.version_info >= (2, 6):
         cmdstr += "-B "
 
@@ -1757,7 +1757,7 @@ def FreezePy(target, inputs, opts):
 def Package(target, inputs, opts):
     assert len(inputs) == 1
     # Invoke the ppackage script.
-    command = sys.executable + " "
+    command = BracketNameWithQuotes(SDK["PYTHONEXEC"]) + " "
     if GetOptimizeOption(opts) >= 4:
         command += "-OO "
 
@@ -3192,6 +3192,7 @@ if (not RUNTIME):
   OPTS=['DIR:panda/src/pnmimage', 'BUILDING:PANDA',  'ZLIB']
   TargetAdd('p3pnmimage_composite1.obj', opts=OPTS, input='p3pnmimage_composite1.cxx')
   TargetAdd('p3pnmimage_composite2.obj', opts=OPTS, input='p3pnmimage_composite2.cxx')
+  TargetAdd('p3pnmimage_convert_srgb_sse2.obj', opts=OPTS+['SSE2'], input='convert_srgb_sse2.cxx')
 
   OPTS=['DIR:panda/src/pnmimage', 'ZLIB']
   IGATEFILES=GetDirectoryContents('panda/src/pnmimage', ["*.h", "*_composite*.cxx"])
@@ -3621,6 +3622,7 @@ if (not RUNTIME):
   TargetAdd('libpanda.dll', input='p3pnmimagetypes_composite2.obj')
   TargetAdd('libpanda.dll', input='p3pnmimage_composite1.obj')
   TargetAdd('libpanda.dll', input='p3pnmimage_composite2.obj')
+  TargetAdd('libpanda.dll', input='p3pnmimage_convert_srgb_sse2.obj')
   TargetAdd('libpanda.dll', input='p3text_composite1.obj')
   TargetAdd('libpanda.dll', input='p3text_composite2.obj')
   TargetAdd('libpanda.dll', input='p3tform_composite1.obj')

+ 1 - 1
panda/src/bullet/bulletSoftBodyNode.cxx

@@ -1018,7 +1018,7 @@ make_tet_mesh(BulletSoftBodyWorldInfo &info, const char *ele, const char *face,
 
   for (int i=0; i<pos.size(); ++i) {
     int index = 0;
-    PN_stdfloat x, y, z;
+    float x, y, z;
 
     sscanf(node, "%d %f %f %f", &index, &x, &y, &z);
     node += next_line(node);

+ 16 - 19
panda/src/cocoadisplay/cocoaGraphicsPipe.mm

@@ -324,34 +324,31 @@ make_output(const string &name,
   }
 
   // Second thing to try: a GLGraphicsBuffer
+
   if (retry == 1) {
-    if ((host==0)||
-  //        (!gl_support_fbo)||
-        ((flags&BF_require_parasite)!=0)||
-        ((flags&BF_require_window)!=0)) {
+    if (!gl_support_fbo || host == NULL ||
+        (flags & (BF_require_parasite | BF_require_window)) != 0) {
       return NULL;
     }
     // Early failure - if we are sure that this buffer WONT
     // meet specs, we can bail out early.
-    if ((flags & BF_fb_props_optional)==0) {
-      if ((fb_prop.get_indexed_color() > 0)||
-          (fb_prop.get_back_buffers() > 0)||
-          (fb_prop.get_accum_bits() > 0)||
-          (fb_prop.get_multisamples() > 0)) {
+    if ((flags & BF_fb_props_optional) == 0) {
+      if (fb_prop.get_indexed_color() ||
+          fb_prop.get_back_buffers() > 0 ||
+          fb_prop.get_accum_bits() > 0) {
         return NULL;
       }
     }
-    // Early success - if we are sure that this buffer WILL
-    // meet specs, we can precertify it.
-    if ((cocoagsg != 0) &&
-        (cocoagsg->is_valid()) &&
-        (!cocoagsg->needs_reset()) &&
-        (cocoagsg->_supports_framebuffer_object) &&
-        (cocoagsg->_glDrawBuffers != 0) &&
-        (fb_prop.is_basic())) {
-      precertify = true;
+    if (cocoagsg != NULL && cocoagsg->is_valid() && !cocoagsg->needs_reset()) {
+      if (!cocoagsg->_supports_framebuffer_object ||
+          cocoagsg->_glDrawBuffers == NULL) {
+        return NULL;
+      } else {
+        // Early success - if we are sure that this buffer WILL
+        // meet specs, we can precertify it.
+        precertify = true;
+      }
     }
-
     return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop,
                                 flags, gsg, host);
   }

+ 7 - 13
panda/src/display/config_display.cxx

@@ -418,19 +418,13 @@ ConfigVariableInt depth_bits
 ("depth-bits", 0,
  PRC_DESC("The minimum number of depth buffer bits requested."));
 ConfigVariableInt color_bits
-("color-bits", 0,
- PRC_DESC("The minimum number of total color buffer bits requested.  This "
-          "value is like red-bits + blue-bits + green-bits except Panda "
-          "won't care how the bits are divided up."));
-ConfigVariableInt red_bits
-("red-bits", 0,
- PRC_DESC("The minimum number of red color buffer bits requested."));
-ConfigVariableInt green_bits
-("green-bits", 0,
- PRC_DESC("The minimum number of green color buffer bits requested."));
-ConfigVariableInt blue_bits
-("blue-bits", 0,
- PRC_DESC("The minimum number of blue color buffer bits requested."));
+("color-bits", "",
+ PRC_DESC("The minimum number of total color buffer bits requested.  If you "
+          "specify only one value, it will represent the total value for the "
+          "red, green and blue channels, and indicates you don't care how the "
+          "bits are divided up among the red, green and blue channels.  If "
+          "you specify three values, it represents three separate red, green "
+          "and blue bit requirements."));
 ConfigVariableInt alpha_bits
 ("alpha-bits", 0,
  PRC_DESC("The minimum number of alpha buffer bits requested."));

+ 0 - 3
panda/src/display/config_display.h

@@ -97,9 +97,6 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableBool framebuffer_srgb;
 extern EXPCL_PANDA_DISPLAY ConfigVariableBool framebuffer_float;
 extern EXPCL_PANDA_DISPLAY ConfigVariableInt depth_bits;
 extern EXPCL_PANDA_DISPLAY ConfigVariableInt color_bits;
-extern EXPCL_PANDA_DISPLAY ConfigVariableInt red_bits;
-extern EXPCL_PANDA_DISPLAY ConfigVariableInt green_bits;
-extern EXPCL_PANDA_DISPLAY ConfigVariableInt blue_bits;
 extern EXPCL_PANDA_DISPLAY ConfigVariableInt alpha_bits;
 extern EXPCL_PANDA_DISPLAY ConfigVariableInt stencil_bits;
 extern EXPCL_PANDA_DISPLAY ConfigVariableInt accum_bits;

+ 26 - 21
panda/src/display/frameBufferProperties.I

@@ -305,37 +305,42 @@ get_float_depth() const {
 INLINE void FrameBufferProperties::
 set_depth_bits(int n) {
   _property[FBP_depth_bits] = n;
-  _specified[FBP_depth_bits] = true;
+  _specified |= (1 << FBP_depth_bits);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: FrameBufferProperties::set_color_bits
 //       Access: Published
-//  Description:
+//  Description: Sets the number of requested color bits as a single
+//               number that represents the sum of the individual
+//               numbers of red, green and blue bits.  Panda won't
+//               care how the individual bits are divided up.
+//
+//               See also set_rgba_bits, which allows you to specify
+//               requirements for the individual components.
 ////////////////////////////////////////////////////////////////////
 INLINE void FrameBufferProperties::
 set_color_bits(int n) {
   _property[FBP_color_bits] = n;
-  _specified[FBP_color_bits] = true;
+  _specified |= (1 << FBP_color_bits);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: FrameBufferProperties::set_rgba_bits
 //       Access: Published
-//  Description: Sets all color bit requirements separately.
+//  Description: Convenience method for setting the red, green, blue
+//               and alpha bits in one go.
 ////////////////////////////////////////////////////////////////////
 INLINE void FrameBufferProperties::
 set_rgba_bits(int r, int g, int b, int a) {
   _property[FBP_red_bits] = r;
-  _specified[FBP_red_bits] = true;
   _property[FBP_green_bits] = g;
-  _specified[FBP_green_bits] = true;
   _property[FBP_blue_bits] = b;
-  _specified[FBP_blue_bits] = true;
   _property[FBP_alpha_bits] = a;
-  _specified[FBP_alpha_bits] = true;
   _property[FBP_color_bits] = r + g + b;
-  _specified[FBP_color_bits] = true;
+  _specified |= (1 << FBP_color_bits) | (1 << FBP_red_bits) |
+                (1 << FBP_green_bits) | (1 << FBP_blue_bits) |
+                (1 << FBP_alpha_bits);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -346,7 +351,7 @@ set_rgba_bits(int r, int g, int b, int a) {
 INLINE void FrameBufferProperties::
 set_red_bits(int n) {
   _property[FBP_red_bits] = n;
-  _specified[FBP_red_bits] = true;
+  _specified |= (1 << FBP_red_bits);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -357,7 +362,7 @@ set_red_bits(int n) {
 INLINE void FrameBufferProperties::
 set_green_bits(int n) {
   _property[FBP_green_bits] = n;
-  _specified[FBP_green_bits] = true;
+  _specified |= (1 << FBP_green_bits);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -368,7 +373,7 @@ set_green_bits(int n) {
 INLINE void FrameBufferProperties::
 set_blue_bits(int n) {
   _property[FBP_blue_bits] = n;
-  _specified[FBP_blue_bits] = true;
+  _specified |= (1 << FBP_blue_bits);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -379,7 +384,7 @@ set_blue_bits(int n) {
 INLINE void FrameBufferProperties::
 set_alpha_bits(int n) {
   _property[FBP_alpha_bits] = n;
-  _specified[FBP_alpha_bits] = true;
+  _specified |= (1 << FBP_alpha_bits);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -390,7 +395,7 @@ set_alpha_bits(int n) {
 INLINE void FrameBufferProperties::
 set_stencil_bits(int n) {
   _property[FBP_stencil_bits] = n;
-  _specified[FBP_stencil_bits] = true;
+  _specified |= (1 << FBP_stencil_bits);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -401,7 +406,7 @@ set_stencil_bits(int n) {
 INLINE void FrameBufferProperties::
 set_accum_bits(int n) {
   _property[FBP_accum_bits] = n;
-  _specified[FBP_accum_bits] = true;
+  _specified |= (1 << FBP_accum_bits);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -413,7 +418,7 @@ INLINE void FrameBufferProperties::
 set_aux_rgba(int n) {
   nassertv(n < 4);
   _property[FBP_aux_rgba] = n;
-  _specified[FBP_aux_rgba] = true;
+  _specified |= (1 << FBP_aux_rgba);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -425,7 +430,7 @@ INLINE void FrameBufferProperties::
 set_aux_hrgba(int n) {
   nassertv(n < 4);
   _property[FBP_aux_hrgba] = n;
-  _specified[FBP_aux_hrgba] = true;
+  _specified |= (1 << FBP_aux_hrgba);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -437,7 +442,7 @@ INLINE void FrameBufferProperties::
 set_aux_float(int n) {
   nassertv(n < 4);
   _property[FBP_aux_float] = n;
-  _specified[FBP_aux_float] = true;
+  _specified |= (1 << FBP_aux_float);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -448,7 +453,7 @@ set_aux_float(int n) {
 INLINE void FrameBufferProperties::
 set_multisamples(int n) {
   _property[FBP_multisamples] = n;
-  _specified[FBP_multisamples] = true;
+  _specified |= (1 << FBP_multisamples);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -460,7 +465,7 @@ set_multisamples(int n) {
 INLINE void FrameBufferProperties::
 set_coverage_samples(int n) {
   _property[FBP_coverage_samples] = n;
-  _specified[FBP_coverage_samples] = true;
+  _specified |= (1 << FBP_coverage_samples);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -471,7 +476,7 @@ set_coverage_samples(int n) {
 INLINE void FrameBufferProperties::
 set_back_buffers(int n) {
   _property[FBP_back_buffers] = n;
-  _specified[FBP_back_buffers] = true;
+  _specified |= (1 << FBP_back_buffers);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 29 - 33
panda/src/display/frameBufferProperties.cxx

@@ -37,9 +37,9 @@ void FrameBufferProperties::
 operator = (const FrameBufferProperties &copy) {
   _flags_specified = copy._flags_specified;
   _flags = copy._flags;
+  _specified = copy._specified;
 
   for (int i = 0; i < FBP_COUNT; ++i) {
-    _specified[i] = copy._specified[i];
     _property[i]  = copy._property[i];
   }
 }
@@ -144,17 +144,24 @@ get_default() {
   if (depth_bits > 0) {
     default_props.set_depth_bits(depth_bits);
   }
-  if (color_bits > 0) {
-    default_props.set_color_bits(color_bits);
-  }
-  if (red_bits > 0) {
-    default_props.set_red_bits(red_bits);
-  }
-  if (green_bits > 0) {
-    default_props.set_green_bits(green_bits);
-  }
-  if (blue_bits > 0) {
-    default_props.set_blue_bits(blue_bits);
+  switch (color_bits.size()) {
+  case 0:
+    break;
+  case 1:
+    default_props.set_color_bits(color_bits[0]);
+    break;
+  case 3:
+    default_props.set_color_bits(color_bits[0] + color_bits[1] + color_bits[2]);
+    default_props.set_red_bits(color_bits[0]);
+    default_props.set_green_bits(color_bits[1]);
+    default_props.set_blue_bits(color_bits[2]);
+    break;
+  default:
+    default_props.set_color_bits(color_bits[0]);
+    display_cat.error()
+      << "Configuration variable color-bits takes either 1 or 3 values, not "
+      << color_bits.size() << "\n";
+    break;
   }
   if (alpha_bits > 0) {
     default_props.set_alpha_bits(alpha_bits);
@@ -189,10 +196,11 @@ operator == (const FrameBufferProperties &other) const {
     return false;
   }
 
+  if (_specified != other._specified) {
+    return false;
+  }
+
   for (int i = 0; i < FBP_COUNT; ++i) {
-    if (_specified[i] != other._specified[i]) {
-      return false;
-    }
     if (_property[i] != other._property[i]) {
       return false;
     }
@@ -214,9 +222,9 @@ clear() {
   _flags_specified = 0;
 
   for (int i = 0; i < FBP_COUNT; ++i) {
-    _specified[i] = 0;
     _property[i] = 0;
   }
+  _specified = 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -232,9 +240,9 @@ add_properties(const FrameBufferProperties &other) {
   _flags |= other._flags & other._flags_specified;
 
   for (int i = 0; i < FBP_COUNT; ++i) {
-    if (other._specified[i]) {
+    if (other._specified & (1 << i)) {
       _property[i] = other._property[i];
-      _specified[i] = true;
+      _specified |= (1 << i);
     }
   }
 }
@@ -364,30 +372,18 @@ get_buffer_mask() const {
 ////////////////////////////////////////////////////////////////////
 bool FrameBufferProperties::
 is_any_specified() const {
-  if (_flags_specified != 0) {
-    return true;
-  }
-
-  for (int i = 0; i < FBP_COUNT; ++i) {
-    if (_specified[i]) {
-      return true;
-    }
-  }
-  return false;
+  return (_flags_specified | _specified) != 0;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: FrameBufferProperties::set_all_specified
 //       Access: Published
-//  Description: sets all the specified bits.
+//  Description: Marks all bits as having been specified.
 ////////////////////////////////////////////////////////////////////
 void FrameBufferProperties::
 set_all_specified() {
   _flags_specified = FBF_all;
-
-  for (int i = 0; i < FBP_COUNT; ++i) {
-    _specified[i] = true;
-  }
+  _specified = (1 << FBP_COUNT) - 1;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/display/frameBufferProperties.h

@@ -67,7 +67,7 @@ private:
   };
 
   int _property[FBP_COUNT];
-  bool _specified[FBP_COUNT];
+  int _specified;
 
   int _flags;
   int _flags_specified;

+ 3 - 3
panda/src/distort/nonlinearImager.cxx

@@ -27,7 +27,7 @@
 ////////////////////////////////////////////////////////////////////
 //     Function: NonlinearImager::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 NonlinearImager::
 NonlinearImager() {
@@ -38,7 +38,7 @@ NonlinearImager() {
 ////////////////////////////////////////////////////////////////////
 //     Function: NonlinearImager::Destructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 NonlinearImager::
 ~NonlinearImager() {
@@ -54,7 +54,7 @@ NonlinearImager::
 ////////////////////////////////////////////////////////////////////
 //     Function: NonlinearImager::add_screen
 //       Access: Published
-//               This version of this method is deprecated and will
+//  Description: This version of this method is deprecated and will
 //               soon be removed.  Use the version that takes two
 //               parameters instead.
 ////////////////////////////////////////////////////////////////////

+ 8 - 3
panda/src/doc/eggSyntax.txt

@@ -1476,6 +1476,10 @@ GROUPING ENTRIES
     its geometry is used to define the extent of the collision
     surface (unless the "descend" flag is given; see below).
 
+    It is now deprecated to use <Collide> without "descend"; it will
+    become the default soon.  You should always specify it for best
+    compatibility.
+
     Valid types so far are:
 
     Plane
@@ -1538,11 +1542,12 @@ GROUPING ENTRIES
 
     descend
 
-      Instead of creating only one collision object of the given type,
-      each group descended from this node that contains geometry will
+      Each group descended from this node that contains geometry will
       define a new collision object of the given type.  The event
       name, if any, will also be inherited from the top node and
-      shared among all the collision objects.
+      shared among all the collision objects.  This option will soon
+      be the default; it is suggested that it is always specified for
+      most compatibility.
 
     keep
  

+ 4 - 0
panda/src/egg2pg/eggLoader.cxx

@@ -2970,6 +2970,10 @@ make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
         make_collision_solids(start_group, DCAST(EggGroup, *ci), cnode);
       }
     }
+  } else {
+    egg2pg_cat.warning()
+      << "Using <Collide> without 'descend' is deprecated.  'descend' "
+      << "will become the default in a future version of Panda3D.\n";
   }
 }
 

+ 7 - 21
panda/src/glstuff/glCgShaderContext_src.cxx

@@ -41,7 +41,6 @@ CLP(CgShaderContext)::
 CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext(s) {
   _glgsg = glgsg;
   _cg_program = 0;
-  _glsl_profile = false;
 
   nassertv(s->get_language() == Shader::SL_Cg);
 
@@ -63,10 +62,6 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte
     release_resources();
 
   } else {
-    if (cgGetProgramProfile(_cg_program) == CG_PROFILE_GLSLC) {
-      _glsl_profile = true;
-    }
-
     cgGLLoadProgram(_cg_program);
     CGerror error = cgGetError();
     if (error != CG_NO_ERROR) {
@@ -351,14 +346,7 @@ issue_parameters(int altered) {
     if (altered & (_shader->_mat_spec[i]._dep[0] | _shader->_mat_spec[i]._dep[1])) {
       const LMatrix4 *val = _glgsg->fetch_specified_value(_shader->_mat_spec[i], altered);
       if (!val) continue;
-#ifndef STDFLOAT_DOUBLE
-      // In this case, the data is already single-precision.
-      const PN_float32 *data = val->get_data();
-#else
-      // In this case, we have to convert it.
-      LMatrix4f valf = LCAST(PN_float32, *val);
-      const PN_float32 *data = valf.get_data();
-#endif
+      const PN_stdfloat *data = val->get_data();
 
       CGparameter p = _cg_parameter_map[_shader->_mat_spec[i]._id._seqno];
       switch (_shader->_mat_spec[i]._piece) {
@@ -377,13 +365,13 @@ issue_parameters(int altered) {
       case Shader::SMP_row3x3: GLfv(cgGLSetParameter3)(p, data+12); continue;
       case Shader::SMP_upper3x3:
         {
-          LMatrix3f upper3 = val->get_upper_3();
+          LMatrix3 upper3 = val->get_upper_3();
           GLfc(cgGLSetMatrixParameter)(p, upper3.get_data());
           continue;
         }
       case Shader::SMP_transpose3x3:
         {
-          LMatrix3f upper3 = val->get_upper_3();
+          LMatrix3 upper3 = val->get_upper_3();
           GLfr(cgGLSetMatrixParameter)(p, upper3.get_data());
           continue;
         }
@@ -412,7 +400,7 @@ disable_shader_vertex_arrays() {
     CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno];
     if (p == 0) continue;
 
-    if (_glsl_profile && cgGetParameterBaseResource(p) == CG_ATTR0) {
+    if (cgGetParameterBaseResource(p) == CG_ATTR0) {
       int index = cgGetParameterResourceIndex(p);
       if (index >= 8) {
         _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (index - 8));
@@ -501,11 +489,9 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) {
           num_values = GL_BGRA;
         }
 
-        // This is truly the most preposterous hack.  When using the GLSL
-        // profiles, cgGLSetParameterPointer relies on the the driver mapping
-        // standard attributes to fixed indices (and breaking the spec doing
-        // so), which only the NVIDIA drivers do.  Unbelievable.
-        if (_glsl_profile && cgGetParameterBaseResource(p) == CG_ATTR0) {
+        // cgGLSetParameterPointer is just stupidly bugged on every level.
+        // Sigh.  This seems to work on both NVIDIA and AMD cards now.
+        if (cgGetParameterBaseResource(p) == CG_ATTR0) {
           int index = cgGetParameterResourceIndex(p);
           switch (index) {
           case 0:  // gl_Vertex

+ 0 - 1
panda/src/glstuff/glCgShaderContext_src.h

@@ -52,7 +52,6 @@ public:
 
 private:
   CGprogram _cg_program;
-  bool _glsl_profile;
 
   pvector<CGparameter> _cg_parameter_map;
 

+ 82 - 73
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -30,10 +30,10 @@ CLP(GraphicsBuffer)(GraphicsEngine *engine, GraphicsPipe *pipe,
   GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host),
   _bind_texture_pcollector(_draw_window_pcollector, "Bind textures"),
   _generate_mipmap_pcollector(_draw_window_pcollector, "Generate mipmaps"),
-  _resolve_multisample_pcollector(_draw_window_pcollector, "Resolve multisamples")
+  _resolve_multisample_pcollector(_draw_window_pcollector, "Resolve multisamples"),
+  _requested_multisamples(0),
+  _requested_coverage_samples(0)
 {
-  CLP(GraphicsStateGuardian) *glgsg;
-
   // A FBO doesn't have a back buffer.
   _draw_buffer_type       = RenderBuffer::T_front;
   _screenshot_buffer_type = RenderBuffer::T_front;
@@ -42,36 +42,6 @@ CLP(GraphicsBuffer)(GraphicsEngine *engine, GraphicsPipe *pipe,
   _fbo_multisample = 0;
   _initial_clear = true;
   _needs_rebuild = true;
-  DCAST_INTO_V(glgsg, _gsg);
-
-  if (glgsg->get_supports_framebuffer_multisample() && glgsg->get_supports_framebuffer_blit()) {
-    _requested_multisamples = fb_prop.get_multisamples();
-  } else {
-    _requested_multisamples = 0;
-  }
-
-  if (glgsg->get_supports_framebuffer_multisample_coverage_nv() && glgsg->get_supports_framebuffer_blit()) {
-    _requested_coverage_samples = fb_prop.get_coverage_samples();
-    // Note:  Only 4 and 8 actual samples are supported by the extension, with 8 or 16 coverage samples.
-    if ((_requested_coverage_samples <= 8) && (_requested_coverage_samples > 0)) {
-      _requested_multisamples = 4;
-      _requested_coverage_samples = 8;
-    } else if (_requested_coverage_samples > 8) {
-      if (_requested_multisamples < 8) {
-        _requested_multisamples = 4;
-      } else {
-        _requested_multisamples = 8;
-      }
-      _requested_coverage_samples = 16;
-    }
-
-  } else {
-    _requested_coverage_samples = 0;
-  }
-
-  if (_requested_multisamples > glgsg->_max_fb_samples) {
-    _requested_multisamples = glgsg->_max_fb_samples;
-  }
 
   _rb_size_x = 0;
   _rb_size_y = 0;
@@ -382,9 +352,9 @@ rebuild_bitplanes() {
     }
   }
 
-  // Decide whether we should use a depth stencil or just a regular depth attachment.
-  // If nothing was attached to either RTP_depth_stencil or RTP_depth, we use a
-  // depth-stencil renderbuffer.
+  // Decide whether we should use a depth stencil or just a regular depth
+  // attachment.  If nothing was attached to either RTP_depth_stencil or
+  // RTP_depth, we use a depth-stencil renderbuffer.
   _use_depth_stencil = false;
   if (_gsg->get_supports_depth_stencil()) {
     if (attach[RTP_depth_stencil]) {
@@ -396,19 +366,16 @@ rebuild_bitplanes() {
       // explicitly bound something to RTP_depth.
       _use_depth_stencil = false;
 
-    } else if (_fb_properties.get_float_depth()) {
-      // Let's not bother with a depth-stencil buffer
-      // if a float buffer was requested.
-      _use_depth_stencil = false;
-
-    } else if (_fb_properties.get_depth_bits() > 24) {
-      // We can't give more than 24 depth bits with a depth-stencil buffer.
-      _use_depth_stencil = false;
-
     } else if (_fb_properties.get_stencil_bits() > 0) {
       // The user requested stencil bits.  Let's take the hint.
       _use_depth_stencil = true;
 
+    } else if (_fb_properties.get_depth_bits() > 24 ||
+               _fb_properties.get_float_depth()) {
+      // 32-bit float depth is supported in conjunction with depth stencil,
+      // but it's a waste.  Let's not do it unless the user requested stencil.
+      _use_depth_stencil = false;
+
     } else if (_fb_properties.get_depth_bits() > 0) {
       // Let's use a depth stencil buffer by default, if a depth
       // buffer was requested.
@@ -416,10 +383,9 @@ rebuild_bitplanes() {
     }
   }
 
-  // Knowing this, we can already be a tiny bit
-  // more accurate about the framebuffer properties.
+  // Knowing this, we can already be a tiny bit more accurate about the
+  // framebuffer properties.
   if (_use_depth_stencil) {
-    _fb_properties.set_depth_bits(24);
     _fb_properties.set_stencil_bits(8);
   } else {
     _fb_properties.set_stencil_bits(0);
@@ -553,8 +519,7 @@ rebuild_bitplanes() {
 #endif  // OPENGLES
 
   if (!_have_any_color) {
-    _fb_properties.set_color_bits(0);
-    _fb_properties.set_alpha_bits(0);
+    _fb_properties.set_rgba_bits(0, 0, 0, 0);
   }
 
   _initial_clear = false;
@@ -699,8 +664,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
         glGetTexLevelParameteriv(target, 0, GL_TEXTURE_BLUE_SIZE, &blue_size);
         glGetTexLevelParameteriv(target, 0, GL_TEXTURE_ALPHA_SIZE, &alpha_size);
 
-        _fb_properties.set_color_bits(red_size + green_size + blue_size);
-        _fb_properties.set_alpha_bits(alpha_size);
+        _fb_properties.set_rgba_bits(red_size, green_size, blue_size, alpha_size);
       }
 #endif
     }
@@ -717,6 +681,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
     // No texture to bind.  Instead, create a renderbuffer.
     // Choose a suitable renderbuffer format based on the requirements.
 #ifdef OPENGLES
+    // OpenGL ES case.
     GLuint gl_format = GL_RGBA4;
     switch (slot) {
     case RTP_depth_stencil:
@@ -731,6 +696,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
         gl_format = GL_DEPTH_COMPONENT16;
       }
       break;
+    //NB: we currently use RTP_stencil to store the right eye for stereo.
     //case RTP_stencil:
     //  gl_format = GL_STENCIL_INDEX8;
     //  break
@@ -762,7 +728,8 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
     GLuint gl_format = GL_RGBA;
     switch (slot) {
       case RTP_depth_stencil:
-        if (_fb_properties.get_float_depth()) {
+        if (_fb_properties.get_depth_bits() > 24 ||
+            _fb_properties.get_float_depth()) {
           gl_format = GL_DEPTH32F_STENCIL8;
         } else {
           gl_format = GL_DEPTH24_STENCIL8;
@@ -775,7 +742,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
           gl_format = GL_DEPTH_COMPONENT32;
         } else if (_fb_properties.get_depth_bits() > 16) {
           gl_format = GL_DEPTH_COMPONENT24;
-        } else if (_fb_properties.get_depth_bits() > 8) {
+        } else if (_fb_properties.get_depth_bits() > 1) {
           gl_format = GL_DEPTH_COMPONENT16;
         } else {
           gl_format = GL_DEPTH_COMPONENT;
@@ -903,8 +870,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
         glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_GREEN_SIZE_EXT, &green_size);
         glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_BLUE_SIZE_EXT, &blue_size);
         glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_ALPHA_SIZE_EXT, &alpha_size);
-        _fb_properties.set_color_bits(red_size + green_size + blue_size);
-        _fb_properties.set_alpha_bits(alpha_size);
+        _fb_properties.set_rgba_bits(red_size, green_size, blue_size, alpha_size);
       }
       glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, 0);
       glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, attachpoint,
@@ -976,7 +942,6 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot,
             format = GL_DEPTH_COMPONENT32;
             break;
           default:
-            format = GL_DEPTH_COMPONENT;
             break;
         }
       }
@@ -1269,14 +1234,12 @@ open_buffer() {
 
   // Describe the framebuffer properties of the FBO.
   //
-  // Unfortunately, we can't possibly predict which formats
+  // Unfortunately, we can't currently predict which formats
   // the implementation will allow us to use at this point, so
   // we'll just have to make some guesses and parrot the rest
   // of the properties back to the user.
   // When we actually attach the textures, we'll update the
   // properties more appropriately.
-  // This is probably safe, as we can usually bind just about
-  // any supported texture format to the FBO.
 
   // Rounding the depth bits is not spectacular, but at least we're
   // telling the user *something* about what we're going to get.
@@ -1286,6 +1249,9 @@ open_buffer() {
   if (_fb_properties.get_color_bits() == 0 &&
       _fb_properties.get_rgb_color()) {
     _fb_properties.set_color_bits(1);
+    _fb_properties.set_red_bits(1);
+    _fb_properties.set_green_bits(1);
+    _fb_properties.set_blue_bits(1);
   }
 
   // Actually, let's always get a colour buffer for now until we
@@ -1294,18 +1260,28 @@ open_buffer() {
     _fb_properties.set_color_bits(1);
   }
 
-  if (_fb_properties.get_depth_bits() >= 32) {
+  if (_fb_properties.get_depth_bits() > 24) {
     _fb_properties.set_depth_bits(32);
   } else if (_fb_properties.get_depth_bits() > 16) {
     _fb_properties.set_depth_bits(24);
-  } else if (_fb_properties.get_depth_bits() > 8) {
+  } else {
     _fb_properties.set_depth_bits(16);
   }
 
-  // We're not going to get more than this, ever.
+  // We're not going to get more than this, ever.  At least not until OpenGL
+  // introduces 64-bit texture formats.
   if (_fb_properties.get_color_bits() > 96) {
     _fb_properties.set_color_bits(96);
   }
+  if (_fb_properties.get_red_bits() > 32) {
+    _fb_properties.set_red_bits(32);
+  }
+  if (_fb_properties.get_green_bits() > 32) {
+    _fb_properties.set_green_bits(32);
+  }
+  if (_fb_properties.get_blue_bits() > 32) {
+    _fb_properties.set_blue_bits(32);
+  }
   if (_fb_properties.get_alpha_bits() > 32) {
     _fb_properties.set_alpha_bits(32);
   }
@@ -1317,30 +1293,63 @@ open_buffer() {
 
   // We currently only support color formats this big as float.
   if (_fb_properties.get_color_bits() > 16 * 3) {
-    _fb_properties.set_color_bits(32 * 3);
     _fb_properties.set_float_color(true);
-
-    if (_fb_properties.get_alpha_bits() > 0) {
-      _fb_properties.set_alpha_bits(32);
-    }
   }
 
   if (_fb_properties.get_srgb_color()) {
-    _fb_properties.set_color_bits(24);
+    // This is the only sRGB color format OpenGL supports.
+    _fb_properties.set_rgba_bits(8, 8, 8,
+      (_fb_properties.get_alpha_bits() > 0) ? 8 : 0);
     _fb_properties.set_float_color(false);
-
-    if (_fb_properties.get_alpha_bits() > 0) {
-      _fb_properties.set_alpha_bits(32);
-    }
   }
 
   if (!_gsg->get_supports_depth_stencil()) {
     // At least we know we won't be getting stencil bits.
     _fb_properties.set_stencil_bits(0);
+
+  } else if (_fb_properties.get_stencil_bits() > 0) {
+    // We don't currently support stencil-only targets.
+    _fb_properties.set_stencil_bits(8);
+    if (_fb_properties.get_depth_bits() < 24) {
+      _fb_properties.set_depth_bits(24);
+    }
   }
+
+  // Accumulation buffers aren't supported for FBOs.
   _fb_properties.set_accum_bits(0);
 
+  if (glgsg->get_supports_framebuffer_multisample() && glgsg->get_supports_framebuffer_blit()) {
+    _requested_multisamples = _fb_properties.get_multisamples();
+  } else {
+    _requested_multisamples = 0;
+  }
+
+#ifndef OPENGLES
+  if (glgsg->get_supports_framebuffer_multisample_coverage_nv() && glgsg->get_supports_framebuffer_blit()) {
+    _requested_coverage_samples = _fb_properties.get_coverage_samples();
+    // Note:  Only 4 and 8 actual samples are supported by the extension, with 8 or 16 coverage samples.
+    if ((_requested_coverage_samples <= 8) && (_requested_coverage_samples > 0)) {
+      _requested_multisamples = 4;
+      _requested_coverage_samples = 8;
+    } else if (_requested_coverage_samples > 8) {
+      if (_requested_multisamples < 8) {
+        _requested_multisamples = 4;
+      } else {
+        _requested_multisamples = 8;
+      }
+      _requested_coverage_samples = 16;
+    }
+
+  } else {
+    _requested_coverage_samples = 0;
+  }
+#endif
+
+  if (_requested_multisamples > glgsg->_max_fb_samples) {
+    _requested_multisamples = glgsg->_max_fb_samples;
+  }
   _fb_properties.set_multisamples(_requested_multisamples);
+  _fb_properties.set_coverage_samples(_requested_coverage_samples);
 
   // Update aux settings to reflect the GL_MAX_DRAW_BUFFERS limit,
   // if we exceed it, that is.

+ 8 - 0
panda/src/glstuff/glShaderContext_src.cxx

@@ -1069,13 +1069,21 @@ issue_parameters(int altered) {
       case Shader::SMP_row3x3: _glgsg->_glUniform3fv(p, 1, data+12); continue;
       case Shader::SMP_upper3x3:
         {
+#ifndef STDFLOAT_DOUBLE
           LMatrix3f upper3 = val->get_upper_3();
+#else
+          LMatrix3f upper3 = valf.get_upper_3();
+#endif
           _glgsg->_glUniformMatrix3fv(p, 1, false, upper3.get_data());
           continue;
         }
       case Shader::SMP_transpose3x3:
         {
+#ifndef STDFLOAT_DOUBLE
           LMatrix3f upper3 = val->get_upper_3();
+#else
+          LMatrix3f upper3 = valf.get_upper_3();
+#endif
           _glgsg->_glUniformMatrix3fv(p, 1, true, upper3.get_data());
           continue;
         }

+ 15 - 22
panda/src/glxdisplay/glxGraphicsPipe.cxx

@@ -136,35 +136,28 @@ make_output(const string &name,
   // Second thing to try: a GLGraphicsBuffer
 
   if (retry == 1) {
-    if ((host==0)||
-        (!gl_support_fbo)||
-        ((flags&BF_require_parasite)!=0)||
-        ((flags&BF_require_window)!=0)) {
+    if (!gl_support_fbo || host == NULL ||
+        (flags & (BF_require_parasite | BF_require_window)) != 0) {
       return NULL;
     }
     // Early failure - if we are sure that this buffer WONT
     // meet specs, we can bail out early.
-    int _fbo_multisample = 0;
-    if (!ConfigVariableBool("framebuffer-object-multisample", false, PRC_DESC("Enabled Multisample."))) {
-      _fbo_multisample = 16;
-    }
-    if ((flags & BF_fb_props_optional)==0) {
-      if ((fb_prop.get_indexed_color() > 0)||
-          (fb_prop.get_back_buffers() > 0)||
-          (fb_prop.get_accum_bits() > 0)||
-          (fb_prop.get_multisamples() > _fbo_multisample)) {
+    if ((flags & BF_fb_props_optional) == 0) {
+      if (fb_prop.get_indexed_color() ||
+          fb_prop.get_back_buffers() > 0 ||
+          fb_prop.get_accum_bits() > 0) {
         return NULL;
       }
     }
-    // Early success - if we are sure that this buffer WILL
-    // meet specs, we can precertify it.
-    if ((posixgsg != 0) &&
-        (posixgsg->is_valid()) &&
-        (!posixgsg->needs_reset()) &&
-        (posixgsg->_supports_framebuffer_object) &&
-        (posixgsg->_glDrawBuffers != 0)&&
-        (fb_prop.is_basic())) {
-      precertify = true;
+    if (posixgsg != NULL && posixgsg->is_valid() && !posixgsg->needs_reset()) {
+      if (!posixgsg->_supports_framebuffer_object ||
+          posixgsg->_glDrawBuffers == NULL) {
+        return NULL;
+      } else {
+        // Early success - if we are sure that this buffer WILL
+        // meet specs, we can precertify it.
+        precertify = true;
+      }
     }
     return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop,
                                 flags, gsg, host);

+ 77 - 72
panda/src/gobj/texture.cxx

@@ -41,6 +41,7 @@
 #include "pbitops.h"
 #include "streamReader.h"
 #include "texturePeeker.h"
+#include "convert_srgb.h"
 
 #ifdef HAVE_SQUISH
 #include <squish.h>
@@ -131,46 +132,6 @@ struct DDSHeader {
   DDSCaps2 caps;
 };
 
-// This table is used for converting unsigned char texture values in an sRGB
-// texture to linear RGB values, for use in mipmap generation.
-static float srgb_to_lrgbf[256] = {0.000000f, 0.000304f, 0.000607f, 0.000911f,
-  0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f,
-  0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f,
-  0.006049f, 0.006512f, 0.006995f, 0.007499f, 0.008023f, 0.008568f, 0.009134f,
-  0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f,
-  0.014444f, 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f,
-  0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, 0.025187f, 0.026241f,
-  0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f,
-  0.035601f, 0.036889f, 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f,
-  0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, 0.054480f,
-  0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f,
-  0.068478f, 0.070360f, 0.072272f, 0.074214f, 0.076185f, 0.078187f, 0.080220f,
-  0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f,
-  0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f,
-  0.114435f, 0.116971f, 0.119538f, 0.122139f, 0.124772f, 0.127438f, 0.130136f,
-  0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f,
-  0.152926f, 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f,
-  0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, 0.191202f, 0.194618f,
-  0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f,
-  0.223228f, 0.226966f, 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f,
-  0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, 0.274677f,
-  0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f,
-  0.309469f, 0.313989f, 0.318547f, 0.323143f, 0.327778f, 0.332452f, 0.337164f,
-  0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f,
-  0.376262f, 0.381326f, 0.386429f, 0.391572f, 0.396755f, 0.401978f, 0.407240f,
-  0.412543f, 0.417885f, 0.423268f, 0.428690f, 0.434154f, 0.439657f, 0.445201f,
-  0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473531f, 0.479320f, 0.485150f,
-  0.491021f, 0.496933f, 0.502886f, 0.508881f, 0.514918f, 0.520996f, 0.527115f,
-  0.533276f, 0.539479f, 0.545724f, 0.552011f, 0.558340f, 0.564712f, 0.571125f,
-  0.577580f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f,
-  0.623960f, 0.630757f, 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f,
-  0.672443f, 0.679542f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, 0.715694f,
-  0.723055f, 0.730461f, 0.737910f, 0.745404f, 0.752942f, 0.760525f, 0.768151f,
-  0.775822f, 0.783538f, 0.791298f, 0.799103f, 0.806952f, 0.814847f, 0.822786f,
-  0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f,
-  0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f,
-  0.947307f, 0.955973f, 0.964686f, 0.973445f, 0.982251f, 0.991102f, 1.000000f};
-
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::Constructor
 //       Access: Published
@@ -4436,7 +4397,7 @@ do_get_clear_data(const CData *cdata, unsigned char *into) const {
   switch (cdata->_component_type) {
   case T_unsigned_byte:
     {
-      LColorf scaled = cdata->_clear_color.fmin(LColorf(1)).fmax(LColorf::zero());
+      LColor scaled = cdata->_clear_color.fmin(LColor(1)).fmax(LColor::zero());
       scaled *= 255;
       switch (cdata->_num_components) {
       case 2:
@@ -4457,7 +4418,7 @@ do_get_clear_data(const CData *cdata, unsigned char *into) const {
 
   case T_unsigned_short:
     {
-      LColorf scaled = cdata->_clear_color.fmin(LColorf(1)).fmax(LColorf::zero());
+      LColor scaled = cdata->_clear_color.fmin(LColor(1)).fmax(LColor::zero());
       scaled *= 65535;
       switch (cdata->_num_components) {
       case 2:
@@ -6990,7 +6951,13 @@ do_filter_2d_mipmap_pages(const CData *cdata,
     // We currently only support sRGB mipmap generation for
     // unsigned byte textures, due to our use of a lookup table.
     nassertv(cdata->_component_type == T_unsigned_byte);
-    filter_component = &filter_2d_unsigned_byte_srgb;
+
+    if (has_sse2_sRGB_encode()) {
+      filter_component = &filter_2d_unsigned_byte_srgb_sse2;
+    } else {
+      filter_component = &filter_2d_unsigned_byte_srgb;
+    }
+
     // Alpha is always linear.
     filter_alpha = &filter_2d_unsigned_byte;
 
@@ -7140,7 +7107,13 @@ do_filter_3d_mipmap_level(const CData *cdata,
     // We currently only support sRGB mipmap generation for
     // unsigned byte textures, due to our use of a lookup table.
     nassertv(cdata->_component_type == T_unsigned_byte);
-    filter_component = &filter_3d_unsigned_byte_srgb;
+
+    if (has_sse2_sRGB_encode()) {
+      filter_component = &filter_3d_unsigned_byte_srgb_sse2;
+    } else {
+      filter_component = &filter_3d_unsigned_byte_srgb;
+    }
+
     // Alpha is always linear.
     filter_alpha = &filter_3d_unsigned_byte;
 
@@ -7385,18 +7358,32 @@ filter_2d_unsigned_byte(unsigned char *&p, const unsigned char *&q,
 void Texture::
 filter_2d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q,
                              size_t pixel_size, size_t row_size) {
-  float result = (srgb_to_lrgbf[q[0]] +
-                  srgb_to_lrgbf[q[pixel_size]] +
-                  srgb_to_lrgbf[q[row_size]] +
-                  srgb_to_lrgbf[q[pixel_size + row_size]]) / 4.0f;
-
-  // This is based on the formula out of the EXT_texture_sRGB
-  // specification, except the factors are multiplied with 255.0f.
-  if (result < 0.0031308f) {
-    *p = (unsigned char)(result * 3294.6f);
-  } else {
-    *p = (unsigned char)(269.025f * powf(result, 0.41666f) - 14.025f);
-  }
+  float result = (decode_sRGB_float(q[0]) +
+                  decode_sRGB_float(q[pixel_size]) +
+                  decode_sRGB_float(q[row_size]) +
+                  decode_sRGB_float(q[pixel_size + row_size]));
+
+  *p = encode_sRGB_uchar(result * 0.25f);
+  ++p;
+  ++q;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::filter_2d_unsigned_byte_srgb_sse2
+//       Access: Public, Static
+//  Description: Averages a 2x2 block of pixel components into a
+//               single pixel component, for producing the next mipmap
+//               level.  Increments p and q to the next component.
+////////////////////////////////////////////////////////////////////
+void Texture::
+filter_2d_unsigned_byte_srgb_sse2(unsigned char *&p, const unsigned char *&q,
+                                  size_t pixel_size, size_t row_size) {
+  float result = (decode_sRGB_float(q[0]) +
+                  decode_sRGB_float(q[pixel_size]) +
+                  decode_sRGB_float(q[row_size]) +
+                  decode_sRGB_float(q[pixel_size + row_size]));
+
+  *p = encode_sRGB_uchar_sse2(result * 0.25f);
   ++p;
   ++q;
 }
@@ -7470,22 +7457,40 @@ filter_3d_unsigned_byte(unsigned char *&p, const unsigned char *&q,
 void Texture::
 filter_3d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q,
                              size_t pixel_size, size_t row_size, size_t page_size) {
-  float result = (srgb_to_lrgbf[q[0]] +
-                  srgb_to_lrgbf[q[pixel_size]] +
-                  srgb_to_lrgbf[q[row_size]] +
-                  srgb_to_lrgbf[q[pixel_size + row_size]] +
-                  srgb_to_lrgbf[q[page_size]] +
-                  srgb_to_lrgbf[q[pixel_size + page_size]] +
-                  srgb_to_lrgbf[q[row_size + page_size]] +
-                  srgb_to_lrgbf[q[pixel_size + row_size + page_size]]) / 8.0f;
-
-  // This is based on the formula out of the EXT_texture_sRGB
-  // specification, except the factors are multiplied with 255.0f.
-  if (result < 0.0031308f) {
-    *p = (unsigned char)(result * 3294.6f);
-  } else {
-    *p = (unsigned char)(269.025f * powf(result, 0.41666f) - 14.025f);
-  }
+  float result = (decode_sRGB_float(q[0]) +
+                  decode_sRGB_float(q[pixel_size]) +
+                  decode_sRGB_float(q[row_size]) +
+                  decode_sRGB_float(q[pixel_size + row_size]) +
+                  decode_sRGB_float(q[page_size]) +
+                  decode_sRGB_float(q[pixel_size + page_size]) +
+                  decode_sRGB_float(q[row_size + page_size]) +
+                  decode_sRGB_float(q[pixel_size + row_size + page_size]));
+
+  *p = encode_sRGB_uchar(result * 0.125f);
+  ++p;
+  ++q;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::filter_3d_unsigned_byte_srgb_sse2
+//       Access: Public, Static
+//  Description: Averages a 2x2x2 block of pixel components into a
+//               single pixel component, for producing the next mipmap
+//               level.  Increments p and q to the next component.
+////////////////////////////////////////////////////////////////////
+void Texture::
+filter_3d_unsigned_byte_srgb_sse2(unsigned char *&p, const unsigned char *&q,
+                                  size_t pixel_size, size_t row_size, size_t page_size) {
+  float result = (decode_sRGB_float(q[0]) +
+                  decode_sRGB_float(q[pixel_size]) +
+                  decode_sRGB_float(q[row_size]) +
+                  decode_sRGB_float(q[pixel_size + row_size]) +
+                  decode_sRGB_float(q[page_size]) +
+                  decode_sRGB_float(q[pixel_size + page_size]) +
+                  decode_sRGB_float(q[row_size + page_size]) +
+                  decode_sRGB_float(q[pixel_size + row_size + page_size]));
+
+  *p = encode_sRGB_uchar_sse2(result * 0.125f);
   ++p;
   ++q;
 }

+ 7 - 0
panda/src/gobj/texture.h

@@ -752,6 +752,9 @@ private:
   static void filter_2d_unsigned_byte_srgb(unsigned char *&p,
                                            const unsigned char *&q,
                                            size_t pixel_size, size_t row_size);
+  static void filter_2d_unsigned_byte_srgb_sse2(unsigned char *&p,
+                                                const unsigned char *&q,
+                                                size_t pixel_size, size_t row_size);
   static void filter_2d_unsigned_short(unsigned char *&p,
                                        const unsigned char *&q,
                                        size_t pixel_size, size_t row_size);
@@ -766,6 +769,10 @@ private:
                                            const unsigned char *&q,
                                            size_t pixel_size, size_t row_size,
                                            size_t page_size);
+  static void filter_3d_unsigned_byte_srgb_sse2(unsigned char *&p,
+                                                const unsigned char *&q,
+                                                size_t pixel_size, size_t row_size,
+                                                size_t page_size);
   static void filter_3d_unsigned_short(unsigned char *&p,
                                        const unsigned char *&q,
                                        size_t pixel_size, size_t row_size,

+ 8 - 8
panda/src/grutil/pfmVizzer.cxx

@@ -57,11 +57,11 @@ void PfmVizzer::
 project(const Lens *lens, const PfmFile *undist_lut) {
   nassertv(_pfm.is_valid());
 
-  static LMatrix4f to_uv(0.5f, 0.0f, 0.0f, 0.0f,
-                         0.0f, 0.5f, 0.0f, 0.0f, 
-                         0.0f, 0.0f, 0.5f, 0.0f, 
-                         0.5f, 0.5f, 0.5f, 1.0f);
-  
+  static LMatrix4 to_uv(0.5f, 0.0f, 0.0f, 0.0f,
+                        0.0f, 0.5f, 0.0f, 0.0f,
+                        0.0f, 0.0f, 0.5f, 0.0f,
+                        0.5f, 0.5f, 0.5f, 1.0f);
+
   for (int yi = 0; yi < _pfm.get_y_size(); ++yi) {
     for (int xi = 0; xi < _pfm.get_x_size(); ++xi) {
       if (!_pfm.has_point(xi, yi)) {
@@ -79,8 +79,8 @@ project(const Lens *lens, const PfmFile *undist_lut) {
       } else {
         // Now the lens gives us coordinates in the range [-1, 1].
         // Rescale these to [0, 1].
-        LPoint3f uvw = film * to_uv;
-        
+        LPoint3f uvw = LCAST(float, film * to_uv);
+
         if (undist_lut != NULL) {
           // Apply the undistortion map, if given.
           LPoint3f p2;
@@ -88,7 +88,7 @@ project(const Lens *lens, const PfmFile *undist_lut) {
           uvw = p2;
           uvw[1] = 1.0 - uvw[1];
         }
-        
+
         p = uvw;
       }
     }

+ 2 - 0
panda/src/ode/odeRayGeom.I

@@ -11,6 +11,8 @@
 // with this source code in a file named "LICENSE."
 //
 ////////////////////////////////////////////////////////////////////
+
+
 INLINE void OdeRayGeom::
 set_length(dReal length) {
   dGeomRaySetLength(_id, length);

+ 2 - 0
panda/src/ode/odeTriMeshData.h

@@ -62,6 +62,7 @@ PUBLISHED:
   // INLINE void get_buffer(unsigned char** buf, int* buf_len) const;
   // INLINE void set_buffer(unsigned char* buf);
   // INLINE void update();
+
   virtual void write(ostream &out = cout, unsigned int indent=0) const;
   void write_faces(ostream &out) const;
 
@@ -90,6 +91,7 @@ public:
                             const int* indices, int index_count, \
                             const int* normals);
   */
+
   INLINE void preprocess();
 
   INLINE dTriMeshDataID get_id() const;

+ 17 - 22
panda/src/osxdisplay/osxGraphicsPipe.cxx

@@ -412,37 +412,32 @@ make_output(const string &name,
     return new osxGraphicsWindow(engine, this, name, fb_prop, win_prop,
                                  flags, gsg, host);
   }
-  
+
   // Second thing to try: a GLGraphicsBuffer
-  
+
   if (retry == 1) {
-    if (!osx_support_gl_buffer) {
-      return NULL;
-    }
-    if ((host==0)||
-        ((flags&BF_require_parasite)!=0)||
-        ((flags&BF_require_window)!=0)) {
+    if (!osx_support_gl_buffer || !gl_support_fbo || host == NULL ||
+        (flags & (BF_require_parasite | BF_require_window)) != 0) {
       return NULL;
     }
     // Early failure - if we are sure that this buffer WONT
     // meet specs, we can bail out early.
-    if ((flags & BF_fb_props_optional)==0) {
-      if ((fb_prop.get_indexed_color() > 0)||
-          (fb_prop.get_back_buffers() > 0)||
-          (fb_prop.get_accum_bits() > 0)||
-          (fb_prop.get_multisamples() > 0)) {
+    if ((flags & BF_fb_props_optional) == 0) {
+      if (fb_prop.get_indexed_color() ||
+          fb_prop.get_back_buffers() > 0 ||
+          fb_prop.get_accum_bits() > 0) {
         return NULL;
       }
     }
-    // Early success - if we are sure that this buffer WILL
-    // meet specs, we can precertify it.
-    if ((osxgsg != 0) &&
-        (osxgsg->is_valid()) &&
-        (!osxgsg->needs_reset()) &&
-        (osxgsg->_supports_framebuffer_object) &&
-        (osxgsg->_glDrawBuffers != 0)&&
-        (fb_prop.is_basic())) {
-      precertify = true;
+    if (posixgsg != NULL && posixgsg->is_valid() && !posixgsg->needs_reset()) {
+      if (!posixgsg->_supports_framebuffer_object ||
+          posixgsg->_glDrawBuffers == NULL) {
+        return NULL;
+      } else {
+        // Early success - if we are sure that this buffer WILL
+        // meet specs, we can precertify it.
+        precertify = true;
+      }
     }
     return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop, flags, gsg, host);
   }

+ 1 - 1
panda/src/pgraph/cullBinManager.h

@@ -99,7 +99,7 @@ private:
   class EXPCL_PANDA_PGRAPH BinDefinition {
   public:
 #ifndef NDEBUG
-    LColorf _flash_color;
+    LColor _flash_color;
     bool _flash_active;
 #endif
     bool _in_use;

+ 7 - 5
panda/src/pgraph/pandaNode.I

@@ -721,13 +721,15 @@ get_user_bounds(int pipeline_stage, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
 mark_bounds_stale(int pipeline_stage, Thread *current_thread) const {
-  // It's important that we don't hold the lock during the call to
-  // force_bounds_stale().
+  // We check whether it is already marked stale.  If so, we don't have
+  // to make the call to force_bounds_stale().
   bool is_stale_bounds;
   {
     CDStageReader cdata(_cycler, pipeline_stage, current_thread);
-    is_stale_bounds = (cdata->_last_bounds_update != cdata->_next_update);
+    is_stale_bounds = (cdata->_last_update != cdata->_next_update);
   }
+  // It's important that we don't hold the lock during the call to
+  // force_bounds_stale().
   if (!is_stale_bounds) {
     ((PandaNode *)this)->force_bounds_stale(pipeline_stage, current_thread);
   }
@@ -1833,7 +1835,7 @@ get_off_clip_planes() const {
 ////////////////////////////////////////////////////////////////////
 INLINE CPT(BoundingVolume) PandaNodePipelineReader::
 get_bounds() const {
-  nassertr(_cdata->_last_update == _cdata->_next_update, _cdata->_external_bounds);
+  nassertr(_cdata->_last_bounds_update == _cdata->_next_update, _cdata->_external_bounds);
   return _cdata->_external_bounds;
 }
 
@@ -1852,7 +1854,7 @@ get_bounds() const {
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNodePipelineReader::
 get_nested_vertices() const {
-  nassertr(_cdata->_last_update == _cdata->_next_update, _cdata->_nested_vertices);
+  nassertr(_cdata->_last_bounds_update == _cdata->_next_update, _cdata->_nested_vertices);
   return _cdata->_nested_vertices;
 }
 

+ 175 - 0
panda/src/pnmimage/convert_srgb.I

@@ -0,0 +1,175 @@
+// Filename: convert_srgb.I
+// Created by:  rdb (29Oct14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: decode_sRGB_float
+//  Description: Decodes the sRGB-encoded unsigned char value to
+//               a linearized float in the range 0-1.
+////////////////////////////////////////////////////////////////////
+CONSTEXPR float decode_sRGB_float(unsigned char val) {
+  return to_linear_float_table[val];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: encode_sRGB_float
+//  Description: Decodes the sRGB-encoded floating-point value in
+//               the range 0-1 to a linearized float in the range
+//               0-1.  Inputs outside this range produce invalid
+//               results.
+////////////////////////////////////////////////////////////////////
+INLINE float decode_sRGB_float(float val) {
+  return (val <= 0.04045f)
+    ? (val * (1.f / 12.92f))
+    : cpow((val + 0.055f) * (1.f / 1.055f), 2.4f);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: decode_sRGB_uchar
+//  Description: Decodes the sRGB-encoded unsigned char value to
+//               a linearized unsigned char value.
+////////////////////////////////////////////////////////////////////
+CONSTEXPR unsigned char decode_sRGB_uchar(unsigned char val) {
+  return to_linear_uchar_table[val];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: decode_sRGB_uchar
+//  Description: Decodes the sRGB-encoded floating-point value in
+//               the range 0-1 to a linearized unsigned char value.
+//               Inputs outside this range are clamped.
+////////////////////////////////////////////////////////////////////
+INLINE unsigned char decode_sRGB_uchar(float val) {
+  return (val <= 0.04045f)
+    ? (unsigned char)(max(0.f, val) * (255.f / 12.92f) + 0.5f)
+    : (unsigned char)(cpow((min(val, 1.f) + 0.055f) * (1.f / 1.055f), 2.4f) * 255.f + 0.5f);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: encode_sRGB_float
+//  Description: Encodes the linearized unsigned char value to an
+//               sRGB-encoded floating-point value in ther range 0-1.
+////////////////////////////////////////////////////////////////////
+INLINE float
+encode_sRGB_float(unsigned char val) {
+  // This seems like a very unlikely use case, so I didn't bother
+  // making a look-up table for this.
+  return (val == 0) ? 0
+    : (1.055f * cpow((float)val * (1.f / 255.f), 0.41666f) - 0.055);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: encode_sRGB_float
+//  Description: Encodes the linearized floating-point value in the
+//               range 0-1 to an sRGB-encoded float in the range
+//               0-1.  Inputs outside this range produce invalid
+//               results.
+////////////////////////////////////////////////////////////////////
+INLINE float
+encode_sRGB_float(float val) {
+  return (val < 0.0031308f)
+    ? (val * 12.92f)
+    : (1.055f * cpow(val, 0.41666f) - 0.055);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: encode_sRGB_uchar
+//  Description: Encodes the linearized unsigned char value to an
+//               sRGB-encoded unsigned char value.
+////////////////////////////////////////////////////////////////////
+CONSTEXPR unsigned char
+encode_sRGB_uchar(unsigned char val) {
+  return to_srgb8_table[val];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: encode_sRGB_uchar
+//  Description: Encodes the linearized floating-point value in the
+//               range 0-1 to an sRGB-encoded unsigned char value.
+//               Inputs outside this range are clamped.
+//
+//               When SSE2 support is known at compile time, this
+//               automatically uses an optimized version.  Otherwise,
+//               it does not attempt runtime CPU detection.  If you
+//               know that SSE2 is supported (ie. if the function
+//               has_sse2_sRGB_encode() returns true) you should
+//               call encode_sRGB_uchar_sse2 instead.
+////////////////////////////////////////////////////////////////////
+INLINE unsigned char
+encode_sRGB_uchar(float val) {
+#if defined(__SSE2__) || (_M_IX86_FP >= 2) || defined(_M_X64) || defined(_M_AMD64)
+  // Use a highly optimized approximation that has more than enough
+  // accuracy for an unsigned char.
+  return encode_sRGB_uchar_sse2(val);
+#else
+  return (val < 0.0031308f)
+    ? (unsigned char) (max(0.f, val) * 3294.6f + 0.5f)
+    : (unsigned char) (269.025f * cpow(min(val, 1.f), 0.41666f) - 13.525f);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: encode_sRGB_uchar
+//  Description: Encodes the linearized floating-point color value
+//               an sRGB-encoded xel in the range 0-255.
+//
+//               When SSE2 support is known at compile time, this
+//               automatically uses an optimized version.  Otherwise,
+//               it does not attempt runtime CPU detection.  If you
+//               know that SSE2 is supported (ie. if the function
+//               has_sse2_sRGB_encode() returns true) you should
+//               call encode_sRGB_uchar_sse2 instead.
+////////////////////////////////////////////////////////////////////
+INLINE void
+encode_sRGB_uchar(const LColorf &color, xel &into) {
+#if defined(__SSE2__) || (_M_IX86_FP >= 2) || defined(_M_X64) || defined(_M_AMD64)
+  // SSE2 support compiled-in; we're guaranteed to have it.
+  encode_sRGB_uchar_sse2(color, into);
+#else
+  // Boring, slow, non-SSE2 version.
+  PPM_ASSIGN(into,
+    encode_sRGB_uchar(color[0]),
+    encode_sRGB_uchar(color[1]),
+    encode_sRGB_uchar(color[2]));
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: encode_sRGB_uchar
+//  Description: Encodes the linearized floating-point color value
+//               an sRGB-encoded xel and alpha in the range 0-255.
+//               The alpha value is not sRGB-encoded.
+//
+//               When SSE2 support is known at compile time, this
+//               automatically uses an optimized version.  Otherwise,
+//               it does not attempt runtime CPU detection.  If you
+//               know that SSE2 is supported (ie. if the function
+//               has_sse2_sRGB_encode() returns true) you should
+//               call encode_sRGB_uchar_sse2 instead.
+////////////////////////////////////////////////////////////////////
+INLINE void
+encode_sRGB_uchar(const LColorf &color, xel &into, xelval &into_alpha) {
+#if defined(__SSE2__) || (_M_IX86_FP >= 2) || defined(_M_X64) || defined(_M_AMD64)
+  // SSE2 support compiled-in; we're guaranteed to have it.
+  encode_sRGB_uchar_sse2(color, into, into_alpha);
+#else
+  // Boring, slow, non-SSE2 version.
+  PPM_ASSIGN(into,
+    encode_sRGB_uchar(color[0]),
+    encode_sRGB_uchar(color[1]),
+    encode_sRGB_uchar(color[2]));
+
+  into_alpha = (xelval) (color[3] * 255.f + 0.5f);
+#endif
+}

+ 165 - 0
panda/src/pnmimage/convert_srgb.cxx

@@ -0,0 +1,165 @@
+// Filename: convert_srgb.cxx
+// Created by:  rdb (13Nov14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "convert_srgb.h"
+
+#ifdef __GNUC__
+#include <cpuid.h>
+#endif
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#include <windows.h>
+#endif
+
+// Lookup tables for converting from unsigned char formats.
+ALIGN_64BYTE const
+unsigned char to_srgb8_table[256] = { 0x00, 0x0d, 0x16, 0x1c, 0x22, 0x26, 0x2a,
+  0x2e, 0x32, 0x35, 0x38, 0x3b, 0x3d, 0x40, 0x42, 0x45, 0x47, 0x49, 0x4b, 0x4d,
+  0x4f, 0x51, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5d, 0x5f, 0x60, 0x62, 0x63,
+  0x65, 0x66, 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, 0x70, 0x71, 0x72, 0x73, 0x75,
+  0x76, 0x77, 0x78, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+  0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90,
+  0x91, 0x92, 0x93, 0x94, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b,
+  0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa3, 0xa4, 0xa5, 0xa6,
+  0xa7, 0xa7, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac, 0xad, 0xad, 0xae, 0xaf, 0xaf,
+  0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb4, 0xb4, 0xb5, 0xb6, 0xb6, 0xb7, 0xb8, 0xb9,
+  0xb9, 0xba, 0xbb, 0xbb, 0xbc, 0xbd, 0xbd, 0xbe, 0xbe, 0xbf, 0xc0, 0xc0, 0xc1,
+  0xc2, 0xc2, 0xc3, 0xc4, 0xc4, 0xc5, 0xc5, 0xc6, 0xc7, 0xc7, 0xc8, 0xc8, 0xc9,
+  0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf, 0xd0, 0xd0, 0xd1,
+  0xd1, 0xd2, 0xd2, 0xd3, 0xd4, 0xd4, 0xd5, 0xd5, 0xd6, 0xd6, 0xd7, 0xd7, 0xd8,
+  0xd8, 0xd9, 0xda, 0xda, 0xdb, 0xdb, 0xdc, 0xdc, 0xdd, 0xdd, 0xde, 0xde, 0xdf,
+  0xdf, 0xe0, 0xe0, 0xe1, 0xe2, 0xe2, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe5, 0xe6,
+  0xe6, 0xe7, 0xe7, 0xe8, 0xe8, 0xe9, 0xe9, 0xea, 0xea, 0xeb, 0xeb, 0xec, 0xec,
+  0xed, 0xed, 0xee, 0xee, 0xee, 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2,
+  0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf8, 0xf8,
+  0xf9, 0xf9, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe,
+  0xff, 0xff};
+
+ALIGN_64BYTE const
+unsigned char to_linear_uchar_table[256] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+  0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04,
+  0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07,
+  0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0b,
+  0x0b, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x10,
+  0x11, 0x11, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+  0x17, 0x18, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e,
+  0x1f, 0x20, 0x20, 0x21, 0x22, 0x23, 0x23, 0x24, 0x25, 0x25, 0x26, 0x27, 0x28,
+  0x29, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
+  0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+  0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4c, 0x4d,
+  0x4e, 0x4f, 0x50, 0x51, 0x52, 0x54, 0x55, 0x56, 0x57, 0x58, 0x5a, 0x5b, 0x5c,
+  0x5d, 0x5f, 0x60, 0x61, 0x63, 0x64, 0x65, 0x67, 0x68, 0x69, 0x6b, 0x6c, 0x6d,
+  0x6f, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7f, 0x80,
+  0x82, 0x83, 0x85, 0x86, 0x88, 0x8a, 0x8b, 0x8d, 0x8e, 0x90, 0x92, 0x93, 0x95,
+  0x97, 0x98, 0x9a, 0x9c, 0x9d, 0x9f, 0xa1, 0xa3, 0xa4, 0xa6, 0xa8, 0xaa, 0xab,
+  0xad, 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb8, 0xba, 0xbc, 0xbe, 0xc0, 0xc2, 0xc4,
+  0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
+  0xe0, 0xe2, 0xe5, 0xe7, 0xe9, 0xeb, 0xed, 0xef, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa,
+  0xfd, 0xff};
+
+ALIGN_64BYTE
+const float to_linear_float_table[256] = { 0, 0.000304f, 0.000607f, 0.000911f,
+  0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f,
+  0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f,
+  0.006049f, 0.006512f, 0.006995f, 0.007499f, 0.008023f, 0.008568f, 0.009134f,
+  0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f,
+  0.014444f, 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f,
+  0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, 0.025187f, 0.026241f,
+  0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f,
+  0.035601f, 0.036889f, 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f,
+  0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, 0.054480f,
+  0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f,
+  0.068478f, 0.070360f, 0.072272f, 0.074214f, 0.076185f, 0.078187f, 0.080220f,
+  0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f,
+  0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f,
+  0.114435f, 0.116971f, 0.119538f, 0.122139f, 0.124772f, 0.127438f, 0.130136f,
+  0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f,
+  0.152926f, 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f,
+  0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, 0.191202f, 0.194618f,
+  0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f,
+  0.223228f, 0.226966f, 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f,
+  0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, 0.274677f,
+  0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f,
+  0.309469f, 0.313989f, 0.318547f, 0.323143f, 0.327778f, 0.332452f, 0.337164f,
+  0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f,
+  0.376262f, 0.381326f, 0.386429f, 0.391572f, 0.396755f, 0.401978f, 0.407240f,
+  0.412543f, 0.417885f, 0.423268f, 0.428690f, 0.434154f, 0.439657f, 0.445201f,
+  0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473531f, 0.479320f, 0.485150f,
+  0.491021f, 0.496933f, 0.502886f, 0.508881f, 0.514918f, 0.520996f, 0.527115f,
+  0.533276f, 0.539479f, 0.545724f, 0.552011f, 0.558340f, 0.564712f, 0.571125f,
+  0.577580f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f,
+  0.623960f, 0.630757f, 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f,
+  0.672443f, 0.679542f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, 0.715694f,
+  0.723055f, 0.730461f, 0.737910f, 0.745404f, 0.752942f, 0.760525f, 0.768151f,
+  0.775822f, 0.783538f, 0.791298f, 0.799103f, 0.806952f, 0.814847f, 0.822786f,
+  0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f,
+  0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f,
+  0.947307f, 0.955973f, 0.964686f, 0.973445f, 0.982251f, 0.991102f, 1.000000f};
+
+
+#if defined(__SSE2__) || (_M_IX86_FP >= 2) || defined(_M_X64) || defined(_M_AMD64)
+// SSE2 support enabled at compile time.  No runtime detection mechanism needed.
+bool
+has_sse2_sRGB_encode() {
+  return true;
+}
+
+#else
+// SSE2 support not guaranteed.  Use a runtime detection mechanism.
+
+bool
+has_sse2_sRGB_encode() {
+#if defined(__GNUC__)
+  unsigned int a, b, c, d;
+  static const bool has_support =
+    (__get_cpuid(1, &a, &b, &c, &d) == 1 && (d & 0x04000000) != 0);
+
+#elif defined(_WIN32)
+  static const bool has_support =
+    (IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE) != FALSE);
+
+#else
+  static const bool has_support = false;
+#endif
+
+  if (pnmimage_cat.is_debug()) {
+    static bool checked = false;
+    if (!checked) {
+#if defined(__GNUC__) || defined(_WIN32)
+      if (has_support) {
+        pnmimage_cat.debug()
+          << "Runtime detection reports SSE2 instructions available: "
+          << "SSE2-optimized sRGB encoding routines enabled.\n";
+      } else {
+        pnmimage_cat.debug()
+          << "Runtime detection reports SSE2 instructions unavailable: "
+          << "SSE2-optimized sRGB encoding routines disabled.\n";
+      }
+#else
+      pnmimage_cat.debug()
+        << "No runtime detection mechanism for SSE2 instructions available: "
+        << "SSE2-optimized sRGB encoding routines disabled.\n";
+#endif
+      checked = true;
+    }
+  }
+
+  return has_support;
+}
+
+#endif  // __SSE2__

+ 59 - 0
panda/src/pnmimage/convert_srgb.h

@@ -0,0 +1,59 @@
+// Filename: convert_srgb.h
+// Created by:  rdb (13Nov14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONVERT_SRGB_H
+#define CONVERT_SRGB_H
+
+#include "pandabase.h"
+#include "luse.h"
+#include "pnmimage_base.h"
+
+// The below functions can encode and decode sRGB colors in various
+// representations.  Some of them are implemented using look-up tables,
+// some others using SSE2 intrinsics.
+extern EXPCL_PANDA_PNMIMAGE const unsigned char to_srgb8_table[256];
+extern EXPCL_PANDA_PNMIMAGE const unsigned char to_linear_uchar_table[256];
+extern EXPCL_PANDA_PNMIMAGE const float to_linear_float_table[256];
+
+EXPCL_PANDA_PNMIMAGE CONSTEXPR float decode_sRGB_float(unsigned char val);
+EXPCL_PANDA_PNMIMAGE INLINE float decode_sRGB_float(float val);
+EXPCL_PANDA_PNMIMAGE CONSTEXPR unsigned char decode_sRGB_uchar(unsigned char val);
+EXPCL_PANDA_PNMIMAGE INLINE unsigned char decode_sRGB_uchar(float val);
+
+EXPCL_PANDA_PNMIMAGE INLINE float encode_sRGB_float(unsigned char val);
+EXPCL_PANDA_PNMIMAGE INLINE float encode_sRGB_float(float val);
+EXPCL_PANDA_PNMIMAGE CONSTEXPR unsigned char encode_sRGB_uchar(unsigned char val);
+EXPCL_PANDA_PNMIMAGE INLINE unsigned char encode_sRGB_uchar(float val);
+
+// These functions convert more than one component in one go,
+// which can be faster due to vectorization.
+EXPCL_PANDA_PNMIMAGE INLINE void encode_sRGB_uchar(const LColorf &from,
+                                                   xel &into);
+EXPCL_PANDA_PNMIMAGE INLINE void encode_sRGB_uchar(const LColorf &from,
+                                                   xel &into, xelval &into_alpha);
+
+// Use these functions if you know that SSE2 support is available.
+// Otherwise, they will crash!
+EXPCL_PANDA_PNMIMAGE unsigned char encode_sRGB_uchar_sse2(float val);
+EXPCL_PANDA_PNMIMAGE void encode_sRGB_uchar_sse2(const LColorf &from,
+                                                 xel &into);
+EXPCL_PANDA_PNMIMAGE void encode_sRGB_uchar_sse2(const LColorf &from,
+                                                 xel &into, xelval &into_alpha);
+
+// Use the following to find out if you can call either of the above.
+EXPCL_PANDA_PNMIMAGE bool has_sse2_sRGB_encode();
+
+#include "convert_srgb.I"
+
+#endif

+ 151 - 0
panda/src/pnmimage/convert_srgb_sse2.cxx

@@ -0,0 +1,151 @@
+// Filename: convert_srgb_sse2.cxx
+// Created by:  rdb (13Nov14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+// This file should always be compiled with SSE2 support.  These
+// functions will only be called when SSE2 support is detected at
+// run-time.
+
+#include "convert_srgb.h"
+#include "luse.h"
+
+#if defined(__SSE2__) || (_M_IX86_FP >= 2) || defined(_M_X64) || defined(_M_AMD64)
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+
+static INLINE __m128i _encode_sRGB_sse2_mul255(__m128 val) {
+  // This an SSE2-based approximation of the sRGB encode function.
+  // It has a maximum error of around 0.001, which is by far small
+  // enough for a uchar.  It is also at least 10x as fast as the
+  // original; up to 40x when taking advantage of vectorization.
+  // Note that the fourth float is only multiplied with 255.
+
+  // Part of the code in this function is derived from:
+  // http://stackoverflow.com/a/6486630/2135754
+
+  // Clamp to 0-1 range.
+  val = _mm_max_ps(val, _mm_set1_ps(0.0f));
+  val = _mm_min_ps(val, _mm_set1_ps(1.0f));
+
+  // Pre-multiply with constant factor to adjust for exp bias.
+  __m128 xf = _mm_mul_ps(val, _mm_set1_ps(6.3307e18f));
+
+  // Approximate logarithm by... casting!
+  xf = _mm_cvtepi32_ps(_mm_castps_si128(xf));
+
+  // Multiply 'logarithm' by power.
+  xf = _mm_mul_ps(xf, _mm_set1_ps(2.0f / 3.0f));
+
+  // Reverse operation of above: cast the other way.
+  xf = _mm_castsi128_ps(_mm_cvtps_epi32(xf));
+
+  // Make an overestimate and an underestimate.
+  __m128 xover = _mm_mul_ps(val, xf);
+  __m128 xunder = _mm_mul_ps(_mm_mul_ps(val, val),
+                             _mm_rsqrt_ps(xf));
+
+  // Average the two factors, with a slight bias.
+  __m128 xavg = _mm_mul_ps(_mm_add_ps(xover, xunder),
+                           _mm_set1_ps(0.5286098f));
+
+  // Take square root twice.  Note that this is faster than
+  // the more expensive _mm_sqrt_ps instruction.
+  xavg = _mm_mul_ps(xavg, _mm_rsqrt_ps(xavg));
+  xavg = _mm_mul_ps(xavg, _mm_rsqrt_ps(xavg));
+
+  // Bring it into the correct range.  These factors are determined
+  // not on the basis of accuracy, but are chosen such that the
+  // decoder lookup table produces an equivalent result for any value.
+  xavg = _mm_mul_ps(xavg, _mm_set1_ps(269.122f));
+  xavg = _mm_sub_ps(xavg, _mm_set1_ps(13.55f));
+
+  // Compute the linear section.  This is also the path that
+  // the alpha channel takes, so we set the alpha multiplier
+  // to 255 (since alpha is not sRGB-converted).
+  __m128 lval = _mm_mul_ps(val,
+    _mm_set_ps(255.0f, 3294.6f, 3294.6f, 3294.6f));
+
+  lval = _mm_add_ps(lval, _mm_set1_ps(0.5f));
+
+  // Decide which version to return.  Rig the alpha
+  // comparator to always fail so that the linear path
+  // is always chosen for alpha.
+  __m128 mask = _mm_cmpge_ps(val,
+    _mm_set_ps(2.0f, 0.0031308f, 0.0031308f, 0.0031308f));
+
+  // This is a non-branching way to return one or the other value.
+  return _mm_cvttps_epi32(_mm_or_ps(
+    _mm_and_ps(mask, xavg),
+    _mm_andnot_ps(mask, lval)));
+}
+
+unsigned char
+encode_sRGB_uchar_sse2(float val) {
+  // Running only a single component through this function is still
+  // way faster than the equivalent non-SSE2 version.
+  return (unsigned char)
+    _mm_extract_epi16(_encode_sRGB_sse2_mul255(_mm_set1_ps(val)), 0);
+}
+
+void
+encode_sRGB_uchar_sse2(const LColorf &color, xel &into) {
+#ifdef LINMATH_ALIGN
+  __m128 vec = _mm_load_ps(color.get_data());
+#else
+  __m128 vec = _mm_loadu_ps(color.get_data());
+#endif
+
+  __m128i vals = _encode_sRGB_sse2_mul255(vec);
+  into.r = _mm_extract_epi16(vals, 0);
+  into.g = _mm_extract_epi16(vals, 2);
+  into.b = _mm_extract_epi16(vals, 4);
+}
+
+void
+encode_sRGB_uchar_sse2(const LColorf &color, xel &into, xelval &into_alpha) {
+#ifdef LINMATH_ALIGN
+  __m128 vec = _mm_load_ps(color.get_data());
+#else
+  __m128 vec = _mm_loadu_ps(color.get_data());
+#endif
+
+  __m128i vals = _encode_sRGB_sse2_mul255(vec);
+  into.r = _mm_extract_epi16(vals, 0);
+  into.g = _mm_extract_epi16(vals, 2);
+  into.b = _mm_extract_epi16(vals, 4);
+  into_alpha = _mm_extract_epi16(vals, 6);
+}
+
+#else
+// Somehow we're still compiling this without SSE2 support.  We'll
+// still have to define these functions, but emit a warning that the
+// build system isn't configured properly.
+#warning convert_srgb_sse2.cxx is being compiled without SSE2 support!
+
+unsigned char
+encode_sRGB_uchar_sse2(float val) {
+  return encode_sRGB_uchar(val);
+}
+
+void
+encode_sRGB_uchar_sse2(const LColorf &color, xel &into) {
+  encode_sRGB_uchar(color, into);
+}
+
+void
+encode_sRGB_uchar_sse2(const LColorf &color, xel &into, xelval &into_alpha) {
+  encode_sRGB_uchar(color, into, into_alpha);
+}
+
+#endif

+ 1 - 0
panda/src/pnmimage/p3pnmimage_composite1.cxx

@@ -1,4 +1,5 @@
 #include "config_pnmimage.cxx"
+#include "convert_srgb.cxx"
 #include "pfmFile.cxx"
 #include "pnm-image-filter.cxx"
 #include "pnmbitio.cxx"

+ 1 - 1
panda/src/rocket/rocketRenderInterface.h

@@ -63,7 +63,7 @@ private:
 
   // Hold the scissor settings and whether or not to enable scissoring.
   bool _enable_scissor;
-  LVecBase4f _scissor;
+  LVecBase4 _scissor;
 
   // These are temporarily filled in by render().
   CullTraverser *_trav;

+ 1 - 1
panda/src/tinydisplay/zbuffer.h

@@ -75,7 +75,7 @@ typedef unsigned int ZPOINT;
   ((((unsigned int)(a) << 24) & 0xff000000) | (((unsigned int)(r) << 16) & 0xff0000) | (((unsigned int)(g) << 8) & 0xff00) | (unsigned int)(b))
 
 #define SRGB_TO_PIXEL(r,g,b) \
-  ((encode_sRGB[(unsigned int)(r) >> 4] << 16) | (encode_sRGB10[(unsigned int)(g) >> 4] << 8) | (encode_sRGB[(unsigned int)(b) >> 4]))
+  ((encode_sRGB[(unsigned int)(r) >> 4] << 16) | (encode_sRGB[(unsigned int)(g) >> 4] << 8) | (encode_sRGB[(unsigned int)(b) >> 4]))
 #define SRGBA_TO_PIXEL(r,g,b,a) \
   ((((unsigned int)(a) << 16) & 0xff000000) | (encode_sRGB[(unsigned int)(r) >> 4] << 16) | (encode_sRGB[(unsigned int)(g) >> 4] << 8) | (encode_sRGB[(unsigned int)(b) >> 4]))
 

+ 16 - 30
panda/src/wgldisplay/wglGraphicsPipe.cxx

@@ -153,46 +153,32 @@ make_output(const string &name,
     return new wglGraphicsWindow(engine, this, name, fb_prop, win_prop,
                                  flags, gsg, host);
   }
-  
+
   // Second thing to try: a GLGraphicsBuffer
-  
+
   if (retry == 1) {
-    if ((!gl_support_fbo)||
-        (host==0)||
-        ((flags&BF_require_parasite)!=0)||
-        ((flags&BF_require_window)!=0)) {
+    if (!gl_support_fbo || host == NULL ||
+        (flags & (BF_require_parasite | BF_require_window)) != 0) {
       return NULL;
     }
     // Early failure - if we are sure that this buffer WONT
     // meet specs, we can bail out early.
-    int _fbo_multisample = 0;
-    if (!ConfigVariableBool("framebuffer-object-multisample", false, PRC_DESC("Enabled Multisample."))) {
-        _fbo_multisample = 16;
-    }
-    if ((flags & BF_fb_props_optional)==0) {
+    if ((flags & BF_fb_props_optional) == 0) {
       if (fb_prop.get_indexed_color() ||
-          (fb_prop.get_back_buffers() > 0)||
-          (fb_prop.get_accum_bits() > 0)||
-          (fb_prop.get_multisamples() > _fbo_multisample)) {
+          fb_prop.get_back_buffers() > 0 ||
+          fb_prop.get_accum_bits() > 0) {
         return NULL;
       }
     }
-    if ((wglgsg != 0) &&
-        (wglgsg->is_valid()) &&
-        (!wglgsg->needs_reset()) &&
-	!wglgsg->_supports_framebuffer_object) {
-      return NULL;
-    }
-
-    // Early success - if we are sure that this buffer WILL
-    // meet specs, we can precertify it.
-    if ((wglgsg != 0) &&
-        (wglgsg->is_valid()) &&
-        (!wglgsg->needs_reset()) &&
-        (wglgsg->_supports_framebuffer_object) &&
-        (wglgsg->_glDrawBuffers != 0)&&
-        (fb_prop.is_basic())) {
-      precertify = true;
+    if (wglgsg != NULL && wglgsg->is_valid() && !wglgsg->needs_reset()) {
+      if (!wglgsg->_supports_framebuffer_object ||
+          wglgsg->_glDrawBuffers == NULL) {
+        return NULL;
+      } else {
+        // Early success - if we are sure that this buffer WILL
+        // meet specs, we can precertify it.
+        precertify = true;
+      }
     }
     return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop,
                                 flags, gsg, host);

+ 1 - 1
pandatool/src/daeegg/daeCharacter.cxx

@@ -320,7 +320,7 @@ build_table(EggTable *parent, FCDSceneNode* node, const pset<float> &keys) {
   }
 
   // Quantize the FPS, otherwise Panda complains about FPS mismatches.
-  float fps = round(((keys.size() - 1) / timing_total) * 100) * 0.01f;
+  float fps = cfloor(((keys.size() - 1) / timing_total) * 100 + 0.5f) * 0.01f;
   xform->set_fps(fps);
 
   // Loop through the children joints

+ 1 - 1
pandatool/src/daeegg/daeCharacter.h

@@ -45,7 +45,7 @@ public:
       _bind_pose(LMatrix4d::ident_mat()) {}
 
     LMatrix4d _bind_pose;
-    const PT(EggGroup) _group;
+    PT(EggGroup) _group;
     const FCDSceneNode *_scene_node;
     DaeCharacter *_character;
   };

+ 1 - 1
pandatool/src/daeegg/daeToEggConverter.cxx

@@ -228,7 +228,7 @@ convert_file(const Filename &filename) {
       Characters::iterator it;
       DaeCharacter *character;
       for (it = _characters.begin(); it != _characters.end(); ++it) {
-        DaeCharacter *character = *it;
+        character = *it;
 
         // Collect key frame timings.
         if (get_animation_convert() == AC_both ||

+ 7 - 0
pandatool/src/daeegg/pre_fcollada_include.h

@@ -22,6 +22,13 @@
   #error You must include pre_fcollada_include.h before including FCollada.h!
 #endif
 
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#include <winsock2.h>
+#endif
+
 // FCollada expects LINUX to be defined on linux
 #ifdef IS_LINUX
   #ifndef LINUX

+ 399 - 0
samples/asteroids/main.py

@@ -0,0 +1,399 @@
+#!/usr/bin/env python
+
+# Author: Shao Zhang, Phil Saltzman, and Greg Lindley
+# Last Updated: 2015-03-13
+#
+# This tutorial demonstrates the use of tasks. A task is a function that
+# gets called once every frame. They are good for things that need to be
+# updated very often. In the case of asteroids, we use tasks to update
+# the positions of all the objects, and to check if the bullets or the
+# ship have hit the asteroids.
+#
+# Note: This definitely a complicated example. Tasks are the cores of
+# most games so it seemed appropriate to show what a full game in Panda
+# could look like.
+
+from direct.showbase.ShowBase import ShowBase
+from panda3d.core import TextNode, TransparencyAttrib
+from panda3d.core import LPoint3, LVector3
+from direct.gui.OnscreenText import OnscreenText
+from direct.task.Task import Task
+from math import sin, cos, pi
+from random import randint, choice, random
+from direct.interval.MetaInterval import Sequence
+from direct.interval.FunctionInterval import Wait, Func
+import sys
+
+# Constants that will control the behavior of the game. It is good to
+# group constants like this so that they can be changed once without
+# having to find everywhere they are used in code
+SPRITE_POS = 55     # At default field of view and a depth of 55, the screen
+# dimensions is 40x30 units
+SCREEN_X = 20       # Screen goes from -20 to 20 on X
+SCREEN_Y = 15       # Screen goes from -15 to 15 on Y
+TURN_RATE = 360     # Degrees ship can turn in 1 second
+ACCELERATION = 10   # Ship acceleration in units/sec/sec
+MAX_VEL = 6         # Maximum ship velocity in units/sec
+MAX_VEL_SQ = MAX_VEL ** 2  # Square of the ship velocity
+DEG_TO_RAD = pi / 180  # translates degrees to radians for sin and cos
+BULLET_LIFE = 2     # How long bullets stay on screen before removed
+BULLET_REPEAT = .2  # How often bullets can be fired
+BULLET_SPEED = 10   # Speed bullets move
+AST_INIT_VEL = 1    # Velocity of the largest asteroids
+AST_INIT_SCALE = 3  # Initial asteroid scale
+AST_VEL_SCALE = 2.2  # How much asteroid speed multiplies when broken up
+AST_SIZE_SCALE = .6  # How much asteroid scale changes when broken up
+AST_MIN_SCALE = 1.1  # If and asteroid is smaller than this and is hit,
+# it disapears instead of splitting up
+
+
+# This helps reduce the amount of code used by loading objects, since all of
+# the objects are pretty much the same.
+def loadObject(tex=None, pos=LPoint3(0, 0), depth=SPRITE_POS, scale=1,
+               transparency=True):
+    # Every object uses the plane model and is parented to the camera
+    # so that it faces the screen.
+    obj = loader.loadModel("models/plane")
+    obj.reparentTo(camera)
+
+    # Set the initial position and scale.
+    obj.setPos(pos.getX(), depth, pos.getY())
+    obj.setScale(scale)
+
+    # This tells Panda not to worry about the order that things are drawn in
+    # (ie. disable Z-testing).  This prevents an effect known as Z-fighting.
+    obj.setBin("unsorted", 0)
+    obj.setDepthTest(False)
+
+    if transparency:
+        # Enable transparency blending.
+        obj.setTransparency(TransparencyAttrib.MAlpha)
+
+    if tex:
+        # Load and set the requested texture.
+        tex = loader.loadTexture("textures/" + tex)
+        obj.setTexture(tex, 1)
+
+    return obj
+
+
+# Macro-like function used to reduce the amount to code needed to create the
+# on screen instructions
+def genLabelText(text, i):
+    return OnscreenText(text=text, parent=base.a2dTopLeft, pos=(0.07, -.06 * i - 0.1),
+                        fg=(1, 1, 1, 1), align=TextNode.ALeft, shadow=(0, 0, 0, 0.5), scale=.05)
+
+
+class AsteroidsDemo(ShowBase):
+
+    def __init__(self):
+        # Initialize the ShowBase class from which we inherit, which will
+        # create a window and set up everything we need for rendering into it.
+        ShowBase.__init__(self)
+
+        # This code puts the standard title and instruction text on screen
+        self.title = OnscreenText(text="Panda3D: Tutorial - Tasks",
+                                  parent=base.a2dBottomRight, scale=.07,
+                                  align=TextNode.ARight, pos=(-0.1, 0.1),
+                                  fg=(1, 1, 1, 1), shadow=(0, 0, 0, 0.5))
+        self.escapeText = genLabelText("ESC: Quit", 0)
+        self.leftkeyText = genLabelText("[Left Arrow]: Turn Left (CCW)", 1)
+        self.rightkeyText = genLabelText("[Right Arrow]: Turn Right (CW)", 2)
+        self.upkeyText = genLabelText("[Up Arrow]: Accelerate", 3)
+        self.spacekeyText = genLabelText("[Space Bar]: Fire", 4)
+
+        # Disable default mouse-based camera control.  This is a method on the
+        # ShowBase class from which we inherit.
+        self.disableMouse()
+
+        # Load the background starfield.
+        self.setBackgroundColor((0, 0, 0, 1))
+        self.bg = loadObject("stars.jpg", scale=146, depth=200,
+                             transparency=False)
+
+        # Load the ship and set its initial velocity.
+        self.ship = loadObject("ship.png")
+        self.setVelocity(self.ship, LVector3.zero())
+
+        # A dictionary of what keys are currently being pressed
+        # The key events update this list, and our task will query it as input
+        self.keys = {"turnLeft": 0, "turnRight": 0,
+                     "accel": 0, "fire": 0}
+
+        self.accept("escape", sys.exit)  # Escape quits
+        # Other keys events set the appropriate value in our key dictionary
+        self.accept("arrow_left",     self.setKey, ["turnLeft", 1])
+        self.accept("arrow_left-up",  self.setKey, ["turnLeft", 0])
+        self.accept("arrow_right",    self.setKey, ["turnRight", 1])
+        self.accept("arrow_right-up", self.setKey, ["turnRight", 0])
+        self.accept("arrow_up",       self.setKey, ["accel", 1])
+        self.accept("arrow_up-up",    self.setKey, ["accel", 0])
+        self.accept("space",          self.setKey, ["fire", 1])
+
+        # Now we create the task. taskMgr is the task manager that actually
+        # calls the function each frame. The add method creates a new task.
+        # The first argument is the function to be called, and the second
+        # argument is the name for the task.  It returns a task object which
+        # is passed to the function each frame.
+        self.gameTask = taskMgr.add(self.gameLoop, "gameLoop")
+
+        # Stores the time at which the next bullet may be fired.
+        self.nextBullet = 0.0
+
+        # This list will stored fired bullets.
+        self.bullets = []
+
+        # Complete initialization by spawning the asteroids.
+        self.spawnAsteroids()
+
+    # As described earlier, this simply sets a key in the self.keys dictionary
+    # to the given value.
+    def setKey(self, key, val):
+        self.keys[key] = val
+
+    def setVelocity(self, obj, val):
+        obj.setPythonTag("velocity", val)
+
+    def getVelocity(self, obj):
+        return obj.getPythonTag("velocity")
+
+    def setExpires(self, obj, val):
+        obj.setPythonTag("expires", val)
+
+    def getExpires(self, obj):
+        return obj.getPythonTag("expires")
+
+    def spawnAsteroids(self):
+        # Control variable for if the ship is alive
+        self.alive = True
+        self.asteroids = []  # List that will contain our asteroids
+
+        for i in range(10):
+            # This loads an asteroid. The texture chosen is random
+            # from "asteroid1.png" to "asteroid3.png".
+            asteroid = loadObject("asteroid%d.png" % (randint(1, 3)),
+                                  scale=AST_INIT_SCALE)
+            self.asteroids.append(asteroid)
+
+            # This is kind of a hack, but it keeps the asteroids from spawning
+            # near the player.  It creates the list (-20, -19 ... -5, 5, 6, 7,
+            # ... 20) and chooses a value from it. Since the player starts at 0
+            # and this list doesn't contain anything from -4 to 4, it won't be
+            # close to the player.
+            asteroid.setX(choice(range(-SCREEN_X, -5) + range(5, SCREEN_X)))
+            # Same thing for Y, but from -15 to 15
+            asteroid.setZ(choice(range(-SCREEN_Y, -5) + range(5, SCREEN_Y)))
+
+            # Heading is a random angle in radians
+            heading = random() * 2 * pi
+
+            # Converts the heading to a vector and multiplies it by speed to
+            # get a velocity vector
+            v = LVector3(sin(heading), 0, cos(heading)) * AST_INIT_VEL
+            self.setVelocity(self.asteroids[i], v)
+
+    # This is our main task function, which does all of the per-frame
+    # processing.  It takes in self like all functions in a class, and task,
+    # the task object returned by taskMgr.
+    def gameLoop(self, task):
+        # Get the time elapsed since the next frame.  We need this for our
+        # distance and velocity calculations.
+        dt = globalClock.getDt()
+
+        # If the ship is not alive, do nothing.  Tasks return Task.cont to
+        # signify that the task should continue running. If Task.done were
+        # returned instead, the task would be removed and would no longer be
+        # called every frame.
+        if not self.alive:
+            return Task.cont
+
+        # update ship position
+        self.updateShip(dt)
+
+        # check to see if the ship can fire
+        if self.keys["fire"] and task.time > self.nextBullet:
+            self.fire(task.time)  # If so, call the fire function
+            # And disable firing for a bit
+            self.nextBullet = task.time + BULLET_REPEAT
+        # Remove the fire flag until the next spacebar press
+        self.keys["fire"] = 0
+
+        # update asteroids
+        for obj in self.asteroids:
+            self.updatePos(obj, dt)
+
+        # update bullets
+        newBulletArray = []
+        for obj in self.bullets:
+            self.updatePos(obj, dt)  # Update the bullet
+            # Bullets have an experation time (see definition of fire)
+            # If a bullet has not expired, add it to the new bullet list so
+            # that it will continue to exist.
+            if self.getExpires(obj) > task.time:
+                newBulletArray.append(obj)
+            else:
+                obj.removeNode()  # Otherwise, remove it from the scene.
+        # Set the bullet array to be the newly updated array
+        self.bullets = newBulletArray
+
+        # Check bullet collision with asteroids
+        # In short, it checks every bullet against every asteroid. This is
+        # quite slow.  A big optimization would be to sort the objects left to
+        # right and check only if they overlap.  Framerate can go way down if
+        # there are many bullets on screen, but for the most part it's okay.
+        for bullet in self.bullets:
+            # This range statement makes it step though the asteroid list
+            # backwards.  This is because if an asteroid is removed, the
+            # elements after it will change position in the list.  If you go
+            # backwards, the length stays constant.
+            for i in range(len(self.asteroids) - 1, -1, -1):
+                asteroid = self.asteroids[i]
+                # Panda's collision detection is more complicated than we need
+                # here.  This is the basic sphere collision check. If the
+                # distance between the object centers is less than sum of the
+                # radii of the two objects, then we have a collision. We use
+                # lengthSquared() since it is faster than length().
+                if ((bullet.getPos() - asteroid.getPos()).lengthSquared() <
+                    (((bullet.getScale().getX() + asteroid.getScale().getX())
+                      * .5) ** 2)):
+                    # Schedule the bullet for removal
+                    self.setExpires(bullet, 0)
+                    self.asteroidHit(i)      # Handle the hit
+
+        # Now we do the same collision pass for the ship
+        shipSize = self.ship.getScale().getX()
+        for ast in self.asteroids:
+            # Same sphere collision check for the ship vs. the asteroid
+            if ((self.ship.getPos() - ast.getPos()).lengthSquared() <
+                    (((shipSize + ast.getScale().getX()) * .5) ** 2)):
+                # If there is a hit, clear the screen and schedule a restart
+                self.alive = False         # Ship is no longer alive
+                # Remove every object in asteroids and bullets from the scene
+                for i in self.asteroids + self.bullets:
+                    i.removeNode()
+                self.bullets = []          # Clear the bullet list
+                self.ship.hide()           # Hide the ship
+                # Reset the velocity
+                self.setVelocity(self.ship, LVector3(0, 0, 0))
+                Sequence(Wait(2),          # Wait 2 seconds
+                         Func(self.ship.setR, 0),  # Reset heading
+                         Func(self.ship.setX, 0),  # Reset position X
+                         # Reset position Y (Z for Panda)
+                         Func(self.ship.setZ, 0),
+                         Func(self.ship.show),     # Show the ship
+                         Func(self.spawnAsteroids)).start()  # Remake asteroids
+                return Task.cont
+
+        # If the player has successfully destroyed all asteroids, respawn them
+        if len(self.asteroids) == 0:
+            self.spawnAsteroids()
+
+        return Task.cont    # Since every return is Task.cont, the task will
+        # continue indefinitely
+
+    # Updates the positions of objects
+    def updatePos(self, obj, dt):
+        vel = self.getVelocity(obj)
+        newPos = obj.getPos() + (vel * dt)
+
+        # Check if the object is out of bounds. If so, wrap it
+        radius = .5 * obj.getScale().getX()
+        if newPos.getX() - radius > SCREEN_X:
+            newPos.setX(-SCREEN_X)
+        elif newPos.getX() + radius < -SCREEN_X:
+            newPos.setX(SCREEN_X)
+        if newPos.getZ() - radius > SCREEN_Y:
+            newPos.setZ(-SCREEN_Y)
+        elif newPos.getZ() + radius < -SCREEN_Y:
+            newPos.setZ(SCREEN_Y)
+
+        obj.setPos(newPos)
+
+    # The handler when an asteroid is hit by a bullet
+    def asteroidHit(self, index):
+        # If the asteroid is small it is simply removed
+        if self.asteroids[index].getScale().getX() <= AST_MIN_SCALE:
+            self.asteroids[index].removeNode()
+            # Remove the asteroid from the list of asteroids.
+            del self.asteroids[index]
+        else:
+            # If it is big enough, divide it up into little asteroids.
+            # First we update the current asteroid.
+            asteroid = self.asteroids[index]
+            newScale = asteroid.getScale().getX() * AST_SIZE_SCALE
+            asteroid.setScale(newScale)  # Rescale it
+
+            # The new direction is chosen as perpendicular to the old direction
+            # This is determined using the cross product, which returns a
+            # vector perpendicular to the two input vectors.  By crossing
+            # velocity with a vector that goes into the screen, we get a vector
+            # that is orthagonal to the original velocity in the screen plane.
+            vel = self.getVelocity(asteroid)
+            speed = vel.length() * AST_VEL_SCALE
+            vel.normalize()
+            vel = LVector3(0, 1, 0).cross(vel)
+            vel *= speed
+            self.setVelocity(asteroid, vel)
+
+            # Now we create a new asteroid identical to the current one
+            newAst = loadObject(scale=newScale)
+            self.setVelocity(newAst, vel * -1)
+            newAst.setPos(asteroid.getPos())
+            newAst.setTexture(asteroid.getTexture(), 1)
+            self.asteroids.append(newAst)
+
+    # This updates the ship's position. This is similar to the general update
+    # but takes into account turn and thrust
+    def updateShip(self, dt):
+        heading = self.ship.getR()  # Heading is the roll value for this model
+        # Change heading if left or right is being pressed
+        if self.keys["turnRight"]:
+            heading += dt * TURN_RATE
+            self.ship.setR(heading % 360)
+        elif self.keys["turnLeft"]:
+            heading -= dt * TURN_RATE
+            self.ship.setR(heading % 360)
+
+        # Thrust causes acceleration in the direction the ship is currently
+        # facing
+        if self.keys["accel"]:
+            heading_rad = DEG_TO_RAD * heading
+            # This builds a new velocity vector and adds it to the current one
+            # relative to the camera, the screen in Panda is the XZ plane.
+            # Therefore all of our Y values in our velocities are 0 to signify
+            # no change in that direction.
+            newVel = \
+                LVector3(sin(heading_rad), 0, cos(heading_rad)) * ACCELERATION * dt
+            newVel += self.getVelocity(self.ship)
+            # Clamps the new velocity to the maximum speed. lengthSquared() is
+            # used again since it is faster than length()
+            if newVel.lengthSquared() > MAX_VEL_SQ:
+                newVel.normalize()
+                newVel *= MAX_VEL
+            self.setVelocity(self.ship, newVel)
+
+        # Finally, update the position as with any other object
+        self.updatePos(self.ship, dt)
+
+    # Creates a bullet and adds it to the bullet list
+    def fire(self, time):
+        direction = DEG_TO_RAD * self.ship.getR()
+        pos = self.ship.getPos()
+        bullet = loadObject("bullet.png", scale=.2)  # Create the object
+        bullet.setPos(pos)
+        # Velocity is in relation to the ship
+        vel = (self.getVelocity(self.ship) +
+               (LVector3(sin(direction), 0, cos(direction)) *
+                BULLET_SPEED))
+        self.setVelocity(bullet, vel)
+        # Set the bullet expiration time to be a certain amount past the
+        # current time
+        self.setExpires(bullet, time + BULLET_LIFE)
+
+        # Finally, add the new bullet to the list
+        self.bullets.append(bullet)
+
+# We now have everything we need. Make an instance of the class and start
+# 3D rendering
+demo = AsteroidsDemo()
+demo.run()

+ 39 - 0
samples/asteroids/models/plane.egg

@@ -0,0 +1,39 @@
+<CoordinateSystem> { Y-Up }
+
+<Comment> {
+  "maya2egg plane.mb plane.egg"
+}
+<Group> groundPlane_transform {
+}
+<Group> pPlane1 {
+  <VertexPool> pPlaneShape1.verts {
+    <Vertex> 1 {
+      -0.5 -0.5 0
+      <Normal> { 0 0 -1 }
+      <UV> { 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 2 {
+      -0.5 0.5 0
+      <Normal> { 0 0 -1 }
+      <UV> { 0 1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 3 {
+      0.5 -0.5 0
+      <Normal> { 0 0 -1 }
+      <UV> { 1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 4 {
+      0.5 0.5 0
+      <Normal> { 0 0 -1 }
+      <UV> { 1 1 }
+      <RGBA> { 1 1 1 1 }
+    }
+  }
+  <Polygon> {
+    <Normal> { 0 0 -1 }
+    <VertexRef>{ 3 4 2 1 <Ref> { pPlaneShape1.verts } }
+  }
+}

BIN
samples/asteroids/textures/asteroid1.png


BIN
samples/asteroids/textures/asteroid2.png


BIN
samples/asteroids/textures/asteroid3.png


BIN
samples/asteroids/textures/bullet.png


BIN
samples/asteroids/textures/ship.png


BIN
samples/asteroids/textures/stars.jpg


+ 338 - 0
samples/ball-in-maze/main.py

@@ -0,0 +1,338 @@
+#!/usr/bin/env python
+
+# Author: Shao Zhang, Phil Saltzman
+# Last Updated: 2015-03-13
+#
+# This tutorial shows how to detect and respond to collisions. It uses solids
+# create in code and the egg files, how to set up collision masks, a traverser,
+# and a handler, how to detect collisions, and how to dispatch function based
+# on the collisions. All of this is put together to simulate a labyrinth-style
+# game
+
+from direct.showbase.ShowBase import ShowBase
+from panda3d.core import CollisionTraverser, CollisionNode
+from panda3d.core import CollisionHandlerQueue, CollisionRay
+from panda3d.core import Material, LRotationf, NodePath
+from panda3d.core import AmbientLight, DirectionalLight
+from panda3d.core import TextNode
+from panda3d.core import LVector3, BitMask32
+from direct.gui.OnscreenText import OnscreenText
+from direct.interval.MetaInterval import Sequence, Parallel
+from direct.interval.LerpInterval import LerpFunc
+from direct.interval.FunctionInterval import Func, Wait
+from direct.task.Task import Task
+import sys
+
+# Some constants for the program
+ACCEL = 70         # Acceleration in ft/sec/sec
+MAX_SPEED = 5      # Max speed in ft/sec
+MAX_SPEED_SQ = MAX_SPEED ** 2  # Squared to make it easier to use lengthSquared
+# Instead of length
+
+
+class BallInMazeDemo(ShowBase):
+
+    def __init__(self):
+        # Initialize the ShowBase class from which we inherit, which will
+        # create a window and set up everything we need for rendering into it.
+        ShowBase.__init__(self)
+
+        # This code puts the standard title and instruction text on screen
+        self.title = \
+            OnscreenText(text="Panda3D: Tutorial - Collision Detection",
+                         parent=base.a2dBottomRight, align=TextNode.ARight,
+                         fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=.08,
+                         shadow=(0, 0, 0, 0.5))
+        self.instructions = \
+            OnscreenText(text="Mouse pointer tilts the board",
+                         parent=base.a2dTopLeft, align=TextNode.ALeft,
+                         pos=(0.05, -0.08), fg=(1, 1, 1, 1), scale=.06,
+                         shadow=(0, 0, 0, 0.5))
+
+        self.accept("escape", sys.exit)  # Escape quits
+
+        # Disable default mouse-based camera control.  This is a method on the
+        # ShowBase class from which we inherit.
+        self.disableMouse()
+        camera.setPosHpr(0, 0, 25, 0, -90, 0)  # Place the camera
+
+        # Load the maze and place it in the scene
+        self.maze = loader.loadModel("models/maze")
+        self.maze.reparentTo(render)
+
+        # Most times, you want collisions to be tested against invisible geometry
+        # rather than every polygon. This is because testing against every polygon
+        # in the scene is usually too slow. You can have simplified or approximate
+        # geometry for the solids and still get good results.
+        #
+        # Sometimes you'll want to create and position your own collision solids in
+        # code, but it's often easier to have them built automatically. This can be
+        # done by adding special tags into an egg file. Check maze.egg and ball.egg
+        # and look for lines starting with <Collide>. The part is brackets tells
+        # Panda exactly what to do. Polyset means to use the polygons in that group
+        # as solids, while Sphere tells panda to make a collision sphere around them
+        # Keep means to keep the polygons in the group as visable geometry (good
+        # for the ball, not for the triggers), and descend means to make sure that
+        # the settings are applied to any subgroups.
+        #
+        # Once we have the collision tags in the models, we can get to them using
+        # NodePath's find command
+
+        # Find the collision node named wall_collide
+        self.walls = self.maze.find("**/wall_collide")
+
+        # Collision objects are sorted using BitMasks. BitMasks are ordinary numbers
+        # with extra methods for working with them as binary bits. Every collision
+        # solid has both a from mask and an into mask. Before Panda tests two
+        # objects, it checks to make sure that the from and into collision masks
+        # have at least one bit in common. That way things that shouldn't interact
+        # won't. Normal model nodes have collision masks as well. By default they
+        # are set to bit 20. If you want to collide against actual visable polygons,
+        # set a from collide mask to include bit 20
+        #
+        # For this example, we will make everything we want the ball to collide with
+        # include bit 0
+        self.walls.node().setIntoCollideMask(BitMask32.bit(0))
+        # CollisionNodes are usually invisible but can be shown. Uncomment the next
+        # line to see the collision walls
+        #self.walls.show()
+
+        # We will now find the triggers for the holes and set their masks to 0 as
+        # well. We also set their names to make them easier to identify during
+        # collisions
+        self.loseTriggers = []
+        for i in range(6):
+            trigger = self.maze.find("**/hole_collide" + str(i))
+            trigger.node().setIntoCollideMask(BitMask32.bit(0))
+            trigger.node().setName("loseTrigger")
+            self.loseTriggers.append(trigger)
+            # Uncomment this line to see the triggers
+            # trigger.show()
+
+        # Ground_collide is a single polygon on the same plane as the ground in the
+        # maze. We will use a ray to collide with it so that we will know exactly
+        # what height to put the ball at every frame. Since this is not something
+        # that we want the ball itself to collide with, it has a different
+        # bitmask.
+        self.mazeGround = self.maze.find("**/ground_collide")
+        self.mazeGround.node().setIntoCollideMask(BitMask32.bit(1))
+
+        # Load the ball and attach it to the scene
+        # It is on a root dummy node so that we can rotate the ball itself without
+        # rotating the ray that will be attached to it
+        self.ballRoot = render.attachNewNode("ballRoot")
+        self.ball = loader.loadModel("models/ball")
+        self.ball.reparentTo(self.ballRoot)
+
+        # Find the collison sphere for the ball which was created in the egg file
+        # Notice that it has a from collision mask of bit 0, and an into collison
+        # mask of no bits. This means that the ball can only cause collisions, not
+        # be collided into
+        self.ballSphere = self.ball.find("**/ball")
+        self.ballSphere.node().setFromCollideMask(BitMask32.bit(0))
+        self.ballSphere.node().setIntoCollideMask(BitMask32.allOff())
+
+        # No we create a ray to start above the ball and cast down. This is to
+        # Determine the height the ball should be at and the angle the floor is
+        # tilting. We could have used the sphere around the ball itself, but it
+        # would not be as reliable
+        self.ballGroundRay = CollisionRay()     # Create the ray
+        self.ballGroundRay.setOrigin(0, 0, 10)    # Set its origin
+        self.ballGroundRay.setDirection(0, 0, -1)  # And its direction
+        # Collision solids go in CollisionNode
+        # Create and name the node
+        self.ballGroundCol = CollisionNode('groundRay')
+        self.ballGroundCol.addSolid(self.ballGroundRay)  # Add the ray
+        self.ballGroundCol.setFromCollideMask(
+            BitMask32.bit(1))  # Set its bitmasks
+        self.ballGroundCol.setIntoCollideMask(BitMask32.allOff())
+        # Attach the node to the ballRoot so that the ray is relative to the ball
+        # (it will always be 10 feet over the ball and point down)
+        self.ballGroundColNp = self.ballRoot.attachNewNode(self.ballGroundCol)
+        # Uncomment this line to see the ray
+        #self.ballGroundColNp.show()
+
+        # Finally, we create a CollisionTraverser. CollisionTraversers are what
+        # do the job of walking the scene graph and calculating collisions.
+        # For a traverser to actually do collisions, you need to call
+        # traverser.traverse() on a part of the scene. Fortunately, ShowBase
+        # has a task that does this for the entire scene once a frame.  By
+        # assigning it to self.cTrav, we designate that this is the one that
+        # it should call traverse() on each frame.
+        self.cTrav = CollisionTraverser()
+
+        # Collision traversers tell collision handlers about collisions, and then
+        # the handler decides what to do with the information. We are using a
+        # CollisionHandlerQueue, which simply creates a list of all of the
+        # collisions in a given pass. There are more sophisticated handlers like
+        # one that sends events and another that tries to keep collided objects
+        # apart, but the results are often better with a simple queue
+        self.cHandler = CollisionHandlerQueue()
+        # Now we add the collision nodes that can create a collision to the
+        # traverser. The traverser will compare these to all others nodes in the
+        # scene. There is a limit of 32 CollisionNodes per traverser
+        # We add the collider, and the handler to use as a pair
+        self.cTrav.addCollider(self.ballSphere, self.cHandler)
+        self.cTrav.addCollider(self.ballGroundColNp, self.cHandler)
+
+        # Collision traversers have a built in tool to help visualize collisions.
+        # Uncomment the next line to see it.
+        #self.cTrav.showCollisions(render)
+
+        # This section deals with lighting for the ball. Only the ball was lit
+        # because the maze has static lighting pregenerated by the modeler
+        ambientLight = AmbientLight("ambientLight")
+        ambientLight.setColor((.55, .55, .55, 1))
+        directionalLight = DirectionalLight("directionalLight")
+        directionalLight.setDirection(LVector3(0, 0, -1))
+        directionalLight.setColor((0.375, 0.375, 0.375, 1))
+        directionalLight.setSpecularColor((1, 1, 1, 1))
+        self.ballRoot.setLight(render.attachNewNode(ambientLight))
+        self.ballRoot.setLight(render.attachNewNode(directionalLight))
+
+        # This section deals with adding a specular highlight to the ball to make
+        # it look shiny.  Normally, this is specified in the .egg file.
+        m = Material()
+        m.setSpecular((1, 1, 1, 1))
+        m.setShininess(96)
+        self.ball.setMaterial(m, 1)
+
+        # Finally, we call start for more initialization
+        self.start()
+
+    def start(self):
+        # The maze model also has a locator in it for where to start the ball
+        # To access it we use the find command
+        startPos = self.maze.find("**/start").getPos()
+        # Set the ball in the starting position
+        self.ballRoot.setPos(startPos)
+        self.ballV = LVector3(0, 0, 0)         # Initial velocity is 0
+        self.accelV = LVector3(0, 0, 0)        # Initial acceleration is 0
+
+        # Create the movement task, but first make sure it is not already
+        # running
+        taskMgr.remove("rollTask")
+        self.mainLoop = taskMgr.add(self.rollTask, "rollTask")
+
+    # This function handles the collision between the ray and the ground
+    # Information about the interaction is passed in colEntry
+    def groundCollideHandler(self, colEntry):
+        # Set the ball to the appropriate Z value for it to be exactly on the
+        # ground
+        newZ = colEntry.getSurfacePoint(render).getZ()
+        self.ballRoot.setZ(newZ + .4)
+
+        # Find the acceleration direction. First the surface normal is crossed with
+        # the up vector to get a vector perpendicular to the slope
+        norm = colEntry.getSurfaceNormal(render)
+        accelSide = norm.cross(LVector3.up())
+        # Then that vector is crossed with the surface normal to get a vector that
+        # points down the slope. By getting the acceleration in 3D like this rather
+        # than in 2D, we reduce the amount of error per-frame, reducing jitter
+        self.accelV = norm.cross(accelSide)
+
+    # This function handles the collision between the ball and a wall
+    def wallCollideHandler(self, colEntry):
+        # First we calculate some numbers we need to do a reflection
+        norm = colEntry.getSurfaceNormal(render) * -1  # The normal of the wall
+        curSpeed = self.ballV.length()                # The current speed
+        inVec = self.ballV / curSpeed                 # The direction of travel
+        velAngle = norm.dot(inVec)                    # Angle of incidance
+        hitDir = colEntry.getSurfacePoint(render) - self.ballRoot.getPos()
+        hitDir.normalize()
+        # The angle between the ball and the normal
+        hitAngle = norm.dot(hitDir)
+
+        # Ignore the collision if the ball is either moving away from the wall
+        # already (so that we don't accidentally send it back into the wall)
+        # and ignore it if the collision isn't dead-on (to avoid getting caught on
+        # corners)
+        if velAngle > 0 and hitAngle > .995:
+            # Standard reflection equation
+            reflectVec = (norm * norm.dot(inVec * -1) * 2) + inVec
+
+            # This makes the velocity half of what it was if the hit was dead-on
+            # and nearly exactly what it was if this is a glancing blow
+            self.ballV = reflectVec * (curSpeed * (((1 - velAngle) * .5) + .5))
+            # Since we have a collision, the ball is already a little bit buried in
+            # the wall. This calculates a vector needed to move it so that it is
+            # exactly touching the wall
+            disp = (colEntry.getSurfacePoint(render) -
+                    colEntry.getInteriorPoint(render))
+            newPos = self.ballRoot.getPos() + disp
+            self.ballRoot.setPos(newPos)
+
+    # This is the task that deals with making everything interactive
+    def rollTask(self, task):
+        # Standard technique for finding the amount of time since the last
+        # frame
+        dt = globalClock.getDt()
+
+        # If dt is large, then there has been a # hiccup that could cause the ball
+        # to leave the field if this functions runs, so ignore the frame
+        if dt > .2:
+            return Task.cont
+
+        # The collision handler collects the collisions. We dispatch which function
+        # to handle the collision based on the name of what was collided into
+        for i in range(self.cHandler.getNumEntries()):
+            entry = self.cHandler.getEntry(i)
+            name = entry.getIntoNode().getName()
+            if name == "wall_collide":
+                self.wallCollideHandler(entry)
+            elif name == "ground_collide":
+                self.groundCollideHandler(entry)
+            elif name == "loseTrigger":
+                self.loseGame(entry)
+
+        # Read the mouse position and tilt the maze accordingly
+        if base.mouseWatcherNode.hasMouse():
+            mpos = base.mouseWatcherNode.getMouse()  # get the mouse position
+            self.maze.setP(mpos.getY() * -10)
+            self.maze.setR(mpos.getX() * 10)
+
+        # Finally, we move the ball
+        # Update the velocity based on acceleration
+        self.ballV += self.accelV * dt * ACCEL
+        # Clamp the velocity to the maximum speed
+        if self.ballV.lengthSquared() > MAX_SPEED_SQ:
+            self.ballV.normalize()
+            self.ballV *= MAX_SPEED
+        # Update the position based on the velocity
+        self.ballRoot.setPos(self.ballRoot.getPos() + (self.ballV * dt))
+
+        # This block of code rotates the ball. It uses something called a quaternion
+        # to rotate the ball around an arbitrary axis. That axis perpendicular to
+        # the balls rotation, and the amount has to do with the size of the ball
+        # This is multiplied on the previous rotation to incrimentally turn it.
+        prevRot = LRotationf(self.ball.getQuat())
+        axis = LVector3.up().cross(self.ballV)
+        newRot = LRotationf(axis, 45.5 * dt * self.ballV.length())
+        self.ball.setQuat(prevRot * newRot)
+
+        return Task.cont       # Continue the task indefinitely
+
+    # If the ball hits a hole trigger, then it should fall in the hole.
+    # This is faked rather than dealing with the actual physics of it.
+    def loseGame(self, entry):
+        # The triggers are set up so that the center of the ball should move to the
+        # collision point to be in the hole
+        toPos = entry.getInteriorPoint(render)
+        taskMgr.remove('rollTask')  # Stop the maze task
+
+        # Move the ball into the hole over a short sequence of time. Then wait a
+        # second and call start to reset the game
+        Sequence(
+            Parallel(
+                LerpFunc(self.ballRoot.setX, fromData=self.ballRoot.getX(),
+                         toData=toPos.getX(), duration=.1),
+                LerpFunc(self.ballRoot.setY, fromData=self.ballRoot.getY(),
+                         toData=toPos.getY(), duration=.1),
+                LerpFunc(self.ballRoot.setZ, fromData=self.ballRoot.getZ(),
+                         toData=self.ballRoot.getZ() - .9, duration=.2)),
+            Wait(1),
+            Func(self.start)).start()
+
+# Finally, create an instance of our class and start 3d rendering
+demo = BallInMazeDemo()
+demo.run()

BIN
samples/ball-in-maze/models/ball.egg.pz


BIN
samples/ball-in-maze/models/iron05.jpg


BIN
samples/ball-in-maze/models/limba.jpg


BIN
samples/ball-in-maze/models/maze.egg.pz


+ 199 - 0
samples/boxing-robots/main.py

@@ -0,0 +1,199 @@
+#!/usr/bin/env python
+
+# Author: Shao Zhang, Phil Saltzman, and Eddie Caanan
+# Last Updated: 2015-03-13
+#
+# This tutorial shows how to play animations on models aka "actors".
+# It is based on the popular game of "Rock 'em Sock 'em Robots".
+
+from direct.showbase.ShowBase import ShowBase
+from panda3d.core import AmbientLight, DirectionalLight
+from panda3d.core import TextNode
+from panda3d.core import LVector3
+from direct.gui.OnscreenText import OnscreenText
+from direct.interval.MetaInterval import Sequence
+from direct.interval.FunctionInterval import Func, Wait
+from direct.actor import Actor
+from random import random
+import sys
+
+
+class BoxingRobotDemo(ShowBase):
+    # Macro-like function used to reduce the amount to code needed to create the
+    # on screen instructions
+
+    def genLabelText(self, text, i):
+        return OnscreenText(text=text, parent=base.a2dTopLeft, scale=.05,
+                            pos=(0.1, - 0.1 -.07 * i), fg=(1, 1, 1, 1),
+                            align=TextNode.ALeft)
+
+    def __init__(self):
+        # Initialize the ShowBase class from which we inherit, which will
+        # create a window and set up everything we need for rendering into it.
+        ShowBase.__init__(self)
+
+        # This code puts the standard title and instruction text on screen
+        self.title = OnscreenText(text="Panda3D: Tutorial - Actors",
+                                  parent=base.a2dBottomRight, style=1,
+                                  fg=(0, 0, 0, 1), pos=(-0.2, 0.1),
+                                  align=TextNode.ARight, scale=.09)
+
+        self.escapeEventText = self.genLabelText("ESC: Quit", 0)
+        self.akeyEventText = self.genLabelText("[A]: Robot 1 Left Punch", 1)
+        self.skeyEventText = self.genLabelText("[S]: Robot 1 Right Punch", 2)
+        self.kkeyEventText = self.genLabelText("[K]: Robot 2 Left Punch", 3)
+        self.lkeyEventText = self.genLabelText("[L]: Robot 2 Right Punch", 4)
+
+        # Set the camera in a fixed position
+        self.disableMouse()
+        camera.setPosHpr(14.5, -15.4, 14, 45, -14, 0)
+        self.setBackgroundColor(0, 0, 0)
+
+        # Add lighting so that the objects are not drawn flat
+        self.setupLights()
+
+        # Load the ring
+        self.ring = loader.loadModel('models/ring')
+        self.ring.reparentTo(render)
+
+        # Models that use skeletal animation are known as Actors instead of models
+        # Instead of just one file, the have one file for the main model, and an
+        # additional file for each playable animation.
+        # They are loaded using Actor.Actor instead of loader.LoadModel.
+        # The constructor takes the location of the main object as with a normal model
+        # and a dictionary (A fancy python structure that is like a lookup table)
+        # that contains names for animations, and paths to the appropriate
+        # files
+        self.robot1 = Actor.Actor('models/robot',
+                                  {'leftPunch': 'models/robot_left_punch',
+                                   'rightPunch': 'models/robot_right_punch',
+                                   'headUp': 'models/robot_head_up',
+                                   'headDown': 'models/robot_head_down'})
+
+        # Actors need to be positioned and parented like normal objects
+        self.robot1.setPosHprScale(-1, -2.5, 4, 45, 0, 0, 1.25, 1.25, 1.25)
+        self.robot1.reparentTo(render)
+
+        # We'll repeat the process for the second robot. The only thing that changes
+        # here is the robot's color and position
+        self.robot2 = Actor.Actor('models/robot',
+                                  {'leftPunch': 'models/robot_left_punch',
+                                   'rightPunch': 'models/robot_right_punch',
+                                   'headUp': 'models/robot_head_up',
+                                   'headDown': 'models/robot_head_down'})
+
+        # Set the properties of this robot
+        self.robot2.setPosHprScale(1, 1.5, 4, 225, 0, 0, 1.25, 1.25, 1.25)
+        self.robot2.setColor((.7, 0, 0, 1))
+        self.robot2.reparentTo(render)
+
+        # Now we define how the animated models will move. Animations are played
+        # through special intervals. In this case we use actor intervals in a
+        # sequence to play the part of the punch animation where the arm extends,
+        # call a function to check if the punch landed, and then play the part of the
+        # animation where the arm retracts
+
+        # Punch sequence for robot 1's left arm
+        self.robot1.punchLeft = Sequence(
+            # Interval for the outstreched animation
+            self.robot1.actorInterval('leftPunch', startFrame=1, endFrame=10),
+            # Function to check if the punch was successful
+            Func(self.checkPunch, 2),
+            # Interval for the retract animation
+            self.robot1.actorInterval('leftPunch', startFrame=11, endFrame=32))
+
+        # Punch sequence for robot 1's right arm
+        self.robot1.punchRight = Sequence(
+            self.robot1.actorInterval('rightPunch', startFrame=1, endFrame=10),
+            Func(self.checkPunch, 2),
+            self.robot1.actorInterval('rightPunch', startFrame=11, endFrame=32))
+
+        # Punch sequence for robot 2's left arm
+        self.robot2.punchLeft = Sequence(
+            self.robot2.actorInterval('leftPunch', startFrame=1, endFrame=10),
+            Func(self.checkPunch, 1),
+            self.robot2.actorInterval('leftPunch', startFrame=11, endFrame=32))
+
+        # Punch sequence for robot 2's right arm
+        self.robot2.punchRight = Sequence(
+            self.robot2.actorInterval('rightPunch', startFrame=1, endFrame=10),
+            Func(self.checkPunch, 1),
+            self.robot2.actorInterval('rightPunch', startFrame=11, endFrame=32))
+
+        # We use the same techinique to create a sequence for when a robot is knocked
+        # out where the head pops up, waits a while, and then resets
+
+        # Head animation for robot 1
+        self.robot1.resetHead = Sequence(
+            # Interval for the head going up. Since no start or end frames were given,
+            # the entire animation is played.
+            self.robot1.actorInterval('headUp'),
+            Wait(1.5),
+            # The head down animation was animated a little too quickly, so this will
+            # play it at 75% of it's normal speed
+            self.robot1.actorInterval('headDown', playRate=.75))
+
+        # Head animation for robot 2
+        self.robot2.resetHead = Sequence(
+            self.robot2.actorInterval('headUp'),
+            Wait(1.5),
+            self.robot2.actorInterval('headDown', playRate=.75))
+
+        # Now that we have defined the motion, we can define our key input.
+        # Each fist is bound to a key. When a key is pressed, self.tryPunch checks to
+        # make sure that the both robots have their heads down, and if they do it
+        # plays the given interval
+        self.accept('escape', sys.exit)
+        self.accept('a', self.tryPunch, [self.robot1.punchLeft])
+        self.accept('s', self.tryPunch, [self.robot1.punchRight])
+        self.accept('k', self.tryPunch, [self.robot2.punchLeft])
+        self.accept('l', self.tryPunch, [self.robot2.punchRight])
+
+    # tryPunch will play the interval passed to it only if
+    # neither robot has 'resetHead' playing (a head is up) AND
+    # the punch interval passed to it is not already playing
+    def tryPunch(self, interval):
+        if (not self.robot1.resetHead.isPlaying() and
+                not self.robot2.resetHead.isPlaying() and
+                not interval.isPlaying()):
+            interval.start()
+
+    # checkPunch will determine if a successful punch has been thrown
+    def checkPunch(self, robot):
+        if robot == 1:
+            # punch is directed to robot 1
+            # if robot 1 is playing'resetHead', do nothing
+            if self.robot1.resetHead.isPlaying():
+                return
+            # if robot 1 is not punching...
+            if (not self.robot1.punchLeft.isPlaying() and
+                    not self.robot1.punchRight.isPlaying()):
+                # ...15% chance of successful hit
+                if random() > .85:
+                    self.robot1.resetHead.start()
+            # Otherwise, only 5% chance of sucessful hit
+            elif random() > .95:
+                self.robot1.resetHead.start()
+        else:
+            # punch is directed to robot 2, same as above
+            if self.robot2.resetHead.isPlaying():
+                return
+            if (not self.robot2.punchLeft.isPlaying() and
+                    not self.robot2.punchRight.isPlaying()):
+                if random() > .85:
+                    self.robot2.resetHead.start()
+            elif random() > .95:
+                self.robot2.resetHead.start()
+
+    # This function sets up the lighting
+    def setupLights(self):
+        ambientLight = AmbientLight("ambientLight")
+        ambientLight.setColor((.8, .8, .75, 1))
+        directionalLight = DirectionalLight("directionalLight")
+        directionalLight.setDirection(LVector3(0, 0, -2.5))
+        directionalLight.setColor((0.9, 0.8, 0.9, 1))
+        render.setLight(render.attachNewNode(ambientLight))
+        render.setLight(render.attachNewNode(directionalLight))
+
+demo = BoxingRobotDemo()
+demo.run()

BIN
samples/boxing-robots/models/ring.egg.pz


BIN
samples/boxing-robots/models/robot.egg.pz


BIN
samples/boxing-robots/models/robot_head_down.egg.pz


BIN
samples/boxing-robots/models/robot_head_up.egg.pz


BIN
samples/boxing-robots/models/robot_left_punch.egg.pz


BIN
samples/boxing-robots/models/robot_right_punch.egg.pz


+ 188 - 0
samples/bump-mapping/main.py

@@ -0,0 +1,188 @@
+#!/usr/bin/env python
+#
+# Bump mapping is a way of making polygonal surfaces look
+# less flat.  This sample uses normal mapping for all
+# surfaces, and also parallax mapping for the column.
+#
+# This is a tutorial to show how to do normal mapping
+# in panda3d using the Shader Generator.
+
+from direct.showbase.ShowBase import ShowBase
+from panda3d.core import loadPrcFileData
+from panda3d.core import WindowProperties
+from panda3d.core import Filename, Shader
+from panda3d.core import AmbientLight, PointLight
+from panda3d.core import TextNode
+from panda3d.core import LPoint3, LVector3
+from direct.task.Task import Task
+from direct.actor.Actor import Actor
+from direct.gui.OnscreenText import OnscreenText
+from direct.showbase.DirectObject import DirectObject
+from direct.filter.CommonFilters import *
+import sys
+import os
+
+
+# Function to put instructions on the screen.
+def addInstructions(pos, msg):
+    return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05,
+                        shadow=(0, 0, 0, 1), parent=base.a2dTopLeft,
+                        pos=(0.08, -pos - 0.04), align=TextNode.ALeft)
+
+# Function to put title on the screen.
+def addTitle(text):
+    return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), scale=.08,
+                        parent=base.a2dBottomRight, align=TextNode.ARight,
+                        pos=(-0.1, 0.09), shadow=(0, 0, 0, 1))
+
+
+class BumpMapDemo(ShowBase):
+
+    def __init__(self):
+        # Configure the parallax mapping settings (these are just the defaults)
+        loadPrcFileData("", "parallax-mapping-samples 3\n"
+                            "parallax-mapping-scale 0.1")
+
+        # Initialize the ShowBase class from which we inherit, which will
+        # create a window and set up everything we need for rendering into it.
+        ShowBase.__init__(self)
+
+        # Check video card capabilities.
+        if not self.win.getGsg().getSupportsBasicShaders():
+            addTitle("Bump Mapping: "
+                "Video driver reports that Cg shaders are not supported.")
+            return
+
+        # Post the instructions
+        self.title = addTitle("Panda3D: Tutorial - Bump Mapping")
+        self.inst1 = addInstructions(0.06, "Press ESC to exit")
+        self.inst2 = addInstructions(0.12, "Move mouse to rotate camera")
+        self.inst3 = addInstructions(0.18, "Left mouse button: Move forwards")
+        self.inst4 = addInstructions(0.24, "Right mouse button: Move backwards")
+        self.inst5 = addInstructions(0.30, "Enter: Turn bump maps Off")
+
+        # Load the 'abstract room' model.  This is a model of an
+        # empty room containing a pillar, a pyramid, and a bunch
+        # of exaggeratedly bumpy textures.
+
+        self.room = loader.loadModel("models/abstractroom")
+        self.room.reparentTo(render)
+
+        # Make the mouse invisible, turn off normal mouse controls
+        self.disableMouse()
+        props = WindowProperties()
+        props.setCursorHidden(True)
+        self.win.requestProperties(props)
+        self.camLens.setFov(60)
+
+        # Set the current viewing target
+        self.focus = LVector3(55, -55, 20)
+        self.heading = 180
+        self.pitch = 0
+        self.mousex = 0
+        self.mousey = 0
+        self.last = 0
+        self.mousebtn = [0, 0, 0]
+
+        # Start the camera control task:
+        taskMgr.add(self.controlCamera, "camera-task")
+        self.accept("escape", sys.exit, [0])
+        self.accept("mouse1", self.setMouseBtn, [0, 1])
+        self.accept("mouse1-up", self.setMouseBtn, [0, 0])
+        self.accept("mouse2", self.setMouseBtn, [1, 1])
+        self.accept("mouse2-up", self.setMouseBtn, [1, 0])
+        self.accept("mouse3", self.setMouseBtn, [2, 1])
+        self.accept("mouse3-up", self.setMouseBtn, [2, 0])
+        self.accept("enter", self.toggleShader)
+        self.accept("j", self.rotateLight, [-1])
+        self.accept("k", self.rotateLight, [1])
+        self.accept("arrow_left", self.rotateCam, [-1])
+        self.accept("arrow_right", self.rotateCam, [1])
+
+        # Add a light to the scene.
+        self.lightpivot = render.attachNewNode("lightpivot")
+        self.lightpivot.setPos(0, 0, 25)
+        self.lightpivot.hprInterval(10, LPoint3(360, 0, 0)).loop()
+        plight = PointLight('plight')
+        plight.setColor((1, 1, 1, 1))
+        plight.setAttenuation(LVector3(0.7, 0.05, 0))
+        plnp = self.lightpivot.attachNewNode(plight)
+        plnp.setPos(45, 0, 0)
+        self.room.setLight(plnp)
+
+        # Add an ambient light
+        alight = AmbientLight('alight')
+        alight.setColor((0.2, 0.2, 0.2, 1))
+        alnp = render.attachNewNode(alight)
+        self.room.setLight(alnp)
+
+        # Create a sphere to denote the light
+        sphere = loader.loadModel("models/icosphere")
+        sphere.reparentTo(plnp)
+
+        # Tell Panda that it should generate shaders performing per-pixel
+        # lighting for the room.
+        self.room.setShaderAuto()
+
+        self.shaderenable = 1
+
+    def setMouseBtn(self, btn, value):
+        self.mousebtn[btn] = value
+
+    def rotateLight(self, offset):
+        self.lightpivot.setH(self.lightpivot.getH() + offset * 20)
+
+    def rotateCam(self, offset):
+        self.heading = self.heading - offset * 10
+
+    def toggleShader(self):
+        self.inst5.destroy()
+        if (self.shaderenable):
+            self.inst5 = addInstructions(0.30, "Enter: Turn bump maps On")
+            self.shaderenable = 0
+            self.room.setShaderOff()
+        else:
+            self.inst5 = addInstructions(0.30, "Enter: Turn bump maps Off")
+            self.shaderenable = 1
+            self.room.setShaderAuto()
+
+    def controlCamera(self, task):
+        # figure out how much the mouse has moved (in pixels)
+        md = self.win.getPointer(0)
+        x = md.getX()
+        y = md.getY()
+        if self.win.movePointer(0, 100, 100):
+            self.heading = self.heading - (x - 100) * 0.2
+            self.pitch = self.pitch - (y - 100) * 0.2
+        if self.pitch < -45:
+            self.pitch = -45
+        if self.pitch > 45:
+            self.pitch = 45
+        self.camera.setHpr(self.heading, self.pitch, 0)
+        dir = self.camera.getMat().getRow3(1)
+        elapsed = task.time - self.last
+        if self.last == 0:
+            elapsed = 0
+        if self.mousebtn[0]:
+            self.focus = self.focus + dir * elapsed * 30
+        if self.mousebtn[1] or self.mousebtn[2]:
+            self.focus = self.focus - dir * elapsed * 30
+        self.camera.setPos(self.focus - (dir * 5))
+        if self.camera.getX() < -59.0:
+            self.camera.setX(-59)
+        if self.camera.getX() > 59.0:
+            self.camera.setX(59)
+        if self.camera.getY() < -59.0:
+            self.camera.setY(-59)
+        if self.camera.getY() > 59.0:
+            self.camera.setY(59)
+        if self.camera.getZ() < 5.0:
+            self.camera.setZ(5)
+        if self.camera.getZ() > 45.0:
+            self.camera.setZ(45)
+        self.focus = self.camera.getPos() + (dir * 5)
+        self.last = task.time
+        return Task.cont
+
+demo = BumpMapDemo()
+demo.run()

+ 1671 - 0
samples/bump-mapping/models/abstractroom.egg

@@ -0,0 +1,1671 @@
+<CoordinateSystem> { Y-Up }
+
+<Comment> {
+  "maya2egg85 -o room.egg room.mb"
+}
+<Texture> phong3SG.tref3 {
+  layingrock-n.jpg
+  <Scalar> format { rgba }
+  <Scalar> alpha-file { layingrock-h.jpg }
+  <Scalar> wrapu { repeat }
+  <Scalar> wrapv { repeat }
+  <Scalar> minfilter { linear_mipmap_linear }
+  <Scalar> magfilter { linear }
+  <Scalar> envtype { normal_height }
+}
+<Texture> phong3SG {
+  layingrock-c.jpg
+  <Scalar> format { rgb }
+  <Scalar> wrapu { repeat }
+  <Scalar> wrapv { repeat }
+  <Scalar> minfilter { linear_mipmap_linear }
+  <Scalar> magfilter { linear }
+}
+<Texture> phong2SG.tref1 {
+  brick-n.jpg
+  <Scalar> format { rgb }
+  <Scalar> wrapu { repeat }
+  <Scalar> wrapv { repeat }
+  <Scalar> minfilter { linear_mipmap_linear }
+  <Scalar> magfilter { linear }
+  <Scalar> envtype { normal }
+}
+<Texture> phong2SG {
+  brick-c.jpg
+  <Scalar> format { rgb }
+  <Scalar> wrapu { repeat }
+  <Scalar> wrapv { repeat }
+  <Scalar> minfilter { linear_mipmap_linear }
+  <Scalar> magfilter { linear }
+}
+<Texture> phong1SG.tref2 {
+  fieldstone-n.jpg
+  <Scalar> format { rgb }
+  <Scalar> wrapu { repeat }
+  <Scalar> wrapv { repeat }
+  <Scalar> minfilter { linear_mipmap_linear }
+  <Scalar> magfilter { linear }
+  <Scalar> envtype { normal }
+}
+<Texture> phong1SG {
+  fieldstone-c.jpg
+  <Scalar> format { rgb }
+  <Scalar> wrapu { repeat }
+  <Scalar> wrapv { repeat }
+  <Scalar> minfilter { linear_mipmap_linear }
+  <Scalar> magfilter { linear }
+}
+<Group> groundPlane_transform {
+}
+<Group> polySurface2 {
+  <VertexPool> polySurfaceShape2.verts {
+    <Vertex> 0 {
+      -60 0 -60
+      <UV> {
+        3 3
+        <Tangent> { 0 -3.83044e-009 -1 }
+        <Binormal> { -1 -3.83044e-009 0 }
+      }
+      <Normal> { -3.83044e-009 1 -3.83044e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 1 {
+      -60 0 -60
+      <UV> {
+        -0.5 -0.5
+        <Tangent> { 1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 2 {
+      -60 0 -60
+      <UV> {
+        1.5 -0.5
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 3 {
+      -60 0 60
+      <UV> {
+        1.5 -0.5
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 -1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 4 {
+      -60 0 60
+      <UV> {
+        -2 3
+        <Tangent> { -5.15653e-008 4.13254e-011 -1 }
+        <Binormal> { -1 4.13254e-011 5.15653e-008 }
+      }
+      <Normal> { 4.13254e-011 1 4.13254e-011 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 5 {
+      -60 0 60
+      <UV> {
+        -0.5 -0.5
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 6 {
+      -60 50 -60
+      <UV> {
+        -0.5 1.5
+        <Tangent> { 1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 7 {
+      -60 50 -60
+      <UV> {
+        1.5 1.5
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 8 {
+      -60 50 60
+      <UV> {
+        1.5 1.5
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 -1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 9 {
+      -60 50 60
+      <UV> {
+        -0.5 1.5
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 10 {
+      -37.5 0 25
+      <UV> {
+        -1.49994 -1
+        <Tangent> { 0 0 1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 11 {
+      -37.5 0 25
+      <UV> {
+        2.50006 -1
+        <Tangent> { 0 0 1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 12 {
+      -37.5 50 25
+      <UV> {
+        -1.49994 2
+        <Tangent> { 0 0 1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 13 {
+      -37.5 50 25
+      <UV> {
+        2.50006 2
+        <Tangent> { 0 0 1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 14 {
+      -37.5 50 25
+      <UV> {
+        -0.541667 2.0625
+        <Tangent> { 0 0 -1 }
+        <Binormal> { -1 0 0 }
+      }
+      <Normal> { 0 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 15 {
+      -36.8882 0 21.1373
+      <UV> {
+        2.30006 -1
+        <Tangent> { -0.309017 0 0.951057 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.951057 0 -0.309017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 16 {
+      -36.8882 0 28.8627
+      <UV> {
+        -1.29994 -1
+        <Tangent> { 0.309017 0 0.951057 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.951057 0 0.309017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 17 {
+      -36.8882 50 21.1373
+      <UV> {
+        2.30006 2
+        <Tangent> { -0.309017 0 0.951057 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.951057 0 -0.309017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 18 {
+      -36.8882 50 21.1373
+      <UV> {
+        -0.38072 2.03701
+        <Tangent> { 6.17012e-006 0 -1 }
+        <Binormal> { -1 0 -6.17012e-006 }
+      }
+      <Normal> { 0 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 19 {
+      -36.8882 50 28.8627
+      <UV> {
+        -1.29994 2
+        <Tangent> { 0.309017 0 0.951057 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.951057 0 0.309017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 20 {
+      -36.8882 50 28.8627
+      <UV> {
+        -0.702613 2.03701
+        <Tangent> { -6.17012e-006 0 -1 }
+        <Binormal> { -1 0 6.17012e-006 }
+      }
+      <Normal> { 0 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 21 {
+      -35.1127 0 17.6527
+      <UV> {
+        2.10006 -1
+        <Tangent> { -0.587785 0 0.809017 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.809017 0 -0.587785 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 22 {
+      -35.1127 0 32.3473
+      <UV> {
+        -1.09994 -1
+        <Tangent> { 0.587785 0 0.809017 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.809017 0 0.587785 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 23 {
+      -35.1127 50 17.6527
+      <UV> {
+        2.10006 2
+        <Tangent> { -0.587785 0 0.809017 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.809017 0 -0.587785 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 24 {
+      -35.1127 50 17.6527
+      <UV> {
+        -0.235528 1.96303
+        <Tangent> { -1.03074e-007 0 -1 }
+        <Binormal> { -1 0 1.03074e-007 }
+      }
+      <Normal> { 0 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 25 {
+      -35.1127 50 32.3473
+      <UV> {
+        -1.09994 2
+        <Tangent> { 0.587785 0 0.809017 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.809017 0 0.587785 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 26 {
+      -35.1127 50 32.3473
+      <UV> {
+        -0.847805 1.96303
+        <Tangent> { 1.03075e-007 0 -1 }
+        <Binormal> { -1 0 -1.03075e-007 }
+      }
+      <Normal> { 0 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 27 {
+      -32.3473 0 14.8873
+      <UV> {
+        1.90006 -1
+        <Tangent> { -0.809017 0 0.587786 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.587786 0 -0.809017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 28 {
+      -32.3473 0 35.1127
+      <UV> {
+        -0.899936 -1
+        <Tangent> { 0.809017 0 0.587785 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.587785 0 0.809017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 29 {
+      -32.3473 0 35.1127
+      <UV> {
+        -0.96303 1.84781
+        <Tangent> { 1.52608e-007 4.13254e-011 -1 }
+        <Binormal> { -1 4.13254e-011 -1.52608e-007 }
+      }
+      <Normal> { 4.13254e-011 1 4.13254e-011 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 30 {
+      -32.3473 50 14.8873
+      <UV> {
+        1.90006 2
+        <Tangent> { -0.809017 0 0.587786 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.587786 0 -0.809017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 31 {
+      -32.3473 50 14.8873
+      <UV> {
+        -0.120303 1.84781
+        <Tangent> { -8.0634e-006 0 -1 }
+        <Binormal> { -1 0 8.0634e-006 }
+      }
+      <Normal> { 0 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 32 {
+      -32.3473 50 35.1127
+      <UV> {
+        -0.899936 2
+        <Tangent> { 0.809017 0 0.587785 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.587785 0 0.809017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 33 {
+      -28.8627 0 13.1118
+      <UV> {
+        1.70006 -1
+        <Tangent> { -0.951057 0 0.309017 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.309017 0 -0.951057 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 34 {
+      -28.8627 0 36.8882
+      <UV> {
+        -0.699936 -1
+        <Tangent> { 0.951057 0 0.309016 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.309016 0 0.951057 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 35 {
+      -28.8627 50 13.1118
+      <UV> {
+        1.70006 2
+        <Tangent> { -0.951057 0 0.309017 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.309017 0 -0.951057 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 36 {
+      -28.8627 50 13.1118
+      <UV> {
+        -0.0463246 1.70261
+        <Tangent> { 3.81471e-007 0 -1 }
+        <Binormal> { -1 0 -3.81471e-007 }
+      }
+      <Normal> { 0 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 37 {
+      -28.8627 50 36.8882
+      <UV> {
+        -0.699936 2
+        <Tangent> { 0.951057 0 0.309016 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -0.309016 0 0.951057 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 38 {
+      -28.8627 50 36.8882
+      <UV> {
+        -1.03701 1.70261
+        <Tangent> { -3.81475e-007 0 -1 }
+        <Binormal> { -1 -4.89652e-008 3.81475e-007 }
+      }
+      <Normal> { 4.89652e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 39 {
+      -25 0 12.5
+      <UV> {
+        1.50006 -1
+        <Tangent> { -1 0 -6.03476e-007 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 6.03476e-007 0 -1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 40 {
+      -25 0 37.5
+      <UV> {
+        -0.499936 -1
+        <Tangent> { 1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 41 {
+      -25 50 12.5
+      <UV> {
+        -0.0208334 1.54167
+        <Tangent> { 1.16912e-005 0 -1 }
+        <Binormal> { -1 0 -1.16912e-005 }
+      }
+      <Normal> { 0 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 42 {
+      -25 50 12.5
+      <UV> {
+        1.50006 2
+        <Tangent> { -1 0 -6.03476e-007 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 6.03476e-007 0 -1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 43 {
+      -25 50 37.5
+      <UV> {
+        -0.499936 2
+        <Tangent> { 1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 44 {
+      -25 50 37.5
+      <UV> {
+        -1.0625 1.54167
+        <Tangent> { -1.16911e-005 0 -1 }
+        <Binormal> { -1 -4.89652e-008 1.16911e-005 }
+      }
+      <Normal> { 4.89652e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 45 {
+      -21.1373 0 13.1118
+      <UV> {
+        1.30006 -1
+        <Tangent> { -0.951057 0 -0.309017 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.309017 0 -0.951057 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 46 {
+      -21.1373 0 36.8882
+      <UV> {
+        -0.299936 -1
+        <Tangent> { 0.951057 0 -0.309016 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.309016 0 0.951057 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 47 {
+      -21.1373 50 13.1118
+      <UV> {
+        -0.0463246 1.38072
+        <Tangent> { -1.09281e-005 0 -1 }
+        <Binormal> { -1 0 1.09281e-005 }
+      }
+      <Normal> { 0 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 48 {
+      -21.1373 50 13.1118
+      <UV> {
+        1.30006 2
+        <Tangent> { -0.951057 0 -0.309017 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.309017 0 -0.951057 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 49 {
+      -21.1373 50 36.8882
+      <UV> {
+        -1.03701 1.38072
+        <Tangent> { 1.09282e-005 0 -1 }
+        <Binormal> { -1 -4.89652e-008 -1.09282e-005 }
+      }
+      <Normal> { 4.89652e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 50 {
+      -21.1373 50 36.8882
+      <UV> {
+        -0.299936 2
+        <Tangent> { 0.951057 0 -0.309016 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.309016 0 0.951057 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 51 {
+      -17.6527 0 14.8873
+      <UV> {
+        1.10006 -1
+        <Tangent> { -0.809017 0 -0.587785 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.587785 0 -0.809017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 52 {
+      -17.6527 0 35.1127
+      <UV> {
+        -0.0999364 -1
+        <Tangent> { 0.809017 0 -0.587785 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.587785 0 0.809017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 53 {
+      -17.6527 50 14.8873
+      <UV> {
+        -0.120304 1.23553
+        <Tangent> { -7.25597e-007 0 -1 }
+        <Binormal> { -1 0 7.25597e-007 }
+      }
+      <Normal> { 0 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 54 {
+      -17.6527 50 14.8873
+      <UV> {
+        1.10006 2
+        <Tangent> { -0.809017 0 -0.587785 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.587785 0 -0.809017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 55 {
+      -17.6527 50 35.1127
+      <UV> {
+        -0.96303 1.23553
+        <Tangent> { 7.25593e-007 0 -1 }
+        <Binormal> { -1 -4.89652e-008 -7.25593e-007 }
+      }
+      <Normal> { 4.89652e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 56 {
+      -17.6527 50 35.1127
+      <UV> {
+        -0.0999364 2
+        <Tangent> { 0.809017 0 -0.587785 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.587785 0 0.809017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 57 {
+      -14.8873 0 17.6527
+      <UV> {
+        -0.235528 1.1203
+        <Tangent> { -5.45099e-007 4.13254e-011 -1 }
+        <Binormal> { -1 4.13254e-011 5.45099e-007 }
+      }
+      <Normal> { 4.13254e-011 1 4.13254e-011 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 58 {
+      -14.8873 0 17.6527
+      <UV> {
+        0.900064 -1
+        <Tangent> { -0.587785 0 -0.809017 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.809017 0 -0.587785 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 59 {
+      -14.8873 0 32.3473
+      <UV> {
+        0.100064 -1
+        <Tangent> { 0.587785 0 -0.809017 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.809017 0 0.587785 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 60 {
+      -14.8873 50 17.6527
+      <UV> {
+        0.900064 2
+        <Tangent> { -0.587785 0 -0.809017 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.809017 0 -0.587785 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 61 {
+      -14.8873 50 32.3473
+      <UV> {
+        -0.847805 1.1203
+        <Tangent> { -1.79128e-006 0 -1 }
+        <Binormal> { -1 -4.89652e-008 1.79128e-006 }
+      }
+      <Normal> { 4.89652e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 62 {
+      -14.8873 50 32.3473
+      <UV> {
+        0.100064 2
+        <Tangent> { 0.587785 0 -0.809017 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.809017 0 0.587785 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 63 {
+      -13.1118 0 21.1373
+      <UV> {
+        0.700064 -1
+        <Tangent> { -0.309017 0 -0.951057 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.951057 0 -0.309017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 64 {
+      -13.1118 0 28.8627
+      <UV> {
+        0.300064 -1
+        <Tangent> { 0.309017 0 -0.951057 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.951057 0 0.309017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 65 {
+      -13.1118 50 21.1373
+      <UV> {
+        -0.38072 1.04633
+        <Tangent> { 3.58256e-006 0 -1 }
+        <Binormal> { -1 -4.89652e-008 -3.58256e-006 }
+      }
+      <Normal> { 4.89652e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 66 {
+      -13.1118 50 21.1373
+      <UV> {
+        0.700064 2
+        <Tangent> { -0.309017 0 -0.951057 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.951057 0 -0.309017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 67 {
+      -13.1118 50 28.8627
+      <UV> {
+        -0.702613 1.04633
+        <Tangent> { -3.58256e-006 0 -1 }
+        <Binormal> { -1 -4.89652e-008 3.58256e-006 }
+      }
+      <Normal> { 4.89652e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 68 {
+      -13.1118 50 28.8627
+      <UV> {
+        0.300064 2
+        <Tangent> { 0.309017 0 -0.951057 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0.951057 0 0.309017 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 69 {
+      -12.5 0 25
+      <UV> {
+        0.500064 -1
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 70 {
+      -12.5 50 25
+      <UV> {
+        -0.541667 1.02083
+        <Tangent> { 0 0 -1 }
+        <Binormal> { -1 -4.89652e-008 0 }
+      }
+      <Normal> { 4.89652e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 71 {
+      -12.5 50 25
+      <UV> {
+        0.500064 2
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 72 {
+      5 0 -5
+      <UV> {
+        0.708333 0.291667
+        <Tangent> { -1.84039e-007 4.13254e-011 -1 }
+        <Binormal> { -1 4.13254e-011 1.84039e-007 }
+      }
+      <Normal> { 4.13254e-011 1 4.13254e-011 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 73 {
+      5 1.19209e-006 -45
+      <UV> {
+        1.5 -0.5
+        <Tangent> { -1 -1.49012e-008 0 }
+        <Binormal> { -1.49012e-008 1 0 }
+      }
+      <Normal> { 0 0 -1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 74 {
+      5 10 -45
+      <UV> {
+        1.5 0.166667
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 -1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 75 {
+      5 10 -45
+      <UV> {
+        1.5 0.166667
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 0.707107 0.707107 }
+      }
+      <Normal> { 0 0.707107 -0.707107 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 76 {
+      25 30 -25
+      <UV> {
+        0.5 1.5
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 0.707107 0.707107 }
+      }
+      <Normal> { 0 0.707107 -0.707107 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 77 {
+      25 30 -25
+      <UV> {
+        0.5 1.5
+        <Tangent> { 0 0 -1 }
+        <Binormal> { -0.707107 0.707107 0 }
+      }
+      <Normal> { 0.707107 0.707107 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 78 {
+      45 0 -45
+      <UV> {
+        -0.5 -0.5
+        <Tangent> { -1 -1.49012e-008 0 }
+        <Binormal> { -1.49012e-008 1 0 }
+      }
+      <Normal> { 0 0 -1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 79 {
+      45 0 -45
+      <UV> {
+        2.375 -1.375
+        <Tangent> { -2.98023e-008 4.13254e-011 -1 }
+        <Binormal> { -1 4.13254e-011 2.98023e-008 }
+      }
+      <Normal> { 4.13254e-011 1 4.13254e-011 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 80 {
+      45 0 -45
+      <UV> {
+        1.5 -0.5
+        <Tangent> { 0 1.49012e-008 -1 }
+        <Binormal> { 0 1 1.49012e-008 }
+      }
+      <Normal> { 1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 81 {
+      45 1.19209e-006 -5
+      <UV> {
+        -0.5 -0.5
+        <Tangent> { 0 1.49012e-008 -1 }
+        <Binormal> { 0 1 1.49012e-008 }
+      }
+      <Normal> { 1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 82 {
+      45 10 -45
+      <UV> {
+        -0.5 0.166667
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 -1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 83 {
+      45 10 -45
+      <UV> {
+        -0.5 0.166667
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 0.707107 0.707107 }
+      }
+      <Normal> { 0 0.707107 -0.707107 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 84 {
+      45 10 -45
+      <UV> {
+        1.5 0.166667
+        <Tangent> { 0 0 -1 }
+        <Binormal> { -0.707107 0.707107 0 }
+      }
+      <Normal> { 0.707107 0.707107 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 85 {
+      45 10 -45
+      <UV> {
+        1.5 0.166667
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 86 {
+      45 10 -5
+      <UV> {
+        -0.5 0.166667
+        <Tangent> { 0 0 -1 }
+        <Binormal> { -0.707107 0.707107 0 }
+      }
+      <Normal> { 0.707107 0.707107 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 87 {
+      45 10 -5
+      <UV> {
+        -0.5 0.166667
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 88 {
+      60 0 -60
+      <UV> {
+        1.5 1.5
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 -1 0 }
+      }
+      <Normal> { -1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 89 {
+      60 0 -60
+      <UV> {
+        1.5 -0.5
+        <Tangent> { 1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 90 {
+      60 0 -60
+      <UV> {
+        3 -2
+        <Tangent> { -1.19209e-008 4.13254e-011 -1 }
+        <Binormal> { -1 4.13254e-011 1.19209e-008 }
+      }
+      <Normal> { 4.13254e-011 1 4.13254e-011 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 91 {
+      60 0 60
+      <UV> {
+        -0.5 1.5
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 -1 0 }
+      }
+      <Normal> { -1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 92 {
+      60 0 60
+      <UV> {
+        -0.5 -0.5
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 -1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 93 {
+      60 0 60
+      <UV> {
+        -2 -2
+        <Tangent> { 0 3.91309e-009 -1 }
+        <Binormal> { -1 3.91309e-009 0 }
+      }
+      <Normal> { 3.91309e-009 1 3.91309e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 94 {
+      60 50 -60
+      <UV> {
+        1.5 -0.5
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 -1 0 }
+      }
+      <Normal> { -1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 95 {
+      60 50 -60
+      <UV> {
+        1.5 1.5
+        <Tangent> { 1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 96 {
+      60 50 60
+      <UV> {
+        -0.5 -0.5
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 -1 0 }
+      }
+      <Normal> { -1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 97 {
+      60 50 60
+      <UV> {
+        -0.5 1.5
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 -1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 98 {
+      -60 50 -60
+      <UV> {
+        3 3
+        <Tangent> { 0 0 -1 }
+        <Binormal> { -1 0 0 }
+      }
+      <Normal> { 0 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 99 {
+      -60 50 60
+      <UV> {
+        -2 3
+        <Tangent> { -1.18287e-008 0 -1 }
+        <Binormal> { -1 -2.44826e-008 1.18287e-008 }
+      }
+      <Normal> { 2.44826e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 100 {
+      -37.5 0 25
+      <UV> {
+        -0.541667 2.0625
+        <Tangent> { 0 -3.83044e-009 -1 }
+        <Binormal> { -1 -3.83044e-009 0 }
+      }
+      <Normal> { -3.83044e-009 1 -3.83044e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 101 {
+      -36.8882 0 21.1373
+      <UV> {
+        -0.38072 2.03701
+        <Tangent> { -2.784e-007 -3.83044e-009 -1 }
+        <Binormal> { -1 -3.83044e-009 2.784e-007 }
+      }
+      <Normal> { -3.83044e-009 1 -3.83044e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 102 {
+      -36.8882 0 28.8627
+      <UV> {
+        -0.702613 2.03701
+        <Tangent> { 2.784e-007 -3.83044e-009 -1 }
+        <Binormal> { -1 -3.83044e-009 -2.784e-007 }
+      }
+      <Normal> { -3.83044e-009 1 -3.83044e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 103 {
+      -35.1127 0 17.6527
+      <UV> {
+        -0.235528 1.96303
+        <Tangent> { -1.03074e-007 -3.83044e-009 -1 }
+        <Binormal> { -1 -3.83044e-009 1.03074e-007 }
+      }
+      <Normal> { -3.83044e-009 1 -3.83044e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 104 {
+      -35.1127 0 32.3473
+      <UV> {
+        -0.847805 1.96303
+        <Tangent> { 1.03075e-007 -3.83044e-009 -1 }
+        <Binormal> { -1 -3.83044e-009 -1.03075e-007 }
+      }
+      <Normal> { -3.83044e-009 1 -3.83044e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 105 {
+      -32.3473 0 14.8873
+      <UV> {
+        -0.120303 1.84781
+        <Tangent> { -8.0634e-006 -3.83047e-009 -1 }
+        <Binormal> { -1 -3.83041e-009 8.0634e-006 }
+      }
+      <Normal> { -3.83044e-009 1 -3.83044e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 106 {
+      -32.3473 50 35.1127
+      <UV> {
+        -0.96303 1.84781
+        <Tangent> { 1.52608e-007 0 -1 }
+        <Binormal> { -1 -2.44826e-008 -1.52608e-007 }
+      }
+      <Normal> { 2.44826e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 107 {
+      -28.8627 0 13.1118
+      <UV> {
+        -0.0463246 1.70261
+        <Tangent> { 3.81471e-007 -3.83044e-009 -1 }
+        <Binormal> { -1 -3.83044e-009 -3.81471e-007 }
+      }
+      <Normal> { -3.83044e-009 1 -3.83044e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 108 {
+      -28.8627 0 36.8882
+      <UV> {
+        -1.03701 1.70261
+        <Tangent> { -3.81475e-007 3.91309e-009 -1 }
+        <Binormal> { -1 3.91309e-009 3.81475e-007 }
+      }
+      <Normal> { 3.91309e-009 1 3.91309e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 109 {
+      -25 0 12.5
+      <UV> {
+        -0.0208334 1.54167
+        <Tangent> { 1.16912e-005 -3.83039e-009 -1 }
+        <Binormal> { -1 -3.83048e-009 -1.16912e-005 }
+      }
+      <Normal> { -3.83044e-009 1 -3.83044e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 110 {
+      -25 0 37.5
+      <UV> {
+        -1.0625 1.54167
+        <Tangent> { -1.16911e-005 3.91314e-009 -1 }
+        <Binormal> { -1 3.91304e-009 1.16911e-005 }
+      }
+      <Normal> { 3.91309e-009 1 3.91309e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 111 {
+      -21.1373 0 13.1118
+      <UV> {
+        -0.0463246 1.38072
+        <Tangent> { -1.09281e-005 -3.83048e-009 -1 }
+        <Binormal> { -1 -3.8304e-009 1.09281e-005 }
+      }
+      <Normal> { -3.83044e-009 1 -3.83044e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 112 {
+      -21.1373 0 36.8882
+      <UV> {
+        -1.03701 1.38072
+        <Tangent> { 1.09282e-005 3.91305e-009 -1 }
+        <Binormal> { -1 3.91313e-009 -1.09282e-005 }
+      }
+      <Normal> { 3.91309e-009 1 3.91309e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 113 {
+      -17.6527 0 14.8873
+      <UV> {
+        -0.120304 1.23553
+        <Tangent> { -7.25597e-007 -3.83044e-009 -1 }
+        <Binormal> { -1 -3.83044e-009 7.25597e-007 }
+      }
+      <Normal> { -3.83044e-009 1 -3.83044e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 114 {
+      -17.6527 0 35.1127
+      <UV> {
+        -0.96303 1.23553
+        <Tangent> { 7.25593e-007 3.91309e-009 -1 }
+        <Binormal> { -1 3.91309e-009 -7.25593e-007 }
+      }
+      <Normal> { 3.91309e-009 1 3.91309e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 115 {
+      -14.8873 0 32.3473
+      <UV> {
+        -0.847805 1.1203
+        <Tangent> { -1.79128e-006 3.9131e-009 -1 }
+        <Binormal> { -1 3.91308e-009 1.79128e-006 }
+      }
+      <Normal> { 3.91309e-009 1 3.91309e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 116 {
+      -14.8873 50 17.6527
+      <UV> {
+        -0.235528 1.1203
+        <Tangent> { -4.96743e-007 0 -1 }
+        <Binormal> { -1 -2.44826e-008 4.96743e-007 }
+      }
+      <Normal> { 2.44826e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 117 {
+      -13.1118 0 21.1373
+      <UV> {
+        -0.38072 1.04633
+        <Tangent> { 3.58256e-006 3.91308e-009 -1 }
+        <Binormal> { -1 3.9131e-009 -3.58256e-006 }
+      }
+      <Normal> { 3.91309e-009 1 3.91309e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 118 {
+      -13.1118 0 28.8627
+      <UV> {
+        -0.702613 1.04633
+        <Tangent> { -3.58256e-006 3.9131e-009 -1 }
+        <Binormal> { -1 3.91308e-009 3.58256e-006 }
+      }
+      <Normal> { 3.91309e-009 1 3.91309e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 119 {
+      -12.5 0 25
+      <UV> {
+        -0.541667 1.02083
+        <Tangent> { 0 3.91309e-009 -1 }
+        <Binormal> { -1 3.91309e-009 0 }
+      }
+      <Normal> { 3.91309e-009 1 3.91309e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 120 {
+      5 0 -5
+      <UV> {
+        -0.5 -0.5
+        <Tangent> { 0 -1.49012e-008 -1 }
+        <Binormal> { 0 1 -1.49012e-008 }
+      }
+      <Normal> { -1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 121 {
+      5 0 -5
+      <UV> {
+        1.5 -0.5
+        <Tangent> { -1 1.49012e-008 0 }
+        <Binormal> { 1.49012e-008 1 0 }
+      }
+      <Normal> { 0 0 1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 122 {
+      5 1.19209e-006 -45
+      <UV> {
+        1.5 -0.5
+        <Tangent> { 0 -1.49012e-008 -1 }
+        <Binormal> { 0 1 -1.49012e-008 }
+      }
+      <Normal> { -1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 123 {
+      5 1.19209e-006 -45
+      <UV> {
+        2.375 0.291667
+        <Tangent> { 0 -3.83044e-009 -1 }
+        <Binormal> { -1 -3.83044e-009 0 }
+      }
+      <Normal> { -3.83044e-009 1 -3.83044e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 124 {
+      5 10 -45
+      <UV> {
+        1.5 0.166667
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 125 {
+      5 10 -45
+      <UV> {
+        1.5 0.166667
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0.707107 0.707107 0 }
+      }
+      <Normal> { -0.707107 0.707107 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 126 {
+      5 10 -5
+      <UV> {
+        -0.5 0.166667
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { -1 0 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 127 {
+      5 10 -5
+      <UV> {
+        -0.5 0.166667
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0.707107 0.707107 0 }
+      }
+      <Normal> { -0.707107 0.707107 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 128 {
+      5 10 -5
+      <UV> {
+        1.5 0.166667
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 129 {
+      5 10 -5
+      <UV> {
+        1.5 0.166667
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 0.707107 -0.707107 }
+      }
+      <Normal> { 0 0.707107 0.707107 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 130 {
+      25 30 -25
+      <UV> {
+        0.5 1.5
+        <Tangent> { 0 0 -1 }
+        <Binormal> { 0.707107 0.707107 0 }
+      }
+      <Normal> { -0.707107 0.707107 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 131 {
+      25 30 -25
+      <UV> {
+        0.5 1.5
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 0.707107 -0.707107 }
+      }
+      <Normal> { 0 0.707107 0.707107 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 132 {
+      45 1.19209e-006 -5
+      <UV> {
+        -0.5 -0.5
+        <Tangent> { -1 1.49012e-008 0 }
+        <Binormal> { 1.49012e-008 1 0 }
+      }
+      <Normal> { 0 0 1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 133 {
+      45 1.19209e-006 -5
+      <UV> {
+        0.708333 -1.375
+        <Tangent> { 0 3.91309e-009 -1 }
+        <Binormal> { -1 3.91309e-009 0 }
+      }
+      <Normal> { 3.91309e-009 1 3.91309e-009 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 134 {
+      45 10 -5
+      <UV> {
+        -0.5 0.166667
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 1 0 }
+      }
+      <Normal> { 0 0 1 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 135 {
+      45 10 -5
+      <UV> {
+        -0.5 0.166667
+        <Tangent> { -1 0 0 }
+        <Binormal> { 0 0.707107 -0.707107 }
+      }
+      <Normal> { 0 0.707107 0.707107 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 136 {
+      60 50 -60
+      <UV> {
+        3 -2
+        <Tangent> { -6.29762e-008 0 -1 }
+        <Binormal> { -1 -2.44826e-008 6.29762e-008 }
+      }
+      <Normal> { 2.44826e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+    <Vertex> 137 {
+      60 50 60
+      <UV> {
+        -2 -2
+        <Tangent> { 0 0 -1 }
+        <Binormal> { -1 -4.89652e-008 0 }
+      }
+      <Normal> { 4.89652e-008 -1 0 }
+      <RGBA> { 1 1 1 1 }
+    }
+  }
+  <Polygon> {
+    <Normal> { 0 0.707107 -0.707107 }
+    <TRef> { phong2SG }
+    <TRef> { phong2SG.tref1 }
+    <VertexRef> { 75 76 83 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -0.707107 0.707107 0 }
+    <TRef> { phong2SG }
+    <TRef> { phong2SG.tref1 }
+    <VertexRef> { 127 130 125 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0 0.707107 0.707107 }
+    <TRef> { phong2SG }
+    <TRef> { phong2SG.tref1 }
+    <VertexRef> { 135 131 129 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0.707107 0.707107 0 }
+    <TRef> { phong2SG }
+    <TRef> { phong2SG.tref1 }
+    <VertexRef> { 84 77 86 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0 0 -1 }
+    <TRef> { phong2SG }
+    <TRef> { phong2SG.tref1 }
+    <VertexRef> { 78 73 74 82 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -1 0 0 }
+    <TRef> { phong2SG }
+    <TRef> { phong2SG.tref1 }
+    <VertexRef> { 122 120 126 124 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0 0 1 }
+    <TRef> { phong2SG }
+    <TRef> { phong2SG.tref1 }
+    <VertexRef> { 121 132 134 128 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 1 0 0 }
+    <TRef> { phong2SG }
+    <TRef> { phong2SG.tref1 }
+    <VertexRef> { 81 80 85 87 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -1 0 0 }
+    <TRef> { phong1SG }
+    <TRef> { phong1SG.tref2 }
+    <VertexRef> { 91 96 94 88 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 4.89652e-008 -1 0 }
+    <TRef> { phong1SG }
+    <TRef> { phong1SG.tref2 }
+    <VertexRef> {
+      49 55 61 67 70 65 116 136 137 99 106 38 44
+      <Ref> { polySurfaceShape2.verts }
+    }
+  }
+  <Polygon> {
+    <Normal> { 0 -1 0 }
+    <TRef> { phong1SG }
+    <TRef> { phong1SG.tref2 }
+    <VertexRef> {
+      99 98 136 116 53 47 41 36 31 24 18 14 20 26 106
+      <Ref> { polySurfaceShape2.verts }
+    }
+  }
+  <Polygon> {
+    <Normal> { 1 0 0 }
+    <TRef> { phong1SG }
+    <TRef> { phong1SG.tref2 }
+    <VertexRef> { 9 5 2 7 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 3.91309e-009 1 3.91309e-009 }
+    <TRef> { phong1SG }
+    <TRef> { phong1SG.tref2 }
+    <VertexRef> {
+      93 90 79 133 72 57 117 119 118 115 114 112 110 108 29 4
+      <Ref> { polySurfaceShape2.verts }
+    }
+  }
+  <Polygon> {
+    <Normal> { -3.83044e-009 1 -3.83044e-009 }
+    <TRef> { phong1SG }
+    <TRef> { phong1SG.tref2 }
+    <VertexRef> {
+      123 79 90 0 4 29 104 102 100 101 103 105 107 109 111 113 57 72
+      <Ref> { polySurfaceShape2.verts }
+    }
+  }
+  <Polygon> {
+    <Normal> { 0 0 1 }
+    <TRef> { phong1SG }
+    <TRef> { phong1SG.tref2 }
+    <VertexRef> { 89 95 6 1 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0 0 -1 }
+    <TRef> { phong1SG }
+    <TRef> { phong1SG.tref2 }
+    <VertexRef> { 3 8 97 92 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -0.987688 0 0.156435 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 10 16 19 12 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -0.987688 0 -0.156435 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 15 11 13 17 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -0.891007 0 -0.45399 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 21 15 17 23 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -0.707107 0 -0.707107 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 27 21 23 30 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -0.453991 0 -0.891006 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 33 27 30 35 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -0.156433 0 -0.987689 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 39 33 35 42 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0.156434 0 -0.987688 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 45 39 42 48 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0.453991 0 -0.891007 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 51 45 48 54 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0.707107 0 -0.707107 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 58 51 54 60 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0.891007 0 -0.453991 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 63 58 60 66 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0.987688 0 -0.156434 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 69 63 66 71 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0.987688 0 0.156434 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 64 69 71 68 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0.891007 0 0.453991 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 59 64 68 62 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0.707107 0 0.707106 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 52 59 62 56 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0.45399 0 0.891007 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 46 52 56 50 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { 0.156434 0 0.987688 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 40 46 50 43 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -0.156434 0 0.987688 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 34 40 43 37 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -0.45399 0 0.891007 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 28 34 37 32 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -0.707107 0 0.707107 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 22 28 32 25 <Ref> { polySurfaceShape2.verts } }
+  }
+  <Polygon> {
+    <Normal> { -0.891007 0 0.45399 }
+    <TRef> { phong3SG }
+    <TRef> { phong3SG.tref3 }
+    <VertexRef> { 16 22 25 19 <Ref> { polySurfaceShape2.verts } }
+  }
+}

BIN
samples/bump-mapping/models/abstractroom.mb


BIN
samples/bump-mapping/models/brick-c.jpg


BIN
samples/bump-mapping/models/brick-n.jpg


BIN
samples/bump-mapping/models/fieldstone-c.jpg


BIN
samples/bump-mapping/models/fieldstone-n.jpg


+ 497 - 0
samples/bump-mapping/models/icosphere.egg

@@ -0,0 +1,497 @@
+<CoordinateSystem> { Z-Up }
+
+<Comment> {
+  "egg-trans -F icosphere.egg -o icosphere.egg"
+}
+<Group> Icosphere {
+  <VertexPool> Icosphere {
+    <Vertex> 0 {
+      0 0 -1
+      <Normal> { 0 0 -1 }
+    }
+    <Vertex> 1 {
+      0.425323 -0.309011 -0.850654
+      <Normal> { 0.425306 -0.309 -0.850642 }
+    }
+    <Vertex> 2 {
+      -0.162456 -0.499995 -0.850654
+      <Normal> { -0.16245 -0.499985 -0.850642 }
+    }
+    <Vertex> 3 {
+      0.723607 -0.525725 -0.44722
+      <Normal> { 0.723594 -0.525712 -0.447188 }
+    }
+    <Vertex> 4 {
+      0.850648 0 -0.525736
+      <Normal> { 0.850642 0 -0.525712 }
+    }
+    <Vertex> 5 {
+      -0.52573 0 -0.850652
+      <Normal> { -0.525712 0 -0.850642 }
+    }
+    <Vertex> 6 {
+      -0.162456 0.499995 -0.850654
+      <Normal> { -0.16245 0.499985 -0.850642 }
+    }
+    <Vertex> 7 {
+      0.425323 0.309011 -0.850654
+      <Normal> { 0.425306 0.309 -0.850642 }
+    }
+    <Vertex> 8 {
+      0.951058 -0.309013 0
+      <Normal> { 0.951048 -0.309 0 }
+    }
+    <Vertex> 9 {
+      -0.276388 -0.850649 -0.44722
+      <Normal> { -0.276376 -0.850642 -0.447218 }
+    }
+    <Vertex> 10 {
+      0.262869 -0.809012 -0.525738
+      <Normal> { 0.262856 -0.808985 -0.525712 }
+    }
+    <Vertex> 11 {
+      0 -1 0
+      <Normal> { 0 -1 0 }
+    }
+    <Vertex> 12 {
+      -0.894426 0 -0.447216
+      <Normal> { -0.894406 0 -0.447188 }
+    }
+    <Vertex> 13 {
+      -0.688189 -0.499997 -0.525736
+      <Normal> { -0.688162 -0.499985 -0.525712 }
+    }
+    <Vertex> 14 {
+      -0.951058 -0.309013 0
+      <Normal> { -0.951048 -0.309 0 }
+    }
+    <Vertex> 15 {
+      -0.276388 0.850649 -0.44722
+      <Normal> { -0.276376 0.850642 -0.447218 }
+    }
+    <Vertex> 16 {
+      -0.688189 0.499997 -0.525736
+      <Normal> { -0.688162 0.499985 -0.525712 }
+    }
+    <Vertex> 17 {
+      -0.587786 0.809017 0
+      <Normal> { -0.587756 0.809015 0 }
+    }
+    <Vertex> 18 {
+      0.723607 0.525725 -0.44722
+      <Normal> { 0.723594 0.525712 -0.447188 }
+    }
+    <Vertex> 19 {
+      0.262869 0.809012 -0.525738
+      <Normal> { 0.262856 0.808985 -0.525712 }
+    }
+    <Vertex> 20 {
+      0.587786 0.809017 0
+      <Normal> { 0.587756 0.809015 0 }
+    }
+    <Vertex> 21 {
+      0.587786 -0.809017 0
+      <Normal> { 0.587756 -0.809015 0 }
+    }
+    <Vertex> 22 {
+      -0.587786 -0.809017 0
+      <Normal> { -0.587756 -0.809015 0 }
+    }
+    <Vertex> 23 {
+      -0.951058 0.309013 0
+      <Normal> { -0.951048 0.309 0 }
+    }
+    <Vertex> 24 {
+      0 1 0
+      <Normal> { 0 1 0 }
+    }
+    <Vertex> 25 {
+      0.951058 0.309013 0
+      <Normal> { 0.951048 0.309 0 }
+    }
+    <Vertex> 26 {
+      0.276388 -0.850649 0.44722
+      <Normal> { 0.276376 -0.850642 0.447218 }
+    }
+    <Vertex> 27 {
+      0.688189 -0.499997 0.525736
+      <Normal> { 0.688162 -0.499985 0.525712 }
+    }
+    <Vertex> 28 {
+      0.162456 -0.499995 0.850654
+      <Normal> { 0.16245 -0.499985 0.850642 }
+    }
+    <Vertex> 29 {
+      -0.723607 -0.525725 0.44722
+      <Normal> { -0.723594 -0.525712 0.447188 }
+    }
+    <Vertex> 30 {
+      -0.262869 -0.809012 0.525738
+      <Normal> { -0.262856 -0.808985 0.525712 }
+    }
+    <Vertex> 31 {
+      -0.425323 -0.309011 0.850654
+      <Normal> { -0.425306 -0.309 0.850642 }
+    }
+    <Vertex> 32 {
+      -0.723607 0.525725 0.44722
+      <Normal> { -0.723594 0.525712 0.447188 }
+    }
+    <Vertex> 33 {
+      -0.850648 0 0.525736
+      <Normal> { -0.850642 0 0.525712 }
+    }
+    <Vertex> 34 {
+      -0.425323 0.309011 0.850654
+      <Normal> { -0.425306 0.309 0.850642 }
+    }
+    <Vertex> 35 {
+      0.276388 0.850649 0.44722
+      <Normal> { 0.276376 0.850642 0.447218 }
+    }
+    <Vertex> 36 {
+      -0.262869 0.809012 0.525738
+      <Normal> { -0.262856 0.808985 0.525712 }
+    }
+    <Vertex> 37 {
+      0.162456 0.499995 0.850654
+      <Normal> { 0.16245 0.499985 0.850642 }
+    }
+    <Vertex> 38 {
+      0.894426 0 0.447216
+      <Normal> { 0.894406 0 0.447188 }
+    }
+    <Vertex> 39 {
+      0.688189 0.499997 0.525736
+      <Normal> { 0.688162 0.499985 0.525712 }
+    }
+    <Vertex> 40 {
+      0.52573 0 0.850652
+      <Normal> { 0.525712 0 0.850642 }
+    }
+    <Vertex> 41 {
+      0 0 1
+      <Normal> { 0 0 1 }
+    }
+  }
+  <Polygon> {
+    <Normal> { 0.102381 -0.31509 -0.943523 }
+    <VertexRef> { 0 1 2 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.700224 -0.268032 -0.661699 }
+    <VertexRef> { 3 1 4 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.268034 -0.194736 -0.943523 }
+    <VertexRef> { 0 2 5 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.268034 0.194737 -0.943523 }
+    <VertexRef> { 0 5 6 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.102381 0.31509 -0.943523 }
+    <VertexRef> { 0 6 7 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.904989 -0.268032 -0.330385 }
+    <VertexRef> { 3 4 8 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.024747 -0.943521 -0.330386 }
+    <VertexRef> { 9 10 11 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.889697 -0.315095 -0.330385 }
+    <VertexRef> { 12 13 14 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.574602 0.748784 -0.330388 }
+    <VertexRef> { 15 16 17 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.534576 0.777865 -0.330387 }
+    <VertexRef> { 18 19 20 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.802609 -0.583126 -0.125627 }
+    <VertexRef> { 3 8 21 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.306569 -0.943522 -0.125629 }
+    <VertexRef> { 9 11 22 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.992077 0 -0.125628 }
+    <VertexRef> { 12 14 23 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.306569 0.943522 -0.125629 }
+    <VertexRef> { 15 17 24 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.802609 0.583126 -0.125627 }
+    <VertexRef> { 18 20 25 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.408946 -0.628425 0.661698 }
+    <VertexRef> { 26 27 28 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.4713 -0.583122 0.661699 }
+    <VertexRef> { 29 30 31 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.700224 0.268032 0.661699 }
+    <VertexRef> { 32 33 34 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.03853 0.748779 0.661699 }
+    <VertexRef> { 35 36 37 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.724042 0.194736 0.661695 }
+    <VertexRef> { 38 39 40 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.268034 0.194737 0.943523 }
+    <VertexRef> { 40 37 41 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.491119 0.356821 0.794657 }
+    <VertexRef> { 40 39 37 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.408946 0.628425 0.661699 }
+    <VertexRef> { 39 35 37 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.102381 0.31509 0.943523 }
+    <VertexRef> { 37 34 41 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.187594 0.577345 0.794658 }
+    <VertexRef> { 37 36 34 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.4713 0.583122 0.661699 }
+    <VertexRef> { 36 32 34 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.331305 0 0.943524 }
+    <VertexRef> { 34 31 41 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.60706 0 0.794656 }
+    <VertexRef> { 34 33 31 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.700224 -0.268032 0.661699 }
+    <VertexRef> { 33 29 31 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.102381 -0.31509 0.943523 }
+    <VertexRef> { 31 28 41 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.187594 -0.577345 0.794658 }
+    <VertexRef> { 31 30 28 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.03853 -0.748779 0.661699 }
+    <VertexRef> { 30 26 28 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.268034 -0.194737 0.943523 }
+    <VertexRef> { 28 40 41 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.491119 -0.356821 0.794657 }
+    <VertexRef> { 28 27 40 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.724042 -0.194736 0.661695 }
+    <VertexRef> { 27 38 40 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.889697 0.315095 0.330385 }
+    <VertexRef> { 25 39 38 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.794656 0.577348 0.187595 }
+    <VertexRef> { 25 20 39 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.574602 0.748784 0.330388 }
+    <VertexRef> { 20 35 39 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.024747 0.943521 0.330386 }
+    <VertexRef> { 24 36 35 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.303531 0.934171 0.187597 }
+    <VertexRef> { 24 17 36 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.534576 0.777865 0.330387 }
+    <VertexRef> { 17 32 36 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.904989 0.268032 0.330385 }
+    <VertexRef> { 23 33 32 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.982246 0 0.187599 }
+    <VertexRef> { 23 14 33 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.904989 -0.268031 0.330385 }
+    <VertexRef> { 14 29 33 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.534576 -0.777865 0.330387 }
+    <VertexRef> { 22 30 29 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.303531 -0.934171 0.187597 }
+    <VertexRef> { 22 11 30 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.024747 -0.943521 0.330386 }
+    <VertexRef> { 11 26 30 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.574602 -0.748784 0.330388 }
+    <VertexRef> { 21 27 26 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.794656 -0.577348 0.187595 }
+    <VertexRef> { 21 8 27 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.889697 -0.315095 0.330385 }
+    <VertexRef> { 8 38 27 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.306569 0.943522 0.125629 }
+    <VertexRef> { 20 24 35 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.303531 0.934171 -0.187597 }
+    <VertexRef> { 20 19 24 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.024747 0.943521 -0.330386 }
+    <VertexRef> { 19 15 24 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.802609 0.583126 0.125627 }
+    <VertexRef> { 17 23 32 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.794656 0.577348 -0.187595 }
+    <VertexRef> { 17 16 23 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.889697 0.315095 -0.330385 }
+    <VertexRef> { 16 12 23 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.802609 -0.583126 0.125627 }
+    <VertexRef> { 14 22 29 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.794656 -0.577348 -0.187595 }
+    <VertexRef> { 14 13 22 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.574602 -0.748784 -0.330388 }
+    <VertexRef> { 13 9 22 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.306569 -0.943522 0.125629 }
+    <VertexRef> { 11 21 26 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.303531 -0.934171 -0.187597 }
+    <VertexRef> { 11 10 21 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.534576 -0.777865 -0.330387 }
+    <VertexRef> { 10 3 21 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.992077 0 0.125628 }
+    <VertexRef> { 8 25 38 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.982246 0 -0.187599 }
+    <VertexRef> { 8 4 25 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.904989 0.268031 -0.330385 }
+    <VertexRef> { 4 18 25 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.4713 0.583122 -0.661699 }
+    <VertexRef> { 7 19 18 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.187594 0.577345 -0.794658 }
+    <VertexRef> { 7 6 19 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.03853 0.748779 -0.661699 }
+    <VertexRef> { 6 15 19 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.408946 0.628425 -0.661698 }
+    <VertexRef> { 6 16 15 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.491119 0.356821 -0.794657 }
+    <VertexRef> { 6 5 16 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.724042 0.194736 -0.661695 }
+    <VertexRef> { 5 12 16 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.724042 -0.194736 -0.661695 }
+    <VertexRef> { 5 13 12 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.491119 -0.356821 -0.794657 }
+    <VertexRef> { 5 2 13 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.408946 -0.628425 -0.661698 }
+    <VertexRef> { 2 9 13 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.700224 0.268032 -0.661699 }
+    <VertexRef> { 4 7 18 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.60706 0 -0.794656 }
+    <VertexRef> { 4 1 7 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.331305 0 -0.943524 }
+    <VertexRef> { 1 0 7 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { -0.03853 -0.748779 -0.661699 }
+    <VertexRef> { 2 10 9 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.187594 -0.577345 -0.794658 }
+    <VertexRef> { 2 1 10 <Ref> { Icosphere } }
+  }
+  <Polygon> {
+    <Normal> { 0.4713 -0.583122 -0.661699 }
+    <VertexRef> { 1 3 10 <Ref> { Icosphere } }
+  }
+}

Some files were not shown because too many files changed in this diff