Selaa lähdekoodia

Merge branch 'master' into input-overhaul

rdb 7 vuotta sitten
vanhempi
sitoutus
357b6d3093
100 muutettua tiedostoa jossa 1139 lisäystä ja 1114 poistoa
  1. 4 1
      direct/src/interval/FunctionInterval.py
  2. 0 20
      doc/INSTALLING-PLUGINS.TXT
  3. 0 183
      doc/InstallerNotes
  4. 1 5
      dtool/src/cppparser/cppStructType.cxx
  5. 8 0
      dtool/src/dtoolbase/dtoolbase.cxx
  6. 2 0
      dtool/src/dtoolbase/dtoolbase.h
  7. 9 0
      dtool/src/dtoolbase/dtoolbase_cc.h
  8. 1 0
      dtool/src/dtoolutil/p3dtoolutil_ext_composite.cxx
  9. 1 1
      dtool/src/dtoolutil/stringDecoder.I
  10. 69 10
      dtool/src/dtoolutil/stringDecoder.cxx
  11. 9 6
      dtool/src/dtoolutil/stringDecoder.h
  12. 46 16
      dtool/src/dtoolutil/textEncoder.I
  13. 73 19
      dtool/src/dtoolutil/textEncoder.cxx
  14. 42 13
      dtool/src/dtoolutil/textEncoder.h
  15. 30 0
      dtool/src/dtoolutil/textEncoder_ext.I
  16. 164 0
      dtool/src/dtoolutil/textEncoder_ext.cxx
  17. 50 0
      dtool/src/dtoolutil/textEncoder_ext.h
  18. 1 1
      dtool/src/dtoolutil/unicodeLatinMap.cxx
  19. 5 5
      dtool/src/dtoolutil/unicodeLatinMap.h
  20. 2 1
      dtool/src/interrogate/interrogateBuilder.cxx
  21. 9 1
      dtool/src/interrogatedb/py_panda.cxx
  22. 1 1
      dtool/src/parser-inc/Python.h
  23. 0 20
      dtool/src/prc/notifyCategory.I
  24. 2 2
      dtool/src/prc/notifyCategory.h
  25. 0 12
      dtool/src/prc/notifyCategoryProxy.I
  26. 2 2
      dtool/src/prc/notifyCategoryProxy.h
  27. 45 54
      makepanda/makepanda.py
  28. 0 20
      makepanda/makepanda.vcproj
  29. 2 2
      panda/src/chan/animControl.cxx
  30. 3 1
      panda/src/chan/animControl.h
  31. 7 0
      panda/src/chan/partBundle.cxx
  32. 6 0
      panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm
  33. 1 0
      panda/src/cocoadisplay/cocoaPandaApp.mm
  34. 1 1
      panda/src/collide/collisionTraverser.cxx
  35. 5 5
      panda/src/display/graphicsEngine.cxx
  36. 13 13
      panda/src/display/graphicsOutput.cxx
  37. 1 0
      panda/src/display/graphicsOutput.h
  38. 14 28
      panda/src/display/graphicsPipeSelection.cxx
  39. 59 40
      panda/src/display/graphicsStateGuardian.cxx
  40. 1 0
      panda/src/display/graphicsWindowProc.h
  41. 0 1
      panda/src/display/p3display_composite2.cxx
  42. 10 20
      panda/src/display/standardMunger.cxx
  43. 3 2
      panda/src/display/standardMunger.h
  44. 2 0
      panda/src/display/subprocessWindow.cxx
  45. 3 5
      panda/src/distort/projectionScreen.cxx
  46. 0 31
      panda/src/dxgsg9/dxGeomMunger9.I
  47. 60 0
      panda/src/dxgsg9/dxGeomMunger9.cxx
  48. 1 1
      panda/src/dxgsg9/dxGeomMunger9.h
  49. 47 7
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  50. 2 6
      panda/src/dxgsg9/dxGraphicsStateGuardian9.h
  51. 0 269
      panda/src/dxgsg9/dxInput9.cxx
  52. 0 40
      panda/src/dxgsg9/dxInput9.h
  53. 23 0
      panda/src/dxgsg9/dxShaderContext9.cxx
  54. 4 1
      panda/src/dxgsg9/wdxGraphicsWindow9.cxx
  55. 0 1
      panda/src/dxgsg9/wdxGraphicsWindow9.h
  56. 2 1
      panda/src/express/pointerToArray_ext.I
  57. 5 5
      panda/src/express/pointerToArray_ext.h
  58. 1 1
      panda/src/gles2gsg/gles2gsg.h
  59. 1 1
      panda/src/glesgsg/glesgsg.h
  60. 11 9
      panda/src/glstuff/glCgShaderContext_src.cxx
  61. 9 0
      panda/src/glstuff/glGraphicsBuffer_src.cxx
  62. 12 2
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  63. 2 2
      panda/src/glstuff/glShaderContext_src.cxx
  64. 23 2
      panda/src/gobj/geom.cxx
  65. 2 0
      panda/src/gobj/geom.h
  66. 3 1
      panda/src/gobj/geomVertexArrayData.I
  67. 16 3
      panda/src/gobj/geomVertexData.I
  68. 18 5
      panda/src/gobj/geomVertexData.cxx
  69. 2 2
      panda/src/gobj/preparedGraphicsObjects.cxx
  70. 1 1
      panda/src/gobj/textureReloadRequest.cxx
  71. 1 1
      panda/src/gobj/textureStage.cxx
  72. 1 1
      panda/src/grutil/multitexReducer.cxx
  73. 1 1
      panda/src/net/connection.cxx
  74. 1 1
      panda/src/osxdisplay/osxGraphicsStateGuardian.h
  75. 0 1
      panda/src/particlesystem/baseParticle.cxx
  76. 0 2
      panda/src/particlesystem/baseParticle.h
  77. 1 1
      panda/src/pgraph/colorAttrib.cxx
  78. 1 1
      panda/src/pgraph/colorAttrib.h
  79. 3 0
      panda/src/pgraph/cullTraverserData.cxx
  80. 2 3
      panda/src/pgraph/geomNode.cxx
  81. 4 1
      panda/src/pgraph/renderAttrib.cxx
  82. 3 0
      panda/src/pgraph/renderState.cxx
  83. 3 0
      panda/src/pgraph/transformState.cxx
  84. 0 8
      panda/src/pipeline/blockerSimple.I
  85. 2 2
      panda/src/pipeline/blockerSimple.h
  86. 3 0
      panda/src/pipeline/config_pipeline.cxx
  87. 9 10
      panda/src/pipeline/contextSwitch_longjmp_src.c
  88. 14 15
      panda/src/pipeline/contextSwitch_posix_src.c
  89. 6 7
      panda/src/pipeline/contextSwitch_ucontext_src.c
  90. 9 10
      panda/src/pipeline/contextSwitch_windows_src.c
  91. 3 0
      panda/src/pipeline/cycleData.h
  92. 25 13
      panda/src/pipeline/cycleDataLockedReader.I
  93. 37 14
      panda/src/pipeline/cycleDataLockedStageReader.I
  94. 40 17
      panda/src/pipeline/cycleDataStageWriter.I
  95. 1 1
      panda/src/pipeline/pythonThread.cxx
  96. 0 8
      panda/src/pipeline/threadSimpleImpl.I
  97. 11 3
      panda/src/pipeline/threadSimpleImpl.cxx
  98. 1 1
      panda/src/pipeline/threadSimpleImpl.h
  99. 5 13
      panda/src/pipeline/threadSimpleManager.cxx
  100. 0 77
      panda/src/text/textNode.I

+ 4 - 1
direct/src/interval/FunctionInterval.py

@@ -77,7 +77,10 @@ class FunctionInterval(Interval.Interval):
 
     @staticmethod
     def makeUniqueName(func, suffix = ''):
-        name = 'Func-%s-%d' % (getattr(func, '__name__', str(func)), FunctionInterval.functionIntervalNum)
+        func_name = getattr(func, '__name__', None)
+        if func_name is None:
+            func_name = str(func)
+        name = 'Func-%s-%d' % (func_name, FunctionInterval.functionIntervalNum)
         FunctionInterval.functionIntervalNum += 1
         if suffix:
             name = '%s-%s' % (name, str(suffix))

+ 0 - 20
doc/INSTALLING-PLUGINS.TXT

@@ -1,20 +0,0 @@
-HOW TO INSTALL MAX PANDA PLUGINS.
-
-Step 1. Install the visual studio 2008 runtime by
-running "vcredist_x86-sp1.exe" as administrator.
-As a convenience, this installer is included with panda.
-
-Step 2. Make sure that there is only one copy of panda
-in your system PATH.  If you only have one copy of panda
-installed, you can skip this step.
-
-Step 3. Copy the relevant DLLs for your version
-of max from the panda plugins directory to the
-max plugins directory.  For instance, if you are 
-using Max 9, copy maxegg9.dlo and maxeggimport9.dlo
-
-HOW TO INSTALL MAYA PANDA PLUGINS.
-
-(To be written)
-
-

+ 0 - 183
doc/InstallerNotes

@@ -1,183 +0,0 @@
-------------------------  RELEASE 1.0.0  ---------------------------------
-
-	* We now have working exporters for Max5, Max6, Max7, Maya5, Maya6
-
-	* The Max exporter is dramatically improved:
-
-	  - it now includes support for character studio.
-	  - the polygon winding bug has been fixed.
-
-	* Panda no longer requires any registry keys or environment
-	variables. This means it is now possible to:
-
-	     - run panda directly from a compact disc
-	     - install multiple copies of panda on a single machine
-	     - install panda by copying the tree from another computer
-
-        Note that the installer does add the panda 'bin' directory to
-	your PATH, and it does store an uninstall key in the registry,
-	but neither of these is needed for panda to function.
-
-	* The 'makepanda' build system is now capable of building
-	prepackaged games for Windows.  These prepackaged games are simply
-	copies of panda with the game code included, some of the
-	unnecessary stuff stripped out, and some changes to the start
-	menu.  See "Airblade - Installer" on the panda downloads page
-	for an example.
-
-	* All of the sample programs have been tested.  The ones that didn't
-	work have been removed, the ones that do work have been (lightly)
-	documented.
-
-	* This is the first release to include not just a binary installer
-	for windows, but:
-
-	    - a binary installer (RPM) for Fedora 2
-	    - a binary installer (RPM) for Fedora 3
-	    - a binary installer (RPM) for Redhat 9
-	    - a binary installer for windows, as always
-	    - a source tar-ball for linux
-	    - a source zip-file for windows
-
-------------------------  RELEASE 2004-12-13  ---------------------------------
-
-	* Basic server-client networking support is back in Panda3D. There is a
-	networking sample in the samples directory. This uses the Panda3d
-	distributed object system.The README file will explain how to run this.
-	Documentation of this if forthcoming.
-
-	* Panda3d now reduces the number of environment variables such that only 2
-	are needed now - PRC_PATH and PLAYER.
-
-	* GraphicsChannel and GraphicsLayer class have been removed from the
-	panda/src/display directory. Most Panda applications won't need to be
-	changed, since most applications simply use ShowBase.py (which has been
-	adjustedappropriately) to open a window and do the initial setup.  For
-	those rare applications where you need to create your own DisplayRegions,
-	the makeDisplayRegion() interface has been moved from GraphicsLayer to
-	GraphicsWindow (actually, to GraphicsOutput, which is the base class of
-	GraphicsWindow).  You can modify your application to call
-	base.win.makeDisplayRegion() accordingly.  If you have something like
-	displayRegion.getLayer(), replace it with displayRegion.getWindow()
-	instead.
-
-	* Effective with the current version of Panda, the way that HPR angles are
-	calculated will be changing. The change will make a difference to existing
-	code or databases that store a hard-coded rotation as a HPR, but only when
-	R is involved, or both H and P are involved together.  That is to say more
-	precisely, HPR angles with (R != 0 || (H != 0 && P != 0)) now represent a
-	different rotation than they used to. If you find some legacy code that no
-	longer works correctly (e.g. it introduces crazy rotations), try putting
-	the following in your Config.prc file:
-
-		  temp-hpr-fix 0
-
-	To turn off the correct behavior and return to the old, broken behavior.
-	Note that a longer-term solution will be to represent the HPR angles
-	correctly in all legacy code.  The function oldToNewHpr() is provided to
-	aid this transition.
-
-	* PandaNode definition has been changed to support setting an
-	into_collide_mask for any arbitrary node, in particular for any GeomNode.
-	It used to be that only CollisionNodes had an into_collide_mask.  This
-	change obviates the need for CollisionNode::set_collide_geom(), which is
-	now a deprecated interface and will be removed at some point in the future.
-
-	Details:
-	There's now a NodePath::set_collide_mask() and
-	NodePath::get_collide_mask(), which operate on all CollisionNodes and
-	GeomNodes at and below the current node. By default, set_collide_mask()
-	will replace the entire collide mask, but you may also specify (via a
-	second parameter) the subset of bits that are to be changed; other bits
-	will be left alone.  You can also specify a particular type of node to
-	modify via a third parameter, e.g. you can adjust the masks for GeomNodes
-	or CollisionNodes only.
-
-	The NodePath set_collide_mask() interface changes the into_collide_mask.
-	Those familiar with the collision system will recall that a CollisionNode
-	(but only a CollisionNode) also has a from_collide_mask.  The
-	from_collide_mask of the active mover is compared with the into_collide_mask
-	of each object in the world; a collision is only possible if there are some
-	bits in common.
-
-	It used to be that only other CollisionNodes had an into_collide_mask.  A
-	mover would only test for collisions with CollisionNodes that matched its
-	collide_mask. If you wanted to make your mover detect collisions with
-	visible geometry which had no into_collide_mask, you had to call
-	set_collide_geom(1). This allowed the mover to detect collisions with *all*
-	visible geometry; it was either an all-or-none thing.
-
-	Now that GeomNodes also have an into_collide_mask, there's no longer a need
-	for set_collide_geom().  A mover will detect collisions with any
-	CollisionNodes or GeomNodes that match its collide_mask.  This means, for
-	the purposes of collision detection, you can use CollisionNodes and
-	GeomNodes pretty much interchangeably; simply set the appropriate bits on
-	the objects you want to collide with, regardless of whether they are
-	invisible collision solids or visible geometry.
-
-	(This should not be taken as a license to avoid using CollisionNodes
-	altogether. The intersection computation with visible geometry is still
-	less efficient than the same computation with collision solids. And visible
-	geometry tends to be many times more complex than is strictly necessary for
-	collisions.)
-
-	There's one more detail: every GeomNode, by default, has one bit set on in
-	its collide_mask, unless it is explicitly turned off.  This bit is
-	GeomNode::get_default_collide_mask().  This bit is provided for the
-	convenience of programmers who still want the old behavior of
-	set_collide_geom(): it allows you to easily create a CollisionNode that
-	will collide with all visible geometry in the world.
-
-	Along the same lines, there's also CollisionNode::get_default_collide_mask(),
-	which is 0x000fffff.  This is the default mask that is created for a new
-	CollisionNode (and it does not include the bit reserved for GeomNodes, 
-	above). Previously, a new CollisionNode would have all bits on by default.
-
-
-
-------------------------  RELEASE 2004-11-11  -----------------------------------
-
-	* Multiple mice can now be used with Panda3D. showbase has a list called
-	pointerWatcherNodes. The first mouse on this list is the system mouse. The
-	getMouseX() and getMouseY() will return coordinates relative to the
-	application window. The rest of the mice on the list will give raw mouse
-	positions and will change when they are moved on the screen.
-
-	In addition there are new events for mouse buttons. Each mouse will be have
-	a corresponding event. mouse1 will send mousedev1-mouse1, mousedev1-mouse2
-	and mousedev1-mouse3 events. mouse2 and any other mouse attached
-	will send similar events mousedev2-mouse1 etc.
-
-	The old mouse buttons work too. mouse1, mouse2, mouse3 events will be
-	triggered if that button is pressed on any mouse
-
-------------------------  RELEASE 2004-10-13  -----------------------------------
-
-General
-
-	* Release notes: Each release will now have an entry associated with
-	it in this document. This will be updated in reverse-chronological order.
-
-Panda3D
-	* Distributed with this release is a working version of the SceneEditor
-	created in Spring 2004 at the ETC. Documentation will be forthcoming on the
-	website. This can be found in <InstallPath>/SceneEditor
-
-	* The latest version of FMOD is distributed with this release. The latest
-	version is 3.73.
-
-	* AudioSound object now allows more types of sound. These include wma and 
-	ogg vorbis formats. This is valid when using the fmod sound system. Midi, 
-	Mod, s3m, it, xm and such sequencer type file formats are not supported. 
-	Exception - Midi files can be played. This is not fully implemented.
-
-	* A bug in SoundInterval is fixed. SoundInterval looping would incorrectly 
-	add a minimum of 1.5 seconds to the sound. This has been fixed. Sound 
-	looping problems in general should be fixed. Midi's still don't support 
-	looping through the AudioSound object. They should loop through 
-	SoundIntervals though.
-
-	* Cg support has been added to Panda3D. Documentation for this is 
-	forthcoming.
-
-

+ 1 - 5
dtool/src/cppparser/cppStructType.cxx

@@ -306,7 +306,6 @@ is_trivial() const {
   }
 
   // Now look for functions that are virtual or con/destructors.
-  bool is_default_constructible = true;
   CPPScope::Functions::const_iterator fi;
   for (fi = _scope->_functions.begin(); fi != _scope->_functions.end(); ++fi) {
     CPPFunctionGroup *fgroup = (*fi).second;
@@ -343,9 +342,6 @@ is_trivial() const {
           // Same for the default constructor.
           return false;
         }
-        // The presence of a non-default constructor makes the class not
-        // default-constructible.
-        is_default_constructible = false;
       }
 
       if (fgroup->_name == "operator =") {
@@ -356,7 +352,7 @@ is_trivial() const {
   }
 
   // Finally, the class must be default-constructible.
-  return is_default_constructible;
+  return is_default_constructible(V_public);
 }
 
 /**

+ 8 - 0
dtool/src/dtoolbase/dtoolbase.cxx

@@ -62,4 +62,12 @@ default_thread_consider_yield() {
 void (*global_thread_yield)() = default_thread_yield;
 void (*global_thread_consider_yield)() = default_thread_consider_yield;
 
+#ifdef HAVE_PYTHON
+static PyThreadState *
+default_thread_state_swap(PyThreadState *state) {
+  return nullptr;
+}
+PyThreadState *(*global_thread_state_swap)(PyThreadState *tstate) = default_thread_state_swap;
+#endif  // HAVE_PYTHON
+
 #endif  // HAVE_THREADS && SIMPLE_THREADS

+ 2 - 0
dtool/src/dtoolbase/dtoolbase.h

@@ -63,8 +63,10 @@
 /* Windows likes to define min() and max() macros, which will conflict with
    std::min() and std::max() respectively, unless we do this: */
 #ifdef WIN32
+#ifndef NOMINMAX
 #define NOMINMAX
 #endif
+#endif
 
 #ifndef __has_builtin
 #define __has_builtin(x) 0

+ 9 - 0
dtool/src/dtoolbase/dtoolbase_cc.h

@@ -233,6 +233,15 @@ INLINE void thread_consider_yield() {
   (*global_thread_consider_yield)();
 }
 
+#ifdef HAVE_PYTHON
+typedef struct _ts PyThreadState;
+extern EXPCL_DTOOL_DTOOLBASE PyThreadState *(*global_thread_state_swap)(PyThreadState *tstate);
+
+INLINE PyThreadState *thread_state_swap(PyThreadState *tstate) {
+  return (*global_thread_state_swap)(tstate);
+}
+#endif  // HAVE_PYTHON
+
 #else
 
 INLINE void thread_yield() {

+ 1 - 0
dtool/src/dtoolutil/p3dtoolutil_ext_composite.cxx

@@ -1,2 +1,3 @@
 #include "filename_ext.cxx"
 #include "globPattern_ext.cxx"
+#include "textEncoder_ext.cxx"

+ 1 - 1
dtool/src/dtoolutil/stringDecoder.I

@@ -53,5 +53,5 @@ StringUtf8Decoder(const std::string &input) : StringDecoder(input) {
  *
  */
 INLINE StringUnicodeDecoder::
-StringUnicodeDecoder(const std::string &input) : StringDecoder(input) {
+StringUtf16Decoder(const std::string &input) : StringDecoder(input) {
 }

+ 69 - 10
dtool/src/dtoolutil/stringDecoder.cxx

@@ -26,7 +26,7 @@ StringDecoder::
 /**
  * Returns the next character in sequence.
  */
-int StringDecoder::
+char32_t StringDecoder::
 get_next_character() {
   if (test_eof()) {
     return -1;
@@ -57,19 +57,20 @@ get_notify_ptr() {
 
 /*
 In UTF-8, each 16-bit Unicode character is encoded as a sequence of
-one, two, or three 8-bit bytes, depending on the value of the
+one, two, three or four 8-bit bytes, depending on the value of the
 character. The following table shows the format of such UTF-8 byte
 sequences (where the "free bits" shown by x's in the table are
 combined in the order shown, and interpreted from most significant to
 least significant):
 
  Binary format of bytes in sequence:
-                                        Number of    Maximum expressible
- 1st byte     2nd byte    3rd byte      free bits:      Unicode value:
+                                              Number of    Maximum expressible
+ 1st byte    2nd byte   3rd byte   4th byte   free bits:     Unicode value:
 
- 0xxxxxxx                                  7           007F hex   (127)
- 110xxxxx     10xxxxxx                  (5+6)=11       07FF hex  (2047)
- 1110xxxx     10xxxxxx    10xxxxxx     (4+6+6)=16      FFFF hex (65535)
+ 0xxxxxxx                                         7          007F hex   (127)
+ 110xxxxx    10xxxxxx                          (5+6)=11      07FF hex  (2047)
+ 1110xxxx    10xxxxxx   10xxxxxx              (4+6+6)=16     FFFF hex (65535)
+ 11110xxx    10xxxxxx   10xxxxxx   10xxxxxx   (4+6*3)=21   10FFFF hex (1114111)
 
 The value of each individual byte indicates its UTF-8 function, as follows:
 
@@ -77,12 +78,13 @@ The value of each individual byte indicates its UTF-8 function, as follows:
  80 to BF hex (128 to 191):  continuing byte in a multi-byte sequence.
  C2 to DF hex (194 to 223):  first byte of a two-byte sequence.
  E0 to EF hex (224 to 239):  first byte of a three-byte sequence.
+ F0 to F7 hex (240 to 247):  first byte of a four-byte sequence.
 */
 
 /**
  * Returns the next character in sequence.
  */
-int StringUtf8Decoder::
+char32_t StringUtf8Decoder::
 get_next_character() {
   unsigned int result;
   while (!test_eof()) {
@@ -125,6 +127,35 @@ get_next_character() {
       unsigned int three = (unsigned char)_input[_p++];
       result = ((result & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
       return result;
+
+    } else if ((result & 0xf8) == 0xf0) {
+      // First byte of four.
+      if (test_eof()) {
+        if (_notify_ptr != nullptr) {
+          (*_notify_ptr)
+            << "utf-8 encoded string '" << _input << "' ends abruptly.\n";
+        }
+        return -1;
+      }
+      unsigned int two = (unsigned char)_input[_p++];
+      if (test_eof()) {
+        if (_notify_ptr != nullptr) {
+          (*_notify_ptr)
+            << "utf-8 encoded string '" << _input << "' ends abruptly.\n";
+        }
+        return -1;
+      }
+      unsigned int three = (unsigned char)_input[_p++];
+      if (test_eof()) {
+        if (_notify_ptr != nullptr) {
+          (*_notify_ptr)
+            << "utf-8 encoded string '" << _input << "' ends abruptly.\n";
+        }
+        return -1;
+      }
+      unsigned int four = (unsigned char)_input[_p++];
+      result = ((result & 0x07) << 18) | ((two & 0x3f) << 12) | ((three & 0x3f) << 6) | (four & 0x3f);
+      return result;
     }
 
     // Otherwise--the high bit is set but it is not one of the introductory
@@ -144,7 +175,7 @@ get_next_character() {
 /**
  * Returns the next character in sequence.
  */
-int StringUnicodeDecoder::
+char32_t StringUtf16Decoder::
 get_next_character() {
   if (test_eof()) {
     return -1;
@@ -159,5 +190,33 @@ get_next_character() {
     return -1;
   }
   unsigned int low = (unsigned char)_input[_p++];
-  return ((high << 8) | low);
+  int ch = ((high << 8) | low);
+
+  /*
+  using std::swap;
+
+  if (ch == 0xfffe) {
+    // This is a byte-swapped byte-order-marker.  That means we need to swap
+    // the endianness of the rest of the stream.
+    char *data = (char *)_input.data();
+    for (size_t p = _p; p < _input.size() - 1; p += 2) {
+      std::swap(data[p], data[p + 1]);
+    }
+    ch = 0xfeff;
+  }
+  */
+
+  if (ch >= 0xd800 && ch < 0xdc00 && (_p + 1) < _input.size()) {
+    // This is a high surrogate.  Look for a subsequent low surrogate.
+    unsigned int high = (unsigned char)_input[_p];
+    unsigned int low = (unsigned char)_input[_p + 1];
+    int ch2 = ((high << 8) | low);
+    if (ch2 >= 0xdc00 && ch2 < 0xe000) {
+      // Yes, this is a low surrogate.
+      _p += 2;
+      return 0x10000 + ((ch - 0xd800) << 10) + (ch2 - 0xdc00);
+    }
+  }
+  // No, this is just a regular character, or an unpaired surrogate.
+  return ch;
 }

+ 9 - 6
dtool/src/dtoolutil/stringDecoder.h

@@ -26,7 +26,7 @@ public:
   INLINE StringDecoder(const std::string &input);
   virtual ~StringDecoder();
 
-  virtual int get_next_character();
+  virtual char32_t get_next_character();
   INLINE bool is_eof();
 
   static void set_notify_ptr(std::ostream *ptr);
@@ -48,20 +48,23 @@ class StringUtf8Decoder : public StringDecoder {
 public:
   INLINE StringUtf8Decoder(const std::string &input);
 
-  virtual int get_next_character();
+  virtual char32_t get_next_character();
 };
 
 /**
  * This decoder extracts characters two at a time to get a plain wide
- * character sequence.
+ * character sequence.  It supports surrogate pairs.
  */
-class StringUnicodeDecoder : public StringDecoder {
+class StringUtf16Decoder : public StringDecoder {
 public:
-  INLINE StringUnicodeDecoder(const std::string &input);
+  INLINE StringUtf16Decoder(const std::string &input);
 
-  virtual int get_next_character();
+  virtual char32_t get_next_character();
 };
 
+// Deprecated alias of StringUtf16Encoder.
+typedef StringUtf16Decoder StringUnicodeDecoder;
+
 #include "stringDecoder.I"
 
 #endif

+ 46 - 16
dtool/src/dtoolutil/textEncoder.I

@@ -90,6 +90,7 @@ set_text(const std::string &text) {
   if (!has_text() || _text != text) {
     _text = text;
     _flags = (_flags | F_got_text) & ~F_got_wtext;
+    text_changed();
   }
 }
 
@@ -101,7 +102,11 @@ set_text(const std::string &text) {
  */
 INLINE void TextEncoder::
 set_text(const std::string &text, TextEncoder::Encoding encoding) {
-  set_wtext(decode_text(text, encoding));
+  if (encoding == _encoding) {
+    set_text(text);
+  } else {
+    set_wtext(decode_text(text, encoding));
+  }
 }
 
 /**
@@ -112,6 +117,7 @@ clear_text() {
   _text = std::string();
   _wtext = std::wstring();
   _flags |= (F_got_text | F_got_wtext);
+  text_changed();
 }
 
 /**
@@ -151,8 +157,11 @@ get_text(TextEncoder::Encoding encoding) const {
  */
 INLINE void TextEncoder::
 append_text(const std::string &text) {
-  _text = get_text() + text;
-  _flags = (_flags | F_got_text) & ~F_got_wtext;
+  if (!text.empty()) {
+    _text = get_text() + text;
+    _flags = (_flags | F_got_text) & ~F_got_wtext;
+    text_changed();
+  }
 }
 
 /**
@@ -160,9 +169,25 @@ append_text(const std::string &text) {
  * wide character, up to 16 bits in Unicode.
  */
 INLINE void TextEncoder::
-append_unicode_char(int character) {
+append_unicode_char(char32_t character) {
+#if WCHAR_MAX >= 0x10FFFF
+  // wchar_t might be UTF-32.
   _wtext = get_wtext() + std::wstring(1, (wchar_t)character);
+#else
+  if ((character & ~0xffff) == 0) {
+    _wtext = get_wtext() + std::wstring(1, (wchar_t)character);
+  } else {
+    // Encode as a surrogate pair.
+    uint32_t v = (uint32_t)character - 0x10000u;
+    wchar_t wstr[2] = {
+      (wchar_t)((v >> 10u) | 0xd800u),
+      (wchar_t)((v & 0x3ffu) | 0xdc00u),
+    };
+    _wtext = get_wtext() + std::wstring(wstr, 2);
+  }
+#endif
   _flags = (_flags | F_got_wtext) & ~F_got_text;
+  text_changed();
 }
 
 /**
@@ -195,11 +220,12 @@ get_unicode_char(size_t index) const {
  * according to set_encoding().
  */
 INLINE void TextEncoder::
-set_unicode_char(size_t index, int character) {
+set_unicode_char(size_t index, char32_t character) {
   get_wtext();
   if (index < _wtext.length()) {
     _wtext[index] = character;
     _flags &= ~F_got_text;
+    text_changed();
   }
 }
 
@@ -257,7 +283,7 @@ reencode_text(const std::string &text, TextEncoder::Encoding from,
  * otherwise.  This is akin to ctype's isalpha(), extended to Unicode.
  */
 INLINE bool TextEncoder::
-unicode_isalpha(int character) {
+unicode_isalpha(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
     return false;
@@ -271,7 +297,7 @@ unicode_isalpha(int character) {
  * otherwise.  This is akin to ctype's isdigit(), extended to Unicode.
  */
 INLINE bool TextEncoder::
-unicode_isdigit(int character) {
+unicode_isdigit(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
     // The digits aren't actually listed in the map.
@@ -286,11 +312,11 @@ unicode_isdigit(int character) {
  * otherwise.  This is akin to ctype's ispunct(), extended to Unicode.
  */
 INLINE bool TextEncoder::
-unicode_ispunct(int character) {
+unicode_ispunct(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
     // Some punctuation marks aren't listed in the map.
-    return (character >= 0 && character < 128 && ispunct(character));
+    return (character < 128 && ispunct(character));
   }
   return entry->_char_type == UnicodeLatinMap::CT_punct;
 }
@@ -300,7 +326,7 @@ unicode_ispunct(int character) {
  * otherwise.  This is akin to ctype's isupper(), extended to Unicode.
  */
 INLINE bool TextEncoder::
-unicode_isupper(int character) {
+unicode_isupper(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
     return false;
@@ -313,7 +339,7 @@ unicode_isupper(int character) {
  * otherwise.  This is akin to ctype's isspace(), extended to Unicode.
  */
 INLINE bool TextEncoder::
-unicode_isspace(int character) {
+unicode_isspace(char32_t character) {
   switch (character) {
   case ' ':
   case '\t':
@@ -330,7 +356,7 @@ unicode_isspace(int character) {
  * otherwise.  This is akin to ctype's islower(), extended to Unicode.
  */
 INLINE bool TextEncoder::
-unicode_islower(int character) {
+unicode_islower(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
     return false;
@@ -343,7 +369,7 @@ unicode_islower(int character) {
  * akin to ctype's toupper(), extended to Unicode.
  */
 INLINE int TextEncoder::
-unicode_toupper(int character) {
+unicode_toupper(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
     return character;
@@ -356,7 +382,7 @@ unicode_toupper(int character) {
  * akin to ctype's tolower(), extended to Unicode.
  */
 INLINE int TextEncoder::
-unicode_tolower(int character) {
+unicode_tolower(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
     return character;
@@ -418,6 +444,7 @@ set_wtext(const std::wstring &wtext) {
   if (!has_text() || _wtext != wtext) {
     _wtext = wtext;
     _flags = (_flags | F_got_wtext) & ~F_got_text;
+    text_changed();
   }
 }
 
@@ -439,8 +466,11 @@ get_wtext() const {
  */
 INLINE void TextEncoder::
 append_wtext(const std::wstring &wtext) {
-  _wtext = get_wtext() + wtext;
-  _flags = (_flags | F_got_wtext) & ~F_got_text;
+  if (!wtext.empty()) {
+    _wtext = get_wtext() + wtext;
+    _flags = (_flags | F_got_wtext) & ~F_got_text;
+    text_changed();
+  }
 }
 
 /**

+ 73 - 19
dtool/src/dtoolutil/textEncoder.cxx

@@ -21,7 +21,7 @@ using std::ostream;
 using std::string;
 using std::wstring;
 
-TextEncoder::Encoding TextEncoder::_default_encoding = TextEncoder::E_iso8859;
+TextEncoder::Encoding TextEncoder::_default_encoding = TextEncoder::E_utf8;
 
 /**
  * Adjusts the text stored within the encoder to all uppercase letters
@@ -35,6 +35,7 @@ make_upper() {
     (*si) = unicode_toupper(*si);
   }
   _flags &= ~F_got_text;
+  text_changed();
 }
 
 /**
@@ -49,6 +50,7 @@ make_lower() {
     (*si) = unicode_tolower(*si);
   }
   _flags &= ~F_got_text;
+  text_changed();
 }
 
 /**
@@ -107,11 +109,11 @@ is_wtext() const {
 }
 
 /**
- * Encodes a single wide char into a one-, two-, or three-byte string,
- * according to the given encoding system.
+ * Encodes a single Unicode character into a one-, two-, three-, or four-byte
+ * string, according to the given encoding system.
  */
 string TextEncoder::
-encode_wchar(wchar_t ch, TextEncoder::Encoding encoding) {
+encode_wchar(char32_t ch, TextEncoder::Encoding encoding) {
   switch (encoding) {
   case E_iso8859:
     if ((ch & ~0xff) == 0) {
@@ -143,17 +145,38 @@ encode_wchar(wchar_t ch, TextEncoder::Encoding encoding) {
       return
         string(1, (char)((ch >> 6) | 0xc0)) +
         string(1, (char)((ch & 0x3f) | 0x80));
-    } else {
+    } else if ((ch & ~0xffff) == 0) {
       return
         string(1, (char)((ch >> 12) | 0xe0)) +
         string(1, (char)(((ch >> 6) & 0x3f) | 0x80)) +
         string(1, (char)((ch & 0x3f) | 0x80));
+    } else {
+      return
+        string(1, (char)((ch >> 18) | 0xf0)) +
+        string(1, (char)(((ch >> 12) & 0x3f) | 0x80)) +
+        string(1, (char)(((ch >> 6) & 0x3f) | 0x80)) +
+        string(1, (char)((ch & 0x3f) | 0x80));
     }
 
-  case E_unicode:
-    return
-      string(1, (char)(ch >> 8)) +
-      string(1, (char)(ch & 0xff));
+  case E_utf16be:
+    if ((ch & ~0xffff) == 0) {
+      // Note that this passes through surrogates and BOMs unharmed.
+      return
+        string(1, (char)(ch >> 8)) +
+        string(1, (char)(ch & 0xff));
+    } else {
+      // Use a surrogate pair.
+      uint32_t v = (uint32_t)ch - 0x10000u;
+      uint16_t hi = (v >> 10u) | 0xd800u;
+      uint16_t lo = (v & 0x3ffu) | 0xdc00u;
+      char encoded[4] = {
+        (char)(hi >> 8),
+        (char)(hi & 0xff),
+        (char)(lo >> 8),
+        (char)(lo & 0xff),
+      };
+      return string(encoded, 4);
+    }
   }
 
   return "";
@@ -167,8 +190,25 @@ string TextEncoder::
 encode_wtext(const wstring &wtext, TextEncoder::Encoding encoding) {
   string result;
 
-  for (wstring::const_iterator pi = wtext.begin(); pi != wtext.end(); ++pi) {
-    result += encode_wchar(*pi, encoding);
+  for (size_t i = 0; i < wtext.size(); ++i) {
+    wchar_t ch = wtext[i];
+
+    // On some systems, wstring may be UTF-16, and contain surrogate pairs.
+#if WCHAR_MAX < 0x10FFFF
+    if (ch >= 0xd800 && ch < 0xdc00 && (i + 1) < wtext.size()) {
+      // This is a high surrogate.  Look for a subsequent low surrogate.
+      wchar_t ch2 = wtext[i + 1];
+      if (ch2 >= 0xdc00 && ch2 < 0xe000) {
+        // Yes, this is a low surrogate.
+        char32_t code_point = 0x10000 + ((ch - 0xd800) << 10) + (ch2 - 0xdc00);
+        result += encode_wchar(code_point, encoding);
+        i++;
+        continue;
+      }
+    }
+#endif
+
+    result += encode_wchar(ch, encoding);
   }
 
   return result;
@@ -187,9 +227,9 @@ decode_text(const string &text, TextEncoder::Encoding encoding) {
       return decode_text_impl(decoder);
     }
 
-  case E_unicode:
+  case E_utf16be:
     {
-      StringUnicodeDecoder decoder(text);
+      StringUtf16Decoder decoder(text);
       return decode_text_impl(decoder);
     }
 
@@ -211,7 +251,7 @@ decode_text_impl(StringDecoder &decoder) {
   wstring result;
   // bool expand_amp = get_expand_amp();
 
-  wchar_t character = decoder.get_next_character();
+  char32_t character = decoder.get_next_character();
   while (!decoder.is_eof()) {
     /*
     if (character == '&' && expand_amp) {
@@ -219,7 +259,14 @@ decode_text_impl(StringDecoder &decoder) {
       character = expand_amp_sequence(decoder);
     }
     */
-    result += character;
+    if (character <= WCHAR_MAX) {
+      result += character;
+    } else {
+      // We need to encode this as a surrogate pair.
+      uint32_t v = (uint32_t)character - 0x10000u;
+      result += (wchar_t)((v >> 10u) | 0xd800u);
+      result += (wchar_t)((v & 0x3ffu) | 0xdc00u);
+    }
     character = decoder.get_next_character();
   }
 
@@ -314,6 +361,12 @@ expand_amp_sequence(StringDecoder &decoder) const {
 }
 */
 
+/**
+ * Called whenever the text has been changed.
+ */
+void TextEncoder::
+text_changed() {
+}
 
 /**
  *
@@ -327,8 +380,8 @@ operator << (ostream &out, TextEncoder::Encoding encoding) {
   case TextEncoder::E_utf8:
     return out << "utf8";
 
-  case TextEncoder::E_unicode:
-    return out << "unicode";
+  case TextEncoder::E_utf16be:
+    return out << "utf16be";
   };
 
   return out << "**invalid TextEncoder::Encoding(" << (int)encoding << ")**";
@@ -346,8 +399,9 @@ operator >> (istream &in, TextEncoder::Encoding &encoding) {
     encoding = TextEncoder::E_iso8859;
   } else if (word == "utf8" || word == "utf-8") {
     encoding = TextEncoder::E_utf8;
-  } else if (word == "unicode") {
-    encoding = TextEncoder::E_unicode;
+  } else if (word == "unicode" || word == "utf16be" || word == "utf-16be" ||
+                                  word == "utf16-be" || word == "utf-16-be") {
+    encoding = TextEncoder::E_utf16be;
   } else {
     ostream *notify_ptr = StringDecoder::get_notify_ptr();
     if (notify_ptr != nullptr) {

+ 42 - 13
dtool/src/dtoolutil/textEncoder.h

@@ -23,7 +23,7 @@ class StringDecoder;
 
 /**
  * This class can be used to convert text between multiple representations,
- * e.g.  utf-8 to Unicode.  You may use it as a static class object, passing
+ * e.g.  UTF-8 to UTF-16.  You may use it as a static class object, passing
  * the encoding each time, or you may create an instance and use that object,
  * which will record the current encoding and retain the current string.
  *
@@ -35,12 +35,17 @@ PUBLISHED:
   enum Encoding {
     E_iso8859,
     E_utf8,
-    E_unicode
+    E_utf16be,
+
+    // Deprecated alias for E_utf16be
+    E_unicode = E_utf16be,
   };
 
   INLINE TextEncoder();
   INLINE TextEncoder(const TextEncoder &copy);
 
+  virtual ~TextEncoder() = default;
+
   INLINE void set_encoding(Encoding encoding);
   INLINE Encoding get_encoding() const;
 
@@ -48,35 +53,46 @@ PUBLISHED:
   INLINE static Encoding get_default_encoding();
   MAKE_PROPERTY(default_encoding, get_default_encoding, set_default_encoding);
 
+#ifdef CPPPARSER
+  EXTEND void set_text(PyObject *text);
+  EXTEND void set_text(PyObject *text, Encoding encoding);
+#else
   INLINE void set_text(const std::string &text);
   INLINE void set_text(const std::string &text, Encoding encoding);
+#endif
   INLINE void clear_text();
   INLINE bool has_text() const;
 
   void make_upper();
   void make_lower();
 
+#ifdef CPPPARSER
+  EXTEND PyObject *get_text() const;
+  EXTEND PyObject *get_text(Encoding encoding) const;
+  EXTEND void append_text(PyObject *text);
+#else
   INLINE std::string get_text() const;
   INLINE std::string get_text(Encoding encoding) const;
   INLINE void append_text(const std::string &text);
-  INLINE void append_unicode_char(int character);
+#endif
+  INLINE void append_unicode_char(char32_t character);
   INLINE size_t get_num_chars() const;
   INLINE int get_unicode_char(size_t index) const;
-  INLINE void set_unicode_char(size_t index, int character);
+  INLINE void set_unicode_char(size_t index, char32_t character);
   INLINE std::string get_encoded_char(size_t index) const;
   INLINE std::string get_encoded_char(size_t index, Encoding encoding) const;
   INLINE std::string get_text_as_ascii() const;
 
   INLINE static std::string reencode_text(const std::string &text, Encoding from, Encoding to);
 
-  INLINE static bool unicode_isalpha(int character);
-  INLINE static bool unicode_isdigit(int character);
-  INLINE static bool unicode_ispunct(int character);
-  INLINE static bool unicode_islower(int character);
-  INLINE static bool unicode_isupper(int character);
-  INLINE static bool unicode_isspace(int character);
-  INLINE static int unicode_toupper(int character);
-  INLINE static int unicode_tolower(int character);
+  INLINE static bool unicode_isalpha(char32_t character);
+  INLINE static bool unicode_isdigit(char32_t character);
+  INLINE static bool unicode_ispunct(char32_t character);
+  INLINE static bool unicode_islower(char32_t character);
+  INLINE static bool unicode_isupper(char32_t character);
+  INLINE static bool unicode_isspace(char32_t character);
+  INLINE static int unicode_toupper(char32_t character);
+  INLINE static int unicode_tolower(char32_t character);
 
   INLINE static std::string upper(const std::string &source);
   INLINE static std::string upper(const std::string &source, Encoding encoding);
@@ -91,11 +107,24 @@ PUBLISHED:
   std::wstring get_wtext_as_ascii() const;
   bool is_wtext() const;
 
-  static std::string encode_wchar(wchar_t ch, Encoding encoding);
+#ifdef CPPPARSER
+  EXTEND static PyObject *encode_wchar(char32_t ch, Encoding encoding);
+  EXTEND INLINE PyObject *encode_wtext(const std::wstring &wtext) const;
+  EXTEND static PyObject *encode_wtext(const std::wstring &wtext, Encoding encoding);
+  EXTEND INLINE PyObject *decode_text(PyObject *text) const;
+  EXTEND static PyObject *decode_text(PyObject *text, Encoding encoding);
+#else
+  static std::string encode_wchar(char32_t ch, Encoding encoding);
   INLINE std::string encode_wtext(const std::wstring &wtext) const;
   static std::string encode_wtext(const std::wstring &wtext, Encoding encoding);
   INLINE std::wstring decode_text(const std::string &text) const;
   static std::wstring decode_text(const std::string &text, Encoding encoding);
+#endif
+
+  MAKE_PROPERTY(text, get_text, set_text);
+
+protected:
+  virtual void text_changed();
 
 private:
   enum Flags {

+ 30 - 0
dtool/src/dtoolutil/textEncoder_ext.I

@@ -0,0 +1,30 @@
+/**
+ * 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."
+ *
+ * @file textEncoder_ext.I
+ * @author rdb
+ * @date 2018-10-08
+ */
+
+/**
+ * Encodes a wide-text string into a single-char string, according to the
+ * current encoding.
+ */
+INLINE PyObject *Extension<TextEncoder>::
+encode_wtext(const std::wstring &wtext) const {
+  return encode_wtext(wtext, _this->get_encoding());
+}
+
+/**
+ * Returns the given wstring decoded to a single-byte string, via the current
+ * encoding system.
+ */
+INLINE PyObject *Extension<TextEncoder>::
+decode_text(PyObject *text) const {
+  return decode_text(text, _this->get_encoding());
+}

+ 164 - 0
dtool/src/dtoolutil/textEncoder_ext.cxx

@@ -0,0 +1,164 @@
+/**
+ * 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."
+ *
+ * @file textEncoder_ext.cxx
+ * @author rdb
+ * @date 2018-09-29
+ */
+
+#include "textEncoder_ext.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Sets the text as a Unicode string.  In Python 2, if a regular str is given,
+ * it is assumed to be in the TextEncoder's specified encoding.
+ */
+void Extension<TextEncoder>::
+set_text(PyObject *text) {
+  if (PyUnicode_Check(text)) {
+#if PY_VERSION_HEX >= 0x03030000
+    Py_ssize_t len;
+    const char *str = PyUnicode_AsUTF8AndSize(text, &len);
+    _this->set_text(std::string(str, len), TextEncoder::E_utf8);
+#else
+    Py_ssize_t len = PyUnicode_GET_SIZE(text);
+    wchar_t *str = (wchar_t *)alloca(sizeof(wchar_t) * (len + 1));
+    PyUnicode_AsWideChar((PyUnicodeObject *)text, str, len);
+    _this->set_wtext(std::wstring(str, len));
+#endif
+  } else {
+#if PY_MAJOR_VERSION >= 3
+    Dtool_Raise_TypeError("expected string");
+#else
+    char *str;
+    Py_ssize_t len;
+    if (PyString_AsStringAndSize(text, (char **)&str, &len) != -1) {
+      _this->set_text(std::string(str, len));
+    }
+#endif
+  }
+}
+
+/**
+ * Sets the text as an encoded byte string of the given encoding.
+ */
+void Extension<TextEncoder>::
+set_text(PyObject *text, TextEncoder::Encoding encoding) {
+  char *str;
+  Py_ssize_t len;
+  if (PyBytes_AsStringAndSize(text, &str, &len) >= 0) {
+    _this->set_text(std::string(str, len), encoding);
+  }
+}
+
+/**
+ * Returns the text as a string.  In Python 2, the returned string is in the
+ * TextEncoder's specified encoding.  In Python 3, it is returned as unicode.
+ */
+PyObject *Extension<TextEncoder>::
+get_text() const {
+#if PY_MAJOR_VERSION >= 3
+  std::wstring text = _this->get_wtext();
+  return PyUnicode_FromWideChar(text.data(), (Py_ssize_t)text.size());
+#else
+  std::string text = _this->get_text();
+  return PyString_FromStringAndSize((char *)text.data(), (Py_ssize_t)text.size());
+#endif
+}
+
+/**
+ * Returns the text as a bytes object in the given encoding.
+ */
+PyObject *Extension<TextEncoder>::
+get_text(TextEncoder::Encoding encoding) const {
+  std::string text = _this->get_text(encoding);
+#if PY_MAJOR_VERSION >= 3
+  return PyBytes_FromStringAndSize((char *)text.data(), (Py_ssize_t)text.size());
+#else
+  return PyString_FromStringAndSize((char *)text.data(), (Py_ssize_t)text.size());
+#endif
+}
+
+/**
+ * Appends the text as a string (or Unicode object in Python 2).
+ */
+void Extension<TextEncoder>::
+append_text(PyObject *text) {
+  if (PyUnicode_Check(text)) {
+#if PY_VERSION_HEX >= 0x03030000
+    Py_ssize_t len;
+    const char *str = PyUnicode_AsUTF8AndSize(text, &len);
+    std::string text_str(str, len);
+    if (_this->get_encoding() == TextEncoder::E_utf8) {
+      _this->append_text(text_str);
+    } else {
+      _this->append_wtext(TextEncoder::decode_text(text_str, TextEncoder::E_utf8));
+    }
+#else
+    Py_ssize_t len = PyUnicode_GET_SIZE(text);
+    wchar_t *str = (wchar_t *)alloca(sizeof(wchar_t) * (len + 1));
+    PyUnicode_AsWideChar((PyUnicodeObject *)text, str, len);
+    _this->append_wtext(std::wstring(str, len));
+#endif
+  } else {
+#if PY_MAJOR_VERSION >= 3
+    Dtool_Raise_TypeError("expected string");
+#else
+    char *str;
+    Py_ssize_t len;
+    if (PyString_AsStringAndSize(text, (char **)&str, &len) != -1) {
+      _this->append_text(std::string(str, len));
+    }
+#endif
+  }
+}
+
+/**
+ * Encodes the given wide character as byte string in the given encoding.
+ */
+PyObject *Extension<TextEncoder>::
+encode_wchar(char32_t ch, TextEncoder::Encoding encoding) {
+  std::string value = TextEncoder::encode_wchar(ch, encoding);
+#if PY_MAJOR_VERSION >= 3
+  return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
+#else
+  return PyString_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
+#endif
+}
+
+/**
+ * Encodes a wide-text string into a single-char string, according to the
+ * given encoding.
+ */
+PyObject *Extension<TextEncoder>::
+encode_wtext(const wstring &wtext, TextEncoder::Encoding encoding) {
+  std::string value = TextEncoder::encode_wtext(wtext, encoding);
+#if PY_MAJOR_VERSION >= 3
+  return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
+#else
+  return PyString_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
+#endif
+}
+
+/**
+ * Returns the given wstring decoded to a single-byte string, via the given
+ * encoding system.
+ */
+PyObject *Extension<TextEncoder>::
+decode_text(PyObject *text, TextEncoder::Encoding encoding) {
+  char *str;
+  Py_ssize_t len;
+  if (PyBytes_AsStringAndSize(text, &str, &len) >= 0) {
+    return Dtool_WrapValue(TextEncoder::decode_text(std::string(str, len), encoding));
+  } else {
+    return nullptr;
+  }
+}
+
+#endif  // HAVE_PYTHON

+ 50 - 0
dtool/src/dtoolutil/textEncoder_ext.h

@@ -0,0 +1,50 @@
+/**
+ * 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."
+ *
+ * @file textEncoder_ext.h
+ * @author rdb
+ * @date 2018-09-29
+ */
+
+#ifndef TEXTENCODER_EXT_H
+#define TEXTENCODER_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "textEncoder.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for TextEncoder, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<TextEncoder> : public ExtensionBase<TextEncoder> {
+public:
+  void set_text(PyObject *text);
+  void set_text(PyObject *text, TextEncoder::Encoding encoding);
+
+  PyObject *get_text() const;
+  PyObject *get_text(TextEncoder::Encoding encoding) const;
+  void append_text(PyObject *text);
+
+  static PyObject *encode_wchar(char32_t ch, TextEncoder::Encoding encoding);
+  INLINE PyObject *encode_wtext(const std::wstring &wtext) const;
+  static PyObject *encode_wtext(const std::wstring &wtext, TextEncoder::Encoding encoding);
+  INLINE PyObject *decode_text(PyObject *text) const;
+  static PyObject *decode_text(PyObject *text, TextEncoder::Encoding encoding);
+};
+
+#include "textEncoder_ext.I"
+
+#endif  // HAVE_PYTHON
+
+#endif  // TEXTENCODER_EXT_H

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

@@ -1378,7 +1378,7 @@ static const wchar_t combining_accent_map[] = {
  * Returns the Entry associated with the indicated character, if there is one.
  */
 const UnicodeLatinMap::Entry *UnicodeLatinMap::
-look_up(wchar_t character) {
+look_up(char32_t character) {
   if (!_initialized) {
     init();
   }

+ 5 - 5
dtool/src/dtoolutil/unicodeLatinMap.h

@@ -112,17 +112,17 @@ public:
 
   class Entry {
   public:
-    wchar_t _character;
+    char32_t _character;
     CharType _char_type;
     char _ascii_equiv;
     char _ascii_additional;
-    wchar_t _tolower_character;
-    wchar_t _toupper_character;
+    char32_t _tolower_character;
+    char32_t _toupper_character;
     AccentType _accent_type;
     int _additional_flags;
   };
 
-  static const Entry *look_up(wchar_t character);
+  static const Entry *look_up(char32_t character);
 
   static wchar_t get_combining_accent(AccentType accent);
 
@@ -130,7 +130,7 @@ private:
   static void init();
   static bool _initialized;
 
-  typedef phash_map<wchar_t, const Entry *, integer_hash<wchar_t> > ByCharacter;
+  typedef phash_map<char32_t, const Entry *, integer_hash<char32_t> > ByCharacter;
   static ByCharacter *_by_character;
   enum { max_direct_chars = 256 };
   static const Entry *_direct_chars[max_direct_chars];

+ 2 - 1
dtool/src/interrogate/interrogateBuilder.cxx

@@ -1389,7 +1389,8 @@ scan_element(CPPInstance *element, CPPStructType *struct_type,
     // We can only generate a getter and a setter if we can talk about the
     // type it is.
 
-    if (parameter_type->as_struct_type() != nullptr) {
+    if (parameter_type->as_struct_type() != nullptr &&
+        !parameter_type->is_trivial()) {
       // Wrap the type in a const reference.
       parameter_type = TypeManager::wrap_const_reference(parameter_type);
     }

+ 9 - 1
dtool/src/interrogatedb/py_panda.cxx

@@ -722,7 +722,10 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) {
 
     // Extract the __file__ attribute, if present.
     Filename main_dir;
-    PyObject *file_attr = PyObject_GetAttrString(main_module, "__file__");
+    PyObject *file_attr = nullptr;
+    if (main_module != nullptr) {
+      file_attr = PyObject_GetAttrString(main_module, "__file__");
+    }
     if (file_attr == nullptr) {
       // Must be running in the interactive interpreter.  Use the CWD.
       main_dir = ExecutionEnvironment::get_cwd();
@@ -752,6 +755,11 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) {
     ExecutionEnvironment::shadow_environment_variable("MAIN_DIR", main_dir.to_os_specific());
     PyErr_Clear();
     initialized_main_dir = true;
+
+    // Also, while we are at it, initialize the thread swap hook.
+#if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
+    global_thread_state_swap = PyThreadState_Swap;
+#endif
   }
 
   PyModule_AddIntConstant(module, "Dtool_PyNativeInterface", 1);

+ 1 - 1
dtool/src/parser-inc/Python.h

@@ -28,7 +28,7 @@ typedef _typeobject PyTypeObject;
 typedef struct {} PyStringObject;
 typedef struct {} PyUnicodeObject;
 
-class PyThreadState;
+typedef struct _ts PyThreadState;
 typedef int Py_ssize_t;
 typedef struct bufferinfo Py_buffer;
 

+ 0 - 20
dtool/src/prc/notifyCategory.I

@@ -82,26 +82,6 @@ is_debug() const {
   // Instruct the compiler to optimize for the usual case.
   return UNLIKELY(is_on(NS_debug));
 }
-#else
-/**
- * When NOTIFY_DEBUG is not defined, the categories are never set to "spam" or
- * "debug" severities, and these methods are redefined to be static to make it
- * more obvious to the compiler.
- */
-constexpr bool NotifyCategory::
-is_spam() {
-  return false;
-}
-
-/**
- * When NOTIFY_DEBUG is not defined, the categories are never set to "spam" or
- * "debug" severities, and these methods are redefined to be static to make it
- * more obvious to the compiler.
- */
-constexpr bool NotifyCategory::
-is_debug() {
-  return false;
-}
 #endif
 
 /**

+ 2 - 2
dtool/src/prc/notifyCategory.h

@@ -55,8 +55,8 @@ PUBLISHED:
   INLINE bool is_spam() const;
   INLINE bool is_debug() const;
 #else
-  constexpr static bool is_spam();
-  constexpr static bool is_debug();
+  constexpr static bool is_spam() { return false; }
+  constexpr static bool is_debug() { return false; }
 #endif
   INLINE bool is_info() const;
   INLINE bool is_warning() const;

+ 0 - 12
dtool/src/prc/notifyCategoryProxy.I

@@ -72,12 +72,6 @@ is_spam() {
   // Instruct the compiler to optimize for the usual case.
   return UNLIKELY(get_unsafe_ptr()->is_spam());
 }
-#else
-template<class GetCategory>
-constexpr bool NotifyCategoryProxy<GetCategory>::
-is_spam() {
-  return false;
-}
 #endif
 
 /**
@@ -90,12 +84,6 @@ is_debug() {
   // Instruct the compiler to optimize for the usual case.
   return UNLIKELY(get_unsafe_ptr()->is_debug());
 }
-#else
-template<class GetCategory>
-constexpr bool NotifyCategoryProxy<GetCategory>::
-is_debug() {
-  return false;
-}
 #endif
 
 /**

+ 2 - 2
dtool/src/prc/notifyCategoryProxy.h

@@ -75,8 +75,8 @@ public:
   INLINE bool is_spam();
   INLINE bool is_debug();
 #else
-  constexpr static bool is_spam();
-  constexpr static bool is_debug();
+  constexpr static bool is_spam() { return false; }
+  constexpr static bool is_debug() { return false; }
 #endif
   INLINE bool is_info();
   INLINE bool is_warning();

+ 45 - 54
makepanda/makepanda.py

@@ -681,6 +681,9 @@ if (COMPILER == "MSVC"):
         IncDirectory("FCOLLADA", GetThirdpartyDir() + "fcollada/include/FCollada")
     if (PkgSkip("ASSIMP")==0):
         LibName("ASSIMP", GetThirdpartyDir() + "assimp/lib/assimp.lib")
+        path = GetThirdpartyDir() + "assimp/lib/IrrXML.lib"
+        if os.path.isfile(path):
+            LibName("ASSIMP", GetThirdpartyDir() + "assimp/lib/IrrXML.lib")
         IncDirectory("ASSIMP", GetThirdpartyDir() + "assimp/include/assimp")
     if (PkgSkip("SQUISH")==0):
         if GetOptimize() <= 2:
@@ -947,8 +950,6 @@ if (COMPILER=="GCC"):
 
     if GetTarget() == 'darwin':
         LibName("ALWAYS", "-framework AppKit")
-        if (PkgSkip("OPENCV")==0):
-            LibName("OPENCV", "-framework QuickTime")
         LibName("AGL", "-framework AGL")
         LibName("CARBON", "-framework Carbon")
         LibName("COCOA", "-framework Cocoa")
@@ -1335,9 +1336,10 @@ def CompileCxx(obj,src,opts):
                     # Work around Apple compiler bug.
                     cmd += " -U__EXCEPTIONS"
 
-            if 'RTTI' not in opts:
+            target = GetTarget()
+            if 'RTTI' not in opts and target != "darwin":
                 # We always disable RTTI on Android for memory usage reasons.
-                if optlevel >= 4 or GetTarget() == "android":
+                if optlevel >= 4 or target == "android":
                     cmd += " -fno-rtti"
 
         if ('SSE2' in opts or not PkgSkip("SSE2")) and not arch.startswith("arm") and arch != 'aarch64':
@@ -1597,6 +1599,8 @@ def CompileLib(lib, obj, opts):
         else:
             cmd = GetAR() + ' cru ' + BracketNameWithQuotes(lib)
         for x in obj:
+            if GetLinkAllStatic() and x.endswith('.a'):
+                continue
             cmd += ' ' + BracketNameWithQuotes(x)
         oscmd(cmd)
 
@@ -3161,10 +3165,10 @@ CopyAllHeaders('panda/src/movies')
 CopyAllHeaders('panda/src/pgraphnodes')
 CopyAllHeaders('panda/src/pgraph')
 CopyAllHeaders('panda/src/cull')
+CopyAllHeaders('panda/src/display')
 CopyAllHeaders('panda/src/chan')
 CopyAllHeaders('panda/src/char')
 CopyAllHeaders('panda/src/dgraph')
-CopyAllHeaders('panda/src/display')
 CopyAllHeaders('panda/src/device')
 CopyAllHeaders('panda/src/pnmtext')
 CopyAllHeaders('panda/src/text')
@@ -3297,7 +3301,6 @@ if (PkgSkip("PANDATOOL")==0):
     CopyAllHeaders('pandatool/src/ptloader')
     CopyAllHeaders('pandatool/src/miscprogs')
     CopyAllHeaders('pandatool/src/pstatserver')
-    CopyAllHeaders('pandatool/src/softprogs')
     CopyAllHeaders('pandatool/src/text-stats')
     CopyAllHeaders('pandatool/src/vrmlprogs')
     CopyAllHeaders('pandatool/src/win-stats')
@@ -3553,6 +3556,7 @@ IGATEFILES += [
     "dSearchPath.h",
     "executionEnvironment.h",
     "textEncoder.h",
+    "textEncoder_ext.h",
     "filename.h",
     "filename_ext.h",
     "globPattern.h",
@@ -3817,7 +3821,7 @@ if (not RUNTIME):
 if (not RUNTIME):
   OPTS=['DIR:panda/src/gobj', 'BUILDING:PANDA',  'NVIDIACG', 'ZLIB', 'SQUISH']
   TargetAdd('p3gobj_composite1.obj', opts=OPTS, input='p3gobj_composite1.cxx')
-  TargetAdd('p3gobj_composite2.obj', opts=OPTS, input='p3gobj_composite2.cxx')
+  TargetAdd('p3gobj_composite2.obj', opts=OPTS+['BIGOBJ'], input='p3gobj_composite2.cxx')
 
   OPTS=['DIR:panda/src/gobj', 'NVIDIACG', 'ZLIB', 'SQUISH', 'PYTHON']
   IGATEFILES=GetDirectoryContents('panda/src/gobj', ["*.h", "*_composite*.cxx"])
@@ -3876,38 +3880,6 @@ if (not RUNTIME):
   TargetAdd('libp3cull.in', opts=['IMOD:panda3d.core', 'ILIB:libp3cull', 'SRCDIR:panda/src/cull'])
   TargetAdd('libp3cull_igate.obj', input='libp3cull.in', opts=["DEPENDENCYONLY"])
 
-#
-# DIRECTORY: panda/src/chan/
-#
-
-if (not RUNTIME):
-  OPTS=['DIR:panda/src/chan', 'BUILDING:PANDA']
-  TargetAdd('p3chan_composite1.obj', opts=OPTS, input='p3chan_composite1.cxx')
-  TargetAdd('p3chan_composite2.obj', opts=OPTS, input='p3chan_composite2.cxx')
-
-  OPTS=['DIR:panda/src/chan', 'PYTHON']
-  IGATEFILES=GetDirectoryContents('panda/src/chan', ["*.h", "*_composite*.cxx"])
-  IGATEFILES.remove('movingPart.h')
-  IGATEFILES.remove('animChannelFixed.h')
-  TargetAdd('libp3chan.in', opts=OPTS, input=IGATEFILES)
-  TargetAdd('libp3chan.in', opts=['IMOD:panda3d.core', 'ILIB:libp3chan', 'SRCDIR:panda/src/chan'])
-  TargetAdd('libp3chan_igate.obj', input='libp3chan.in', opts=["DEPENDENCYONLY"])
-
-
-# DIRECTORY: panda/src/char/
-#
-
-if (not RUNTIME):
-  OPTS=['DIR:panda/src/char', 'BUILDING:PANDA']
-  TargetAdd('p3char_composite1.obj', opts=OPTS, input='p3char_composite1.cxx')
-  TargetAdd('p3char_composite2.obj', opts=OPTS, input='p3char_composite2.cxx')
-
-  OPTS=['DIR:panda/src/char', 'PYTHON']
-  IGATEFILES=GetDirectoryContents('panda/src/char', ["*.h", "*_composite*.cxx"])
-  TargetAdd('libp3char.in', opts=OPTS, input=IGATEFILES)
-  TargetAdd('libp3char.in', opts=['IMOD:panda3d.core', 'ILIB:libp3char', 'SRCDIR:panda/src/char'])
-  TargetAdd('libp3char_igate.obj', input='libp3char.in', opts=["DEPENDENCYONLY"])
-
 #
 # DIRECTORY: panda/src/dgraph/
 #
@@ -3944,6 +3916,7 @@ if (not RUNTIME):
 
 if (not RUNTIME):
   OPTS=['DIR:panda/src/display', 'BUILDING:PANDA']
+  TargetAdd('p3display_graphicsStateGuardian.obj', opts=OPTS, input='graphicsStateGuardian.cxx')
   TargetAdd('p3display_composite1.obj', opts=OPTS, input='p3display_composite1.cxx')
   TargetAdd('p3display_composite2.obj', opts=OPTS, input='p3display_composite2.cxx')
 
@@ -3962,6 +3935,38 @@ if (not RUNTIME):
     TargetAdd('subprocessWindowBuffer.obj', opts=OPTS, input='subprocessWindowBuffer.cxx')
     TargetAdd('libp3subprocbuffer.ilb', input='subprocessWindowBuffer.obj')
 
+#
+# DIRECTORY: panda/src/chan/
+#
+
+if (not RUNTIME):
+  OPTS=['DIR:panda/src/chan', 'BUILDING:PANDA']
+  TargetAdd('p3chan_composite1.obj', opts=OPTS, input='p3chan_composite1.cxx')
+  TargetAdd('p3chan_composite2.obj', opts=OPTS, input='p3chan_composite2.cxx')
+
+  OPTS=['DIR:panda/src/chan', 'PYTHON']
+  IGATEFILES=GetDirectoryContents('panda/src/chan', ["*.h", "*_composite*.cxx"])
+  IGATEFILES.remove('movingPart.h')
+  IGATEFILES.remove('animChannelFixed.h')
+  TargetAdd('libp3chan.in', opts=OPTS, input=IGATEFILES)
+  TargetAdd('libp3chan.in', opts=['IMOD:panda3d.core', 'ILIB:libp3chan', 'SRCDIR:panda/src/chan'])
+  TargetAdd('libp3chan_igate.obj', input='libp3chan.in', opts=["DEPENDENCYONLY"])
+
+
+# DIRECTORY: panda/src/char/
+#
+
+if (not RUNTIME):
+  OPTS=['DIR:panda/src/char', 'BUILDING:PANDA']
+  TargetAdd('p3char_composite1.obj', opts=OPTS, input='p3char_composite1.cxx')
+  TargetAdd('p3char_composite2.obj', opts=OPTS, input='p3char_composite2.cxx')
+
+  OPTS=['DIR:panda/src/char', 'PYTHON']
+  IGATEFILES=GetDirectoryContents('panda/src/char', ["*.h", "*_composite*.cxx"])
+  TargetAdd('libp3char.in', opts=OPTS, input=IGATEFILES)
+  TargetAdd('libp3char.in', opts=['IMOD:panda3d.core', 'ILIB:libp3char', 'SRCDIR:panda/src/char'])
+  TargetAdd('libp3char_igate.obj', input='libp3char.in', opts=["DEPENDENCYONLY"])
+
 #
 # DIRECTORY: panda/src/pnmtext/
 #
@@ -4169,6 +4174,7 @@ if (not RUNTIME):
   TargetAdd('libpanda.dll', input='p3device_composite2.obj')
   TargetAdd('libpanda.dll', input='p3dgraph_composite1.obj')
   TargetAdd('libpanda.dll', input='p3dgraph_composite2.obj')
+  TargetAdd('libpanda.dll', input='p3display_graphicsStateGuardian.obj')
   TargetAdd('libpanda.dll', input='p3display_composite1.obj')
   TargetAdd('libpanda.dll', input='p3display_composite2.obj')
   TargetAdd('libpanda.dll', input='p3pipeline_composite1.obj')
@@ -6348,21 +6354,6 @@ if (PkgSkip("PANDATOOL")==0):
     TargetAdd('p3pstatserver_composite1.obj', opts=OPTS, input='p3pstatserver_composite1.cxx')
     TargetAdd('libp3pstatserver.lib', input='p3pstatserver_composite1.obj')
 
-#
-# DIRECTORY: pandatool/src/softprogs/
-#
-
-if (PkgSkip("PANDATOOL")==0):
-    OPTS=['DIR:pandatool/src/softprogs', 'OPENSSL']
-    TargetAdd('softcvs_softCVS.obj', opts=OPTS, input='softCVS.cxx')
-    TargetAdd('softcvs_softFilename.obj', opts=OPTS, input='softFilename.cxx')
-    TargetAdd('softcvs.exe', input='softcvs_softCVS.obj')
-    TargetAdd('softcvs.exe', input='softcvs_softFilename.obj')
-    TargetAdd('softcvs.exe', input='libp3progbase.lib')
-    TargetAdd('softcvs.exe', input='libp3pandatoolbase.lib')
-    TargetAdd('softcvs.exe', input=COMMON_PANDA_LIBS)
-    TargetAdd('softcvs.exe', opts=['ADVAPI'])
-
 #
 # DIRECTORY: pandatool/src/text-stats/
 #

+ 0 - 20
makepanda/makepanda.vcproj

@@ -4770,12 +4770,6 @@
 				<File RelativePath="..\pandatool\src\ptloader\config_ptloader.h"></File>
 				<File RelativePath="..\pandatool\src\ptloader\loaderFileTypePandatool.h"></File>
 			</Filter>
-			<Filter Name="softprogs">
-				<File RelativePath="..\pandatool\src\softprogs\softCVS.cxx"></File>
-				<File RelativePath="..\pandatool\src\softprogs\softFilename.h"></File>
-				<File RelativePath="..\pandatool\src\softprogs\softCVS.h"></File>
-				<File RelativePath="..\pandatool\src\softprogs\softFilename.cxx"></File>
-			</Filter>
 			<Filter Name="imageprogs">
 				<File RelativePath="..\pandatool\src\imageprogs\imageTrans.h"></File>
 				<File RelativePath="..\pandatool\src\imageprogs\imageTransformColors.cxx"></File>
@@ -5305,20 +5299,6 @@
 				<File RelativePath="..\pandatool\src\lwo\lwoSurfaceBlockImage.h"></File>
 				<File RelativePath="..\pandatool\src\lwo\lwoSurfaceBlockCoordSys.cxx"></File>
 			</Filter>
-			<Filter Name="softegg">
-				<File RelativePath="..\pandatool\src\softegg\soft2Egg.c"></File>
-				<File RelativePath="..\pandatool\src\softegg\softNodeTree.cxx"></File>
-				<File RelativePath="..\pandatool\src\softegg\softNodeDesc.h"></File>
-				<File RelativePath="..\pandatool\src\softegg\softEggGroupUserData.cxx"></File>
-				<File RelativePath="..\pandatool\src\softegg\softEggGroupUserData.I"></File>
-				<File RelativePath="..\pandatool\src\softegg\config_softegg.cxx"></File>
-				<File RelativePath="..\pandatool\src\softegg\softToEggConverter.cxx"></File>
-				<File RelativePath="..\pandatool\src\softegg\softNodeTree.h"></File>
-				<File RelativePath="..\pandatool\src\softegg\config_softegg.h"></File>
-				<File RelativePath="..\pandatool\src\softegg\softNodeDesc.cxx"></File>
-				<File RelativePath="..\pandatool\src\softegg\softToEggConverter.h"></File>
-				<File RelativePath="..\pandatool\src\softegg\softEggGroupUserData.h"></File>
-			</Filter>
 			<Filter Name="xfileprogs">
 				<File RelativePath="..\pandatool\src\xfileprogs\eggToX.cxx"></File>
 				<File RelativePath="..\pandatool\src\xfileprogs\xFileTrans.cxx"></File>

+ 2 - 2
panda/src/chan/animControl.cxx

@@ -32,14 +32,14 @@ AnimControl(const std::string &name, PartBundle *part,
   Namable(name),
   _pending_lock(name),
   _pending_cvar(_pending_lock),
-  _bound_joints(BitArray::all_on())
+  _bound_joints(BitArray::all_on()),
+  _part(part)
 {
 #ifdef DO_MEMORY_USAGE
   MemoryUsage::update_type(this, get_class_type());
 #endif
 
   _pending = true;
-  _part = part;
   _anim = nullptr;
   _channel_index = -1;
   set_frame_rate(frame_rate);

+ 3 - 1
panda/src/chan/animControl.h

@@ -39,6 +39,8 @@ class EXPCL_PANDA_CHAN AnimControl : public TypedReferenceCount, public AnimInte
 public:
   AnimControl(const std::string &name, PartBundle *part,
               double frame_rate, int num_frames);
+  AnimControl(const AnimControl &copy) = delete;
+
   void setup_anim(PartBundle *part, AnimBundle *anim, int channel_index,
                   const BitArray &bound_joints);
   void set_bound_joints(const BitArray &bound_joints);
@@ -82,7 +84,7 @@ private:
   // This is a PT(PartGroup) instead of a PT(PartBundle), just because we
   // can't include partBundle.h for circular reasons.  But it actually keeps a
   // pointer to a PartBundle.
-  PT(PartGroup) _part;
+  const PT(PartGroup) _part;
   PT(AnimBundle) _anim;
   int _channel_index;
 

+ 7 - 0
panda/src/chan/partBundle.cxx

@@ -556,8 +556,15 @@ control_removed(AnimControl *control) {
     CDStageWriter cdata(_cycler, pipeline_stage);
     ChannelBlend::iterator cbi = cdata->_blend.find(control);
     if (cbi != cdata->_blend.end()) {
+      cdata->_net_blend -= cbi->second;
       cdata->_blend.erase(cbi);
       cdata->_anim_changed = true;
+
+      // We need to make sure that any _effective_channel pointers that point
+      // to this control are cleared.
+      if (pipeline_stage == 0) {
+        determine_effective_channels(cdata);
+      }
     }
   }
   CLOSE_ITERATE_ALL_STAGES(_cycler);

+ 6 - 0
panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm

@@ -134,6 +134,12 @@ get_properties(FrameBufferProperties &properties, NSOpenGLPixelFormat* pixel_for
   if (accelerated) {
     properties.set_force_hardware(1);
   }
+
+  // Cautiously setting this to true.  It appears that macOS framebuffers are
+  // sRGB-capable, but I don't really know how to verify this.
+  if (color_size == 32 && !color_float) {
+    properties.set_srgb_color(true);
+  }
 }
 
 /**

+ 1 - 0
panda/src/cocoadisplay/cocoaPandaApp.mm

@@ -12,6 +12,7 @@
  */
 
 #import "cocoaPandaApp.h"
+#include "config_cocoadisplay.h"
 
 @implementation CocoaPandaApp
 - (void) sendEvent: (NSEvent *) event {

+ 1 - 1
panda/src/collide/collisionTraverser.cxx

@@ -1254,7 +1254,7 @@ compare_collider_to_geom(CollisionEntry &entry, const Geom *geom,
 
     if (geom->get_primitive_type() == Geom::PT_polygons) {
       Thread *current_thread = Thread::get_current_thread();
-      CPT(GeomVertexData) data = geom->get_vertex_data()->animate_vertices(true, current_thread);
+      CPT(GeomVertexData) data = geom->get_animated_vertex_data(true, current_thread);
       GeomVertexReader vertex(data, InternalName::get_vertex());
 
       int num_primitives = geom->get_num_primitives();

+ 5 - 5
panda/src/display/graphicsEngine.cxx

@@ -753,7 +753,7 @@ render_frame() {
         // frames, so we won't have to recompute it each frame.
         int num_drs = win->get_num_active_display_regions();
         for (int i = 0; i < num_drs; ++i) {
-          DisplayRegion *dr = win->get_active_display_region(i);
+          PT(DisplayRegion) dr = win->get_active_display_region(i);
           if (dr != nullptr) {
             NodePath camera_np = dr->get_camera(current_thread);
             if (!camera_np.is_empty()) {
@@ -1359,7 +1359,7 @@ is_scene_root(const PandaNode *node) {
     if (win->is_active() && win->get_gsg()->is_active()) {
       int num_display_regions = win->get_num_active_display_regions();
       for (int i = 0; i < num_display_regions; i++) {
-        DisplayRegion *dr = win->get_active_display_region(i);
+        PT(DisplayRegion) dr = win->get_active_display_region(i);
         if (dr != nullptr) {
           NodePath camera = dr->get_camera();
           if (camera.is_empty()) {
@@ -1435,7 +1435,7 @@ cull_and_draw_together(GraphicsEngine::Windows wlist,
 
         int num_display_regions = win->get_num_active_display_regions();
         for (int i = 0; i < num_display_regions; i++) {
-          DisplayRegion *dr = win->get_active_display_region(i);
+          PT(DisplayRegion) dr = win->get_active_display_region(i);
           if (dr != nullptr) {
             cull_and_draw_together(win, dr, current_thread);
           }
@@ -1539,7 +1539,7 @@ cull_to_bins(GraphicsEngine::Windows wlist, Thread *current_thread) {
       PStatTimer timer(win->get_cull_window_pcollector(), current_thread);
       int num_display_regions = win->get_num_active_display_regions();
       for (int i = 0; i < num_display_regions; ++i) {
-        DisplayRegion *dr = win->get_active_display_region(i);
+        PT(DisplayRegion) dr = win->get_active_display_region(i);
         if (dr != nullptr) {
           PT(SceneSetup) scene_setup;
           PT(CullResult) cull_result;
@@ -1659,7 +1659,7 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
           }
           int num_display_regions = win->get_num_active_display_regions();
           for (int i = 0; i < num_display_regions; ++i) {
-            DisplayRegion *dr = win->get_active_display_region(i);
+            PT(DisplayRegion) dr = win->get_active_display_region(i);
             if (dr != nullptr) {
               do_draw(win, gsg, dr, current_thread);
             }

+ 13 - 13
panda/src/display/graphicsOutput.cxx

@@ -697,9 +697,6 @@ void GraphicsOutput::
 remove_all_display_regions() {
   LightMutexHolder holder(_lock);
 
-  CDWriter cdata(_cycler, true);
-  cdata->_active_display_regions_stale = true;
-
   TotalDisplayRegions::iterator dri;
   for (dri = _total_display_regions.begin();
        dri != _total_display_regions.end();
@@ -713,6 +710,12 @@ remove_all_display_regions() {
   }
   _total_display_regions.clear();
   _total_display_regions.push_back(_overlay_display_region);
+
+  OPEN_ITERATE_ALL_STAGES(_cycler) {
+    CDStageWriter cdata(_cycler, pipeline_stage);
+    cdata->_active_display_regions_stale = true;
+  }
+  CLOSE_ITERATE_ALL_STAGES(_cycler);
 }
 
 /**
@@ -740,13 +743,8 @@ set_overlay_display_region(DisplayRegion *display_region) {
  */
 int GraphicsOutput::
 get_num_display_regions() const {
-  determine_display_regions();
-  int result;
-  {
-    LightMutexHolder holder(_lock);
-    result = _total_display_regions.size();
-  }
-  return result;
+  LightMutexHolder holder(_lock);
+  return _total_display_regions.size();
 }
 
 /**
@@ -1504,13 +1502,15 @@ do_remove_display_region(DisplayRegion *display_region) {
     find(_total_display_regions.begin(), _total_display_regions.end(), drp);
   if (dri != _total_display_regions.end()) {
     // Let's aggressively clean up the display region too.
-    CDWriter cdata(_cycler, true);
     display_region->cleanup();
     display_region->_window = nullptr;
     _total_display_regions.erase(dri);
 
-    cdata->_active_display_regions_stale = true;
-
+    OPEN_ITERATE_ALL_STAGES(_cycler) {
+      CDStageWriter cdata(_cycler, pipeline_stage);
+      cdata->_active_display_regions_stale = true;
+    }
+    CLOSE_ITERATE_ALL_STAGES(_cycler);
     return true;
   }
 

+ 1 - 0
panda/src/display/graphicsOutput.h

@@ -394,6 +394,7 @@ protected:
   typedef CycleDataLockedReader<CData> CDLockedReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
+  typedef CycleDataStageWriter<CData> CDStageWriter;
 
 protected:
   int _creation_flags;

+ 14 - 28
panda/src/display/graphicsPipeSelection.cxx

@@ -18,7 +18,6 @@
 #include "load_dso.h"
 #include "config_display.h"
 #include "typeRegistry.h"
-#include "pset.h"
 #include "config_putil.h"
 
 #include <algorithm>
@@ -61,8 +60,8 @@ GraphicsPipeSelection() : _lock("GraphicsPipeSelection") {
 
   // Also get the set of modules named in the various aux-display Config
   // variables.  We'll want to know this when we call load_modules() later.
-  int num_aux = aux_display.get_num_unique_values();
-  for (int i = 0; i < num_aux; i++) {
+  size_t num_aux = aux_display.get_num_unique_values();
+  for (size_t i = 0; i < num_aux; ++i) {
     string name = aux_display.get_unique_value(i);
     if (name != _default_display_module) {
       _display_modules.push_back(name);
@@ -124,9 +123,7 @@ print_pipe_types() const {
 
   LightMutexHolder holder(_lock);
   nout << "Known pipe types:" << std::endl;
-  PipeTypes::const_iterator pi;
-  for (pi = _pipe_types.begin(); pi != _pipe_types.end(); ++pi) {
-    const PipeType &pipe_type = (*pi);
+  for (const PipeType &pipe_type : _pipe_types) {
     nout << "  " << pipe_type._type << "\n";
   }
   if (_display_modules.empty()) {
@@ -187,11 +184,9 @@ make_pipe(const string &type_name, const string &module_name) {
 PT(GraphicsPipe) GraphicsPipeSelection::
 make_pipe(TypeHandle type) {
   LightMutexHolder holder(_lock);
-  PipeTypes::const_iterator ti;
 
   // First, look for an exact match of the requested type.
-  for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
-    const PipeType &ptype = (*ti);
+  for (const PipeType &ptype : _pipe_types) {
     if (ptype._type == type) {
       // Here's an exact match.
       PT(GraphicsPipe) pipe = (*ptype._constructor)();
@@ -202,8 +197,7 @@ make_pipe(TypeHandle type) {
   }
 
   // Now look for a more-specific type.
-  for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
-    const PipeType &ptype = (*ti);
+  for (const PipeType &ptype : _pipe_types) {
     if (ptype._type.is_derived_from(type)) {
       // Here's an approximate match.
       PT(GraphicsPipe) pipe = (*ptype._constructor)();
@@ -215,8 +209,7 @@ make_pipe(TypeHandle type) {
 
   // Couldn't find any match; load the default module and try again.
   load_default_module();
-  for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
-    const PipeType &ptype = (*ti);
+  for (const PipeType &ptype : _pipe_types) {
     if (ptype._type.is_derived_from(type)) {
       // Here's an approximate match.
       PT(GraphicsPipe) pipe = (*ptype._constructor)();
@@ -260,13 +253,11 @@ make_default_pipe() {
   load_default_module();
 
   LightMutexHolder holder(_lock);
-  PipeTypes::const_iterator ti;
 
   if (!_default_pipe_name.empty()) {
     // First, look for an exact match of the default type name from the
     // Configrc file (excepting case and hyphenunderscore).
-    for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
-      const PipeType &ptype = (*ti);
+    for (const PipeType &ptype : _pipe_types) {
       if (cmp_nocase_uh(ptype._type.get_name(), _default_pipe_name) == 0) {
         // Here's an exact match.
         PT(GraphicsPipe) pipe = (*ptype._constructor)();
@@ -278,8 +269,7 @@ make_default_pipe() {
 
     // No match; look for a substring match.
     string preferred_name = downcase(_default_pipe_name);
-    for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
-      const PipeType &ptype = (*ti);
+    for (const PipeType &ptype : _pipe_types) {
       string ptype_name = downcase(ptype._type.get_name());
       if (ptype_name.find(preferred_name) != string::npos) {
         // Here's a substring match.
@@ -292,8 +282,7 @@ make_default_pipe() {
   }
 
   // Couldn't find a matching pipe type; choose the first one on the list.
-  for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
-    const PipeType &ptype = (*ti);
+  for (const PipeType &ptype : _pipe_types) {
     PT(GraphicsPipe) pipe = (*ptype._constructor)();
     if (pipe != nullptr) {
       return pipe;
@@ -310,9 +299,8 @@ make_default_pipe() {
  */
 void GraphicsPipeSelection::
 load_aux_modules() {
-  DisplayModules::iterator di;
-  for (di = _display_modules.begin(); di != _display_modules.end(); ++di) {
-    load_named_module(*di);
+  for (const string &module : _display_modules) {
+    load_named_module(module);
   }
 
   _display_modules.clear();
@@ -337,9 +325,7 @@ add_pipe_type(TypeHandle type, PipeConstructorFunc *func) {
 
   // First, make sure we don't already have a GraphicsPipe of this type.
   LightMutexHolder holder(_lock);
-  PipeTypes::const_iterator ti;
-  for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
-    const PipeType &ptype = (*ti);
+  for (const PipeType &ptype : _pipe_types) {
     if (ptype._type == type) {
       display_cat->warning()
         << "Attempt to register GraphicsPipe type " << type
@@ -375,8 +361,8 @@ do_load_default_module() {
   load_named_module(_default_display_module);
 
   DisplayModules::iterator di =
-    find(_display_modules.begin(), _display_modules.end(),
-         _default_display_module);
+    std::find(_display_modules.begin(), _display_modules.end(),
+              _default_display_module);
   if (di != _display_modules.end()) {
     _display_modules.erase(di);
   }

+ 59 - 40
panda/src/display/graphicsStateGuardian.cxx

@@ -24,7 +24,6 @@
 #include "renderBuffer.h"
 #include "light.h"
 #include "planeNode.h"
-#include "ambientLight.h"
 #include "throw_event.h"
 #include "clockObject.h"
 #include "pStatTimer.h"
@@ -60,7 +59,6 @@
 #include "fogAttrib.h"
 #include "config_pstatclient.h"
 
-#include <algorithm>
 #include <limits.h>
 
 using std::string;
@@ -932,12 +930,12 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
   }
   case Shader::SMO_frame_time: {
     PN_stdfloat time = ClockObject::get_global_clock()->get_frame_time();
-    t = LMatrix4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, time, time, time, time);
+    t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, time, time, time, time);
     return &t;
   }
   case Shader::SMO_frame_delta: {
     PN_stdfloat dt = ClockObject::get_global_clock()->get_dt();
-    t = LMatrix4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, dt, dt, dt, dt);
+    t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, dt, dt, dt, dt);
     return &t;
   }
   case Shader::SMO_texpad_x: {
@@ -949,7 +947,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     double cx = (sx * 0.5) / tex->get_x_size();
     double cy = (sy * 0.5) / tex->get_y_size();
     double cz = (sz * 0.5) / tex->get_z_size();
-    t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,cx,cy,cz,0);
+    t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, cx, cy, cz, 0);
     return &t;
   }
   case Shader::SMO_texpix_x: {
@@ -958,7 +956,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     double px = 1.0 / tex->get_x_size();
     double py = 1.0 / tex->get_y_size();
     double pz = 1.0 / tex->get_z_size();
-    t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,px,py,pz,0);
+    t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, px, py, pz, 0);
     return &t;
   }
   case Shader::SMO_attr_material: {
@@ -966,7 +964,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       _target_rs->get_attrib_def(MaterialAttrib::get_class_slot());
     // Material matrix contains AMBIENT, DIFFUSE, EMISSION, SPECULAR+SHININESS
     if (target_material->is_off()) {
-      t = LMatrix4(1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0);
+      t.set(1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
       return &t;
     }
     Material *m = target_material->get_material();
@@ -975,17 +973,17 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     LVecBase4 const &emm = m->get_emission();
     LVecBase4 spc = m->get_specular();
     spc[3] = m->get_shininess();
-    t = LMatrix4(amb[0],amb[1],amb[2],amb[3],
-                  dif[0],dif[1],dif[2],dif[3],
-                  emm[0],emm[1],emm[2],emm[3],
-                  spc[0],spc[1],spc[2],spc[3]);
+    t.set(amb[0], amb[1], amb[2], amb[3],
+          dif[0], dif[1], dif[2], dif[3],
+          emm[0], emm[1], emm[2], emm[3],
+          spc[0], spc[1], spc[2], spc[3]);
     return &t;
   }
   case Shader::SMO_attr_material2: {
     const MaterialAttrib *target_material = (const MaterialAttrib *)
       _target_rs->get_attrib_def(MaterialAttrib::get_class_slot());
     if (target_material->is_off()) {
-      t = LMatrix4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
+      t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
       return &t;
     }
     Material *m = target_material->get_material();
@@ -1000,7 +998,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       return &LMatrix4::ones_mat();
     }
     LVecBase4 c = target_color->get_color();
-    t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,c[0],c[1],c[2],c[3]);
+    t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, c[0], c[1], c[2], c[3]);
     return &t;
   }
   case Shader::SMO_attr_colorscale: {
@@ -1010,7 +1008,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       return &LMatrix4::ones_mat();
     }
     LVecBase4 cs = target_color->get_scale();
-    t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,cs[0],cs[1],cs[2],cs[3]);
+    t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, cs[0], cs[1], cs[2], cs[3]);
     return &t;
   }
   case Shader::SMO_attr_fog: {
@@ -1022,7 +1020,8 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     }
     PN_stdfloat start, end;
     fog->get_linear_range(start, end);
-    t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,fog->get_exp_density(),start,end,1.0f/(end-start));
+    t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+          fog->get_exp_density(), start, end, 1.0f / (end - start));
     return &t;
   }
   case Shader::SMO_attr_fogcolor: {
@@ -1033,7 +1032,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       return &LMatrix4::ones_mat();
     }
     LVecBase4 c = fog->get_color();
-    t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,c[0],c[1],c[2],c[3]);
+    t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, c[0], c[1], c[2], c[3]);
     return &t;
   }
   case Shader::SMO_alight_x: {
@@ -1042,7 +1041,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     AmbientLight *lt;
     DCAST_INTO_R(lt, np.node(), &LMatrix4::zeros_mat());
     LColor const &c = lt->get_color();
-    t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,c[0],c[1],c[2],c[3]);
+    t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, c[0], c[1], c[2], c[3]);
     return &t;
   }
   case Shader::SMO_satten_x: {
@@ -1052,7 +1051,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     DCAST_INTO_R(lt, np.node(), &LMatrix4::ones_mat());
     LVecBase3 const &a = lt->get_attenuation();
     PN_stdfloat x = lt->get_exponent();
-    t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,a[0],a[1],a[2],x);
+    t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a[0], a[1], a[2], x);
     return &t;
   }
   case Shader::SMO_dlight_x: {
@@ -1069,7 +1068,10 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     d.normalize();
     LVecBase3 h = d + LVecBase3(0,-1,0);
     h.normalize();
-    t = LMatrix4(c[0],c[1],c[2],c[3],s[0],s[1],s[2],c[3],d[0],d[1],d[2],0,h[0],h[1],h[2],0);
+    t.set(c[0], c[1], c[2], c[3],
+          s[0], s[1], s[2], c[3],
+          d[0], d[1], d[2], 0,
+          h[0], h[1], h[2], 0);
     return &t;
   }
   case Shader::SMO_plight_x: {
@@ -1087,7 +1089,10 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     Lens *lens = lt->get_lens(0);
     PN_stdfloat lnear = lens->get_near();
     PN_stdfloat lfar = lens->get_far();
-    t = LMatrix4(c[0],c[1],c[2],c[3],s[0],s[1],s[2],s[3],p[0],p[1],p[2],lnear,a[0],a[1],a[2],lfar);
+    t.set(c[0], c[1], c[2], c[3],
+          s[0], s[1], s[2], s[3],
+          p[0], p[1], p[2], lnear,
+          a[0], a[1], a[2], lfar);
     return &t;
   }
   case Shader::SMO_slight_x: {
@@ -1105,7 +1110,10 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       _scene_setup->get_world_transform()->get_mat();
     LVecBase3 p = t.xform_point(lens->get_nodal_point());
     LVecBase3 d = -(t.xform_vec(lens->get_view_vector()));
-    t = LMatrix4(c[0],c[1],c[2],c[3],s[0],s[1],s[2],s[3],p[0],p[1],p[2],0,d[0],d[1],d[2],cutoff);
+    t.set(c[0], c[1], c[2], c[3],
+          s[0], s[1], s[2], s[3],
+          p[0], p[1], p[2], 0,
+          d[0], d[1], d[2], cutoff);
     return &t;
   }
   case Shader::SMO_light_ambient: {
@@ -1149,7 +1157,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     if (_target_rs->get_attrib(ta) && _target_rs->get_attrib(tma) &&
         index < ta->get_num_on_stages()) {
       LVecBase3 scale = tma->get_transform(ta->get_on_stage(index))->get_scale();
-      t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,scale[0],scale[1],scale[2],0);
+      t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, scale[0], scale[1], scale[2], 0);
       return &t;
     } else {
       return &LMatrix4::ident_mat();
@@ -1173,7 +1181,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
         index < ta->get_num_on_stages()) {
       TextureStage *ts = ta->get_on_stage(index);
       PN_stdfloat v = (ta->get_on_texture(ts)->get_format() == Texture::F_alpha);
-      t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,v,v,v,0);
+      t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, v, v, v, 0);
       return &t;
     } else {
       return &LMatrix4::zeros_mat();
@@ -1185,7 +1193,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     const PlaneNode *plane_node;
     DCAST_INTO_R(plane_node, np.node(), &LMatrix4::zeros_mat());
     LPlane p = plane_node->get_plane();
-    t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,p[0],p[1],p[2],p[3]);
+    t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, p[0], p[1], p[2], p[3]);
     return &t;
   }
   case Shader::SMO_clipplane_x: {
@@ -1235,10 +1243,10 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
   case Shader::SMO_vec_constant_x: {
     const LVecBase4 &input = _target_shader->get_shader_input_vector(name);
     const PN_stdfloat *data = input.get_data();
-    t = LMatrix4(data[0],data[1],data[2],data[3],
-                 data[0],data[1],data[2],data[3],
-                 data[0],data[1],data[2],data[3],
-                 data[0],data[1],data[2],data[3]);
+    t.set(data[0], data[1], data[2], data[3],
+          data[0], data[1], data[2], data[3],
+          data[0], data[1], data[2], data[3],
+          data[0], data[1], data[2], data[3]);
     return &t;
   }
   case Shader::SMO_world_to_view: {
@@ -1394,10 +1402,10 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       // There is an input specifying precisely this whole thing, with dot and
       // all.  Support this, even if only for backward compatibility.
       const LVecBase4 &data = _target_shader->get_shader_input_vector(name);
-      t = LMatrix4(data[0],data[1],data[2],data[3],
-                   data[0],data[1],data[2],data[3],
-                   data[0],data[1],data[2],data[3],
-                   data[0],data[1],data[2],data[3]);
+      t.set(data[0], data[1], data[2], data[3],
+            data[0], data[1], data[2], data[3],
+            data[0], data[1], data[2], data[3],
+            data[0], data[1], data[2], data[3]);
       return &t;
     }
 
@@ -1578,11 +1586,11 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
 
   } else if (attrib == IN_position) {
     if (np.is_empty()) {
-      t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0);
+      t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
       return &t;
     } else if (node->is_ambient_light()) {
       // Ambient light has no position.
-      t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
+      t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
       return &t;
     } else if (node->is_of_type(DirectionalLight::get_class_type())) {
       DirectionalLight *light;
@@ -1591,7 +1599,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
       CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
       LVector3 dir = -(light->get_direction() * transform->get_mat());
       dir *= _scene_setup->get_cs_world_transform()->get_mat();
-      t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,dir[0],dir[1],dir[2],0);
+      t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, dir[0], dir[1], dir[2], 0);
       return &t;
     } else {
       LightLensNode *light;
@@ -1611,11 +1619,11 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
 
   } else if (attrib == IN_halfVector) {
     if (np.is_empty()) {
-      t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0);
+      t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
       return &t;
     } else if (node->is_ambient_light()) {
       // Ambient light has no half-vector.
-      t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
+      t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
       return &t;
     } else if (node->is_of_type(DirectionalLight::get_class_type())) {
       DirectionalLight *light;
@@ -1627,7 +1635,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
       dir.normalize();
       dir += LVector3(0, 0, 1);
       dir.normalize();
-      t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,dir[0],dir[1],dir[2],1);
+      t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, dir[0], dir[1], dir[2], 1);
       return &t;
     } else {
       LightLensNode *light;
@@ -1644,7 +1652,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
       pos.normalize();
       pos += LVector3(0, 0, 1);
       pos.normalize();
-      t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,pos[0],pos[1],pos[2],1);
+      t.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pos[0],pos[1],pos[2], 1);
       return &t;
     }
 
@@ -2722,7 +2730,7 @@ do_issue_color_scale() {
   }
 
   if (_alpha_scale_via_texture && !_has_scene_graph_color &&
-      target_color_scale->has_alpha_scale()) {
+      _vertex_colors_enabled && target_color_scale->has_alpha_scale()) {
     // This color scale will set a special texture--so again, clear the
     // texture.
     _state_mask.clear_bit(TextureAttrib::get_class_slot());
@@ -3168,6 +3176,17 @@ determine_light_color_scale() {
                                 _scene_graph_color[3] * _current_color_scale[3]);
     }
 
+  } else if (!_vertex_colors_enabled) {
+    // We don't have a scene graph color, but we don't want to enable vertex
+    // colors either, so we still need to force a white material color in
+    // absence of any other color.
+    _has_material_force_color = true;
+    _material_force_color.set(1.0f, 1.0f, 1.0f, 1.0f);
+    _light_color_scale.set(1.0f, 1.0f, 1.0f, 1.0f);
+    if (!_color_blend_involves_color_scale && _color_scale_enabled) {
+      _material_force_color.componentwise_mult(_current_color_scale);
+    }
+
   } else {
     // Otherise, leave the materials alone, but we might still scale the
     // lights.

+ 1 - 0
panda/src/display/graphicsWindowProc.h

@@ -32,6 +32,7 @@ class GraphicsWindow;
 class EXPCL_PANDA_DISPLAY GraphicsWindowProc {
 public:
   GraphicsWindowProc();
+  virtual ~GraphicsWindowProc() = default;
 #if defined(__WIN32__) || defined(_WIN32)
   virtual LONG wnd_proc(GraphicsWindow* graphicsWindow, HWND hwnd,
                         UINT msg, WPARAM wparam, LPARAM lparam);

+ 0 - 1
panda/src/display/p3display_composite2.cxx

@@ -1,5 +1,4 @@
 #include "graphicsPipeSelection.cxx"
-#include "graphicsStateGuardian.cxx"
 #include "graphicsThreadingModel.cxx"
 #include "graphicsWindow.cxx"
 #include "graphicsWindowProc.cxx"

+ 10 - 20
panda/src/display/standardMunger.cxx

@@ -12,10 +12,14 @@
  */
 
 #include "standardMunger.h"
-#include "renderState.h"
-#include "graphicsStateGuardian.h"
+
 #include "config_gobj.h"
+
 #include "displayRegion.h"
+#include "graphicsStateGuardian.h"
+#include "lightAttrib.h"
+#include "materialAttrib.h"
+#include "renderState.h"
 
 TypeHandle StandardMunger::_type_handle;
 
@@ -55,24 +59,10 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state,
     const ColorScaleAttrib *color_scale_attrib;
 
     if (state->get_attrib(color_attrib) &&
-        color_attrib->get_color_type() == ColorAttrib::T_flat) {
-
-      if (!get_gsg()->get_color_scale_via_lighting()) {
-        // We only need to munge the color directly if the GSG says it can't
-        // cheat the color via lighting (presumably, in this case, by applying
-        // a material).
-        _color = color_attrib->get_color();
-        if (state->get_attrib(color_scale_attrib) &&
-            color_scale_attrib->has_scale()) {
-          const LVecBase4 &cs = color_scale_attrib->get_scale();
-          _color.set(_color[0] * cs[0],
-                     _color[1] * cs[1],
-                     _color[2] * cs[2],
-                     _color[3] * cs[3]);
-        }
-        _munge_color = true;
-        _should_munge_state = true;
-      }
+        color_attrib->get_color_type() != ColorAttrib::T_vertex) {
+
+      // In this case, we don't need to munge anything as we can apply the
+      // color and color scale via glColor4f.
 
     } else if (state->get_attrib(color_scale_attrib) &&
                color_scale_attrib->has_scale()) {

+ 3 - 2
panda/src/display/standardMunger.h

@@ -51,12 +51,13 @@ private:
   NumericType _numeric_type;
   Contents _contents;
 
-  bool _munge_color;
-  bool _munge_color_scale;
   bool _auto_shader;
   bool _shader_skinning;
   bool _remove_material;
 
+protected:
+  bool _munge_color;
+  bool _munge_color_scale;
   LColor _color;
   LVecBase4 _color_scale;
 

+ 2 - 0
panda/src/display/subprocessWindow.cxx

@@ -18,6 +18,8 @@
 #include "graphicsEngine.h"
 #include "config_display.h"
 #include "nativeWindowHandle.h"
+#include "mouseButton.h"
+#include "throw_event.h"
 
 using std::string;
 

+ 3 - 5
panda/src/distort/projectionScreen.cxx

@@ -484,8 +484,7 @@ recompute_geom(Geom *geom, const LMatrix4 &rel_mat) {
   const LMatrix4 &to_uv = _invert_uvs ? lens_to_uv_inverted : lens_to_uv;
 
   // Iterate through all the vertices in the Geom.
-  CPT(GeomVertexData) vdata = geom->get_vertex_data(current_thread);
-  vdata = vdata->animate_vertices(true, current_thread);
+  CPT(GeomVertexData) vdata = geom->get_animated_vertex_data(true, current_thread);
 
   CPT(GeomVertexFormat) vformat = vdata->get_format();
   if (!vformat->has_column(_texcoord_name) || (_texcoord_3d && vformat->get_column(_texcoord_name)->get_num_components() < 3)) {
@@ -507,7 +506,7 @@ recompute_geom(Geom *geom, const LMatrix4 &rel_mat) {
   PT(GeomVertexData) modify_vdata = geom->modify_vertex_data();
 
   // Maybe the vdata has animation that we should consider.
-  CPT(GeomVertexData) animated_vdata = geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread);
+  CPT(GeomVertexData) animated_vdata = geom->get_animated_vertex_data(true, current_thread);
 
   GeomVertexWriter texcoord(modify_vdata, _texcoord_name, current_thread);
   GeomVertexWriter color(modify_vdata, current_thread);
@@ -674,9 +673,8 @@ make_mesh_geom(const Geom *geom, Lens *lens, LMatrix4 &rel_mat) {
 
   Thread *current_thread = Thread::get_current_thread();
   PT(Geom) new_geom = geom->make_copy();
+  new_geom->set_vertex_data(new_geom->get_animated_vertex_data(false, current_thread));
   PT(GeomVertexData) vdata = new_geom->modify_vertex_data();
-  new_geom->set_vertex_data(vdata->animate_vertices(false, current_thread));
-  vdata = new_geom->modify_vertex_data();
   GeomVertexRewriter vertex(vdata, InternalName::get_vertex());
   while (!vertex.is_at_end()) {
     LVertex vert = vertex.get_data3();

+ 0 - 31
panda/src/dxgsg9/dxGeomMunger9.I

@@ -10,34 +10,3 @@
  * @author drose
  * @date 2005-03-11
  */
-
-/**
- *
- */
-INLINE DXGeomMunger9::
-DXGeomMunger9(GraphicsStateGuardian *gsg, const RenderState *state) :
-  StandardMunger(gsg, state, 1, NT_packed_dabc, C_color),
-  _texture(nullptr),
-  _tex_gen(nullptr)
-{
-  const TextureAttrib *texture = nullptr;
-  const TexGenAttrib *tex_gen = nullptr;
-  state->get_attrib(texture);
-  state->get_attrib(tex_gen);
-  _texture = texture;
-  _tex_gen = tex_gen;
-
-  _filtered_texture = nullptr;
-  _reffed_filtered_texture = false;
-  if (texture != nullptr) {
-    _filtered_texture = texture->filter_to_max(gsg->get_max_texture_stages());
-    if (_filtered_texture != texture) {
-      _filtered_texture->ref();
-      _reffed_filtered_texture = true;
-    }
-  }
-  // Set a callback to unregister ourselves when either the Texture or the
-  // TexGen object gets deleted.
-  _texture.add_callback(this);
-  _tex_gen.add_callback(this);
-}

+ 60 - 0
panda/src/dxgsg9/dxGeomMunger9.cxx

@@ -19,6 +19,66 @@
 GeomMunger *DXGeomMunger9::_deleted_chain = nullptr;
 TypeHandle DXGeomMunger9::_type_handle;
 
+/**
+ *
+ */
+DXGeomMunger9::
+DXGeomMunger9(GraphicsStateGuardian *gsg, const RenderState *state) :
+  StandardMunger(gsg, state, 1, NT_packed_dabc, C_color),
+  _texture(nullptr),
+  _tex_gen(nullptr)
+{
+  const TextureAttrib *texture = nullptr;
+  const TexGenAttrib *tex_gen = nullptr;
+  state->get_attrib(texture);
+  state->get_attrib(tex_gen);
+  _texture = texture;
+  _tex_gen = tex_gen;
+
+  if (!gsg->get_color_scale_via_lighting()) {
+    // We might need to munge the colors, if we are overriding the vertex
+    // colors and the GSG can't cheat the color via lighting.
+
+    const ColorAttrib *color_attrib;
+    const ShaderAttrib *shader_attrib;
+    state->get_attrib_def(shader_attrib);
+
+    if (!shader_attrib->auto_shader() &&
+        shader_attrib->get_shader() == nullptr &&
+        state->get_attrib(color_attrib) &&
+        color_attrib->get_color_type() != ColorAttrib::T_vertex) {
+
+      if (color_attrib->get_color_type() == ColorAttrib::T_off) {
+        _color.set(1, 1, 1, 1);
+      } else {
+        _color = color_attrib->get_color();
+      }
+
+      const ColorScaleAttrib *color_scale_attrib;
+      if (state->get_attrib(color_scale_attrib) &&
+          color_scale_attrib->has_scale()) {
+        _color.componentwise_mult(color_scale_attrib->get_scale());
+      }
+      _munge_color = true;
+      _should_munge_state = true;
+    }
+  }
+
+  _filtered_texture = nullptr;
+  _reffed_filtered_texture = false;
+  if (texture != nullptr) {
+    _filtered_texture = texture->filter_to_max(gsg->get_max_texture_stages());
+    if (_filtered_texture != texture) {
+      _filtered_texture->ref();
+      _reffed_filtered_texture = true;
+    }
+  }
+  // Set a callback to unregister ourselves when either the Texture or the
+  // TexGen object gets deleted.
+  _texture.add_callback(this);
+  _tex_gen.add_callback(this);
+}
+
 /**
  *
  */

+ 1 - 1
panda/src/dxgsg9/dxGeomMunger9.h

@@ -28,7 +28,7 @@
  */
 class EXPCL_PANDADX DXGeomMunger9 : public StandardMunger, public WeakPointerCallback {
 public:
-  INLINE DXGeomMunger9(GraphicsStateGuardian *gsg, const RenderState *state);
+  DXGeomMunger9(GraphicsStateGuardian *gsg, const RenderState *state);
   virtual ~DXGeomMunger9();
   ALLOC_DELETED_CHAIN(DXGeomMunger9);
 

+ 47 - 7
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -140,6 +140,7 @@ DXGraphicsStateGuardian9(GraphicsEngine *engine, GraphicsPipe *pipe) :
 
   _last_fvf = 0;
   _num_bound_streams = 0;
+  _white_vbuffer = nullptr;
 
   _vertex_shader_version_major = 0;
   _vertex_shader_version_minor = 0;
@@ -798,9 +799,9 @@ clear(DrawableRegion *clearable) {
     main_flags |=  D3DCLEAR_TARGET;
   }
 
-  if (clearable->get_clear_depth_active()) {
+  if (clearable->get_clear_depth_active() &&
+      _screen->_presentation_params.EnableAutoDepthStencil) {
     aux_flags |=  D3DCLEAR_ZBUFFER;
-    nassertv(_screen->_presentation_params.EnableAutoDepthStencil);
   }
 
   if (clearable->get_clear_stencil_active()) {
@@ -4545,6 +4546,11 @@ reset_d3d_device(D3DPRESENT_PARAMETERS *presentation_params,
     release_all_vertex_buffers();
     release_all_index_buffers();
 
+    if (_white_vbuffer != nullptr) {
+      _white_vbuffer->Release();
+      _white_vbuffer = nullptr;
+    }
+
     // must be called before reset
     Thread *current_thread = Thread::get_current_thread();
     _prepared_objects->begin_frame(this, current_thread);
@@ -5234,9 +5240,9 @@ calc_fb_properties(DWORD cformat, DWORD dformat,
 #define GAMMA_1 (255.0 * 256.0)
 
 static bool _gamma_table_initialized = false;
-static unsigned short _orignial_gamma_table [256 * 3];
+static unsigned short _original_gamma_table [256 * 3];
 
-void _create_gamma_table (PN_stdfloat gamma, unsigned short *original_red_table, unsigned short *original_green_table, unsigned short *original_blue_table, unsigned short *red_table, unsigned short *green_table, unsigned short *blue_table) {
+void _create_gamma_table_dx9 (PN_stdfloat gamma, unsigned short *original_red_table, unsigned short *original_green_table, unsigned short *original_blue_table, unsigned short *red_table, unsigned short *green_table, unsigned short *blue_table) {
   int i;
   double gamma_correction;
 
@@ -5298,7 +5304,7 @@ get_gamma_table(void) {
     HDC hdc = GetDC(nullptr);
 
     if (hdc) {
-      if (GetDeviceGammaRamp (hdc, (LPVOID) _orignial_gamma_table)) {
+      if (GetDeviceGammaRamp (hdc, (LPVOID) _original_gamma_table)) {
         _gamma_table_initialized = true;
         get = true;
       }
@@ -5323,10 +5329,10 @@ static_set_gamma(bool restore, PN_stdfloat gamma) {
     unsigned short ramp [256 * 3];
 
     if (restore && _gamma_table_initialized) {
-      _create_gamma_table (gamma, &_orignial_gamma_table [0], &_orignial_gamma_table [256], &_orignial_gamma_table [512], &ramp [0], &ramp [256], &ramp [512]);
+      _create_gamma_table_dx9 (gamma, &_original_gamma_table [0], &_original_gamma_table [256], &_original_gamma_table [512], &ramp [0], &ramp [256], &ramp [512]);
     }
     else {
-      _create_gamma_table (gamma, 0, 0, 0, &ramp [0], &ramp [256], &ramp [512]);
+      _create_gamma_table_dx9 (gamma, 0, 0, 0, &ramp [0], &ramp [256], &ramp [512]);
     }
 
     if (SetDeviceGammaRamp (hdc, ramp)) {
@@ -5404,6 +5410,40 @@ set_cg_device(LPDIRECT3DDEVICE9 cg_device) {
 #endif // HAVE_CG
 }
 
+/**
+ * Returns a vertex buffer containing only a full-white color.
+ */
+LPDIRECT3DVERTEXBUFFER9 DXGraphicsStateGuardian9::
+get_white_vbuffer() {
+  if (_white_vbuffer != nullptr) {
+    return _white_vbuffer;
+  }
+
+  LPDIRECT3DVERTEXBUFFER9 vbuffer;
+  HRESULT hr;
+  hr = _screen->_d3d_device->CreateVertexBuffer(sizeof(D3DCOLOR), D3DUSAGE_WRITEONLY, D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &vbuffer, nullptr);
+
+  if (FAILED(hr)) {
+    dxgsg9_cat.error()
+      << "CreateVertexBuffer failed" << D3DERRORSTRING(hr);
+    return nullptr;
+  }
+
+  D3DCOLOR *local_pointer;
+  hr = vbuffer->Lock(0, sizeof(D3DCOLOR), (void **) &local_pointer, D3DLOCK_DISCARD);
+  if (FAILED(hr)) {
+    dxgsg9_cat.error()
+      << "VertexBuffer::Lock failed" << D3DERRORSTRING(hr);
+    return false;
+  }
+
+  *local_pointer = D3DCOLOR_ARGB(255, 255, 255, 255);
+
+  vbuffer->Unlock();
+  _white_vbuffer = vbuffer;
+  return vbuffer;
+}
+
 typedef std::string KEY;
 
 typedef struct _KEY_ELEMENT

+ 2 - 6
panda/src/dxgsg9/dxGraphicsStateGuardian9.h

@@ -168,6 +168,7 @@ public:
   static void set_cg_device(LPDIRECT3DDEVICE9 cg_device);
   virtual bool get_supports_cg_profile(const std::string &name) const;
 
+  LPDIRECT3DVERTEXBUFFER9 get_white_vbuffer();
 
 protected:
   void do_issue_transform();
@@ -274,12 +275,6 @@ protected:
 
   RenderBuffer::Type _cur_read_pixel_buffer;  // source for copy_pixel_buffer operation
 
-  PN_stdfloat _material_ambient;
-  PN_stdfloat _material_diffuse;
-  PN_stdfloat _material_specular;
-  PN_stdfloat _material_shininess;
-  PN_stdfloat _material_emission;
-
   enum DxgsgFogType {
     None,
     PerVertexFog=D3DRS_FOGVERTEXMODE,
@@ -320,6 +315,7 @@ protected:
 
   DWORD _last_fvf;
   int _num_bound_streams;
+  LPDIRECT3DVERTEXBUFFER9 _white_vbuffer;
 
   // Cache the data necessary to bind each particular light each frame, so if
   // we bind a given light multiple times, we only have to compute its data

+ 0 - 269
panda/src/dxgsg9/dxInput9.cxx

@@ -1,269 +0,0 @@
-/**
- * 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."
- *
- * @file dxInput9.cxx
- * @author angelina jolie
- * @date 1999-10-07
- */
-
-#include "config_wdxdisplay9.h"
-#include "dxInput9.h"
-
-#define AXIS_RESOLUTION 2000   // use this many levels of resolution by default (could be more if needed and device supported it)
-#define AXIS_RANGE_CENTERED    // if defined, axis range is centered on 0, instead of starting on 0
-
-using std::endl;
-
-BOOL CALLBACK EnumGameCtrlsCallback( const DIDEVICEINSTANCE* pdidInstance,
-                                     VOID* pContext ) {
-    DI_DeviceInfos *pDevInfos = (DI_DeviceInfos *)pContext;
-
-    (*pDevInfos).push_back(*pdidInstance);
-
-    if(wdxdisplay_cat.is_debug())
-        wdxdisplay_cat.debug() << "Found DevType 0x" << (void*)pdidInstance->dwDevType << ": " << pdidInstance->tszInstanceName << ": " << pdidInstance->tszProductName <<endl;
-
-    return DIENUM_CONTINUE;
-}
-
-extern BOOL CALLBACK EnumObjectsCallbackJoystick(const DIDEVICEOBJECTINSTANCE* pdidoi,VOID* pContext);
-
-DInput9Info::DInput9Info() {
-    _pDInput9 = nullptr;
-    _hDInputDLL = nullptr;
-    _JoystickPollTimer = nullptr;
-}
-
-DInput9Info::~DInput9Info() {
-  for(UINT i=0;i<_DeviceList.size();i++) {
-      _DeviceList[i]->Unacquire();
-      SAFE_RELEASE(_DeviceList[i]);
-  }
-
-  // bugbug: need to handle this if(_JoystickPollTimer!=NULL) KillTimer(...)
-
-  SAFE_RELEASE(_pDInput9);
-  if(_hDInputDLL) {
-      FreeLibrary(_hDInputDLL);
-      _hDInputDLL=nullptr;
-  }
-}
-
-bool DInput9Info::InitDirectInput() {
-    HRESULT hr;
-
-    // assumes dx9 exists use dynamic load so non-dinput programs don't have
-    // to load dinput
-    #define DLLNAME "dinput9.dll"
-    #define DINPUTCREATE "DirectInput9Create"
-
-    HINSTANCE _hDInputDLL = LoadLibrary(DLLNAME);
-    if(_hDInputDLL == 0) {
-        wdxdisplay_cat.fatal() << "LoadLibrary(" << DLLNAME <<") failed!, error=" << GetLastError() << endl;
-        exit(1);
-    }
-
-    typedef HRESULT (WINAPI * LPDIRECTINPUT9CREATE)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
-    LPDIRECTINPUT9CREATE pDInputCreate9;
-
-    pDInputCreate9 = (LPDIRECTINPUT9CREATE) GetProcAddress(_hDInputDLL,DINPUTCREATE);
-    if(pDInputCreate9 == nullptr) {
-        wdxdisplay_cat.fatal() << "GetProcAddr failed for " << DINPUTCREATE << endl;
-        exit(1);
-    }
-
-    // Register with the DirectInput subsystem and get a pointer to a
-    // IDirectInput interface we can use.  Create a DInput object
-    if( FAILED( hr = (*pDInputCreate9)(GetModuleHandle(nullptr), DIRECTINPUT_VERSION,
-                                         IID_IDirectInput9, (VOID**)&_pDInput9, nullptr ) ) ) {
-        wdxdisplay_cat.error() << DINPUTCREATE << "failed" << D3DERRORSTRING(hr);
-        return false;
-    }
-
-    // enum all the joysticks,etc  (but not keybdmouse)
-    if( FAILED( hr = _pDInput9->EnumDevices(DI9DEVCLASS_GAMECTRL,
-                                         EnumGameCtrlsCallback,
-                                         (LPVOID)&_DevInfos, DIEDFL_ATTACHEDONLY ) ) ) {
-        wdxdisplay_cat.error() << "EnumDevices failed" << D3DERRORSTRING(hr);
-        return false;
-    }
-
-    return true;
-}
-
-bool DInput9Info::CreateJoystickOrPad(HWND _window) {
-    bool bFoundDev = false;
-    UINT devnum=0;
-    char *errstr=nullptr;
-
-    // look through the list for the first joystick or gamepad
-    for(;devnum<_DevInfos.size();devnum++) {
-        DWORD devType = GET_DIDEVICE_TYPE(_DevInfos[devnum].dwDevType);
-        if((devType==DI9DEVTYPE_GAMEPAD) ||(devType==DI9DEVTYPE_JOYSTICK)) {
-          bFoundDev=true;
-          break;
-        }
-    }
-
-    if(!bFoundDev) {
-        wdxdisplay_cat.error() << "Cant find an attached Joystick or GamePad!\n";
-        return false;
-    }
-
-    LPDIRECTINPUTDEVICE9 pJoyDevice;
-
-    // Obtain an interface to the enumerated joystick.
-    HRESULT hr = _pDInput9->CreateDevice(_DevInfos[devnum].guidInstance, &pJoyDevice, nullptr );
-    if(FAILED(hr)) {
-        errstr="CreateDevice";
-        goto handle_error;
-    }
-
-    assert(pJoyDevice!=nullptr);
-    _DeviceList.push_back(pJoyDevice);
-
-    // Set the data format to "simple joystick" - a predefined data format A
-    // data format specifies which controls on a device we are interested in,
-    // and how they should be reported.  This tells DInput that we will be
-    // passing a DIJOYSTATE2 structure to
-    // IDirectInputDevice::GetDeviceState().
-    hr = pJoyDevice->SetDataFormat(&c_dfDIJoystick2);
-    if(FAILED(hr)) {
-        errstr="SetDataFormat";
-        goto handle_error;
-    }
-
-    // must be called AFTER SetDataFormat to get all the proper flags
-    DX_DECLARE_CLEAN(DIDEVCAPS, DIDevCaps);
-    hr = pJoyDevice->GetCapabilities(&DIDevCaps);
-    assert(SUCCEEDED(hr));
-
-    _DevCaps.push_back(DIDevCaps);
-
-    if(wdxdisplay_cat.is_debug())
-        wdxdisplay_cat.debug() << "Joy/Pad has " << DIDevCaps.dwAxes << " Axes, " <<  DIDevCaps.dwButtons << " Buttons, " <<  DIDevCaps.dwPOVs << " POVs" << endl;
-
-    // Set the cooperative level to let DInput know how this device should
-    // interact with the system and with other DInput applications.
-    hr = pJoyDevice->SetCooperativeLevel( _window, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
-    if(FAILED(hr)) {
-        errstr="SetCooperativeLevel";
-        goto handle_error;
-    }
-
-    // set the minmax values property for discovered axes.
-    hr = pJoyDevice->EnumObjects(EnumObjectsCallbackJoystick, (LPVOID)pJoyDevice, DIDFT_AXIS);
-    if(FAILED(hr)) {
-        errstr="EnumObjects";
-        goto handle_error;
-    }
-
-    return true;
-
-  handle_error:
-    wdxdisplay_cat.error() << errstr << " failed for (" << _DevInfos[devnum].tszInstanceName << ":" << _DevInfos[devnum].tszProductName << ")" << D3DERRORSTRING(hr);
-    return false;
-}
-
-// ---------------------------------------------------------------------------
-// -- Name: EnumObjectsCallback() Desc: Callback function for enumerating
-// objects (axes, buttons, POVs) on a joystick.  This function enables user
-// interface elements for objects that are found to exist, and scales axes
-// minmax values.  -----------------------------------------------------------
-// ------------------
-BOOL CALLBACK EnumObjectsCallbackJoystick( const DIDEVICEOBJECTINSTANCE* pdidoi,
-                                   VOID* pContext ) {
-
-    LPDIRECTINPUTDEVICE9 pJoyDevice = (LPDIRECTINPUTDEVICE9) pContext;
-    HRESULT hr;
-
-    // For axes that are returned, set the DIPROP_RANGE property for the
-    // enumerated axis in order to scale minmax values.
-    if( pdidoi->dwType & DIDFT_AXIS ) {
-        DIPROPRANGE diprg;
-        diprg.diph.dwSize       = sizeof(DIPROPRANGE);
-        diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
-        diprg.diph.dwHow        = DIPH_BYID;
-        diprg.diph.dwObj        = pdidoi->dwType; // Specify the enumerated axis
-
-     #ifdef AXIS_RANGE_CENTERED
-        diprg.lMin              = -AXIS_RESOLUTION/2;
-        diprg.lMax              = +AXIS_RESOLUTION/2;
-     #else
-        diprg.lMin              = 0;
-        diprg.lMax              = +AXIS_RESOLUTION;
-     #endif
-
-        // Set the range for the axis
-        hr = pJoyDevice->SetProperty( DIPROP_RANGE, &diprg.diph);
-        if(FAILED(hr)) {
-          wdxdisplay_cat.error() << "SetProperty on axis failed" << D3DERRORSTRING(hr);
-          return DIENUM_STOP;
-        }
-    }
-
-    return DIENUM_CONTINUE;
-}
-
-bool DInput9Info::ReadJoystick(int devnum, DIJOYSTATE2 &js) {
-    LPDIRECTINPUTDEVICE9 pJoystick = _DeviceList[devnum];
-    assert(pJoystick!=nullptr);
-    HRESULT hr;
-    char *errstr;
-
-    // Poll the device to read the current state
-
-    hr = pJoystick->Poll();
-
-    if( FAILED(hr) ) {
-        // DInput is telling us that the input stream has been interrupted.
-        // We aren't tracking any state between polls, so we don't have any
-        // special reset that needs to be done.  We just re-acquire and try
-        // again.
-
-        if((hr==DIERR_NOTACQUIRED)||(hr == DIERR_INPUTLOST)) {
-            hr = pJoystick->Acquire();
-
-            if(FAILED(hr)) {
-                if(wdxdisplay_cat.is_spam())
-                   wdxdisplay_cat.spam() << "Acquire failed" << D3DERRORSTRING(hr);
-
-                // hr may be DIERR_OTHERAPPHASPRIO or other errors.  This may
-                // occur when the app is minimized or in the process of
-                // switching, so just try again later
-                return false;
-            }
-
-            hr = pJoystick->Poll();
-            if(FAILED(hr)) {
-                // should never happen!
-                errstr = "Poll after successful Acquire failed";
-                goto handle_error;
-            }
-        } else {
-            errstr =  "Unknown Poll failure";
-            goto handle_error;
-        }
-    }
-
-    // should we make a vector of devstate dataformats to generalize this fn
-    // for all device types?
-
-    // Get the input's device state
-    hr = pJoystick->GetDeviceState( sizeof(DIJOYSTATE2), &js);
-    if(FAILED(hr)) {
-        errstr =  "GetDeviceState failed";
-        goto handle_error;
-    }
-
-    return true;
-
-  handle_error:
-     wdxdisplay_cat.fatal() << errstr << D3DERRORSTRING(hr);
-     return false;
-}

+ 0 - 40
panda/src/dxgsg9/dxInput9.h

@@ -1,40 +0,0 @@
-/**
- * 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."
- *
- * @file dxInput9.h
- * @author blllyjo
- * @date 1999-10-07
- */
-
-#ifndef DXINPUT9_H
-#define DXINPUT9_H
-
-#define DIRECTINPUT_VERSION 0x900
-#include <dinput.h>
-typedef std::vector<DIDEVICEINSTANCE> DI_DeviceInfos;
-typedef std::vector<DIDEVICEOBJECTINSTANCE> DI_DeviceObjInfos;
-
-class DInput9Info {
-public:
- DInput9Info();
- ~DInput9Info();
- bool InitDirectInput();
- bool CreateJoystickOrPad(HWND _window);
- bool ReadJoystick(int devnum, DIJOYSTATE2 &js);
-
- HINSTANCE _hDInputDLL;
- UINT_PTR  _JoystickPollTimer;
- LPDIRECTINPUT8 _pDInput9;
- DI_DeviceInfos _DevInfos;
- // arrays for all created devices.  Should probably put these together in a
- // struct, along with the data fmt info
- std::vector<LPDIRECTINPUTDEVICE8> _DeviceList;
- std::vector<DIDEVCAPS> _DevCaps;
-};
-
-#endif

+ 23 - 0
panda/src/dxgsg9/dxShaderContext9.cxx

@@ -390,6 +390,8 @@ update_shader_vertex_arrays(DXShaderContext9 *prev, GSG *gsg, bool force) {
     // arrays ("streams"), and we repeatedly iterate the parameters to pull
     // out only those for a single stream.
 
+    bool apply_white_color = false;
+
     int number_of_arrays = gsg->_data_reader->get_num_arrays();
     for (int array_index = 0; array_index < number_of_arrays; ++array_index) {
       const GeomVertexArrayDataHandle* array_reader =
@@ -423,6 +425,11 @@ update_shader_vertex_arrays(DXShaderContext9 *prev, GSG *gsg, bool force) {
           }
         }
 
+        if (name == InternalName::get_color() && !gsg->_vertex_colors_enabled) {
+          apply_white_color = true;
+          continue;
+        }
+
         const GeomVertexArrayDataHandle *param_array_reader;
         Geom::NumericType numeric_type;
         int num_values, start, stride;
@@ -435,6 +442,9 @@ update_shader_vertex_arrays(DXShaderContext9 *prev, GSG *gsg, bool force) {
           // shader parameter, which can cause Bad Things to happen so I'd
           // like to at least get a hint as to what's gone wrong.
           dxgsg9_cat.info() << "Geometry contains no data for shader parameter " << *name << "\n";
+          if (name == InternalName::get_color()) {
+            apply_white_color = true;
+          }
           continue;
         }
 
@@ -564,6 +574,19 @@ update_shader_vertex_arrays(DXShaderContext9 *prev, GSG *gsg, bool force) {
 
     _num_bound_streams = number_of_arrays;
 
+    if (apply_white_color) {
+      // The shader needs a vertex color, but vertex colors are disabled.
+      // Bind a vertex buffer containing only one white colour.
+      int array_index = number_of_arrays;
+      LPDIRECT3DVERTEXBUFFER9 vbuffer = gsg->get_white_vbuffer();
+      hr = device->SetStreamSource(array_index, vbuffer, 0, 0);
+      if (FAILED(hr)) {
+        dxgsg9_cat.error() << "SetStreamSource failed" << D3DERRORSTRING(hr);
+      }
+      vertex_element_array->add_diffuse_color_vertex_element(array_index, 0);
+      ++_num_bound_streams;
+    }
+
     if (_vertex_element_array != nullptr &&
         _vertex_element_array->add_end_vertex_element()) {
       if (dxgsg9_cat.is_debug()) {

+ 4 - 1
panda/src/dxgsg9/wdxGraphicsWindow9.cxx

@@ -1229,7 +1229,10 @@ init_resized_window() {
   DWORD flags;
   D3DCOLOR clear_color;
 
-  flags = D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER;
+  flags = D3DCLEAR_TARGET;
+  if (_fb_properties.get_depth_bits() > 0) {
+    flags |= D3DCLEAR_ZBUFFER;
+  }
   clear_color = 0x00000000;
   hr = _wcontext._d3d_device-> Clear (0, nullptr, flags, clear_color, 0.0f, 0);
   if (FAILED(hr)) {

+ 0 - 1
panda/src/dxgsg9/wdxGraphicsWindow9.h

@@ -17,7 +17,6 @@
 #include "pandabase.h"
 #include "winGraphicsWindow.h"
 #include "dxGraphicsStateGuardian9.h"
-#include "dxInput9.h"
 #include "wdxGraphicsPipe9.h"
 
 class wdxGraphicsPipe9;

+ 2 - 1
panda/src/express/pointerToArray_ext.I

@@ -38,7 +38,8 @@ INLINE void set_matrix_view(Py_buffer &view, int flags, int length, int size, bo
   } else if (size == 4 && double_prec) {
     mat_size = sizeof(UnalignedLMatrix4d);
   } else {
-    assert(false);
+    nassertv_always(false);
+    return; // Make sure compiler knows control flow doesn't proceed.
   }
 
   view.len = length * mat_size;

+ 5 - 5
panda/src/express/pointerToArray_ext.h

@@ -97,11 +97,11 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
 
 #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"
+#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>;

+ 1 - 1
panda/src/gles2gsg/gles2gsg.h

@@ -51,7 +51,7 @@
 // OpenGL ES 2 has no fixed-function pipeline.
 #undef SUPPORT_FIXED_FUNCTION
 
-#ifdef IS_OSX
+#ifdef BUILD_IPHONE
   #include <OpenGLES/ES2/gl.h>
 // #include <OpenGLESES2glext.h>
 #else

+ 1 - 1
panda/src/glesgsg/glesgsg.h

@@ -54,7 +54,7 @@
 #define __glext_h_
 #define ES1_GLEXT_H_GUARD
 
-#ifdef IS_OSX
+#ifdef BUILD_IPHONE
   #include <OpenGLES/ES1/gl.h>
 // #include <OpenGLESES1glext.h>
 #else

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

@@ -226,12 +226,14 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte
         if (!resource) {
           resource = "unknown";
         }
-        GLCAT.error()
-          << "Could not find Cg varying " << cgGetParameterName(p);
-        if (attribname) {
-          GLCAT.error(false) << " : " << attribname;
+        if (GLCAT.is_debug()) {
+          GLCAT.debug()
+            << "Could not find Cg varying " << cgGetParameterName(p);
+          if (attribname) {
+            GLCAT.debug(false) << " : " << attribname;
+          }
+          GLCAT.debug(false) << " (" << resource << ") in the compiled GLSL program.\n";
         }
-        GLCAT.error(false) << " (" << resource << ") in the compiled GLSL program.\n";
 
       } else if (loc != 0 && bind._id._name == "vtx_position") {
         // We really have to bind the vertex position to attribute 0, since
@@ -312,10 +314,10 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte
         GLCAT.debug(false)
           << " is bound to a conventional attribute (" << resource << ")\n";
       }
-    }
-    if (loc == CA_unknown) {
-      // Suggest fix to developer.
-      GLCAT.error() << "Try using a different semantic.\n";
+      if (loc == CA_unknown) {
+        // Suggest fix to developer.
+        GLCAT.debug() << "Try using a different semantic.\n";
+      }
     }
 #endif
 

+ 9 - 0
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -1097,6 +1097,15 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot,
 #endif
       glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rbm[slot]);
       GLuint format = GL_DEPTH_COMPONENT;
+#ifndef OPENGLES
+      if (_fb_properties.get_float_depth()) {
+        if (!glgsg->_use_remapped_depth_range) {
+          format = GL_DEPTH_COMPONENT32F;
+        } else {
+          format = GL_DEPTH_COMPONENT32F_NV;
+        }
+      } else
+#endif
       if (tex) {
         switch (tex->get_format()) {
           case Texture::F_depth_component16:

+ 12 - 2
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -4392,7 +4392,8 @@ update_standard_vertex_arrays(bool force) {
       GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f);
     } else
 #endif // NDEBUG
-      if (_data_reader->get_color_info(array_reader, num_values, numeric_type,
+      if (_vertex_colors_enabled &&
+          _data_reader->get_color_info(array_reader, num_values, numeric_type,
                                        start, stride)) {
         if (!setup_array_data(client_pointer, array_reader, force)) {
           return false;
@@ -4409,7 +4410,13 @@ update_standard_vertex_arrays(bool force) {
         glDisableClientState(GL_COLOR_ARRAY);
 
         // Since we don't have per-vertex color, the implicit color is white.
-        GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f);
+        if (_color_scale_via_lighting) {
+          GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f);
+        } else {
+          LColor color = _scene_graph_color;
+          color.componentwise_mult(_current_color_scale);
+          GLPf(Color4)(color[0], color[1], color[2], color[3]);
+        }
       }
 
     // Now set up each of the active texture coordinate stages--or at least
@@ -8918,6 +8925,9 @@ get_panda_wrap_mode(GLenum wm) {
   case GL_REPEAT:
     return SamplerState::WM_repeat;
 
+  case GL_MIRRORED_REPEAT:
+    return SamplerState::WM_mirror;
+
 #ifndef OPENGLES
   case GL_MIRROR_CLAMP_EXT:
   case GL_MIRROR_CLAMP_TO_EDGE_EXT:

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

@@ -2440,9 +2440,9 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) {
         if (p == _color_attrib_index) {
           // Vertex colors are disabled or not present.  Apply flat color.
 #ifdef STDFLOAT_DOUBLE
-          _glgsg->_glVertexAttrib4dv(p, color_attrib->get_color().get_data());
+          _glgsg->_glVertexAttrib4dv(p, _glgsg->_scene_graph_color.get_data());
 #else
-          _glgsg->_glVertexAttrib4fv(p, color_attrib->get_color().get_data());
+          _glgsg->_glVertexAttrib4fv(p, _glgsg->_scene_graph_color.get_data());
 #endif
         }
       }

+ 23 - 2
panda/src/gobj/geom.cxx

@@ -286,6 +286,28 @@ make_nonindexed(bool composite_only) {
   return num_changed;
 }
 
+/**
+ * Returns a GeomVertexData that represents the results of computing the
+ * vertex animation on the CPU for this Geom's vertex data.
+ *
+ * If there is no CPU-defined vertex animation on this object, this just
+ * returns the original object.
+ *
+ * If there is vertex animation, but the VertexTransform values have not
+ * changed since last time, this may return the same pointer it returned
+ * previously.  Even if the VertexTransform values have changed, it may still
+ * return the same pointer, but with its contents modified (this is preferred,
+ * since it allows the graphics backend to update vertex buffers optimally).
+ *
+ * If force is false, this method may return immediately with stale data, if
+ * the vertex data is not completely resident.  If force is true, this method
+ * will never return stale data, but may block until the data is available.
+ */
+CPT(GeomVertexData) Geom::
+get_animated_vertex_data(bool force, Thread *current_thread) const {
+  return get_vertex_data()->animate_vertices(force, current_thread);
+}
+
 /**
  * Replaces the ith GeomPrimitive object stored within the Geom with the new
  * object.
@@ -1311,8 +1333,7 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
   int num_vertices = 0;
 
   // Get the vertex data, after animation.
-  CPT(GeomVertexData) vertex_data = cdata->_data.get_read_pointer(current_thread);
-  vertex_data = vertex_data->animate_vertices(true, current_thread);
+  CPT(GeomVertexData) vertex_data = get_animated_vertex_data(true, current_thread);
 
   // Now actually compute the bounding volume.  We do this by using
   // calc_tight_bounds to determine our box first.

+ 2 - 0
panda/src/gobj/geom.h

@@ -85,6 +85,8 @@ PUBLISHED:
   void offset_vertices(const GeomVertexData *data, int offset);
   int make_nonindexed(bool composite_only);
 
+  CPT(GeomVertexData) get_animated_vertex_data(bool force, Thread *current_thread) const;
+
   INLINE bool is_empty() const;
 
   INLINE size_t get_num_primitives() const;

+ 3 - 1
panda/src/gobj/geomVertexArrayData.I

@@ -45,7 +45,9 @@ has_column(const InternalName *name) const {
  */
 INLINE int GeomVertexArrayData::
 get_num_rows() const {
-  return get_handle()->get_num_rows();
+  CDReader cdata(_cycler);
+  nassertr(_array_format->get_stride() != 0, 0);
+  return cdata->_buffer.get_size() / _array_format->get_stride();
 }
 
 /**

+ 16 - 3
panda/src/gobj/geomVertexData.I

@@ -60,9 +60,20 @@ has_column(const InternalName *name) const {
  */
 INLINE int GeomVertexData::
 get_num_rows() const {
-  GeomVertexDataPipelineReader reader(this, Thread::get_current_thread());
-  reader.check_array_readers();
-  return reader.get_num_rows();
+  CPT(GeomVertexArrayData) array;
+  {
+    CDReader cdata(_cycler);
+    nassertr(cdata->_format->get_num_arrays() == cdata->_arrays.size(), 0);
+
+    if (cdata->_arrays.size() == 0) {
+      // No arrays means no rows.  Weird but legal.
+      return 0;
+    }
+
+    array = cdata->_arrays[0].get_read_pointer();
+  }
+
+  return array->get_num_rows();
 }
 
 /**
@@ -761,7 +772,9 @@ set_object(const GeomVertexData *object) {
   _cdata = (GeomVertexData::CData *)_object->_cycler.read_unlocked(_current_thread);
   _got_array_readers = false;
 
+#ifdef DO_PIPELINING
   _cdata->ref();
+#endif  // DO_PIPELINING
 }
 
 /**

+ 18 - 5
panda/src/gobj/geomVertexData.cxx

@@ -621,13 +621,26 @@ copy_from(const GeomVertexData *source, bool keep_data_objects,
             const TransformBlend &blend = blend_table->get_blend(from.get_data1i());
             LVecBase4 weights = LVecBase4::zero();
             LVecBase4i indices(0, 0, 0, 0);
-            nassertv(blend.get_num_transforms() <= 4);
 
-            for (size_t i = 0; i < blend.get_num_transforms(); i++) {
-              weights[i] = blend.get_weight(i);
-              indices[i] = add_transform(transform_table, blend.get_transform(i),
-                                         already_added);
+            if (blend.get_num_transforms() <= 4) {
+              for (size_t i = 0; i < blend.get_num_transforms(); i++) {
+                weights[i] = blend.get_weight(i);
+                indices[i] = add_transform(transform_table, blend.get_transform(i),
+                                           already_added);
+              }
+            } else {
+              // Limit the number of blends to the four with highest weights.
+              TransformBlend blend2(blend);
+              blend2.limit_transforms(4);
+              blend2.normalize_weights();
+
+              for (size_t i = 0; i < 4; i++) {
+                weights[i] = blend2.get_weight(i);
+                indices[i] = add_transform(transform_table, blend2.get_transform(i),
+                                           already_added);
+              }
             }
+
             if (weight.has_column()) {
               weight.set_data4(weights);
             }

+ 2 - 2
panda/src/gobj/preparedGraphicsObjects.cxx

@@ -1619,8 +1619,8 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) {
        ++qsi) {
     Shader *shader = qsi->first;
     ShaderContext *sc = shader->prepare_now(this, gsg);
-    if (qti->second != nullptr) {
-      qti->second->set_result(sc);
+    if (qsi->second != nullptr) {
+      qsi->second->set_result(sc);
     }
   }
 

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

@@ -40,7 +40,7 @@ do_task() {
       // become a kind of a leak (if the texture is never rendered again on
       // this GSG, we'll just end up carrying the texture memory in RAM
       // forever, instead of dumping it as soon as it gets prepared).
-      _texture->prepare(_pgo);
+      _pgo->enqueue_texture(_texture);
     }
   }
 

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

@@ -84,7 +84,7 @@ operator = (const TextureStage &other) {
   _combine_rgb_operand2 = other._combine_rgb_operand2;
   _combine_alpha_mode = other._combine_alpha_mode;
   _combine_alpha_source0 = other._combine_alpha_source0;
-  _combine_alpha_operand0 = _combine_alpha_operand0;
+  _combine_alpha_operand0 = other._combine_alpha_operand0;
   _combine_alpha_source1 = other._combine_alpha_source1;
   _combine_alpha_operand1 = other._combine_alpha_operand1;
   _combine_alpha_source2 = other._combine_alpha_source2;

+ 1 - 1
panda/src/grutil/multitexReducer.cxx

@@ -866,7 +866,7 @@ transfer_geom(GeomNode *geom_node, const InternalName *texcoord_name,
     PT(Geom) geom = orig_geom->make_copy();
 
     // Ensure that any vertex animation has been applied.
-    geom->set_vertex_data(geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread));
+    geom->set_vertex_data(geom->get_animated_vertex_data(true, current_thread));
 
     // Now get a modifiable pointer to the vertex data in the new Geom.  This
     // will actually perform a deep copy of the vertex data.

+ 1 - 1
panda/src/net/connection.cxx

@@ -445,7 +445,7 @@ do_flush() {
     if (data_sent > 0) {
       total_sent += data_sent;
     }
-    double last_report = 0;
+
     while (!okflag && tcp->Active() &&
            (data_sent > 0 || tcp->GetLastError() == LOCAL_BLOCKING_ERROR)) {
       if (data_sent == 0) {

+ 1 - 1
panda/src/osxdisplay/osxGraphicsStateGuardian.h

@@ -64,7 +64,7 @@ private:
   CGGammaValue _gOriginalRedTable[ 256 ];
   CGGammaValue _gOriginalGreenTable[ 256 ];
   CGGammaValue _gOriginalBlueTable[ 256 ];
-  CGTableCount _sampleCount;
+  uint32_t _sampleCount;
   CGDisplayErr _cgErr;
 
 public:

+ 0 - 1
panda/src/particlesystem/baseParticle.cxx

@@ -68,7 +68,6 @@ write(std::ostream &out, int indent) const {
   out.width(indent+2); out<<""; out<<"_lifespan "<<_lifespan<<"\n";
   out.width(indent+2); out<<""; out<<"_alive "<<_alive<<"\n";
   out.width(indent+2); out<<""; out<<"_index "<<_index<<"\n";
-  out.width(indent+2); out<<""; out<<"_last_position "<<_last_position<<"\n";
   PhysicsObject::write(out, indent+2);
   #endif //] NDEBUG
 }

+ 0 - 2
panda/src/particlesystem/baseParticle.h

@@ -62,8 +62,6 @@ private:
   PN_stdfloat _lifespan;
   bool _alive;
   int _index;
-
-  LPoint3 _last_position;
 };
 
 #include "baseParticle.I"

+ 1 - 1
panda/src/pgraph/colorAttrib.cxx

@@ -68,7 +68,7 @@ make_off() {
  */
 CPT(RenderAttrib) ColorAttrib::
 make_default() {
-  return make_off();
+  return make_vertex();
 }
 
 /**

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

@@ -88,7 +88,7 @@ public:
     register_type(_type_handle, "ColorAttrib",
                   RenderAttrib::get_class_type());
     _attrib_slot = register_slot(_type_handle, 100,
-      new ColorAttrib(T_off, LColor(1, 1, 1, 1)));
+      new ColorAttrib(T_vertex, LColor::zero()));
   }
   virtual TypeHandle get_type() const {
     return get_class_type();

+ 3 - 0
panda/src/pgraph/cullTraverserData.cxx

@@ -54,6 +54,9 @@ apply_transform_and_state(CullTraverser *trav) {
     CPT(TransformState) node_transform = _node_reader.get_transform();
     node_effects->cull_callback(trav, *this, node_transform, node_state);
     apply_transform(node_transform);
+
+    // The cull callback may have changed the node properties.
+    _node_reader.check_cached(false);
   }
 
   if (!node_state->is_empty()) {

+ 2 - 3
panda/src/pgraph/geomNode.cxx

@@ -376,8 +376,7 @@ r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
     geom = transformer.premunge_geom(geom, munger);
 
     // Prepare each of the vertex arrays in the munged Geom.
-    CPT(GeomVertexData) vdata = geom->get_vertex_data(current_thread);
-    vdata = vdata->animate_vertices(false, current_thread);
+    CPT(GeomVertexData) vdata = geom->get_animated_vertex_data(false, current_thread);
     GeomVertexDataPipelineReader vdata_reader(vdata, current_thread);
     int num_arrays = vdata_reader.get_num_arrays();
     for (int i = 0; i < num_arrays; ++i) {
@@ -474,7 +473,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any,
   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     CPT(Geom) geom = (*gi)._geom.get_read_pointer();
     geom->calc_tight_bounds(min_point, max_point, found_any,
-                            geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread),
+                            geom->get_animated_vertex_data(true, current_thread),
                             !next_transform->is_identity(), mat,
                             current_thread);
   }

+ 4 - 1
panda/src/pgraph/renderAttrib.cxx

@@ -211,7 +211,7 @@ garbage_collect() {
   }
 
   num_this_pass = std::min(num_this_pass, size);
-  size_t stop_at_element = (_garbage_index + num_this_pass) % size;
+  size_t stop_at_element = (si + num_this_pass) % size;
 
   do {
     RenderAttrib *attrib = (RenderAttrib *)_attribs->get_key(si);
@@ -229,6 +229,9 @@ garbage_collect() {
       // still need to visit.
       --size;
       --si;
+      if (stop_at_element > 0) {
+        --stop_at_element;
+      }
     }
 
     si = (si + 1) % size;

+ 3 - 0
panda/src/pgraph/renderState.cxx

@@ -950,6 +950,9 @@ garbage_collect() {
       // still need to visit.
       --size;
       --si;
+      if (stop_at_element > 0) {
+        --stop_at_element;
+      }
     }
 
     si = (si + 1) % size;

+ 3 - 0
panda/src/pgraph/transformState.cxx

@@ -1216,6 +1216,9 @@ garbage_collect() {
       // still need to visit.
       --size;
       --si;
+      if (stop_at_element > 0) {
+        --stop_at_element;
+      }
     }
 
     si = (si + 1) % size;

+ 0 - 8
panda/src/pipeline/blockerSimple.I

@@ -11,14 +11,6 @@
  * @date 2007-06-20
  */
 
-/**
- *
- */
-INLINE BlockerSimple::
-BlockerSimple() {
-  _flags = 0;
-}
-
 /**
  *
  */

+ 2 - 2
panda/src/pipeline/blockerSimple.h

@@ -28,7 +28,7 @@
  */
 class EXPCL_PANDA_PIPELINE BlockerSimple {
 protected:
-  INLINE BlockerSimple();
+  constexpr BlockerSimple() = default;
   INLINE ~BlockerSimple();
 
 protected:
@@ -38,7 +38,7 @@ protected:
     F_has_waiters  = 0x40000000,
   };
 
-  unsigned int _flags;
+  unsigned int _flags = 0;
 
   friend class ThreadSimpleManager;
 };

+ 3 - 0
panda/src/pipeline/config_pipeline.cxx

@@ -71,7 +71,10 @@ init_libpipeline() {
   }
   initialized = true;
 
+#ifdef DO_PIPELINING
   CycleData::init_type();
+#endif
+
   MainThread::init_type();
   ExternalThread::init_type();
   GenericThread::init_type();

+ 9 - 10
panda/src/pipeline/contextSwitch_longjmp_src.c

@@ -1,8 +1,4 @@
-/* Filename: contextSwitch_longjmp_src.c
- * Created by:  drose (15Apr10)
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
+/**
  * PANDA 3D SOFTWARE
  * Copyright (c) Carnegie Mellon University.  All rights reserved.
  *
@@ -10,7 +6,10 @@
  * license.  You should have received a copy of this license along
  * with this source code in a file named "LICENSE."
  *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+ * @file contextSwitch_longjmp_src.c
+ * @author drose
+ * @date 2010-04-15
+ */
 
 /* This is the implementation of user-space context switching using
    setmp() / longjmp().  This is the hackier implementation,
@@ -90,14 +89,14 @@ void
 cs_longjmp(cs_jmp_buf env) {
   _asm {
     mov eax, env;
-    
+
     mov ebx, [eax + 0];
     mov edi, [eax + 4];
     mov esi, [eax + 8];
     mov ebp, [eax + 12];
     mov esp, [eax + 16];
     mov edx, [eax + 20];
-    
+
     frstor [eax + 24];  /* restore floating-point state */
 
     mov eax, 1;   /* return 1 from setjmp: pass 2 return */
@@ -251,7 +250,7 @@ setup_context_1(void) {
 }
 
 void
-init_thread_context(struct ThreadContext *context, 
+init_thread_context(struct ThreadContext *context,
                     unsigned char *stack, size_t stack_size,
                     ThreadFunction *thread_func, void *data) {
   /* Copy all of the input parameters to static variables, then begin
@@ -263,7 +262,7 @@ init_thread_context(struct ThreadContext *context,
   st_data = data;
 
   setup_context_1();
-}  
+}
 
 void
 save_thread_context(struct ThreadContext *context,

+ 14 - 15
panda/src/pipeline/contextSwitch_posix_src.c

@@ -1,8 +1,4 @@
-/* Filename: contextSwitch_posix_src.c
- * Created by:  drose (15Apr10)
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
+/**
  * PANDA 3D SOFTWARE
  * Copyright (c) Carnegie Mellon University.  All rights reserved.
  *
@@ -10,7 +6,10 @@
  * license.  You should have received a copy of this license along
  * with this source code in a file named "LICENSE."
  *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+ * @file contextSwitch_posix_src.c
+ * @author drose
+ * @date 2010-04-15
+ */
 
 /* This is the implementation of user-space context switching using
    posix threads to manage the different execution contexts.  This
@@ -44,7 +43,7 @@ struct ThreadContext {
   pthread_mutex_t _ready_mutex;
   pthread_cond_t _ready_cvar;
   int _ready_flag;
-  
+
   /* This is set FALSE while the thread is alive, and TRUE if the
      thread is to be terminated when it next wakes up. */
   int _terminated;
@@ -83,19 +82,19 @@ thread_main(void *data) {
 }
 
 void
-init_thread_context(struct ThreadContext *context, 
+init_thread_context(struct ThreadContext *context,
                     unsigned char *stack, size_t stack_size,
                     ThreadFunction *thread_func, void *data) {
   context->_thread_func = thread_func;
   context->_data = data;
 
-  pthread_attr_t attr; 
-  pthread_attr_init(&attr); 
+  pthread_attr_t attr;
+  pthread_attr_init(&attr);
   pthread_attr_setstacksize(&attr, stack_size);
-  pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); 
+  pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
 
-  pthread_create(&(context->_thread), &attr, thread_main, context); 
-  pthread_attr_destroy(&attr); 
+  pthread_create(&(context->_thread), &attr, thread_main, context);
+  pthread_attr_destroy(&attr);
 }
 
 void
@@ -142,7 +141,7 @@ switch_to_thread_context(struct ThreadContext *from_context,
     /* We've been rudely terminated.  Exit gracefully. */
     pthread_exit(NULL);
   }
-  
+
   /* Now we have been signaled again, and we're ready to resume the
      thread. */
   longjmp(from_context->_jmp_context, 1);
@@ -164,7 +163,7 @@ alloc_thread_context() {
   pthread_mutexattr_init(&attr);
   // The symbol PTHREAD_MUTEX_DEFAULT isn't always available?
   //  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
-  int result = pthread_mutex_init(&context->_ready_mutex, &attr);
+  pthread_mutex_init(&context->_ready_mutex, &attr);
   pthread_mutexattr_destroy(&attr);
 
   pthread_cond_init(&context->_ready_cvar, NULL);

+ 6 - 7
panda/src/pipeline/contextSwitch_ucontext_src.c

@@ -1,8 +1,4 @@
-/* Filename: contextSwitch_ucontext_src.c
- * Created by:  drose (15Apr10)
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
+/**
  * PANDA 3D SOFTWARE
  * Copyright (c) Carnegie Mellon University.  All rights reserved.
  *
@@ -10,7 +6,10 @@
  * license.  You should have received a copy of this license along
  * with this source code in a file named "LICENSE."
  *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+ * @file contextSwitch_ucontext_src.c
+ * @author drose
+ * @date 2010-04-15
+ */
 
 /* This is the implementation of user-space context switching using
    getcontext() / setcontext().  This is the preferred implementation,
@@ -43,7 +42,7 @@ begin_context(ThreadFunction *thread_func, void *data) {
 }
 
 void
-init_thread_context(struct ThreadContext *context, 
+init_thread_context(struct ThreadContext *context,
                     unsigned char *stack, size_t stack_size,
                     ThreadFunction *thread_func, void *data) {
   if (getcontext(&context->_ucontext) != 0) {

+ 9 - 10
panda/src/pipeline/contextSwitch_windows_src.c

@@ -1,8 +1,4 @@
-/* Filename: contextSwitch_windows_src.c
- * Created by:  drose (15Apr10)
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
+/**
  * PANDA 3D SOFTWARE
  * Copyright (c) Carnegie Mellon University.  All rights reserved.
  *
@@ -10,7 +6,10 @@
  * license.  You should have received a copy of this license along
  * with this source code in a file named "LICENSE."
  *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+ * @file contextSwitch_windows_src.c
+ * @author drose
+ * @date 2010-04-15
+ */
 
 /* This is the implementation of user-space context switching using
    native Windows threading constructs to manage the different
@@ -37,7 +36,7 @@ struct ThreadContext {
   /* This event is in the signaled state when the thread is ready to
      roll. */
   HANDLE _ready;
-  
+
   /* This is set FALSE while the thread is alive, and TRUE if the
      thread is to be terminated when it next wakes up. */
   int _terminated;
@@ -71,13 +70,13 @@ thread_main(LPVOID data) {
 }
 
 void
-init_thread_context(struct ThreadContext *context, 
+init_thread_context(struct ThreadContext *context,
                     unsigned char *stack, size_t stack_size,
                     ThreadFunction *thread_func, void *data) {
   context->_thread_func = thread_func;
   context->_data = data;
 
-  context->_thread = CreateThread(NULL, stack_size, 
+  context->_thread = CreateThread(NULL, stack_size,
                                   thread_main, context, 0, NULL);
 }
 
@@ -117,7 +116,7 @@ switch_to_thread_context(struct ThreadContext *from_context,
     /* We've been rudely terminated.  Exit gracefully. */
     ExitThread(1);
   }
-  
+
   /* Now we have been signaled again, and we're ready to resume the
      thread. */
   longjmp(from_context->_jmp_context, 1);

+ 3 - 0
panda/src/pipeline/cycleData.h

@@ -54,6 +54,9 @@ public:
   INLINE CycleData(const CycleData &copy) = default;
   virtual ~CycleData();
 
+  CycleData &operator = (CycleData &&from) = default;
+  CycleData &operator = (const CycleData &copy) = default;
+
   virtual CycleData *make_copy() const=0;
 
   virtual void write_datagram(BamWriter *, Datagram &) const;

+ 25 - 13
panda/src/pipeline/cycleDataLockedReader.I

@@ -45,6 +45,19 @@ CycleDataLockedReader(const CycleDataLockedReader<CycleDataType> &copy) :
   _cycler->increment_read(_pointer);
 }
 
+/**
+ *
+ */
+template<class CycleDataType>
+INLINE CycleDataLockedReader<CycleDataType>::
+CycleDataLockedReader(CycleDataLockedReader<CycleDataType> &&from) noexcept :
+  _cycler(from._cycler),
+  _current_thread(from._current_thread),
+  _pointer(from._pointer)
+{
+  from._pointer = nullptr;
+}
+
 /**
  *
  */
@@ -61,19 +74,6 @@ operator = (const CycleDataLockedReader<CycleDataType> &copy) {
   _cycler->increment_read(_pointer);
 }
 
-/**
- *
- */
-template<class CycleDataType>
-INLINE CycleDataLockedReader<CycleDataType>::
-CycleDataLockedReader(CycleDataLockedReader<CycleDataType> &&from) noexcept :
-  _cycler(from._cycler),
-  _current_thread(from._current_thread),
-  _pointer(from._pointer)
-{
-  from._pointer = nullptr;
-}
-
 /**
  *
  */
@@ -177,6 +177,18 @@ operator = (const CycleDataLockedReader<CycleDataType> &copy) {
   _pointer = copy._pointer;
 }
 
+/**
+ *
+ */
+template<class CycleDataType>
+INLINE void CycleDataLockedReader<CycleDataType>::
+operator = (CycleDataLockedReader<CycleDataType> &&from) noexcept {
+  nassertv(_pointer == nullptr);
+
+  _pointer = from._pointer;
+  from._pointer = nullptr;
+}
+
 /**
  *
  */

+ 37 - 14
panda/src/pipeline/cycleDataLockedStageReader.I

@@ -47,6 +47,20 @@ CycleDataLockedStageReader(const CycleDataLockedStageReader<CycleDataType> &copy
   _cycler->increment_read(_pointer);
 }
 
+/**
+ *
+ */
+template<class CycleDataType>
+INLINE CycleDataLockedStageReader<CycleDataType>::
+CycleDataLockedStageReader(CycleDataLockedStageReader<CycleDataType> &&from) noexcept :
+  _cycler(from._cycler),
+  _current_thread(from._current_thread),
+  _pointer(from._pointer),
+  _stage(from._stage)
+{
+  from._pointer = nullptr;
+}
+
 /**
  *
  */
@@ -64,20 +78,6 @@ operator = (const CycleDataLockedStageReader<CycleDataType> &copy) {
   _cycler->increment_read(_pointer);
 }
 
-/**
- *
- */
-template<class CycleDataType>
-INLINE CycleDataLockedStageReader<CycleDataType>::
-CycleDataLockedStageReader(CycleDataLockedStageReader<CycleDataType> &&from) noexcept :
-  _cycler(from._cycler),
-  _current_thread(from._current_thread),
-  _pointer(from._pointer),
-  _stage(from._stage)
-{
-  from._pointer = nullptr;
-}
-
 /**
  *
  */
@@ -174,6 +174,17 @@ CycleDataLockedStageReader(const CycleDataLockedStageReader<CycleDataType> &copy
 {
 }
 
+/**
+ *
+ */
+template<class CycleDataType>
+INLINE CycleDataLockedStageReader<CycleDataType>::
+CycleDataLockedStageReader(CycleDataLockedStageReader<CycleDataType> &&from) noexcept :
+  _pointer(from._cycler)
+{
+  from._pointer = nullptr;
+}
+
 /**
  *
  */
@@ -183,6 +194,18 @@ operator = (const CycleDataLockedStageReader<CycleDataType> &copy) {
   _pointer = copy._pointer;
 }
 
+/**
+ *
+ */
+template<class CycleDataType>
+INLINE void CycleDataLockedStageReader<CycleDataType>::
+operator = (CycleDataLockedStageReader<CycleDataType> &&from) noexcept {
+  nassertv(_pointer == nullptr);
+
+  _pointer = from._pointer;
+  from._pointer = nullptr;
+}
+
 /**
  *
  */

+ 40 - 17
panda/src/pipeline/cycleDataStageWriter.I

@@ -62,23 +62,6 @@ CycleDataStageWriter(const CycleDataStageWriter<CycleDataType> &copy) :
   _cycler->increment_write(_pointer);
 }
 
-/**
- *
- */
-template<class CycleDataType>
-INLINE void CycleDataStageWriter<CycleDataType>::
-operator = (const CycleDataStageWriter<CycleDataType> &copy) {
-  nassertv(_pointer == nullptr);
-  nassertv(_current_thread == copy._current_thread);
-
-  _cycler = copy._cycler;
-  _pointer = copy._pointer;
-  _stage = copy._stage;
-
-  nassertv(_pointer != nullptr);
-  _cycler->increment_write(_pointer);
-}
-
 /**
  * This flavor of the constructor elevates the pointer from the
  * CycleDataLockedStageReader from a read to a write pointer (and invalidates
@@ -128,6 +111,23 @@ CycleDataStageWriter(CycleDataStageWriter<CycleDataType> &&from) noexcept :
   from._pointer = nullptr;
 }
 
+/**
+ *
+ */
+template<class CycleDataType>
+INLINE void CycleDataStageWriter<CycleDataType>::
+operator = (const CycleDataStageWriter<CycleDataType> &copy) {
+  nassertv(_pointer == nullptr);
+  nassertv(_current_thread == copy._current_thread);
+
+  _cycler = copy._cycler;
+  _pointer = copy._pointer;
+  _stage = copy._stage;
+
+  nassertv(_pointer != nullptr);
+  _cycler->increment_write(_pointer);
+}
+
 /**
  *
  */
@@ -227,6 +227,17 @@ CycleDataStageWriter(const CycleDataStageWriter<CycleDataType> &copy) :
 {
 }
 
+/**
+ *
+ */
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+CycleDataStageWriter(CycleDataStageWriter<CycleDataType> &&from) noexcept :
+  _pointer(from._pointer)
+{
+  from._pointer = nullptr;
+}
+
 /**
  *
  */
@@ -236,6 +247,18 @@ operator = (const CycleDataStageWriter<CycleDataType> &copy) {
   _pointer = copy._pointer;
 }
 
+/**
+ *
+ */
+template<class CycleDataType>
+INLINE void CycleDataStageWriter<CycleDataType>::
+operator = (CycleDataStageWriter<CycleDataType> &&from) noexcept {
+  nassertv(_pointer == nullptr);
+
+  _pointer = from._pointer;
+  from._pointer = nullptr;
+}
+
 /**
  * This flavor of the constructor elevates the pointer from the
  * CycleDataLockedStageReader from a read to a write pointer (and invalidates

+ 1 - 1
panda/src/pipeline/pythonThread.cxx

@@ -225,7 +225,7 @@ call_python_func(PyObject *function, PyObject *args) {
 
     } else {
       // No exception.  Restore the thread state normally.
-      PyThreadState *state = PyThreadState_Swap(orig_thread_state);
+      PyThreadState_Swap(orig_thread_state);
       thread_states.push_back(new_thread_state);
       // PyThreadState_Clear(new_thread_state);
       // PyThreadState_Delete(new_thread_state);

+ 0 - 8
panda/src/pipeline/threadSimpleImpl.I

@@ -51,14 +51,6 @@ is_threading_supported() {
   return true;
 }
 
-/**
- *
- */
-INLINE bool ThreadSimpleImpl::
-is_true_threads() {
-  return (is_os_threads != 0);
-}
-
 /**
  *
  */

+ 11 - 3
panda/src/pipeline/threadSimpleImpl.cxx

@@ -141,8 +141,8 @@ start(ThreadPriority priority, bool joinable) {
 
 #ifdef HAVE_PYTHON
   // Query the current Python thread state.
-  _python_state = PyThreadState_Swap(nullptr);
-  PyThreadState_Swap(_python_state);
+  _python_state = thread_state_swap(nullptr);
+  thread_state_swap(_python_state);
 #endif  // HAVE_PYTHON
 
   init_thread_context(_context, _stack, _stack_size, st_begin_thread, this);
@@ -201,6 +201,14 @@ prepare_for_exit() {
   manager->prepare_for_exit();
 }
 
+/**
+ *
+ */
+bool ThreadSimpleImpl::
+is_true_threads() {
+  return (is_os_threads != 0);
+}
+
 /**
  *
  */
@@ -238,7 +246,7 @@ st_begin_thread(void *data) {
 void ThreadSimpleImpl::
 begin_thread() {
 #ifdef HAVE_PYTHON
-  PyThreadState_Swap(_python_state);
+  thread_state_swap(_python_state);
 #endif  // HAVE_PYTHON
 
 #ifdef HAVE_POSIX_THREADS

+ 1 - 1
panda/src/pipeline/threadSimpleImpl.h

@@ -63,7 +63,7 @@ public:
 
   INLINE static void bind_thread(Thread *thread);
   INLINE static bool is_threading_supported();
-  INLINE static bool is_true_threads();
+  static bool is_true_threads();
   INLINE static bool is_simple_threads();
   INLINE static void sleep(double seconds);
   INLINE static void yield();

+ 5 - 13
panda/src/pipeline/threadSimpleManager.cxx

@@ -20,7 +20,9 @@
 #include "mainThread.h"
 
 #ifdef WIN32
-#define WIN32_LEAN_AND_MEAN
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
 #include <windows.h>
 #endif
 
@@ -235,7 +237,7 @@ next_context() {
 
 #ifdef HAVE_PYTHON
   // Save the current Python thread state.
-  _current_thread->_python_state = PyThreadState_Swap(nullptr);
+  _current_thread->_python_state = thread_state_swap(nullptr);
 #endif  // HAVE_PYTHON
 
 #ifdef DO_PSTATS
@@ -256,7 +258,7 @@ next_context() {
 #endif  // DO_PSTATS
 
 #ifdef HAVE_PYTHON
-  PyThreadState_Swap(_current_thread->_python_state);
+  thread_state_swap(_current_thread->_python_state);
 #endif  // HAVE_PYTHON
 }
 
@@ -468,16 +470,6 @@ init_pointers() {
     _pointers_initialized = true;
     _global_ptr = new ThreadSimpleManager;
     Thread::get_main_thread();
-
-#ifdef HAVE_PYTHON
-    // Ensure that the Python threading system is initialized and ready to go.
-
-#if PY_VERSION_HEX >= 0x03020000
-    Py_Initialize();
-#endif
-
-    PyEval_InitThreads();
-#endif
   }
 }
 

+ 0 - 77
panda/src/text/textNode.I

@@ -1010,61 +1010,6 @@ clear_glyph_shift() {
   invalidate_with_measure();
 }
 
-
-/**
- * Changes the text that is displayed under the TextNode.
- */
-INLINE void TextNode::
-set_text(const std::string &text) {
-  MutexHolder holder(_lock);
-  TextEncoder::set_text(text);
-  invalidate_with_measure();
-}
-
-/**
- * The two-parameter version of set_text() accepts an explicit encoding; the
- * text is immediately decoded and stored as a wide-character string.
- * Subsequent calls to get_text() will return the same text re-encoded using
- * whichever encoding is specified by set_encoding().
- */
-INLINE void TextNode::
-set_text(const std::string &text, TextNode::Encoding encoding) {
-  MutexHolder holder(_lock);
-  TextEncoder::set_text(text, encoding);
-  invalidate_with_measure();
-}
-
-/**
- * Removes the text from the TextNode.
- */
-INLINE void TextNode::
-clear_text() {
-  MutexHolder holder(_lock);
-  TextEncoder::clear_text();
-  invalidate_with_measure();
-}
-
-/**
- * Appends the indicates string to the end of the stored text.
- */
-INLINE void TextNode::
-append_text(const std::string &text) {
-  MutexHolder holder(_lock);
-  TextEncoder::append_text(text);
-  invalidate_with_measure();
-}
-
-/**
- * Appends a single character to the end of the stored text.  This may be a
- * wide character, up to 16 bits in Unicode.
- */
-INLINE void TextNode::
-append_unicode_char(wchar_t character) {
-  MutexHolder holder(_lock);
-  TextEncoder::append_unicode_char(character);
-  invalidate_with_measure();
-}
-
 /**
  * Returns a string that represents the contents of the text, as it has been
  * formatted by wordwrap rules.
@@ -1086,28 +1031,6 @@ calc_width(const std::string &line) const {
   return calc_width(decode_text(line));
 }
 
-/**
- * Changes the text that is displayed under the TextNode, with a wide text.
- * This automatically sets the string reported by get_text() to the 8-bit
- * encoded version of the same string.
- */
-INLINE void TextNode::
-set_wtext(const std::wstring &wtext) {
-  MutexHolder holder(_lock);
-  TextEncoder::set_wtext(wtext);
-  invalidate_with_measure();
-}
-
-/**
- * Appends the indicates string to the end of the stored wide-character text.
- */
-INLINE void TextNode::
-append_wtext(const std::wstring &wtext) {
-  MutexHolder holder(_lock);
-  TextEncoder::append_wtext(wtext);
-  invalidate_with_measure();
-}
-
 /**
  * Returns a wstring that represents the contents of the text, as it has been
  * formatted by wordwrap rules.

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä