Browse Source

Merge branch 'master' into cmake

Sam Edwards 10 years ago
parent
commit
25e2e0221d
98 changed files with 4026 additions and 738 deletions
  1. 5 3
      direct/src/directbase/ppython.cxx
  2. 5 7
      direct/src/directscripts/eggcacher.py
  3. 24 10
      direct/src/gui/DirectDialog.py
  4. 5 3
      direct/src/gui/DirectRadioButton.py
  5. 8 6
      direct/src/showbase/Transitions.py
  6. 1 1
      dtool/src/dtoolutil/executionEnvironment.cxx
  7. 69 0
      dtool/src/dtoolutil/filename.I
  8. 11 1
      dtool/src/dtoolutil/filename.h
  9. 6 3
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  10. 1276 0
      makepanda/installer.nsi
  11. 18 22
      makepanda/makepanda.py
  12. 5 1
      makepanda/makepandacore.py
  13. 0 0
      makepanda/panda-install.bmp
  14. 1 1
      panda/src/bullet/bulletAllHitsRayResult.cxx
  15. 1 1
      panda/src/bullet/bulletAllHitsRayResult.h
  16. 5 4
      panda/src/bullet/bulletWorld.cxx
  17. 2 1
      panda/src/bullet/bulletWorld.h
  18. 10 6
      panda/src/collide/collisionBox.cxx
  19. 45 24
      panda/src/display/graphicsStateGuardian.cxx
  20. 8 3
      panda/src/doc/eggSyntax.txt
  21. 6 0
      panda/src/downloader/httpDate.cxx
  22. 23 22
      panda/src/egg2pg/eggLoader.cxx
  23. 49 2
      panda/src/egg2pg/eggSaver.cxx
  24. 2 1
      panda/src/egg2pg/eggSaver.h
  25. 7 7
      panda/src/egldisplay/eglGraphicsBuffer.cxx
  26. 1 1
      panda/src/egldisplay/eglGraphicsBuffer.h
  27. 7 7
      panda/src/egldisplay/eglGraphicsPixmap.cxx
  28. 2 2
      panda/src/egldisplay/eglGraphicsPixmap.h
  29. 27 0
      panda/src/express/datagram.I
  30. 5 0
      panda/src/express/datagram.h
  31. 90 0
      panda/src/express/pointerToArray.I
  32. 23 0
      panda/src/express/pointerToArray.h
  33. 25 11
      panda/src/express/pointerToArrayBase.I
  34. 4 0
      panda/src/express/pointerToArrayBase.h
  35. 41 23
      panda/src/express/pointerToArray_ext.I
  36. 20 0
      panda/src/express/pointerToArray_ext.h
  37. 8 3
      panda/src/ffmpeg/ffmpegAudioCursor.cxx
  38. 5 0
      panda/src/ffmpeg/ffmpegVideoCursor.cxx
  39. 39 5
      panda/src/framework/windowFramework.cxx
  40. 12 13
      panda/src/gles2gsg/gles2gsg.h
  41. 3 0
      panda/src/glesgsg/glesgsg.h
  42. 4 11
      panda/src/glstuff/glCgShaderContext_src.cxx
  43. 0 1
      panda/src/glstuff/glCgShaderContext_src.h
  44. 1 1
      panda/src/glstuff/glGraphicsBuffer_src.cxx
  45. 6 1
      panda/src/glstuff/glGraphicsStateGuardian_src.I
  46. 98 83
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  47. 4 0
      panda/src/glstuff/glSamplerContext_src.cxx
  48. 4 0
      panda/src/glstuff/glSamplerContext_src.h
  49. 22 2
      panda/src/glstuff/glShaderContext_src.cxx
  50. 2 0
      panda/src/glstuff/glmisc_src.cxx
  51. 1 1
      panda/src/gobj/internalName_ext.cxx
  52. 1 0
      panda/src/gobj/samplerContext.h
  53. 1 0
      panda/src/gobj/samplerState.h
  54. 75 70
      panda/src/gobj/texture.cxx
  55. 7 0
      panda/src/gobj/texture.h
  56. 1 1
      panda/src/linmath/lmatrix3_src.cxx
  57. 1 1
      panda/src/linmath/lmatrix4_src.cxx
  58. 28 0
      panda/src/mathutil/pta_LMatrix3_ext.h
  59. 28 0
      panda/src/mathutil/pta_LMatrix4_ext.h
  60. 30 0
      panda/src/mathutil/pta_LVecBase2_ext.h
  61. 30 0
      panda/src/mathutil/pta_LVecBase3_ext.h
  62. 30 0
      panda/src/mathutil/pta_LVecBase4_ext.h
  63. 3 0
      panda/src/pgraph/Sources.pp
  64. 2 0
      panda/src/pgraph/config_pgraph.cxx
  65. 1 0
      panda/src/pgraph/cullBinManager.h
  66. 4 4
      panda/src/pgraph/loader.cxx
  67. 3 3
      panda/src/pgraph/nodePath.h
  68. 1 0
      panda/src/pgraph/p3pgraph_composite3.cxx
  69. 7 5
      panda/src/pgraph/pandaNode.I
  70. 60 0
      panda/src/pgraph/paramNodePath.I
  71. 108 0
      panda/src/pgraph/paramNodePath.cxx
  72. 75 0
      panda/src/pgraph/paramNodePath.h
  73. 2 3
      panda/src/pgraph/polylightNode.I
  74. 24 0
      panda/src/pgraph/polylightNode.cxx
  75. 4 4
      panda/src/pgraph/polylightNode.h
  76. 0 27
      panda/src/pgraph/shaderInput.I
  77. 30 2
      panda/src/pgraph/shaderInput.cxx
  78. 4 4
      panda/src/pgraph/shaderInput.h
  79. 1 0
      panda/src/pgraph/shaderPool.cxx
  80. 175 0
      panda/src/pnmimage/convert_srgb.I
  81. 165 0
      panda/src/pnmimage/convert_srgb.cxx
  82. 59 0
      panda/src/pnmimage/convert_srgb.h
  83. 151 0
      panda/src/pnmimage/convert_srgb_sse2.cxx
  84. 1 0
      panda/src/pnmimage/p3pnmimage_composite1.cxx
  85. 2 0
      panda/src/putil/Sources.pp
  86. 20 10
      panda/src/putil/bamCache.cxx
  87. 6 4
      panda/src/putil/bamCache.h
  88. 2 0
      panda/src/putil/callbackObject.h
  89. 32 0
      panda/src/putil/callbackObject_ext.I
  90. 47 0
      panda/src/putil/callbackObject_ext.h
  91. 1 1
      panda/src/tinydisplay/zbuffer.h
  92. 270 42
      pandatool/src/daeegg/daeCharacter.cxx
  93. 43 12
      pandatool/src/daeegg/daeCharacter.h
  94. 11 10
      pandatool/src/daeegg/daeMaterials.cxx
  95. 396 221
      pandatool/src/daeegg/daeToEggConverter.cxx
  96. 23 20
      pandatool/src/daeegg/daeToEggConverter.h
  97. 7 0
      pandatool/src/daeegg/pre_fcollada_include.h
  98. 8 0
      pandatool/src/daeprogs/daeToEgg.cxx

+ 5 - 3
direct/src/directbase/ppython.cxx

@@ -7,6 +7,8 @@
 //
 //
 ///////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////
 
 
+#include "dtoolbase.h"
+
 #include <Python.h>
 #include <Python.h>
 #if PY_MAJOR_VERSION >= 3
 #if PY_MAJOR_VERSION >= 3
 #include <wchar.h>
 #include <wchar.h>
@@ -71,7 +73,7 @@ int main(int argc, char *argv[]) {
   char *path = getenv("PATH");
   char *path = getenv("PATH");
   char *result = strtok(path, ";");
   char *result = strtok(path, ";");
   while (result != NULL) {
   while (result != NULL) {
-    struct stat st;       
+    struct stat st;
     char *ppython = (char*) malloc(strlen(result) + 13);
     char *ppython = (char*) malloc(strlen(result) + 13);
     strcpy(ppython, result);
     strcpy(ppython, result);
     strcat(ppython, "\\ppython.exe");
     strcat(ppython, "\\ppython.exe");
@@ -79,13 +81,13 @@ int main(int argc, char *argv[]) {
       Py_SetPythonHome(result);
       Py_SetPythonHome(result);
       free(ppython);
       free(ppython);
       break;
       break;
-    }                                
+    }
     result = strtok(NULL, ";");
     result = strtok(NULL, ";");
     free(ppython);
     free(ppython);
   }
   }
 #endif
 #endif
 #endif
 #endif
-  
+
   Py_Initialize();
   Py_Initialize();
 
 
   if (Py_VerboseFlag) {
   if (Py_VerboseFlag) {

+ 5 - 7
direct/src/directscripts/eggcacher.py

@@ -9,15 +9,15 @@
 ##############################################################################
 ##############################################################################
 
 
 import os,sys,gc
 import os,sys,gc
-from pandac.PandaModules import *
+from panda3d.core import *
 
 
 class EggCacher:
 class EggCacher:
     def __init__(self, args):
     def __init__(self, args):
         maindir = Filename.fromOsSpecific(os.getcwd()).getFullpath()
         maindir = Filename.fromOsSpecific(os.getcwd()).getFullpath()
         ExecutionEnvironment.setEnvironmentVariable("MAIN_DIR", maindir)
         ExecutionEnvironment.setEnvironmentVariable("MAIN_DIR", maindir)
         self.bamcache = BamCache.getGlobalPtr()
         self.bamcache = BamCache.getGlobalPtr()
-        self.pandaloader = PandaLoader()
-        self.loaderopts = LoaderOptions()
+        self.pandaloader = Loader()
+        self.loaderopts = LoaderOptions(LoaderOptions.LF_no_ram_cache)
         if (self.bamcache.getActive() == 0):
         if (self.bamcache.getActive() == 0):
             print "The model cache is not currently active."
             print "The model cache is not currently active."
             print "You must set a model-cache-dir in your config file."
             print "You must set a model-cache-dir in your config file."
@@ -69,12 +69,12 @@ class EggCacher:
 
 
     def processFiles(self, files):
     def processFiles(self, files):
         total = 0
         total = 0
-        for (path,size) in files:
+        for (path, size) in files:
             total += size
             total += size
         progress = 0
         progress = 0
         for (path,size) in files:
         for (path,size) in files:
             fn = Filename.fromOsSpecific(path)
             fn = Filename.fromOsSpecific(path)
-            cached = self.bamcache.lookup(fn,"bam")
+            cached = self.bamcache.lookup(fn, "bam")
             percent = (progress * 100) / total
             percent = (progress * 100) / total
             report = path
             report = path
             if (self.concise): report = os.path.basename(report)
             if (self.concise): report = os.path.basename(report)
@@ -87,6 +87,4 @@ class EggCacher:
             TexturePool.releaseAllTextures()
             TexturePool.releaseAllTextures()
             progress += size
             progress += size
 
 
-
 cacher = EggCacher(sys.argv[1:])
 cacher = EggCacher(sys.argv[1:])
-

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

@@ -94,8 +94,9 @@ class DirectDialog(DirectFrame):
             ('text',              '',            None),
             ('text',              '',            None),
             ('text_align',        TextNode.ALeft,   None),
             ('text_align',        TextNode.ALeft,   None),
             ('text_scale',        0.06,          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),
             ('buttonTextList',    [],            DGG.INITOPT),
             ('buttonGeomList',    [],            DGG.INITOPT),
             ('buttonGeomList',    [],            DGG.INITOPT),
             ('buttonImageList',   [],            DGG.INITOPT),
             ('buttonImageList',   [],            DGG.INITOPT),
@@ -210,10 +211,16 @@ class DirectDialog(DirectFrame):
         bounds = self.stateNodePath[0].getTightBounds()
         bounds = self.stateNodePath[0].getTightBounds()
         if image:
         if image:
             image.reparentTo(self.stateNodePath[0])
             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
         # Center text and geom around origin
         # How far is center of text from origin?
         # How far is center of text from origin?
         xOffset = -(l+r)*0.5
         xOffset = -(l+r)*0.5
@@ -246,10 +253,16 @@ class DirectDialog(DirectFrame):
                 bl = br = bb = bt = 0
                 bl = br = bb = bt = 0
                 for button in self.buttonList:
                 for button in self.buttonList:
                     bounds = button.stateNodePath[0].getTightBounds()
                     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]
                 bl -= bpad[0]
                 br += bpad[0]
                 br += bpad[0]
                 bb -= bpad[1]
                 bb -= bpad[1]
@@ -303,6 +316,7 @@ class DirectDialog(DirectFrame):
         # reduce bottom by pad, button height and 2*button pad
         # reduce bottom by pad, button height and 2*button pad
         b = min(b - self['midPad'] - bpad[1] - bHeight - bpad[1], b) - pad[1]
         b = min(b - self['midPad'] - bpad[1] - bHeight - bpad[1], b) - pad[1]
         t = t + self['topPad'] + pad[1]
         t = t + self['topPad'] + pad[1]
+        self['frameSize'] = (l, r, b, t)
         self['image_scale'] = (r - l, 1, t - b)
         self['image_scale'] = (r - l, 1, t - b)
         # Center frame about text and buttons
         # Center frame about text and buttons
         self['image_pos'] = ((l+r)*0.5, 0.0, (b+t)*0.5)
         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),
             ('boxGeom', None, None),
             ('boxGeomColor', None, None),
             ('boxGeomColor', None, None),
             ('boxGeomScale', 1.0, None),
             ('boxGeomScale', 1.0, None),
-            ('boxImage', loader.loadModel('models/gui/radio_button_gui'), None),
+            ('boxImage', None, None),
             ('boxImageScale', 1.0, None),
             ('boxImageScale', 1.0, None),
             ('boxImageColor', VBase4(1, 1, 1, 1), None),
             ('boxImageColor', VBase4(1, 1, 1, 1), None),
             ('boxRelief', None, None),
             ('boxRelief', None, None),
@@ -69,9 +69,11 @@ class DirectRadioButton(DirectButton):
         # Call option initialization functions
         # Call option initialization functions
         self.initialiseoptions(DirectRadioButton)
         self.initialiseoptions(DirectRadioButton)
         # After initialization with X giving it the correct size, put back space
         # 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'] = (' ', '*')
-            self.indicator['text_pos'] = (0, -.5)
+            self.indicator['text_pos'] = (0, -.25)
         else:
         else:
             self.indicator['text'] = (' ', ' ')
             self.indicator['text'] = (' ', ' ')
 
 

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

@@ -59,15 +59,12 @@ class Transitions:
 
 
         # Reload fade if its already been created
         # Reload fade if its already been created
         if self.fade:
         if self.fade:
-            del self.fade
+            self.fade.destroy()
             self.fade = None
             self.fade = None
             self.loadFade()
             self.loadFade()
 
 
     def loadFade(self):
     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
             # We create a DirectFrame for the fade polygon, instead of
             # simply loading the polygon model and using it directly,
             # simply loading the polygon model and using it directly,
             # so that it will also obscure mouse events for objects
             # so that it will also obscure mouse events for objects
@@ -80,10 +77,15 @@ class Transitions:
                 image_scale = (4, 2, 2),
                 image_scale = (4, 2, 2),
                 state = DGG.NORMAL,
                 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.setBin('unsorted', 0)
             self.fade.setColor(0,0,0,0)
             self.fade.setColor(0,0,0,0)
 
 
-    
     def getFadeInIval(self, t=0.5, finishIval=None):
     def getFadeInIval(self, t=0.5, finishIval=None):
         """
         """
         Returns an interval without starting it.  This is particularly useful in
         Returns an interval without starting it.  This is particularly useful in

+ 1 - 1
dtool/src/dtoolutil/executionEnvironment.cxx

@@ -60,7 +60,7 @@ extern char **environ;
 #include <sys/sysctl.h>
 #include <sys/sysctl.h>
 #endif
 #endif
 
 
-#if defined(IS_LINUX) || defined(IS_OSX)
+#if defined(IS_LINUX) || defined(IS_OSX) || defined(IS_FREEBSD)
 // For link_map and dlinfo.
 // For link_map and dlinfo.
 #include <link.h>
 #include <link.h>
 #include <dlfcn.h>
 #include <dlfcn.h>

+ 69 - 0
dtool/src/dtoolutil/filename.I

@@ -64,6 +64,39 @@ Filename(const Filename &copy) :
 {
 {
 }
 }
 
 
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Move Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename::
+Filename(string &&filename) NOEXCEPT {
+  _flags = 0;
+  (*this) = move(filename);
+}
+#endif  // USE_MOVE_SEMANTICS
+
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Move Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename::
+Filename(Filename &&from) NOEXCEPT :
+  _filename(move(from._filename)),
+  _dirname_end(from._dirname_end),
+  _basename_start(from._basename_start),
+  _basename_end(from._basename_end),
+  _extension_start(from._extension_start),
+  _hash_start(from._hash_start),
+  _hash_end(from._hash_end),
+  _flags(from._flags)
+{
+}
+#endif  // USE_MOVE_SEMANTICS
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Filename::text_filename named constructor
 //     Function: Filename::text_filename named constructor
 //       Access: Published
 //       Access: Published
@@ -216,6 +249,42 @@ operator = (const Filename &copy) {
   return *this;
   return *this;
 }
 }
 
 
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Move assignment operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename &Filename::
+operator = (string &&filename) NOEXCEPT {
+  _filename = move(filename);
+
+  locate_basename();
+  locate_extension();
+  locate_hash();
+  return *this;
+}
+#endif  // USE_MOVE_SEMANTICS
+
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Move assignment operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename &Filename::
+operator = (Filename &&from) NOEXCEPT {
+  _filename = MOVE(from._filename);
+  _dirname_end = from._dirname_end;
+  _basename_start = from._basename_start;
+  _basename_end = from._basename_end;
+  _extension_start = from._extension_start;
+  _hash_start = from._hash_start;
+  _hash_end = from._hash_end;
+  _flags = from._flags;
+  return *this;
+}
+#endif  // USE_MOVE_SEMANTICS
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Filename::string typecast operator
 //     Function: Filename::string typecast operator

+ 11 - 1
dtool/src/dtoolutil/filename.h

@@ -68,6 +68,11 @@ PUBLISHED:
   Filename(const Filename &dirname, const Filename &basename);
   Filename(const Filename &dirname, const Filename &basename);
   INLINE ~Filename();
   INLINE ~Filename();
 
 
+#ifdef USE_MOVE_SEMANTICS
+  INLINE Filename(string &&filename) NOEXCEPT;
+  INLINE Filename(Filename &&from) NOEXCEPT;
+#endif
+
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
   PyObject *__reduce__(PyObject *self) const;
   PyObject *__reduce__(PyObject *self) const;
 #endif
 #endif
@@ -88,7 +93,7 @@ PUBLISHED:
                                    Type type = T_general);
                                    Type type = T_general);
   static Filename from_os_specific_w(const wstring &os_specific,
   static Filename from_os_specific_w(const wstring &os_specific,
                                      Type type = T_general);
                                      Type type = T_general);
-  static Filename expand_from(const string &user_string, 
+  static Filename expand_from(const string &user_string,
                               Type type = T_general);
                               Type type = T_general);
   static Filename temporary(const string &dirname, const string &prefix,
   static Filename temporary(const string &dirname, const string &prefix,
                             const string &suffix = string(),
                             const string &suffix = string(),
@@ -105,6 +110,11 @@ PUBLISHED:
   INLINE Filename &operator = (const char *filename);
   INLINE Filename &operator = (const char *filename);
   INLINE Filename &operator = (const Filename &copy);
   INLINE Filename &operator = (const Filename &copy);
 
 
+#ifdef USE_MOVE_SEMANTICS
+  INLINE Filename &operator = (string &&filename) NOEXCEPT;
+  INLINE Filename &operator = (Filename &&from) NOEXCEPT;
+#endif
+
   // And retrieval is by any of the classic string operations.
   // And retrieval is by any of the classic string operations.
   INLINE operator const string & () const;
   INLINE operator const string & () const;
   INLINE const char *c_str() const;
   INLINE const char *c_str() const;

+ 6 - 3
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -3204,7 +3204,8 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj,
       }
       }
       extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
       extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
       extra_param_check += " && " + param_name + "_long != NULL";
       extra_param_check += " && " + param_name + "_long != NULL";
-      pexpr_string = "PyLong_AsUnsignedLongLong(" + param_name + "_long)";
+      pexpr_string = "(" + type->get_local_name(&parser) + ")" +
+                     "PyLong_AsUnsignedLongLong(" + param_name + "_long)";
       extra_cleanup += "Py_XDECREF(" + param_name + "_long);";
       extra_cleanup += "Py_XDECREF(" + param_name + "_long);";
       expected_params += "unsigned long long";
       expected_params += "unsigned long long";
       ++num_params;
       ++num_params;
@@ -3219,7 +3220,8 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj,
       }
       }
       extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
       extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
       extra_param_check += " && " + param_name + "_long != NULL";
       extra_param_check += " && " + param_name + "_long != NULL";
-      pexpr_string = "PyLong_AsLongLong(" + param_name + "_long)";
+      pexpr_string = "(" + type->get_local_name(&parser) + ")" +
+                     "PyLong_AsLongLong(" + param_name + "_long)";
       extra_cleanup += "Py_XDECREF(" + param_name + "_long);";
       extra_cleanup += "Py_XDECREF(" + param_name + "_long);";
       expected_params += "long long";
       expected_params += "long long";
       ++num_params;
       ++num_params;
@@ -3234,7 +3236,8 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj,
       }
       }
       extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
       extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");";
       extra_param_check += " && " + param_name + "_long != NULL";
       extra_param_check += " && " + param_name + "_long != NULL";
-      pexpr_string = "PyLong_AsUnsignedLong(" + param_name + "_long)";
+      pexpr_string = "(" + type->get_local_name(&parser) + ")" +
+                     "PyLong_AsUnsignedLong(" + param_name + "_long)";
       extra_cleanup += "Py_XDECREF(" + param_name + "_long);";
       extra_cleanup += "Py_XDECREF(" + param_name + "_long);";
       expected_params += "unsigned int";
       expected_params += "unsigned int";
       ++num_params;
       ++num_params;

+ 1276 - 0
makepanda/installer.nsi

@@ -0,0 +1,1276 @@
+; Panda3D installation script for the Nullsoft Installation System (NSIS).
+; Jon Parise <[email protected]>
+; with Ben Johnson <[email protected]>
+; with Jason Pratt <[email protected]>
+; mangled by Josh Yelon <[email protected]>
+; Heavily restructured by rdb
+
+; Caller needs to define these variables:
+;
+;   COMPRESSOR    - either zlib or lzma
+;   TITLE         - title                         (eg. "Panda3D SDK 1.9.0")
+;   INSTALLDIR    - default install location      (eg. "C:\Panda3D-1.9.0-x64")
+;   OUTFILE       - where to put the output file  (eg. "..\nsis-output.exe")
+;
+;   BUILT         - location of panda install tree.
+;   SOURCE        - location of the panda source-tree if available, OR location of panda install tree.
+;   PYVER         - version of Python that Panda was built with (ie, "2.7")
+;   PYEXTRAS      - directory containing python extras, if any.
+;   REGVIEW       - either 32 or 64, depending on the build architecture.
+;
+
+Name "${TITLE}"
+InstallDir "${INSTALLDIR}"
+OutFile "${OUTFILE}"
+
+RequestExecutionLevel user
+
+SetCompress auto
+SetCompressor ${COMPRESSOR}
+
+!include "MUI2.nsh"
+!include "Sections.nsh"
+!include "WinMessages.nsh"
+!include "WordFunc.nsh"
+
+!define MUI_WELCOMEFINISHPAGE_BITMAP "panda-install.bmp"
+!define MUI_UNWELCOMEFINISHPAGE_BITMAP "panda-install.bmp"
+
+!define MUI_ABORTWARNING
+!define MUI_FINISHPAGE_NOREBOOTSUPPORT
+!define MUI_FINISHPAGE_RUN
+!define MUI_FINISHPAGE_RUN_FUNCTION runFunction
+!define MUI_FINISHPAGE_RUN_TEXT "Visit the Panda3D Manual"
+
+!insertmacro MUI_PAGE_WELCOME
+!insertmacro MUI_PAGE_LICENSE "../doc/LICENSE"
+!insertmacro MUI_PAGE_DIRECTORY
+
+!define MUI_PAGE_CUSTOMFUNCTION_LEAVE ConfirmPythonSelection
+!insertmacro MUI_PAGE_COMPONENTS
+
+!insertmacro MUI_PAGE_INSTFILES
+!insertmacro MUI_PAGE_FINISH
+
+!insertmacro MUI_UNPAGE_WELCOME
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+!insertmacro MUI_UNPAGE_FINISH
+
+!insertmacro MUI_LANGUAGE "English"
+
+ShowInstDetails hide
+ShowUninstDetails hide
+
+LicenseData "${LICENSE}"
+
+InstType "Full (Recommended)"
+InstType "Minimal"
+
+LangString DESC_SecCore ${LANG_ENGLISH} "The Panda3D core libraries, configuration files and models/textures that are needed to use Panda3D."
+LangString DESC_SecOpenGL ${LANG_ENGLISH} "The OpenGL graphics back-end is the most well-supported renderer."
+LangString DESC_SecDirect3D9 ${LANG_ENGLISH} "The optional Direct3D 9 renderer."
+LangString DESC_SecOpenAL ${LANG_ENGLISH} "Support for playing audio via the OpenAL library.  You need either OpenAL or FMOD to be able to play audio."
+LangString DESC_SecFMOD ${LANG_ENGLISH} "Support for decoding and playing audio via the FMOD Ex library.  You need either OpenAL or FMOD to be able to play audio."
+LangString DESC_SecFFMpeg ${LANG_ENGLISH} "Support for decoding video and audio via the FFMpeg library.  Without this option, Panda3D will only be able to play .wav and .ogg audio files."
+LangString DESC_SecBullet ${LANG_ENGLISH} "Support for the Bullet physics engine."
+LangString DESC_SecODE ${LANG_ENGLISH} "Support for the Open Dynamics Engine to implement physics."
+LangString DESC_SecPhysX ${LANG_ENGLISH} "Support for NVIDIA PhysX to implement physics."
+LangString DESC_SecRocket ${LANG_ENGLISH} "Support for the libRocket GUI library.  This is an optional library that offers an HTML/CSS-like approach to creating user interfaces."
+LangString DESC_SecTools ${LANG_ENGLISH} "Useful tools and model converters to help with Panda3D development.  Recommended."
+LangString DESC_SecPyBindings ${LANG_ENGLISH} "Contains the Python modules that allow use of Panda3D using Python.  These will only work with a ${REGVIEW}-bit version of Python ${PYVER}."
+LangString DESC_SecPython ${LANG_ENGLISH} "Contains a ${REGVIEW}-bit copy of Python ${PYVER} preconfigured to make use of Panda3D."
+LangString DESC_SecHeadersLibs ${LANG_ENGLISH} "Headers and libraries needed for C++ development with Panda3D."
+LangString DESC_SecSamples ${LANG_ENGLISH} "The sample programs demonstrate how to make Python applications with Panda3D."
+LangString DESC_SecMaxPlugins ${LANG_ENGLISH} "Plug-ins for Autodesk 3ds Max (${REGVIEW}-bit) that can be used to export models to Panda3D."
+LangString DESC_SecMayaPlugins ${LANG_ENGLISH} "Plug-ins and scripts for Autodesk Maya (${REGVIEW}-bit) that can be used to export models to Panda3D."
+
+var READABLE
+var MANPAGE
+
+; See http://nsis.sourceforge.net/Check_if_a_file_exists_at_compile_time for documentation
+!macro !defineifexist _VAR_NAME _FILE_NAME
+    !tempfile _TEMPFILE
+    !ifdef NSIS_WIN32_MAKENSIS
+        ; Windows - cmd.exe
+        !system 'if exist "${_FILE_NAME}" echo !define ${_VAR_NAME} > "${_TEMPFILE}"'
+    !else
+        ; Posix - sh
+        !system 'if [ -e "${_FILE_NAME}" ]; then echo "!define ${_VAR_NAME}" > "${_TEMPFILE}"; fi'
+    !endif
+    !include '${_TEMPFILE}'
+    !delfile '${_TEMPFILE}'
+    !undef _TEMPFILE
+!macroend
+
+!insertmacro !defineifexist HAVE_GL "${BUILT}\bin\libpandagl.dll"
+!insertmacro !defineifexist HAVE_DX9 "${BUILT}\bin\libpandadx9.dll"
+!insertmacro !defineifexist HAVE_OPENAL "${BUILT}\bin\libp3openal_audio.dll"
+!insertmacro !defineifexist HAVE_FMOD "${BUILT}\bin\libp3fmod_audio.dll"
+!insertmacro !defineifexist HAVE_FFMPEG "${BUILT}\bin\libp3ffmpeg.dll"
+!insertmacro !defineifexist HAVE_BULLET "${BUILT}\bin\libpandabullet.dll"
+!insertmacro !defineifexist HAVE_ODE "${BUILT}\bin\libpandaode.dll"
+!insertmacro !defineifexist HAVE_PHYSX "${BUILT}\bin\libpandaphysx.dll"
+!insertmacro !defineifexist HAVE_ROCKET "${BUILT}\bin\libp3rocket.dll"
+!insertmacro !defineifexist HAVE_PYTHON "${BUILT}\python"
+!insertmacro !defineifexist HAVE_SAMPLES "${SOURCE}\samples"
+!insertmacro !defineifexist HAVE_MAX_PLUGINS "${BUILT}\plugins\*.dlo"
+!insertmacro !defineifexist HAVE_MAYA_PLUGINS "${BUILT}\plugins\*.mll"
+
+Function runFunction
+    ExecShell "open" "$SMPROGRAMS\${TITLE}\Panda3D Manual.lnk"
+FunctionEnd
+
+SectionGroup "Panda3D Libraries"
+    Section "Core Libraries" SecCore
+        SectionIn 1 2 RO
+
+        SetShellVarContext current
+        SetOverwrite try
+
+        SetDetailsPrint both
+        DetailPrint "Installing Panda3D libraries..."
+        SetDetailsPrint listonly
+
+        SetOutPath "$INSTDIR"
+        File "${BUILT}\LICENSE"
+        File /r /x CVS "${BUILT}\ReleaseNotes"
+        SetOutPath $INSTDIR\bin
+        File /r /x libpandagl.dll /x libpandadx9.dll /x cgD3D*.dll /x python*.dll /x libpandaode.dll /x libp3fmod_audio.dll /x fmodex*.dll /x libp3ffmpeg.dll /x av*.dll /x postproc*.dll /x swscale*.dll /x swresample*.dll /x libp3rocket.dll /x boost_python*.dll /x Rocket*.dll /x _rocket*.pyd /x libpandabullet.dll /x OpenAL32.dll /x *_oal.dll /x libp3openal_audio.dll "${BUILT}\bin\*.dll"
+        File /nonfatal /r "${BUILT}\bin\Microsoft.*.manifest"
+        SetOutPath $INSTDIR\etc
+        File /r "${BUILT}\etc\*"
+
+        SetDetailsPrint both
+        DetailPrint "Installing models..."
+        SetDetailsPrint listonly
+
+        SetOutPath $INSTDIR\models
+        File /r /x CVS "${BUILT}\models\*"
+
+        RMDir /r "$SMPROGRAMS\${TITLE}"
+        CreateDirectory "$SMPROGRAMS\${TITLE}"
+    SectionEnd
+
+    !ifdef HAVE_GL
+    Section "OpenGL" SecOpenGL
+        SectionIn 1 2 RO
+
+        SetOutPath "$INSTDIR\bin"
+        File "${BUILT}\bin\libpandagl.dll"
+    SectionEnd
+    !endif
+
+    !ifdef HAVE_DX9
+    Section "Direct3D 9" SecDirect3D9
+        SectionIn 1
+
+        SetOutPath "$INSTDIR\bin"
+        File "${BUILT}\bin\libpandadx9.dll"
+        File /nonfatal /r "${BUILT}\bin\cgD3D9.dll"
+    SectionEnd
+    !endif
+
+    !ifdef HAVE_OPENAL
+    Section "OpenAL Audio" SecOpenAL
+        SectionIn 1 2
+
+        SetOutPath "$INSTDIR\bin"
+        File "${BUILT}\bin\libp3openal_audio.dll"
+        File /nonfatal /r "${BUILT}\bin\OpenAL32.dll"
+        File /nonfatal /r "${BUILT}\bin\*_oal.dll"
+    SectionEnd
+    !endif
+
+    !ifdef HAVE_FMOD
+    Section "FMOD Audio" SecFMOD
+        SectionIn 1
+
+        SetOutPath "$INSTDIR\bin"
+        File "${BUILT}\bin\libp3fmod_audio.dll"
+        File /r "${BUILT}\bin\fmodex*.dll"
+    SectionEnd
+    !endif
+
+    !ifdef HAVE_FFMPEG
+    Section "FFMpeg" SecFFMpeg
+        SectionIn 1
+
+        SetOutPath "$INSTDIR\bin"
+        File "${BUILT}\bin\libp3ffmpeg.dll"
+        File /nonfatal /r "${BUILT}\bin\av*.dll"
+        File /nonfatal /r "${BUILT}\bin\swscale*.dll"
+        File /nonfatal /r "${BUILT}\bin\swresample*.dll"
+        File /nonfatal /r "${BUILT}\bin\postproc*.dll"
+    SectionEnd
+    !endif
+
+    !ifdef HAVE_BULLET
+    Section "Bullet Physics" SecBullet
+        SectionIn 1
+
+        SetOutPath "$INSTDIR\bin"
+        File "${BUILT}\bin\libpandabullet.dll"
+    SectionEnd
+    !endif
+
+    !ifdef HAVE_ODE
+    Section "ODE Physics" SecODE
+        SectionIn 1
+
+        SetOutPath "$INSTDIR\bin"
+        File "${BUILT}\bin\libpandaode.dll"
+    SectionEnd
+    !endif
+
+    !ifdef HAVE_ROCKET
+    Section "libRocket GUI" SecRocket
+        SectionIn 1
+
+        SetOutPath "$INSTDIR\bin"
+        File "${BUILT}\bin\libp3rocket.dll"
+        File /nonfatal /r "${BUILT}\bin\Rocket*.dll"
+        File /nonfatal /r "${BUILT}\bin\_rocket*.pyd"
+        File /nonfatal /r "${BUILT}\bin\boost_python*.dll"
+    SectionEnd
+    !endif
+SectionGroupEnd
+
+Section "Tools and utilities" SecTools
+    SectionIn 1 2
+
+    SetDetailsPrint both
+    DetailPrint "Installing utilities..."
+    SetDetailsPrint listonly
+
+    SetOutPath "$INSTDIR\bin"
+    File /r "${BUILT}\bin\*.exe"
+    File /nonfatal /r "${BUILT}\bin\*.p3d"
+    SetOutPath "$INSTDIR\NSIS"
+    File /r /x CVS "${NSISDIR}\*"
+SectionEnd
+
+SectionGroup "Python support"
+    Section "Python bindings" SecPyBindings
+        SectionIn 1 2
+
+        SetDetailsPrint both
+        DetailPrint "Installing Panda3D Python modules..."
+        SetDetailsPrint listonly
+
+        SetOutPath "$INSTDIR\bin"
+        File /nonfatal /r "${BUILT}\bin\*.pyd"
+
+        SetOutPath $INSTDIR\direct\directscripts
+        File /r /x CVS /x Opt?-Win32 "${BUILT}\direct\directscripts\*"
+        SetOutPath $INSTDIR\direct\filter
+        File /r /x CVS /x Opt?-Win32 "${BUILT}\direct\filter\*.sha"
+        SetOutPath $INSTDIR\direct
+        File /r /x CVS /x Opt?-Win32 "${BUILT}\direct\*.py"
+
+        Delete "$INSTDIR\panda3d.py"
+        Delete "$INSTDIR\panda3d.pyc"
+        Delete "$INSTDIR\panda3d.pyo"
+        SetOutPath $INSTDIR\pandac
+        File /r "${BUILT}\pandac\*.py"
+        SetOutPath $INSTDIR\panda3d
+        File /r "${BUILT}\panda3d\*.py"
+
+        File /r /x bullet.pyd /x ode.pyd /x physx.pyd /x rocket.pyd "${BUILT}\panda3d\*.pyd"
+
+        !ifdef HAVE_BULLET
+            SectionGetFlags ${SecBullet} $R0
+            IntOp $R0 $R0 & ${SF_SELECTED}
+            StrCmp $R0 ${SF_SELECTED} 0 SkipBulletPyd
+            File /nonfatal /r "${BUILT}\panda3d\bullet.pyd"
+            SkipBulletPyd:
+        !endif
+
+        !ifdef HAVE_ODE
+            SectionGetFlags ${SecODE} $R0
+            IntOp $R0 $R0 & ${SF_SELECTED}
+            StrCmp $R0 ${SF_SELECTED} 0 SkipODEPyd
+            File /nonfatal /r "${BUILT}\panda3d\ode.pyd"
+            SkipODEPyd:
+        !endif
+
+        !ifdef HAVE_PHYSX
+            SectionGetFlags ${SecPhysX} $R0
+            IntOp $R0 $R0 & ${SF_SELECTED}
+            StrCmp $R0 ${SF_SELECTED} 0 SkipPhysXPyd
+            File /nonfatal /r "${BUILT}\panda3d\physx.pyd"
+            SkipPhysXPyd:
+        !endif
+
+        !ifdef HAVE_ROCKET
+            SectionGetFlags ${SecRocket} $R0
+            IntOp $R0 $R0 & ${SF_SELECTED}
+            StrCmp $R0 ${SF_SELECTED} 0 SkipRocketPyd
+            File /nonfatal /r "${BUILT}\panda3d\rocket.pyd"
+            SkipRocketPyd:
+        !endif
+
+        SetOutPath $INSTDIR\pandac\input
+        File /r "${BUILT}\pandac\input\*"
+        SetOutPath $INSTDIR\Pmw
+        File /r /x CVS "${BUILT}\Pmw\*"
+
+        ; Check for a user installation of Python.
+        ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" ""
+        StrCmp $0 "$INSTDIR\python" CheckSystemWidePython 0
+        StrCmp $0 "" CheckSystemWidePython 0
+        IfFileExists "$0\ppython.exe" CheckSystemWidePython 0
+        IfFileExists "$0\python.exe" AskExternalPth 0
+
+        ; Check for a system-wide Python installation.
+        CheckSystemWidePython:
+        ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" ""
+        StrCmp $0 "$INSTDIR\python" SkipExternalPth 0
+        StrCmp $0 "" SkipExternalPth 0
+        IfFileExists "$0\ppython.exe" SkipExternalPth 0
+        IfFileExists "$0\python.exe" AskExternalPth SkipExternalPth
+
+        AskExternalPth:
+        MessageBox MB_YESNO|MB_ICONQUESTION \
+            "Your system already has a ${REGVIEW}-bit copy of Python ${PYVER} installed in:$\r$\n$0$\r$\nWould you like to configure it to be able to use the Panda3D libraries?$\r$\nIf you choose no, you will only be able to use Panda3D's own copy of Python." \
+            IDNO SkipExternalPth
+
+        FileOpen $1 "$0\Lib\site-packages\panda.pth" w
+        FileWrite $1 "$INSTDIR$\r$\n"
+        FileWrite $1 "$INSTDIR\bin$\r$\n"
+        FileClose $1
+        SkipExternalPth:
+    SectionEnd
+
+    !ifdef HAVE_PYTHON
+    Section "Python ${PYVER}" SecPython
+        SectionIn 1 2
+
+        !ifdef REGVIEW
+        SetRegView ${REGVIEW}
+        !endif
+
+        SetDetailsPrint both
+        DetailPrint "Installing Python ${PYVER} (${REGVIEW}-bit)..."
+        SetDetailsPrint listonly
+
+        SetOutPath "$INSTDIR\bin"
+        File /nonfatal "${BUILT}\bin\python*.dll"
+
+        SetOutPath "$INSTDIR\python"
+        File /r "${BUILT}\python\*"
+
+        !ifdef PYEXTRAS
+        SetOutPath "$INSTDIR\python\lib"
+        File /nonfatal /r "${PYEXTRAS}\*"
+        !endif
+
+        SetDetailsPrint both
+        DetailPrint "Adding registry keys for Python..."
+        SetDetailsPrint listonly
+
+        ; Check if a copy of Python is installed for this user.
+        ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" ""
+        StrCmp "$0" "$INSTDIR\python" RegPath 0
+        StrCmp "$0" "" SkipFileCheck 0
+        IfFileExists "$0\python.exe" AskRegPath 0
+        SkipFileCheck:
+
+        ; Check if a system-wide copy of Python is installed.
+        ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" ""
+        StrCmp "$0" "$INSTDIR\python" RegPath 0
+        StrCmp "$0" "" RegPath 0
+        IfFileExists "$0\python.exe" AskRegPath RegPath
+
+        AskRegPath:
+        MessageBox MB_YESNO|MB_ICONQUESTION \
+            "Your system already has a ${REGVIEW}-bit copy of Python ${PYVER} installed in:$\r$\n$0$\r$\n$\r$\nPanda3D installs its own copy of Python ${PYVER}, which will install alongside your existing copy.  Would you like to make Panda's copy the default Python for your user account?" \
+            IDNO SkipRegPath
+
+        RegPath:
+        WriteRegStr HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" "" "$INSTDIR\python"
+        SkipRegPath:
+
+    SectionEnd
+    !endif
+SectionGroupEnd
+
+Function ConfirmPythonSelection
+    ; Check the current state of the "Python" section selection.
+    SectionGetFlags ${SecPython} $R0
+    IntOp $R1 $R0 & ${SF_SELECTED}
+
+    ; Is the "Python" selection deselected?
+    StrCmp $R1 ${SF_SELECTED} SkipCheck 0
+
+    ; Maybe the user just doesn't want Python support at all?
+    SectionGetFlags ${SecPyBindings} $R1
+    IntOp $R1 $R1 & ${SF_SELECTED}
+    StrCmp $R1 ${SF_SELECTED} 0 SkipCheck
+
+    ; Check for a user installation of Python.
+    ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" ""
+    StrCmp $0 "$INSTDIR\python" CheckSystemWidePython 0
+    StrCmp $0 "" CheckSystemWidePython 0
+    IfFileExists "$0\ppython.exe" CheckSystemWidePython 0
+    IfFileExists "$0\python.exe" SkipCheck CheckSystemWidePython
+
+    ; Check for a system-wide Python installation.
+    CheckSystemWidePython:
+    ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" ""
+    StrCmp $0 "$INSTDIR\python" AskConfirmation 0
+    StrCmp $0 "" AskConfirmation 0
+    IfFileExists "$0\ppython.exe" AskConfirmation 0
+    IfFileExists "$0\python.exe" SkipCheck AskConfirmation
+
+    ; No compatible Python version found (that wasn't shipped as part
+    ; of a different Panda3D build.)  Ask the user if he's sure about this.
+    AskConfirmation:
+    MessageBox MB_YESNO|MB_ICONQUESTION \
+        "You do not appear to have a ${REGVIEW}-bit version of Python ${PYVER} installed that is compatible with Panda3D.  Are you sure you don't want Panda to install a compatible copy of Python?$\r$\n$\r$\nIf you choose Yes, you will not be able to do Python development with Panda3D until you install a ${REGVIEW}-bit version of Python ${PYVER} and manually configure it to be able to use Panda3D." \
+        IDYES SkipCheck
+
+    ; User clicked no, so re-enable the select box and abort.
+    IntOp $R0 $R0 | ${SF_SELECTED}
+    SectionSetFlags ${SecPython} $R0
+    Abort
+
+    SkipCheck:
+FunctionEnd
+
+Section "C++ support" SecHeadersLibs
+    SectionIn 1
+
+    SetDetailsPrint both
+    DetailPrint "Installing header files..."
+    SetDetailsPrint listonly
+
+    SetOutPath $INSTDIR\include
+    File /r /x *.exp "${BUILT}\include\*"
+
+    SetDetailsPrint both
+    DetailPrint "Installing library archives..."
+    SetDetailsPrint listonly
+
+    SetOutPath $INSTDIR\lib
+    File /r /x *.exp "${BUILT}\lib\*"
+SectionEnd
+
+!ifdef HAVE_SAMPLES
+Section "Sample programs" SecSamples
+    SectionIn 1
+
+    ; Necessary for proper start menu shortcut installation
+    SetShellVarContext current
+
+    SetDetailsPrint both
+    DetailPrint "Installing sample programs..."
+    SetDetailsPrint listonly
+
+    SetOutPath $INSTDIR\samples
+    File /nonfatal /r /x CVS "${SOURCE}\samples\*"
+
+    SetDetailsPrint both
+    DetailPrint "Creating shortcuts..."
+    SetDetailsPrint listonly
+
+    SetOutPath $INSTDIR
+    WriteINIStr $INSTDIR\Website.url "InternetShortcut" "URL" "https://www.panda3d.org/"
+    WriteINIStr $INSTDIR\Manual.url "InternetShortcut" "URL" "https://www.panda3d.org/manual/index.php"
+    WriteINIStr $INSTDIR\Samples.url "InternetShortcut" "URL" "https://www.panda3d.org/manual/index.php/Sample_Programs_in_the_Distribution"
+    SetOutPath $INSTDIR
+    CreateShortCut "$SMPROGRAMS\${TITLE}\Panda3D Manual.lnk" "$INSTDIR\Manual.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Panda3D Manual"
+    CreateShortCut "$SMPROGRAMS\${TITLE}\Panda3D Website.lnk" "$INSTDIR\Website.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Panda3D Website"
+    CreateShortCut "$SMPROGRAMS\${TITLE}\Sample Program Manual.lnk" "$INSTDIR\Samples.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Sample Program Manual"
+
+    FindFirst $0 $1 $INSTDIR\samples\*
+    loop:
+        StrCmp $1 "" done
+        StrCmp $1 "." next
+        StrCmp $1 ".." next
+        Push $1
+        Push "-"
+        Push " "
+        Call StrRep
+        Call Capitalize
+        Pop $R0
+        StrCpy $READABLE $R0
+        Push $1
+        Push "-"
+        Push "_"
+        Call StrRep
+        Pop $R0
+        StrCpy $MANPAGE $R0
+        DetailPrint "Creating shortcuts for sample program $READABLE"
+        CreateDirectory "$SMPROGRAMS\${TITLE}\Sample Programs\$READABLE"
+        SetOutPath $INSTDIR\samples\$1
+        WriteINIStr $INSTDIR\samples\$1\ManualPage.url "InternetShortcut" "URL" "http://panda3d.org/wiki/index.php/Sample_Programs:_$MANPAGE"
+        CreateShortCut "$SMPROGRAMS\${TITLE}\Sample Programs\$READABLE\Manual Page.lnk" "$INSTDIR\samples\$1\ManualPage.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Manual Entry on this Sample Program"
+        CreateShortCut "$SMPROGRAMS\${TITLE}\Sample Programs\$READABLE\View Source Code.lnk" "$INSTDIR\samples\$1"
+        FindFirst $2 $3 $INSTDIR\samples\$1\*.py
+        iloop:
+            StrCmp $3 "" idone
+            CreateShortCut "$SMPROGRAMS\${TITLE}\Sample Programs\$READABLE\Run $3.lnk" "$INSTDIR\python\python.exe" "-E $3" "$INSTDIR\bin\eggcacher.exe" 0 SW_SHOWMINIMIZED "" "Run $3"
+            CreateShortCut "$INSTDIR\samples\$1\Run $3.lnk" "$INSTDIR\python\python.exe" "-E $3" "$INSTDIR\bin\eggcacher.exe" 0 SW_SHOWMINIMIZED "" "Run $3"
+            FindNext $2 $3
+            goto iloop
+        idone:
+    next:
+        FindNext $0 $1
+        Goto loop
+    done:
+SectionEnd
+!endif
+
+!ifdef HAVE_MAX_PLUGINS
+Section "3ds Max plug-ins" SecMaxPlugins
+    SectionIn 1 3
+
+    SetDetailsPrint both
+    DetailPrint "Installing Autodesk 3ds Max plug-ins..."
+    SetDetailsPrint listonly
+
+    SetOutPath $INSTDIR\plugins
+    File /nonfatal /r "${BUILT}\plugins\*.dle"
+    File /nonfatal /r "${BUILT}\plugins\*.dlo"
+    File "${SOURCE}\doc\INSTALLING-PLUGINS.TXT"
+SectionEnd
+!endif
+
+!ifdef HAVE_MAYA_PLUGINS
+Section "Maya plug-ins" SecMayaPlugins
+    SectionIn 1 3
+
+    SetDetailsPrint both
+    DetailPrint "Installing Autodesk Maya plug-ins..."
+    SetDetailsPrint listonly
+
+    SetOutPath $INSTDIR\plugins
+    File /nonfatal /r "${BUILT}\plugins\*.mll"
+    File /nonfatal /r "${BUILT}\plugins\*.mel"
+    File /nonfatal /r "${BUILT}\plugins\*.ms"
+    File "${SOURCE}\doc\INSTALLING-PLUGINS.TXT"
+SectionEnd
+!endif
+
+Section -post
+    !ifdef REGVIEW
+    SetRegView ${REGVIEW}
+    !endif
+
+    ; Run eggcacher.  We can't do this in SecCore because we haven't
+    ; installed eggcacher at that point yet.
+    SetDetailsPrint both
+    DetailPrint "Preloading .egg files into the model cache..."
+    SetDetailsPrint listonly
+
+    ; We need to set the $PATH for eggcacher.
+    SetOutPath $INSTDIR
+    ReadEnvStr $R0 "PATH"
+    StrCpy $R0 "$INSTDIR\python;$INSTDIR\bin;$R0"
+    System::Call 'Kernel32::SetEnvironmentVariableA(t, t) i("PATH", R0).r2'
+
+    nsExec::ExecToLog '"$INSTDIR\bin\eggcacher.exe" --concise models samples'
+
+    SetDetailsPrint both
+    DetailPrint "Writing the uninstaller ..."
+    SetDetailsPrint listonly
+
+    Delete "$INSTDIR\uninst.exe"
+    WriteUninstaller "$INSTDIR\uninst.exe"
+    WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${TITLE}" "DisplayName" "${TITLE}"
+    WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${TITLE}" "UninstallString" '"$INSTDIR\uninst.exe"'
+    SetOutPath $INSTDIR
+    CreateShortcut "$SMPROGRAMS\${TITLE}\Uninstall ${TITLE}.lnk" "$INSTDIR\uninst.exe" ""
+
+    SetDetailsPrint both
+    DetailPrint "Adding directories to system PATH..."
+    SetDetailsPrint listonly
+
+    # Add the "bin" directory to the PATH.
+    Push "$INSTDIR\python"
+    Call RemoveFromPath
+    Push "$INSTDIR\python\Scripts"
+    Call RemoveFromPath
+    Push "$INSTDIR\bin"
+    Call RemoveFromPath
+    Push "$INSTDIR\python"
+    Call AddToPath
+    Push "$INSTDIR\python\Scripts"
+    Call AddToPath
+    Push "$INSTDIR\bin"
+    Call AddToPath
+
+    # This is needed for the environment variable changes to take effect.
+    DetailPrint "Broadcasting WM_WININICHANGE message..."
+    SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=500
+
+SectionEnd
+
+Section Uninstall
+    SetDetailsPrint listonly
+
+    SetShellVarContext current
+    !ifdef REGVIEW
+    SetRegView ${REGVIEW}
+    !endif
+
+    SetDetailsPrint both
+    DetailPrint "Removing registry entries..."
+    SetDetailsPrint listonly
+
+    DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${TITLE}"
+    DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${TITLE}"
+
+    ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" ""
+    StrCmp $0 "$INSTDIR\python" 0 SkipUnRegHKLM
+    DeleteRegKey HKLM "Software\Python\PythonCore\${PYVER}"
+    SkipUnRegHKLM:
+
+    ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" ""
+    StrCmp $0 "$INSTDIR\python" 0 SkipUnRegHKCU
+    DeleteRegKey HKCU "Software\Python\PythonCore\${PYVER}"
+    SkipUnRegHKCU:
+
+    SetDetailsPrint both
+    DetailPrint "Deleting files..."
+    SetDetailsPrint listonly
+
+    Delete "$INSTDIR\uninst.exe"
+    RMDir /r "$INSTDIR"
+
+    SetDetailsPrint both
+    DetailPrint "Removing Start Menu entries..."
+    SetDetailsPrint listonly
+
+    SetShellVarContext current
+    RMDir /r "$SMPROGRAMS\${TITLE}"
+    SetShellVarContext all
+    RMDir /r "$SMPROGRAMS\${TITLE}"
+
+    SetDetailsPrint both
+    DetailPrint "Removing entries from PATH..."
+    SetDetailsPrint listonly
+
+    Push "$INSTDIR\python"
+    Call un.RemoveFromPath
+    Push "$INSTDIR\python\Scripts"
+    Call un.RemoveFromPath
+    Push "$INSTDIR\bin"
+    Call un.RemoveFromPath
+
+    # This is needed for the environment variable changes to take effect.
+    DetailPrint "Broadcasting WM_WININICHANGE message..."
+    SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=500
+
+SectionEnd
+
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecCore} $(DESC_SecCore)
+  !ifdef HAVE_GL
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenGL} $(DESC_SecOpenGL)
+  !endif
+  !ifdef HAVE_DX9
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecDirect3D9} $(DESC_SecDirect3D9)
+  !endif
+  !ifdef HAVE_OPENAL
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenAL} $(DESC_SecOpenAL)
+  !endif
+  !ifdef HAVE_FMOD
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecFMOD} $(DESC_SecFMOD)
+  !endif
+  !ifdef HAVE_FFMPEG
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecFFMpeg} $(DESC_SecFFMpeg)
+  !endif
+  !ifdef HAVE_BULLET
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecBullet} $(DESC_SecBullet)
+  !endif
+  !ifdef HAVE_ODE
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecODE} $(DESC_SecODE)
+  !endif
+  !ifdef HAVE_PHYSX
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecPhysX} $(DESC_SecPhysX)
+  !endif
+  !ifdef HAVE_ROCKET
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecRocket} $(DESC_SecRocket)
+  !endif
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecTools} $(DESC_SecTools)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings} $(DESC_SecPyBindings)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecHeadersLibs} $(DESC_SecHeadersLibs)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecSamples} $(DESC_SecSamples)
+  !ifdef HAVE_MAX_PLUGINS
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecMaxPlugins} $(DESC_SecMaxPlugins)
+  !endif
+  !ifdef HAVE_MAYA_PLUGINS
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecMayaPlugins} $(DESC_SecMayaPlugins)
+  !endif
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+# --[ Utility Functions ]------------------------------------------------------
+
+; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
+Function IsNT
+        Push $0
+        ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
+        StrCmp $0 "" 0 IsNT_yes
+        ; we are not NT.
+        Pop $0
+        Push 0
+        Return
+        IsNT_yes:
+                ; NT!!!
+                Pop $0
+                Push 1
+FunctionEnd
+
+; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
+Function un.IsNT
+        Push $0
+        ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
+        StrCmp $0 "" 0 unIsNT_yes
+        ; we are not NT.
+        Pop $0
+        Push 0
+        Return
+        unIsNT_yes:
+                ; NT!!!
+                Pop $0
+                Push 1
+FunctionEnd
+
+; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
+Function StrStr
+        Push $0
+        Exch
+        Pop $0 ; $0 now have the string to find
+        Push $1
+        Exch 2
+        Pop $1 ; $1 now have the string to find in
+        Exch
+        Push $2
+        Push $3
+        Push $4
+        Push $5
+        StrCpy $2 -1
+        StrLen $3 $0
+        StrLen $4 $1
+        IntOp $4 $4 - $3
+        unStrStr_loop:
+                IntOp $2 $2 + 1
+                IntCmp $2 $4 0 0 unStrStrReturn_notFound
+                StrCpy $5 $1 $3 $2
+                StrCmp $5 $0 unStrStr_done unStrStr_loop
+        unStrStrReturn_notFound:
+                StrCpy $2 -1
+        unStrStr_done:
+                Pop $5
+                Pop $4
+                Pop $3
+                Exch $2
+                Exch 2
+                Pop $0
+                Pop $1
+FunctionEnd
+
+; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
+Function un.StrStr
+        Push $0
+        Exch
+        Pop $0 ; $0 now have the string to find
+        Push $1
+        Exch 2
+        Pop $1 ; $1 now have the string to find in
+        Exch
+        Push $2
+        Push $3
+        Push $4
+        Push $5
+        StrCpy $2 -1
+        StrLen $3 $0
+        StrLen $4 $1
+        IntOp $4 $4 - $3
+        unStrStr_loop:
+                IntOp $2 $2 + 1
+                IntCmp $2 $4 0 0 unStrStrReturn_notFound
+                StrCpy $5 $1 $3 $2
+                StrCmp $5 $0 unStrStr_done unStrStr_loop
+        unStrStrReturn_notFound:
+                StrCpy $2 -1
+        unStrStr_done:
+                Pop $5
+                Pop $4
+                Pop $3
+                Exch $2
+                Exch 2
+                Pop $0
+                Pop $1
+FunctionEnd
+
+; Capitalizes the first letter of every word.
+Function Capitalize
+        Exch $R0
+        Push $0
+        Push $1
+        Push $2
+
+        StrCpy $0 0
+
+        capNext:
+        ; Grab the next character.
+        StrCpy $1 $R0 1 $0
+        StrCmp $1 '' end
+
+        ; Capitalize it.
+        ${StrFilter} '$1' '+eng' '' '' $1
+        ${StrFilter} '$1' '+rus' '' '' $1
+
+        ; Splice it into the string.
+        StrCpy $2 $R0 $0
+        IntOp $0 $0 + 1
+        StrCpy $R0 $R0 '' $0
+        StrCpy $R0 '$2$1$R0'
+
+        ; Keep looping through the characters until we find a
+        ; delimiter or reach the end of the string.
+        loop:
+        StrCpy $1 $R0 1 $0
+        IntOp $0 $0 + 1
+        StrCmp $1 '' end
+        StrCmp $1 ' ' capNext
+        StrCmp $1 '_' capNext
+        StrCmp $1 '-' capNext
+        StrCmp $1 '(' capNext
+        StrCmp $1 '[' capNext
+        Goto loop
+
+        end:
+        Pop $2
+        Pop $1
+        Pop $0
+        Exch $R0
+FunctionEnd
+
+; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
+; Commentary and smarter ';' checking by Jon Parise <[email protected]>
+Function AddToPath
+        Exch $0
+        Push $1
+        Push $2
+        Push $3
+        Call IsNT
+        Pop $1
+
+        DetailPrint "Adding to PATH: $0"
+
+        StrCmp $1 1 AddToPath_NT
+                ; We're not on NT, so modify the AUTOEXEC.BAT file.
+                StrCpy $1 $WINDIR 2
+                FileOpen $1 "$1\autoexec.bat" a
+                FileSeek $1 0 END
+                GetFullPathName /SHORT $0 $0
+                FileWrite $1 "$\r$\nSET PATH=%PATH%;$0$\r$\n"
+                FileClose $1
+                Goto AddToPath_done
+
+        AddToPath_NT:
+                ReadRegStr $1 HKCU "Environment" "PATH"
+                Call IsUserAdmin
+                Pop $3
+                ; If this is an Admin user, use the System env. variable instead of the user's env. variable
+                StrCmp $3 1 0 +2
+                        ReadRegStr $1 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH"
+
+                ; If the PATH string is empty, jump over the mangling routines.
+                StrCmp $1 "" AddToPath_NTdoIt
+
+                ; Pull off the last character of the PATH string.  If it's a semicolon,
+                ; we don't need to add another one, so jump to the section where we
+                ; append the new PATH component(s).
+                StrCpy $2 $1 1 -1
+                StrCmp $2 ";" AddToPath_NTAddPath AddToPath_NTAddSemi
+
+                AddToPath_NTAddSemi:
+                        StrCpy $1 "$1;"
+                        Goto AddToPath_NTAddPath
+                AddToPath_NTAddPath:
+                        StrCpy $0 "$1$0"
+                        Goto AddToPath_NTdoIt
+                AddToPath_NTdoIt:
+                        Call IsUserAdmin
+                        Pop $3
+                        StrCmp $3 1 0 NotAdmin
+                                WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $0
+                                Goto AddToPath_done
+
+                        NotAdmin:
+                                WriteRegExpandStr HKCU "Environment" "PATH" $0
+        AddToPath_done:
+                Pop $3
+                Pop $2
+                Pop $1
+                Pop $0
+FunctionEnd
+
+; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
+Function RemoveFromPath
+        Exch $0
+        Push $1
+        Push $2
+        Push $3
+        Push $4
+        Push $5
+        Call IsNT
+        Pop $1
+
+        DetailPrint "Removing from PATH: $0"
+
+        StrCmp $1 1 unRemoveFromPath_NT
+                ; Not on NT
+                StrCpy $1 $WINDIR 2
+                FileOpen $1 "$1\autoexec.bat" r
+                GetTempFileName $4
+                FileOpen $2 $4 w
+                GetFullPathName /SHORT $0 $0
+                StrCpy $0 "SET PATH=%PATH%;$0"
+                SetRebootFlag true
+                Goto unRemoveFromPath_dosLoop
+
+                unRemoveFromPath_dosLoop:
+                        FileRead $1 $3
+                        StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoop
+                        StrCmp $3 "$0$\n" unRemoveFromPath_dosLoop
+                        StrCmp $3 "$0" unRemoveFromPath_dosLoop
+                        StrCmp $3 "" unRemoveFromPath_dosLoopEnd
+                        FileWrite $2 $3
+                        Goto unRemoveFromPath_dosLoop
+
+                unRemoveFromPath_dosLoopEnd:
+                        FileClose $2
+                        FileClose $1
+                        StrCpy $1 $WINDIR 2
+                        Delete "$1\autoexec.bat"
+                        CopyFiles /SILENT $4 "$1\autoexec.bat"
+                        Delete $4
+                        Goto unRemoveFromPath_done
+
+                unRemoveFromPath_NT:
+                        StrLen $2 $0
+                        Call IsUserAdmin
+                        Pop $5
+                        StrCmp $5 1 0 NotAdmin
+                                ReadRegStr $1 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH"
+                                Push $1
+                                Push $0
+                                Call StrStr ; Find $0 in $1
+                                Pop $0 ; pos of our dir
+                                IntCmp $0 -1 unRemoveFromPath_done
+                                        ; else, it is in path
+                                        StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir
+                                        IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';')
+                                        IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon.
+                                        StrLen $0 $1
+                                        StrCpy $1 $1 $0 $2
+                                        StrCpy $3 "$3$1"
+                                        WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $3
+                                        Goto unRemoveFromPath_done
+
+
+                        NotAdmin:
+                                ReadRegStr $1 HKCU "Environment" "PATH"
+                                Push $1
+                                Push $0
+                                Call StrStr ; Find $0 in $1
+                                Pop $0 ; pos of our dir
+                                IntCmp $0 -1 unRemoveFromPath_done
+                                        ; else, it is in path
+                                        StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir
+                                        IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';')
+                                        IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon.
+                                        StrLen $0 $1
+                                        StrCpy $1 $1 $0 $2
+                                        StrCpy $3 "$3$1"
+                                        WriteRegExpandStr HKCU "Environment" "PATH" $3
+
+                unRemoveFromPath_done:
+                        Pop $5
+                        Pop $4
+                        Pop $3
+                        Pop $2
+                        Pop $1
+                        Pop $0
+FunctionEnd
+
+; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
+Function un.RemoveFromPath
+        Exch $0
+        Push $1
+        Push $2
+        Push $3
+        Push $4
+        Push $5
+        Call un.IsNT
+        Pop $1
+        StrCmp $1 1 unRemoveFromPath_NT
+                ; Not on NT
+                StrCpy $1 $WINDIR 2
+                FileOpen $1 "$1\autoexec.bat" r
+                GetTempFileName $4
+                FileOpen $2 $4 w
+                GetFullPathName /SHORT $0 $0
+                StrCpy $0 "SET PATH=%PATH%;$0"
+                SetRebootFlag true
+                Goto unRemoveFromPath_dosLoop
+
+                unRemoveFromPath_dosLoop:
+                        FileRead $1 $3
+                        StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoop
+                        StrCmp $3 "$0$\n" unRemoveFromPath_dosLoop
+                        StrCmp $3 "$0" unRemoveFromPath_dosLoop
+                        StrCmp $3 "" unRemoveFromPath_dosLoopEnd
+                        FileWrite $2 $3
+                        Goto unRemoveFromPath_dosLoop
+
+                unRemoveFromPath_dosLoopEnd:
+                        FileClose $2
+                        FileClose $1
+                        StrCpy $1 $WINDIR 2
+                        Delete "$1\autoexec.bat"
+                        CopyFiles /SILENT $4 "$1\autoexec.bat"
+                        Delete $4
+                        Goto unRemoveFromPath_done
+
+                unRemoveFromPath_NT:
+                        StrLen $2 $0
+                        Call un.IsUserAdmin
+                        Pop $5
+                        StrCmp $5 1 0 NotAdmin
+                                ReadRegStr $1 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH"
+                                Push $1
+                                Push $0
+                                Call un.StrStr ; Find $0 in $1
+                                Pop $0 ; pos of our dir
+                                IntCmp $0 -1 unRemoveFromPath_done
+                                        ; else, it is in path
+                                        StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir
+                                        IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';')
+                                        IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon.
+                                        StrLen $0 $1
+                                        StrCpy $1 $1 $0 $2
+                                        StrCpy $3 "$3$1"
+                                        WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $3
+                                        Goto unRemoveFromPath_done
+
+
+                        NotAdmin:
+                                ReadRegStr $1 HKCU "Environment" "PATH"
+                                Push $1
+                                Push $0
+                                Call un.StrStr ; Find $0 in $1
+                                Pop $0 ; pos of our dir
+                                IntCmp $0 -1 unRemoveFromPath_done
+                                        ; else, it is in path
+                                        StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir
+                                        IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';')
+                                        IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon.
+                                        StrLen $0 $1
+                                        StrCpy $1 $1 $0 $2
+                                        StrCpy $3 "$3$1"
+                                        WriteRegExpandStr HKCU "Environment" "PATH" $3
+
+                unRemoveFromPath_done:
+                        Pop $5
+                        Pop $4
+                        Pop $3
+                        Pop $2
+                        Pop $1
+                        Pop $0
+FunctionEnd
+
+; From: http://nsis.sourceforge.net/archive/nsisweb.php?page=329&instances=0,11
+; Localized by Ben Johnson ([email protected])
+Function IsUserAdmin
+        Push $0
+        Push $1
+        Push $2
+        Push $3
+        Call IsNT
+        Pop $1
+
+        ClearErrors
+        UserInfo::GetName
+        ;IfErrors Win9x
+        Pop $2
+        UserInfo::GetAccountType
+        Pop $3
+
+        ; Compare results of IsNT with "1"
+        StrCmp $1 1 0 NotNT
+                ;This is NT
+
+
+                StrCmp $3 "Admin" 0 NotAdmin
+                        ; Observation: I get here when running Win98SE. (Lilla)
+                        ; The functions UserInfo.dll looks for are there on Win98 too,
+                        ; but just don't work. So UserInfo.dll, knowing that admin isn't required
+                        ; on Win98, returns admin anyway. (per kichik)
+                        ; MessageBox MB_OK 'User "$R1" is in the Administrators group'
+                        Pop $3
+                        Pop $2
+                        Pop $1
+                        Pop $0
+
+                        Push 1
+                        Return
+
+                NotAdmin:
+                        ; You should still check for an empty string because the functions
+                        ; UserInfo.dll looks for may not be present on Windows 95. (per kichik)
+
+                        #StrCmp $2 "" Win9x
+                        #StrCpy $0 0
+                        ;MessageBox MB_OK 'User "$2" is in the "$3" group'
+                        Pop $3
+                        Pop $2
+                        Pop $1
+                        Pop $0
+
+                        Push 0
+                        Return
+
+        ;Because we use IsNT, this is redundant.
+        #Win9x:
+        #       ; comment/message below is by UserInfo.nsi author:
+        #       ; This one means you don't need to care about admin or
+        #       ; not admin because Windows 9x doesn't either
+        #       ;MessageBox MB_OK "Error! This DLL can't run under Windows 9x!"
+        #       StrCpy $0 0
+
+        NotNT:
+                ;We are not NT
+                ;Win9x doesn't have "admin" users.
+                ;Let the user do whatever.
+                Pop $3
+                Pop $2
+                Pop $1
+                Pop $0
+
+                Push 1
+
+FunctionEnd
+
+Function un.IsUserAdmin
+        Push $0
+        Push $1
+        Push $2
+        Push $3
+        Call un.IsNT
+        Pop $1
+
+        ClearErrors
+        UserInfo::GetName
+        ;IfErrors Win9x
+        Pop $2
+        UserInfo::GetAccountType
+        Pop $3
+
+        ; Compare results of IsNT with "1"
+        StrCmp $1 1 0 NotNT
+                ;This is NT
+
+
+                StrCmp $3 "Admin" 0 NotAdmin
+                        ; Observation: I get here when running Win98SE. (Lilla)
+                        ; The functions UserInfo.dll looks for are there on Win98 too,
+                        ; but just don't work. So UserInfo.dll, knowing that admin isn't required
+                        ; on Win98, returns admin anyway. (per kichik)
+                        ; MessageBox MB_OK 'User "$R1" is in the Administrators group'
+                        Pop $3
+                        Pop $2
+                        Pop $1
+                        Pop $0
+
+                        Push 1
+                        Return
+
+                NotAdmin:
+                        ; You should still check for an empty string because the functions
+                        ; UserInfo.dll looks for may not be present on Windows 95. (per kichik)
+
+                        #StrCmp $2 "" Win9x
+                        #StrCpy $0 0
+                        ;MessageBox MB_OK 'User "$2" is in the "$3" group'
+                        Pop $3
+                        Pop $2
+                        Pop $1
+                        Pop $0
+
+                        Push 0
+                        Return
+
+        ;Because we use IsNT, this is redundant.
+        #Win9x:
+        #       ; comment/message below is by UserInfo.nsi author:
+        #       ; This one means you don't need to care about admin or
+        #       ; not admin because Windows 9x doesn't either
+        #       ;MessageBox MB_OK "Error! This DLL can't run under Windows 9x!"
+        #       StrCpy $0 0
+
+        NotNT:
+                ;We are not NT
+                ;Win9x doesn't have "admin" users.
+                ;Let the user do whatever.
+                Pop $3
+                Pop $2
+                Pop $1
+                Pop $0
+
+                Push 1
+
+FunctionEnd
+
+Function StrRep
+
+  ;Written by dirtydingus 2003-02-20 04:30:09
+  ; USAGE
+  ;Push String to do replacement in (haystack)
+  ;Push String to replace (needle)
+  ;Push Replacement
+  ;Call StrRep
+  ;Pop $R0 result
+  ;StrCpy $Result STR $R0
+
+  Exch $R4 ; $R4 = Replacement String
+  Exch
+  Exch $R3 ; $R3 = String to replace (needle)
+  Exch 2
+  Exch $R1 ; $R1 = String to do replacement in (haystack)
+  Push $R2 ; Replaced haystack
+  Push $R5 ; Len (needle)
+  Push $R6 ; len (haystack)
+  Push $R7 ; Scratch reg
+  StrCpy $R2 ""
+  StrLen $R5 $R3
+  StrLen $R6 $R1
+loop:
+  StrCpy $R7 $R1 $R5
+  StrCmp $R7 $R3 found
+  StrCpy $R7 $R1 1 ; - optimization can be removed if U know len needle=1
+  StrCpy $R2 "$R2$R7"
+  StrCpy $R1 $R1 $R6 1
+  StrCmp $R1 "" done loop
+found:
+  StrCpy $R2 "$R2$R4"
+  StrCpy $R1 $R1 $R6 $R5
+  StrCmp $R1 "" done loop
+done:
+  StrCpy $R3 $R2
+  Pop $R7
+  Pop $R6
+  Pop $R5
+  Pop $R2
+  Pop $R1
+  Pop $R4
+  Exch $R3
+
+FunctionEnd

+ 18 - 22
makepanda/makepanda.py

@@ -949,7 +949,7 @@ def CompileCxx(obj,src,opts):
             if PkgSkip("TOUCHINPUT") == 0:
             if PkgSkip("TOUCHINPUT") == 0:
                 cmd += "/DWINVER=0x601 "
                 cmd += "/DWINVER=0x601 "
             cmd += "/Fo" + obj + " /nologo /c"
             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"
                 cmd += " /arch:SSE2"
             for x in ipath: cmd += " /I" + x
             for x in ipath: cmd += " /I" + x
             for (opt,dir) in INCDIRECTORIES:
             for (opt,dir) in INCDIRECTORIES:
@@ -1160,7 +1160,7 @@ def CompileCxx(obj,src,opts):
                 if optlevel >= 4 or GetTarget() == "android":
                 if optlevel >= 4 or GetTarget() == "android":
                     cmd += " -fno-rtti"
                     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"
             cmd += " -msse2"
 
 
         if optlevel >= 3:
         if optlevel >= 3:
@@ -1705,7 +1705,7 @@ def RunGenPyCode(target, inputs, opts):
     if (PkgSkip("PYTHON") != 0):
     if (PkgSkip("PYTHON") != 0):
         return
         return
 
 
-    cmdstr = sys.executable + " "
+    cmdstr = BracketNameWithQuotes(SDK["PYTHONEXEC"]) + " "
     if sys.version_info >= (2, 6):
     if sys.version_info >= (2, 6):
         cmdstr += "-B "
         cmdstr += "-B "
 
 
@@ -1729,7 +1729,7 @@ def RunGenPyCode(target, inputs, opts):
 def FreezePy(target, inputs, opts):
 def FreezePy(target, inputs, opts):
     assert len(inputs) > 0
     assert len(inputs) > 0
     # Make sure this function isn't called before genpycode is run.
     # Make sure this function isn't called before genpycode is run.
-    cmdstr = sys.executable + " "
+    cmdstr = BracketNameWithQuotes(SDK["PYTHONEXEC"]) + " "
     if sys.version_info >= (2, 6):
     if sys.version_info >= (2, 6):
         cmdstr += "-B "
         cmdstr += "-B "
 
 
@@ -1757,7 +1757,7 @@ def FreezePy(target, inputs, opts):
 def Package(target, inputs, opts):
 def Package(target, inputs, opts):
     assert len(inputs) == 1
     assert len(inputs) == 1
     # Invoke the ppackage script.
     # Invoke the ppackage script.
-    command = sys.executable + " "
+    command = BracketNameWithQuotes(SDK["PYTHONEXEC"]) + " "
     if GetOptimizeOption(opts) >= 4:
     if GetOptimizeOption(opts) >= 4:
         command += "-OO "
         command += "-OO "
 
 
@@ -3192,6 +3192,7 @@ if (not RUNTIME):
   OPTS=['DIR:panda/src/pnmimage', 'BUILDING:PANDA',  'ZLIB']
   OPTS=['DIR:panda/src/pnmimage', 'BUILDING:PANDA',  'ZLIB']
   TargetAdd('p3pnmimage_composite1.obj', opts=OPTS, input='p3pnmimage_composite1.cxx')
   TargetAdd('p3pnmimage_composite1.obj', opts=OPTS, input='p3pnmimage_composite1.cxx')
   TargetAdd('p3pnmimage_composite2.obj', opts=OPTS, input='p3pnmimage_composite2.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']
   OPTS=['DIR:panda/src/pnmimage', 'ZLIB']
   IGATEFILES=GetDirectoryContents('panda/src/pnmimage', ["*.h", "*_composite*.cxx"])
   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='p3pnmimagetypes_composite2.obj')
   TargetAdd('libpanda.dll', input='p3pnmimage_composite1.obj')
   TargetAdd('libpanda.dll', input='p3pnmimage_composite1.obj')
   TargetAdd('libpanda.dll', input='p3pnmimage_composite2.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_composite1.obj')
   TargetAdd('libpanda.dll', input='p3text_composite2.obj')
   TargetAdd('libpanda.dll', input='p3text_composite2.obj')
   TargetAdd('libpanda.dll', input='p3tform_composite1.obj')
   TargetAdd('libpanda.dll', input='p3tform_composite1.obj')
@@ -4609,7 +4611,7 @@ if (PkgSkip("DIRECT")==0):
   OPTS=['DIR:direct/src/dcparser', 'WITHINPANDA', 'BUILDING:DIRECT', 'BISONPREFIX_dcyy']
   OPTS=['DIR:direct/src/dcparser', 'WITHINPANDA', 'BUILDING:DIRECT', 'BISONPREFIX_dcyy']
   CreateFile(GetOutputDir()+"/include/dcParser.h")
   CreateFile(GetOutputDir()+"/include/dcParser.h")
   TargetAdd('p3dcparser_dcParser.obj', opts=OPTS, input='dcParser.yxx')
   TargetAdd('p3dcparser_dcParser.obj', opts=OPTS, input='dcParser.yxx')
-  TargetAdd('dcParser.h', input='p3egg_parser.obj', opts=['DEPENDENCYONLY'])
+  TargetAdd('dcParser.h', input='p3dcparser_dcParser.obj', opts=['DEPENDENCYONLY'])
   TargetAdd('p3dcparser_dcLexer.obj', opts=OPTS, input='dcLexer.lxx')
   TargetAdd('p3dcparser_dcLexer.obj', opts=OPTS, input='dcLexer.lxx')
   TargetAdd('p3dcparser_composite1.obj', opts=OPTS, input='p3dcparser_composite1.cxx')
   TargetAdd('p3dcparser_composite1.obj', opts=OPTS, input='p3dcparser_composite1.cxx')
   TargetAdd('p3dcparser_composite2.obj', opts=OPTS, input='p3dcparser_composite2.cxx')
   TargetAdd('p3dcparser_composite2.obj', opts=OPTS, input='p3dcparser_composite2.cxx')
@@ -6171,7 +6173,7 @@ except:
 #
 #
 ##########################################################################################
 ##########################################################################################
 
 
-def MakeInstallerNSIS(file, fullname, smdirectory, installdir):
+def MakeInstallerNSIS(file, title, installdir):
     if (os.path.isfile(file)):
     if (os.path.isfile(file)):
         os.remove(file)
         os.remove(file)
     elif (os.path.isdir(file)):
     elif (os.path.isdir(file)):
@@ -6199,7 +6201,7 @@ def MakeInstallerNSIS(file, fullname, smdirectory, installdir):
         shutil.move("direct\\src\\plugin_installer\\p3d-setup.exe", file)
         shutil.move("direct\\src\\plugin_installer\\p3d-setup.exe", file)
         return
         return
 
 
-    print("Building "+fullname+" installer. This can take up to an hour.")
+    print("Building "+title+" installer. This can take up to an hour.")
     if (COMPRESSOR != "lzma"):
     if (COMPRESSOR != "lzma"):
         print("Note: you are using zlib, which is faster, but lzma gives better compression.")
         print("Note: you are using zlib, which is faster, but lzma gives better compression.")
     if (os.path.exists("nsis-output.exe")):
     if (os.path.exists("nsis-output.exe")):
@@ -6210,19 +6212,13 @@ def MakeInstallerNSIS(file, fullname, smdirectory, installdir):
 
 
     nsis_defs = {
     nsis_defs = {
         'COMPRESSOR'  : COMPRESSOR,
         'COMPRESSOR'  : COMPRESSOR,
-        'NAME'        : fullname,
-        'SMDIRECTORY' : smdirectory,
+        'TITLE'       : title,
         'INSTALLDIR'  : installdir,
         'INSTALLDIR'  : installdir,
         'OUTFILE'     : os.path.join(psource, 'nsis-output.exe'),
         'OUTFILE'     : os.path.join(psource, 'nsis-output.exe'),
         'LICENSE'     : os.path.join(panda, 'LICENSE'),
         'LICENSE'     : os.path.join(panda, 'LICENSE'),
-        'LANGUAGE'    : "English",
-        'RUNTEXT'     : "Visit the Panda Manual",
-        'IBITMAP'     : "panda-install.bmp",
-        'UBITMAP'     : "panda-install.bmp",
-        'PANDA'       : panda,
+        'BUILT'       : panda,
+        'SOURCE'      : psource,
         'PYVER'       : SDK["PYTHONVERSION"][6:9],
         'PYVER'       : SDK["PYTHONVERSION"][6:9],
-        'PANDACONF'   : os.path.join(panda, 'etc'),
-        'PSOURCE'     : psource,
         'PYEXTRAS'    : os.path.join(os.path.abspath(GetThirdpartyBase()), 'win-extras'),
         'PYEXTRAS'    : os.path.join(os.path.abspath(GetThirdpartyBase()), 'win-extras'),
         'REGVIEW'     : regview,
         'REGVIEW'     : regview,
     }
     }
@@ -6236,7 +6232,7 @@ def MakeInstallerNSIS(file, fullname, smdirectory, installdir):
         for item in nsis_defs.items():
         for item in nsis_defs.items():
             cmd += ' -D%s="%s"' % item
             cmd += ' -D%s="%s"' % item
 
 
-    cmd += ' "%s"' % (os.path.join(psource, 'direct', 'src', 'directscripts', 'packpanda.nsi'))
+    cmd += ' "%s"' % (os.path.join(psource, 'makepanda', 'installer.nsi'))
     oscmd(cmd)
     oscmd(cmd)
     os.rename("nsis-output.exe", file)
     os.rename("nsis-output.exe", file)
 
 
@@ -6761,14 +6757,14 @@ try:
             if (GetOptimize() <= 2): dbg = "-dbg"
             if (GetOptimize() <= 2): dbg = "-dbg"
             if GetTargetArch() == 'x64':
             if GetTargetArch() == 'x64':
                 if (RUNTIME):
                 if (RUNTIME):
-                    MakeInstallerNSIS("Panda3D-Runtime-"+VERSION+dbg+"-x64.exe", "Panda3D", "Panda3D "+VERSION, "C:\\Panda3D-"+VERSION+"-x64")
+                    MakeInstallerNSIS("Panda3D-Runtime-"+VERSION+dbg+"-x64.exe", "Panda3D "+VERSION, "C:\\Panda3D-"+VERSION+"-x64")
                 else:
                 else:
-                    MakeInstallerNSIS("Panda3D-"+VERSION+dbg+"-x64.exe", "Panda3D", "Panda3D "+VERSION, "C:\\Panda3D-"+VERSION+"-x64")
+                    MakeInstallerNSIS("Panda3D-"+VERSION+dbg+"-x64.exe", "Panda3D SDK "+VERSION, "C:\\Panda3D-"+VERSION+"-x64")
             else:
             else:
                 if (RUNTIME):
                 if (RUNTIME):
-                    MakeInstallerNSIS("Panda3D-Runtime-"+VERSION+dbg+".exe", "Panda3D", "Panda3D "+VERSION, "C:\\Panda3D-"+VERSION)
+                    MakeInstallerNSIS("Panda3D-Runtime-"+VERSION+dbg+".exe", "Panda3D "+VERSION, "C:\\Panda3D-"+VERSION)
                 else:
                 else:
-                    MakeInstallerNSIS("Panda3D-"+VERSION+dbg+".exe", "Panda3D", "Panda3D "+VERSION, "C:\\Panda3D-"+VERSION)
+                    MakeInstallerNSIS("Panda3D-"+VERSION+dbg+".exe", "Panda3D SDK "+VERSION, "C:\\Panda3D-"+VERSION)
         elif (target == 'linux'):
         elif (target == 'linux'):
             MakeInstallerLinux()
             MakeInstallerLinux()
         elif (target == 'darwin'):
         elif (target == 'darwin'):

+ 5 - 1
makepanda/makepandacore.py

@@ -210,6 +210,8 @@ def PrettyTime(t):
     return "%d sec" % (seconds)
     return "%d sec" % (seconds)
 
 
 def ProgressOutput(progress, msg, target = None):
 def ProgressOutput(progress, msg, target = None):
+    sys.stdout.flush()
+    sys.stderr.flush()
     prefix = ""
     prefix = ""
     thisthread = threading.currentThread()
     thisthread = threading.currentThread()
     if thisthread is MAINTHREAD:
     if thisthread is MAINTHREAD:
@@ -235,6 +237,8 @@ def ProgressOutput(progress, msg, target = None):
         suffix = GetColor()
         suffix = GetColor()
 
 
     print(''.join((prefix, msg, suffix)))
     print(''.join((prefix, msg, suffix)))
+    sys.stdout.flush()
+    sys.stderr.flush()
 
 
 def exit(msg = ""):
 def exit(msg = ""):
     sys.stdout.flush()
     sys.stdout.flush()
@@ -498,7 +502,7 @@ def oscmd(cmd, ignoreError = False):
         print(GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor())
         print(GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor())
     sys.stdout.flush()
     sys.stdout.flush()
 
 
-    if sys.platform == "win32":
+    if sys.platform in ("win32", "cygwin"):
         exe = cmd.split()[0]
         exe = cmd.split()[0]
         exe_path = LocateBinary(exe)
         exe_path = LocateBinary(exe)
         if exe_path is None:
         if exe_path is None:

+ 0 - 0
direct/src/directscripts/panda-install.bmp → makepanda/panda-install.bmp


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

@@ -156,7 +156,7 @@ get_hit_fraction() const {
 //       Access: Published
 //       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PandaNode *BulletRayHit::
+const PandaNode *BulletRayHit::
 get_node() const {
 get_node() const {
 
 
   return (_object) ? (PandaNode *)_object->getUserPointer() : NULL;
   return (_object) ? (PandaNode *)_object->getUserPointer() : NULL;

+ 1 - 1
panda/src/bullet/bulletAllHitsRayResult.h

@@ -33,7 +33,7 @@ struct EXPCL_PANDABULLET BulletRayHit {
 PUBLISHED:
 PUBLISHED:
   INLINE static BulletRayHit empty();
   INLINE static BulletRayHit empty();
 
 
-  PandaNode *get_node() const;
+  const PandaNode *get_node() const;
   LPoint3 get_hit_pos() const;
   LPoint3 get_hit_pos() const;
   LVector3 get_hit_normal() const;
   LVector3 get_hit_normal() const;
   PN_stdfloat get_hit_fraction() const;
   PN_stdfloat get_hit_fraction() const;

+ 5 - 4
panda/src/bullet/bulletWorld.cxx

@@ -608,11 +608,12 @@ remove_vehicle(BulletVehicle *vehicle) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BulletWorld::attach_constraint
 //     Function: BulletWorld::attach_constraint
 //       Access: Published
 //       Access: Published
-//  Description: Deprecated!
-//               Please use BulletWorld::attach
+//  Description: Attaches a single constraint to a world. Collision
+//               checks between the linked objects will be disabled
+//               if the second parameter is set to TRUE.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void BulletWorld::
 void BulletWorld::
-attach_constraint(BulletConstraint *constraint) {
+attach_constraint(BulletConstraint *constraint, bool linked_collision) {
 
 
   nassertv(constraint);
   nassertv(constraint);
 
 
@@ -622,7 +623,7 @@ attach_constraint(BulletConstraint *constraint) {
 
 
   if (found == _constraints.end()) {
   if (found == _constraints.end()) {
     _constraints.push_back(constraint);
     _constraints.push_back(constraint);
-    _world->addConstraint(constraint->ptr());
+    _world->addConstraint(constraint->ptr(), linked_collision);
   }
   }
   else {
   else {
     bullet_cat.warning() << "constraint already attached" << endl;
     bullet_cat.warning() << "constraint already attached" << endl;

+ 2 - 1
panda/src/bullet/bulletWorld.h

@@ -70,6 +70,8 @@ PUBLISHED:
 
 
   // Attach/Remove
   // Attach/Remove
   void attach(TypedObject *object);
   void attach(TypedObject *object);
+  void attach_constraint(BulletConstraint *constraint, bool linked_collision=false);
+
   void remove(TypedObject *object);
   void remove(TypedObject *object);
 
 
   // Ghost object
   // Ghost object
@@ -172,7 +174,6 @@ PUBLISHED: // Deprecated methods, will become private soon
   void attach_character(BulletBaseCharacterControllerNode *node);
   void attach_character(BulletBaseCharacterControllerNode *node);
   void remove_character(BulletBaseCharacterControllerNode *node);
   void remove_character(BulletBaseCharacterControllerNode *node);
 
 
-  void attach_constraint(BulletConstraint *constraint);
   void remove_constraint(BulletConstraint *constraint);
   void remove_constraint(BulletConstraint *constraint);
 
 
 public:
 public:

+ 10 - 6
panda/src/collide/collisionBox.cxx

@@ -144,15 +144,19 @@ test_intersection(const CollisionEntry &entry) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CollisionBox::
 void CollisionBox::
 xform(const LMatrix4 &mat) {
 xform(const LMatrix4 &mat) {
+  _min = _min * mat;
+  _max = _max * mat;
   _center = _center * mat;
   _center = _center * mat;
-  for(int v = 0; v < 8; v++)
+  for(int v = 0; v < 8; v++) {
     _vertex[v] = _vertex[v] * mat;
     _vertex[v] = _vertex[v] * mat;
-  for(int p = 0; p < 6 ; p++)
+  }
+  for(int p = 0; p < 6 ; p++) {
     _planes[p] = set_plane(p);
     _planes[p] = set_plane(p);
-  _x = _vertex[0].get_x()-_center.get_x(); 
-  _y = _vertex[0].get_y()-_center.get_y();
-  _z = _vertex[0].get_z()-_center.get_z();
-  _radius = sqrt( _x*_x + _y*_y + _z*_z );
+  }
+  _x = _vertex[0].get_x() - _center.get_x();
+  _y = _vertex[0].get_y() - _center.get_y();
+  _z = _vertex[0].get_z() - _center.get_z();
+  _radius = sqrt(_x * _x + _y * _y + _z * _z);
   setup_box();
   setup_box();
   mark_viz_stale();
   mark_viz_stale();
   mark_internal_bounds_stale();
   mark_internal_bounds_stale();

+ 45 - 24
panda/src/display/graphicsStateGuardian.cxx

@@ -1407,22 +1407,29 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     static const CPT_InternalName IN_quadraticAttenuation("quadraticAttenuation");
     static const CPT_InternalName IN_quadraticAttenuation("quadraticAttenuation");
 
 
     if (attrib == IN_ambient) {
     if (attrib == IN_ambient) {
-#ifndef NDEBUG
       Light *light = np.node()->as_light();
       Light *light = np.node()->as_light();
       nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
       nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
-#endif
-      // Lights don't currently have an ambient color in Panda3D.
-      // We still have to support the attribute.
-      t.set_row(3, LColor(0.0f, 0.0f, 0.0f, 1.0f));
+      if (np.node()->is_of_type(AmbientLight::get_class_type())) {
+        LColor c = light->get_color();
+        c.componentwise_mult(_light_color_scale);
+        t.set_row(3, c);
+      } else {
+        // Non-ambient lights don't currently have an ambient color in Panda3D.
+        t.set_row(3, LColor(0.0f, 0.0f, 0.0f, 1.0f));
+      }
       return &t;
       return &t;
 
 
     } else if (attrib == IN_diffuse) {
     } else if (attrib == IN_diffuse) {
       Light *light = np.node()->as_light();
       Light *light = np.node()->as_light();
       nassertr(light != (Light *)NULL, &LMatrix4::ones_mat());
       nassertr(light != (Light *)NULL, &LMatrix4::ones_mat());
-
-      LColor c = light->get_color();
-      c.componentwise_mult(_light_color_scale);
-      t.set_row(3, c);
+      if (np.node()->is_of_type(AmbientLight::get_class_type())) {
+        // Ambient light has no diffuse color.
+        t.set_row(3, LColor(0.0f, 0.0f, 0.0f, 1.0f));
+      } else {
+        LColor c = light->get_color();
+        c.componentwise_mult(_light_color_scale);
+        t.set_row(3, c);
+      }
       return &t;
       return &t;
 
 
     } else if (attrib == IN_specular) {
     } else if (attrib == IN_specular) {
@@ -1432,7 +1439,11 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       return &t;
       return &t;
 
 
     } else if (attrib == IN_position) {
     } else if (attrib == IN_position) {
-      if (np.node()->is_of_type(DirectionalLight::get_class_type())) {
+      if (np.node()->is_of_type(AmbientLight::get_class_type())) {
+        // Ambient light has no position.
+        t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
+        return &t;
+      } else if (np.node()->is_of_type(DirectionalLight::get_class_type())) {
         DirectionalLight *light;
         DirectionalLight *light;
         DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
         DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
 
 
@@ -1458,7 +1469,11 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       }
       }
 
 
     } else if (attrib == IN_halfVector) {
     } else if (attrib == IN_halfVector) {
-      if (np.node()->is_of_type(DirectionalLight::get_class_type())) {
+      if (np.node()->is_of_type(AmbientLight::get_class_type())) {
+        // Ambient light has no half-vector.
+        t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
+        return &t;
+      } else if (np.node()->is_of_type(DirectionalLight::get_class_type())) {
         DirectionalLight *light;
         DirectionalLight *light;
         DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
         DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
 
 
@@ -1490,19 +1505,25 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       }
       }
 
 
     } else if (attrib == IN_spotDirection) {
     } else if (attrib == IN_spotDirection) {
-      LightLensNode *light;
-      DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
-      Lens *lens = light->get_lens();
-      nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
-
-      CPT(TransformState) transform =
-        get_scene()->get_cs_world_transform()->compose(
-          np.get_transform(_scene_setup->get_scene_root().get_parent()));
-
-      const LMatrix4 &light_mat = transform->get_mat();
-      LVector3 dir = lens->get_view_vector() * light_mat;
-      t.set_row(3, dir);
-      return &t;
+      if (np.node()->is_of_type(AmbientLight::get_class_type())) {
+        // Ambient light has no spot direction.
+        t.set_row(3, LVector3(0.0f, 0.0f, 0.0f));
+        return &t;
+      } else {
+        LightLensNode *light;
+        DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
+        Lens *lens = light->get_lens();
+        nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
+
+        CPT(TransformState) transform =
+          get_scene()->get_cs_world_transform()->compose(
+            np.get_transform(_scene_setup->get_scene_root().get_parent()));
+
+        const LMatrix4 &light_mat = transform->get_mat();
+        LVector3 dir = lens->get_view_vector() * light_mat;
+        t.set_row(3, dir);
+        return &t;
+      }
 
 
     } else if (attrib == IN_spotCutoff) {
     } else if (attrib == IN_spotCutoff) {
       if (np.node()->is_of_type(Spotlight::get_class_type())) {
       if (np.node()->is_of_type(Spotlight::get_class_type())) {

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

@@ -1476,6 +1476,10 @@ GROUPING ENTRIES
     its geometry is used to define the extent of the collision
     its geometry is used to define the extent of the collision
     surface (unless the "descend" flag is given; see below).
     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:
     Valid types so far are:
 
 
     Plane
     Plane
@@ -1538,11 +1542,12 @@ GROUPING ENTRIES
 
 
     descend
     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
       define a new collision object of the given type.  The event
       name, if any, will also be inherited from the top node and
       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
     keep
  
  

+ 6 - 0
panda/src/downloader/httpDate.cxx

@@ -223,8 +223,14 @@ HTTPDate(const string &format) {
   if (_time != (time_t)-1) {
   if (_time != (time_t)-1) {
     // Unfortunately, mktime() assumes local time; convert this back
     // Unfortunately, mktime() assumes local time; convert this back
     // to GMT.
     // to GMT.
+#ifdef IS_FREEBSD
+    time_t now = time(NULL);
+    struct tm *tp = localtime(&now);
+    _time -= tp->tm_gmtoff;
+#else /* IS_FREEBSD */
     extern long int timezone;
     extern long int timezone;
     _time -= timezone;
     _time -= timezone;
+#endif /* IS_FREEBSD */
   }
   }
 #endif  // __GNUC__
 #endif  // __GNUC__
 }
 }

+ 23 - 22
panda/src/egg2pg/eggLoader.cxx

@@ -1865,6 +1865,10 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
     node = new CollisionNode(egg_group->get_name());
     node = new CollisionNode(egg_group->get_name());
 
 
     make_collision_solids(egg_group, egg_group, (CollisionNode *)node.p());
     make_collision_solids(egg_group, egg_group, (CollisionNode *)node.p());
+
+    // Transform all of the collision solids into local space.
+    node->xform(LCAST(PN_stdfloat, egg_group->get_vertex_to_node()));
+
     if ((egg_group->get_collide_flags() & EggGroup::CF_keep) != 0) {
     if ((egg_group->get_collide_flags() & EggGroup::CF_keep) != 0) {
       // If we also specified to keep the geometry, continue the
       // If we also specified to keep the geometry, continue the
       // traversal.  In this case, we create a new PandaNode to be the
       // traversal.  In this case, we create a new PandaNode to be the
@@ -1925,8 +1929,11 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
     pnode->set_pos(center);
     pnode->set_pos(center);
     pnode->set_color(color);
     pnode->set_color(color);
     pnode->set_radius(radius);
     pnode->set_radius(radius);
+
+    pnode->xform(LCAST(PN_stdfloat, egg_group->get_vertex_to_node()));
+
     node = pnode;
     node = pnode;
-    
+
   } else if (egg_group->get_switch_flag()) {
   } else if (egg_group->get_switch_flag()) {
     if (egg_group->get_switch_fps() != 0.0) {
     if (egg_group->get_switch_fps() != 0.0) {
       // Create a sequence node.
       // Create a sequence node.
@@ -2790,7 +2797,6 @@ find_first_polygon(EggGroup *egg_group) {
 bool EggLoader::
 bool EggLoader::
 make_sphere(EggGroup *egg_group, EggGroup::CollideFlags flags, 
 make_sphere(EggGroup *egg_group, EggGroup::CollideFlags flags, 
             LPoint3 &center, PN_stdfloat &radius, LColor &color) {
             LPoint3 &center, PN_stdfloat &radius, LColor &color) {
-  bool success=false;
   EggGroup *geom_group = find_collision_geometry(egg_group, flags);
   EggGroup *geom_group = find_collision_geometry(egg_group, flags);
   if (geom_group != (EggGroup *)NULL) {
   if (geom_group != (EggGroup *)NULL) {
     // Collect all of the vertices.
     // Collect all of the vertices.
@@ -2822,15 +2828,12 @@ make_sphere(EggGroup *egg_group, EggGroup::CollideFlags flags,
       d_center /= (double)num_vertices;
       d_center /= (double)num_vertices;
       //egg2pg_cat.debug() << "make_sphere d_center: " << d_center << "\n";
       //egg2pg_cat.debug() << "make_sphere d_center: " << d_center << "\n";
 
 
-      LMatrix4d mat = egg_group->get_vertex_to_node();
-      d_center = d_center * mat;
-
       // And the furthest vertex determines the radius.
       // And the furthest vertex determines the radius.
       double radius2 = 0.0;
       double radius2 = 0.0;
       for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
       for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
         EggVertex *vtx = (*vi);
         EggVertex *vtx = (*vi);
         LPoint3d p3 = vtx->get_pos3();
         LPoint3d p3 = vtx->get_pos3();
-                LVector3d v = p3 * mat - d_center;
+                LVector3d v = p3 - d_center;
         radius2 = max(radius2, v.length_squared());
         radius2 = max(radius2, v.length_squared());
       }
       }
 
 
@@ -2841,10 +2844,10 @@ make_sphere(EggGroup *egg_group, EggGroup::CollideFlags flags,
       vi = vertices.begin();
       vi = vertices.begin();
       EggVertex *clr_vtx = (*vi);
       EggVertex *clr_vtx = (*vi);
       color = clr_vtx->get_color();
       color = clr_vtx->get_color();
-      success = true;
+      return true;
     }
     }
   }
   }
-  return success;
+  return false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2900,9 +2903,8 @@ make_box(EggGroup *egg_group, EggGroup::CollideFlags flags,
                  max(max_pd[2], pos[2]));
                  max(max_pd[2], pos[2]));
     }
     }
 
 
-    LMatrix4d mat = egg_group->get_vertex_to_node();
-    min_p = LCAST(PN_stdfloat, min_pd * mat);
-    max_p = LCAST(PN_stdfloat, max_pd * mat);
+    min_p = LCAST(PN_stdfloat, min_pd);
+    max_p = LCAST(PN_stdfloat, max_pd);
     return (min_pd != max_pd);
     return (min_pd != max_pd);
   }
   }
   return false;
   return false;
@@ -2968,6 +2970,10 @@ make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
         make_collision_solids(start_group, DCAST(EggGroup, *ci), cnode);
         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";
   }
   }
 }
 }
 
 
@@ -3170,7 +3176,6 @@ make_collision_tube(EggGroup *egg_group, CollisionNode *cnode,
     // also determine the centroid).  We compute this in node space.
     // also determine the centroid).  We compute this in node space.
     size_t num_vertices = vertices.size();
     size_t num_vertices = vertices.size();
     if (num_vertices != 0) {
     if (num_vertices != 0) {
-      LMatrix4d mat = egg_group->get_vertex_to_node();
       pvector<LPoint3d> vpos;
       pvector<LPoint3d> vpos;
       vpos.reserve(num_vertices);
       vpos.reserve(num_vertices);
       
       
@@ -3178,7 +3183,7 @@ make_collision_tube(EggGroup *egg_group, CollisionNode *cnode,
       pset<EggVertex *>::const_iterator vi;
       pset<EggVertex *>::const_iterator vi;
       for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
       for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
         EggVertex *vtx = (*vi);
         EggVertex *vtx = (*vi);
-        LPoint3d pos = vtx->get_pos3() * mat;
+        const LPoint3d &pos = vtx->get_pos3();
         vpos.push_back(pos);
         vpos.push_back(pos);
         center += pos;
         center += pos;
       }
       }
@@ -3420,20 +3425,18 @@ create_collision_plane(EggPolygon *egg_poly, EggGroup *parent_group) {
       << "\n";
       << "\n";
   }
   }
 
 
-  LMatrix4d mat = egg_poly->get_vertex_to_node();
-
   pvector<LVertex> vertices;
   pvector<LVertex> vertices;
   if (!egg_poly->empty()) {
   if (!egg_poly->empty()) {
     EggPolygon::const_iterator vi;
     EggPolygon::const_iterator vi;
     vi = egg_poly->begin();
     vi = egg_poly->begin();
 
 
-    LVertexd vert = (*vi)->get_pos3() * mat;
+    LVertexd vert = (*vi)->get_pos3();
     vertices.push_back(LCAST(PN_stdfloat, vert));
     vertices.push_back(LCAST(PN_stdfloat, vert));
 
 
     LVertexd last_vert = vert;
     LVertexd last_vert = vert;
     ++vi;
     ++vi;
     while (vi != egg_poly->end()) {
     while (vi != egg_poly->end()) {
-      vert = (*vi)->get_pos3() * mat;
+      vert = (*vi)->get_pos3();
       if (!vert.almost_equal(last_vert)) {
       if (!vert.almost_equal(last_vert)) {
         vertices.push_back(LCAST(PN_stdfloat, vert));
         vertices.push_back(LCAST(PN_stdfloat, vert));
       }
       }
@@ -3461,7 +3464,6 @@ void EggLoader::
 create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly,
 create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly,
                           EggGroup *parent_group,
                           EggGroup *parent_group,
                           EggGroup::CollideFlags flags) {
                           EggGroup::CollideFlags flags) {
-  LMatrix4d mat = egg_poly->get_vertex_to_node();
 
 
   PT(EggGroup) group = new EggGroup;
   PT(EggGroup) group = new EggGroup;
 
 
@@ -3489,13 +3491,13 @@ create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly,
       EggPolygon::const_iterator vi;
       EggPolygon::const_iterator vi;
       vi = poly->begin();
       vi = poly->begin();
 
 
-      LVertexd vert = (*vi)->get_pos3() * mat;
+      LVertexd vert = (*vi)->get_pos3();
       vertices.push_back(LCAST(PN_stdfloat, vert));
       vertices.push_back(LCAST(PN_stdfloat, vert));
 
 
       LVertexd last_vert = vert;
       LVertexd last_vert = vert;
       ++vi;
       ++vi;
       while (vi != poly->end()) {
       while (vi != poly->end()) {
-        vert = (*vi)->get_pos3() * mat;
+        vert = (*vi)->get_pos3();
         if (!vert.almost_equal(last_vert)) {
         if (!vert.almost_equal(last_vert)) {
           vertices.push_back(LCAST(PN_stdfloat, vert));
           vertices.push_back(LCAST(PN_stdfloat, vert));
         }
         }
@@ -3513,12 +3515,11 @@ create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly,
       if (cspoly->is_valid()) {
       if (cspoly->is_valid()) {
         apply_collision_flags(cspoly, flags);
         apply_collision_flags(cspoly, flags);
         cnode->add_solid(cspoly);
         cnode->add_solid(cspoly);
-      }        
+      }
     }
     }
   }
   }
 }
 }
 
 
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggLoader::create_collision_floor_mesh
 //     Function: EggLoader::create_collision_floor_mesh
 //       Access: Private
 //       Access: Private

+ 49 - 2
panda/src/egg2pg/eggSaver.cxx

@@ -22,6 +22,7 @@
 #include "transformState.h"
 #include "transformState.h"
 #include "colorScaleAttrib.h"
 #include "colorScaleAttrib.h"
 #include "colorAttrib.h"
 #include "colorAttrib.h"
+#include "materialAttrib.h"
 #include "textureAttrib.h"
 #include "textureAttrib.h"
 #include "cullFaceAttrib.h"
 #include "cullFaceAttrib.h"
 #include "transparencyAttrib.h"
 #include "transparencyAttrib.h"
@@ -347,7 +348,7 @@ convert_character_bundle(PartGroup *bundleNode, EggGroupNode *egg_parent, Charac
     CharacterJoint *character_joint = DCAST(CharacterJoint, bundleNode);
     CharacterJoint *character_joint = DCAST(CharacterJoint, bundleNode);
 
 
     LMatrix4 transformf;
     LMatrix4 transformf;
-    character_joint->get_net_transform(transformf);
+    character_joint->get_transform(transformf);
     LMatrix4d transformd(LCAST(double, transformf));
     LMatrix4d transformd(LCAST(double, transformf));
     EggGroup *joint = new EggGroup(bundleNode->get_name());
     EggGroup *joint = new EggGroup(bundleNode->get_name());
     joint->add_matrix4(transformd);
     joint->add_matrix4(transformd);
@@ -577,6 +578,13 @@ convert_primitive(const GeomVertexData *vertex_data,
     }
     }
   }
   }
 
 
+  // Check for a material.
+  EggMaterial *egg_mat = (EggMaterial *)NULL;
+  const MaterialAttrib *ma = DCAST(MaterialAttrib, net_state->get_attrib(MaterialAttrib::get_class_type()));
+  if (ma != (const MaterialAttrib *)NULL) {
+    egg_mat = get_egg_material(ma->get_material());
+  }
+
   // Check for a texture.
   // Check for a texture.
   EggTexture *egg_tex = (EggTexture *)NULL;
   EggTexture *egg_tex = (EggTexture *)NULL;
   const TextureAttrib *ta = DCAST(TextureAttrib, net_state->get_attrib(TextureAttrib::get_class_type()));
   const TextureAttrib *ta = DCAST(TextureAttrib, net_state->get_attrib(TextureAttrib::get_class_type()));
@@ -703,11 +711,15 @@ convert_primitive(const GeomVertexData *vertex_data,
     // Huh, an unknown geometry type.
     // Huh, an unknown geometry type.
     return;
     return;
   }
   }
-  
+
   for (int i = 0; i < num_primitives; ++i) {
   for (int i = 0; i < num_primitives; ++i) {
     PT(EggPrimitive) egg_prim = (*make_func)();
     PT(EggPrimitive) egg_prim = (*make_func)();
 
 
     egg_parent->add_child(egg_prim);
     egg_parent->add_child(egg_prim);
+
+    if (egg_mat != (EggMaterial *)NULL) {
+      egg_prim->set_material(egg_mat);
+    }
     if (egg_tex != (EggTexture *)NULL) {
     if (egg_tex != (EggTexture *)NULL) {
       egg_prim->set_texture(egg_tex);
       egg_prim->set_texture(egg_tex);
     }
     }
@@ -947,6 +959,41 @@ apply_tag(EggGroup *egg_group, PandaNode *node, const string &tag) {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggSaver::get_egg_material
+//       Access: Private
+//  Description: Returns an EggMaterial pointer that corresponds to
+//               the indicated Material.
+////////////////////////////////////////////////////////////////////
+EggMaterial *EggSaver::
+get_egg_material(Material *mat) {
+  if (mat != (Material *)NULL) {
+    EggMaterial temp(mat->get_name());
+    if (mat->has_ambient()) {
+      temp.set_amb(mat->get_ambient());
+    }
+
+    if (mat->has_diffuse()) {
+      temp.set_diff(mat->get_diffuse());
+    }
+
+    if (mat->has_specular()) {
+      temp.set_spec(mat->get_specular());
+    }
+
+    if (mat->has_emission()) {
+      temp.set_emit(mat->get_emission());
+    }
+
+    temp.set_shininess(mat->get_shininess());
+    temp.set_local(mat->get_local());
+
+    return _materials.create_unique_material(temp, ~EggMaterial::E_mref_name);
+  }
+
+  return NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggSaver::get_egg_texture
 //     Function: EggSaver::get_egg_texture
 //       Access: Private
 //       Access: Private

+ 2 - 1
panda/src/egg2pg/eggSaver.h

@@ -92,6 +92,7 @@ private:
   bool apply_tags(EggGroup *egg_group, PandaNode *node);
   bool apply_tags(EggGroup *egg_group, PandaNode *node);
   bool apply_tag(EggGroup *egg_group, PandaNode *node, const string &tag);
   bool apply_tag(EggGroup *egg_group, PandaNode *node, const string &tag);
 
 
+  EggMaterial *get_egg_material(Material *tex);
   EggTexture *get_egg_texture(Texture *tex);
   EggTexture *get_egg_texture(Texture *tex);
 
 
   static EggPrimitive *make_egg_polygon();
   static EggPrimitive *make_egg_polygon();
@@ -102,8 +103,8 @@ private:
   PT(EggData) _data;
   PT(EggData) _data;
 
 
   PT(EggVertexPool) _vpool;
   PT(EggVertexPool) _vpool;
-  EggTextureCollection _textures;
   EggMaterialCollection _materials;
   EggMaterialCollection _materials;
+  EggTextureCollection _textures;
 };
 };
 
 
 #include "eggSaver.I"
 #include "eggSaver.I"

+ 7 - 7
panda/src/egldisplay/eglGraphicsBuffer.cxx

@@ -1,5 +1,5 @@
 // Filename: eglGraphicsBuffer.cxx
 // Filename: eglGraphicsBuffer.cxx
-// Created by:  pro-rsoft (13Jun09)
+// Created by:  rdb (13Jun09)
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //
 //
@@ -28,7 +28,7 @@ TypeHandle eglGraphicsBuffer::_type_handle;
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 eglGraphicsBuffer::
 eglGraphicsBuffer::
-eglGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe, 
+eglGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe,
                   const string &name,
                   const string &name,
                   const FrameBufferProperties &fb_prop,
                   const FrameBufferProperties &fb_prop,
                   const WindowProperties &win_prop,
                   const WindowProperties &win_prop,
@@ -187,7 +187,7 @@ open_buffer() {
       _gsg = eglgsg;
       _gsg = eglgsg;
     }
     }
   }
   }
-  
+
   if (eglgsg->_fbconfig == None) {
   if (eglgsg->_fbconfig == None) {
     // If we didn't use an fbconfig to create the GSG, we can't create
     // If we didn't use an fbconfig to create the GSG, we can't create
     // a PBuffer.
     // a PBuffer.
@@ -195,11 +195,11 @@ open_buffer() {
   }
   }
 
 
   int attrib_list[] = {
   int attrib_list[] = {
-    EGL_WIDTH, _x_size,
-    EGL_HEIGHT, _y_size,
+    EGL_WIDTH, _size.get_x(),
+    EGL_HEIGHT, _size.get_y(),
     EGL_NONE
     EGL_NONE
   };
   };
-  
+
   _pbuffer = eglCreatePbufferSurface(eglgsg->_egl_display, eglgsg->_fbconfig, attrib_list);
   _pbuffer = eglCreatePbufferSurface(eglgsg->_egl_display, eglgsg->_fbconfig, attrib_list);
 
 
   if (_pbuffer == EGL_NO_SURFACE) {
   if (_pbuffer == EGL_NO_SURFACE) {
@@ -224,7 +224,7 @@ open_buffer() {
     return false;
     return false;
   }
   }
   _fb_properties = eglgsg->get_fb_properties();
   _fb_properties = eglgsg->get_fb_properties();
-  
+
   _is_valid = true;
   _is_valid = true;
   return true;
   return true;
 }
 }

+ 1 - 1
panda/src/egldisplay/eglGraphicsBuffer.h

@@ -1,5 +1,5 @@
 // Filename: eglGraphicsBuffer.h
 // Filename: eglGraphicsBuffer.h
-// Created by:  pro-rsoft (13Jun09)
+// Created by:  rdb (13Jun09)
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //
 //

+ 7 - 7
panda/src/egldisplay/eglGraphicsPixmap.cxx

@@ -1,5 +1,5 @@
 // Filename: eglGraphicsPixmap.cxx
 // Filename: eglGraphicsPixmap.cxx
-// Created by:  pro-rsoft (13Jun09)
+// Created by:  rdb (13Jun09)
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //
 //
@@ -29,7 +29,7 @@ TypeHandle eglGraphicsPixmap::_type_handle;
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 eglGraphicsPixmap::
 eglGraphicsPixmap::
-eglGraphicsPixmap(GraphicsEngine *engine, GraphicsPipe *pipe, 
+eglGraphicsPixmap(GraphicsEngine *engine, GraphicsPipe *pipe,
                   const string &name,
                   const string &name,
                   const FrameBufferProperties &fb_prop,
                   const FrameBufferProperties &fb_prop,
                   const WindowProperties &win_prop,
                   const WindowProperties &win_prop,
@@ -105,7 +105,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
     }
     }
     clear_cube_map_selection();
     clear_cube_map_selection();
   }
   }
-  
+
   _gsg->set_current_properties(&get_fb_properties());
   _gsg->set_current_properties(&get_fb_properties());
   return _gsg->begin_frame(current_thread);
   return _gsg->begin_frame(current_thread);
 }
 }
@@ -195,7 +195,7 @@ open_buffer() {
       _gsg = eglgsg;
       _gsg = eglgsg;
     }
     }
   }
   }
-  
+
   if (eglgsg->_fbconfig == None) {
   if (eglgsg->_fbconfig == None) {
     // If we didn't use an fbconfig to create the GSG, we can't create
     // If we didn't use an fbconfig to create the GSG, we can't create
     // a PBuffer.
     // a PBuffer.
@@ -221,8 +221,8 @@ open_buffer() {
     }
     }
   }
   }
 
 
-  _x_pixmap = XCreatePixmap(_display, _drawable, 
-                            _x_size, _y_size, visual_info->depth);
+  _x_pixmap = XCreatePixmap(_display, _drawable,
+                            _size.get_x(), _size.get_y(), visual_info->depth);
   if (_x_pixmap == None) {
   if (_x_pixmap == None) {
     egldisplay_cat.error()
     egldisplay_cat.error()
       << "Failed to create X pixmap.\n";
       << "Failed to create X pixmap.\n";
@@ -253,7 +253,7 @@ open_buffer() {
     return false;
     return false;
   }
   }
   _fb_properties = eglgsg->get_fb_properties();
   _fb_properties = eglgsg->get_fb_properties();
-  
+
   _is_valid = true;
   _is_valid = true;
   return true;
   return true;
 }
 }

+ 2 - 2
panda/src/egldisplay/eglGraphicsPixmap.h

@@ -1,5 +1,5 @@
 // Filename: eglGraphicsPixmap.h
 // Filename: eglGraphicsPixmap.h
-// Created by:  pro-rsoft (13Jun09)
+// Created by:  rdb (13Jun09)
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //
 //
@@ -29,7 +29,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class eglGraphicsPixmap : public GraphicsBuffer {
 class eglGraphicsPixmap : public GraphicsBuffer {
 public:
 public:
-  eglGraphicsPixmap(GraphicsEngine *engine, GraphicsPipe *pipe, 
+  eglGraphicsPixmap(GraphicsEngine *engine, GraphicsPipe *pipe,
                     const string &name,
                     const string &name,
                     const FrameBufferProperties &fb_prop,
                     const FrameBufferProperties &fb_prop,
                     const WindowProperties &win_prop,
                     const WindowProperties &win_prop,

+ 27 - 0
panda/src/express/datagram.I

@@ -82,6 +82,33 @@ operator = (const Datagram &copy) {
   _stdfloat_double = copy._stdfloat_double;
   _stdfloat_double = copy._stdfloat_double;
 }
 }
 
 
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: Datagram::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Datagram::
+Datagram(Datagram &&from) NOEXCEPT :
+  _data(move(from._data)),
+  _stdfloat_double(from._stdfloat_double)
+{
+}
+#endif  // USE_MOVE_SEMANTICS
+
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: Datagram::Move Assignment Operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void Datagram::
+operator = (Datagram &&from) NOEXCEPT {
+  _data = move(from._data);
+  _stdfloat_double = from._stdfloat_double;
+}
+#endif  // USE_MOVE_SEMANTICS
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Datagram::add_bool
 //     Function: Datagram::add_bool
 //       Access: Public
 //       Access: Public

+ 5 - 0
panda/src/express/datagram.h

@@ -48,6 +48,11 @@ PUBLISHED:
   INLINE Datagram(const Datagram &copy);
   INLINE Datagram(const Datagram &copy);
   INLINE void operator = (const Datagram &copy);
   INLINE void operator = (const Datagram &copy);
 
 
+#ifdef USE_MOVE_SEMANTICS
+  INLINE Datagram(Datagram &&from) NOEXCEPT;
+  INLINE void operator = (Datagram &&from) NOEXCEPT;
+#endif
+
   virtual ~Datagram();
   virtual ~Datagram();
 
 
   virtual void clear();
   virtual void clear();

+ 90 - 0
panda/src/express/pointerToArray.I

@@ -77,6 +77,21 @@ PointerToArray(const PointerToArray<Element> &copy) :
 {
 {
 }
 }
 
 
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: PointerToArray::Move Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE PointerToArray<Element>::
+PointerToArray(PointerToArray<Element> &&from) NOEXCEPT :
+  PointerToArrayBase<Element>(move(from)),
+  _type_handle(from._type_handle)
+{
+}
+#endif  // USE_MOVE_SEMANTICS
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PointerToArray::begin
 //     Function: PointerToArray::begin
 //       Access: Public
 //       Access: Public
@@ -682,6 +697,21 @@ operator = (const PointerToArray<Element> &copy) {
   return *this;
   return *this;
 }
 }
 
 
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: PointerToArray::Assignment operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE PointerToArray<Element> &PointerToArray<Element>::
+operator = (PointerToArray<Element> &&from) NOEXCEPT {
+  _type_handle = from._type_handle;
+  ((PointerToArray<Element> *)this)->reassign(move(from));
+  return *this;
+}
+#endif  // USE_MOVE_SEMANTICS
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PointerToArray::clear
 //     Function: PointerToArray::clear
 //       Access: Public
 //       Access: Public
@@ -736,6 +766,36 @@ ConstPointerToArray(const ConstPointerToArray<Element> &copy) :
 {
 {
 }
 }
 
 
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: ConstPointerToArray::Move Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE ConstPointerToArray<Element>::
+ConstPointerToArray(PointerToArray<Element> &&from) NOEXCEPT :
+  PointerToArrayBase<Element>(move(from)),
+  _type_handle(from._type_handle)
+{
+}
+#endif  // USE_MOVE_SEMANTICS
+
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: ConstPointerToArray::Move Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE ConstPointerToArray<Element>::
+ConstPointerToArray(ConstPointerToArray<Element> &&from) NOEXCEPT :
+  PointerToArrayBase<Element>(move(from)),
+  _type_handle(from._type_handle)
+{
+}
+#endif  // USE_MOVE_SEMANTICS
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConstPointerToArray::begin
 //     Function: ConstPointerToArray::begin
 //       Access: Public
 //       Access: Public
@@ -1141,6 +1201,36 @@ operator = (const ConstPointerToArray<Element> &copy) {
   return *this;
   return *this;
 }
 }
 
 
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: ConstPointerToArray::Assignment operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE ConstPointerToArray<Element> &ConstPointerToArray<Element>::
+operator = (PointerToArray<Element> &&from) NOEXCEPT {
+  _type_handle = from._type_handle;
+  ((ConstPointerToArray<Element> *)this)->reassign(move(from));
+  return *this;
+}
+#endif  // USE_MOVE_SEMANTICS
+
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: ConstPointerToArray::Assignment operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE ConstPointerToArray<Element> &ConstPointerToArray<Element>::
+operator = (ConstPointerToArray<Element> &&from) NOEXCEPT {
+  _type_handle = from._type_handle;
+  ((ConstPointerToArray<Element> *)this)->reassign(move(from));
+  return *this;
+}
+#endif  // USE_MOVE_SEMANTICS
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConstPointerToArray::clear
 //     Function: ConstPointerToArray::clear
 //       Access: Public
 //       Access: Public

+ 23 - 0
panda/src/express/pointerToArray.h

@@ -151,6 +151,10 @@ public:
   INLINE PointerToArray(size_type n, const Element &value, TypeHandle type_handle = get_type_handle(Element));
   INLINE PointerToArray(size_type n, const Element &value, TypeHandle type_handle = get_type_handle(Element));
   INLINE PointerToArray(const PointerToArray<Element> &copy);
   INLINE PointerToArray(const PointerToArray<Element> &copy);
 
 
+#ifdef USE_MOVE_SEMANTICS
+  INLINE PointerToArray(PointerToArray<Element> &&from) NOEXCEPT;
+#endif
+
 public:
 public:
   // Duplicating the interface of vector.  The following member
   // Duplicating the interface of vector.  The following member
   // functions are all const, because they do not reassign the
   // functions are all const, because they do not reassign the
@@ -231,6 +235,12 @@ public:
   operator = (ReferenceCountedVector<Element> *ptr);
   operator = (ReferenceCountedVector<Element> *ptr);
   INLINE PointerToArray<Element> &
   INLINE PointerToArray<Element> &
   operator = (const PointerToArray<Element> &copy);
   operator = (const PointerToArray<Element> &copy);
+
+#ifdef USE_MOVE_SEMANTICS
+  INLINE PointerToArray<Element> &
+  operator = (PointerToArray<Element> &&from) NOEXCEPT;
+#endif
+
   INLINE void clear();
   INLINE void clear();
 
 
 private:
 private:
@@ -305,6 +315,11 @@ PUBLISHED:
   INLINE ConstPointerToArray(const PointerToArray<Element> &copy);
   INLINE ConstPointerToArray(const PointerToArray<Element> &copy);
   INLINE ConstPointerToArray(const ConstPointerToArray<Element> &copy);
   INLINE ConstPointerToArray(const ConstPointerToArray<Element> &copy);
 
 
+#ifdef USE_MOVE_SEMANTICS
+  INLINE ConstPointerToArray(PointerToArray<Element> &&from) NOEXCEPT;
+  INLINE ConstPointerToArray(ConstPointerToArray<Element> &&from) NOEXCEPT;
+#endif
+
   // Duplicating the interface of vector.
   // Duplicating the interface of vector.
 
 
   INLINE iterator begin() const;
   INLINE iterator begin() const;
@@ -355,6 +370,14 @@ PUBLISHED:
   operator = (const PointerToArray<Element> &copy);
   operator = (const PointerToArray<Element> &copy);
   INLINE ConstPointerToArray<Element> &
   INLINE ConstPointerToArray<Element> &
   operator = (const ConstPointerToArray<Element> &copy);
   operator = (const ConstPointerToArray<Element> &copy);
+
+#ifdef USE_MOVE_SEMANTICS
+  INLINE ConstPointerToArray<Element> &
+  operator = (PointerToArray<Element> &&from) NOEXCEPT;
+  INLINE ConstPointerToArray<Element> &
+  operator = (ConstPointerToArray<Element> &&from) NOEXCEPT;
+#endif
+
   INLINE void clear();
   INLINE void clear();
 
 
 private:
 private:

+ 25 - 11
panda/src/express/pointerToArrayBase.I

@@ -57,11 +57,11 @@ template<class Element>
 INLINE ReferenceCountedVector<Element>::
 INLINE ReferenceCountedVector<Element>::
 ~ReferenceCountedVector() {
 ~ReferenceCountedVector() {
 }
 }
- 
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCountedVector::size
 //     Function: ReferenceCountedVector::size
 //       Access: Public
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 template<class Element>
 INLINE TYPENAME ReferenceCountedVector<Element>::size_type ReferenceCountedVector<Element>::
 INLINE TYPENAME ReferenceCountedVector<Element>::size_type ReferenceCountedVector<Element>::
@@ -72,7 +72,7 @@ size() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCountedVector::insert
 //     Function: ReferenceCountedVector::insert
 //       Access: Public
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 template<class Element>
 INLINE TYPENAME ReferenceCountedVector<Element>::iterator ReferenceCountedVector<Element>::
 INLINE TYPENAME ReferenceCountedVector<Element>::iterator ReferenceCountedVector<Element>::
@@ -83,7 +83,7 @@ insert(iterator position, const Element &x) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCountedVector::insert
 //     Function: ReferenceCountedVector::insert
 //       Access: Public
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 template<class Element>
 INLINE void ReferenceCountedVector<Element>::
 INLINE void ReferenceCountedVector<Element>::
@@ -94,7 +94,7 @@ insert(iterator position, size_type n, const Element &x) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCountedVector::erase
 //     Function: ReferenceCountedVector::erase
 //       Access: Public
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 template<class Element>
 INLINE void ReferenceCountedVector<Element>::
 INLINE void ReferenceCountedVector<Element>::
@@ -105,7 +105,7 @@ erase(iterator position) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCountedVector::erase
 //     Function: ReferenceCountedVector::erase
 //       Access: Public
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 template<class Element>
 INLINE void ReferenceCountedVector<Element>::
 INLINE void ReferenceCountedVector<Element>::
@@ -116,7 +116,7 @@ erase(iterator first, iterator last) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCountedVector::pop_back
 //     Function: ReferenceCountedVector::pop_back
 //       Access: Public
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 template<class Element>
 INLINE void ReferenceCountedVector<Element>::
 INLINE void ReferenceCountedVector<Element>::
@@ -127,7 +127,7 @@ pop_back() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCountedVector::clear
 //     Function: ReferenceCountedVector::clear
 //       Access: Public
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 template<class Element>
 INLINE void ReferenceCountedVector<Element>::
 INLINE void ReferenceCountedVector<Element>::
@@ -138,7 +138,7 @@ clear() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PointerToArrayBase::Constructor
 //     Function: PointerToArrayBase::Constructor
 //       Access: Protected
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 template<class Element>
 INLINE PointerToArrayBase<Element>::
 INLINE PointerToArrayBase<Element>::
@@ -150,7 +150,7 @@ PointerToArrayBase(ReferenceCountedVector<Element> *ptr) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PointerToArrayBase::Copy Constructor
 //     Function: PointerToArrayBase::Copy Constructor
 //       Access: Protected
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 template<class Element>
 INLINE PointerToArrayBase<Element>::
 INLINE PointerToArrayBase<Element>::
@@ -159,10 +159,24 @@ PointerToArrayBase(const PointerToArrayBase<Element> &copy) :
 {
 {
 }
 }
 
 
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: PointerToArrayBase::Move Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE PointerToArrayBase<Element>::
+PointerToArrayBase(PointerToArrayBase<Element> &&from) NOEXCEPT :
+  PointerToBase<ReferenceCountedVector<Element> >(move(from))
+{
+}
+#endif  // USE_MOVE_SEMANTICS
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PointerToArrayBase::Destructor
 //     Function: PointerToArrayBase::Destructor
 //       Access: Published
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 template<class Element>
 INLINE PointerToArrayBase<Element>::
 INLINE PointerToArrayBase<Element>::

+ 4 - 0
panda/src/express/pointerToArrayBase.h

@@ -82,6 +82,10 @@ protected:
   INLINE PointerToArrayBase(ReferenceCountedVector<Element> *ptr);
   INLINE PointerToArrayBase(ReferenceCountedVector<Element> *ptr);
   INLINE PointerToArrayBase(const PointerToArrayBase<Element> &copy);
   INLINE PointerToArrayBase(const PointerToArrayBase<Element> &copy);
 
 
+#ifdef USE_MOVE_SEMANTICS
+  INLINE PointerToArrayBase(PointerToArrayBase<Element> &&from) NOEXCEPT;
+#endif
+
 PUBLISHED:
 PUBLISHED:
   INLINE ~PointerToArrayBase();
   INLINE ~PointerToArrayBase();
 };
 };

+ 41 - 23
panda/src/express/pointerToArray_ext.I

@@ -22,26 +22,33 @@
 //               Python buffer protocol.
 //               Python buffer protocol.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 template<class Element>
-void Extension<PointerToArray<Element> >::
+INLINE void Extension<PointerToArray<Element> >::
 __init__(PyObject *self, PyObject *source) {
 __init__(PyObject *self, PyObject *source) {
 #if PY_VERSION_HEX >= 0x02060000
 #if PY_VERSION_HEX >= 0x02060000
   if (PyObject_CheckBuffer(source)) {
   if (PyObject_CheckBuffer(source)) {
     // User passed a buffer object.
     // User passed a buffer object.
     Py_buffer view;
     Py_buffer view;
     if (PyObject_GetBuffer(source, &view, PyBUF_CONTIG_RO) == -1) {
     if (PyObject_GetBuffer(source, &view, PyBUF_CONTIG_RO) == -1) {
-      PyErr_SetString(PyExc_TypeError, "PointerToArray constructor requires a contiguous buffer");
+      PyErr_SetString(PyExc_TypeError,
+                      "PointerToArray constructor requires a contiguous buffer");
       return;
       return;
     }
     }
 
 
     if (view.itemsize != 1 && view.itemsize != sizeof(Element)) {
     if (view.itemsize != 1 && view.itemsize != sizeof(Element)) {
-      PyErr_SetString(PyExc_TypeError, "buffer.itemsize does not match PointerToArray element size");
+      PyErr_SetString(PyExc_TypeError,
+                      "buffer.itemsize does not match PointerToArray element size");
       return;
       return;
     }
     }
 
 
-    int num_elements = view.len / sizeof(Element);
-    this->_this->insert(this->_this->begin(), num_elements, Element());
+    if (view.len % sizeof(Element) != 0) {
+      PyErr_Format(PyExc_ValueError,
+                   "byte buffer is not a multiple of %zu bytes",
+                   sizeof(Element));
+      return;
+    }
 
 
     if (view.len > 0) {
     if (view.len > 0) {
+      this->_this->resize(view.len / sizeof(Element));
       memcpy(this->_this->p(), view.buf, view.len);
       memcpy(this->_this->p(), view.buf, view.len);
     }
     }
 
 
@@ -52,21 +59,22 @@ __init__(PyObject *self, PyObject *source) {
 
 
   if (!PySequence_Check(source)) {
   if (!PySequence_Check(source)) {
     // If passed with a non-sequence, this isn't the right constructor.
     // If passed with a non-sequence, this isn't the right constructor.
-    PyErr_SetString(PyExc_TypeError, "PointerToArray constructor requires a sequence or buffer object");
+    PyErr_SetString(PyExc_TypeError,
+                    "PointerToArray constructor requires a sequence or buffer object");
     return;
     return;
   }
   }
 
 
   // If we were passed a Python string, then instead of storing it
   // If we were passed a Python string, then instead of storing it
   // character-at-a-time, just load the whole string as a data
   // character-at-a-time, just load the whole string as a data
-  // buffer.
+  // buffer.  Not sure if this case is still necessary - don't Python
+  // str/bytes objects export the buffer protocol, as above?
 #if PY_MAJOR_VERSION >= 3
 #if PY_MAJOR_VERSION >= 3
   if (PyBytes_Check(source)) {
   if (PyBytes_Check(source)) {
     int size = PyBytes_Size(source);
     int size = PyBytes_Size(source);
     if (size % sizeof(Element) != 0) {
     if (size % sizeof(Element) != 0) {
-      ostringstream stream;
-      stream << "Buffer not a multiple of " << sizeof(Element) << " bytes";
-      string str = stream.str();
-      PyErr_SetString(PyExc_ValueError, str.c_str());
+      PyErr_Format(PyExc_ValueError,
+                   "bytes object is not a multiple of %zu bytes",
+                   sizeof(Element));
       return;
       return;
     }
     }
 
 
@@ -85,10 +93,9 @@ __init__(PyObject *self, PyObject *source) {
   if (PyString_CheckExact(source)) {
   if (PyString_CheckExact(source)) {
     int size = PyString_Size(source);
     int size = PyString_Size(source);
     if (size % sizeof(Element) != 0) {
     if (size % sizeof(Element) != 0) {
-      ostringstream stream;
-      stream << "Buffer not a multiple of " << sizeof(Element) << " bytes";
-      string str = stream.str();
-      PyErr_SetString(PyExc_ValueError, str.c_str());
+      PyErr_Format(PyExc_ValueError,
+                   "str object is not a multiple of %zu bytes",
+                   sizeof(Element));
       return;
       return;
     }
     }
 
 
@@ -107,20 +114,29 @@ __init__(PyObject *self, PyObject *source) {
 
 
   // Now construct the internal list by copying the elements
   // Now construct the internal list by copying the elements
   // one-at-a-time from Python.
   // one-at-a-time from Python.
+  PyObject *push_back = PyObject_GetAttrString(self, "push_back");
+  if (push_back == NULL) {
+    PyErr_BadArgument();
+    return;
+  }
+
+  // We need to initialize the this pointer before we can call push_back.
+  ((Dtool_PyInstDef *)self)->_ptr_to_object = (void *)this->_this;
+
   int size = PySequence_Size(source);
   int size = PySequence_Size(source);
   for (int i = 0; i < size; ++i) {
   for (int i = 0; i < size; ++i) {
     PyObject *item = PySequence_GetItem(source, i);
     PyObject *item = PySequence_GetItem(source, i);
     if (item == NULL) {
     if (item == NULL) {
       return;
       return;
     }
     }
-    PyObject *result = PyObject_CallMethod(self, (char *)"push_back", (char *)"O", item);
+    PyObject *result = PyObject_CallFunctionObjArgs(push_back, item, NULL);
     Py_DECREF(item);
     Py_DECREF(item);
     if (result == NULL) {
     if (result == NULL) {
       // Unable to add item--probably it wasn't of the appropriate type.
       // Unable to add item--probably it wasn't of the appropriate type.
-      ostringstream stream;
-      stream << "Element " << i << " in sequence passed to PointerToArray constructor could not be added";
-      string str = stream.str();
-      PyErr_SetString(PyExc_TypeError, str.c_str());
+      PyErr_Print();
+      PyErr_Format(PyExc_TypeError,
+                   "Element %d in sequence passed to PointerToArray "
+                   "constructor could not be added", i);
       return;
       return;
     }
     }
     Py_DECREF(result);
     Py_DECREF(result);
@@ -161,7 +177,9 @@ __setitem__(size_t n, const Element &value) {
 template<class Element>
 template<class Element>
 INLINE void Extension<ConstPointerToArray<Element> >::
 INLINE void Extension<ConstPointerToArray<Element> >::
 __init__(PyObject *self, PyObject *source) {
 __init__(PyObject *self, PyObject *source) {
-  new (this->_this) ConstPointerToArray<Element>(get_type_handle(Element));
+  PointerToArray<Element> array;
+  invoke_extension(&array).__init__(self, source);
+  *(this->_this) = MOVE(array);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -240,7 +258,7 @@ __releasebuffer__(PyObject *self, Py_buffer *view) const {
 
 
   if (view->internal != NULL) {
   if (view->internal != NULL) {
     // Oh, right, let's not forget to unref this.
     // Oh, right, let's not forget to unref this.
-    ((const PointerToArray<Element> *) view->internal)->unref();
+    unref_delete((const PointerToArray<Element> *)view->internal);
     view->internal = NULL;
     view->internal = NULL;
   }
   }
 }
 }
@@ -314,7 +332,7 @@ __releasebuffer__(PyObject *self, Py_buffer *view) const {
 
 
   if (view->internal != NULL) {
   if (view->internal != NULL) {
     // Oh, right, let's not forget to unref this.
     // Oh, right, let's not forget to unref this.
-    ((const PointerToArray<Element> *) view->internal)->unref();
+    unref_delete((const PointerToArray<Element> *)view->internal);
     view->internal = NULL;
     view->internal = NULL;
   }
   }
 }
 }

+ 20 - 0
panda/src/express/pointerToArray_ext.h

@@ -66,6 +66,26 @@ public:
 #endif
 #endif
 };
 };
 
 
+#ifdef _MSC_VER
+// Ugh... MSVC needs this because they still don't have a decent linker.
+#include "PTA_uchar.h"
+#include "PTA_ushort.h"
+#include "PTA_float.h"
+#include "PTA_double.h"
+#include "PTA_int.h"
+
+template class EXPORT_THIS Extension<PTA_uchar>;
+template class EXPORT_THIS Extension<PTA_ushort>;
+template class EXPORT_THIS Extension<PTA_float>;
+template class EXPORT_THIS Extension<PTA_double>;
+template class EXPORT_THIS Extension<PTA_int>;
+template class EXPORT_THIS Extension<CPTA_uchar>;
+template class EXPORT_THIS Extension<CPTA_ushort>;
+template class EXPORT_THIS Extension<CPTA_float>;
+template class EXPORT_THIS Extension<CPTA_double>;
+template class EXPORT_THIS Extension<CPTA_int>;
+#endif
+
 // This macro is used to map a data type to a format code
 // This macro is used to map a data type to a format code
 // as used in the Python 'struct' and 'array' modules.
 // as used in the Python 'struct' and 'array' modules.
 #define get_format_code(type) _get_format_code((const type *)0)
 #define get_format_code(type) _get_format_code((const type *)0)

+ 8 - 3
panda/src/ffmpeg/ffmpegAudioCursor.cxx

@@ -148,7 +148,11 @@ FfmpegAudioCursor(FfmpegAudio *src) :
   _can_seek = true;
   _can_seek = true;
   _can_seek_fast = true;
   _can_seek_fast = true;
 
 
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 59, 100)
+  _frame = av_frame_alloc();
+#else
   _frame = avcodec_alloc_frame();
   _frame = avcodec_alloc_frame();
+#endif
 
 
   _packet = new AVPacket;
   _packet = new AVPacket;
   _buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE / 2;
   _buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE / 2;
@@ -194,7 +198,9 @@ FfmpegAudioCursor::
 void FfmpegAudioCursor::
 void FfmpegAudioCursor::
 cleanup() {
 cleanup() {
   if (_frame) {
   if (_frame) {
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 59, 100)
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 45, 101)
+    av_frame_free(&_frame);
+#elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 59, 100)
     avcodec_free_frame(&_frame);
     avcodec_free_frame(&_frame);
 #else
 #else
     av_free(&_frame);
     av_free(&_frame);
@@ -313,8 +319,7 @@ reload_buffer() {
 #ifdef HAVE_SWRESAMPLE
 #ifdef HAVE_SWRESAMPLE
         if (_resample_ctx) {
         if (_resample_ctx) {
           // Resample the data to signed 16-bit sample format.
           // Resample the data to signed 16-bit sample format.
-          uint8_t* out[SWR_CH_MAX] = {(uint8_t*) _buffer, NULL};
-          bufsize = swr_convert(_resample_ctx, out, _buffer_size / 2, (const uint8_t**)_frame->extended_data, _frame->nb_samples);
+          bufsize = swr_convert(_resample_ctx, (uint8_t **)&_buffer, _buffer_size / 2, (const uint8_t**)_frame->extended_data, _frame->nb_samples);
           bufsize *= _audio_channels * 2;
           bufsize *= _audio_channels * 2;
         } else
         } else
 #endif
 #endif

+ 5 - 0
panda/src/ffmpeg/ffmpegVideoCursor.cxx

@@ -94,8 +94,13 @@ init_from(FfmpegVideo *source) {
                                 PIX_FMT_BGR24, SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
                                 PIX_FMT_BGR24, SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
 #endif  // HAVE_SWSCALE
 #endif  // HAVE_SWSCALE
 
 
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 59, 100)
+  _frame = av_frame_alloc();
+  _frame_out = av_frame_alloc();
+#else
   _frame = avcodec_alloc_frame();
   _frame = avcodec_alloc_frame();
   _frame_out = avcodec_alloc_frame();
   _frame_out = avcodec_alloc_frame();
+#endif
 
 
   if ((_frame == 0)||(_frame_out == 0)) {
   if ((_frame == 0)||(_frame_out == 0)) {
     cleanup();
     cleanup();

+ 39 - 5
panda/src/framework/windowFramework.cxx

@@ -798,15 +798,28 @@ next_anim_control() {
   if (_anim_controls_enabled) {
   if (_anim_controls_enabled) {
     destroy_anim_controls();
     destroy_anim_controls();
 
 
+    if (_anim_controls.get_num_anims() == 0) {
+      set_anim_controls(false);
+      return;
+    }
+
+    // Stop the active animation.
+    pause_button();
     ++_anim_index;
     ++_anim_index;
+
     if (_anim_index >= _anim_controls.get_num_anims()) {
     if (_anim_index >= _anim_controls.get_num_anims()) {
       set_anim_controls(false);
       set_anim_controls(false);
+      _anim_controls.loop_all(true);
     } else {
     } else {
       create_anim_controls();
       create_anim_controls();
+      play_button();
     }
     }
   } else {
   } else {
     _anim_index = 0;
     _anim_index = 0;
     set_anim_controls(true);
     set_anim_controls(true);
+    if (_anim_controls.get_num_anims() > 0) {
+      play_button();
+    }
   }
   }
 }
 }
 
 
@@ -1451,6 +1464,23 @@ create_anim_controls() {
   AnimControl *control = _anim_controls.get_anim(_anim_index);
   AnimControl *control = _anim_controls.get_anim(_anim_index);
   nassertv(control != (AnimControl *)NULL);
   nassertv(control != (AnimControl *)NULL);
 
 
+  if (control->get_num_frames() <= 1) {
+    // Don't show the controls when the animation has only 0 or 1 frames.
+    ostringstream text;
+    text << _anim_controls.get_anim_name(_anim_index);
+    text << " (" << control->get_num_frames() << " frame"
+         << ((control->get_num_frames() == 1) ? "" : "s") << ")";
+
+    PT(TextNode) label = new TextNode("label");
+    label->set_align(TextNode::A_center);
+    label->set_text(text.str());
+    NodePath tnp = _anim_controls_group.attach_new_node(label);
+    tnp.set_pos(0.0f, 0.0f, 0.07f);
+    tnp.set_scale(0.1f);
+
+    return;
+  }
+
   PT(TextNode) label = new TextNode("anim_name");
   PT(TextNode) label = new TextNode("anim_name");
   label->set_align(TextNode::A_left);
   label->set_align(TextNode::A_left);
   label->set_text(_anim_controls.get_anim_name(_anim_index));
   label->set_text(_anim_controls.get_anim_name(_anim_index));
@@ -1525,13 +1555,17 @@ update_anim_controls() {
   AnimControl *control = _anim_controls.get_anim(_anim_index);
   AnimControl *control = _anim_controls.get_anim(_anim_index);
   nassertv(control != (AnimControl *)NULL);
   nassertv(control != (AnimControl *)NULL);
 
 
-  if (_anim_slider->is_button_down()) {
-    control->pose((int)(_anim_slider->get_value() + 0.5));
-  } else {
-    _anim_slider->set_value((PN_stdfloat)control->get_frame());
+  if (_anim_slider != NULL) {
+    if (_anim_slider->is_button_down()) {
+      control->pose((int)(_anim_slider->get_value() + 0.5));
+    } else {
+      _anim_slider->set_value((PN_stdfloat)control->get_frame());
+    }
   }
   }
 
 
-  _frame_number->set_text(format_string(control->get_frame()));
+  if (_frame_number != NULL) {
+    _frame_number->set_text(format_string(control->get_frame()));
+  }
 
 
   control->set_play_rate(_play_rate_slider->get_value());
   control->set_play_rate(_play_rate_slider->get_value());
 }
 }

+ 12 - 13
panda/src/gles2gsg/gles2gsg.h

@@ -57,7 +57,7 @@
 //  #include <GLES2/gl2ext.h>
 //  #include <GLES2/gl2ext.h>
 #endif
 #endif
 
 
-#include "panda_esgl2ext.h" 
+#include "panda_esgl2ext.h"
 
 
 // This helps to keep the source clean of hundreds of #ifdefs.
 // This helps to keep the source clean of hundreds of #ifdefs.
 typedef char GLchar;
 typedef char GLchar;
@@ -100,20 +100,19 @@ typedef char GLchar;
 #define GL_RGBA16F GL_RGBA16F_EXT
 #define GL_RGBA16F GL_RGBA16F_EXT
 #define GL_RGB32F GL_RGB32F_EXT
 #define GL_RGB32F GL_RGB32F_EXT
 #define GL_RGBA32F GL_RGBA32F_EXT
 #define GL_RGBA32F GL_RGBA32F_EXT
-#define GL_DEBUG_SEVERITY_HIGH GL_DEBUG_SEVERITY_HIGH_KHR
-#define GL_DEBUG_SEVERITY_MEDIUM GL_DEBUG_SEVERITY_MEDIUM_KHR
-#define GL_DEBUG_SEVERITY_LOW GL_DEBUG_SEVERITY_LOW_KHR
-#define GL_DEBUG_SEVERITY_NOTIFICATION GL_DEBUG_SEVERITY_NOTIFICATION_KHR
-#define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR
-#define GL_FRAMEBUFFER_SRGB GL_FRAMEBUFFER_SRGB_EXT
 #define GL_SRGB GL_SRGB_EXT
 #define GL_SRGB GL_SRGB_EXT
 #define GL_SRGB_ALPHA GL_SRGB_ALPHA_EXT
 #define GL_SRGB_ALPHA GL_SRGB_ALPHA_EXT
-#define GL_SRGB8 GL_SRGB8_EXT
-#define GL_SRGB8_ALPHA GL_SRGB8_ALPHA_EXT
-#define GL_SLUMINANCE GL_SLUMINANCE_NV
-#define GL_SLUMINANCE_ALPHA GL_SLUMINANCE_ALPHA_NV
-#define GL_SLUMINANCE8 GL_SLUMINANCE8_NV
-#define GL_SLUMINANCE8_ALPHA GL_SLUMINANCE8_ALPHA_NV
+#define GL_SRGB8_ALPHA8 GL_SRGB8_ALPHA8_EXT
+#define GL_RGBA8 GL_RGBA8_OES
+#define GL_R8 GL_R8_EXT
+#define GL_RG8 GL_RG8_EXT
+#define GL_ALPHA8 GL_ALPHA8_OES
+#define GL_LUMINANCE8 GL_LUMINANCE8_OES
+#define GL_LUMINANCE8_ALPHA8 GL_LUMINANCE8_ALPHA8_EXT
+#define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_EXT
+#define GL_R32F GL_R32F_EXT
+#define GL_RG32F GL_RG32F_EXT
+#define GL_RGB8 GL_RGB8_OES
 
 
 #undef SUPPORT_IMMEDIATE_MODE
 #undef SUPPORT_IMMEDIATE_MODE
 #define APIENTRY
 #define APIENTRY

+ 3 - 0
panda/src/glesgsg/glesgsg.h

@@ -115,6 +115,9 @@
 #define GL_RGBA16F GL_RGBA16F_EXT
 #define GL_RGBA16F GL_RGBA16F_EXT
 #define GL_RGB32F GL_RGB32F_EXT
 #define GL_RGB32F GL_RGB32F_EXT
 #define GL_RGBA32F GL_RGBA32F_EXT
 #define GL_RGBA32F GL_RGBA32F_EXT
+#define GL_ALPHA8 GL_ALPHA8_EXT
+#define GL_LUMINANCE8 GL_LUMINANCE8_EXT
+#define GL_LUMINANCE8_ALPHA8 GL_LUMINANCE8_ALPHA8_EXT
 
 
 #undef SUPPORT_IMMEDIATE_MODE
 #undef SUPPORT_IMMEDIATE_MODE
 #define APIENTRY
 #define APIENTRY

+ 4 - 11
panda/src/glstuff/glCgShaderContext_src.cxx

@@ -41,7 +41,6 @@ CLP(CgShaderContext)::
 CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext(s) {
 CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext(s) {
   _glgsg = glgsg;
   _glgsg = glgsg;
   _cg_program = 0;
   _cg_program = 0;
-  _glsl_profile = false;
 
 
   nassertv(s->get_language() == Shader::SL_Cg);
   nassertv(s->get_language() == Shader::SL_Cg);
 
 
@@ -63,10 +62,6 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte
     release_resources();
     release_resources();
 
 
   } else {
   } else {
-    if (cgGetProgramProfile(_cg_program) == CG_PROFILE_GLSLC) {
-      _glsl_profile = true;
-    }
-
     cgGLLoadProgram(_cg_program);
     cgGLLoadProgram(_cg_program);
     CGerror error = cgGetError();
     CGerror error = cgGetError();
     if (error != CG_NO_ERROR) {
     if (error != CG_NO_ERROR) {
@@ -412,7 +407,7 @@ disable_shader_vertex_arrays() {
     CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno];
     CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno];
     if (p == 0) continue;
     if (p == 0) continue;
 
 
-    if (_glsl_profile && cgGetParameterBaseResource(p) == CG_ATTR0) {
+    if (cgGetParameterBaseResource(p) == CG_ATTR0) {
       int index = cgGetParameterResourceIndex(p);
       int index = cgGetParameterResourceIndex(p);
       if (index >= 8) {
       if (index >= 8) {
         _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (index - 8));
         _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (index - 8));
@@ -501,11 +496,9 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) {
           num_values = GL_BGRA;
           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);
           int index = cgGetParameterResourceIndex(p);
           switch (index) {
           switch (index) {
           case 0:  // gl_Vertex
           case 0:  // gl_Vertex

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

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

+ 1 - 1
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -1674,7 +1674,7 @@ resolve_multisamples() {
                               GL_NEAREST);
                               GL_NEAREST);
   }
   }
   // Now handle the other color buffers.
   // Now handle the other color buffers.
-#ifndef OPENGLES_1
+#ifndef OPENGLES
   int next = GL_COLOR_ATTACHMENT1_EXT;
   int next = GL_COLOR_ATTACHMENT1_EXT;
   if (_fb_properties.is_stereo()) {
   if (_fb_properties.is_stereo()) {
     glReadBuffer(next);
     glReadBuffer(next);

+ 6 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.I

@@ -745,9 +745,12 @@ call_glTexParameterfv(GLenum target, GLenum pname, const LVecBase4 &value) {
 //       Access: Public
 //       Access: Public
 //  Description: Convert index to gl light id
 //  Description: Convert index to gl light id
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE GLenum CLP(GraphicsStateGuardian)::get_light_id(int index) const {
+INLINE GLenum CLP(GraphicsStateGuardian)::
+get_light_id(int index) const {
 #ifndef OPENGLES_2
 #ifndef OPENGLES_2
   return GL_LIGHT0 + index;
   return GL_LIGHT0 + index;
+#else
+  return 0;
 #endif
 #endif
 }
 }
 
 
@@ -760,6 +763,8 @@ INLINE GLenum CLP(GraphicsStateGuardian)::
 get_clip_plane_id(int index) const {
 get_clip_plane_id(int index) const {
 #ifndef OPENGLES_2
 #ifndef OPENGLES_2
   return GL_CLIP_PLANE0 + index;
   return GL_CLIP_PLANE0 + index;
+#else
+  return 0;
 #endif
 #endif
 }
 }
 
 

+ 98 - 83
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -127,16 +127,16 @@ null_glBlendColor(GLclampf, GLclampf, GLclampf, GLclampf) {
 // a fixed-function pipeline.
 // a fixed-function pipeline.
 // This default shader just outputs a red color, telling
 // This default shader just outputs a red color, telling
 // the user that something went wrong.
 // the user that something went wrong.
-CPT(Shader::ShaderFile) default_shader_name = new Shader::ShaderFile("default-shader");
-CPT(Shader::ShaderFile) default_shader_body = new Shader::ShaderFile("\
-uniform mediump mat4 p3d_ModelViewProjectionMatrix;\
-attribute highp vec4 p3d_Vertex;\
-void main(void) {\
-  gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;\
-}\n",
-"void main(void) {\
-  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\
-}\n", "", "", "");
+static const string default_vshader =
+  "uniform mediump mat4 p3d_ModelViewProjectionMatrix;\n"
+  "attribute highp vec4 p3d_Vertex;\n"
+  "void main(void) {\n"
+  "  gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;\n"
+  "}\n";
+static const string default_fshader =
+  "void main(void) {\n"
+  "  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
+  "}\n";
 #endif
 #endif
 
 
 
 
@@ -558,10 +558,13 @@ reset() {
   _glPrimitiveRestartIndex = NULL;
   _glPrimitiveRestartIndex = NULL;
 
 
   if (gl_support_primitive_restart_index) {
   if (gl_support_primitive_restart_index) {
-    if (is_at_least_gl_version(4, 3) || has_extension("GL_ARB_ES3_compatibility")) {
+    if ((is_at_least_gl_version(4, 3) || has_extension("GL_ARB_ES3_compatibility")) &&
+        _gl_renderer.substr(0, 7) != "Gallium") {
       // As long as we enable this, OpenGL will always use the highest possible index
       // As long as we enable this, OpenGL will always use the highest possible index
       // for a numeric type as strip cut index, which coincides with our convention.
       // for a numeric type as strip cut index, which coincides with our convention.
-      // This saves us a call to glPrimitiveRestartIndex.
+      // This saves us a call to glPrimitiveRestartIndex
+      // ... of course, though, the Gallium driver bugs out here.  See also:
+      // https://www.panda3d.org/forums/viewtopic.php?f=5&t=17512
       glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
       glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
       _supported_geom_rendering |= Geom::GR_strip_cut_index;
       _supported_geom_rendering |= Geom::GR_strip_cut_index;
 
 
@@ -1370,7 +1373,7 @@ reset() {
   // if it failed to compile. This default shader just outputs
   // if it failed to compile. This default shader just outputs
   // a red color, indicating that something went wrong.
   // a red color, indicating that something went wrong.
   if (_default_shader == NULL) {
   if (_default_shader == NULL) {
-    _default_shader = new Shader(default_shader_name, default_shader_body, Shader::SL_GLSL);
+    _default_shader = Shader::load(Shader::SL_GLSL, default_vshader, default_fshader);
   }
   }
 #endif
 #endif
 
 
@@ -1915,6 +1918,8 @@ reset() {
   }
   }
 
 
   _supports_sampler_objects = false;
   _supports_sampler_objects = false;
+
+#ifndef OPENGLES
   if (gl_support_sampler_objects &&
   if (gl_support_sampler_objects &&
       ((is_at_least_gl_version(3, 3) || has_extension("GL_ARB_sampler_objects")))) {
       ((is_at_least_gl_version(3, 3) || has_extension("GL_ARB_sampler_objects")))) {
     _glGenSamplers = (PFNGLGENSAMPLERSPROC) get_extension_func("glGenSamplers");
     _glGenSamplers = (PFNGLGENSAMPLERSPROC) get_extension_func("glGenSamplers");
@@ -1935,6 +1940,7 @@ reset() {
       _supports_sampler_objects = true;
       _supports_sampler_objects = true;
     }
     }
   }
   }
+#endif  // OPENGLES
 
 
   // Check availability of multi-bind functions.
   // Check availability of multi-bind functions.
   _supports_multi_bind = false;
   _supports_multi_bind = false;
@@ -1944,10 +1950,12 @@ reset() {
     _glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)
     _glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)
       get_extension_func("glBindImageTextures");
       get_extension_func("glBindImageTextures");
 
 
+#ifndef OPENGLES
     if (_supports_sampler_objects) {
     if (_supports_sampler_objects) {
       _glBindSamplers = (PFNGLBINDSAMPLERSPROC)
       _glBindSamplers = (PFNGLBINDSAMPLERSPROC)
         get_extension_func("glBindSamplers");
         get_extension_func("glBindSamplers");
     }
     }
+#endif  // OPENGLES
 
 
     if (_glBindTextures != NULL && _glBindImageTextures != NULL) {
     if (_glBindTextures != NULL && _glBindImageTextures != NULL) {
       _supports_multi_bind = true;
       _supports_multi_bind = true;
@@ -2011,14 +2019,18 @@ reset() {
 
 
   _supports_stencil_wrap =
   _supports_stencil_wrap =
     has_extension("GL_EXT_stencil_wrap") || has_extension("GL_OES_stencil_wrap");
     has_extension("GL_EXT_stencil_wrap") || has_extension("GL_OES_stencil_wrap");
-  _supports_two_sided_stencil = has_extension("GL_EXT_stencil_two_side");
-  if (_supports_two_sided_stencil) {
+
+
+  _supports_two_sided_stencil = false;
+#ifndef OPENGLES
+  if (has_extension("GL_EXT_stencil_two_side")) {
     _glActiveStencilFaceEXT = (PFNGLACTIVESTENCILFACEEXTPROC)
     _glActiveStencilFaceEXT = (PFNGLACTIVESTENCILFACEEXTPROC)
       get_extension_func("glActiveStencilFaceEXT");
       get_extension_func("glActiveStencilFaceEXT");
-  }
-  else {
+    _supports_two_sided_stencil = true;
+  } else {
     _glActiveStencilFaceEXT = 0;
     _glActiveStencilFaceEXT = 0;
   }
   }
+#endif
 
 
 #ifndef OPENGLES
 #ifndef OPENGLES
   // Some drivers expose one, some expose the other. ARB seems to be the newer one.
   // Some drivers expose one, some expose the other. ARB seems to be the newer one.
@@ -2618,9 +2630,11 @@ clear_before_callback() {
 
 
   // Clear the bound sampler object, so that we do not inadvertently
   // Clear the bound sampler object, so that we do not inadvertently
   // override the callback's desired sampler settings.
   // override the callback's desired sampler settings.
+#ifndef OPENGLES
   if (_supports_sampler_objects) {
   if (_supports_sampler_objects) {
     _glBindSampler(0, 0);
     _glBindSampler(0, 0);
   }
   }
+#endif
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2761,7 +2775,7 @@ begin_frame(Thread *current_thread) {
   }*/
   }*/
 #endif
 #endif
 
 
-#ifndef OPENGLES_1
+#ifndef OPENGLES
   if (_current_properties->get_srgb_color()) {
   if (_current_properties->get_srgb_color()) {
     glEnable(GL_FRAMEBUFFER_SRGB);
     glEnable(GL_FRAMEBUFFER_SRGB);
   }
   }
@@ -2819,7 +2833,7 @@ void CLP(GraphicsStateGuardian)::
 end_frame(Thread *current_thread) {
 end_frame(Thread *current_thread) {
   report_my_gl_errors();
   report_my_gl_errors();
 
 
-#ifndef OPENGLES_1
+#ifndef OPENGLES
   if (_current_properties->get_srgb_color()) {
   if (_current_properties->get_srgb_color()) {
     glDisable(GL_FRAMEBUFFER_SRGB);
     glDisable(GL_FRAMEBUFFER_SRGB);
   }
   }
@@ -4521,8 +4535,10 @@ prepare_sampler(const SamplerState &sampler) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
 void CLP(GraphicsStateGuardian)::
 release_sampler(SamplerContext *sc) {
 release_sampler(SamplerContext *sc) {
+#ifndef OPENGLES
   CLP(SamplerContext) *gsc = DCAST(CLP(SamplerContext), sc);
   CLP(SamplerContext) *gsc = DCAST(CLP(SamplerContext), sc);
   delete gsc;
   delete gsc;
+#endif
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -5205,7 +5221,7 @@ make_geom_munger(const RenderState *state, Thread *current_thread) {
 //               from the camera plane.  The point is assumed to be
 //               from the camera plane.  The point is assumed to be
 //               in the GSG's internal coordinate system.
 //               in the GSG's internal coordinate system.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PN_stdfloat GLGraphicsStateGuardian::
+PN_stdfloat CLP(GraphicsStateGuardian)::
 compute_distance_to(const LPoint3 &point) const {
 compute_distance_to(const LPoint3 &point) const {
   return -point[2];
   return -point[2];
 }
 }
@@ -7447,7 +7463,7 @@ get_external_image_format(Texture *tex) const {
       break;
       break;
 
 
     case Texture::CM_dxt1:
     case Texture::CM_dxt1:
-#ifndef OPENGLES_1
+#ifndef OPENGLES
       if (format == Texture::F_srgb_alpha) {
       if (format == Texture::F_srgb_alpha) {
         return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
         return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
       } else if (format == Texture::F_srgb) {
       } else if (format == Texture::F_srgb) {
@@ -7484,13 +7500,13 @@ get_external_image_format(Texture *tex) const {
 
 
 #else
 #else
     case Texture::CM_pvr1_2bpp:
     case Texture::CM_pvr1_2bpp:
-#ifndef OPENGLES_1
+#ifndef OPENGLES
       if (format == Texture::F_srgb_alpha) {
       if (format == Texture::F_srgb_alpha) {
         return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT;
         return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT;
       } else if (format == Texture::F_srgb) {
       } else if (format == Texture::F_srgb) {
         return GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT;
         return GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT;
       } else
       } else
-#endif  // OPENGLES_1
+#endif  // OPENGLES
       if (Texture::has_alpha(format)) {
       if (Texture::has_alpha(format)) {
         return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
         return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
       } else {
       } else {
@@ -7498,13 +7514,13 @@ get_external_image_format(Texture *tex) const {
       }
       }
 
 
     case Texture::CM_pvr1_4bpp:
     case Texture::CM_pvr1_4bpp:
-#ifndef OPENGLES_1
+#ifndef OPENGLES
       if (format == Texture::F_srgb_alpha) {
       if (format == Texture::F_srgb_alpha) {
         return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT;
         return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT;
       } else if (format == Texture::F_srgb) {
       } else if (format == Texture::F_srgb) {
         return GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT;
         return GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT;
       } else
       } else
-#endif  // OPENGLES_1
+#endif  // OPENGLES
       if (Texture::has_alpha(format)) {
       if (Texture::has_alpha(format)) {
         return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
         return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
       } else {
       } else {
@@ -7762,7 +7778,7 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
 #endif
 #endif
 
 
     case Texture::CM_dxt1:
     case Texture::CM_dxt1:
-#ifndef OPENGLES_1
+#ifndef OPENGLES
       if (format == Texture::F_srgb_alpha) {
       if (format == Texture::F_srgb_alpha) {
         return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
         return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
       } else if (format == Texture::F_srgb) {
       } else if (format == Texture::F_srgb) {
@@ -7798,13 +7814,6 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
       }
       }
 #else
 #else
     case Texture::CM_pvr1_2bpp:
     case Texture::CM_pvr1_2bpp:
-#ifndef OPENGLES_1
-      if (format == Texture::F_srgb_alpha) {
-        return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT;
-      } else if (format == Texture::F_srgb) {
-        return GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT;
-      } else
-#endif  // OPENGLES_1
       if (Texture::has_alpha(format)) {
       if (Texture::has_alpha(format)) {
         return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
         return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
       } else {
       } else {
@@ -7812,13 +7821,6 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
       }
       }
 
 
     case Texture::CM_pvr1_4bpp:
     case Texture::CM_pvr1_4bpp:
-#ifndef OPENGLES_1
-      if (format == Texture::F_srgb_alpha) {
-        return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT;
-      } else if (format == Texture::F_srgb) {
-        return GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT;
-      } else
-#endif  // OPENGLES_1
       if (Texture::has_alpha(format)) {
       if (Texture::has_alpha(format)) {
         return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
         return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
       } else {
       } else {
@@ -7843,7 +7845,7 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
 
 
   case Texture::F_depth_stencil:
   case Texture::F_depth_stencil:
     if (_supports_depth_stencil) {
     if (_supports_depth_stencil) {
-#ifndef OPENGLES_1
+#ifndef OPENGLES
       if (tex->get_component_type() == Texture::T_float) {
       if (tex->get_component_type() == Texture::T_float) {
         return GL_DEPTH32F_STENCIL8;
         return GL_DEPTH32F_STENCIL8;
       } else
       } else
@@ -7903,7 +7905,10 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
     if (tex->get_component_type() == Texture::T_float) {
     if (tex->get_component_type() == Texture::T_float) {
       return GL_RGBA16F;
       return GL_RGBA16F;
-    } else if (tex->get_component_type() == Texture::T_unsigned_short) {
+    } else
+#endif
+#ifndef OPENGLES
+    if (tex->get_component_type() == Texture::T_unsigned_short) {
       return GL_RGBA16;
       return GL_RGBA16;
     } else
     } else
 #endif
 #endif
@@ -7949,7 +7954,7 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
   case Texture::F_rgba12:
   case Texture::F_rgba12:
     return GL_RGBA12;
     return GL_RGBA12;
 #endif  // OPENGLES
 #endif  // OPENGLES
-#ifndef OPENGLES_1
+#ifndef OPENGLES
   case Texture::F_rgba16:
   case Texture::F_rgba16:
     if (tex->get_component_type() == Texture::T_float) {
     if (tex->get_component_type() == Texture::T_float) {
       return GL_RGBA16F;
       return GL_RGBA16F;
@@ -8040,26 +8045,36 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
     return force_sized ? GL_ALPHA8 : GL_ALPHA;
     return force_sized ? GL_ALPHA8 : GL_ALPHA;
 
 
   case Texture::F_luminance:
   case Texture::F_luminance:
+#ifndef OPENGLES
     if (tex->get_component_type() == Texture::T_float) {
     if (tex->get_component_type() == Texture::T_float) {
       return GL_LUMINANCE16F_ARB;
       return GL_LUMINANCE16F_ARB;
     } else if (tex->get_component_type() == Texture::T_unsigned_short) {
     } else if (tex->get_component_type() == Texture::T_unsigned_short) {
       return GL_LUMINANCE16;
       return GL_LUMINANCE16;
-    } else {
+    } else
+#endif  // OPENGLES
+    {
       return force_sized ? GL_LUMINANCE8 : GL_LUMINANCE;
       return force_sized ? GL_LUMINANCE8 : GL_LUMINANCE;
     }
     }
   case Texture::F_luminance_alpha:
   case Texture::F_luminance_alpha:
   case Texture::F_luminance_alphamask:
   case Texture::F_luminance_alphamask:
+#ifndef OPENGLES
     if (tex->get_component_type() == Texture::T_float || tex->get_component_type() == Texture::T_unsigned_short) {
     if (tex->get_component_type() == Texture::T_float || tex->get_component_type() == Texture::T_unsigned_short) {
       return GL_LUMINANCE_ALPHA16F_ARB;
       return GL_LUMINANCE_ALPHA16F_ARB;
-    } else {
+    } else
+#endif  // OPENGLES
+    {
       return force_sized ? GL_LUMINANCE8_ALPHA8 : GL_LUMINANCE_ALPHA;
       return force_sized ? GL_LUMINANCE8_ALPHA8 : GL_LUMINANCE_ALPHA;
     }
     }
 
 
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
   case Texture::F_srgb:
   case Texture::F_srgb:
+#ifndef OPENGLES
     return GL_SRGB8;
     return GL_SRGB8;
+#endif
   case Texture::F_srgb_alpha:
   case Texture::F_srgb_alpha:
     return GL_SRGB8_ALPHA8;
     return GL_SRGB8_ALPHA8;
+#endif
+#ifndef OPENGLES
   case Texture::F_sluminance:
   case Texture::F_sluminance:
     return GL_SLUMINANCE8;
     return GL_SLUMINANCE8;
   case Texture::F_sluminance_alpha:
   case Texture::F_sluminance_alpha:
@@ -9043,7 +9058,7 @@ set_state_and_transform(const RenderState *target,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
 void CLP(GraphicsStateGuardian)::
 free_pointers() {
 free_pointers() {
-#ifdef HAVE_CG
+#if defined(HAVE_CG) && !defined(OPENGLES)
   if (_cg_context != 0) {
   if (_cg_context != 0) {
     cgDestroyContext(_cg_context);
     cgDestroyContext(_cg_context);
     _cg_context = 0;
     _cg_context = 0;
@@ -10081,6 +10096,7 @@ bool CLP(GraphicsStateGuardian)::
 apply_sampler(GLuint unit, const SamplerState &sampler, TextureContext *tc) {
 apply_sampler(GLuint unit, const SamplerState &sampler, TextureContext *tc) {
   CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
   CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
 
 
+#ifndef OPENGLES
   if (_supports_sampler_objects) {
   if (_supports_sampler_objects) {
     // We support sampler objects.  Prepare the sampler object and
     // We support sampler objects.  Prepare the sampler object and
     // bind it to the indicated texture unit.
     // bind it to the indicated texture unit.
@@ -10097,7 +10113,9 @@ apply_sampler(GLuint unit, const SamplerState &sampler, TextureContext *tc) {
         << "bind " << unit << " " << sampler << "\n";
         << "bind " << unit << " " << sampler << "\n";
     }
     }
 
 
-  } else {
+  } else
+#endif  // OPENGLES
+  {
     // We don't support sampler objects.  We'll have to bind the
     // We don't support sampler objects.  We'll have to bind the
     // texture and change the texture parameters if they don't match.
     // texture and change the texture parameters if they don't match.
     if (gtc->_active_sampler != sampler) {
     if (gtc->_active_sampler != sampler) {
@@ -10678,6 +10696,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
           if (tex->has_clear_color()) {
           if (tex->has_clear_color()) {
             // The texture has a clear color, so we should fill this mipmap
             // The texture has a clear color, so we should fill this mipmap
             // level to a solid color.
             // level to a solid color.
+#ifndef OPENGLES
             if (_supports_clear_texture) {
             if (_supports_clear_texture) {
               // We can do that with the convenient glClearTexImage function.
               // We can do that with the convenient glClearTexImage function.
               string clear_data = tex->get_clear_data();
               string clear_data = tex->get_clear_data();
@@ -10685,12 +10704,13 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
               _glClearTexImage(gtc->_index, n - mipmap_bias, external_format,
               _glClearTexImage(gtc->_index, n - mipmap_bias, external_format,
                                component_type, (void *)clear_data.data());
                                component_type, (void *)clear_data.data());
               continue;
               continue;
-            } else {
-              // Ask the Texture class to create the mipmap level in RAM.
-              // It'll fill it in with the correct clear color, which we
-              // can then upload.
-              ptimage = tex->make_ram_mipmap_image(n);
             }
             }
+#endif  // OPENGLES
+            // Ask the Texture class to create the mipmap level in RAM.
+            // It'll fill it in with the correct clear color, which we
+            // can then upload.
+            ptimage = tex->make_ram_mipmap_image(n);
+
           } else {
           } else {
             // No clear color and no more images.
             // No clear color and no more images.
             break;
             break;
@@ -10726,13 +10746,8 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
       _data_transferred_pcollector.add_level(view_size);
       _data_transferred_pcollector.add_level(view_size);
 #endif
 #endif
       switch (texture_target) {
       switch (texture_target) {
-#ifdef OPENGLES_2
-      case GL_TEXTURE_3D_OES:
-#endif
-#ifndef OPENGLES
-      case GL_TEXTURE_3D:
-#endif
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
+      case GL_TEXTURE_3D:
         if (_supports_3d_texture) {
         if (_supports_3d_texture) {
           if (image_compression == Texture::CM_off) {
           if (image_compression == Texture::CM_off) {
             _glTexSubImage3D(page_target, n - mipmap_bias, 0, 0, 0, width, height, depth,
             _glTexSubImage3D(page_target, n - mipmap_bias, 0, 0, 0, width, height, depth,
@@ -10746,7 +10761,9 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
           return false;
           return false;
         }
         }
         break;
         break;
+#endif  // OPENGLES_1
 
 
+#ifndef OPENGLES
       case GL_TEXTURE_1D:
       case GL_TEXTURE_1D:
         if (image_compression == Texture::CM_off) {
         if (image_compression == Texture::CM_off) {
           glTexSubImage1D(page_target, n - mipmap_bias, 0, width,
           glTexSubImage1D(page_target, n - mipmap_bias, 0, width,
@@ -10756,7 +10773,8 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
                                      external_format, view_size, image_ptr);
                                      external_format, view_size, image_ptr);
         }
         }
         break;
         break;
-#endif
+#endif  // OPENGLES
+
 #ifndef OPENGLES
 #ifndef OPENGLES
       case GL_TEXTURE_2D_ARRAY_EXT:
       case GL_TEXTURE_2D_ARRAY_EXT:
         if (_supports_2d_texture_array) {
         if (_supports_2d_texture_array) {
@@ -10772,7 +10790,8 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
           return false;
           return false;
         }
         }
         break;
         break;
-#endif
+#endif  // OPENGLES
+
       default:
       default:
         if (image_compression == Texture::CM_off) {
         if (image_compression == Texture::CM_off) {
           if (n==0) {
           if (n==0) {
@@ -11319,7 +11338,7 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
     type = Texture::T_unsigned_int_24_8;
     type = Texture::T_unsigned_int_24_8;
     format = Texture::F_depth_stencil;
     format = Texture::F_depth_stencil;
     break;
     break;
-#ifndef OPENGLES_1
+#ifndef OPENGLES
   case GL_DEPTH32F_STENCIL8:
   case GL_DEPTH32F_STENCIL8:
     type = Texture::T_float;
     type = Texture::T_float;
     format = Texture::F_depth_stencil;
     format = Texture::F_depth_stencil;
@@ -11483,26 +11502,34 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
     format = Texture::F_alpha;
     format = Texture::F_alpha;
     break;
     break;
   case GL_LUMINANCE:
   case GL_LUMINANCE:
+#ifndef OPENGLES
   case GL_LUMINANCE16:
   case GL_LUMINANCE16:
   case GL_LUMINANCE16F_ARB:
   case GL_LUMINANCE16F_ARB:
+#endif
   case 1:
   case 1:
     format = Texture::F_luminance;
     format = Texture::F_luminance;
     break;
     break;
   case GL_LUMINANCE_ALPHA:
   case GL_LUMINANCE_ALPHA:
+#ifndef OPENGLES
   case GL_LUMINANCE_ALPHA16F_ARB:
   case GL_LUMINANCE_ALPHA16F_ARB:
+#endif
   case 2:
   case 2:
     format = Texture::F_luminance_alpha;
     format = Texture::F_luminance_alpha;
     break;
     break;
 
 
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
   case GL_SRGB:
   case GL_SRGB:
+#ifndef OPENGLES
   case GL_SRGB8:
   case GL_SRGB8:
+#endif
     format = Texture::F_srgb;
     format = Texture::F_srgb;
     break;
     break;
   case GL_SRGB_ALPHA:
   case GL_SRGB_ALPHA:
   case GL_SRGB8_ALPHA8:
   case GL_SRGB8_ALPHA8:
     format = Texture::F_srgb_alpha;
     format = Texture::F_srgb_alpha;
     break;
     break;
+#endif  // OPENGLES_1
+#ifndef OPENGLES
   case GL_SLUMINANCE:
   case GL_SLUMINANCE:
   case GL_SLUMINANCE8:
   case GL_SLUMINANCE8:
     format = Texture::F_sluminance;
     format = Texture::F_sluminance;
@@ -11511,7 +11538,7 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
   case GL_SLUMINANCE8_ALPHA8:
   case GL_SLUMINANCE8_ALPHA8:
     format = Texture::F_sluminance_alpha;
     format = Texture::F_sluminance_alpha;
     break;
     break;
-#endif
+#endif  // OPENGLES
 
 
 #ifndef OPENGLES
 #ifndef OPENGLES
   case GL_COMPRESSED_RGB:
   case GL_COMPRESSED_RGB:
@@ -11561,7 +11588,7 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
     format = Texture::F_rgbm;
     format = Texture::F_rgbm;
     compression = Texture::CM_dxt1;
     compression = Texture::CM_dxt1;
     break;
     break;
-#ifndef OPENGLES_1
+#ifndef OPENGLES
   case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
   case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
     format = Texture::F_srgb;
     format = Texture::F_srgb;
     compression = Texture::CM_dxt1;
     compression = Texture::CM_dxt1;
@@ -11572,25 +11599,6 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
     break;
     break;
 #endif
 #endif
 
 
-#ifdef OPENGLES_2
-  case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT:
-    format = Texture::F_srgb;
-    compression = Texture::CM_pvr1_2bpp;
-    break;
-  case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT:
-    format = Texture::F_srgb_alpha;
-    compression = Texture::CM_pvr1_2bpp;
-    break;
-  case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT:
-    format = Texture::F_srgb;
-    compression = Texture::CM_pvr1_4bpp;
-    break;
-  case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT:
-    format = Texture::F_srgb_alpha;
-    compression = Texture::CM_pvr1_4bpp;
-    break;
-#endif  // OPENGLES_2
-
 #ifdef OPENGLES
 #ifdef OPENGLES
   case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
   case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
     format = Texture::F_rgb;
     format = Texture::F_rgb;
@@ -11963,6 +11971,7 @@ do_issue_stencil() {
                     << "SRS_back_stencil_pass_z_pass_operation " << (int)stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_pass_operation) << "\n";
                     << "SRS_back_stencil_pass_z_pass_operation " << (int)stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_pass_operation) << "\n";
     }
     }
 
 
+#ifndef OPENGLES
     if (_supports_two_sided_stencil) {
     if (_supports_two_sided_stencil) {
       //TODO: add support for OpenGL 2.0-style glStencilFuncSeparate.
       //TODO: add support for OpenGL 2.0-style glStencilFuncSeparate.
       unsigned int back_compare;
       unsigned int back_compare;
@@ -11989,6 +11998,7 @@ do_issue_stencil() {
 
 
       _glActiveStencilFaceEXT(GL_FRONT);
       _glActiveStencilFaceEXT(GL_FRONT);
     }
     }
+#endif  // OPENGLES
 
 
     unsigned int front_compare;
     unsigned int front_compare;
     front_compare = stencil->get_render_state(StencilAttrib::SRS_front_comparison_function);
     front_compare = stencil->get_render_state(StencilAttrib::SRS_front_comparison_function);
@@ -12018,9 +12028,11 @@ do_issue_stencil() {
     }
     }
   } else {
   } else {
     glDisable(GL_STENCIL_TEST);
     glDisable(GL_STENCIL_TEST);
+#ifndef OPENGLES
     if (_supports_two_sided_stencil) {
     if (_supports_two_sided_stencil) {
       glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
       glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
     }
     }
+#endif  // OPENGLES
   }
   }
 }
 }
 
 
@@ -12067,9 +12079,12 @@ do_issue_scissor() {
     if (_scissor_array.size() > 0) {
     if (_scissor_array.size() > 0) {
       // Scissoring is enabled on the display region.
       // Scissoring is enabled on the display region.
       // Revert to the scissor state specified in the DisplayRegion.
       // Revert to the scissor state specified in the DisplayRegion.
+#ifndef OPENGLES
       if (_supports_viewport_arrays) {
       if (_supports_viewport_arrays) {
         _glScissorArrayv(0, _scissor_array.size(), _scissor_array[0].get_data());
         _glScissorArrayv(0, _scissor_array.size(), _scissor_array[0].get_data());
-      } else {
+      } else
+#endif  // OPENGLES
+      {
         const LVecBase4i sr = _scissor_array[0];
         const LVecBase4i sr = _scissor_array[0];
         glScissor(sr[0], sr[1], sr[2], sr[3]);
         glScissor(sr[0], sr[1], sr[2], sr[3]);
       }
       }

+ 4 - 0
panda/src/glstuff/glSamplerContext_src.cxx

@@ -14,6 +14,8 @@
 
 
 #include "pnotify.h"
 #include "pnotify.h"
 
 
+#ifndef OPENGLES
+
 TypeHandle CLP(SamplerContext)::_type_handle;
 TypeHandle CLP(SamplerContext)::_type_handle;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -81,3 +83,5 @@ reset_data() {
   // re-load the sampler later.
   // re-load the sampler later.
   //glGenSamplers(1, &_index);
   //glGenSamplers(1, &_index);
 }
 }
+
+#endif  // OPENGLES

+ 4 - 0
panda/src/glstuff/glSamplerContext_src.h

@@ -12,6 +12,8 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+#ifndef OPENGLES
+
 #include "pandabase.h"
 #include "pandabase.h"
 #include "samplerContext.h"
 #include "samplerContext.h"
 #include "deletedChain.h"
 #include "deletedChain.h"
@@ -56,3 +58,5 @@ public:
 private:
 private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 };
 };
+
+#endif  // OPENGLES

+ 22 - 2
panda/src/glstuff/glShaderContext_src.cxx

@@ -412,6 +412,20 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
             s->_mat_spec.push_back(bind);
             s->_mat_spec.push_back(bind);
             continue;
             continue;
           }
           }
+          if (noprefix == "Color") {
+            Shader::ShaderMatSpec bind;
+            bind._id = arg_id;
+            bind._piece = Shader::SMP_row3;
+            bind._func = Shader::SMF_first;
+            bind._part[0] = Shader::SMO_attr_color;
+            bind._arg[0] = NULL;
+            bind._dep[0] = Shader::SSD_general | Shader::SSD_color;
+            bind._part[1] = Shader::SMO_identity;
+            bind._arg[1] = NULL;
+            bind._dep[1] = Shader::SSD_NONE;
+            s->_mat_spec.push_back(bind);
+            continue;
+          }
           if (noprefix == "ClipPlane") {
           if (noprefix == "ClipPlane") {
             for (int i = 0; i < param_size; ++i) {
             for (int i = 0; i < param_size; ++i) {
               Shader::ShaderMatSpec bind;
               Shader::ShaderMatSpec bind;
@@ -821,10 +835,12 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
                        param_type == GL_INT_VEC2 ||
                        param_type == GL_INT_VEC2 ||
                        param_type == GL_INT_VEC3 ||
                        param_type == GL_INT_VEC3 ||
                        param_type == GL_INT_VEC4 ||
                        param_type == GL_INT_VEC4 ||
-                       param_type == GL_UNSIGNED_INT ||
+#ifndef OPENGLES
                        param_type == GL_UNSIGNED_INT_VEC2 ||
                        param_type == GL_UNSIGNED_INT_VEC2 ||
                        param_type == GL_UNSIGNED_INT_VEC3 ||
                        param_type == GL_UNSIGNED_INT_VEC3 ||
-                       param_type == GL_UNSIGNED_INT_VEC4);
+                       param_type == GL_UNSIGNED_INT_VEC4 ||
+#endif
+                       param_type == GL_UNSIGNED_INT );
 
 
       if (noprefix.empty()) {
       if (noprefix.empty()) {
         // Arbitrarily named attribute.
         // Arbitrarily named attribute.
@@ -863,12 +879,16 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
       // Get the number of bind points.
       // Get the number of bind points.
       switch (param_type) {
       switch (param_type) {
       case GL_FLOAT_MAT3:
       case GL_FLOAT_MAT3:
+#ifndef OPENGLES
       case GL_DOUBLE_MAT3:
       case GL_DOUBLE_MAT3:
+#endif
         bind._elements = 3 * param_size;
         bind._elements = 3 * param_size;
         break;
         break;
 
 
       case GL_FLOAT_MAT4:
       case GL_FLOAT_MAT4:
+#ifndef OPENGLES
       case GL_DOUBLE_MAT4:
       case GL_DOUBLE_MAT4:
+#endif
         bind._elements = 4 * param_size;
         bind._elements = 4 * param_size;
         break;
         break;
 
 

+ 2 - 0
panda/src/glstuff/glmisc_src.cxx

@@ -283,7 +283,9 @@ void CLP(init_classes)() {
   CLP(ShaderContext)::init_type();
   CLP(ShaderContext)::init_type();
 #endif
 #endif
   CLP(TextureContext)::init_type();
   CLP(TextureContext)::init_type();
+#ifndef OPENGLES
   CLP(SamplerContext)::init_type();
   CLP(SamplerContext)::init_type();
+#endif
   CLP(VertexBufferContext)::init_type();
   CLP(VertexBufferContext)::init_type();
   CLP(GraphicsBuffer)::init_type();
   CLP(GraphicsBuffer)::init_type();
 
 

+ 1 - 1
panda/src/gobj/internalName_ext.cxx

@@ -36,7 +36,7 @@ make(PyUnicodeObject *str) {
     }
     }
 
 
     string name(c_str, len);
     string name(c_str, len);
-    return InternalName::make(c_str, len);
+    return InternalName::make(name);
   }
   }
 
 
   InternalName::PyInternTable::const_iterator it;
   InternalName::PyInternTable::const_iterator it;

+ 1 - 0
panda/src/gobj/samplerContext.h

@@ -18,6 +18,7 @@
 #include "pandabase.h"
 #include "pandabase.h"
 
 
 #include "adaptiveLru.h"
 #include "adaptiveLru.h"
+#include "simpleLru.h"
 #include "samplerState.h"
 #include "samplerState.h"
 #include "savedContext.h"
 #include "savedContext.h"
 
 

+ 1 - 0
panda/src/gobj/samplerState.h

@@ -21,6 +21,7 @@
 #include "namable.h"
 #include "namable.h"
 #include "luse.h"
 #include "luse.h"
 #include "numeric_types.h"
 #include "numeric_types.h"
+#include "bamReader.h"
 #include "config_gobj.h"
 #include "config_gobj.h"
 
 
 class FactoryParams;
 class FactoryParams;

+ 75 - 70
panda/src/gobj/texture.cxx

@@ -41,6 +41,7 @@
 #include "pbitops.h"
 #include "pbitops.h"
 #include "streamReader.h"
 #include "streamReader.h"
 #include "texturePeeker.h"
 #include "texturePeeker.h"
+#include "convert_srgb.h"
 
 
 #ifdef HAVE_SQUISH
 #ifdef HAVE_SQUISH
 #include <squish.h>
 #include <squish.h>
@@ -131,46 +132,6 @@ struct DDSHeader {
   DDSCaps2 caps;
   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
 //     Function: Texture::Constructor
 //       Access: Published
 //       Access: Published
@@ -6990,7 +6951,13 @@ do_filter_2d_mipmap_pages(const CData *cdata,
     // We currently only support sRGB mipmap generation for
     // We currently only support sRGB mipmap generation for
     // unsigned byte textures, due to our use of a lookup table.
     // unsigned byte textures, due to our use of a lookup table.
     nassertv(cdata->_component_type == T_unsigned_byte);
     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.
     // Alpha is always linear.
     filter_alpha = &filter_2d_unsigned_byte;
     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
     // We currently only support sRGB mipmap generation for
     // unsigned byte textures, due to our use of a lookup table.
     // unsigned byte textures, due to our use of a lookup table.
     nassertv(cdata->_component_type == T_unsigned_byte);
     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.
     // Alpha is always linear.
     filter_alpha = &filter_3d_unsigned_byte;
     filter_alpha = &filter_3d_unsigned_byte;
 
 
@@ -7385,18 +7358,32 @@ filter_2d_unsigned_byte(unsigned char *&p, const unsigned char *&q,
 void Texture::
 void Texture::
 filter_2d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q,
 filter_2d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q,
                              size_t pixel_size, size_t row_size) {
                              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;
   ++p;
   ++q;
   ++q;
 }
 }
@@ -7470,22 +7457,40 @@ filter_3d_unsigned_byte(unsigned char *&p, const unsigned char *&q,
 void Texture::
 void Texture::
 filter_3d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q,
 filter_3d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q,
                              size_t pixel_size, size_t row_size, size_t page_size) {
                              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;
   ++p;
   ++q;
   ++q;
 }
 }

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

@@ -749,6 +749,9 @@ private:
   static void filter_2d_unsigned_byte_srgb(unsigned char *&p,
   static void filter_2d_unsigned_byte_srgb(unsigned char *&p,
                                            const unsigned char *&q,
                                            const unsigned char *&q,
                                            size_t pixel_size, size_t row_size);
                                            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,
   static void filter_2d_unsigned_short(unsigned char *&p,
                                        const unsigned char *&q,
                                        const unsigned char *&q,
                                        size_t pixel_size, size_t row_size);
                                        size_t pixel_size, size_t row_size);
@@ -763,6 +766,10 @@ private:
                                            const unsigned char *&q,
                                            const unsigned char *&q,
                                            size_t pixel_size, size_t row_size,
                                            size_t pixel_size, size_t row_size,
                                            size_t page_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,
   static void filter_3d_unsigned_short(unsigned char *&p,
                                        const unsigned char *&q,
                                        const unsigned char *&q,
                                        size_t pixel_size, size_t row_size,
                                        size_t pixel_size, size_t row_size,

+ 1 - 1
panda/src/linmath/lmatrix3_src.cxx

@@ -353,7 +353,7 @@ bool FLOATNAME(LMatrix3)::
 almost_equal(const FLOATNAME(LMatrix3) &other, FLOATTYPE threshold) const {
 almost_equal(const FLOATNAME(LMatrix3) &other, FLOATTYPE threshold) const {
   TAU_PROFILE("bool LMatrix3::almost_equal(const LMatrix3 &, FLOATTYPE)", " ", TAU_USER);
   TAU_PROFILE("bool LMatrix3::almost_equal(const LMatrix3 &, FLOATTYPE)", " ", TAU_USER);
 #ifdef HAVE_EIGEN
 #ifdef HAVE_EIGEN
-  return ((_m - other._m).cwiseAbs().maxCoeff() < NEARLY_ZERO(FLOATTYPE));
+  return ((_m - other._m).cwiseAbs().maxCoeff() < threshold);
 #else
 #else
   return (IS_THRESHOLD_EQUAL((*this)(0, 0), other(0, 0), threshold) &&
   return (IS_THRESHOLD_EQUAL((*this)(0, 0), other(0, 0), threshold) &&
           IS_THRESHOLD_EQUAL((*this)(0, 1), other(0, 1), threshold) &&
           IS_THRESHOLD_EQUAL((*this)(0, 1), other(0, 1), threshold) &&

+ 1 - 1
panda/src/linmath/lmatrix4_src.cxx

@@ -296,7 +296,7 @@ bool FLOATNAME(LMatrix4)::
 almost_equal(const FLOATNAME(LMatrix4) &other, FLOATTYPE threshold) const {
 almost_equal(const FLOATNAME(LMatrix4) &other, FLOATTYPE threshold) const {
   TAU_PROFILE("bool LMatrix4::almost_equal(const LMatrix4 &, FLOATTYPE)", " ", TAU_USER);
   TAU_PROFILE("bool LMatrix4::almost_equal(const LMatrix4 &, FLOATTYPE)", " ", TAU_USER);
 #ifdef HAVE_EIGEN
 #ifdef HAVE_EIGEN
-  return ((_m - other._m).cwiseAbs().maxCoeff() < NEARLY_ZERO(FLOATTYPE));
+  return ((_m - other._m).cwiseAbs().maxCoeff() < threshold);
 #else
 #else
   return (IS_THRESHOLD_EQUAL((*this)(0, 0), other(0, 0), threshold) &&
   return (IS_THRESHOLD_EQUAL((*this)(0, 0), other(0, 0), threshold) &&
           IS_THRESHOLD_EQUAL((*this)(0, 1), other(0, 1), threshold) &&
           IS_THRESHOLD_EQUAL((*this)(0, 1), other(0, 1), threshold) &&

+ 28 - 0
panda/src/mathutil/pta_LMatrix3_ext.h

@@ -0,0 +1,28 @@
+// Filename: pta_LMatrix3_ext.h
+// Created by:  rdb (25Feb15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PTA_LMATRIX3_EXT_H
+#define PTA_LMATRIX3_EXT_H
+
+#include "pointerToArray_ext.h"
+#include "pta_LMatrix3.h"
+
+#if defined(_MSC_VER) && !defined(CPPPARSER)
+template class EXPORT_THIS Extension<PTA_LMatrix3f>;
+template class EXPORT_THIS Extension<PTA_LMatrix3d>;
+template class EXPORT_THIS Extension<CPTA_LMatrix3f>;
+template class EXPORT_THIS Extension<CPTA_LMatrix3d>;
+#endif
+
+#endif

+ 28 - 0
panda/src/mathutil/pta_LMatrix4_ext.h

@@ -0,0 +1,28 @@
+// Filename: pta_LMatrix4_ext.h
+// Created by:  rdb (25Feb15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PTA_LMATRIX4_EXT_H
+#define PTA_LMATRIX4_EXT_H
+
+#include "pointerToArray_ext.h"
+#include "pta_LMatrix4.h"
+
+#if defined(_MSC_VER) && !defined(CPPPARSER)
+template class EXPORT_THIS Extension<PTA_LMatrix4f>;
+template class EXPORT_THIS Extension<PTA_LMatrix4d>;
+template class EXPORT_THIS Extension<CPTA_LMatrix4f>;
+template class EXPORT_THIS Extension<CPTA_LMatrix4d>;
+#endif
+
+#endif

+ 30 - 0
panda/src/mathutil/pta_LVecBase2_ext.h

@@ -0,0 +1,30 @@
+// Filename: pta_LVecBase2_ext.h
+// Created by:  rdb (25Feb15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PTA_LVECBASE2_EXT_H
+#define PTA_LVECBASE2_EXT_H
+
+#include "pointerToArray_ext.h"
+#include "pta_LVecBase2.h"
+
+#if defined(_MSC_VER) && !defined(CPPPARSER)
+template class EXPORT_THIS Extension<PTA_LVecBase2f>;
+template class EXPORT_THIS Extension<PTA_LVecBase2d>;
+template class EXPORT_THIS Extension<PTA_LVecBase2i>;
+template class EXPORT_THIS Extension<CPTA_LVecBase2f>;
+template class EXPORT_THIS Extension<CPTA_LVecBase2d>;
+template class EXPORT_THIS Extension<CPTA_LVecBase2i>;
+#endif
+
+#endif

+ 30 - 0
panda/src/mathutil/pta_LVecBase3_ext.h

@@ -0,0 +1,30 @@
+// Filename: pta_LVecBase3_ext.h
+// Created by:  rdb (25Feb15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PTA_LVECBASE3_EXT_H
+#define PTA_LVECBASE3_EXT_H
+
+#include "pointerToArray_ext.h"
+#include "pta_LVecBase3.h"
+
+#if defined(_MSC_VER) && !defined(CPPPARSER)
+template class EXPORT_THIS Extension<PTA_LVecBase3f>;
+template class EXPORT_THIS Extension<PTA_LVecBase3d>;
+template class EXPORT_THIS Extension<PTA_LVecBase3i>;
+template class EXPORT_THIS Extension<CPTA_LVecBase3f>;
+template class EXPORT_THIS Extension<CPTA_LVecBase3d>;
+template class EXPORT_THIS Extension<CPTA_LVecBase3i>;
+#endif
+
+#endif

+ 30 - 0
panda/src/mathutil/pta_LVecBase4_ext.h

@@ -0,0 +1,30 @@
+// Filename: pta_LVecBase4_ext.h
+// Created by:  rdb (25Feb15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PTA_LVECBASE4_EXT_H
+#define PTA_LVECBASE4_EXT_H
+
+#include "pointerToArray_ext.h"
+#include "pta_LVecBase4.h"
+
+#if defined(_MSC_VER) && !defined(CPPPARSER)
+template class EXPORT_THIS Extension<PTA_LVecBase4f>;
+template class EXPORT_THIS Extension<PTA_LVecBase4d>;
+template class EXPORT_THIS Extension<PTA_LVecBase4i>;
+template class EXPORT_THIS Extension<CPTA_LVecBase4f>;
+template class EXPORT_THIS Extension<CPTA_LVecBase4d>;
+template class EXPORT_THIS Extension<CPTA_LVecBase4i>;
+#endif
+
+#endif

+ 3 - 0
panda/src/pgraph/Sources.pp

@@ -79,6 +79,7 @@
     occluderNode.I occluderNode.h \
     occluderNode.I occluderNode.h \
     pandaNode.I pandaNode.h \
     pandaNode.I pandaNode.h \
     pandaNodeChain.I pandaNodeChain.h \
     pandaNodeChain.I pandaNodeChain.h \
+    paramNodePath.I paramNodePath.h \
     planeNode.I planeNode.h \
     planeNode.I planeNode.h \
     polylightEffect.I polylightEffect.h \
     polylightEffect.I polylightEffect.h \
     polylightNode.I polylightNode.h \
     polylightNode.I polylightNode.h \
@@ -184,6 +185,7 @@
     occluderNode.cxx \
     occluderNode.cxx \
     pandaNode.cxx \
     pandaNode.cxx \
     pandaNodeChain.cxx \
     pandaNodeChain.cxx \
+    paramNodePath.cxx \
     planeNode.cxx \
     planeNode.cxx \
     polylightEffect.cxx \
     polylightEffect.cxx \
     polylightNode.cxx \
     polylightNode.cxx \
@@ -286,6 +288,7 @@
     pandaNode.I pandaNode.h \
     pandaNode.I pandaNode.h \
     pandaNode_ext.h pandaNode_ext.cxx \
     pandaNode_ext.h pandaNode_ext.cxx \
     pandaNodeChain.I pandaNodeChain.h \
     pandaNodeChain.I pandaNodeChain.h \
+    paramNodePath.I paramNodePath.h \
     planeNode.I planeNode.h \
     planeNode.I planeNode.h \
     polylightEffect.I polylightEffect.h \
     polylightEffect.I polylightEffect.h \
     polylightNode.I polylightNode.h \
     polylightNode.I polylightNode.h \

+ 2 - 0
panda/src/pgraph/config_pgraph.cxx

@@ -60,6 +60,7 @@
 #include "nodePath.h"
 #include "nodePath.h"
 #include "nodePathComponent.h"
 #include "nodePathComponent.h"
 #include "pandaNode.h"
 #include "pandaNode.h"
+#include "paramNodePath.h"
 #include "planeNode.h"
 #include "planeNode.h"
 #include "polylightEffect.h"
 #include "polylightEffect.h"
 #include "polylightNode.h"
 #include "polylightNode.h"
@@ -436,6 +437,7 @@ init_libpgraph() {
   NodePathComponent::init_type();
   NodePathComponent::init_type();
   PandaNode::init_type();
   PandaNode::init_type();
   PandaNodePipelineReader::init_type();
   PandaNodePipelineReader::init_type();
+  ParamNodePath::init_type();
   PlaneNode::init_type();
   PlaneNode::init_type();
   PolylightNode::init_type();
   PolylightNode::init_type();
   PolylightEffect::init_type();
   PolylightEffect::init_type();

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

@@ -20,6 +20,7 @@
 #include "cullBinEnums.h"
 #include "cullBinEnums.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "pvector.h"
 #include "pvector.h"
+#include "epvector.h"
 #include "pmap.h"
 #include "pmap.h"
 #include "vector_int.h"
 #include "vector_int.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"

+ 4 - 4
panda/src/pgraph/loader.cxx

@@ -343,11 +343,11 @@ try_load_file(const Filename &pathname, const LoaderOptions &options,
         return result;
         return result;
       }
       }
     }
     }
-  }
 
 
-  if (loader_cat.is_debug()) {
-    loader_cat.debug()
-      << "Model " << pathname << " not found in cache.\n";
+    if (loader_cat.is_debug()) {
+      loader_cat.debug()
+        << "Model " << pathname << " not found in cache.\n";
+    }
   }
   }
 
 
   bool cache_only = (options.get_flags() & LoaderOptions::LF_cache_only) != 0;
   bool cache_only = (options.get_flags() & LoaderOptions::LF_cache_only) != 0;

+ 3 - 3
panda/src/pgraph/nodePath.h

@@ -38,6 +38,9 @@
 #include "pta_LVecBase3.h"
 #include "pta_LVecBase3.h"
 #include "pta_LVecBase2.h"
 #include "pta_LVecBase2.h"
 #include "stl_compares.h"
 #include "stl_compares.h"
+#include "shaderInput.h"
+#include "textureCollection.h"
+#include "textureStageCollection.h"
 
 
 class NodePathCollection;
 class NodePathCollection;
 class FindApproxPath;
 class FindApproxPath;
@@ -1008,9 +1011,6 @@ private:
 
 
 INLINE ostream &operator << (ostream &out, const NodePath &node_path);
 INLINE ostream &operator << (ostream &out, const NodePath &node_path);
 
 
-// We have to put this down here, to work around a circular include.
-#include "shaderInput.h"
-
 #include "nodePath.I"
 #include "nodePath.I"
 
 
 #endif
 #endif

+ 1 - 0
panda/src/pgraph/p3pgraph_composite3.cxx

@@ -21,6 +21,7 @@
 #include "occluderNode.cxx"
 #include "occluderNode.cxx"
 #include "pandaNode.cxx"
 #include "pandaNode.cxx"
 #include "pandaNodeChain.cxx"
 #include "pandaNodeChain.cxx"
+#include "paramNodePath.cxx"
 #include "planeNode.cxx"
 #include "planeNode.cxx"
 #include "polylightEffect.cxx"
 #include "polylightEffect.cxx"
 #include "polylightNode.cxx"
 #include "polylightNode.cxx"

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

@@ -721,13 +721,15 @@ get_user_bounds(int pipeline_stage, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
 INLINE void PandaNode::
 mark_bounds_stale(int pipeline_stage, Thread *current_thread) const {
 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;
   bool is_stale_bounds;
   {
   {
     CDStageReader cdata(_cycler, pipeline_stage, current_thread);
     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) {
   if (!is_stale_bounds) {
     ((PandaNode *)this)->force_bounds_stale(pipeline_stage, current_thread);
     ((PandaNode *)this)->force_bounds_stale(pipeline_stage, current_thread);
   }
   }
@@ -1833,7 +1835,7 @@ get_off_clip_planes() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CPT(BoundingVolume) PandaNodePipelineReader::
 INLINE CPT(BoundingVolume) PandaNodePipelineReader::
 get_bounds() const {
 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;
   return _cdata->_external_bounds;
 }
 }
 
 
@@ -1852,7 +1854,7 @@ get_bounds() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNodePipelineReader::
 INLINE int PandaNodePipelineReader::
 get_nested_vertices() const {
 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;
   return _cdata->_nested_vertices;
 }
 }
 
 

+ 60 - 0
panda/src/pgraph/paramNodePath.I

@@ -0,0 +1,60 @@
+// Filename: paramNodePath.I
+// Created by:  rdb (25Feb15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: ParamNodePath::Constructor
+//       Access: Published
+//  Description: Creates a new ParamNodePath storing the given
+//               node path object.
+////////////////////////////////////////////////////////////////////
+INLINE ParamNodePath::
+ParamNodePath(const NodePath &node_path) :
+  _node_path(node_path)
+{
+}
+
+#ifdef USE_MOVE_SEMANTICS
+////////////////////////////////////////////////////////////////////
+//     Function: ParamNodePath::Move Constructor
+//       Access: Published
+//  Description: Creates a new ParamNodePath storing the given
+//               node path object.
+////////////////////////////////////////////////////////////////////
+INLINE ParamNodePath::
+ParamNodePath(NodePath &&node_path) NOEXCEPT :
+  _node_path(move(node_path))
+{
+}
+#endif  // USE_MOVE_SEMANTICS
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamNodePath::get_value_type
+//       Access: Published, Virtual
+//  Description: Returns NodePath::get_class_type().
+////////////////////////////////////////////////////////////////////
+INLINE TypeHandle ParamNodePath::
+get_value_type() const {
+  return NodePath::get_class_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamNodePath::get_value
+//       Access: Published
+//  Description: Retrieves the NodePath stored in the parameter.
+////////////////////////////////////////////////////////////////////
+INLINE const NodePath &ParamNodePath::
+get_value() const {
+  return _node_path;
+}

+ 108 - 0
panda/src/pgraph/paramNodePath.cxx

@@ -0,0 +1,108 @@
+// Filename: paramNodePath.cxx
+// Created by:  rdb (25Feb15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "paramNodePath.h"
+#include "dcast.h"
+#include "pandaNode.h"
+
+TypeHandle ParamNodePath::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamNodePath::output
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void ParamNodePath::
+output(ostream &out) const {
+  out << "node path " << _node_path;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamNodePath::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               ParamValue.
+////////////////////////////////////////////////////////////////////
+void ParamNodePath::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamNodePath::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void ParamNodePath::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  ParamValueBase::write_datagram(manager, dg);
+
+  // We can't store a NodePath, so we store a pointer to the
+  // underlying node, and pray that there is an unambiguous path
+  // from the root to it.
+  if (_node_path.is_empty()) {
+    manager->write_pointer(dg, NULL);
+  } else {
+    manager->write_pointer(dg, _node_path.node());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamNodePath::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int ParamNodePath::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = ParamValueBase::complete_pointers(p_list, manager);
+  _node_path = NodePath(DCAST(PandaNode, p_list[pi++]));
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamNodePath::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type ParamValue is encountered
+//               in the Bam file.  It should create the ParamValue
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *ParamNodePath::
+make_from_bam(const FactoryParams &params) {
+  ParamNodePath *param = new ParamNodePath;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  param->fillin(scan, manager);
+
+  return param;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamNodePath::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new ParamValue.
+////////////////////////////////////////////////////////////////////
+void ParamNodePath::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  ParamValueBase::fillin(scan, manager);
+  manager->read_pointer(scan);
+}

+ 75 - 0
panda/src/pgraph/paramNodePath.h

@@ -0,0 +1,75 @@
+// Filename: paramNodePath.h
+// Created by:  rdb (25Feb15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PARAMNODEPATH_H
+#define PARAMNODEPATH_H
+
+#include "pandabase.h"
+#include "paramValue.h"
+#include "nodePath.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ParamNodePath
+// Description : A class object for storing a NodePath as a parameter.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_GOBJ ParamNodePath : public ParamValueBase {
+protected:
+  INLINE ParamNodePath() {};
+
+PUBLISHED:
+  INLINE ParamNodePath(const NodePath &node_path);
+
+#ifdef USE_MOVE_SEMANTICS
+  INLINE ParamNodePath(NodePath &&node_path) NOEXCEPT;
+#endif
+
+  INLINE virtual TypeHandle get_value_type() const;
+  INLINE const NodePath &get_value() const;
+
+  virtual void output(ostream &out) const;
+
+private:
+  NodePath _node_path;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist,
+                                BamReader *manager);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ParamValueBase::init_type();
+    register_type(_type_handle, "ParamNodePath",
+                  ParamValueBase::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "paramNodePath.I"
+
+#endif

+ 2 - 3
panda/src/pgraph/polylightNode.I

@@ -85,11 +85,10 @@ disable(){
 //  Description: Set this light's position
 //  Description: Set this light's position
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PolylightNode::
 INLINE void PolylightNode::
-set_pos(const LVecBase3 &position){
+set_pos(const LPoint3 &position) {
   _position = position;
   _position = position;
 }
 }
 
 
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PolylightNode::set_pos
 //     Function: PolylightNode::set_pos
 //       Access: Published
 //       Access: Published
@@ -107,7 +106,7 @@ set_pos(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z){
 //       Access: Published
 //       Access: Published
 //  Description: Returns position as a LPoint3
 //  Description: Returns position as a LPoint3
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE LVecBase3 PolylightNode::
+INLINE LPoint3 PolylightNode::
 get_pos() const {
 get_pos() const {
   return _position;
   return _position;
 }
 }

+ 24 - 0
panda/src/pgraph/polylightNode.cxx

@@ -65,6 +65,30 @@ make_copy() const {
   return new PolylightNode(*this);
   return new PolylightNode(*this);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PolylightNode::xform
+//       Access: Public, Virtual
+//  Description: Transforms the contents of this node by the indicated
+//               matrix, if it means anything to do so.  For most
+//               kinds of nodes, this does nothing.
+////////////////////////////////////////////////////////////////////
+void PolylightNode::
+xform(const LMatrix4 &mat) {
+  nassertv(!mat.is_nan());
+
+  if (mat.almost_equal(LMatrix4::ident_mat())) {
+    return;
+  }
+
+  _position = _position * mat;
+
+  // This is a little cheesy and fails miserably in the presence of a
+  // non-uniform scale.
+  LVector3 radius_v = LVector3(_radius, 0.0f, 0.0f) * mat;
+  _radius = length(radius_v);
+  mark_internal_bounds_stale();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PolylightNode::Constructor
 //     Function: PolylightNode::Constructor
 //       Access: Public
 //       Access: Public

+ 4 - 4
panda/src/pgraph/polylightNode.h

@@ -58,9 +58,9 @@ PUBLISHED:
   PolylightNode(const string &name);
   PolylightNode(const string &name);
   INLINE void enable();
   INLINE void enable();
   INLINE void disable();
   INLINE void disable();
-  INLINE void set_pos(const LVecBase3 &position);
+  INLINE void set_pos(const LPoint3 &position);
   INLINE void set_pos(PN_stdfloat x,PN_stdfloat y, PN_stdfloat z);
   INLINE void set_pos(PN_stdfloat x,PN_stdfloat y, PN_stdfloat z);
-  INLINE LVecBase3 get_pos() const;
+  INLINE LPoint3 get_pos() const;
   INLINE void set_color(const LColor &color);
   INLINE void set_color(const LColor &color);
   INLINE void set_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b);
   INLINE void set_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b);
   INLINE LColor get_color() const;
   INLINE LColor get_color() const;
@@ -100,11 +100,11 @@ PUBLISHED:
 public:
 public:
   LColor flicker() const;
   LColor flicker() const;
   virtual PandaNode *make_copy() const;
   virtual PandaNode *make_copy() const;
+  virtual void xform(const LMatrix4 &mat);
 
 
-  
 private:
 private:
   bool _enabled;
   bool _enabled;
-  LVecBase3 _position;
+  LPoint3 _position;
   LColor _color;
   LColor _color;
   PN_stdfloat _radius;
   PN_stdfloat _radius;
   Attenuation_Type _attenuation_type;
   Attenuation_Type _attenuation_type;

+ 0 - 27
panda/src/pgraph/shaderInput.I

@@ -119,23 +119,6 @@ ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority) :
 {
 {
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: ShaderInput::Constructor
-//       Access: Published
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE ShaderInput::
-ShaderInput(CPT_InternalName name, const NodePath &np, int priority) :
-  _name(MOVE(name)),
-  _type(M_nodepath),
-  _priority(priority),
-  _stored_nodepath(np),
-  _bind_layer(0),
-  _bind_level(0),
-  _access(A_read)
-{
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //     Function: ShaderInput::Constructor
 //       Access: Published
 //       Access: Published
@@ -678,16 +661,6 @@ get_texture() const {
   return DCAST(Texture, _value);
   return DCAST(Texture, _value);
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: ShaderInput::get_nodepath
-//       Access: Published
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE const NodePath &ShaderInput::
-get_nodepath() const {
-  return _stored_nodepath;
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::get_vector
 //     Function: ShaderInput::get_vector
 //       Access: Published
 //       Access: Published

+ 30 - 2
panda/src/pgraph/shaderInput.cxx

@@ -13,6 +13,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "shaderInput.h"
 #include "shaderInput.h"
+#include "paramNodePath.h"
 
 
 TypeHandle ShaderInput::_type_handle;
 TypeHandle ShaderInput::_type_handle;
 
 
@@ -32,13 +33,40 @@ get_blank() {
   return blank;
   return blank;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ShaderInput::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+ShaderInput::
+ShaderInput(CPT_InternalName name, const NodePath &np, int priority) :
+  _name(MOVE(name)),
+  _type(M_nodepath),
+  _priority(priority),
+  _value(new ParamNodePath(np)),
+  _bind_layer(0),
+  _bind_level(0),
+  _access(A_read)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ShaderInput::get_nodepath
+//       Access: Published
+//  Description: Warning: no error checking is done.  This *will*
+//               crash if get_value_type() is not M_nodepath.
+////////////////////////////////////////////////////////////////////
+const NodePath &ShaderInput::
+get_nodepath() const {
+  return DCAST(ParamNodePath, _value)->get_value();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::register_with_read_factory
 //     Function: ShaderInput::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void ShaderInput::
 void ShaderInput::
 register_with_read_factory() {
 register_with_read_factory() {
   // IMPLEMENT ME
   // IMPLEMENT ME
 }
 }
-

+ 4 - 4
panda/src/pgraph/shaderInput.h

@@ -19,7 +19,6 @@
 #include "pandabase.h"
 #include "pandabase.h"
 #include "typedWritableReferenceCount.h"
 #include "typedWritableReferenceCount.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
-#include "nodePath.h"
 #include "internalName.h"
 #include "internalName.h"
 #include "paramValue.h"
 #include "paramValue.h"
 #include "pta_float.h"
 #include "pta_float.h"
@@ -53,7 +52,6 @@ PUBLISHED:
 
 
   static const ShaderInput *get_blank();
   static const ShaderInput *get_blank();
   INLINE ShaderInput(CPT_InternalName name, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, int priority=0);
-  INLINE ShaderInput(CPT_InternalName name, const NodePath &np, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, Texture *tex, const SamplerState &sampler, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, Texture *tex, const SamplerState &sampler, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
@@ -90,6 +88,8 @@ PUBLISHED:
   INLINE ShaderInput(CPT_InternalName name, const LVecBase3i &vec, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const LVecBase3i &vec, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const LVecBase2i &vec, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const LVecBase2i &vec, int priority=0);
 
 
+  ShaderInput(CPT_InternalName name, const NodePath &np, int priority=0);
+
   enum ShaderInputType {
   enum ShaderInputType {
     M_invalid = 0,
     M_invalid = 0,
     M_texture,
     M_texture,
@@ -105,11 +105,12 @@ PUBLISHED:
   INLINE int get_value_type() const;
   INLINE int get_value_type() const;
   INLINE int get_priority() const;
   INLINE int get_priority() const;
   INLINE Texture *get_texture() const;
   INLINE Texture *get_texture() const;
-  INLINE const NodePath &get_nodepath() const;
   INLINE const LVecBase4 &get_vector() const;
   INLINE const LVecBase4 &get_vector() const;
   INLINE const Shader::ShaderPtrData &get_ptr() const;
   INLINE const Shader::ShaderPtrData &get_ptr() const;
   INLINE const SamplerState &get_sampler() const;
   INLINE const SamplerState &get_sampler() const;
 
 
+  const NodePath &get_nodepath() const;
+
 public:
 public:
   INLINE ParamValueBase *get_param() const;
   INLINE ParamValueBase *get_param() const;
 
 
@@ -118,7 +119,6 @@ public:
 private:
 private:
   SamplerState _sampler;
   SamplerState _sampler;
   LVecBase4 _stored_vector;
   LVecBase4 _stored_vector;
-  NodePath _stored_nodepath;
   Shader::ShaderPtrData _stored_ptr;
   Shader::ShaderPtrData _stored_ptr;
   CPT_InternalName _name;
   CPT_InternalName _name;
   PT(TypedWritableReferenceCount) _value;
   PT(TypedWritableReferenceCount) _value;

+ 1 - 0
panda/src/pgraph/shaderPool.cxx

@@ -18,6 +18,7 @@
 #include "virtualFileSystem.h"
 #include "virtualFileSystem.h"
 #include "loader.h"
 #include "loader.h"
 #include "shader.h"
 #include "shader.h"
+#include "string_utils.h"
 
 
 ShaderPool *ShaderPool::_global_ptr = (ShaderPool *)NULL;
 ShaderPool *ShaderPool::_global_ptr = (ShaderPool *)NULL;
 
 

+ 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 "config_pnmimage.cxx"
+#include "convert_srgb.cxx"
 #include "pfmFile.cxx"
 #include "pfmFile.cxx"
 #include "pnm-image-filter.cxx"
 #include "pnm-image-filter.cxx"
 #include "pnmbitio.cxx"
 #include "pnmbitio.cxx"

+ 2 - 0
panda/src/putil/Sources.pp

@@ -27,6 +27,7 @@
     cachedTypedWritableReferenceCount.h cachedTypedWritableReferenceCount.I \
     cachedTypedWritableReferenceCount.h cachedTypedWritableReferenceCount.I \
     callbackData.h callbackData.I \
     callbackData.h callbackData.I \
     callbackObject.h callbackObject.I \
     callbackObject.h callbackObject.I \
+    callbackObject_ext.h callbackObject_ext.I \
     clockObject.h clockObject.I \
     clockObject.h clockObject.I \
     collideMask.h \
     collideMask.h \
     copyOnWriteObject.h copyOnWriteObject.I \
     copyOnWriteObject.h copyOnWriteObject.I \
@@ -139,6 +140,7 @@
     cachedTypedWritableReferenceCount.h cachedTypedWritableReferenceCount.I \
     cachedTypedWritableReferenceCount.h cachedTypedWritableReferenceCount.I \
     callbackData.h callbackData.I \
     callbackData.h callbackData.I \
     callbackObject.h callbackObject.I \
     callbackObject.h callbackObject.I \
+    callbackObject_ext.h callbackObject_ext.I \
     clockObject.h clockObject.I \
     clockObject.h clockObject.I \
     collideMask.h \
     collideMask.h \
     copyOnWriteObject.h copyOnWriteObject.I \
     copyOnWriteObject.h copyOnWriteObject.I \

+ 20 - 10
panda/src/putil/bamCache.cxx

@@ -43,7 +43,7 @@ BamCache() :
   _index_stale_since(0)
   _index_stale_since(0)
 {
 {
   ConfigVariableFilename model_cache_dir
   ConfigVariableFilename model_cache_dir
-    ("model-cache-dir", Filename(), 
+    ("model-cache-dir", Filename(),
      PRC_DESC("The full path to a directory, local to this computer, in which "
      PRC_DESC("The full path to a directory, local to this computer, in which "
               "model and texture files will be cached on load.  If a directory "
               "model and texture files will be cached on load.  If a directory "
               "name is specified here, files may be loaded from the cache "
               "name is specified here, files may be loaded from the cache "
@@ -51,7 +51,7 @@ BamCache() :
               "especially if you are loading egg files instead of bam files, "
               "especially if you are loading egg files instead of bam files, "
               "or if you are loading models from a shared network drive.  "
               "or if you are loading models from a shared network drive.  "
               "If this is the empty string, no cache will be used."));
               "If this is the empty string, no cache will be used."));
-  
+
   ConfigVariableInt model_cache_flush
   ConfigVariableInt model_cache_flush
     ("model-cache-flush", 30,
     ("model-cache-flush", 30,
      PRC_DESC("This is the amount of time, in seconds, between automatic "
      PRC_DESC("This is the amount of time, in seconds, between automatic "
@@ -138,7 +138,7 @@ set_root(const Filename &root) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BamCache::lookup
 //     Function: BamCache::lookup
 //       Access: Published
 //       Access: Published
-//  Description: Looks up a file in the cache.  
+//  Description: Looks up a file in the cache.
 //
 //
 //               If the file is cacheable, then regardless of whether
 //               If the file is cacheable, then regardless of whether
 //               the file is found in the cache or not, this returns a
 //               the file is found in the cache or not, this returns a
@@ -163,7 +163,7 @@ lookup(const Filename &source_filename, const string &cache_extension) {
   consider_flush_index();
   consider_flush_index();
 
 
   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  
+
   Filename source_pathname(source_filename);
   Filename source_pathname(source_filename);
   source_pathname.make_absolute(vfs->get_cwd());
   source_pathname.make_absolute(vfs->get_cwd());
 
 
@@ -200,7 +200,7 @@ store(BamCacheRecord *record) {
   if (_read_only) {
   if (_read_only) {
     return false;
     return false;
   }
   }
-  
+
   consider_flush_index();
   consider_flush_index();
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
@@ -231,7 +231,7 @@ store(BamCacheRecord *record) {
     emergency_read_only();
     emergency_read_only();
     return false;
     return false;
   }
   }
-  
+
   if (!dout.write_header(_bam_header)) {
   if (!dout.write_header(_bam_header)) {
     util_cat.error()
     util_cat.error()
       << "Unable to write to " << temp_pathname << "\n";
       << "Unable to write to " << temp_pathname << "\n";
@@ -247,7 +247,7 @@ store(BamCacheRecord *record) {
       vfs->delete_file(temp_pathname);
       vfs->delete_file(temp_pathname);
       return false;
       return false;
     }
     }
-    
+
     TypeRegistry *type_registry = TypeRegistry::ptr();
     TypeRegistry *type_registry = TypeRegistry::ptr();
     TypeHandle texture_type = type_registry->find_type("Texture");
     TypeHandle texture_type = type_registry->find_type("Texture");
     if (record->get_data()->is_of_type(texture_type)) {
     if (record->get_data()->is_of_type(texture_type)) {
@@ -257,14 +257,14 @@ store(BamCacheRecord *record) {
       // Any other kinds of objects write texture references.
       // Any other kinds of objects write texture references.
       writer.set_file_texture_mode(BamWriter::BTM_fullpath);
       writer.set_file_texture_mode(BamWriter::BTM_fullpath);
     }
     }
-    
+
     if (!writer.write_object(record)) {
     if (!writer.write_object(record)) {
       util_cat.error()
       util_cat.error()
         << "Unable to write object to " << temp_pathname << "\n";
         << "Unable to write object to " << temp_pathname << "\n";
       vfs->delete_file(temp_pathname);
       vfs->delete_file(temp_pathname);
       return false;
       return false;
     }
     }
-    
+
     if (!writer.write_object(record->get_data())) {
     if (!writer.write_object(record->get_data())) {
       util_cat.error()
       util_cat.error()
         << "Unable to write object data to " << temp_pathname << "\n";
         << "Unable to write object data to " << temp_pathname << "\n";
@@ -354,7 +354,7 @@ flush_index() {
       emergency_read_only();
       emergency_read_only();
       return;
       return;
     }
     }
-    
+
     // Now atomically write the name of this index file to the index
     // Now atomically write the name of this index file to the index
     // reference file.
     // reference file.
     VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
     VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
@@ -385,6 +385,16 @@ flush_index() {
   check_cache_size();
   check_cache_size();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamCache::list_index
+//       Access: Published
+//  Description: Writes the contents of the index to standard output.
+////////////////////////////////////////////////////////////////////
+void BamCache::
+list_index(ostream &out, int indent_level) const {
+  _index->write(out, indent_level);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BamCache::read_index
 //     Function: BamCache::read_index
 //       Access: Private
 //       Access: Private

+ 6 - 4
panda/src/putil/bamCache.h

@@ -73,13 +73,15 @@ PUBLISHED:
   INLINE void set_read_only(bool ro);
   INLINE void set_read_only(bool ro);
   INLINE bool get_read_only() const;
   INLINE bool get_read_only() const;
 
 
-  PT(BamCacheRecord) lookup(const Filename &source_filename, 
+  PT(BamCacheRecord) lookup(const Filename &source_filename,
                             const string &cache_extension);
                             const string &cache_extension);
   bool store(BamCacheRecord *record);
   bool store(BamCacheRecord *record);
 
 
   void consider_flush_index();
   void consider_flush_index();
   void flush_index();
   void flush_index();
-  
+
+  void list_index(ostream &out, int indent_level = 0) const;
+
   INLINE static BamCache *get_global_ptr();
   INLINE static BamCache *get_global_ptr();
 
 
 private:
 private:
@@ -96,7 +98,7 @@ private:
   void check_cache_size();
   void check_cache_size();
 
 
   void emergency_read_only();
   void emergency_read_only();
-  
+
   static BamCacheIndex *do_read_index(const Filename &index_pathname);
   static BamCacheIndex *do_read_index(const Filename &index_pathname);
   static bool do_write_index(const Filename &index_pathname, const BamCacheIndex *index);
   static bool do_write_index(const Filename &index_pathname, const BamCacheIndex *index);
 
 
@@ -105,7 +107,7 @@ private:
   PT(BamCacheRecord) read_record(const Filename &source_pathname,
   PT(BamCacheRecord) read_record(const Filename &source_pathname,
                                  const Filename &cache_filename,
                                  const Filename &cache_filename,
                                  int pass);
                                  int pass);
-  static PT(BamCacheRecord) do_read_record(const Filename &cache_pathname, 
+  static PT(BamCacheRecord) do_read_record(const Filename &cache_pathname,
                                            bool read_data);
                                            bool read_data);
 
 
   static string hash_filename(const string &filename);
   static string hash_filename(const string &filename);

+ 2 - 0
panda/src/putil/callbackObject.h

@@ -38,6 +38,8 @@ public:
 PUBLISHED:
 PUBLISHED:
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
 
 
+  EXTENSION(static PT(CallbackObject) make(PyObject *function));
+
 public:
 public:
   virtual void do_callback(CallbackData *cbdata);
   virtual void do_callback(CallbackData *cbdata);
 
 

+ 32 - 0
panda/src/putil/callbackObject_ext.I

@@ -0,0 +1,32 @@
+// Filename: callbackObject_ext.I
+// Created by:  rdb (25Feb15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: Extension<CallbackObject>::make
+//       Access: Published
+//  Description: This static constructor is merely provided so that
+//               interrogate can automatically coerce Python
+//               functions when passing them to a C++ function that
+//               accepts a CallbackObject.
+////////////////////////////////////////////////////////////////////
+INLINE PT(CallbackObject) Extension<CallbackObject>::
+make(PyObject *function) {
+  if (function != Py_None && !PyCallable_Check(function)) {
+    PyErr_SetString(PyExc_TypeError, "expected callable or None");
+    return NULL;
+  } else {
+    return new PythonCallbackObject(function);
+  }
+}

+ 47 - 0
panda/src/putil/callbackObject_ext.h

@@ -0,0 +1,47 @@
+// Filename: callbackObject_ext.h
+// Created by:  rdb (25Feb15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 CALLBACKOBJECT_EXT_H
+#define CALLBACKOBJECT_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "pythonCallbackObject.h"
+#include "py_panda.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : Extension<CallbackObject>
+// Description : This class defines the extension methods for
+//               CallbackObject, which are called instead of
+//               any C++ methods with the same prototype.
+//
+//               This just defines a static constructor, which makes
+//               it possible for Interrogate to automatically accept
+//               a Python function wherever a CallbackObject is
+//               accepted.
+////////////////////////////////////////////////////////////////////
+template<>
+class Extension<CallbackObject> : public ExtensionBase<CallbackObject> {
+public:
+  INLINE static PT(CallbackObject) make(PyObject *function);
+};
+
+#include "callbackObject_ext.I"
+
+#endif  // HAVE_PYTHON
+
+#endif  // CALLBACKOBJECT_EXT_H

+ 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))
   ((((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) \
 #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) \
 #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]))
   ((((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]))
 
 

+ 270 - 42
pandatool/src/daeegg/daeCharacter.cxx

@@ -17,88 +17,316 @@
 #include "fcollada_utils.h"
 #include "fcollada_utils.h"
 #include "pt_EggVertex.h"
 #include "pt_EggVertex.h"
 #include "eggXfmSAnim.h"
 #include "eggXfmSAnim.h"
+#include "daeToEggConverter.h"
+#include "daeMaterials.h"
+
+#include "eggExternalReference.h"
 
 
 #include "FCDocument/FCDocument.h"
 #include "FCDocument/FCDocument.h"
 #include "FCDocument/FCDController.h"
 #include "FCDocument/FCDController.h"
+#include "FCDocument/FCDGeometry.h"
 #include "FCDocument/FCDSceneNodeTools.h"
 #include "FCDocument/FCDSceneNodeTools.h"
 
 
+#include "FCDocument/FCDSceneNode.h"
+#include "FCDocument/FCDTransform.h"
+#include "FCDocument/FCDAnimated.h"
+#include "FCDocument/FCDAnimationCurve.h"
+#include "FCDocument/FCDAnimationKey.h"
+
 TypeHandle DaeCharacter::_type_handle;
 TypeHandle DaeCharacter::_type_handle;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: DaeCharacter::Constructor
 //     Function: DaeCharacter::Constructor
 //       Access: Public
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 DaeCharacter::
 DaeCharacter::
-DaeCharacter(const string name, const FCDControllerInstance* controller_instance) {
-  _controller_instance = (FCDControllerInstance*) controller_instance;
-  _name = name;
-  _frame_rate = 0;
-  _skin_controller = NULL;
+DaeCharacter(EggGroup *node_group, const FCDControllerInstance *instance) :
+  _node_group(node_group),
+  _name(node_group->get_name()),
+  _instance(instance),
+  _skin_controller(NULL),
+  _skin_mesh(NULL) {
+
+  _bind_shape_mat = LMatrix4d::ident_mat();
+
   // If it's a skin controller, add the controller joints.
   // If it's a skin controller, add the controller joints.
-  FCDController* controller = (FCDController*) controller_instance->GetEntity();
-  if (controller == NULL) return;
+  const FCDController *controller = (const FCDController *)instance->GetEntity();
+  if (controller == NULL) {
+    return;
+  }
+  _skin_mesh = controller->GetBaseGeometry()->GetMesh();
+
   if (controller->IsSkin()) {
   if (controller->IsSkin()) {
     _skin_controller = controller->GetSkinController();
     _skin_controller = controller->GetSkinController();
-    if (_skin_controller == NULL) return;
-    for (size_t j = 0; j < _skin_controller->GetJointCount(); ++j) {
-      _controller_joints[FROM_FSTRING(_skin_controller->GetJoint(j)->GetId())] = (FCDSkinControllerJoint*) _skin_controller->GetJoint(j);
+    _bind_shape_mat = DAEToEggConverter::convert_matrix(_skin_controller->GetBindShapeTransform());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeCharacter::bind_joints
+//       Access: Public
+//  Description: Binds the joints to the character.  This means
+//               changing them to the bind pose.  It is necessary
+//               to call this before process_skin_geometry.
+//
+//               Returns the root group.
+////////////////////////////////////////////////////////////////////
+void DaeCharacter::
+bind_joints(JointMap &joint_map) {
+  _joints.clear();
+
+  size_t num_joints = _skin_controller->GetJointCount();
+  _joints.reserve(num_joints);
+
+  // Record the bind pose for each joint.
+  for (size_t j = 0; j < num_joints; ++j) {
+    const FCDSkinControllerJoint *skin_joint = _skin_controller->GetJoint(j);
+    string sid = FROM_FSTRING(skin_joint->GetId());
+    LMatrix4d bind_pose;
+    bind_pose.invert_from(DAEToEggConverter::convert_matrix(
+                          skin_joint->GetBindPoseInverse()));
+
+    // Check that we already encountered this joint during traversal.
+    JointMap::iterator ji = joint_map.find(sid);
+    if (ji != joint_map.end()) {
+      Joint &joint = ji->second;
+
+      if (joint._character != (DaeCharacter *)NULL) {
+        // In some cases, though, multiple controllers share the same joints.
+        // We can't support this without duplicating the joint structure,
+        // so we check if the bind poses are the same.
+        if (!joint._bind_pose.almost_equal(bind_pose, 0.0001)) {
+          // Ugh.  What else could we do?
+          daeegg_cat.error()
+            << "Multiple controllers share joint with sid " << sid
+            << ", with different bind poses.\n";
+        }
+      } else {
+        // Mark the joint as being controlled by this character.
+        joint._bind_pose = bind_pose;
+        joint._character = this;
+      }
+
+      _joints.push_back(joint);
+    } else {
+      daeegg_cat.warning()
+        << "Unknown joint sid being referenced: '" << sid << "'\n";
+
+      // We still have to add a dummy joint or the index will be off.
+      _joints.push_back(Joint(NULL, NULL));
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeCharacter::adjust_joints
+//       Access: Public
+//  Description: Traverses through the character hierarchy in order
+//               to bind the mesh to the character.  This involves
+//               reorienting the joints to match the bind pose.
+//
+//               It is important that this is called only once.
+////////////////////////////////////////////////////////////////////
+void DaeCharacter::
+adjust_joints(FCDSceneNode *node, const JointMap &joint_map,
+              const LMatrix4d &transform) {
+
+  LMatrix4d this_transform = transform;
+
+  if (node->IsJoint()) {
+    string sid = FROM_FSTRING(node->GetSubId());
+
+    JointMap::const_iterator ji = joint_map.find(sid);
+    if (ji != joint_map.end()) {
+      const Joint &joint = ji->second;
+
+      // Panda needs the joints to be in bind pose.  Not fun!  We copy the joint
+      // transform to the default pose, though, so that Panda will restore the
+      // joint transformation after binding.
+
+      if (joint._character == this) {
+        LMatrix4d bind_pose = joint._bind_pose * _bind_shape_mat *
+                        invert(transform);
+        //LMatrix4d bind_pose = joint._bind_pose * _bind_shape_mat *
+        //                joint._group->get_parent()->get_node_frame_inv();
+
+        this_transform = bind_pose * this_transform;
+        joint._group->set_default_pose(*joint._group);
+        joint._group->set_transform3d(bind_pose);
+
+        /*
+        PT(EggGroup) sphere = new EggGroup;
+        sphere->add_uniform_scale(0.1);
+        sphere->set_group_type(EggGroup::GT_instance);
+        sphere->add_child(new EggExternalReference("", "jack.egg"));
+        joint._group->add_child(sphere);
+        */
+      }
+    }
+  } else {
+    //this_transform = DAEToEggConverter::convert_matrix(node->ToMatrix());
+  }
+
+  // Loop through the children joints
+  for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
+    //if (node->GetChild(ch)->IsJoint()) {
+    adjust_joints(node->GetChild(ch), joint_map, this_transform);
+    //}
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeCharacter::influence_vertex
+//       Access: Public
+//  Description: Adds the influences for the given vertex.
+////////////////////////////////////////////////////////////////////
+void DaeCharacter::
+influence_vertex(int index, EggVertex *vertex) {
+  const FCDSkinControllerVertex *influence = _skin_controller->GetVertexInfluence(index);
+
+  for (size_t pa = 0; pa < influence->GetPairCount(); ++pa) {
+    const FCDJointWeightPair* jwpair = influence->GetPair(pa);
+
+    if (jwpair->jointIndex >= 0 && jwpair->jointIndex < _joints.size()) {
+      EggGroup *joint = _joints[jwpair->jointIndex]._group.p();
+      if (joint != NULL) {
+        joint->ref_vertex(vertex, jwpair->weight);
+      }
+    } else {
+      daeegg_cat.error()
+        << "Invalid joint index: " << jwpair->jointIndex << "\n";
     }
     }
   }
   }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: DaeCharacter::as_egg_bundle
+//     Function: DaeCharacter::collect_keys
 //       Access: Public
 //       Access: Public
-//  Description: Returns the character as a <Bundle> element,
-//               suited for the animation table.
-////////////////////////////////////////////////////////////////////
-PT(EggTable) DaeCharacter::
-as_egg_bundle() {
-  PT(EggTable) bundle = new EggTable(_name);
-  bundle->set_table_type(EggTable::TT_bundle);
-  PT(EggTable) skeleton = new EggTable("<skeleton>");
-  skeleton->set_table_type(EggTable::TT_table);
-  bundle->add_child(skeleton);
-  // Loop through the joint hierarchy
+//  Description: Collects all animation keys of animations applied
+//               to this character.
+////////////////////////////////////////////////////////////////////
+void DaeCharacter::
+collect_keys(pset<float> &keys) {
 #if FCOLLADA_VERSION < 0x00030005
 #if FCOLLADA_VERSION < 0x00030005
-  FCDSceneNodeList roots = _controller_instance->FindSkeletonNodes();
+  FCDSceneNodeList roots = _instance->FindSkeletonNodes();
 #else
 #else
   FCDSceneNodeList roots;
   FCDSceneNodeList roots;
-  _controller_instance->FindSkeletonNodes(roots);
+  _instance->FindSkeletonNodes(roots);
 #endif
 #endif
+
   for (FCDSceneNodeList::iterator it = roots.begin(); it != roots.end(); ++it) {
   for (FCDSceneNodeList::iterator it = roots.begin(); it != roots.end(); ++it) {
-    process_joint(skeleton, *it);
+    r_collect_keys(*it, keys);
   }
   }
-  return bundle;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: DaeCharacter::process_joint
+//     Function: DaeCharacter::r_collect_keys
+//       Access: Public
+//  Description: Collects all animation keys found for the given
+//               node tree.
+////////////////////////////////////////////////////////////////////
+void DaeCharacter::
+r_collect_keys(FCDSceneNode* node, pset<float> &keys) {
+  FCDAnimatedList animateds;
+
+  // Collect all the animation curves
+  for (size_t t = 0; t < node->GetTransformCount(); ++t) {
+    FCDTransform *transform = node->GetTransform(t);
+    FCDAnimated *animated = transform->GetAnimated();
+
+    if (animated != NULL) {
+      const FCDAnimationCurveListList &all_curves = animated->GetCurves();
+
+      for (size_t ci = 0; ci < all_curves.size(); ++ci) {
+        const FCDAnimationCurveTrackList &curves = all_curves[ci];
+        if (curves.empty()) {
+          continue;
+        }
+
+        size_t num_keys = curves.front()->GetKeyCount();
+        const FCDAnimationKey **curve_keys = curves.front()->GetKeys();
+
+        for (size_t c = 0; c < num_keys; ++c) {
+          keys.insert(curve_keys[c]->input);
+        }
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeCharacter::build_table
 //       Access: Public
 //       Access: Public
 //  Description: Processes a joint node and its transforms.
 //  Description: Processes a joint node and its transforms.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DaeCharacter::
 void DaeCharacter::
-process_joint(PT(EggTable) parent, FCDSceneNode* node) {
+build_table(EggTable *parent, FCDSceneNode* node, const pset<float> &keys) {
   nassertv(node != NULL);
   nassertv(node != NULL);
+
+  if (!node->IsJoint()) {
+    for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
+      build_table(parent, node->GetChild(ch), keys);
+    }
+    return;
+  }
+
   string node_id = FROM_FSTRING(node->GetDaeId());
   string node_id = FROM_FSTRING(node->GetDaeId());
-  PT(EggTable) joint = new EggTable(node_id);
-  joint->set_table_type(EggTable::TT_table);
-  parent->add_child(joint);
+  PT(EggTable) table = new EggTable(node_id);
+  table->set_table_type(EggTable::TT_table);
+  parent->add_child(table);
+
   PT(EggXfmSAnim) xform = new EggXfmSAnim("xform");
   PT(EggXfmSAnim) xform = new EggXfmSAnim("xform");
-  joint->add_child(xform);
-  xform->set_fps(_frame_rate);
+  table->add_child(xform);
+
   // Generate the sampled animation and loop through the matrices
   // Generate the sampled animation and loop through the matrices
-  FCDSceneNodeTools::GenerateSampledAnimation(node);
-  FMMatrix44List matrices = FCDSceneNodeTools::GetSampledAnimationMatrices();
-  for (FMMatrix44List::const_iterator it = matrices.begin(); it != matrices.end(); ++it) {
-    LMatrix4d matr = DAEToEggConverter::convert_matrix(*it);
-    assert(xform->add_data(matr));
+  FCDAnimatedList animateds;
+
+  // Collect all the animation curves
+  for (size_t t = 0; t < node->GetTransformCount(); ++t) {
+    FCDTransform *transform = node->GetTransform(t);
+    FCDAnimated *animated = transform->GetAnimated();
+    if (animated != (FCDAnimated *)NULL) {
+      if (animated->HasCurve()) {
+        animateds.push_back(animated);
+      }
+    }
   }
   }
+
+  // Sample the scene node transform
+  float last_key;
+  float timing_total = 0;
+  pset<float>::const_iterator ki;
+  for (ki = keys.begin(); ki != keys.end(); ++ki) {
+    for (FCDAnimatedList::iterator it = animateds.begin(); it != animateds.end(); ++it) {
+      // Sample each animated, which changes the transform values directly
+      (*it)->Evaluate(*ki);
+    }
+
+    if (ki != keys.begin()) {
+      timing_total += (*ki - last_key);
+    }
+    last_key = *ki;
+
+    // Retrieve the new transform matrix for the COLLADA scene node
+    FMMatrix44 fmat = node->ToMatrix();
+
+    // Work around issue in buggy exporters (like ColladaMax)
+    if (IS_NEARLY_ZERO(fmat[3][3])) {
+      fmat[3][3] = 1;
+    }
+
+    xform->add_data(DAEToEggConverter::convert_matrix(fmat));
+  }
+
+  // Quantize the FPS, otherwise Panda complains about FPS mismatches.
+  float fps = cfloor(((keys.size() - 1) / timing_total) * 100 + 0.5f) * 0.01f;
+  xform->set_fps(fps);
+
   // Loop through the children joints
   // Loop through the children joints
   for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
   for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
-    if (node->GetChild(ch)->IsJoint()) {
-      process_joint(joint, node->GetChild(ch));
-    }
+    //if (node->GetChild(ch)->IsJoint()) {
+      build_table(table, node->GetChild(ch), keys);
+    //}
   }
   }
 }
 }

+ 43 - 12
pandatool/src/daeegg/daeCharacter.h

@@ -12,20 +12,22 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+#ifndef DAECHARACTER_H
+#define DAECHARACTER_H
+
 #include "pandatoolbase.h"
 #include "pandatoolbase.h"
 #include "typedReferenceCount.h"
 #include "typedReferenceCount.h"
 #include "typeHandle.h"
 #include "typeHandle.h"
 #include "eggTable.h"
 #include "eggTable.h"
-#include "daeToEggConverter.h"
 
 
 #include "pre_fcollada_include.h"
 #include "pre_fcollada_include.h"
 #include "FCollada.h"
 #include "FCollada.h"
 #include "FCDocument/FCDSceneNode.h"
 #include "FCDocument/FCDSceneNode.h"
 #include "FCDocument/FCDControllerInstance.h"
 #include "FCDocument/FCDControllerInstance.h"
 #include "FCDocument/FCDSkinController.h"
 #include "FCDocument/FCDSkinController.h"
+#include "FCDocument/FCDGeometryMesh.h"
 
 
-#ifndef DAECHARACTER_H
-#define DAECHARACTER_H
+class DAEToEggConverter;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : DaeCharacter
 //       Class : DaeCharacter
@@ -33,17 +35,46 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class DaeCharacter : public TypedReferenceCount {
 class DaeCharacter : public TypedReferenceCount {
 public:
 public:
-  DaeCharacter(const string name, const FCDControllerInstance* controller_instance);
-  PT(EggTable) as_egg_bundle();
-  void process_joint(PT(EggTable) parent, FCDSceneNode* node);
-  
+  DaeCharacter(EggGroup *node_group, const FCDControllerInstance* controller_instance);
+
+  struct Joint {
+    INLINE Joint(EggGroup *group, const FCDSceneNode *scene_node) :
+      _group(group),
+      _scene_node(scene_node),
+      _character(NULL),
+      _bind_pose(LMatrix4d::ident_mat()) {}
+
+    LMatrix4d _bind_pose;
+    PT(EggGroup) _group;
+    const FCDSceneNode *_scene_node;
+    DaeCharacter *_character;
+  };
+  typedef pvector<Joint> Joints;
+  typedef pmap<string, Joint> JointMap;
+
+  void bind_joints(JointMap &joint_map);
+  void adjust_joints(FCDSceneNode *node, const JointMap &joint_map,
+                     const LMatrix4d &transform = LMatrix4d::ident_mat());
+
+  void influence_vertex(int index, EggVertex *vertex);
+
+  void collect_keys(pset<float> &keys);
+  void r_collect_keys(FCDSceneNode *node, pset<float> &keys);
+
+  void build_table(EggTable *parent, FCDSceneNode* node, const pset<float> &keys);
+
+public:
+  PT(EggGroup) _node_group;
+  const FCDGeometryMesh *_skin_mesh;
+  const FCDControllerInstance *_instance;
+  LMatrix4d _bind_shape_mat;
+
 private:
 private:
-  int _frame_rate;
   string _name;
   string _name;
-  FCDControllerInstance* _controller_instance;
-  FCDSkinController* _skin_controller;
-  pmap<string, FCDSkinControllerJoint*> _controller_joints;
-  
+  const FCDSkinController *_skin_controller;
+  Joints _joints;
+  JointMap _bound_joints;
+
 public:
 public:
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {
     return get_class_type();
     return get_class_type();

+ 11 - 10
pandatool/src/daeegg/daeMaterials.cxx

@@ -35,7 +35,7 @@ TypeHandle DaeMaterials::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: DaeMaterials::Constructor
 //     Function: DaeMaterials::Constructor
 //       Access: Public
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 DaeMaterials::
 DaeMaterials::
 DaeMaterials(const FCDGeometryInstance* geometry_instance) {
 DaeMaterials(const FCDGeometryInstance* geometry_instance) {
@@ -58,7 +58,7 @@ void DaeMaterials::add_material_instance(const FCDMaterialInstance* instance) {
     return;
     return;
   }
   }
   _materials[semantic] = new DaeMaterial();
   _materials[semantic] = new DaeMaterial();
-  
+
   // Load in the uvsets
   // Load in the uvsets
   for (size_t vib = 0; vib < instance->GetVertexInputBindingCount(); ++vib) {
   for (size_t vib = 0; vib < instance->GetVertexInputBindingCount(); ++vib) {
     const FCDMaterialInstanceBindVertexInput* mivib = instance->GetVertexInputBinding(vib);
     const FCDMaterialInstanceBindVertexInput* mivib = instance->GetVertexInputBinding(vib);
@@ -74,7 +74,7 @@ void DaeMaterials::add_material_instance(const FCDMaterialInstance* instance) {
 #endif
 #endif
     _materials[semantic]->_uvsets.push_back(bvi);
     _materials[semantic]->_uvsets.push_back(bvi);
   }
   }
-  
+
   // Handle the material stuff
   // Handle the material stuff
   daeegg_cat.spam() << "Trying to process material with semantic " << semantic << endl;
   daeegg_cat.spam() << "Trying to process material with semantic " << semantic << endl;
   PT_EggMaterial egg_material = new EggMaterial(semantic);
   PT_EggMaterial egg_material = new EggMaterial(semantic);
@@ -204,12 +204,13 @@ process_extra(const string semantic, const FCDExtra* extra) {
   for (size_t et = 0; et < etype->GetTechniqueCount(); ++et) {
   for (size_t et = 0; et < etype->GetTechniqueCount(); ++et) {
     const FCDENode* enode = ((const FCDENode*)(etype->GetTechnique(et)))->FindChildNode("double_sided");
     const FCDENode* enode = ((const FCDENode*)(etype->GetTechnique(et)))->FindChildNode("double_sided");
     if (enode != NULL) {
     if (enode != NULL) {
-      if (trim(enode->GetContent()) == "1") {
+      string content = trim(enode->GetContent());
+      if (content == "1" || content == "true") {
         _materials[semantic]->_double_sided = true;
         _materials[semantic]->_double_sided = true;
-      } else if (trim(enode->GetContent()) == "0") {
+      } else if (content == "0" || content == "false") {
         _materials[semantic]->_double_sided = false;
         _materials[semantic]->_double_sided = false;
       } else {
       } else {
-        daeegg_cat.warning() << "Expected <double_sided> tag to be either 1 or 0, found '" << enode->GetContent() << "' instead" << endl;
+        daeegg_cat.warning() << "Expected <double_sided> tag to be either 1 or 0, found '" << content << "' instead" << endl;
       }
       }
     }
     }
   }
   }
@@ -391,7 +392,7 @@ convert_blend(FCDEffectStandard::TransparencyMode mode, const LColor &transparen
   blend->_color = LColor::zero();
   blend->_color = LColor::zero();
   blend->_operand_a = EggGroup::BO_unspecified;
   blend->_operand_a = EggGroup::BO_unspecified;
   blend->_operand_b = EggGroup::BO_unspecified;
   blend->_operand_b = EggGroup::BO_unspecified;
-  
+
   // First fill in the color value.
   // First fill in the color value.
   if (mode == FCDEffectStandard::A_ONE) {// || mode == FCDEffectStandard::A_ZERO) {
   if (mode == FCDEffectStandard::A_ONE) {// || mode == FCDEffectStandard::A_ZERO) {
     double value = transparent[3] * transparency;
     double value = transparent[3] * transparency;
@@ -404,7 +405,7 @@ convert_blend(FCDEffectStandard::TransparencyMode mode, const LColor &transparen
     blend->_enabled = false;
     blend->_enabled = false;
     return blend;
     return blend;
   }
   }
-  
+
   // Now figure out the operands.
   // Now figure out the operands.
   if (mode == FCDEffectStandard::RGB_ZERO) {// || mode == FCDEffectStandard::A_ZERO) {
   if (mode == FCDEffectStandard::RGB_ZERO) {// || mode == FCDEffectStandard::A_ZERO) {
     blend->_operand_a = EggGroup::BO_one_minus_constant_color;
     blend->_operand_a = EggGroup::BO_one_minus_constant_color;
@@ -417,7 +418,7 @@ convert_blend(FCDEffectStandard::TransparencyMode mode, const LColor &transparen
     blend->_enabled = false;
     blend->_enabled = false;
     return blend;
     return blend;
   }
   }
-  
+
   // See if we can optimize out the color.
   // See if we can optimize out the color.
   if (blend->_operand_a == EggGroup::BO_constant_color) {
   if (blend->_operand_a == EggGroup::BO_constant_color) {
     if (blend->_color == LColor::zero()) {
     if (blend->_color == LColor::zero()) {
@@ -447,7 +448,7 @@ convert_blend(FCDEffectStandard::TransparencyMode mode, const LColor &transparen
       blend->_operand_b = EggGroup::BO_zero;
       blend->_operand_b = EggGroup::BO_zero;
     }
     }
   }
   }
-  
+
   // See if we can entirely disable the blend.
   // See if we can entirely disable the blend.
   if (blend->_operand_a == EggGroup::BO_one && blend->_operand_b == EggGroup::BO_zero) {
   if (blend->_operand_a == EggGroup::BO_one && blend->_operand_b == EggGroup::BO_zero) {
     blend->_enabled = false;
     blend->_enabled = false;

+ 396 - 221
pandatool/src/daeegg/daeToEggConverter.cxx

@@ -56,9 +56,10 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 DAEToEggConverter::
 DAEToEggConverter::
 DAEToEggConverter() {
 DAEToEggConverter() {
+  _unit_name = "meter";
+  _unit_meters = 1.0;
   _document = NULL;
   _document = NULL;
   _table = NULL;
   _table = NULL;
-  _frame_rate = -1;
   _error_handler = NULL;
   _error_handler = NULL;
   _invert_transparency = false;
   _invert_transparency = false;
 }
 }
@@ -131,18 +132,15 @@ convert_file(const Filename &filename) {
   // Reset stuff
   // Reset stuff
   clear_error();
   clear_error();
   _joints.clear();
   _joints.clear();
-  _vertex_pools.clear();
-  _skeletons.clear();
-  _frame_rate = -1;
   if (_error_handler == NULL) {
   if (_error_handler == NULL) {
     _error_handler = new FUErrorSimpleHandler;
     _error_handler = new FUErrorSimpleHandler;
   }
   }
-  
+
   // The default coordinate system is Y-up
   // The default coordinate system is Y-up
   if (_egg_data->get_coordinate_system() == CS_default) {
   if (_egg_data->get_coordinate_system() == CS_default) {
     _egg_data->set_coordinate_system(CS_yup_right);
     _egg_data->set_coordinate_system(CS_yup_right);
   }
   }
-  
+
   // Read the file
   // Read the file
   FCollada::Initialize();
   FCollada::Initialize();
   _document = FCollada::LoadDocument(filename.to_os_specific().c_str());
   _document = FCollada::LoadDocument(filename.to_os_specific().c_str());
@@ -155,201 +153,375 @@ convert_file(const Filename &filename) {
   if (_document->GetAsset() != NULL) {
   if (_document->GetAsset() != NULL) {
     FCDocumentTools::StandardizeUpAxisAndLength(_document);
     FCDocumentTools::StandardizeUpAxisAndLength(_document);
   }
   }
-  
-  _table = new EggTable();
-  _table->set_table_type(EggTable::TT_table);
-  // Process the stuff
+
+  // Process the scene
   process_asset();
   process_asset();
-  preprocess();
+  PT(EggGroup) scene_group;
+  string model_name = _character_name;
+
   FCDSceneNode* visual_scene = _document->GetVisualSceneInstance();
   FCDSceneNode* visual_scene = _document->GetVisualSceneInstance();
   if (visual_scene != NULL) {
   if (visual_scene != NULL) {
-    // First check for an <extra> tag
-    const FCDExtra* extra = visual_scene->GetExtra();
-    //FIXME: eek this looks horrid
-    if (extra != NULL) {
-      const FCDEType* etype = extra->GetDefaultType();
-      if (etype != NULL) {
-        const FCDENode* enode = (const FCDENode*) etype->FindTechnique("MAX3D");
-        if (enode != NULL) {
-          enode = enode->FindChildNode("frame_rate");
-          if (enode != NULL && !string_to_int(enode->GetContent(), _frame_rate)) {
-            daeegg_cat.warning() << "Invalid integer in <frame_rate> tag: '" << enode->GetContent() << "'" << endl;
-    } } } }
-    // Now loop through the children
+    if (model_name.empty()) {
+      // By lack of anything better...
+      model_name = FROM_FSTRING(visual_scene->GetName());
+    }
+    scene_group = new EggGroup(model_name);
+    _egg_data->add_child(scene_group);
+
     for (size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
     for (size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
-      process_node(DCAST(EggGroupNode, _egg_data), visual_scene->GetChild(ch));
+      process_node(scene_group, visual_scene->GetChild(ch));
     }
     }
+  } else {
+    daeegg_cat.warning()
+      << "No visual scene instance found in COLLADA document.\n";
   }
   }
-  SAFE_DELETE(visual_scene);
-  
-  _egg_data->add_child(_table);
-  
+
+  // Now process the characters.  This depends on information from collected
+  // joints, which is why it's done in a second step.
+  if (get_animation_convert() != AC_none) {
+    Characters::iterator it;
+    DaeCharacter *character;
+    for (it = _characters.begin(); it != _characters.end(); ++it) {
+      character = *it;
+      if (get_animation_convert() != AC_chan) {
+        character->bind_joints(_joints);
+
+        const FCDGeometryMesh *mesh = character->_skin_mesh;
+
+        if (mesh != NULL) {
+          PT(DaeMaterials) materials = new DaeMaterials(character->_instance);
+          daeegg_cat.spam() << "Processing mesh for controller\n";
+          process_mesh(character->_node_group, mesh, materials, character);
+        }
+      }
+    }
+
+    // Put the joints in bind pose.
+    for (size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
+      character->adjust_joints(visual_scene->GetChild(ch), _joints, LMatrix4d::ident_mat());
+    }
+
+    if (scene_group != NULL) {
+      // Mark the scene as character.
+      if (get_animation_convert() == AC_chan) {
+        _egg_data->remove_child(scene_group);
+      } else {
+        scene_group->set_dart_type(EggGroup::DT_default);
+      }
+    }
+
+    if (get_animation_convert() != AC_model) {
+      _table = new EggTable();
+      _table->set_table_type(EggTable::TT_table);
+      _egg_data->add_child(_table);
+
+      PT(EggTable) bundle = new EggTable(model_name);
+      bundle->set_table_type(EggTable::TT_bundle);
+      _table->add_child(bundle);
+
+      PT(EggTable) skeleton = new EggTable("<skeleton>");
+      skeleton->set_table_type(EggTable::TT_table);
+      bundle->add_child(skeleton);
+
+      pset<float> keys;
+
+      Characters::iterator it;
+      DaeCharacter *character;
+      for (it = _characters.begin(); it != _characters.end(); ++it) {
+        character = *it;
+
+        // Collect key frame timings.
+        if (get_animation_convert() == AC_both ||
+            get_animation_convert() == AC_chan) {
+          character->collect_keys(keys);
+        }
+      }
+
+      if (_frame_inc != 0.0) {
+        // A frame increment was given, this means that we have to sample the
+        // animation.
+        float start, end;
+        if (_end_frame != _start_frame) {
+          start = _start_frame;
+          end = _end_frame;
+        } else {
+          // No range was given.  Infer the frame range from the keys.
+          start = *keys.begin();
+          end = *keys.rbegin();
+        }
+        keys.clear();
+
+        for (float t = start; t <= end; t += _frame_inc) {
+          keys.insert(t);
+        }
+      } else {
+        // No sampling parameters given; not necessarily a failure, since the
+        // animation may already be sampled.  We use the key frames as animation
+        // frames.
+        if (_end_frame != 0.0) {
+          // An end frame was given, chop off all keys after that.
+          float end = _end_frame;
+          pset<float>::iterator ki;
+          for (ki = keys.begin(); ki != keys.end(); ++ki) {
+            if (*ki > end && !IS_THRESHOLD_EQUAL(*ki, end, 0.001)) {
+              keys.erase(ki, keys.end());
+              break;
+            }
+          }
+        }
+        if (_start_frame != 0.0) {
+          // A start frame was given, chop off all keys before that.
+          float start = _start_frame;
+          pset<float>::iterator ki;
+          for (ki = keys.begin(); ki != keys.end(); ++ki) {
+            if (*ki > start && !IS_THRESHOLD_EQUAL(*ki, start, 0.001)) {
+              keys.erase(keys.begin(), ki);
+              break;
+            }
+          }
+        }
+
+        // Check that this does indeed look like a sampled animation; if not,
+        // issue an appropriate warning.
+        pset<float>::const_iterator ki = keys.begin();
+        if (ki != keys.end()) {
+          float last = *ki;
+          float diff = 0;
+
+          for (++ki; ki != keys.end(); ++ki) {
+            if (diff != 0 && !IS_THRESHOLD_EQUAL((*ki - last), diff, 0.001)) {
+              daeegg_cat.error()
+                << "This does not appear to be a sampled animation.\n"
+                << "Specify the -sf, -ef and -if options to indicate how the "
+                << "animations should be sampled.\n";
+              break;
+            }
+            diff = (*ki - last);
+            last = *ki;
+          }
+        }
+      }
+
+      // It doesn't really matter which character we grab for this as
+      // it'll iterate over the whole graph right now anyway.
+      for (size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
+        character->build_table(skeleton, visual_scene->GetChild(ch), keys);
+      }
+    }
+  }
+
   // Clean up and return
   // Clean up and return
+  SAFE_DELETE(visual_scene);
   SAFE_DELETE(_document);
   SAFE_DELETE(_document);
   FCollada::Release();
   FCollada::Release();
   return true;
   return true;
 }
 }
 
 
-void DAEToEggConverter::process_asset() {
-  if (_document->GetAsset() == NULL) return;
+////////////////////////////////////////////////////////////////////
+//     Function: DAEToEggConverter::get_input_units
+//       Access: Public, Virtual
+//  Description: This may be called after convert_file() has been
+//               called and returned true, indicating a successful
+//               conversion.  It will return the distance units
+//               represented by the converted egg file, if known, or
+//               DU_invalid if not known.
+////////////////////////////////////////////////////////////////////
+DistanceUnit DAEToEggConverter::
+get_input_units() {
+  if (IS_NEARLY_EQUAL(_unit_meters, 0.001)) {
+    return DU_millimeters;
+  }
+  if (IS_NEARLY_EQUAL(_unit_meters, 0.01)) {
+    return DU_centimeters;
+  }
+  if (IS_NEARLY_EQUAL(_unit_meters, 1.0)) {
+    return DU_meters;
+  }
+  if (IS_NEARLY_EQUAL(_unit_meters, 1000.0)) {
+    return DU_kilometers;
+  }
+  if (IS_NEARLY_EQUAL(_unit_meters, 3.0 * 12.0 * 0.0254)) {
+    return DU_yards;
+  }
+  if (IS_NEARLY_EQUAL(_unit_meters, 12.0 * 0.0254)) {
+    return DU_feet;
+  }
+  if (IS_NEARLY_EQUAL(_unit_meters, 0.0254)) {
+    return DU_inches;
+  }
+  if (IS_NEARLY_EQUAL(_unit_meters, 1852.0)) {
+    return DU_nautical_miles;
+  }
+  if (IS_NEARLY_EQUAL(_unit_meters, 5280.0 * 12.0 * 0.0254)) {
+    return DU_statute_miles;
+  }
+
+  // Whatever.
+  return DU_invalid;
+}
+
+void DAEToEggConverter::
+process_asset() {
+  const FCDAsset *asset = _document->GetAsset();
+  if (_document->GetAsset() == NULL) {
+    return;
+  }
+
+  _unit_name = FROM_FSTRING(asset->GetUnitName());
+  _unit_meters = asset->GetUnitConversionFactor();
+
   // Read out the coordinate system
   // Read out the coordinate system
-  FMVector3 up_axis (_document->GetAsset()->GetUpAxis());
+  FMVector3 up_axis = asset->GetUpAxis();
+
   if (up_axis == FMVector3(0, 1, 0)) {
   if (up_axis == FMVector3(0, 1, 0)) {
     _egg_data->set_coordinate_system(CS_yup_right);
     _egg_data->set_coordinate_system(CS_yup_right);
+
   } else if (up_axis == FMVector3(0, 0, 1)) {
   } else if (up_axis == FMVector3(0, 0, 1)) {
     _egg_data->set_coordinate_system(CS_zup_right);
     _egg_data->set_coordinate_system(CS_zup_right);
+
   } else {
   } else {
     _egg_data->set_coordinate_system(CS_invalid);
     _egg_data->set_coordinate_system(CS_invalid);
     daeegg_cat.warning() << "Unrecognized coordinate system!\n";
     daeegg_cat.warning() << "Unrecognized coordinate system!\n";
   }
   }
 }
 }
 
 
-// This function lists all the joints and referenced skeletons
-void DAEToEggConverter::preprocess(const FCDSceneNode* node) {
-  // If the node is NULL, take the visual scene instance.
-  if (node == NULL) {
-    assert(_document != NULL);
-    _skeletons.clear();
-    _joints.clear();
-    node = _document->GetVisualSceneInstance();
-  }
-  if (node == NULL) return;
-  if (node->IsJoint()) {
-    _joints[FROM_FSTRING(node->GetDaeId())] = NULL;
-  }
-  // Loop through the instances first.
-  for (size_t in = 0; in < node->GetInstanceCount(); ++in) {
-    if (node->GetInstance(in)->GetType() == FCDEntityInstance::CONTROLLER) {
-      // Loop through the skeleton roots now.
-#if FCOLLADA_VERSION < 0x00030005
-      FCDSceneNodeList roots = ((FCDControllerInstance*) node->GetInstance(in))->FindSkeletonNodes();
-#else
-      FCDSceneNodeList roots;
-      ((FCDControllerInstance*) node->GetInstance(in))->FindSkeletonNodes(roots);
-#endif
-      for (FCDSceneNodeList::iterator it = roots.begin(); it != roots.end(); ++it) {
-        daeegg_cat.spam() << "Found referenced skeleton root " << FROM_FSTRING((*it)->GetDaeId()) << endl;
-        _skeletons.push_back(FROM_FSTRING((*it)->GetDaeId()));
-      }
-    }
-  }
-  // Now loop through the children and recurse.
-  for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
-    preprocess(node->GetChild(ch));
-  }
-}
-
 // Process the node. If forced is true, it will even process it if its known to be a skeleton root.
 // Process the node. If forced is true, it will even process it if its known to be a skeleton root.
-void DAEToEggConverter::process_node(PT(EggGroupNode) parent, const FCDSceneNode* node, bool forced) {
+void DAEToEggConverter::
+process_node(EggGroupNode *parent, const FCDSceneNode* node, bool forced) {
   nassertv(node != NULL);
   nassertv(node != NULL);
   string node_id = FROM_FSTRING(node->GetDaeId());
   string node_id = FROM_FSTRING(node->GetDaeId());
   daeegg_cat.spam() << "Processing node with ID '" << node_id << "'" << endl;
   daeegg_cat.spam() << "Processing node with ID '" << node_id << "'" << endl;
-  // Important! If it's known to be a skeleton root, ignore it for now, unless we're processing forced.
-  if (!forced && count(_skeletons.begin(), _skeletons.end(), node_id) > 0) {
-    daeegg_cat.spam() << "Ignoring skeleton root node with ID '" << node_id << "', we'll process it later" << endl;
-    return;
-  }
+
   // Create an egg group for this node
   // Create an egg group for this node
-  PT(EggGroup) node_group = new EggGroup(FROM_FSTRING(node->GetName()));
+  PT(EggGroup) node_group = new EggGroup(FROM_FSTRING(node->GetDaeId()));
   process_extra(node_group, node->GetExtra());
   process_extra(node_group, node->GetExtra());
   parent->add_child(node_group);
   parent->add_child(node_group);
+
   // Check if its a joint
   // Check if its a joint
   if (node->IsJoint()) {
   if (node->IsJoint()) {
+    string sid = FROM_FSTRING(node->GetSubId());
     node_group->set_group_type(EggGroup::GT_joint);
     node_group->set_group_type(EggGroup::GT_joint);
-    _joints[node_id] = node_group;
+
+    if (!_joints.insert(DaeCharacter::JointMap::value_type(sid,
+                        DaeCharacter::Joint(node_group, node))).second) {
+      daeegg_cat.error()
+        << "Joint with sid " << sid << " occurs more than once!\n";
+    }
   }
   }
-  // Loop through the transforms and apply them
-  for (size_t tr = 0; tr < node->GetTransformCount(); ++tr) {
-    apply_transform(node_group, node->GetTransform(tr));
+
+  // Loop through the transforms and apply them (in reverse order)
+  for (size_t tr = node->GetTransformCount(); tr > 0; --tr) {
+    apply_transform(node_group, node->GetTransform(tr - 1));
   }
   }
+  //node_group->set_transform3d(convert_matrix(node->ToMatrix()));
+
   // Loop through the instances and process them
   // Loop through the instances and process them
   for (size_t in = 0; in < node->GetInstanceCount(); ++in) {
   for (size_t in = 0; in < node->GetInstanceCount(); ++in) {
     process_instance(node_group, node->GetInstance(in));
     process_instance(node_group, node->GetInstance(in));
   }
   }
+
   // Loop through the children and recursively process them
   // Loop through the children and recursively process them
   for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
   for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
     process_node(DCAST(EggGroupNode, node_group), node->GetChild(ch));
     process_node(DCAST(EggGroupNode, node_group), node->GetChild(ch));
   }
   }
+
   // Loop through any possible scene node instances and process those, too.
   // Loop through any possible scene node instances and process those, too.
   for (size_t in = 0; in < node->GetInstanceCount(); ++in) {
   for (size_t in = 0; in < node->GetInstanceCount(); ++in) {
-    if (node->GetInstance(in)->GetEntity() && node->GetInstance(in)->GetEntity()->GetType() == FCDEntity::SCENE_NODE) {
-      process_node(DCAST(EggGroupNode, node_group), (const FCDSceneNode*) node->GetInstance(in)->GetEntity());
+    const FCDEntity *entity = node->GetInstance(in)->GetEntity();
+    if (entity && entity->GetType() == FCDEntity::SCENE_NODE) {
+      process_node(node_group, (const FCDSceneNode*) entity);
     }
     }
   }
   }
 }
 }
 
 
-void DAEToEggConverter::process_instance(PT(EggGroup) parent, const FCDEntityInstance* instance) {
+void DAEToEggConverter::
+process_instance(EggGroup *parent, const FCDEntityInstance* instance) {
   nassertv(instance != NULL);
   nassertv(instance != NULL);
   nassertv(instance->GetEntity() != NULL);
   nassertv(instance->GetEntity() != NULL);
   // Check what kind of instance this is
   // Check what kind of instance this is
   switch (instance->GetType()) {
   switch (instance->GetType()) {
-    case FCDEntityInstance::GEOMETRY: {
-      const FCDGeometry* geometry = (const FCDGeometry*) instance->GetEntity();
-      assert(geometry != NULL);
-      if (geometry->IsMesh()) {
-        // Now, handle the mesh.
-        process_mesh(parent, geometry->GetMesh(), new DaeMaterials((const FCDGeometryInstance*) instance));
-      }
-      if (geometry->IsSpline()) {
-        process_spline(parent, FROM_FSTRING(geometry->GetName()), const_cast<FCDGeometrySpline*> (geometry->GetSpline()));
+  case FCDEntityInstance::GEOMETRY:
+    {
+      if (get_animation_convert() != AC_chan) {
+        const FCDGeometry* geometry = (const FCDGeometry*) instance->GetEntity();
+        assert(geometry != NULL);
+        if (geometry->IsMesh()) {
+          // Now, handle the mesh.
+          process_mesh(parent, geometry->GetMesh(), new DaeMaterials((const FCDGeometryInstance*) instance));
+        }
+        if (geometry->IsSpline()) {
+          process_spline(parent, FROM_FSTRING(geometry->GetName()), const_cast<FCDGeometrySpline*> (geometry->GetSpline()));
+        }
       }
       }
-      break; }
-    case FCDEntityInstance::CONTROLLER: {
-      // Add the dart tag and process the controller instance
-      parent->set_dart_type(EggGroup::DT_default);
-      process_controller(parent, (const FCDControllerInstance*) instance);
-      break; }
-    case FCDEntityInstance::MATERIAL:
-      // We don't process this directly, handled per-geometry instead.
-      break;
-    case FCDEntityInstance::SIMPLE: {
-      // Grab the entity and check it's type.
+    }
+    break;
+
+  case FCDEntityInstance::CONTROLLER:
+    // Add the dart tag and process the controller instance
+    //parent->set_dart_type(EggGroup::DT_default);
+    process_controller(parent, (const FCDControllerInstance*) instance);
+    break;
+
+  case FCDEntityInstance::MATERIAL:
+    // We don't process this directly, handled per-geometry instead.
+    break;
+
+  case FCDEntityInstance::SIMPLE:
+    {
+      // Grab the entity and check its type.
       const FCDEntity* entity = instance->GetEntity();
       const FCDEntity* entity = instance->GetEntity();
       if (entity->GetType() != FCDEntity::SCENE_NODE) {
       if (entity->GetType() != FCDEntity::SCENE_NODE) {
         daeegg_cat.warning() << "Unsupported entity type found" << endl;
         daeegg_cat.warning() << "Unsupported entity type found" << endl;
       }
       }
-      break; }
-    default:
-      daeegg_cat.warning() << "Unsupported instance type found" << endl;
+    }
+    break;
+
+  default:
+    daeegg_cat.warning() << "Unsupported instance type found" << endl;
   }
   }
 }
 }
 
 
 // Processes the given mesh.
 // Processes the given mesh.
-void DAEToEggConverter::process_mesh(PT(EggGroup) parent, const FCDGeometryMesh* mesh, PT(DaeMaterials) materials) {
+void DAEToEggConverter::
+process_mesh(EggGroup *parent, const FCDGeometryMesh* mesh,
+             DaeMaterials *materials, DaeCharacter *character) {
+
   nassertv(mesh != NULL);
   nassertv(mesh != NULL);
   daeegg_cat.debug() << "Processing mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << endl;
   daeegg_cat.debug() << "Processing mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << endl;
-  
+
   // Create the egg stuff to hold this mesh
   // Create the egg stuff to hold this mesh
   PT(EggGroup) mesh_group = new EggGroup(FROM_FSTRING(mesh->GetDaeId()));
   PT(EggGroup) mesh_group = new EggGroup(FROM_FSTRING(mesh->GetDaeId()));
   parent->add_child(mesh_group);
   parent->add_child(mesh_group);
   PT(EggVertexPool) mesh_pool = new EggVertexPool(FROM_FSTRING(mesh->GetDaeId()));
   PT(EggVertexPool) mesh_pool = new EggVertexPool(FROM_FSTRING(mesh->GetDaeId()));
   mesh_group->add_child(mesh_pool);
   mesh_group->add_child(mesh_pool);
-  _vertex_pools[FROM_FSTRING(mesh->GetDaeId())] = mesh_pool;
-  
+
   // First retrieve the vertex source
   // First retrieve the vertex source
   if (mesh->GetSourceCount() == 0) {
   if (mesh->GetSourceCount() == 0) {
     daeegg_cat.debug() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has no sources" << endl;
     daeegg_cat.debug() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has no sources" << endl;
     return;
     return;
   }
   }
-  const FCDGeometrySource* vsource = mesh->FindSourceByType(FUDaeGeometryInput::POSITION);  
+  const FCDGeometrySource* vsource = mesh->FindSourceByType(FUDaeGeometryInput::POSITION);
   if (vsource == NULL) {
   if (vsource == NULL) {
     daeegg_cat.debug() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has no source for POSITION data" << endl;
     daeegg_cat.debug() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has no source for POSITION data" << endl;
     return;
     return;
   }
   }
-  
+
   // Loop through the polygon groups and add them
   // Loop through the polygon groups and add them
   daeegg_cat.spam() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has " << mesh->GetPolygonsCount() << " polygon groups" << endl;
   daeegg_cat.spam() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has " << mesh->GetPolygonsCount() << " polygon groups" << endl;
   if (mesh->GetPolygonsCount() == 0) return;
   if (mesh->GetPolygonsCount() == 0) return;
-  
+
   // This is an array of pointers, I know. But since they are refcounted, I don't have a better idea.
   // This is an array of pointers, I know. But since they are refcounted, I don't have a better idea.
   PT(EggGroup) *primitive_holders = new PT(EggGroup) [mesh->GetPolygonsCount()];
   PT(EggGroup) *primitive_holders = new PT(EggGroup) [mesh->GetPolygonsCount()];
   for (size_t gr = 0; gr < mesh->GetPolygonsCount(); ++gr) {
   for (size_t gr = 0; gr < mesh->GetPolygonsCount(); ++gr) {
     const FCDGeometryPolygons* polygons = mesh->GetPolygons(gr);
     const FCDGeometryPolygons* polygons = mesh->GetPolygons(gr);
+    string material_semantic = FROM_FSTRING(polygons->GetMaterialSemantic());
+
     // Stores which group holds the primitives.
     // Stores which group holds the primitives.
     PT(EggGroup) primitiveholder;
     PT(EggGroup) primitiveholder;
     // If we have materials, make a group for each material. Then, apply the material's per-group stuff.
     // If we have materials, make a group for each material. Then, apply the material's per-group stuff.
     if (materials != NULL && (!polygons->GetMaterialSemantic().empty()) && mesh->GetPolygonsCount() > 1) {
     if (materials != NULL && (!polygons->GetMaterialSemantic().empty()) && mesh->GetPolygonsCount() > 1) {
-      primitiveholder = new EggGroup(FROM_FSTRING(mesh->GetDaeId()) + "." + FROM_FSTRING(polygons->GetMaterialSemantic()));
+      //primitiveholder = new EggGroup(FROM_FSTRING(mesh->GetDaeId()) + "." + material_semantic);
+      primitiveholder = new EggGroup;
       mesh_group->add_child(primitiveholder);
       mesh_group->add_child(primitiveholder);
     } else {
     } else {
       primitiveholder = mesh_group;
       primitiveholder = mesh_group;
@@ -357,7 +529,7 @@ void DAEToEggConverter::process_mesh(PT(EggGroup) parent, const FCDGeometryMesh*
     primitive_holders[gr] = primitiveholder;
     primitive_holders[gr] = primitiveholder;
     // Apply the per-group data of the materials, if we have it.
     // Apply the per-group data of the materials, if we have it.
     if (materials != NULL) {
     if (materials != NULL) {
-      materials->apply_to_group(FROM_FSTRING(polygons->GetMaterialSemantic()), primitiveholder, _invert_transparency);
+      materials->apply_to_group(material_semantic, primitiveholder, _invert_transparency);
     }
     }
     // Find the position sources
     // Find the position sources
     const FCDGeometryPolygonsInput* pinput = polygons->FindInput(FUDaeGeometryInput::POSITION);
     const FCDGeometryPolygonsInput* pinput = polygons->FindInput(FUDaeGeometryInput::POSITION);
@@ -389,26 +561,47 @@ void DAEToEggConverter::process_mesh(PT(EggGroup) parent, const FCDGeometryMesh*
     const uint32* tindices;
     const uint32* tindices;
     if (tinput != NULL) tindices = tinput->GetIndices();
     if (tinput != NULL) tindices = tinput->GetIndices();
     // Get a name for potential coordinate sets
     // Get a name for potential coordinate sets
-    string tcsetname ("");
+    string tcsetname;
     if (materials != NULL && tcinput != NULL) {
     if (materials != NULL && tcinput != NULL) {
-      daeegg_cat.debug() << "Assigning texcoord set " << tcinput->GetSet() << " to semantic '" << FROM_FSTRING(polygons->GetMaterialSemantic()) << "'\n";
-      tcsetname = materials->get_uvset_name(FROM_FSTRING(polygons->GetMaterialSemantic()), FUDaeGeometryInput::TEXCOORD, tcinput->GetSet());
+      if (daeegg_cat.is_debug()) {
+        daeegg_cat.debug()
+          << "Assigning texcoord set " << tcinput->GetSet()
+          << " to semantic '" << material_semantic << "'\n";
+      }
+      tcsetname = materials->get_uvset_name(material_semantic,
+                    FUDaeGeometryInput::TEXCOORD, tcinput->GetSet());
     }
     }
-    string tbsetname ("");
+    string tbsetname;
     if (materials != NULL && binput != NULL) {
     if (materials != NULL && binput != NULL) {
-      daeegg_cat.debug() << "Assigning texbinormal set " << binput->GetSet() << " to semantic '" << FROM_FSTRING(polygons->GetMaterialSemantic()) << "'\n";
-      tbsetname = materials->get_uvset_name(FROM_FSTRING(polygons->GetMaterialSemantic()), FUDaeGeometryInput::TEXBINORMAL, binput->GetSet());
+      if (daeegg_cat.is_debug()) {
+        daeegg_cat.debug()
+          << "Assigning texbinormal set " << binput->GetSet()
+          << " to semantic '" << material_semantic << "'\n";
+      }
+      tbsetname = materials->get_uvset_name(material_semantic,
+                    FUDaeGeometryInput::TEXBINORMAL, binput->GetSet());
     }
     }
-    string ttsetname ("");
+    string ttsetname;
     if (materials != NULL && tinput != NULL) {
     if (materials != NULL && tinput != NULL) {
-      daeegg_cat.debug() << "Assigning textangent set " << tinput->GetSet() << " to semantic '" << FROM_FSTRING(polygons->GetMaterialSemantic()) << "'\n";
-      ttsetname = materials->get_uvset_name(FROM_FSTRING(polygons->GetMaterialSemantic()), FUDaeGeometryInput::TEXTANGENT, tinput->GetSet());
+      if (daeegg_cat.is_debug()) {
+        daeegg_cat.debug()
+          << "Assigning textangent set " << tinput->GetSet()
+          << " to semantic '" << material_semantic << "'\n";
+        }
+      ttsetname = materials->get_uvset_name(material_semantic,
+                    FUDaeGeometryInput::TEXTANGENT, tinput->GetSet());
     }
     }
     // Loop through the indices and add the vertices.
     // Loop through the indices and add the vertices.
     for (size_t ix = 0; ix < pinput->GetIndexCount(); ++ix) {
     for (size_t ix = 0; ix < pinput->GetIndexCount(); ++ix) {
       PT_EggVertex vertex = mesh_pool->make_new_vertex();
       PT_EggVertex vertex = mesh_pool->make_new_vertex();
       const float* data = &vsource->GetData()[indices[ix]*3];
       const float* data = &vsource->GetData()[indices[ix]*3];
       vertex->set_pos(LPoint3d(data[0], data[1], data[2]));
       vertex->set_pos(LPoint3d(data[0], data[1], data[2]));
+
+      if (character != NULL) {
+        // If this is skinned geometry, add the vertex influences.
+        character->influence_vertex(indices[ix], vertex);
+      }
+
       // Process the normal
       // Process the normal
       if (nsource != NULL && ninput != NULL) {
       if (nsource != NULL && ninput != NULL) {
         assert(nsource->GetStride() == 3);
         assert(nsource->GetStride() == 3);
@@ -469,26 +662,26 @@ void DAEToEggConverter::process_mesh(PT(EggGroup) parent, const FCDGeometryMesh*
       PT(EggPrimitive) primitive = NULL;
       PT(EggPrimitive) primitive = NULL;
       // Create a primitive that matches the fcollada type
       // Create a primitive that matches the fcollada type
       switch (polygons->GetPrimitiveType()) {
       switch (polygons->GetPrimitiveType()) {
-        case FCDGeometryPolygons::LINES:
-          primitive = new EggLine();
-          break;
-        case FCDGeometryPolygons::POLYGONS:
-          primitive = new EggPolygon();
-          break;
-        case FCDGeometryPolygons::TRIANGLE_FANS:
-          primitive = new EggTriangleFan();
-          break;
-        case FCDGeometryPolygons::TRIANGLE_STRIPS:
-          primitive = new EggTriangleStrip();
-          break;
-        case FCDGeometryPolygons::POINTS:
-          primitive = new EggPoint();
-          break;
-        case FCDGeometryPolygons::LINE_STRIPS:
-          daeegg_cat.warning() << "Linestrips not yet supported!" << endl;
-          break;
-        default:
-          daeegg_cat.warning() << "Unsupported primitive type found!" << endl;
+      case FCDGeometryPolygons::LINES:
+        primitive = new EggLine();
+        break;
+      case FCDGeometryPolygons::POLYGONS:
+        primitive = new EggPolygon();
+        break;
+      case FCDGeometryPolygons::TRIANGLE_FANS:
+        primitive = new EggTriangleFan();
+        break;
+      case FCDGeometryPolygons::TRIANGLE_STRIPS:
+        primitive = new EggTriangleStrip();
+        break;
+      case FCDGeometryPolygons::POINTS:
+        primitive = new EggPoint();
+        break;
+      case FCDGeometryPolygons::LINE_STRIPS:
+        daeegg_cat.warning() << "Linestrips not yet supported!" << endl;
+        break;
+      default:
+        daeegg_cat.warning() << "Unsupported primitive type found!" << endl;
       }
       }
       if (primitive != NULL) {
       if (primitive != NULL) {
         primitive_holders[gr]->add_child(primitive);
         primitive_holders[gr]->add_child(primitive);
@@ -506,7 +699,8 @@ void DAEToEggConverter::process_mesh(PT(EggGroup) parent, const FCDGeometryMesh*
   delete[] primitive_holders;
   delete[] primitive_holders;
 }
 }
 
 
-void DAEToEggConverter::process_spline(PT(EggGroup) parent, const string group_name, FCDGeometrySpline* geometry_spline) {
+void DAEToEggConverter::
+process_spline(EggGroup *parent, const string group_name, FCDGeometrySpline* geometry_spline) {
   assert(geometry_spline != NULL);
   assert(geometry_spline != NULL);
   PT(EggGroup) result = new EggGroup(group_name);
   PT(EggGroup) result = new EggGroup(group_name);
   parent->add_child(result);
   parent->add_child(result);
@@ -521,7 +715,8 @@ void DAEToEggConverter::process_spline(PT(EggGroup) parent, const string group_n
   }
   }
 }
 }
 
 
-void DAEToEggConverter::process_spline(PT(EggGroup) parent, const FCDSpline* spline) {
+void DAEToEggConverter::
+process_spline(EggGroup *parent, const FCDSpline* spline) {
   assert(spline != NULL);
   assert(spline != NULL);
   nassertv(spline->GetSplineType() == FUDaeSplineType::NURBS);
   nassertv(spline->GetSplineType() == FUDaeSplineType::NURBS);
   // Now load in the nurbs curve to the egg library
   // Now load in the nurbs curve to the egg library
@@ -542,70 +737,26 @@ void DAEToEggConverter::process_spline(PT(EggGroup) parent, const FCDSpline* spl
   }
   }
 }
 }
 
 
-void DAEToEggConverter::process_controller(PT(EggGroup) parent, const FCDControllerInstance* instance) {
+void DAEToEggConverter::
+process_controller(EggGroup *parent, const FCDControllerInstance *instance) {
   assert(instance != NULL);
   assert(instance != NULL);
-  const FCDController* controller = (const FCDController*) instance->GetEntity();
+  const FCDController* controller = (const FCDController *)instance->GetEntity();
   assert(controller != NULL);
   assert(controller != NULL);
-  PT(EggVertexPool) vertex_pool = NULL;
-  // Add the skin geometry
-  const FCDGeometry* geometry = controller->GetBaseGeometry();
-  if (geometry != NULL) {
-    if (geometry->IsMesh()) {
-      process_mesh(parent, geometry->GetMesh(), new DaeMaterials((const FCDGeometryInstance*) instance));
+
+  if (get_animation_convert() == AC_none) {
+    // If we're exporting a static mesh, export the base geometry as-is.
+    const FCDGeometryMesh *mesh = controller->GetBaseGeometry()->GetMesh();
+    if (mesh != NULL) {
+      PT(DaeMaterials) materials = new DaeMaterials(instance);
       daeegg_cat.spam() << "Processing mesh for controller\n";
       daeegg_cat.spam() << "Processing mesh for controller\n";
-      if (_vertex_pools.count(FROM_FSTRING(geometry->GetMesh()->GetDaeId()))) {
-        daeegg_cat.debug() << "Using vertex pool " << FROM_FSTRING(geometry->GetMesh()->GetDaeId()) << "\n";
-        vertex_pool = _vertex_pools[FROM_FSTRING(geometry->GetMesh()->GetDaeId())];
-      }
-    }
-    if (geometry->IsSpline()) {
-      process_spline(parent, FROM_FSTRING(geometry->GetName()), const_cast<FCDGeometrySpline*> (geometry->GetSpline()));
-    }
-  }
-  // Add the joint hierarchy
-#if FCOLLADA_VERSION < 0x00030005
-  FCDSceneNodeList roots = (const_cast<FCDControllerInstance*> (instance))->FindSkeletonNodes();
-#else
-  FCDSceneNodeList roots;
-  (const_cast<FCDControllerInstance*> (instance))->FindSkeletonNodes(roots);
-#endif
-  for (FCDSceneNodeList::iterator it = roots.begin(); it != roots.end(); ++it) {
-    process_node(DCAST(EggGroupNode, parent), *it, true);
-  }
-  if (controller->IsSkin()) {
-    // Load in the vertex influences first
-    pmap<int32, pvector<pair<PT_EggVertex, PN_stdfloat> > > influences;
-    if (vertex_pool) {
-      for (size_t in = 0; in < controller->GetSkinController()->GetInfluenceCount(); ++in) {
-        assert(vertex_pool->has_vertex(in));
-        for (size_t pa = 0; pa < controller->GetSkinController()->GetVertexInfluence(in)->GetPairCount(); ++pa) {
-          const FCDJointWeightPair* jwpair = controller->GetSkinController()->GetVertexInfluence(in)->GetPair(pa);
-          influences[jwpair->jointIndex].push_back(pair<PT_EggVertex, PN_stdfloat> (vertex_pool->get_vertex(in), jwpair->weight));
-        }
-      }
-    }
-    // Loop through the joints in the vertex influences
-    for (pmap<int32, pvector<pair<PT_EggVertex, PN_stdfloat> > >::iterator it = influences.begin(); it != influences.end(); ++it) {
-      if (it->first == -1) {
-        daeegg_cat.warning() << "Ignoring vertex influence with negative joint index\n";
-        //FIXME: Why are there joints with index -1
-      } else {
-        const string joint_id = FROM_FSTRING(controller->GetSkinController()->GetJoint(it->first)->GetId());
-        //TODO: what if the joints have just not been defined yet?
-        if (_joints.count(joint_id) > 0) {
-          if (_joints[joint_id]) {
-            for (pvector<pair<PT_EggVertex, PN_stdfloat> >::iterator vi = it->second.begin(); vi != it->second.end(); ++vi) {
-              _joints[joint_id]->ref_vertex(vi->first, vi->second);
-            }
-          } else {
-            daeegg_cat.warning() << "Unprocessed joint being referenced: '" << joint_id << "'" << endl;
-          }
-        } else {
-          daeegg_cat.warning() << "Unknown joint being referenced: '" << joint_id << "'" << endl;
-        }
-      }
+      process_mesh(parent, mesh, materials);
     }
     }
+  } else {
+    // Add a character for this to the table, the mesh is processed later
+    PT(DaeCharacter) character = new DaeCharacter(parent, instance);
+    _characters.push_back(character);
   }
   }
+
   if (controller->IsMorph()) {
   if (controller->IsMorph()) {
     assert(controller != NULL);
     assert(controller != NULL);
     const FCDMorphController* morph_controller = controller->GetMorphController();
     const FCDMorphController* morph_controller = controller->GetMorphController();
@@ -628,28 +779,25 @@ void DAEToEggConverter::process_controller(PT(EggGroup) parent, const FCDControl
       morph->add_child(target);
       morph->add_child(target);
     }
     }
   }
   }
-  
-  // Get a <Bundle> for the character and add it to the table
-  PT(DaeCharacter) character = new DaeCharacter(parent->get_name(), instance);
-  _table->add_child(character->as_egg_bundle());
 }
 }
 
 
-void DAEToEggConverter::process_extra(PT(EggGroup) group, const FCDExtra* extra) {
+void DAEToEggConverter::
+process_extra(EggGroup *group, const FCDExtra* extra) {
   if (extra == NULL) {
   if (extra == NULL) {
     return;
     return;
   }
   }
   nassertv(group != NULL);
   nassertv(group != NULL);
-  
+
   const FCDEType* etype = extra->GetDefaultType();
   const FCDEType* etype = extra->GetDefaultType();
   if (etype == NULL) {
   if (etype == NULL) {
     return;
     return;
   }
   }
-  
+
   const FCDENode* enode = (const FCDENode*) etype->FindTechnique("PANDA3D");
   const FCDENode* enode = (const FCDENode*) etype->FindTechnique("PANDA3D");
   if (enode == NULL) {
   if (enode == NULL) {
     return;
     return;
   }
   }
-  
+
   FCDENodeList tags;
   FCDENodeList tags;
   enode->FindChildrenNodes("param", tags);
   enode->FindChildrenNodes("param", tags);
   for (FCDENodeList::iterator it = tags.begin(); it != tags.end(); ++it) {
   for (FCDENodeList::iterator it = tags.begin(); it != tags.end(); ++it) {
@@ -660,18 +808,45 @@ void DAEToEggConverter::process_extra(PT(EggGroup) group, const FCDExtra* extra)
   }
   }
 }
 }
 
 
-LMatrix4d DAEToEggConverter::convert_matrix(const FMMatrix44& matrix) {
-  LMatrix4d result = LMatrix4d::zeros_mat();
-  for (char x = 0; x < 4; ++x) {
-    for (char y = 0; y < 4; ++y) {
-      result(x, y) = matrix[x][y];
-    }
-  }
-  return result;
+LMatrix4d DAEToEggConverter::
+convert_matrix(const FMMatrix44 &matrix) {
+  return LMatrix4d(
+    matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3],
+    matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3],
+    matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3],
+    matrix[3][0], matrix[3][1], matrix[3][2], matrix[3][3]);
 }
 }
 
 
-void DAEToEggConverter::apply_transform(const PT(EggGroup) to, const FCDTransform* from) {
+void DAEToEggConverter::
+apply_transform(EggGroup *to, const FCDTransform* from) {
   assert(from != NULL);
   assert(from != NULL);
   assert(to != NULL);
   assert(to != NULL);
-  to->set_transform3d(convert_matrix(from->ToMatrix()) * to->get_transform3d());
+  //to->set_transform3d(convert_matrix(from->ToMatrix()) * to->get_transform3d());
+  switch (from->GetType()) {
+  case FCDTransform::TRANSLATION:
+    {
+      const FCDTTranslation *trans = (const FCDTTranslation *)from;
+      to->add_translate3d(TO_VEC3(trans->GetTranslation()));
+    }
+    break;
+
+  case FCDTransform::ROTATION:
+    {
+      const FCDTRotation *rot = (const FCDTRotation *)from;
+      to->add_rotate3d(rot->GetAngle(), TO_VEC3(rot->GetAxis()));
+    }
+    break;
+
+  case FCDTransform::SCALE:
+    {
+      const FCDTScale *scale = (const FCDTScale *)from;
+      to->add_scale3d(TO_VEC3(scale->GetScale()));
+    }
+    break;
+
+  default:
+    // Either a matrix, or something we can't handle.
+    to->add_matrix4(convert_matrix(from->ToMatrix()));
+    break;
+  }
 }
 }

+ 23 - 20
pandatool/src/daeegg/daeToEggConverter.h

@@ -35,6 +35,7 @@
 #include "FMath/FMMatrix44.h"
 #include "FMath/FMMatrix44.h"
 
 
 #include "daeMaterials.h"
 #include "daeMaterials.h"
+#include "daeCharacter.h"
 #include "pvector.h" // Include last
 #include "pvector.h" // Include last
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -47,39 +48,41 @@ public:
   DAEToEggConverter();
   DAEToEggConverter();
   DAEToEggConverter(const DAEToEggConverter &copy);
   DAEToEggConverter(const DAEToEggConverter &copy);
   ~DAEToEggConverter();
   ~DAEToEggConverter();
-  
+
   virtual SomethingToEggConverter *make_copy();
   virtual SomethingToEggConverter *make_copy();
-  
+
   virtual string get_name() const;
   virtual string get_name() const;
   virtual string get_extension() const;
   virtual string get_extension() const;
-  
+
   virtual bool convert_file(const Filename &filename);
   virtual bool convert_file(const Filename &filename);
+  virtual DistanceUnit get_input_units();
 
 
   bool _invert_transparency;
   bool _invert_transparency;
 
 
 private:
 private:
+  string _unit_name;
+  double _unit_meters;
   PT(EggTable) _table;
   PT(EggTable) _table;
   FCDocument* _document;
   FCDocument* _document;
   FUErrorSimpleHandler* _error_handler;
   FUErrorSimpleHandler* _error_handler;
-  pmap<const string, PT(EggGroup)> _joints;
-  pmap<const string, PT(EggVertexPool)> _vertex_pools;
-  pvector<string> _skeletons;
-  int _frame_rate;
-  
+  DaeCharacter::JointMap _joints;
+
+  typedef pvector<PT(DaeCharacter)> Characters;
+  Characters _characters;
+
   void process_asset();
   void process_asset();
-  void preprocess(const FCDSceneNode* node = NULL);
-  void process_node(PT(EggGroupNode) parent, const FCDSceneNode* node, bool forced = false);
-  void process_instance(PT(EggGroup) parent, const FCDEntityInstance* instance);
-  void process_mesh(PT(EggGroup) parent, const FCDGeometryMesh* mesh, PT(DaeMaterials) materials);
-  void process_spline(PT(EggGroup) parent, const string group_name, FCDGeometrySpline* geometry_spline);
-  void process_spline(PT(EggGroup) parent, const FCDSpline* spline);
-  void process_controller(PT(EggGroup) parent, const FCDControllerInstance* instance);
-  //void process_table_joint(PT(EggTable) parent, FCDSceneNode* node);
-  void process_extra(PT(EggGroup) group, const FCDExtra* extra);
-  
+  void process_node(EggGroupNode *parent, const FCDSceneNode* node, bool forced = false);
+  void process_instance(EggGroup *parent, const FCDEntityInstance* instance);
+  void process_mesh(EggGroup *parent, const FCDGeometryMesh* mesh,
+                    DaeMaterials *materials, DaeCharacter *character = NULL);
+  void process_spline(EggGroup *parent, const string group_name, FCDGeometrySpline* geometry_spline);
+  void process_spline(EggGroup *parent, const FCDSpline* spline);
+  void process_controller(EggGroup *parent, const FCDControllerInstance* instance);
+  void process_extra(EggGroup *group, const FCDExtra* extra);
+
   static LMatrix4d convert_matrix(const FMMatrix44& matrix);
   static LMatrix4d convert_matrix(const FMMatrix44& matrix);
-  void apply_transform(const PT(EggGroup) to, const FCDTransform* from);
-  
+  void apply_transform(EggGroup *to, const FCDTransform* from);
+
   friend class DaeCharacter;
   friend class DaeCharacter;
 };
 };
 
 

+ 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!
   #error You must include pre_fcollada_include.h before including FCollada.h!
 #endif
 #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
 // FCollada expects LINUX to be defined on linux
 #ifdef IS_LINUX
 #ifdef IS_LINUX
   #ifndef LINUX
   #ifndef LINUX

+ 8 - 0
pandatool/src/daeprogs/daeToEgg.cxx

@@ -26,6 +26,7 @@ DAEToEgg::
 DAEToEgg():
 DAEToEgg():
   SomethingToEgg("COLLADA", ".dae")
   SomethingToEgg("COLLADA", ".dae")
 {
 {
+  add_animation_options();
   add_units_options();
   add_units_options();
   add_normals_options();
   add_normals_options();
   add_transform_options();
   add_transform_options();
@@ -42,6 +43,7 @@ DAEToEgg():
     ("This program converts .dae files (COLLADA Digital Asset Exchange) to .egg.");
     ("This program converts .dae files (COLLADA Digital Asset Exchange) to .egg.");
 
 
   _coordinate_system = CS_yup_right;
   _coordinate_system = CS_yup_right;
+  _animation_convert = AC_both;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -51,6 +53,12 @@ DAEToEgg():
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DAEToEgg::
 void DAEToEgg::
 run() {
 run() {
+  if (_animation_convert != AC_both && _animation_convert != AC_none &&
+      _animation_convert != AC_chan && _animation_convert != AC_model) {
+    cerr << "Unsupported animation convert option.\n";
+    exit(1);
+  }
+
   nout << "Reading " << _input_filename << "\n";
   nout << "Reading " << _input_filename << "\n";
 
 
   _data->set_coordinate_system(_coordinate_system);
   _data->set_coordinate_system(_coordinate_system);