Browse Source

Merge branch 'master' into input-overhaul

rdb 7 years ago
parent
commit
357b6d3093
100 changed files with 1139 additions and 1114 deletions
  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
     @staticmethod
     def makeUniqueName(func, suffix = ''):
     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
         FunctionInterval.functionIntervalNum += 1
         if suffix:
         if suffix:
             name = '%s-%s' % (name, str(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.
   // Now look for functions that are virtual or con/destructors.
-  bool is_default_constructible = true;
   CPPScope::Functions::const_iterator fi;
   CPPScope::Functions::const_iterator fi;
   for (fi = _scope->_functions.begin(); fi != _scope->_functions.end(); ++fi) {
   for (fi = _scope->_functions.begin(); fi != _scope->_functions.end(); ++fi) {
     CPPFunctionGroup *fgroup = (*fi).second;
     CPPFunctionGroup *fgroup = (*fi).second;
@@ -343,9 +342,6 @@ is_trivial() const {
           // Same for the default constructor.
           // Same for the default constructor.
           return false;
           return false;
         }
         }
-        // The presence of a non-default constructor makes the class not
-        // default-constructible.
-        is_default_constructible = false;
       }
       }
 
 
       if (fgroup->_name == "operator =") {
       if (fgroup->_name == "operator =") {
@@ -356,7 +352,7 @@ is_trivial() const {
   }
   }
 
 
   // Finally, the class must be default-constructible.
   // 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_yield)() = default_thread_yield;
 void (*global_thread_consider_yield)() = default_thread_consider_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
 #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
 /* Windows likes to define min() and max() macros, which will conflict with
    std::min() and std::max() respectively, unless we do this: */
    std::min() and std::max() respectively, unless we do this: */
 #ifdef WIN32
 #ifdef WIN32
+#ifndef NOMINMAX
 #define NOMINMAX
 #define NOMINMAX
 #endif
 #endif
+#endif
 
 
 #ifndef __has_builtin
 #ifndef __has_builtin
 #define __has_builtin(x) 0
 #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)();
   (*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
 #else
 
 
 INLINE void thread_yield() {
 INLINE void thread_yield() {

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

@@ -1,2 +1,3 @@
 #include "filename_ext.cxx"
 #include "filename_ext.cxx"
 #include "globPattern_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::
 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.
  * Returns the next character in sequence.
  */
  */
-int StringDecoder::
+char32_t StringDecoder::
 get_next_character() {
 get_next_character() {
   if (test_eof()) {
   if (test_eof()) {
     return -1;
     return -1;
@@ -57,19 +57,20 @@ get_notify_ptr() {
 
 
 /*
 /*
 In UTF-8, each 16-bit Unicode character is encoded as a sequence of
 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
 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
 sequences (where the "free bits" shown by x's in the table are
 combined in the order shown, and interpreted from most significant to
 combined in the order shown, and interpreted from most significant to
 least significant):
 least significant):
 
 
  Binary format of bytes in sequence:
  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:
 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.
  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.
  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.
  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.
  * Returns the next character in sequence.
  */
  */
-int StringUtf8Decoder::
+char32_t StringUtf8Decoder::
 get_next_character() {
 get_next_character() {
   unsigned int result;
   unsigned int result;
   while (!test_eof()) {
   while (!test_eof()) {
@@ -125,6 +127,35 @@ get_next_character() {
       unsigned int three = (unsigned char)_input[_p++];
       unsigned int three = (unsigned char)_input[_p++];
       result = ((result & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
       result = ((result & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
       return result;
       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
     // 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.
  * Returns the next character in sequence.
  */
  */
-int StringUnicodeDecoder::
+char32_t StringUtf16Decoder::
 get_next_character() {
 get_next_character() {
   if (test_eof()) {
   if (test_eof()) {
     return -1;
     return -1;
@@ -159,5 +190,33 @@ get_next_character() {
     return -1;
     return -1;
   }
   }
   unsigned int low = (unsigned char)_input[_p++];
   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);
   INLINE StringDecoder(const std::string &input);
   virtual ~StringDecoder();
   virtual ~StringDecoder();
 
 
-  virtual int get_next_character();
+  virtual char32_t get_next_character();
   INLINE bool is_eof();
   INLINE bool is_eof();
 
 
   static void set_notify_ptr(std::ostream *ptr);
   static void set_notify_ptr(std::ostream *ptr);
@@ -48,20 +48,23 @@ class StringUtf8Decoder : public StringDecoder {
 public:
 public:
   INLINE StringUtf8Decoder(const std::string &input);
   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
  * 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:
 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"
 #include "stringDecoder.I"
 
 
 #endif
 #endif

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

@@ -90,6 +90,7 @@ set_text(const std::string &text) {
   if (!has_text() || _text != text) {
   if (!has_text() || _text != text) {
     _text = text;
     _text = text;
     _flags = (_flags | F_got_text) & ~F_got_wtext;
     _flags = (_flags | F_got_text) & ~F_got_wtext;
+    text_changed();
   }
   }
 }
 }
 
 
@@ -101,7 +102,11 @@ set_text(const std::string &text) {
  */
  */
 INLINE void TextEncoder::
 INLINE void TextEncoder::
 set_text(const std::string &text, TextEncoder::Encoding encoding) {
 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();
   _text = std::string();
   _wtext = std::wstring();
   _wtext = std::wstring();
   _flags |= (F_got_text | F_got_wtext);
   _flags |= (F_got_text | F_got_wtext);
+  text_changed();
 }
 }
 
 
 /**
 /**
@@ -151,8 +157,11 @@ get_text(TextEncoder::Encoding encoding) const {
  */
  */
 INLINE void TextEncoder::
 INLINE void TextEncoder::
 append_text(const std::string &text) {
 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.
  * wide character, up to 16 bits in Unicode.
  */
  */
 INLINE void TextEncoder::
 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);
   _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;
   _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().
  * according to set_encoding().
  */
  */
 INLINE void TextEncoder::
 INLINE void TextEncoder::
-set_unicode_char(size_t index, int character) {
+set_unicode_char(size_t index, char32_t character) {
   get_wtext();
   get_wtext();
   if (index < _wtext.length()) {
   if (index < _wtext.length()) {
     _wtext[index] = character;
     _wtext[index] = character;
     _flags &= ~F_got_text;
     _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.
  * otherwise.  This is akin to ctype's isalpha(), extended to Unicode.
  */
  */
 INLINE bool TextEncoder::
 INLINE bool TextEncoder::
-unicode_isalpha(int character) {
+unicode_isalpha(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
   if (entry == nullptr) {
     return false;
     return false;
@@ -271,7 +297,7 @@ unicode_isalpha(int character) {
  * otherwise.  This is akin to ctype's isdigit(), extended to Unicode.
  * otherwise.  This is akin to ctype's isdigit(), extended to Unicode.
  */
  */
 INLINE bool TextEncoder::
 INLINE bool TextEncoder::
-unicode_isdigit(int character) {
+unicode_isdigit(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
   if (entry == nullptr) {
     // The digits aren't actually listed in the map.
     // 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.
  * otherwise.  This is akin to ctype's ispunct(), extended to Unicode.
  */
  */
 INLINE bool TextEncoder::
 INLINE bool TextEncoder::
-unicode_ispunct(int character) {
+unicode_ispunct(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
   if (entry == nullptr) {
     // Some punctuation marks aren't listed in the map.
     // 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;
   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.
  * otherwise.  This is akin to ctype's isupper(), extended to Unicode.
  */
  */
 INLINE bool TextEncoder::
 INLINE bool TextEncoder::
-unicode_isupper(int character) {
+unicode_isupper(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
   if (entry == nullptr) {
     return false;
     return false;
@@ -313,7 +339,7 @@ unicode_isupper(int character) {
  * otherwise.  This is akin to ctype's isspace(), extended to Unicode.
  * otherwise.  This is akin to ctype's isspace(), extended to Unicode.
  */
  */
 INLINE bool TextEncoder::
 INLINE bool TextEncoder::
-unicode_isspace(int character) {
+unicode_isspace(char32_t character) {
   switch (character) {
   switch (character) {
   case ' ':
   case ' ':
   case '\t':
   case '\t':
@@ -330,7 +356,7 @@ unicode_isspace(int character) {
  * otherwise.  This is akin to ctype's islower(), extended to Unicode.
  * otherwise.  This is akin to ctype's islower(), extended to Unicode.
  */
  */
 INLINE bool TextEncoder::
 INLINE bool TextEncoder::
-unicode_islower(int character) {
+unicode_islower(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
   if (entry == nullptr) {
     return false;
     return false;
@@ -343,7 +369,7 @@ unicode_islower(int character) {
  * akin to ctype's toupper(), extended to Unicode.
  * akin to ctype's toupper(), extended to Unicode.
  */
  */
 INLINE int TextEncoder::
 INLINE int TextEncoder::
-unicode_toupper(int character) {
+unicode_toupper(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
   if (entry == nullptr) {
     return character;
     return character;
@@ -356,7 +382,7 @@ unicode_toupper(int character) {
  * akin to ctype's tolower(), extended to Unicode.
  * akin to ctype's tolower(), extended to Unicode.
  */
  */
 INLINE int TextEncoder::
 INLINE int TextEncoder::
-unicode_tolower(int character) {
+unicode_tolower(char32_t character) {
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   const UnicodeLatinMap::Entry *entry = UnicodeLatinMap::look_up(character);
   if (entry == nullptr) {
   if (entry == nullptr) {
     return character;
     return character;
@@ -418,6 +444,7 @@ set_wtext(const std::wstring &wtext) {
   if (!has_text() || _wtext != wtext) {
   if (!has_text() || _wtext != wtext) {
     _wtext = wtext;
     _wtext = wtext;
     _flags = (_flags | F_got_wtext) & ~F_got_text;
     _flags = (_flags | F_got_wtext) & ~F_got_text;
+    text_changed();
   }
   }
 }
 }
 
 
@@ -439,8 +466,11 @@ get_wtext() const {
  */
  */
 INLINE void TextEncoder::
 INLINE void TextEncoder::
 append_wtext(const std::wstring &wtext) {
 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::string;
 using std::wstring;
 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
  * Adjusts the text stored within the encoder to all uppercase letters
@@ -35,6 +35,7 @@ make_upper() {
     (*si) = unicode_toupper(*si);
     (*si) = unicode_toupper(*si);
   }
   }
   _flags &= ~F_got_text;
   _flags &= ~F_got_text;
+  text_changed();
 }
 }
 
 
 /**
 /**
@@ -49,6 +50,7 @@ make_lower() {
     (*si) = unicode_tolower(*si);
     (*si) = unicode_tolower(*si);
   }
   }
   _flags &= ~F_got_text;
   _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::
 string TextEncoder::
-encode_wchar(wchar_t ch, TextEncoder::Encoding encoding) {
+encode_wchar(char32_t ch, TextEncoder::Encoding encoding) {
   switch (encoding) {
   switch (encoding) {
   case E_iso8859:
   case E_iso8859:
     if ((ch & ~0xff) == 0) {
     if ((ch & ~0xff) == 0) {
@@ -143,17 +145,38 @@ encode_wchar(wchar_t ch, TextEncoder::Encoding encoding) {
       return
       return
         string(1, (char)((ch >> 6) | 0xc0)) +
         string(1, (char)((ch >> 6) | 0xc0)) +
         string(1, (char)((ch & 0x3f) | 0x80));
         string(1, (char)((ch & 0x3f) | 0x80));
-    } else {
+    } else if ((ch & ~0xffff) == 0) {
       return
       return
         string(1, (char)((ch >> 12) | 0xe0)) +
         string(1, (char)((ch >> 12) | 0xe0)) +
         string(1, (char)(((ch >> 6) & 0x3f) | 0x80)) +
         string(1, (char)(((ch >> 6) & 0x3f) | 0x80)) +
         string(1, (char)((ch & 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 "";
   return "";
@@ -167,8 +190,25 @@ string TextEncoder::
 encode_wtext(const wstring &wtext, TextEncoder::Encoding encoding) {
 encode_wtext(const wstring &wtext, TextEncoder::Encoding encoding) {
   string result;
   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;
   return result;
@@ -187,9 +227,9 @@ decode_text(const string &text, TextEncoder::Encoding encoding) {
       return decode_text_impl(decoder);
       return decode_text_impl(decoder);
     }
     }
 
 
-  case E_unicode:
+  case E_utf16be:
     {
     {
-      StringUnicodeDecoder decoder(text);
+      StringUtf16Decoder decoder(text);
       return decode_text_impl(decoder);
       return decode_text_impl(decoder);
     }
     }
 
 
@@ -211,7 +251,7 @@ decode_text_impl(StringDecoder &decoder) {
   wstring result;
   wstring result;
   // bool expand_amp = get_expand_amp();
   // bool expand_amp = get_expand_amp();
 
 
-  wchar_t character = decoder.get_next_character();
+  char32_t character = decoder.get_next_character();
   while (!decoder.is_eof()) {
   while (!decoder.is_eof()) {
     /*
     /*
     if (character == '&' && expand_amp) {
     if (character == '&' && expand_amp) {
@@ -219,7 +259,14 @@ decode_text_impl(StringDecoder &decoder) {
       character = expand_amp_sequence(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();
     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:
   case TextEncoder::E_utf8:
     return out << "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 << ")**";
   return out << "**invalid TextEncoder::Encoding(" << (int)encoding << ")**";
@@ -346,8 +399,9 @@ operator >> (istream &in, TextEncoder::Encoding &encoding) {
     encoding = TextEncoder::E_iso8859;
     encoding = TextEncoder::E_iso8859;
   } else if (word == "utf8" || word == "utf-8") {
   } else if (word == "utf8" || word == "utf-8") {
     encoding = TextEncoder::E_utf8;
     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 {
   } else {
     ostream *notify_ptr = StringDecoder::get_notify_ptr();
     ostream *notify_ptr = StringDecoder::get_notify_ptr();
     if (notify_ptr != nullptr) {
     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,
  * 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,
  * 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.
  * which will record the current encoding and retain the current string.
  *
  *
@@ -35,12 +35,17 @@ PUBLISHED:
   enum Encoding {
   enum Encoding {
     E_iso8859,
     E_iso8859,
     E_utf8,
     E_utf8,
-    E_unicode
+    E_utf16be,
+
+    // Deprecated alias for E_utf16be
+    E_unicode = E_utf16be,
   };
   };
 
 
   INLINE TextEncoder();
   INLINE TextEncoder();
   INLINE TextEncoder(const TextEncoder &copy);
   INLINE TextEncoder(const TextEncoder &copy);
 
 
+  virtual ~TextEncoder() = default;
+
   INLINE void set_encoding(Encoding encoding);
   INLINE void set_encoding(Encoding encoding);
   INLINE Encoding get_encoding() const;
   INLINE Encoding get_encoding() const;
 
 
@@ -48,35 +53,46 @@ PUBLISHED:
   INLINE static Encoding get_default_encoding();
   INLINE static Encoding get_default_encoding();
   MAKE_PROPERTY(default_encoding, get_default_encoding, set_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);
   INLINE void set_text(const std::string &text, Encoding encoding);
   INLINE void set_text(const std::string &text, Encoding encoding);
+#endif
   INLINE void clear_text();
   INLINE void clear_text();
   INLINE bool has_text() const;
   INLINE bool has_text() const;
 
 
   void make_upper();
   void make_upper();
   void make_lower();
   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() const;
   INLINE std::string get_text(Encoding encoding) const;
   INLINE std::string get_text(Encoding encoding) const;
   INLINE void append_text(const std::string &text);
   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 size_t get_num_chars() const;
   INLINE int get_unicode_char(size_t index) 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) const;
   INLINE std::string get_encoded_char(size_t index, Encoding encoding) const;
   INLINE std::string get_encoded_char(size_t index, Encoding encoding) const;
   INLINE std::string get_text_as_ascii() 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 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);
   INLINE static std::string upper(const std::string &source, Encoding encoding);
   INLINE static std::string upper(const std::string &source, Encoding encoding);
@@ -91,11 +107,24 @@ PUBLISHED:
   std::wstring get_wtext_as_ascii() const;
   std::wstring get_wtext_as_ascii() const;
   bool is_wtext() 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;
   INLINE std::string encode_wtext(const std::wstring &wtext) const;
   static std::string encode_wtext(const std::wstring &wtext, Encoding encoding);
   static std::string encode_wtext(const std::wstring &wtext, Encoding encoding);
   INLINE std::wstring decode_text(const std::string &text) const;
   INLINE std::wstring decode_text(const std::string &text) const;
   static std::wstring decode_text(const std::string &text, Encoding encoding);
   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:
 private:
   enum Flags {
   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.
  * Returns the Entry associated with the indicated character, if there is one.
  */
  */
 const UnicodeLatinMap::Entry *UnicodeLatinMap::
 const UnicodeLatinMap::Entry *UnicodeLatinMap::
-look_up(wchar_t character) {
+look_up(char32_t character) {
   if (!_initialized) {
   if (!_initialized) {
     init();
     init();
   }
   }

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

@@ -112,17 +112,17 @@ public:
 
 
   class Entry {
   class Entry {
   public:
   public:
-    wchar_t _character;
+    char32_t _character;
     CharType _char_type;
     CharType _char_type;
     char _ascii_equiv;
     char _ascii_equiv;
     char _ascii_additional;
     char _ascii_additional;
-    wchar_t _tolower_character;
-    wchar_t _toupper_character;
+    char32_t _tolower_character;
+    char32_t _toupper_character;
     AccentType _accent_type;
     AccentType _accent_type;
     int _additional_flags;
     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);
   static wchar_t get_combining_accent(AccentType accent);
 
 
@@ -130,7 +130,7 @@ private:
   static void init();
   static void init();
   static bool _initialized;
   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;
   static ByCharacter *_by_character;
   enum { max_direct_chars = 256 };
   enum { max_direct_chars = 256 };
   static const Entry *_direct_chars[max_direct_chars];
   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
     // We can only generate a getter and a setter if we can talk about the
     // type it is.
     // 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.
       // Wrap the type in a const reference.
       parameter_type = TypeManager::wrap_const_reference(parameter_type);
       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.
     // Extract the __file__ attribute, if present.
     Filename main_dir;
     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) {
     if (file_attr == nullptr) {
       // Must be running in the interactive interpreter.  Use the CWD.
       // Must be running in the interactive interpreter.  Use the CWD.
       main_dir = ExecutionEnvironment::get_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());
     ExecutionEnvironment::shadow_environment_variable("MAIN_DIR", main_dir.to_os_specific());
     PyErr_Clear();
     PyErr_Clear();
     initialized_main_dir = true;
     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);
   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 {} PyStringObject;
 typedef struct {} PyUnicodeObject;
 typedef struct {} PyUnicodeObject;
 
 
-class PyThreadState;
+typedef struct _ts PyThreadState;
 typedef int Py_ssize_t;
 typedef int Py_ssize_t;
 typedef struct bufferinfo Py_buffer;
 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.
   // Instruct the compiler to optimize for the usual case.
   return UNLIKELY(is_on(NS_debug));
   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
 #endif
 
 
 /**
 /**

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

@@ -55,8 +55,8 @@ PUBLISHED:
   INLINE bool is_spam() const;
   INLINE bool is_spam() const;
   INLINE bool is_debug() const;
   INLINE bool is_debug() const;
 #else
 #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
 #endif
   INLINE bool is_info() const;
   INLINE bool is_info() const;
   INLINE bool is_warning() 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.
   // Instruct the compiler to optimize for the usual case.
   return UNLIKELY(get_unsafe_ptr()->is_spam());
   return UNLIKELY(get_unsafe_ptr()->is_spam());
 }
 }
-#else
-template<class GetCategory>
-constexpr bool NotifyCategoryProxy<GetCategory>::
-is_spam() {
-  return false;
-}
 #endif
 #endif
 
 
 /**
 /**
@@ -90,12 +84,6 @@ is_debug() {
   // Instruct the compiler to optimize for the usual case.
   // Instruct the compiler to optimize for the usual case.
   return UNLIKELY(get_unsafe_ptr()->is_debug());
   return UNLIKELY(get_unsafe_ptr()->is_debug());
 }
 }
-#else
-template<class GetCategory>
-constexpr bool NotifyCategoryProxy<GetCategory>::
-is_debug() {
-  return false;
-}
 #endif
 #endif
 
 
 /**
 /**

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

@@ -75,8 +75,8 @@ public:
   INLINE bool is_spam();
   INLINE bool is_spam();
   INLINE bool is_debug();
   INLINE bool is_debug();
 #else
 #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
 #endif
   INLINE bool is_info();
   INLINE bool is_info();
   INLINE bool is_warning();
   INLINE bool is_warning();

+ 45 - 54
makepanda/makepanda.py

@@ -681,6 +681,9 @@ if (COMPILER == "MSVC"):
         IncDirectory("FCOLLADA", GetThirdpartyDir() + "fcollada/include/FCollada")
         IncDirectory("FCOLLADA", GetThirdpartyDir() + "fcollada/include/FCollada")
     if (PkgSkip("ASSIMP")==0):
     if (PkgSkip("ASSIMP")==0):
         LibName("ASSIMP", GetThirdpartyDir() + "assimp/lib/assimp.lib")
         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")
         IncDirectory("ASSIMP", GetThirdpartyDir() + "assimp/include/assimp")
     if (PkgSkip("SQUISH")==0):
     if (PkgSkip("SQUISH")==0):
         if GetOptimize() <= 2:
         if GetOptimize() <= 2:
@@ -947,8 +950,6 @@ if (COMPILER=="GCC"):
 
 
     if GetTarget() == 'darwin':
     if GetTarget() == 'darwin':
         LibName("ALWAYS", "-framework AppKit")
         LibName("ALWAYS", "-framework AppKit")
-        if (PkgSkip("OPENCV")==0):
-            LibName("OPENCV", "-framework QuickTime")
         LibName("AGL", "-framework AGL")
         LibName("AGL", "-framework AGL")
         LibName("CARBON", "-framework Carbon")
         LibName("CARBON", "-framework Carbon")
         LibName("COCOA", "-framework Cocoa")
         LibName("COCOA", "-framework Cocoa")
@@ -1335,9 +1336,10 @@ def CompileCxx(obj,src,opts):
                     # Work around Apple compiler bug.
                     # Work around Apple compiler bug.
                     cmd += " -U__EXCEPTIONS"
                     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.
                 # 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"
                     cmd += " -fno-rtti"
 
 
         if ('SSE2' in opts or not PkgSkip("SSE2")) and not arch.startswith("arm") and arch != 'aarch64':
         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:
         else:
             cmd = GetAR() + ' cru ' + BracketNameWithQuotes(lib)
             cmd = GetAR() + ' cru ' + BracketNameWithQuotes(lib)
         for x in obj:
         for x in obj:
+            if GetLinkAllStatic() and x.endswith('.a'):
+                continue
             cmd += ' ' + BracketNameWithQuotes(x)
             cmd += ' ' + BracketNameWithQuotes(x)
         oscmd(cmd)
         oscmd(cmd)
 
 
@@ -3161,10 +3165,10 @@ CopyAllHeaders('panda/src/movies')
 CopyAllHeaders('panda/src/pgraphnodes')
 CopyAllHeaders('panda/src/pgraphnodes')
 CopyAllHeaders('panda/src/pgraph')
 CopyAllHeaders('panda/src/pgraph')
 CopyAllHeaders('panda/src/cull')
 CopyAllHeaders('panda/src/cull')
+CopyAllHeaders('panda/src/display')
 CopyAllHeaders('panda/src/chan')
 CopyAllHeaders('panda/src/chan')
 CopyAllHeaders('panda/src/char')
 CopyAllHeaders('panda/src/char')
 CopyAllHeaders('panda/src/dgraph')
 CopyAllHeaders('panda/src/dgraph')
-CopyAllHeaders('panda/src/display')
 CopyAllHeaders('panda/src/device')
 CopyAllHeaders('panda/src/device')
 CopyAllHeaders('panda/src/pnmtext')
 CopyAllHeaders('panda/src/pnmtext')
 CopyAllHeaders('panda/src/text')
 CopyAllHeaders('panda/src/text')
@@ -3297,7 +3301,6 @@ if (PkgSkip("PANDATOOL")==0):
     CopyAllHeaders('pandatool/src/ptloader')
     CopyAllHeaders('pandatool/src/ptloader')
     CopyAllHeaders('pandatool/src/miscprogs')
     CopyAllHeaders('pandatool/src/miscprogs')
     CopyAllHeaders('pandatool/src/pstatserver')
     CopyAllHeaders('pandatool/src/pstatserver')
-    CopyAllHeaders('pandatool/src/softprogs')
     CopyAllHeaders('pandatool/src/text-stats')
     CopyAllHeaders('pandatool/src/text-stats')
     CopyAllHeaders('pandatool/src/vrmlprogs')
     CopyAllHeaders('pandatool/src/vrmlprogs')
     CopyAllHeaders('pandatool/src/win-stats')
     CopyAllHeaders('pandatool/src/win-stats')
@@ -3553,6 +3556,7 @@ IGATEFILES += [
     "dSearchPath.h",
     "dSearchPath.h",
     "executionEnvironment.h",
     "executionEnvironment.h",
     "textEncoder.h",
     "textEncoder.h",
+    "textEncoder_ext.h",
     "filename.h",
     "filename.h",
     "filename_ext.h",
     "filename_ext.h",
     "globPattern.h",
     "globPattern.h",
@@ -3817,7 +3821,7 @@ if (not RUNTIME):
 if (not RUNTIME):
 if (not RUNTIME):
   OPTS=['DIR:panda/src/gobj', 'BUILDING:PANDA',  'NVIDIACG', 'ZLIB', 'SQUISH']
   OPTS=['DIR:panda/src/gobj', 'BUILDING:PANDA',  'NVIDIACG', 'ZLIB', 'SQUISH']
   TargetAdd('p3gobj_composite1.obj', opts=OPTS, input='p3gobj_composite1.cxx')
   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']
   OPTS=['DIR:panda/src/gobj', 'NVIDIACG', 'ZLIB', 'SQUISH', 'PYTHON']
   IGATEFILES=GetDirectoryContents('panda/src/gobj', ["*.h", "*_composite*.cxx"])
   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.in', opts=['IMOD:panda3d.core', 'ILIB:libp3cull', 'SRCDIR:panda/src/cull'])
   TargetAdd('libp3cull_igate.obj', input='libp3cull.in', opts=["DEPENDENCYONLY"])
   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/
 # DIRECTORY: panda/src/dgraph/
 #
 #
@@ -3944,6 +3916,7 @@ if (not RUNTIME):
 
 
 if (not RUNTIME):
 if (not RUNTIME):
   OPTS=['DIR:panda/src/display', 'BUILDING:PANDA']
   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_composite1.obj', opts=OPTS, input='p3display_composite1.cxx')
   TargetAdd('p3display_composite2.obj', opts=OPTS, input='p3display_composite2.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('subprocessWindowBuffer.obj', opts=OPTS, input='subprocessWindowBuffer.cxx')
     TargetAdd('libp3subprocbuffer.ilb', input='subprocessWindowBuffer.obj')
     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/
 # DIRECTORY: panda/src/pnmtext/
 #
 #
@@ -4169,6 +4174,7 @@ if (not RUNTIME):
   TargetAdd('libpanda.dll', input='p3device_composite2.obj')
   TargetAdd('libpanda.dll', input='p3device_composite2.obj')
   TargetAdd('libpanda.dll', input='p3dgraph_composite1.obj')
   TargetAdd('libpanda.dll', input='p3dgraph_composite1.obj')
   TargetAdd('libpanda.dll', input='p3dgraph_composite2.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_composite1.obj')
   TargetAdd('libpanda.dll', input='p3display_composite2.obj')
   TargetAdd('libpanda.dll', input='p3display_composite2.obj')
   TargetAdd('libpanda.dll', input='p3pipeline_composite1.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('p3pstatserver_composite1.obj', opts=OPTS, input='p3pstatserver_composite1.cxx')
     TargetAdd('libp3pstatserver.lib', input='p3pstatserver_composite1.obj')
     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/
 # 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\config_ptloader.h"></File>
 				<File RelativePath="..\pandatool\src\ptloader\loaderFileTypePandatool.h"></File>
 				<File RelativePath="..\pandatool\src\ptloader\loaderFileTypePandatool.h"></File>
 			</Filter>
 			</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">
 			<Filter Name="imageprogs">
 				<File RelativePath="..\pandatool\src\imageprogs\imageTrans.h"></File>
 				<File RelativePath="..\pandatool\src\imageprogs\imageTrans.h"></File>
 				<File RelativePath="..\pandatool\src\imageprogs\imageTransformColors.cxx"></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\lwoSurfaceBlockImage.h"></File>
 				<File RelativePath="..\pandatool\src\lwo\lwoSurfaceBlockCoordSys.cxx"></File>
 				<File RelativePath="..\pandatool\src\lwo\lwoSurfaceBlockCoordSys.cxx"></File>
 			</Filter>
 			</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">
 			<Filter Name="xfileprogs">
 				<File RelativePath="..\pandatool\src\xfileprogs\eggToX.cxx"></File>
 				<File RelativePath="..\pandatool\src\xfileprogs\eggToX.cxx"></File>
 				<File RelativePath="..\pandatool\src\xfileprogs\xFileTrans.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),
   Namable(name),
   _pending_lock(name),
   _pending_lock(name),
   _pending_cvar(_pending_lock),
   _pending_cvar(_pending_lock),
-  _bound_joints(BitArray::all_on())
+  _bound_joints(BitArray::all_on()),
+  _part(part)
 {
 {
 #ifdef DO_MEMORY_USAGE
 #ifdef DO_MEMORY_USAGE
   MemoryUsage::update_type(this, get_class_type());
   MemoryUsage::update_type(this, get_class_type());
 #endif
 #endif
 
 
   _pending = true;
   _pending = true;
-  _part = part;
   _anim = nullptr;
   _anim = nullptr;
   _channel_index = -1;
   _channel_index = -1;
   set_frame_rate(frame_rate);
   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:
 public:
   AnimControl(const std::string &name, PartBundle *part,
   AnimControl(const std::string &name, PartBundle *part,
               double frame_rate, int num_frames);
               double frame_rate, int num_frames);
+  AnimControl(const AnimControl &copy) = delete;
+
   void setup_anim(PartBundle *part, AnimBundle *anim, int channel_index,
   void setup_anim(PartBundle *part, AnimBundle *anim, int channel_index,
                   const BitArray &bound_joints);
                   const BitArray &bound_joints);
   void set_bound_joints(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
   // 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
   // can't include partBundle.h for circular reasons.  But it actually keeps a
   // pointer to a PartBundle.
   // pointer to a PartBundle.
-  PT(PartGroup) _part;
+  const PT(PartGroup) _part;
   PT(AnimBundle) _anim;
   PT(AnimBundle) _anim;
   int _channel_index;
   int _channel_index;
 
 

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

@@ -556,8 +556,15 @@ control_removed(AnimControl *control) {
     CDStageWriter cdata(_cycler, pipeline_stage);
     CDStageWriter cdata(_cycler, pipeline_stage);
     ChannelBlend::iterator cbi = cdata->_blend.find(control);
     ChannelBlend::iterator cbi = cdata->_blend.find(control);
     if (cbi != cdata->_blend.end()) {
     if (cbi != cdata->_blend.end()) {
+      cdata->_net_blend -= cbi->second;
       cdata->_blend.erase(cbi);
       cdata->_blend.erase(cbi);
       cdata->_anim_changed = true;
       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);
   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) {
   if (accelerated) {
     properties.set_force_hardware(1);
     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"
 #import "cocoaPandaApp.h"
+#include "config_cocoadisplay.h"
 
 
 @implementation CocoaPandaApp
 @implementation CocoaPandaApp
 - (void) sendEvent: (NSEvent *) event {
 - (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) {
     if (geom->get_primitive_type() == Geom::PT_polygons) {
       Thread *current_thread = Thread::get_current_thread();
       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());
       GeomVertexReader vertex(data, InternalName::get_vertex());
 
 
       int num_primitives = geom->get_num_primitives();
       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.
         // frames, so we won't have to recompute it each frame.
         int num_drs = win->get_num_active_display_regions();
         int num_drs = win->get_num_active_display_regions();
         for (int i = 0; i < num_drs; ++i) {
         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) {
           if (dr != nullptr) {
             NodePath camera_np = dr->get_camera(current_thread);
             NodePath camera_np = dr->get_camera(current_thread);
             if (!camera_np.is_empty()) {
             if (!camera_np.is_empty()) {
@@ -1359,7 +1359,7 @@ is_scene_root(const PandaNode *node) {
     if (win->is_active() && win->get_gsg()->is_active()) {
     if (win->is_active() && win->get_gsg()->is_active()) {
       int num_display_regions = win->get_num_active_display_regions();
       int num_display_regions = win->get_num_active_display_regions();
       for (int i = 0; i < num_display_regions; i++) {
       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) {
         if (dr != nullptr) {
           NodePath camera = dr->get_camera();
           NodePath camera = dr->get_camera();
           if (camera.is_empty()) {
           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();
         int num_display_regions = win->get_num_active_display_regions();
         for (int i = 0; i < num_display_regions; i++) {
         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) {
           if (dr != nullptr) {
             cull_and_draw_together(win, dr, current_thread);
             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);
       PStatTimer timer(win->get_cull_window_pcollector(), current_thread);
       int num_display_regions = win->get_num_active_display_regions();
       int num_display_regions = win->get_num_active_display_regions();
       for (int i = 0; i < num_display_regions; ++i) {
       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) {
         if (dr != nullptr) {
           PT(SceneSetup) scene_setup;
           PT(SceneSetup) scene_setup;
           PT(CullResult) cull_result;
           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();
           int num_display_regions = win->get_num_active_display_regions();
           for (int i = 0; i < num_display_regions; ++i) {
           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) {
             if (dr != nullptr) {
               do_draw(win, gsg, dr, current_thread);
               do_draw(win, gsg, dr, current_thread);
             }
             }

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

@@ -697,9 +697,6 @@ void GraphicsOutput::
 remove_all_display_regions() {
 remove_all_display_regions() {
   LightMutexHolder holder(_lock);
   LightMutexHolder holder(_lock);
 
 
-  CDWriter cdata(_cycler, true);
-  cdata->_active_display_regions_stale = true;
-
   TotalDisplayRegions::iterator dri;
   TotalDisplayRegions::iterator dri;
   for (dri = _total_display_regions.begin();
   for (dri = _total_display_regions.begin();
        dri != _total_display_regions.end();
        dri != _total_display_regions.end();
@@ -713,6 +710,12 @@ remove_all_display_regions() {
   }
   }
   _total_display_regions.clear();
   _total_display_regions.clear();
   _total_display_regions.push_back(_overlay_display_region);
   _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::
 int GraphicsOutput::
 get_num_display_regions() const {
 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);
     find(_total_display_regions.begin(), _total_display_regions.end(), drp);
   if (dri != _total_display_regions.end()) {
   if (dri != _total_display_regions.end()) {
     // Let's aggressively clean up the display region too.
     // Let's aggressively clean up the display region too.
-    CDWriter cdata(_cycler, true);
     display_region->cleanup();
     display_region->cleanup();
     display_region->_window = nullptr;
     display_region->_window = nullptr;
     _total_display_regions.erase(dri);
     _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;
     return true;
   }
   }
 
 

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

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

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

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

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

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

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

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

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

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

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

@@ -12,10 +12,14 @@
  */
  */
 
 
 #include "standardMunger.h"
 #include "standardMunger.h"
-#include "renderState.h"
-#include "graphicsStateGuardian.h"
+
 #include "config_gobj.h"
 #include "config_gobj.h"
+
 #include "displayRegion.h"
 #include "displayRegion.h"
+#include "graphicsStateGuardian.h"
+#include "lightAttrib.h"
+#include "materialAttrib.h"
+#include "renderState.h"
 
 
 TypeHandle StandardMunger::_type_handle;
 TypeHandle StandardMunger::_type_handle;
 
 
@@ -55,24 +59,10 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state,
     const ColorScaleAttrib *color_scale_attrib;
     const ColorScaleAttrib *color_scale_attrib;
 
 
     if (state->get_attrib(color_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) &&
     } else if (state->get_attrib(color_scale_attrib) &&
                color_scale_attrib->has_scale()) {
                color_scale_attrib->has_scale()) {

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

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

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

@@ -18,6 +18,8 @@
 #include "graphicsEngine.h"
 #include "graphicsEngine.h"
 #include "config_display.h"
 #include "config_display.h"
 #include "nativeWindowHandle.h"
 #include "nativeWindowHandle.h"
+#include "mouseButton.h"
+#include "throw_event.h"
 
 
 using std::string;
 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;
   const LMatrix4 &to_uv = _invert_uvs ? lens_to_uv_inverted : lens_to_uv;
 
 
   // Iterate through all the vertices in the Geom.
   // 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();
   CPT(GeomVertexFormat) vformat = vdata->get_format();
   if (!vformat->has_column(_texcoord_name) || (_texcoord_3d && vformat->get_column(_texcoord_name)->get_num_components() < 3)) {
   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();
   PT(GeomVertexData) modify_vdata = geom->modify_vertex_data();
 
 
   // Maybe the vdata has animation that we should consider.
   // 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 texcoord(modify_vdata, _texcoord_name, current_thread);
   GeomVertexWriter color(modify_vdata, 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();
   Thread *current_thread = Thread::get_current_thread();
   PT(Geom) new_geom = geom->make_copy();
   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();
   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());
   GeomVertexRewriter vertex(vdata, InternalName::get_vertex());
   while (!vertex.is_at_end()) {
   while (!vertex.is_at_end()) {
     LVertex vert = vertex.get_data3();
     LVertex vert = vertex.get_data3();

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

@@ -10,34 +10,3 @@
  * @author drose
  * @author drose
  * @date 2005-03-11
  * @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;
 GeomMunger *DXGeomMunger9::_deleted_chain = nullptr;
 TypeHandle DXGeomMunger9::_type_handle;
 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 {
 class EXPCL_PANDADX DXGeomMunger9 : public StandardMunger, public WeakPointerCallback {
 public:
 public:
-  INLINE DXGeomMunger9(GraphicsStateGuardian *gsg, const RenderState *state);
+  DXGeomMunger9(GraphicsStateGuardian *gsg, const RenderState *state);
   virtual ~DXGeomMunger9();
   virtual ~DXGeomMunger9();
   ALLOC_DELETED_CHAIN(DXGeomMunger9);
   ALLOC_DELETED_CHAIN(DXGeomMunger9);
 
 

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

@@ -140,6 +140,7 @@ DXGraphicsStateGuardian9(GraphicsEngine *engine, GraphicsPipe *pipe) :
 
 
   _last_fvf = 0;
   _last_fvf = 0;
   _num_bound_streams = 0;
   _num_bound_streams = 0;
+  _white_vbuffer = nullptr;
 
 
   _vertex_shader_version_major = 0;
   _vertex_shader_version_major = 0;
   _vertex_shader_version_minor = 0;
   _vertex_shader_version_minor = 0;
@@ -798,9 +799,9 @@ clear(DrawableRegion *clearable) {
     main_flags |=  D3DCLEAR_TARGET;
     main_flags |=  D3DCLEAR_TARGET;
   }
   }
 
 
-  if (clearable->get_clear_depth_active()) {
+  if (clearable->get_clear_depth_active() &&
+      _screen->_presentation_params.EnableAutoDepthStencil) {
     aux_flags |=  D3DCLEAR_ZBUFFER;
     aux_flags |=  D3DCLEAR_ZBUFFER;
-    nassertv(_screen->_presentation_params.EnableAutoDepthStencil);
   }
   }
 
 
   if (clearable->get_clear_stencil_active()) {
   if (clearable->get_clear_stencil_active()) {
@@ -4545,6 +4546,11 @@ reset_d3d_device(D3DPRESENT_PARAMETERS *presentation_params,
     release_all_vertex_buffers();
     release_all_vertex_buffers();
     release_all_index_buffers();
     release_all_index_buffers();
 
 
+    if (_white_vbuffer != nullptr) {
+      _white_vbuffer->Release();
+      _white_vbuffer = nullptr;
+    }
+
     // must be called before reset
     // must be called before reset
     Thread *current_thread = Thread::get_current_thread();
     Thread *current_thread = Thread::get_current_thread();
     _prepared_objects->begin_frame(this, 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)
 #define GAMMA_1 (255.0 * 256.0)
 
 
 static bool _gamma_table_initialized = false;
 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;
   int i;
   double gamma_correction;
   double gamma_correction;
 
 
@@ -5298,7 +5304,7 @@ get_gamma_table(void) {
     HDC hdc = GetDC(nullptr);
     HDC hdc = GetDC(nullptr);
 
 
     if (hdc) {
     if (hdc) {
-      if (GetDeviceGammaRamp (hdc, (LPVOID) _orignial_gamma_table)) {
+      if (GetDeviceGammaRamp (hdc, (LPVOID) _original_gamma_table)) {
         _gamma_table_initialized = true;
         _gamma_table_initialized = true;
         get = true;
         get = true;
       }
       }
@@ -5323,10 +5329,10 @@ static_set_gamma(bool restore, PN_stdfloat gamma) {
     unsigned short ramp [256 * 3];
     unsigned short ramp [256 * 3];
 
 
     if (restore && _gamma_table_initialized) {
     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 {
     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)) {
     if (SetDeviceGammaRamp (hdc, ramp)) {
@@ -5404,6 +5410,40 @@ set_cg_device(LPDIRECT3DDEVICE9 cg_device) {
 #endif // HAVE_CG
 #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 std::string KEY;
 
 
 typedef struct _KEY_ELEMENT
 typedef struct _KEY_ELEMENT

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

@@ -168,6 +168,7 @@ public:
   static void set_cg_device(LPDIRECT3DDEVICE9 cg_device);
   static void set_cg_device(LPDIRECT3DDEVICE9 cg_device);
   virtual bool get_supports_cg_profile(const std::string &name) const;
   virtual bool get_supports_cg_profile(const std::string &name) const;
 
 
+  LPDIRECT3DVERTEXBUFFER9 get_white_vbuffer();
 
 
 protected:
 protected:
   void do_issue_transform();
   void do_issue_transform();
@@ -274,12 +275,6 @@ protected:
 
 
   RenderBuffer::Type _cur_read_pixel_buffer;  // source for copy_pixel_buffer operation
   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 {
   enum DxgsgFogType {
     None,
     None,
     PerVertexFog=D3DRS_FOGVERTEXMODE,
     PerVertexFog=D3DRS_FOGVERTEXMODE,
@@ -320,6 +315,7 @@ protected:
 
 
   DWORD _last_fvf;
   DWORD _last_fvf;
   int _num_bound_streams;
   int _num_bound_streams;
+  LPDIRECT3DVERTEXBUFFER9 _white_vbuffer;
 
 
   // Cache the data necessary to bind each particular light each frame, so if
   // 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
   // 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
     // arrays ("streams"), and we repeatedly iterate the parameters to pull
     // out only those for a single stream.
     // out only those for a single stream.
 
 
+    bool apply_white_color = false;
+
     int number_of_arrays = gsg->_data_reader->get_num_arrays();
     int number_of_arrays = gsg->_data_reader->get_num_arrays();
     for (int array_index = 0; array_index < number_of_arrays; ++array_index) {
     for (int array_index = 0; array_index < number_of_arrays; ++array_index) {
       const GeomVertexArrayDataHandle* array_reader =
       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;
         const GeomVertexArrayDataHandle *param_array_reader;
         Geom::NumericType numeric_type;
         Geom::NumericType numeric_type;
         int num_values, start, stride;
         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
           // 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.
           // 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";
           dxgsg9_cat.info() << "Geometry contains no data for shader parameter " << *name << "\n";
+          if (name == InternalName::get_color()) {
+            apply_white_color = true;
+          }
           continue;
           continue;
         }
         }
 
 
@@ -564,6 +574,19 @@ update_shader_vertex_arrays(DXShaderContext9 *prev, GSG *gsg, bool force) {
 
 
     _num_bound_streams = number_of_arrays;
     _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 &&
     if (_vertex_element_array != nullptr &&
         _vertex_element_array->add_end_vertex_element()) {
         _vertex_element_array->add_end_vertex_element()) {
       if (dxgsg9_cat.is_debug()) {
       if (dxgsg9_cat.is_debug()) {

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

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

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

@@ -17,7 +17,6 @@
 #include "pandabase.h"
 #include "pandabase.h"
 #include "winGraphicsWindow.h"
 #include "winGraphicsWindow.h"
 #include "dxGraphicsStateGuardian9.h"
 #include "dxGraphicsStateGuardian9.h"
-#include "dxInput9.h"
 #include "wdxGraphicsPipe9.h"
 #include "wdxGraphicsPipe9.h"
 
 
 class wdxGraphicsPipe9;
 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) {
   } else if (size == 4 && double_prec) {
     mat_size = sizeof(UnalignedLMatrix4d);
     mat_size = sizeof(UnalignedLMatrix4d);
   } else {
   } else {
-    assert(false);
+    nassertv_always(false);
+    return; // Make sure compiler knows control flow doesn't proceed.
   }
   }
 
 
   view.len = length * mat_size;
   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
 #ifdef _MSC_VER
 // Ugh... MSVC needs this because they still don't have a decent linker.
 // 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_uchar>;
 template class EXPORT_THIS Extension<PTA_ushort>;
 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.
 // OpenGL ES 2 has no fixed-function pipeline.
 #undef SUPPORT_FIXED_FUNCTION
 #undef SUPPORT_FIXED_FUNCTION
 
 
-#ifdef IS_OSX
+#ifdef BUILD_IPHONE
   #include <OpenGLES/ES2/gl.h>
   #include <OpenGLES/ES2/gl.h>
 // #include <OpenGLESES2glext.h>
 // #include <OpenGLESES2glext.h>
 #else
 #else

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

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

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

@@ -226,12 +226,14 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte
         if (!resource) {
         if (!resource) {
           resource = "unknown";
           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") {
       } else if (loc != 0 && bind._id._name == "vtx_position") {
         // We really have to bind the vertex position to attribute 0, since
         // 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)
         GLCAT.debug(false)
           << " is bound to a conventional attribute (" << resource << ")\n";
           << " 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
 #endif
 
 

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

@@ -1097,6 +1097,15 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot,
 #endif
 #endif
       glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rbm[slot]);
       glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rbm[slot]);
       GLuint format = GL_DEPTH_COMPONENT;
       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) {
       if (tex) {
         switch (tex->get_format()) {
         switch (tex->get_format()) {
           case Texture::F_depth_component16:
           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);
       GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f);
     } else
     } else
 #endif // NDEBUG
 #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)) {
                                        start, stride)) {
         if (!setup_array_data(client_pointer, array_reader, force)) {
         if (!setup_array_data(client_pointer, array_reader, force)) {
           return false;
           return false;
@@ -4409,7 +4410,13 @@ update_standard_vertex_arrays(bool force) {
         glDisableClientState(GL_COLOR_ARRAY);
         glDisableClientState(GL_COLOR_ARRAY);
 
 
         // Since we don't have per-vertex color, the implicit color is white.
         // 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
     // 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:
   case GL_REPEAT:
     return SamplerState::WM_repeat;
     return SamplerState::WM_repeat;
 
 
+  case GL_MIRRORED_REPEAT:
+    return SamplerState::WM_mirror;
+
 #ifndef OPENGLES
 #ifndef OPENGLES
   case GL_MIRROR_CLAMP_EXT:
   case GL_MIRROR_CLAMP_EXT:
   case GL_MIRROR_CLAMP_TO_EDGE_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) {
         if (p == _color_attrib_index) {
           // Vertex colors are disabled or not present.  Apply flat color.
           // Vertex colors are disabled or not present.  Apply flat color.
 #ifdef STDFLOAT_DOUBLE
 #ifdef STDFLOAT_DOUBLE
-          _glgsg->_glVertexAttrib4dv(p, color_attrib->get_color().get_data());
+          _glgsg->_glVertexAttrib4dv(p, _glgsg->_scene_graph_color.get_data());
 #else
 #else
-          _glgsg->_glVertexAttrib4fv(p, color_attrib->get_color().get_data());
+          _glgsg->_glVertexAttrib4fv(p, _glgsg->_scene_graph_color.get_data());
 #endif
 #endif
         }
         }
       }
       }

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

@@ -286,6 +286,28 @@ make_nonindexed(bool composite_only) {
   return num_changed;
   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
  * Replaces the ith GeomPrimitive object stored within the Geom with the new
  * object.
  * object.
@@ -1311,8 +1333,7 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
   int num_vertices = 0;
   int num_vertices = 0;
 
 
   // Get the vertex data, after animation.
   // 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
   // Now actually compute the bounding volume.  We do this by using
   // calc_tight_bounds to determine our box first.
   // 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);
   void offset_vertices(const GeomVertexData *data, int offset);
   int make_nonindexed(bool composite_only);
   int make_nonindexed(bool composite_only);
 
 
+  CPT(GeomVertexData) get_animated_vertex_data(bool force, Thread *current_thread) const;
+
   INLINE bool is_empty() const;
   INLINE bool is_empty() const;
 
 
   INLINE size_t get_num_primitives() 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::
 INLINE int GeomVertexArrayData::
 get_num_rows() const {
 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::
 INLINE int GeomVertexData::
 get_num_rows() const {
 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);
   _cdata = (GeomVertexData::CData *)_object->_cycler.read_unlocked(_current_thread);
   _got_array_readers = false;
   _got_array_readers = false;
 
 
+#ifdef DO_PIPELINING
   _cdata->ref();
   _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());
             const TransformBlend &blend = blend_table->get_blend(from.get_data1i());
             LVecBase4 weights = LVecBase4::zero();
             LVecBase4 weights = LVecBase4::zero();
             LVecBase4i indices(0, 0, 0, 0);
             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()) {
             if (weight.has_column()) {
               weight.set_data4(weights);
               weight.set_data4(weights);
             }
             }

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

@@ -1619,8 +1619,8 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) {
        ++qsi) {
        ++qsi) {
     Shader *shader = qsi->first;
     Shader *shader = qsi->first;
     ShaderContext *sc = shader->prepare_now(this, gsg);
     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
       // 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
       // this GSG, we'll just end up carrying the texture memory in RAM
       // forever, instead of dumping it as soon as it gets prepared).
       // 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_rgb_operand2 = other._combine_rgb_operand2;
   _combine_alpha_mode = other._combine_alpha_mode;
   _combine_alpha_mode = other._combine_alpha_mode;
   _combine_alpha_source0 = other._combine_alpha_source0;
   _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_source1 = other._combine_alpha_source1;
   _combine_alpha_operand1 = other._combine_alpha_operand1;
   _combine_alpha_operand1 = other._combine_alpha_operand1;
   _combine_alpha_source2 = other._combine_alpha_source2;
   _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();
     PT(Geom) geom = orig_geom->make_copy();
 
 
     // Ensure that any vertex animation has been applied.
     // 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
     // Now get a modifiable pointer to the vertex data in the new Geom.  This
     // will actually perform a deep copy of the vertex data.
     // 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) {
     if (data_sent > 0) {
       total_sent += data_sent;
       total_sent += data_sent;
     }
     }
-    double last_report = 0;
+
     while (!okflag && tcp->Active() &&
     while (!okflag && tcp->Active() &&
            (data_sent > 0 || tcp->GetLastError() == LOCAL_BLOCKING_ERROR)) {
            (data_sent > 0 || tcp->GetLastError() == LOCAL_BLOCKING_ERROR)) {
       if (data_sent == 0) {
       if (data_sent == 0) {

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

@@ -64,7 +64,7 @@ private:
   CGGammaValue _gOriginalRedTable[ 256 ];
   CGGammaValue _gOriginalRedTable[ 256 ];
   CGGammaValue _gOriginalGreenTable[ 256 ];
   CGGammaValue _gOriginalGreenTable[ 256 ];
   CGGammaValue _gOriginalBlueTable[ 256 ];
   CGGammaValue _gOriginalBlueTable[ 256 ];
-  CGTableCount _sampleCount;
+  uint32_t _sampleCount;
   CGDisplayErr _cgErr;
   CGDisplayErr _cgErr;
 
 
 public:
 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<<"_lifespan "<<_lifespan<<"\n";
   out.width(indent+2); out<<""; out<<"_alive "<<_alive<<"\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<<"_index "<<_index<<"\n";
-  out.width(indent+2); out<<""; out<<"_last_position "<<_last_position<<"\n";
   PhysicsObject::write(out, indent+2);
   PhysicsObject::write(out, indent+2);
   #endif //] NDEBUG
   #endif //] NDEBUG
 }
 }

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

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

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

@@ -68,7 +68,7 @@ make_off() {
  */
  */
 CPT(RenderAttrib) ColorAttrib::
 CPT(RenderAttrib) ColorAttrib::
 make_default() {
 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",
     register_type(_type_handle, "ColorAttrib",
                   RenderAttrib::get_class_type());
                   RenderAttrib::get_class_type());
     _attrib_slot = register_slot(_type_handle, 100,
     _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 {
   virtual TypeHandle get_type() const {
     return get_class_type();
     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();
     CPT(TransformState) node_transform = _node_reader.get_transform();
     node_effects->cull_callback(trav, *this, node_transform, node_state);
     node_effects->cull_callback(trav, *this, node_transform, node_state);
     apply_transform(node_transform);
     apply_transform(node_transform);
+
+    // The cull callback may have changed the node properties.
+    _node_reader.check_cached(false);
   }
   }
 
 
   if (!node_state->is_empty()) {
   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);
     geom = transformer.premunge_geom(geom, munger);
 
 
     // Prepare each of the vertex arrays in the munged Geom.
     // 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);
     GeomVertexDataPipelineReader vdata_reader(vdata, current_thread);
     int num_arrays = vdata_reader.get_num_arrays();
     int num_arrays = vdata_reader.get_num_arrays();
     for (int i = 0; i < num_arrays; ++i) {
     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) {
   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     CPT(Geom) geom = (*gi)._geom.get_read_pointer();
     CPT(Geom) geom = (*gi)._geom.get_read_pointer();
     geom->calc_tight_bounds(min_point, max_point, found_any,
     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,
                             !next_transform->is_identity(), mat,
                             current_thread);
                             current_thread);
   }
   }

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

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

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

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

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

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

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

@@ -11,14 +11,6 @@
  * @date 2007-06-20
  * @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 {
 class EXPCL_PANDA_PIPELINE BlockerSimple {
 protected:
 protected:
-  INLINE BlockerSimple();
+  constexpr BlockerSimple() = default;
   INLINE ~BlockerSimple();
   INLINE ~BlockerSimple();
 
 
 protected:
 protected:
@@ -38,7 +38,7 @@ protected:
     F_has_waiters  = 0x40000000,
     F_has_waiters  = 0x40000000,
   };
   };
 
 
-  unsigned int _flags;
+  unsigned int _flags = 0;
 
 
   friend class ThreadSimpleManager;
   friend class ThreadSimpleManager;
 };
 };

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

@@ -71,7 +71,10 @@ init_libpipeline() {
   }
   }
   initialized = true;
   initialized = true;
 
 
+#ifdef DO_PIPELINING
   CycleData::init_type();
   CycleData::init_type();
+#endif
+
   MainThread::init_type();
   MainThread::init_type();
   ExternalThread::init_type();
   ExternalThread::init_type();
   GenericThread::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
  * PANDA 3D SOFTWARE
  * Copyright (c) Carnegie Mellon University.  All rights reserved.
  * Copyright (c) Carnegie Mellon University.  All rights reserved.
  *
  *
@@ -10,7 +6,10 @@
  * license.  You should have received a copy of this license along
  * license.  You should have received a copy of this license along
  * with this source code in a file named "LICENSE."
  * 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
 /* This is the implementation of user-space context switching using
    setmp() / longjmp().  This is the hackier implementation,
    setmp() / longjmp().  This is the hackier implementation,
@@ -90,14 +89,14 @@ void
 cs_longjmp(cs_jmp_buf env) {
 cs_longjmp(cs_jmp_buf env) {
   _asm {
   _asm {
     mov eax, env;
     mov eax, env;
-    
+
     mov ebx, [eax + 0];
     mov ebx, [eax + 0];
     mov edi, [eax + 4];
     mov edi, [eax + 4];
     mov esi, [eax + 8];
     mov esi, [eax + 8];
     mov ebp, [eax + 12];
     mov ebp, [eax + 12];
     mov esp, [eax + 16];
     mov esp, [eax + 16];
     mov edx, [eax + 20];
     mov edx, [eax + 20];
-    
+
     frstor [eax + 24];  /* restore floating-point state */
     frstor [eax + 24];  /* restore floating-point state */
 
 
     mov eax, 1;   /* return 1 from setjmp: pass 2 return */
     mov eax, 1;   /* return 1 from setjmp: pass 2 return */
@@ -251,7 +250,7 @@ setup_context_1(void) {
 }
 }
 
 
 void
 void
-init_thread_context(struct ThreadContext *context, 
+init_thread_context(struct ThreadContext *context,
                     unsigned char *stack, size_t stack_size,
                     unsigned char *stack, size_t stack_size,
                     ThreadFunction *thread_func, void *data) {
                     ThreadFunction *thread_func, void *data) {
   /* Copy all of the input parameters to static variables, then begin
   /* Copy all of the input parameters to static variables, then begin
@@ -263,7 +262,7 @@ init_thread_context(struct ThreadContext *context,
   st_data = data;
   st_data = data;
 
 
   setup_context_1();
   setup_context_1();
-}  
+}
 
 
 void
 void
 save_thread_context(struct ThreadContext *context,
 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
  * PANDA 3D SOFTWARE
  * Copyright (c) Carnegie Mellon University.  All rights reserved.
  * Copyright (c) Carnegie Mellon University.  All rights reserved.
  *
  *
@@ -10,7 +6,10 @@
  * license.  You should have received a copy of this license along
  * license.  You should have received a copy of this license along
  * with this source code in a file named "LICENSE."
  * 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
 /* This is the implementation of user-space context switching using
    posix threads to manage the different execution contexts.  This
    posix threads to manage the different execution contexts.  This
@@ -44,7 +43,7 @@ struct ThreadContext {
   pthread_mutex_t _ready_mutex;
   pthread_mutex_t _ready_mutex;
   pthread_cond_t _ready_cvar;
   pthread_cond_t _ready_cvar;
   int _ready_flag;
   int _ready_flag;
-  
+
   /* This is set FALSE while the thread is alive, and TRUE if the
   /* This is set FALSE while the thread is alive, and TRUE if the
      thread is to be terminated when it next wakes up. */
      thread is to be terminated when it next wakes up. */
   int _terminated;
   int _terminated;
@@ -83,19 +82,19 @@ thread_main(void *data) {
 }
 }
 
 
 void
 void
-init_thread_context(struct ThreadContext *context, 
+init_thread_context(struct ThreadContext *context,
                     unsigned char *stack, size_t stack_size,
                     unsigned char *stack, size_t stack_size,
                     ThreadFunction *thread_func, void *data) {
                     ThreadFunction *thread_func, void *data) {
   context->_thread_func = thread_func;
   context->_thread_func = thread_func;
   context->_data = data;
   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_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
 void
@@ -142,7 +141,7 @@ switch_to_thread_context(struct ThreadContext *from_context,
     /* We've been rudely terminated.  Exit gracefully. */
     /* We've been rudely terminated.  Exit gracefully. */
     pthread_exit(NULL);
     pthread_exit(NULL);
   }
   }
-  
+
   /* Now we have been signaled again, and we're ready to resume the
   /* Now we have been signaled again, and we're ready to resume the
      thread. */
      thread. */
   longjmp(from_context->_jmp_context, 1);
   longjmp(from_context->_jmp_context, 1);
@@ -164,7 +163,7 @@ alloc_thread_context() {
   pthread_mutexattr_init(&attr);
   pthread_mutexattr_init(&attr);
   // The symbol PTHREAD_MUTEX_DEFAULT isn't always available?
   // The symbol PTHREAD_MUTEX_DEFAULT isn't always available?
   //  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
   //  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_mutexattr_destroy(&attr);
 
 
   pthread_cond_init(&context->_ready_cvar, NULL);
   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
  * PANDA 3D SOFTWARE
  * Copyright (c) Carnegie Mellon University.  All rights reserved.
  * Copyright (c) Carnegie Mellon University.  All rights reserved.
  *
  *
@@ -10,7 +6,10 @@
  * license.  You should have received a copy of this license along
  * license.  You should have received a copy of this license along
  * with this source code in a file named "LICENSE."
  * 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
 /* This is the implementation of user-space context switching using
    getcontext() / setcontext().  This is the preferred implementation,
    getcontext() / setcontext().  This is the preferred implementation,
@@ -43,7 +42,7 @@ begin_context(ThreadFunction *thread_func, void *data) {
 }
 }
 
 
 void
 void
-init_thread_context(struct ThreadContext *context, 
+init_thread_context(struct ThreadContext *context,
                     unsigned char *stack, size_t stack_size,
                     unsigned char *stack, size_t stack_size,
                     ThreadFunction *thread_func, void *data) {
                     ThreadFunction *thread_func, void *data) {
   if (getcontext(&context->_ucontext) != 0) {
   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
  * PANDA 3D SOFTWARE
  * Copyright (c) Carnegie Mellon University.  All rights reserved.
  * Copyright (c) Carnegie Mellon University.  All rights reserved.
  *
  *
@@ -10,7 +6,10 @@
  * license.  You should have received a copy of this license along
  * license.  You should have received a copy of this license along
  * with this source code in a file named "LICENSE."
  * 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
 /* This is the implementation of user-space context switching using
    native Windows threading constructs to manage the different
    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
   /* This event is in the signaled state when the thread is ready to
      roll. */
      roll. */
   HANDLE _ready;
   HANDLE _ready;
-  
+
   /* This is set FALSE while the thread is alive, and TRUE if the
   /* This is set FALSE while the thread is alive, and TRUE if the
      thread is to be terminated when it next wakes up. */
      thread is to be terminated when it next wakes up. */
   int _terminated;
   int _terminated;
@@ -71,13 +70,13 @@ thread_main(LPVOID data) {
 }
 }
 
 
 void
 void
-init_thread_context(struct ThreadContext *context, 
+init_thread_context(struct ThreadContext *context,
                     unsigned char *stack, size_t stack_size,
                     unsigned char *stack, size_t stack_size,
                     ThreadFunction *thread_func, void *data) {
                     ThreadFunction *thread_func, void *data) {
   context->_thread_func = thread_func;
   context->_thread_func = thread_func;
   context->_data = data;
   context->_data = data;
 
 
-  context->_thread = CreateThread(NULL, stack_size, 
+  context->_thread = CreateThread(NULL, stack_size,
                                   thread_main, context, 0, NULL);
                                   thread_main, context, 0, NULL);
 }
 }
 
 
@@ -117,7 +116,7 @@ switch_to_thread_context(struct ThreadContext *from_context,
     /* We've been rudely terminated.  Exit gracefully. */
     /* We've been rudely terminated.  Exit gracefully. */
     ExitThread(1);
     ExitThread(1);
   }
   }
-  
+
   /* Now we have been signaled again, and we're ready to resume the
   /* Now we have been signaled again, and we're ready to resume the
      thread. */
      thread. */
   longjmp(from_context->_jmp_context, 1);
   longjmp(from_context->_jmp_context, 1);

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

@@ -54,6 +54,9 @@ public:
   INLINE CycleData(const CycleData &copy) = default;
   INLINE CycleData(const CycleData &copy) = default;
   virtual ~CycleData();
   virtual ~CycleData();
 
 
+  CycleData &operator = (CycleData &&from) = default;
+  CycleData &operator = (const CycleData &copy) = default;
+
   virtual CycleData *make_copy() const=0;
   virtual CycleData *make_copy() const=0;
 
 
   virtual void write_datagram(BamWriter *, Datagram &) const;
   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);
   _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);
   _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;
   _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);
   _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);
   _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;
   _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);
   _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
  * This flavor of the constructor elevates the pointer from the
  * CycleDataLockedStageReader from a read to a write pointer (and invalidates
  * CycleDataLockedStageReader from a read to a write pointer (and invalidates
@@ -128,6 +111,23 @@ CycleDataStageWriter(CycleDataStageWriter<CycleDataType> &&from) noexcept :
   from._pointer = nullptr;
   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;
   _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
  * This flavor of the constructor elevates the pointer from the
  * CycleDataLockedStageReader from a read to a write pointer (and invalidates
  * 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 {
     } else {
       // No exception.  Restore the thread state normally.
       // 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);
       thread_states.push_back(new_thread_state);
       // PyThreadState_Clear(new_thread_state);
       // PyThreadState_Clear(new_thread_state);
       // PyThreadState_Delete(new_thread_state);
       // PyThreadState_Delete(new_thread_state);

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

@@ -51,14 +51,6 @@ is_threading_supported() {
   return true;
   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
 #ifdef HAVE_PYTHON
   // Query the current Python thread state.
   // 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
 #endif  // HAVE_PYTHON
 
 
   init_thread_context(_context, _stack, _stack_size, st_begin_thread, this);
   init_thread_context(_context, _stack, _stack_size, st_begin_thread, this);
@@ -201,6 +201,14 @@ prepare_for_exit() {
   manager->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::
 void ThreadSimpleImpl::
 begin_thread() {
 begin_thread() {
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
-  PyThreadState_Swap(_python_state);
+  thread_state_swap(_python_state);
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON
 
 
 #ifdef HAVE_POSIX_THREADS
 #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 void bind_thread(Thread *thread);
   INLINE static bool is_threading_supported();
   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 bool is_simple_threads();
   INLINE static void sleep(double seconds);
   INLINE static void sleep(double seconds);
   INLINE static void yield();
   INLINE static void yield();

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

@@ -20,7 +20,9 @@
 #include "mainThread.h"
 #include "mainThread.h"
 
 
 #ifdef WIN32
 #ifdef WIN32
-#define WIN32_LEAN_AND_MEAN
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
 #include <windows.h>
 #include <windows.h>
 #endif
 #endif
 
 
@@ -235,7 +237,7 @@ next_context() {
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
   // Save the current Python thread state.
   // Save the current Python thread state.
-  _current_thread->_python_state = PyThreadState_Swap(nullptr);
+  _current_thread->_python_state = thread_state_swap(nullptr);
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON
 
 
 #ifdef DO_PSTATS
 #ifdef DO_PSTATS
@@ -256,7 +258,7 @@ next_context() {
 #endif  // DO_PSTATS
 #endif  // DO_PSTATS
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
-  PyThreadState_Swap(_current_thread->_python_state);
+  thread_state_swap(_current_thread->_python_state);
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON
 }
 }
 
 
@@ -468,16 +470,6 @@ init_pointers() {
     _pointers_initialized = true;
     _pointers_initialized = true;
     _global_ptr = new ThreadSimpleManager;
     _global_ptr = new ThreadSimpleManager;
     Thread::get_main_thread();
     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();
   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
  * Returns a string that represents the contents of the text, as it has been
  * formatted by wordwrap rules.
  * formatted by wordwrap rules.
@@ -1086,28 +1031,6 @@ calc_width(const std::string &line) const {
   return calc_width(decode_text(line));
   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
  * Returns a wstring that represents the contents of the text, as it has been
  * formatted by wordwrap rules.
  * formatted by wordwrap rules.

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