2
0
Эх сурвалжийг харах

Merge branch 'master' into webgl-port

rdb 4 жил өмнө
parent
commit
f21193fcbd
86 өөрчлөгдсөн 2353 нэмэгдсэн , 1050 устгасан
  1. 1 0
      BACKERS.md
  2. 1 1
      contrib/src/ai/aiBehaviors.h
  3. 1 0
      direct/src/dist/commands.py
  4. 8 8
      direct/src/distributed/DistributedObject.py
  5. 7 2
      direct/src/filter/FilterManager.py
  6. 1 1
      direct/src/motiontrail/cMotionTrail.cxx
  7. 458 485
      dtool/src/cppparser/cppBison.cxx.prebuilt
  8. 295 274
      dtool/src/cppparser/cppBison.h.prebuilt
  9. 73 12
      dtool/src/cppparser/cppBison.yxx
  10. 30 2
      dtool/src/cppparser/cppExpression.cxx
  11. 6 0
      dtool/src/cppparser/cppInstance.cxx
  12. 3 1
      dtool/src/cppparser/cppInstance.h
  13. 132 61
      dtool/src/cppparser/cppManifest.cxx
  14. 12 5
      dtool/src/cppparser/cppManifest.h
  15. 28 2
      dtool/src/cppparser/cppPreprocessor.cxx
  16. 4 0
      dtool/src/cppparser/cppSimpleType.cxx
  17. 1 0
      dtool/src/cppparser/cppSimpleType.h
  18. 5 5
      dtool/src/dtoolbase/pallocator.T
  19. 12 12
      dtool/src/dtoolbase/pallocator.h
  20. 11 6
      dtool/src/interrogate/functionRemap.cxx
  21. 1 1
      dtool/src/interrogate/functionRemap.h
  22. 114 30
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  23. 3 0
      dtool/src/interrogate/interfaceMakerPythonNative.h
  24. 5 0
      dtool/src/interrogate/interrogateBuilder.cxx
  25. 3 1
      dtool/src/interrogate/typeManager.cxx
  26. 5 0
      dtool/src/interrogatedb/py_compat.h
  27. 10 2
      dtool/src/interrogatedb/py_panda.cxx
  28. 9 0
      dtool/src/parser-inc/bit
  29. 23 0
      dtool/src/parser-inc/compare
  30. 11 0
      dtool/src/parser-inc/condition_variable
  31. 31 0
      dtool/src/parser-inc/coroutine
  32. 9 0
      dtool/src/parser-inc/forward_list
  33. 27 0
      dtool/src/parser-inc/future
  34. 1 0
      dtool/src/parser-inc/mutex
  35. 10 0
      dtool/src/parser-inc/new
  36. 7 0
      dtool/src/parser-inc/shared_mutex
  37. 21 0
      dtool/src/parser-inc/thread
  38. 19 0
      dtool/src/parser-inc/type_traits
  39. 4 0
      dtool/src/prc/encryptStreamBuf.cxx
  40. 2 0
      makepanda/installer.nsi
  41. 9 8
      makepanda/makepanda.py
  42. 4 0
      makepanda/makepandacore.py
  43. 2 3
      panda/src/bullet/bullet_utils.cxx
  44. 2 2
      panda/src/display/graphicsEngine.cxx
  45. 1 1
      panda/src/display/graphicsOutput.cxx
  46. 2 2
      panda/src/display/graphicsWindow.I
  47. 4 4
      panda/src/display/graphicsWindow.cxx
  48. 1 1
      panda/src/display/subprocessWindow.cxx
  49. 3 0
      panda/src/downloader/virtualFileHTTP.cxx
  50. 79 7
      panda/src/egldisplay/eglGraphicsPipe.cxx
  51. 6 6
      panda/src/event/asyncFuture.I
  52. 16 0
      panda/src/event/asyncFuture.cxx
  53. 4 3
      panda/src/event/asyncFuture.h
  54. 76 5
      panda/src/event/asyncFuture_ext.cxx
  55. 1 0
      panda/src/event/asyncFuture_ext.h
  56. 0 2
      panda/src/event/pythonTask.cxx
  57. 36 19
      panda/src/express/patchfile.cxx
  58. 31 0
      panda/src/glstuff/glShaderContext_src.cxx
  59. 72 5
      panda/src/linmath/lvecBase2_ext_src.I
  60. 4 1
      panda/src/linmath/lvecBase2_ext_src.h
  61. 4 1
      panda/src/linmath/lvecBase2_src.h
  62. 79 6
      panda/src/linmath/lvecBase3_ext_src.I
  63. 4 1
      panda/src/linmath/lvecBase3_ext_src.h
  64. 4 1
      panda/src/linmath/lvecBase3_src.h
  65. 86 7
      panda/src/linmath/lvecBase4_ext_src.I
  66. 4 1
      panda/src/linmath/lvecBase4_ext_src.h
  67. 4 1
      panda/src/linmath/lvecBase4_src.h
  68. 1 1
      panda/src/movies/config_movies.cxx
  69. 18 8
      panda/src/pgraph/nodePath.I
  70. 40 0
      panda/src/pgraph/nodePath.cxx
  71. 9 0
      panda/src/pgraph/transformState.I
  72. 2 0
      panda/src/pgraph/transformState.h
  73. 72 37
      panda/src/pgui/pgItem.cxx
  74. 1 2
      panda/src/pgui/pgScrollFrame.cxx
  75. 1 2
      panda/src/pgui/pgSliderBar.cxx
  76. 3 1
      panda/src/pipeline/pythonThread.cxx
  77. 3 0
      panda/src/putil/CMakeLists.txt
  78. 1 0
      panda/src/putil/p3putil_ext_composite.cxx
  79. 31 0
      panda/src/putil/paramPyObject.I
  80. 42 0
      panda/src/putil/paramPyObject.cxx
  81. 61 0
      panda/src/putil/paramPyObject.h
  82. 1 1
      pandatool/src/assimp/assimpLoader.cxx
  83. 65 0
      tests/event/test_futures.py
  84. 20 0
      tests/linmath/test_lvector2.py
  85. 20 0
      tests/linmath/test_lvector3.py
  86. 20 0
      tests/linmath/test_lvector4.py

+ 1 - 0
BACKERS.md

@@ -32,6 +32,7 @@ This is a list of all the people who are contributing financially to Panda3D.  I
 * Eric Thomson
 * Eric Thomson
 * Kyle Roach
 * Kyle Roach
 * Brian Lach
 * Brian Lach
+* C0MPU73R
 
 
 ## Backers
 ## Backers
 
 

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

@@ -151,7 +151,7 @@ PUBLISHED:
 
 
   void obstacle_avoidance(float feeler_length = 1.0);
   void obstacle_avoidance(float feeler_length = 1.0);
 
 
-  void path_follow(float follow_wt);
+  void path_follow(float follow_wt = 1.0f);
   void add_to_path(LVecBase3 pos);
   void add_to_path(LVecBase3 pos);
   void start_follow(std::string type = "normal");
   void start_follow(std::string type = "normal");
 
 

+ 1 - 0
direct/src/dist/commands.py

@@ -94,6 +94,7 @@ PACKAGE_DATA_DIRS = {
         ('cefpython3/Chromium Embedded Framework.framework/Resources', 'Chromium Embedded Framework.framework/Resources', {}),
         ('cefpython3/Chromium Embedded Framework.framework/Resources', 'Chromium Embedded Framework.framework/Resources', {}),
         ('cefpython3/Chromium Embedded Framework.framework/Chromium Embedded Framework', '', {'PKG_DATA_MAKE_EXECUTABLE'}),
         ('cefpython3/Chromium Embedded Framework.framework/Chromium Embedded Framework', '', {'PKG_DATA_MAKE_EXECUTABLE'}),
     ],
     ],
+    'pytz': [('pytz/zoneinfo/*', 'zoneinfo', ())],
 }
 }
 
 
 # Some dependencies have extra directories that need to be scanned for DLLs.
 # Some dependencies have extra directories that need to be scanned for DLLs.

+ 8 - 8
direct/src/distributed/DistributedObject.py

@@ -124,8 +124,8 @@ class DistributedObject(DistributedObjectBase):
                     if field is not None:
                     if field is not None:
                         p = DCPacker()
                         p = DCPacker()
                         p.setUnpackData(field.getDefaultValue())
                         p.setUnpackData(field.getDefaultValue())
-                        len = p.rawUnpackUint16()/4
-                        for i in range(len):
+                        length = p.rawUnpackUint16() // 4
+                        for i in range(length):
                             zone = int(p.rawUnpackUint32())
                             zone = int(p.rawUnpackUint32())
                             autoInterests.add(zone)
                             autoInterests.add(zone)
                     autoInterests.update(autoInterests)
                     autoInterests.update(autoInterests)
@@ -141,9 +141,9 @@ class DistributedObject(DistributedObjectBase):
         _getAutoInterests = None
         _getAutoInterests = None
         return list(autoInterests)
         return list(autoInterests)
 
 
-    def setNeverDisable(self, bool):
-        assert bool == 1 or bool == 0
-        self.neverDisable = bool
+    def setNeverDisable(self, boolean):
+        assert boolean == 1 or boolean == 0
+        self.neverDisable = boolean
 
 
     def getNeverDisable(self):
     def getNeverDisable(self):
         return self.neverDisable
         return self.neverDisable
@@ -177,9 +177,9 @@ class DistributedObject(DistributedObjectBase):
         # call this to throw out cached data from a previous instantiation
         # call this to throw out cached data from a previous instantiation
         self._cachedData[name].flush()
         self._cachedData[name].flush()
 
 
-    def setCacheable(self, bool):
-        assert bool == 1 or bool == 0
-        self.cacheable = bool
+    def setCacheable(self, boolean):
+        assert boolean == 1 or boolean == 0
+        self.cacheable = boolean
 
 
     def getCacheable(self):
     def getCacheable(self):
         return self.cacheable
         return self.cacheable

+ 7 - 2
direct/src/filter/FilterManager.py

@@ -300,7 +300,7 @@ class FilterManager(DirectObject):
 
 
         return quad
         return quad
 
 
-    def createBuffer(self, name, xsize, ysize, texgroup, depthbits=1, fbprops=None):
+    def createBuffer(self, name, xsize, ysize, texgroup, depthbits=True, fbprops=None):
         """ Low-level buffer creation.  Not intended for public use. """
         """ Low-level buffer creation.  Not intended for public use. """
 
 
         winprops = WindowProperties()
         winprops = WindowProperties()
@@ -308,7 +308,12 @@ class FilterManager(DirectObject):
         props = FrameBufferProperties(FrameBufferProperties.getDefault())
         props = FrameBufferProperties(FrameBufferProperties.getDefault())
         props.setBackBuffers(0)
         props.setBackBuffers(0)
         props.setRgbColor(1)
         props.setRgbColor(1)
-        props.setDepthBits(depthbits)
+        if depthbits is True:
+            # Respect depth-bits from Config.prc
+            if props.getDepthBits() == 0:
+                props.setDepthBits(1)
+        else:
+            props.setDepthBits(depthbits)
         props.setStereo(self.win.isStereo())
         props.setStereo(self.win.isStereo())
         if fbprops is not None:
         if fbprops is not None:
             props.addProperties(fbprops)
             props.addProperties(fbprops)

+ 1 - 1
direct/src/motiontrail/cMotionTrail.cxx

@@ -331,7 +331,7 @@ update_motion_trail (PN_stdfloat current_time, LMatrix4 *transform) {
 
 
     frame_iterator = _frame_list.begin ( );
     frame_iterator = _frame_list.begin ( );
     motion_trail_frame = *frame_iterator;
     motion_trail_frame = *frame_iterator;
-    if (*transform == motion_trail_frame._transform) {
+    if (motion_trail_frame._transform == UnalignedLMatrix4(*transform)) {
       // duplicate transform
       // duplicate transform
       return;
       return;
     }
     }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 458 - 485
dtool/src/cppparser/cppBison.cxx.prebuilt


+ 295 - 274
dtool/src/cppparser/cppBison.h.prebuilt

@@ -1,4 +1,4 @@
-/* A Bison parser, made by GNU Bison 3.5.3.  */
+/* A Bison parser, made by GNU Bison 3.7.3.  */
 
 
 /* Bison interface for Yacc-like parsers in C
 /* Bison interface for Yacc-like parsers in C
 
 
@@ -31,8 +31,9 @@
    This special exception was added by the Free Software Foundation in
    This special exception was added by the Free Software Foundation in
    version 2.2 of Bison.  */
    version 2.2 of Bison.  */
 
 
-/* Undocumented macros, especially those whose name start with YY_,
-   are private implementation details.  Do not rely on them.  */
+/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+   especially those whose name start with YY_ or yy_.  They are
+   private implementation details that can be changed or removed.  */
 
 
 #ifndef YY_CPPYY_BUILT_TMP_CPPBISON_YXX_H_INCLUDED
 #ifndef YY_CPPYY_BUILT_TMP_CPPBISON_YXX_H_INCLUDED
 # define YY_CPPYY_BUILT_TMP_CPPBISON_YXX_H_INCLUDED
 # define YY_CPPYY_BUILT_TMP_CPPBISON_YXX_H_INCLUDED
@@ -44,158 +45,172 @@
 extern int cppyydebug;
 extern int cppyydebug;
 #endif
 #endif
 
 
-/* Token type.  */
+/* Token kinds.  */
 #ifndef YYTOKENTYPE
 #ifndef YYTOKENTYPE
 # define YYTOKENTYPE
 # define YYTOKENTYPE
   enum yytokentype
   enum yytokentype
   {
   {
-    REAL = 258,
-    INTEGER = 259,
-    CHAR_TOK = 260,
-    SIMPLE_STRING = 261,
-    SIMPLE_IDENTIFIER = 262,
-    STRING_LITERAL = 263,
-    CUSTOM_LITERAL = 264,
-    IDENTIFIER = 265,
-    TYPENAME_IDENTIFIER = 266,
-    TYPEPACK_IDENTIFIER = 267,
-    SCOPING = 268,
-    TYPEDEFNAME = 269,
-    ELLIPSIS = 270,
-    OROR = 271,
-    ANDAND = 272,
-    EQCOMPARE = 273,
-    NECOMPARE = 274,
-    LECOMPARE = 275,
-    GECOMPARE = 276,
-    LSHIFT = 277,
-    RSHIFT = 278,
-    POINTSAT_STAR = 279,
-    DOT_STAR = 280,
-    UNARY = 281,
-    UNARY_NOT = 282,
-    UNARY_NEGATE = 283,
-    UNARY_MINUS = 284,
-    UNARY_PLUS = 285,
-    UNARY_STAR = 286,
-    UNARY_REF = 287,
-    POINTSAT = 288,
-    SCOPE = 289,
-    PLUSPLUS = 290,
-    MINUSMINUS = 291,
-    TIMESEQUAL = 292,
-    DIVIDEEQUAL = 293,
-    MODEQUAL = 294,
-    PLUSEQUAL = 295,
-    MINUSEQUAL = 296,
-    OREQUAL = 297,
-    ANDEQUAL = 298,
-    XOREQUAL = 299,
-    LSHIFTEQUAL = 300,
-    RSHIFTEQUAL = 301,
-    ATTR_LEFT = 302,
-    ATTR_RIGHT = 303,
-    KW_ALIGNAS = 304,
-    KW_ALIGNOF = 305,
-    KW_AUTO = 306,
-    KW_BEGIN_PUBLISH = 307,
-    KW_BLOCKING = 308,
-    KW_BOOL = 309,
-    KW_CATCH = 310,
-    KW_CHAR = 311,
-    KW_CHAR16_T = 312,
-    KW_CHAR32_T = 313,
-    KW_CLASS = 314,
-    KW_CONST = 315,
-    KW_CONSTEXPR = 316,
-    KW_CONST_CAST = 317,
-    KW_DECLTYPE = 318,
-    KW_DEFAULT = 319,
-    KW_DELETE = 320,
-    KW_DOUBLE = 321,
-    KW_DYNAMIC_CAST = 322,
-    KW_ELSE = 323,
-    KW_END_PUBLISH = 324,
-    KW_ENUM = 325,
-    KW_EXTENSION = 326,
-    KW_EXTERN = 327,
-    KW_EXPLICIT = 328,
-    KW_PUBLISHED = 329,
-    KW_FALSE = 330,
-    KW_FINAL = 331,
-    KW_FLOAT = 332,
-    KW_FRIEND = 333,
-    KW_FOR = 334,
-    KW_GOTO = 335,
-    KW_HAS_VIRTUAL_DESTRUCTOR = 336,
-    KW_IF = 337,
-    KW_INLINE = 338,
-    KW_INT = 339,
-    KW_IS_ABSTRACT = 340,
-    KW_IS_BASE_OF = 341,
-    KW_IS_CLASS = 342,
-    KW_IS_CONSTRUCTIBLE = 343,
-    KW_IS_CONVERTIBLE_TO = 344,
-    KW_IS_DESTRUCTIBLE = 345,
-    KW_IS_EMPTY = 346,
-    KW_IS_ENUM = 347,
-    KW_IS_FINAL = 348,
-    KW_IS_FUNDAMENTAL = 349,
-    KW_IS_POD = 350,
-    KW_IS_POLYMORPHIC = 351,
-    KW_IS_STANDARD_LAYOUT = 352,
-    KW_IS_TRIVIAL = 353,
-    KW_IS_UNION = 354,
-    KW_LONG = 355,
-    KW_MAKE_MAP_KEYS_SEQ = 356,
-    KW_MAKE_MAP_PROPERTY = 357,
-    KW_MAKE_PROPERTY = 358,
-    KW_MAKE_PROPERTY2 = 359,
-    KW_MAKE_SEQ = 360,
-    KW_MAKE_SEQ_PROPERTY = 361,
-    KW_MUTABLE = 362,
-    KW_NAMESPACE = 363,
-    KW_NEW = 364,
-    KW_NOEXCEPT = 365,
-    KW_NULLPTR = 366,
-    KW_OPERATOR = 367,
-    KW_OVERRIDE = 368,
-    KW_PRIVATE = 369,
-    KW_PROTECTED = 370,
-    KW_PUBLIC = 371,
-    KW_REGISTER = 372,
-    KW_REINTERPRET_CAST = 373,
-    KW_RETURN = 374,
-    KW_SHORT = 375,
-    KW_SIGNED = 376,
-    KW_SIZEOF = 377,
-    KW_STATIC = 378,
-    KW_STATIC_ASSERT = 379,
-    KW_STATIC_CAST = 380,
-    KW_STRUCT = 381,
-    KW_TEMPLATE = 382,
-    KW_THREAD_LOCAL = 383,
-    KW_THROW = 384,
-    KW_TRUE = 385,
-    KW_TRY = 386,
-    KW_TYPEDEF = 387,
-    KW_TYPEID = 388,
-    KW_TYPENAME = 389,
-    KW_UNDERLYING_TYPE = 390,
-    KW_UNION = 391,
-    KW_UNSIGNED = 392,
-    KW_USING = 393,
-    KW_VIRTUAL = 394,
-    KW_VOID = 395,
-    KW_VOLATILE = 396,
-    KW_WCHAR_T = 397,
-    KW_WHILE = 398,
-    START_CPP = 399,
-    START_CONST_EXPR = 400,
-    START_TYPE = 401
+    YYEMPTY = -2,
+    YYEOF = 0,                     /* "end of file"  */
+    YYerror = 256,                 /* error  */
+    YYUNDEF = 257,                 /* "invalid token"  */
+    REAL = 258,                    /* REAL  */
+    INTEGER = 259,                 /* INTEGER  */
+    CHAR_TOK = 260,                /* CHAR_TOK  */
+    SIMPLE_STRING = 261,           /* SIMPLE_STRING  */
+    SIMPLE_IDENTIFIER = 262,       /* SIMPLE_IDENTIFIER  */
+    STRING_LITERAL = 263,          /* STRING_LITERAL  */
+    CUSTOM_LITERAL = 264,          /* CUSTOM_LITERAL  */
+    IDENTIFIER = 265,              /* IDENTIFIER  */
+    TYPENAME_IDENTIFIER = 266,     /* TYPENAME_IDENTIFIER  */
+    TYPEPACK_IDENTIFIER = 267,     /* TYPEPACK_IDENTIFIER  */
+    SCOPING = 268,                 /* SCOPING  */
+    TYPEDEFNAME = 269,             /* TYPEDEFNAME  */
+    ELLIPSIS = 270,                /* ELLIPSIS  */
+    OROR = 271,                    /* OROR  */
+    ANDAND = 272,                  /* ANDAND  */
+    EQCOMPARE = 273,               /* EQCOMPARE  */
+    NECOMPARE = 274,               /* NECOMPARE  */
+    LECOMPARE = 275,               /* LECOMPARE  */
+    GECOMPARE = 276,               /* GECOMPARE  */
+    SPACESHIP = 277,               /* SPACESHIP  */
+    LSHIFT = 278,                  /* LSHIFT  */
+    RSHIFT = 279,                  /* RSHIFT  */
+    POINTSAT_STAR = 280,           /* POINTSAT_STAR  */
+    DOT_STAR = 281,                /* DOT_STAR  */
+    UNARY = 282,                   /* UNARY  */
+    UNARY_NOT = 283,               /* UNARY_NOT  */
+    UNARY_NEGATE = 284,            /* UNARY_NEGATE  */
+    UNARY_MINUS = 285,             /* UNARY_MINUS  */
+    UNARY_PLUS = 286,              /* UNARY_PLUS  */
+    UNARY_STAR = 287,              /* UNARY_STAR  */
+    UNARY_REF = 288,               /* UNARY_REF  */
+    POINTSAT = 289,                /* POINTSAT  */
+    SCOPE = 290,                   /* SCOPE  */
+    PLUSPLUS = 291,                /* PLUSPLUS  */
+    MINUSMINUS = 292,              /* MINUSMINUS  */
+    TIMESEQUAL = 293,              /* TIMESEQUAL  */
+    DIVIDEEQUAL = 294,             /* DIVIDEEQUAL  */
+    MODEQUAL = 295,                /* MODEQUAL  */
+    PLUSEQUAL = 296,               /* PLUSEQUAL  */
+    MINUSEQUAL = 297,              /* MINUSEQUAL  */
+    OREQUAL = 298,                 /* OREQUAL  */
+    ANDEQUAL = 299,                /* ANDEQUAL  */
+    XOREQUAL = 300,                /* XOREQUAL  */
+    LSHIFTEQUAL = 301,             /* LSHIFTEQUAL  */
+    RSHIFTEQUAL = 302,             /* RSHIFTEQUAL  */
+    ATTR_LEFT = 303,               /* ATTR_LEFT  */
+    ATTR_RIGHT = 304,              /* ATTR_RIGHT  */
+    KW_ALIGNAS = 305,              /* KW_ALIGNAS  */
+    KW_ALIGNOF = 306,              /* KW_ALIGNOF  */
+    KW_AUTO = 307,                 /* KW_AUTO  */
+    KW_BEGIN_PUBLISH = 308,        /* KW_BEGIN_PUBLISH  */
+    KW_BLOCKING = 309,             /* KW_BLOCKING  */
+    KW_BOOL = 310,                 /* KW_BOOL  */
+    KW_CATCH = 311,                /* KW_CATCH  */
+    KW_CHAR = 312,                 /* KW_CHAR  */
+    KW_CHAR8_T = 313,              /* KW_CHAR8_T  */
+    KW_CHAR16_T = 314,             /* KW_CHAR16_T  */
+    KW_CHAR32_T = 315,             /* KW_CHAR32_T  */
+    KW_CLASS = 316,                /* KW_CLASS  */
+    KW_CONST = 317,                /* KW_CONST  */
+    KW_CONSTEVAL = 318,            /* KW_CONSTEVAL  */
+    KW_CONSTEXPR = 319,            /* KW_CONSTEXPR  */
+    KW_CONSTINIT = 320,            /* KW_CONSTINIT  */
+    KW_CONST_CAST = 321,           /* KW_CONST_CAST  */
+    KW_DECLTYPE = 322,             /* KW_DECLTYPE  */
+    KW_DEFAULT = 323,              /* KW_DEFAULT  */
+    KW_DELETE = 324,               /* KW_DELETE  */
+    KW_DOUBLE = 325,               /* KW_DOUBLE  */
+    KW_DYNAMIC_CAST = 326,         /* KW_DYNAMIC_CAST  */
+    KW_ELSE = 327,                 /* KW_ELSE  */
+    KW_END_PUBLISH = 328,          /* KW_END_PUBLISH  */
+    KW_ENUM = 329,                 /* KW_ENUM  */
+    KW_EXTENSION = 330,            /* KW_EXTENSION  */
+    KW_EXTERN = 331,               /* KW_EXTERN  */
+    KW_EXPLICIT = 332,             /* KW_EXPLICIT  */
+    KW_EXPLICIT_LPAREN = 333,      /* KW_EXPLICIT_LPAREN  */
+    KW_PUBLISHED = 334,            /* KW_PUBLISHED  */
+    KW_FALSE = 335,                /* KW_FALSE  */
+    KW_FINAL = 336,                /* KW_FINAL  */
+    KW_FLOAT = 337,                /* KW_FLOAT  */
+    KW_FRIEND = 338,               /* KW_FRIEND  */
+    KW_FOR = 339,                  /* KW_FOR  */
+    KW_GOTO = 340,                 /* KW_GOTO  */
+    KW_HAS_VIRTUAL_DESTRUCTOR = 341, /* KW_HAS_VIRTUAL_DESTRUCTOR  */
+    KW_IF = 342,                   /* KW_IF  */
+    KW_INLINE = 343,               /* KW_INLINE  */
+    KW_INT = 344,                  /* KW_INT  */
+    KW_IS_ABSTRACT = 345,          /* KW_IS_ABSTRACT  */
+    KW_IS_BASE_OF = 346,           /* KW_IS_BASE_OF  */
+    KW_IS_CLASS = 347,             /* KW_IS_CLASS  */
+    KW_IS_CONSTRUCTIBLE = 348,     /* KW_IS_CONSTRUCTIBLE  */
+    KW_IS_CONVERTIBLE_TO = 349,    /* KW_IS_CONVERTIBLE_TO  */
+    KW_IS_DESTRUCTIBLE = 350,      /* KW_IS_DESTRUCTIBLE  */
+    KW_IS_EMPTY = 351,             /* KW_IS_EMPTY  */
+    KW_IS_ENUM = 352,              /* KW_IS_ENUM  */
+    KW_IS_FINAL = 353,             /* KW_IS_FINAL  */
+    KW_IS_FUNDAMENTAL = 354,       /* KW_IS_FUNDAMENTAL  */
+    KW_IS_POD = 355,               /* KW_IS_POD  */
+    KW_IS_POLYMORPHIC = 356,       /* KW_IS_POLYMORPHIC  */
+    KW_IS_STANDARD_LAYOUT = 357,   /* KW_IS_STANDARD_LAYOUT  */
+    KW_IS_TRIVIAL = 358,           /* KW_IS_TRIVIAL  */
+    KW_IS_UNION = 359,             /* KW_IS_UNION  */
+    KW_LONG = 360,                 /* KW_LONG  */
+    KW_MAKE_MAP_KEYS_SEQ = 361,    /* KW_MAKE_MAP_KEYS_SEQ  */
+    KW_MAKE_MAP_PROPERTY = 362,    /* KW_MAKE_MAP_PROPERTY  */
+    KW_MAKE_PROPERTY = 363,        /* KW_MAKE_PROPERTY  */
+    KW_MAKE_PROPERTY2 = 364,       /* KW_MAKE_PROPERTY2  */
+    KW_MAKE_SEQ = 365,             /* KW_MAKE_SEQ  */
+    KW_MAKE_SEQ_PROPERTY = 366,    /* KW_MAKE_SEQ_PROPERTY  */
+    KW_MUTABLE = 367,              /* KW_MUTABLE  */
+    KW_NAMESPACE = 368,            /* KW_NAMESPACE  */
+    KW_NEW = 369,                  /* KW_NEW  */
+    KW_NOEXCEPT = 370,             /* KW_NOEXCEPT  */
+    KW_NOEXCEPT_LPAREN = 371,      /* KW_NOEXCEPT_LPAREN  */
+    KW_NULLPTR = 372,              /* KW_NULLPTR  */
+    KW_OPERATOR = 373,             /* KW_OPERATOR  */
+    KW_OVERRIDE = 374,             /* KW_OVERRIDE  */
+    KW_PRIVATE = 375,              /* KW_PRIVATE  */
+    KW_PROTECTED = 376,            /* KW_PROTECTED  */
+    KW_PUBLIC = 377,               /* KW_PUBLIC  */
+    KW_REGISTER = 378,             /* KW_REGISTER  */
+    KW_REINTERPRET_CAST = 379,     /* KW_REINTERPRET_CAST  */
+    KW_RETURN = 380,               /* KW_RETURN  */
+    KW_SHORT = 381,                /* KW_SHORT  */
+    KW_SIGNED = 382,               /* KW_SIGNED  */
+    KW_SIZEOF = 383,               /* KW_SIZEOF  */
+    KW_STATIC = 384,               /* KW_STATIC  */
+    KW_STATIC_ASSERT = 385,        /* KW_STATIC_ASSERT  */
+    KW_STATIC_CAST = 386,          /* KW_STATIC_CAST  */
+    KW_STRUCT = 387,               /* KW_STRUCT  */
+    KW_TEMPLATE = 388,             /* KW_TEMPLATE  */
+    KW_THREAD_LOCAL = 389,         /* KW_THREAD_LOCAL  */
+    KW_THROW = 390,                /* KW_THROW  */
+    KW_TRUE = 391,                 /* KW_TRUE  */
+    KW_TRY = 392,                  /* KW_TRY  */
+    KW_TYPEDEF = 393,              /* KW_TYPEDEF  */
+    KW_TYPEID = 394,               /* KW_TYPEID  */
+    KW_TYPENAME = 395,             /* KW_TYPENAME  */
+    KW_UNDERLYING_TYPE = 396,      /* KW_UNDERLYING_TYPE  */
+    KW_UNION = 397,                /* KW_UNION  */
+    KW_UNSIGNED = 398,             /* KW_UNSIGNED  */
+    KW_USING = 399,                /* KW_USING  */
+    KW_VIRTUAL = 400,              /* KW_VIRTUAL  */
+    KW_VOID = 401,                 /* KW_VOID  */
+    KW_VOLATILE = 402,             /* KW_VOLATILE  */
+    KW_WCHAR_T = 403,              /* KW_WCHAR_T  */
+    KW_WHILE = 404,                /* KW_WHILE  */
+    START_CPP = 405,               /* START_CPP  */
+    START_CONST_EXPR = 406,        /* START_CONST_EXPR  */
+    START_TYPE = 407               /* START_TYPE  */
   };
   };
+  typedef enum yytokentype yytoken_kind_t;
 #endif
 #endif
-/* Tokens.  */
+/* Token kinds.  */
+#define YYEOF 0
+#define YYerror 256
+#define YYUNDEF 257
 #define REAL 258
 #define REAL 258
 #define INTEGER 259
 #define INTEGER 259
 #define CHAR_TOK 260
 #define CHAR_TOK 260
@@ -215,131 +230,137 @@ extern int cppyydebug;
 #define NECOMPARE 274
 #define NECOMPARE 274
 #define LECOMPARE 275
 #define LECOMPARE 275
 #define GECOMPARE 276
 #define GECOMPARE 276
-#define LSHIFT 277
-#define RSHIFT 278
-#define POINTSAT_STAR 279
-#define DOT_STAR 280
-#define UNARY 281
-#define UNARY_NOT 282
-#define UNARY_NEGATE 283
-#define UNARY_MINUS 284
-#define UNARY_PLUS 285
-#define UNARY_STAR 286
-#define UNARY_REF 287
-#define POINTSAT 288
-#define SCOPE 289
-#define PLUSPLUS 290
-#define MINUSMINUS 291
-#define TIMESEQUAL 292
-#define DIVIDEEQUAL 293
-#define MODEQUAL 294
-#define PLUSEQUAL 295
-#define MINUSEQUAL 296
-#define OREQUAL 297
-#define ANDEQUAL 298
-#define XOREQUAL 299
-#define LSHIFTEQUAL 300
-#define RSHIFTEQUAL 301
-#define ATTR_LEFT 302
-#define ATTR_RIGHT 303
-#define KW_ALIGNAS 304
-#define KW_ALIGNOF 305
-#define KW_AUTO 306
-#define KW_BEGIN_PUBLISH 307
-#define KW_BLOCKING 308
-#define KW_BOOL 309
-#define KW_CATCH 310
-#define KW_CHAR 311
-#define KW_CHAR16_T 312
-#define KW_CHAR32_T 313
-#define KW_CLASS 314
-#define KW_CONST 315
-#define KW_CONSTEXPR 316
-#define KW_CONST_CAST 317
-#define KW_DECLTYPE 318
-#define KW_DEFAULT 319
-#define KW_DELETE 320
-#define KW_DOUBLE 321
-#define KW_DYNAMIC_CAST 322
-#define KW_ELSE 323
-#define KW_END_PUBLISH 324
-#define KW_ENUM 325
-#define KW_EXTENSION 326
-#define KW_EXTERN 327
-#define KW_EXPLICIT 328
-#define KW_PUBLISHED 329
-#define KW_FALSE 330
-#define KW_FINAL 331
-#define KW_FLOAT 332
-#define KW_FRIEND 333
-#define KW_FOR 334
-#define KW_GOTO 335
-#define KW_HAS_VIRTUAL_DESTRUCTOR 336
-#define KW_IF 337
-#define KW_INLINE 338
-#define KW_INT 339
-#define KW_IS_ABSTRACT 340
-#define KW_IS_BASE_OF 341
-#define KW_IS_CLASS 342
-#define KW_IS_CONSTRUCTIBLE 343
-#define KW_IS_CONVERTIBLE_TO 344
-#define KW_IS_DESTRUCTIBLE 345
-#define KW_IS_EMPTY 346
-#define KW_IS_ENUM 347
-#define KW_IS_FINAL 348
-#define KW_IS_FUNDAMENTAL 349
-#define KW_IS_POD 350
-#define KW_IS_POLYMORPHIC 351
-#define KW_IS_STANDARD_LAYOUT 352
-#define KW_IS_TRIVIAL 353
-#define KW_IS_UNION 354
-#define KW_LONG 355
-#define KW_MAKE_MAP_KEYS_SEQ 356
-#define KW_MAKE_MAP_PROPERTY 357
-#define KW_MAKE_PROPERTY 358
-#define KW_MAKE_PROPERTY2 359
-#define KW_MAKE_SEQ 360
-#define KW_MAKE_SEQ_PROPERTY 361
-#define KW_MUTABLE 362
-#define KW_NAMESPACE 363
-#define KW_NEW 364
-#define KW_NOEXCEPT 365
-#define KW_NULLPTR 366
-#define KW_OPERATOR 367
-#define KW_OVERRIDE 368
-#define KW_PRIVATE 369
-#define KW_PROTECTED 370
-#define KW_PUBLIC 371
-#define KW_REGISTER 372
-#define KW_REINTERPRET_CAST 373
-#define KW_RETURN 374
-#define KW_SHORT 375
-#define KW_SIGNED 376
-#define KW_SIZEOF 377
-#define KW_STATIC 378
-#define KW_STATIC_ASSERT 379
-#define KW_STATIC_CAST 380
-#define KW_STRUCT 381
-#define KW_TEMPLATE 382
-#define KW_THREAD_LOCAL 383
-#define KW_THROW 384
-#define KW_TRUE 385
-#define KW_TRY 386
-#define KW_TYPEDEF 387
-#define KW_TYPEID 388
-#define KW_TYPENAME 389
-#define KW_UNDERLYING_TYPE 390
-#define KW_UNION 391
-#define KW_UNSIGNED 392
-#define KW_USING 393
-#define KW_VIRTUAL 394
-#define KW_VOID 395
-#define KW_VOLATILE 396
-#define KW_WCHAR_T 397
-#define KW_WHILE 398
-#define START_CPP 399
-#define START_CONST_EXPR 400
-#define START_TYPE 401
+#define SPACESHIP 277
+#define LSHIFT 278
+#define RSHIFT 279
+#define POINTSAT_STAR 280
+#define DOT_STAR 281
+#define UNARY 282
+#define UNARY_NOT 283
+#define UNARY_NEGATE 284
+#define UNARY_MINUS 285
+#define UNARY_PLUS 286
+#define UNARY_STAR 287
+#define UNARY_REF 288
+#define POINTSAT 289
+#define SCOPE 290
+#define PLUSPLUS 291
+#define MINUSMINUS 292
+#define TIMESEQUAL 293
+#define DIVIDEEQUAL 294
+#define MODEQUAL 295
+#define PLUSEQUAL 296
+#define MINUSEQUAL 297
+#define OREQUAL 298
+#define ANDEQUAL 299
+#define XOREQUAL 300
+#define LSHIFTEQUAL 301
+#define RSHIFTEQUAL 302
+#define ATTR_LEFT 303
+#define ATTR_RIGHT 304
+#define KW_ALIGNAS 305
+#define KW_ALIGNOF 306
+#define KW_AUTO 307
+#define KW_BEGIN_PUBLISH 308
+#define KW_BLOCKING 309
+#define KW_BOOL 310
+#define KW_CATCH 311
+#define KW_CHAR 312
+#define KW_CHAR8_T 313
+#define KW_CHAR16_T 314
+#define KW_CHAR32_T 315
+#define KW_CLASS 316
+#define KW_CONST 317
+#define KW_CONSTEVAL 318
+#define KW_CONSTEXPR 319
+#define KW_CONSTINIT 320
+#define KW_CONST_CAST 321
+#define KW_DECLTYPE 322
+#define KW_DEFAULT 323
+#define KW_DELETE 324
+#define KW_DOUBLE 325
+#define KW_DYNAMIC_CAST 326
+#define KW_ELSE 327
+#define KW_END_PUBLISH 328
+#define KW_ENUM 329
+#define KW_EXTENSION 330
+#define KW_EXTERN 331
+#define KW_EXPLICIT 332
+#define KW_EXPLICIT_LPAREN 333
+#define KW_PUBLISHED 334
+#define KW_FALSE 335
+#define KW_FINAL 336
+#define KW_FLOAT 337
+#define KW_FRIEND 338
+#define KW_FOR 339
+#define KW_GOTO 340
+#define KW_HAS_VIRTUAL_DESTRUCTOR 341
+#define KW_IF 342
+#define KW_INLINE 343
+#define KW_INT 344
+#define KW_IS_ABSTRACT 345
+#define KW_IS_BASE_OF 346
+#define KW_IS_CLASS 347
+#define KW_IS_CONSTRUCTIBLE 348
+#define KW_IS_CONVERTIBLE_TO 349
+#define KW_IS_DESTRUCTIBLE 350
+#define KW_IS_EMPTY 351
+#define KW_IS_ENUM 352
+#define KW_IS_FINAL 353
+#define KW_IS_FUNDAMENTAL 354
+#define KW_IS_POD 355
+#define KW_IS_POLYMORPHIC 356
+#define KW_IS_STANDARD_LAYOUT 357
+#define KW_IS_TRIVIAL 358
+#define KW_IS_UNION 359
+#define KW_LONG 360
+#define KW_MAKE_MAP_KEYS_SEQ 361
+#define KW_MAKE_MAP_PROPERTY 362
+#define KW_MAKE_PROPERTY 363
+#define KW_MAKE_PROPERTY2 364
+#define KW_MAKE_SEQ 365
+#define KW_MAKE_SEQ_PROPERTY 366
+#define KW_MUTABLE 367
+#define KW_NAMESPACE 368
+#define KW_NEW 369
+#define KW_NOEXCEPT 370
+#define KW_NOEXCEPT_LPAREN 371
+#define KW_NULLPTR 372
+#define KW_OPERATOR 373
+#define KW_OVERRIDE 374
+#define KW_PRIVATE 375
+#define KW_PROTECTED 376
+#define KW_PUBLIC 377
+#define KW_REGISTER 378
+#define KW_REINTERPRET_CAST 379
+#define KW_RETURN 380
+#define KW_SHORT 381
+#define KW_SIGNED 382
+#define KW_SIZEOF 383
+#define KW_STATIC 384
+#define KW_STATIC_ASSERT 385
+#define KW_STATIC_CAST 386
+#define KW_STRUCT 387
+#define KW_TEMPLATE 388
+#define KW_THREAD_LOCAL 389
+#define KW_THROW 390
+#define KW_TRUE 391
+#define KW_TRY 392
+#define KW_TYPEDEF 393
+#define KW_TYPEID 394
+#define KW_TYPENAME 395
+#define KW_UNDERLYING_TYPE 396
+#define KW_UNION 397
+#define KW_UNSIGNED 398
+#define KW_USING 399
+#define KW_VIRTUAL 400
+#define KW_VOID 401
+#define KW_VOLATILE 402
+#define KW_WCHAR_T 403
+#define KW_WHILE 404
+#define START_CPP 405
+#define START_CONST_EXPR 406
+#define START_TYPE 407
 
 
 /* Value type.  */
 /* Value type.  */
 
 

+ 73 - 12
dtool/src/cppparser/cppBison.yxx

@@ -226,6 +226,7 @@ pop_struct() {
 %token NECOMPARE
 %token NECOMPARE
 %token LECOMPARE
 %token LECOMPARE
 %token GECOMPARE
 %token GECOMPARE
+%token SPACESHIP
 %token LSHIFT
 %token LSHIFT
 %token RSHIFT
 %token RSHIFT
 %token POINTSAT_STAR
 %token POINTSAT_STAR
@@ -262,11 +263,14 @@ pop_struct() {
 %token KW_BOOL
 %token KW_BOOL
 %token KW_CATCH
 %token KW_CATCH
 %token KW_CHAR
 %token KW_CHAR
+%token KW_CHAR8_T
 %token KW_CHAR16_T
 %token KW_CHAR16_T
 %token KW_CHAR32_T
 %token KW_CHAR32_T
 %token KW_CLASS
 %token KW_CLASS
 %token KW_CONST
 %token KW_CONST
+%token KW_CONSTEVAL
 %token KW_CONSTEXPR
 %token KW_CONSTEXPR
+%token KW_CONSTINIT
 %token KW_CONST_CAST
 %token KW_CONST_CAST
 %token KW_DECLTYPE
 %token KW_DECLTYPE
 %token KW_DEFAULT
 %token KW_DEFAULT
@@ -279,6 +283,7 @@ pop_struct() {
 %token KW_EXTENSION
 %token KW_EXTENSION
 %token KW_EXTERN
 %token KW_EXTERN
 %token KW_EXPLICIT
 %token KW_EXPLICIT
+%token KW_EXPLICIT_LPAREN
 %token KW_PUBLISHED
 %token KW_PUBLISHED
 %token KW_FALSE
 %token KW_FALSE
 %token KW_FINAL
 %token KW_FINAL
@@ -316,6 +321,7 @@ pop_struct() {
 %token KW_NAMESPACE
 %token KW_NAMESPACE
 %token KW_NEW
 %token KW_NEW
 %token KW_NOEXCEPT
 %token KW_NOEXCEPT
+%token KW_NOEXCEPT_LPAREN
 %token KW_NULLPTR
 %token KW_NULLPTR
 %token KW_OPERATOR
 %token KW_OPERATOR
 %token KW_OVERRIDE
 %token KW_OVERRIDE
@@ -421,7 +427,7 @@ pop_struct() {
 
 
 /* Precedence rules. */
 /* Precedence rules. */
 
 
-%left IDENTIFIER TYPENAME_IDENTIFIER TYPEDEFNAME KW_ENUM ELLIPSIS KW_OPERATOR KW_TYPENAME KW_INT KW_SHORT KW_UNSIGNED KW_SIGNED KW_LONG KW_FLOAT KW_DOUBLE KW_CHAR KW_WCHAR_T KW_CHAR16_T KW_CHAR32_T KW_BOOL
+%left IDENTIFIER TYPENAME_IDENTIFIER TYPEDEFNAME KW_ENUM ELLIPSIS KW_OPERATOR KW_TYPENAME KW_INT KW_SHORT KW_UNSIGNED KW_SIGNED KW_LONG KW_FLOAT KW_DOUBLE KW_CHAR KW_WCHAR_T KW_CHAR8_T KW_CHAR16_T KW_CHAR32_T KW_BOOL
 
 
 %left '{' ',' ';'
 %left '{' ',' ';'
 
 
@@ -436,6 +442,7 @@ pop_struct() {
 %left '&'
 %left '&'
 %left EQCOMPARE NECOMPARE
 %left EQCOMPARE NECOMPARE
 %left LECOMPARE GECOMPARE '<' '>'
 %left LECOMPARE GECOMPARE '<' '>'
+%left SPACESHIP
 %left LSHIFT RSHIFT
 %left LSHIFT RSHIFT
 %left '+' '-'
 %left '+' '-'
 %left '*' '/' '%'
 %left '*' '/' '%'
@@ -990,6 +997,15 @@ storage_class:
         | KW_EXPLICIT storage_class
         | KW_EXPLICIT storage_class
 {
 {
   $$ = $2 | (int)CPPInstance::SC_explicit;
   $$ = $2 | (int)CPPInstance::SC_explicit;
+}
+        | KW_EXPLICIT_LPAREN const_expr ')' storage_class
+{
+  CPPExpression::Result result = $2->evaluate();
+  if (result._type == CPPExpression::RT_error) {
+    yywarning("explicit() requires a constant expression", @2);
+  } else if (result.as_boolean()) {
+    $$ = $4 | (int)CPPInstance::SC_explicit;
+  }
 }
 }
         | KW_REGISTER storage_class
         | KW_REGISTER storage_class
 {
 {
@@ -1002,10 +1018,18 @@ storage_class:
         | KW_MUTABLE storage_class
         | KW_MUTABLE storage_class
 {
 {
   $$ = $2 | (int)CPPInstance::SC_mutable;
   $$ = $2 | (int)CPPInstance::SC_mutable;
+}
+        | KW_CONSTEVAL storage_class
+{
+  $$ = $2 | (int)CPPInstance::SC_consteval;
 }
 }
         | KW_CONSTEXPR storage_class
         | KW_CONSTEXPR storage_class
 {
 {
   $$ = $2 | (int)CPPInstance::SC_constexpr;
   $$ = $2 | (int)CPPInstance::SC_constexpr;
+}
+        | KW_CONSTINIT storage_class
+{
+  $$ = $2 | (int)CPPInstance::SC_constinit;
 }
 }
         | KW_BLOCKING storage_class
         | KW_BLOCKING storage_class
 {
 {
@@ -1439,15 +1463,15 @@ function_post:
 {
 {
   $$ = $1 | (int)CPPFunctionType::F_noexcept;
   $$ = $1 | (int)CPPFunctionType::F_noexcept;
 }
 }
-/*        | function_post KW_NOEXCEPT '(' const_expr ')'
+        | function_post KW_NOEXCEPT_LPAREN const_expr ')'
 {
 {
-  CPPExpression::Result result = $4->evaluate();
+  CPPExpression::Result result = $3->evaluate();
   if (result._type == CPPExpression::RT_error) {
   if (result._type == CPPExpression::RT_error) {
-    yywarning("noexcept requires a constant expression", @4);
+    yywarning("noexcept() requires a constant expression", @3);
   } else if (result.as_boolean()) {
   } else if (result.as_boolean()) {
     $$ = $1 | (int)CPPFunctionType::F_noexcept;
     $$ = $1 | (int)CPPFunctionType::F_noexcept;
   }
   }
-}*/
+}
         | function_post KW_FINAL
         | function_post KW_FINAL
 {
 {
   $$ = $1 | (int)CPPFunctionType::F_final;
   $$ = $1 | (int)CPPFunctionType::F_final;
@@ -1564,6 +1588,10 @@ function_operator:
         | '>'
         | '>'
 {
 {
   $$ = ">";
   $$ = ">";
+}
+        | SPACESHIP
+{
+  $$ = "<=>";
 }
 }
         | LSHIFT
         | LSHIFT
 {
 {
@@ -3054,6 +3082,12 @@ using_declaration:
   CPPUsing *using_decl = new CPPUsing($3, true, @1.file);
   CPPUsing *using_decl = new CPPUsing($3, true, @1.file);
   current_scope->add_declaration(using_decl, global_scope, current_lexer, @1);
   current_scope->add_declaration(using_decl, global_scope, current_lexer, @1);
   current_scope->add_using(using_decl, global_scope, current_lexer);
   current_scope->add_using(using_decl, global_scope, current_lexer);
+}
+        | KW_USING KW_ENUM name ';'
+{
+  CPPUsing *using_decl = new CPPUsing($3, false, @1.file);
+  current_scope->add_declaration(using_decl, global_scope, current_lexer, @1);
+  current_scope->add_using(using_decl, global_scope, current_lexer);
 }
 }
         ;
         ;
 
 
@@ -3075,6 +3109,10 @@ simple_int_type:
         | KW_WCHAR_T
         | KW_WCHAR_T
 {
 {
   $$ = new CPPSimpleType(CPPSimpleType::T_wchar_t);
   $$ = new CPPSimpleType(CPPSimpleType::T_wchar_t);
+}
+        | KW_CHAR8_T
+{
+  $$ = new CPPSimpleType(CPPSimpleType::T_char8_t);
 }
 }
         | KW_CHAR16_T
         | KW_CHAR16_T
 {
 {
@@ -3188,19 +3226,20 @@ element:
         | SCOPING
         | SCOPING
         | SIMPLE_IDENTIFIER
         | SIMPLE_IDENTIFIER
         | ELLIPSIS | OROR | ANDAND
         | ELLIPSIS | OROR | ANDAND
-        | EQCOMPARE | NECOMPARE | LECOMPARE | GECOMPARE
+        | EQCOMPARE | NECOMPARE | LECOMPARE | GECOMPARE | SPACESHIP
         | LSHIFT | RSHIFT | POINTSAT_STAR | DOT_STAR | POINTSAT
         | LSHIFT | RSHIFT | POINTSAT_STAR | DOT_STAR | POINTSAT
         | SCOPE | PLUSPLUS | MINUSMINUS
         | SCOPE | PLUSPLUS | MINUSMINUS
         | TIMESEQUAL | DIVIDEEQUAL | MODEQUAL | PLUSEQUAL | MINUSEQUAL
         | TIMESEQUAL | DIVIDEEQUAL | MODEQUAL | PLUSEQUAL | MINUSEQUAL
         | OREQUAL | ANDEQUAL | XOREQUAL | LSHIFTEQUAL | RSHIFTEQUAL
         | OREQUAL | ANDEQUAL | XOREQUAL | LSHIFTEQUAL | RSHIFTEQUAL
         | ATTR_LEFT | ATTR_RIGHT
         | ATTR_LEFT | ATTR_RIGHT
         | KW_ALIGNAS | KW_ALIGNOF | KW_AUTO | KW_BOOL | KW_CATCH
         | KW_ALIGNAS | KW_ALIGNOF | KW_AUTO | KW_BOOL | KW_CATCH
-        | KW_CHAR | KW_CHAR16_T | KW_CHAR32_T | KW_CLASS | KW_CONST
-        | KW_CONSTEXPR | KW_CONST_CAST | KW_DECLTYPE | KW_DEFAULT
-        | KW_DELETE | KW_DOUBLE | KW_DYNAMIC_CAST | KW_ELSE | KW_ENUM
-        | KW_EXTERN | KW_EXPLICIT | KW_FALSE | KW_FINAL | KW_FLOAT
-        | KW_FRIEND | KW_FOR | KW_GOTO | KW_IF | KW_INLINE | KW_INT
-        | KW_LONG | KW_MUTABLE | KW_NAMESPACE | KW_NEW | KW_NULLPTR
+        | KW_CHAR | KW_CHAR8_T | KW_CHAR16_T | KW_CHAR32_T | KW_CLASS
+        | KW_CONST | KW_CONSTEVAL | KW_CONSTEXPR | KW_CONSTINIT | KW_CONST_CAST
+        | KW_DECLTYPE | KW_DEFAULT | KW_DELETE | KW_DOUBLE | KW_DYNAMIC_CAST
+        | KW_ELSE | KW_ENUM | KW_EXTERN | KW_EXPLICIT | KW_EXPLICIT_LPAREN
+        | KW_FALSE | KW_FINAL | KW_FLOAT | KW_FRIEND | KW_FOR
+        | KW_GOTO | KW_IF | KW_INLINE | KW_INT | KW_LONG | KW_MUTABLE
+        | KW_NAMESPACE | KW_NEW | KW_NOEXCEPT | KW_NOEXCEPT_LPAREN | KW_NULLPTR
         | KW_OPERATOR | KW_OVERRIDE | KW_PRIVATE | KW_PROTECTED
         | KW_OPERATOR | KW_OVERRIDE | KW_PRIVATE | KW_PROTECTED
         | KW_PUBLIC | KW_PUBLISHED | KW_REGISTER | KW_REINTERPRET_CAST
         | KW_PUBLIC | KW_PUBLISHED | KW_REGISTER | KW_REINTERPRET_CAST
         | KW_RETURN | KW_SHORT | KW_SIGNED | KW_SIZEOF | KW_STATIC
         | KW_RETURN | KW_SHORT | KW_SIGNED | KW_SIZEOF | KW_STATIC
@@ -3369,6 +3408,10 @@ no_angle_bracket_const_expr:
         | no_angle_bracket_const_expr GECOMPARE no_angle_bracket_const_expr
         | no_angle_bracket_const_expr GECOMPARE no_angle_bracket_const_expr
 {
 {
   $$ = new CPPExpression(GECOMPARE, $1, $3);
   $$ = new CPPExpression(GECOMPARE, $1, $3);
+}
+        | no_angle_bracket_const_expr SPACESHIP no_angle_bracket_const_expr
+{
+  $$ = new CPPExpression(SPACESHIP, $1, $3);
 }
 }
         | no_angle_bracket_const_expr LSHIFT no_angle_bracket_const_expr
         | no_angle_bracket_const_expr LSHIFT no_angle_bracket_const_expr
 {
 {
@@ -3471,6 +3514,12 @@ const_expr:
   CPPType *type =
   CPPType *type =
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_wchar_t));
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_wchar_t));
   $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
   $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
+}
+        | KW_CHAR8_T '(' optional_const_expr_comma ')'
+{
+  CPPType *type =
+    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char8_t));
+  $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
 }
 }
         | KW_CHAR16_T '(' optional_const_expr_comma ')'
         | KW_CHAR16_T '(' optional_const_expr_comma ')'
 {
 {
@@ -3655,6 +3704,10 @@ const_expr:
         | const_expr GECOMPARE const_expr
         | const_expr GECOMPARE const_expr
 {
 {
   $$ = new CPPExpression(GECOMPARE, $1, $3);
   $$ = new CPPExpression(GECOMPARE, $1, $3);
+}
+        | const_expr SPACESHIP const_expr
+{
+  $$ = new CPPExpression(SPACESHIP, $1, $3);
 }
 }
         | const_expr '<' const_expr
         | const_expr '<' const_expr
 {
 {
@@ -3687,6 +3740,10 @@ const_expr:
         | const_expr '(' ')'
         | const_expr '(' ')'
 {
 {
   $$ = new CPPExpression('f', $1);
   $$ = new CPPExpression('f', $1);
+}
+        | KW_NOEXCEPT_LPAREN const_expr ')'
+{
+  $$ = new CPPExpression(KW_NOEXCEPT, $2);
 }
 }
         | const_expr '.' name
         | const_expr '.' name
 {
 {
@@ -3987,6 +4044,10 @@ formal_const_expr:
         | formal_const_expr GECOMPARE const_expr
         | formal_const_expr GECOMPARE const_expr
 {
 {
   $$ = new CPPExpression(GECOMPARE, $1, $3);
   $$ = new CPPExpression(GECOMPARE, $1, $3);
+}
+        | formal_const_expr SPACESHIP const_expr
+{
+  $$ = new CPPExpression(SPACESHIP, $1, $3);
 }
 }
         | formal_const_expr '<' const_expr
         | formal_const_expr '<' const_expr
 {
 {

+ 30 - 2
dtool/src/cppparser/cppExpression.cxx

@@ -591,7 +591,7 @@ evaluate() const {
     if (_u._variable->_type != nullptr &&
     if (_u._variable->_type != nullptr &&
         _u._variable->_initializer != nullptr) {
         _u._variable->_initializer != nullptr) {
       // A constexpr variable, which is treated as const.
       // A constexpr variable, which is treated as const.
-      if (_u._variable->_storage_class & CPPInstance::SC_constexpr) {
+      if (_u._variable->_storage_class & (CPPInstance::SC_constexpr | CPPInstance::SC_constinit)) {
         return _u._variable->_initializer->evaluate();
         return _u._variable->_initializer->evaluate();
       }
       }
       // A const variable.  Fetch its assigned value.
       // A const variable.  Fetch its assigned value.
@@ -816,6 +816,13 @@ evaluate() const {
         return Result(r1.as_integer() >= r2.as_integer());
         return Result(r1.as_integer() >= r2.as_integer());
       }
       }
 
 
+    case SPACESHIP:
+      if (r1._type == RT_real || r2._type == RT_real) {
+        return Result((r1.as_real() > r2.as_real()) - (r1.as_real() < r2.as_real()));
+      } else {
+        return Result((r1.as_integer() > r2.as_integer()) - (r1.as_integer() < r2.as_integer()));
+      }
+
     case '<':
     case '<':
       if (r1._type == RT_real || r2._type == RT_real) {
       if (r1._type == RT_real || r2._type == RT_real) {
         return Result(r1.as_real() < r2.as_real());
         return Result(r1.as_real() < r2.as_real());
@@ -853,6 +860,9 @@ evaluate() const {
     case ',':
     case ',':
       return r2;
       return r2;
 
 
+    case KW_NOEXCEPT:
+      return Result();
+
     default:
     default:
       cerr << "**unexpected operator**\n";
       cerr << "**unexpected operator**\n";
       abort();
       abort();
@@ -1173,8 +1183,12 @@ determine_type() const {
     case GECOMPARE:
     case GECOMPARE:
     case '<':
     case '<':
     case '>':
     case '>':
+    case KW_NOEXCEPT:
       return bool_type;
       return bool_type;
 
 
+    case SPACESHIP:
+      return nullptr;
+
     case '?':
     case '?':
       return t2;
       return t2;
 
 
@@ -1566,7 +1580,7 @@ is_tbd() const {
   case T_variable:
   case T_variable:
     if (_u._variable->_type != nullptr &&
     if (_u._variable->_type != nullptr &&
         _u._variable->_initializer != nullptr) {
         _u._variable->_initializer != nullptr) {
-      if (_u._variable->_storage_class & CPPInstance::SC_constexpr) {
+      if (_u._variable->_storage_class & (CPPInstance::SC_constexpr | CPPInstance::SC_constinit)) {
         return false;
         return false;
       }
       }
       CPPConstType *const_type = _u._variable->_type->as_const_type();
       CPPConstType *const_type = _u._variable->_type->as_const_type();
@@ -1898,6 +1912,12 @@ output(std::ostream &out, int indent_level, CPPScope *scope, bool) const {
       out << "()";
       out << "()";
       break;
       break;
 
 
+    case KW_NOEXCEPT:
+      out << "noexcept(";
+      _u._op._op1->output(out, indent_level, scope, false);
+      out << ")";
+      break;
+
     default:
     default:
       out << "(" << (char)_u._op._operator << " ";
       out << "(" << (char)_u._op._operator << " ";
       _u._op._op1->output(out, indent_level, scope, false);
       _u._op._op1->output(out, indent_level, scope, false);
@@ -1956,6 +1976,14 @@ output(std::ostream &out, int indent_level, CPPScope *scope, bool) const {
       out << ")";
       out << ")";
       break;
       break;
 
 
+    case SPACESHIP:
+      out << "(";
+      _u._op._op1->output(out, indent_level, scope, false);
+      out << " <=> ";
+      _u._op._op2->output(out, indent_level, scope, false);
+      out << ")";
+      break;
+
     case LSHIFT:
     case LSHIFT:
       out << "(";
       out << "(";
       _u._op._op1->output(out, indent_level, scope, false);
       _u._op._op1->output(out, indent_level, scope, false);

+ 6 - 0
dtool/src/cppparser/cppInstance.cxx

@@ -575,9 +575,15 @@ output(std::ostream &out, int indent_level, CPPScope *scope, bool complete,
   if (_storage_class & SC_mutable) {
   if (_storage_class & SC_mutable) {
     out << "mutable ";
     out << "mutable ";
   }
   }
+  if (_storage_class & SC_consteval) {
+    out << "consteval ";
+  }
   if (_storage_class & SC_constexpr) {
   if (_storage_class & SC_constexpr) {
     out << "constexpr ";
     out << "constexpr ";
   }
   }
+  if (_storage_class & SC_constinit) {
+    out << "constinit ";
+  }
   if (_storage_class & SC_thread_local) {
   if (_storage_class & SC_thread_local) {
     out << "thread_local ";
     out << "thread_local ";
   }
   }

+ 3 - 1
dtool/src/cppparser/cppInstance.h

@@ -44,7 +44,9 @@ public:
     SC_pure_virtual = 0x0080,
     SC_pure_virtual = 0x0080,
     SC_volatile     = 0x0100,
     SC_volatile     = 0x0100,
     SC_mutable      = 0x0200,
     SC_mutable      = 0x0200,
-    SC_constexpr    = 0x0400,
+    SC_consteval    = 0x080000,
+    SC_constexpr    = 0x000400,
+    SC_constinit    = 0x100000,
 
 
     // This bit is only set by CPPStructType::check_virtual().
     // This bit is only set by CPPStructType::check_virtual().
     SC_inherited_virtual = 0x0800,
     SC_inherited_virtual = 0x0800,

+ 132 - 61
dtool/src/cppparser/cppManifest.cxx

@@ -23,7 +23,7 @@ using std::string;
  */
  */
 CPPManifest::ExpansionNode::
 CPPManifest::ExpansionNode::
 ExpansionNode(int parm_number, bool stringify, bool paste) :
 ExpansionNode(int parm_number, bool stringify, bool paste) :
-  _parm_number(parm_number), _stringify(stringify), _paste(paste)
+  _parm_number(parm_number), _stringify(stringify), _paste(paste), _optional(false)
 {
 {
 }
 }
 
 
@@ -32,7 +32,16 @@ ExpansionNode(int parm_number, bool stringify, bool paste) :
  */
  */
 CPPManifest::ExpansionNode::
 CPPManifest::ExpansionNode::
 ExpansionNode(const string &str, bool paste) :
 ExpansionNode(const string &str, bool paste) :
-  _parm_number(-1), _stringify(false), _paste(paste), _str(str)
+  _parm_number(-1), _stringify(false), _paste(paste), _optional(false), _str(str)
+{
+}
+
+/**
+ *
+ */
+CPPManifest::ExpansionNode::
+ExpansionNode(Expansion nested, bool stringify, bool paste, bool optional) :
+  _parm_number(-1), _stringify(stringify), _paste(paste), _optional(optional), _nested(std::move(nested))
 {
 {
 }
 }
 
 
@@ -76,7 +85,7 @@ CPPManifest(const string &args, const cppyyltype &loc) :
     p++;
     p++;
   }
   }
 
 
-  save_expansion(args.substr(p), parameter_names);
+  save_expansion(_expansion, args.substr(p), parameter_names);
 }
 }
 
 
 /**
 /**
@@ -119,7 +128,7 @@ CPPManifest(const string &macro, const string &definition) :
     _num_parameters = 0;
     _num_parameters = 0;
   }
   }
 
 
-  save_expansion(definition, parameter_names);
+  save_expansion(_expansion, definition, parameter_names);
 }
 }
 
 
 /**
 /**
@@ -186,54 +195,7 @@ stringify(const string &source) {
  */
  */
 string CPPManifest::
 string CPPManifest::
 expand(const vector_string &args) const {
 expand(const vector_string &args) const {
-  string result;
-
-  Expansion::const_iterator ei;
-  for (ei = _expansion.begin(); ei != _expansion.end(); ++ei) {
-    if ((*ei)._parm_number >= 0) {
-      int i = (*ei)._parm_number;
-
-      string subst;
-      if (i < (int)args.size()) {
-        subst = args[i];
-
-        if (i == _variadic_param) {
-          for (++i; i < (int)args.size(); ++i) {
-            subst += ", " + args[i];
-          }
-        }
-        if ((*ei)._stringify) {
-          subst = stringify(subst);
-        }
-      } else if (i == _variadic_param && (*ei)._paste) {
-        // Special case GCC behavior: if __VA_ARGS__ is pasted to a comma and
-        // no arguments are passed, the comma is removed.  MSVC does this
-        // automatically.  Not sure if we should allow MSVC behavior as well.
-        if (!result.empty() && *result.rbegin() == ',') {
-          result.resize(result.size() - 1);
-        }
-      }
-
-      if (!subst.empty()) {
-        if (result.empty() || (*ei)._paste) {
-          result += subst;
-        } else {
-          result += ' ';
-          result += subst;
-        }
-      }
-    }
-    if (!(*ei)._str.empty()) {
-      if (result.empty() || (*ei)._paste) {
-        result += (*ei)._str;
-      } else {
-        result += ' ';
-        result += (*ei)._str;
-      }
-    }
-  }
-
-  return result;
+  return r_expand(_expansion, args);
 }
 }
 
 
 /**
 /**
@@ -283,19 +245,25 @@ output(std::ostream &out) const {
       out << " ";
       out << " ";
     }
     }
 
 
+    if ((*ei)._stringify) {
+      out << "#";
+    }
     if ((*ei)._parm_number >= 0) {
     if ((*ei)._parm_number >= 0) {
-      if ((*ei)._stringify) {
-        out << "#";
-      }
       if ((*ei)._parm_number == _variadic_param) {
       if ((*ei)._parm_number == _variadic_param) {
         out << "__VA_ARGS__";
         out << "__VA_ARGS__";
       } else {
       } else {
         out << "$" << (*ei)._parm_number + 1;
         out << "$" << (*ei)._parm_number + 1;
       }
       }
     }
     }
+    if ((*ei)._optional) {
+      out << "__VA_OPT__(";
+    }
     if (!(*ei)._str.empty()) {
     if (!(*ei)._str.empty()) {
       out << (*ei)._str;
       out << (*ei)._str;
     }
     }
+    if ((*ei)._optional) {
+      out << ")";
+    }
   }
   }
 }
 }
 
 
@@ -350,7 +318,7 @@ parse_parameters(const string &args, size_t &p,
  *
  *
  */
  */
 void CPPManifest::
 void CPPManifest::
-save_expansion(const string &exp, const vector_string &parameter_names) {
+save_expansion(Expansion &expansion, const string &exp, const vector_string &parameter_names) {
   // Walk through the expansion string.  For each substring that is an
   // Walk through the expansion string.  For each substring that is an
   // identifier, check it against parameter_names.
   // identifier, check it against parameter_names.
   size_t p = 0;
   size_t p = 0;
@@ -375,6 +343,40 @@ save_expansion(const string &exp, const vector_string &parameter_names) {
         // C99-style variadics, ie.  #define macro(...) __VA_ARGS__
         // C99-style variadics, ie.  #define macro(...) __VA_ARGS__
         pnum = _variadic_param;
         pnum = _variadic_param;
 
 
+      } else if (ident == "__VA_OPT__") {
+        // Optional expansion, only expands if __VA_ARGS__ is non-empty
+        while (p < exp.size() && isspace(exp[p])) {
+          ++p;
+        }
+        if (p < exp.size() && exp[p] == '(') {
+          int start = ++p;
+          int nesting = 1;
+          while (p < exp.size() && nesting > 0) {
+            if (exp[p] == '(') {
+              ++nesting;
+            }
+            else if (exp[p] == ')') {
+              --nesting;
+            }
+            ++p;
+          }
+
+          if (last != q) {
+            expansion.push_back(ExpansionNode(exp.substr(last, q - last), paste));
+            paste = false;
+          }
+
+          // Store this as a nested expansion, because the whole thing may be
+          // stringified as a whole.
+          Expansion nested;
+          save_expansion(nested, exp.substr(start, p - 1 - start), parameter_names);
+          expansion.push_back(ExpansionNode(std::move(nested), stringify, paste, true));
+          stringify = false;
+          paste = false;
+          last = p;
+          continue;
+        }
+
       } else {
       } else {
         for (int i = 0; pnum == -1 && i < (int)parameter_names.size(); ++i) {
         for (int i = 0; pnum == -1 && i < (int)parameter_names.size(); ++i) {
           const string &pname = parameter_names[i];
           const string &pname = parameter_names[i];
@@ -387,10 +389,10 @@ save_expansion(const string &exp, const vector_string &parameter_names) {
       if (pnum != -1) {
       if (pnum != -1) {
         // Yep!
         // Yep!
         if (last != q) {
         if (last != q) {
-          _expansion.push_back(ExpansionNode(exp.substr(last, q - last), paste));
+          expansion.push_back(ExpansionNode(exp.substr(last, q - last), paste));
           paste = false;
           paste = false;
         }
         }
-        _expansion.push_back(ExpansionNode(pnum, stringify, paste));
+        expansion.push_back(ExpansionNode(pnum, stringify, paste));
         stringify = false;
         stringify = false;
         paste = false;
         paste = false;
         last = p;
         last = p;
@@ -398,7 +400,7 @@ save_expansion(const string &exp, const vector_string &parameter_names) {
     } else if (exp[p] == '#') {
     } else if (exp[p] == '#') {
       // This may be a stringification operator.
       // This may be a stringification operator.
       if (last != p) {
       if (last != p) {
-        _expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste));
+        expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste));
         paste = false;
         paste = false;
       }
       }
 
 
@@ -416,7 +418,7 @@ save_expansion(const string &exp, const vector_string &parameter_names) {
 
 
     } else if (isspace(exp[p])) {
     } else if (isspace(exp[p])) {
       if (last != p) {
       if (last != p) {
-        _expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste));
+        expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste));
         paste = false;
         paste = false;
       }
       }
 
 
@@ -429,6 +431,75 @@ save_expansion(const string &exp, const vector_string &parameter_names) {
   }
   }
 
 
   if (last != p) {
   if (last != p) {
-    _expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste));
+    expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste));
   }
   }
 }
 }
+
+/**
+ *
+ */
+string CPPManifest::
+r_expand(const Expansion &expansion, const vector_string &args) const {
+  std::string result;
+
+  for (const ExpansionNode &node : expansion) {
+    if (node._parm_number >= 0) {
+      int i = node._parm_number;
+
+      string subst;
+      if (i < (int)args.size()) {
+        subst = args[i];
+
+        if (i == _variadic_param) {
+          for (++i; i < (int)args.size(); ++i) {
+            subst += ", " + args[i];
+          }
+        }
+        if (node._stringify) {
+          subst = stringify(subst);
+        }
+      } else if (i == _variadic_param && node._paste) {
+        // Special case GCC behavior: if __VA_ARGS__ is pasted to a comma and
+        // no arguments are passed, the comma is removed.  MSVC does this
+        // automatically.  Not sure if we should allow MSVC behavior as well.
+        if (!result.empty() && *result.rbegin() == ',') {
+          result.resize(result.size() - 1);
+        }
+      }
+
+      if (!subst.empty()) {
+        if (result.empty() || node._paste) {
+          result += subst;
+        } else {
+          result += ' ';
+          result += subst;
+        }
+      }
+    }
+    if (!node._str.empty()) {
+      if (result.empty() || node._paste) {
+        result += node._str;
+      } else {
+        result += ' ';
+        result += node._str;
+      }
+    }
+    if (!node._nested.empty()) {
+      string nested_result;
+      if (node._optional && args.size() >= _num_parameters) {
+        nested_result = r_expand(node._nested, args);
+      }
+      if (node._stringify) {
+        nested_result = stringify(nested_result);
+      }
+      if (result.empty() || node._paste) {
+        result += nested_result;
+      } else {
+        result += ' ';
+        result += nested_result;
+      }
+    }
+  }
+
+  return result;
+}

+ 12 - 5
dtool/src/cppparser/cppManifest.h

@@ -54,21 +54,28 @@ public:
   CPPVisibility _vis;
   CPPVisibility _vis;
 
 
 private:
 private:
-  void parse_parameters(const std::string &args, size_t &p,
-                        vector_string &parameter_names);
-  void save_expansion(const std::string &exp,
-                      const vector_string &parameter_names);
-
   class ExpansionNode {
   class ExpansionNode {
   public:
   public:
     ExpansionNode(int parm_number, bool stringify, bool paste);
     ExpansionNode(int parm_number, bool stringify, bool paste);
     ExpansionNode(const std::string &str, bool paste = false);
     ExpansionNode(const std::string &str, bool paste = false);
+    ExpansionNode(std::vector<ExpansionNode> nested, bool stringify = false, bool paste = false, bool optional = false);
     int _parm_number;
     int _parm_number;
     bool _stringify;
     bool _stringify;
     bool _paste;
     bool _paste;
+    bool _optional;
     std::string _str;
     std::string _str;
+    std::vector<ExpansionNode> _nested;
   };
   };
   typedef std::vector<ExpansionNode> Expansion;
   typedef std::vector<ExpansionNode> Expansion;
+
+  void parse_parameters(const std::string &args, size_t &p,
+                        vector_string &parameter_names);
+  void save_expansion(Expansion &expansion, const std::string &exp,
+                      const vector_string &parameter_names);
+
+  std::string r_expand(const Expansion &expansion,
+                       const vector_string &args = vector_string()) const;
+
   Expansion _expansion;
   Expansion _expansion;
 };
 };
 
 

+ 28 - 2
dtool/src/cppparser/cppPreprocessor.cxx

@@ -1140,6 +1140,10 @@ check_trigraph(int c) {
   case RSHIFT:
   case RSHIFT:
     if (next_c == '=') return RSHIFTEQUAL;
     if (next_c == '=') return RSHIFTEQUAL;
     break;
     break;
+
+  case LECOMPARE:
+    if (next_c == '>') return SPACESHIP;
+    break;
   }
   }
 
 
   return 0;
   return 0;
@@ -1983,6 +1987,24 @@ get_identifier(int c) {
   }
   }
 
 
   if (kw != 0) {
   if (kw != 0) {
+    if (kw == KW_EXPLICIT || kw == KW_NOEXCEPT) {
+      // These can be followed by a left-paren.  Doing this helps to avoid
+      // shift/reduce conflicts in the parser.
+      while (c != EOF && isspace(c)) {
+        get();
+        c = peek();
+      }
+      if (c == '(') {
+        if (kw == KW_EXPLICIT) {
+          kw = KW_EXPLICIT_LPAREN;
+        }
+        else if (kw == KW_NOEXCEPT) {
+          kw = KW_NOEXCEPT_LPAREN;
+        }
+        get();
+      }
+    }
+
     YYSTYPE result;
     YYSTYPE result;
     result.u.identifier = nullptr;
     result.u.identifier = nullptr;
     return CPPToken(kw, loc, name, result);
     return CPPToken(kw, loc, name, result);
@@ -2089,6 +2111,7 @@ get_literal(int token, YYLTYPE loc, const string &str, const YYSTYPE &value) {
         break;
         break;
       } else if (token == CHAR_TOK && (simple == CPPSimpleType::T_char ||
       } else if (token == CHAR_TOK && (simple == CPPSimpleType::T_char ||
                                        simple == CPPSimpleType::T_wchar_t ||
                                        simple == CPPSimpleType::T_wchar_t ||
+                                       simple == CPPSimpleType::T_char8_t ||
                                        simple == CPPSimpleType::T_char16_t ||
                                        simple == CPPSimpleType::T_char16_t ||
                                        simple == CPPSimpleType::T_char32_t)) {
                                        simple == CPPSimpleType::T_char32_t)) {
         // We currently don't have the means to check the exact character
         // We currently don't have the means to check the exact character
@@ -2126,7 +2149,7 @@ get_literal(int token, YYLTYPE loc, const string &str, const YYSTYPE &value) {
         CPPExpression::Type str_type = value.u.expr->_type;
         CPPExpression::Type str_type = value.u.expr->_type;
         if ((str_type == CPPExpression::T_string && simple == CPPSimpleType::T_char) ||
         if ((str_type == CPPExpression::T_string && simple == CPPSimpleType::T_char) ||
             (str_type == CPPExpression::T_wstring && simple == CPPSimpleType::T_wchar_t) ||
             (str_type == CPPExpression::T_wstring && simple == CPPSimpleType::T_wchar_t) ||
-            (str_type == CPPExpression::T_u8string && simple == CPPSimpleType::T_char) ||
+            (str_type == CPPExpression::T_u8string && (simple == CPPSimpleType::T_char || simple == CPPSimpleType::T_char8_t)) ||
             (str_type == CPPExpression::T_u16string && simple == CPPSimpleType::T_char16_t) ||
             (str_type == CPPExpression::T_u16string && simple == CPPSimpleType::T_char16_t) ||
             (str_type == CPPExpression::T_u32string && simple == CPPSimpleType::T_char32_t)) {
             (str_type == CPPExpression::T_u32string && simple == CPPSimpleType::T_char32_t)) {
           expr = value.u.expr;
           expr = value.u.expr;
@@ -2290,7 +2313,7 @@ extract_manifest_args(const string &name, int num_args, int va_arg,
   loc.last_column = first_col;
   loc.last_column = first_col;
   loc.file = first_file;
   loc.file = first_file;
 
 
-  if ((int)args.size() < num_args) {
+  if ((int)args.size() < num_args - (va_arg >= 0)) {
     warning("Not enough arguments for manifest " + name, loc);
     warning("Not enough arguments for manifest " + name, loc);
 
 
   } else if (va_arg < 0 && (int)args.size() > num_args) {
   } else if (va_arg < 0 && (int)args.size() > num_args) {
@@ -2608,13 +2631,16 @@ check_keyword(const string &name) {
   if (name == "bool") return KW_BOOL;
   if (name == "bool") return KW_BOOL;
   if (name == "catch") return KW_CATCH;
   if (name == "catch") return KW_CATCH;
   if (name == "char") return KW_CHAR;
   if (name == "char") return KW_CHAR;
+  if (name == "char8_t") return KW_CHAR8_T;
   if (name == "char16_t") return KW_CHAR16_T;
   if (name == "char16_t") return KW_CHAR16_T;
   if (name == "char32_t") return KW_CHAR32_T;
   if (name == "char32_t") return KW_CHAR32_T;
   if (name == "class") return KW_CLASS;
   if (name == "class") return KW_CLASS;
   if (name == "const") return KW_CONST;
   if (name == "const") return KW_CONST;
   if (name == "__const") return KW_CONST;
   if (name == "__const") return KW_CONST;
   if (name == "__const__") return KW_CONST;
   if (name == "__const__") return KW_CONST;
+  if (name == "consteval") return KW_CONSTEVAL;
   if (name == "constexpr") return KW_CONSTEXPR;
   if (name == "constexpr") return KW_CONSTEXPR;
+  if (name == "constinit") return KW_CONSTINIT;
   if (name == "const_cast") return KW_CONST_CAST;
   if (name == "const_cast") return KW_CONST_CAST;
   if (name == "decltype") return KW_DECLTYPE;
   if (name == "decltype") return KW_DECLTYPE;
   if (name == "default") return KW_DEFAULT;
   if (name == "default") return KW_DEFAULT;

+ 4 - 0
dtool/src/cppparser/cppSimpleType.cxx

@@ -185,6 +185,10 @@ output(std::ostream &out, int, CPPScope *, bool) const {
     out << "wchar_t";
     out << "wchar_t";
     break;
     break;
 
 
+  case T_char8_t:
+    out << "char8_t";
+    break;
+
   case T_char16_t:
   case T_char16_t:
     out << "char16_t";
     out << "char16_t";
     break;
     break;

+ 1 - 0
dtool/src/cppparser/cppSimpleType.h

@@ -28,6 +28,7 @@ public:
     T_bool,
     T_bool,
     T_char,
     T_char,
     T_wchar_t,
     T_wchar_t,
+    T_char8_t,
     T_char16_t,
     T_char16_t,
     T_char32_t,
     T_char32_t,
     T_int,
     T_int,

+ 5 - 5
dtool/src/dtoolbase/pallocator.T

@@ -20,7 +20,7 @@ pallocator_single(TypeHandle type_handle) noexcept :
 
 
 template<class Type>
 template<class Type>
 INLINE Type *pallocator_single<Type>::
 INLINE Type *pallocator_single<Type>::
-allocate(typename pallocator_single<Type>::size_type n, typename std::allocator<void>::const_pointer) {
+allocate(typename pallocator_single<Type>::size_type n, const void *) {
   TAU_PROFILE("pallocator_single:allocate()", " ", TAU_USER);
   TAU_PROFILE("pallocator_single:allocate()", " ", TAU_USER);
   // This doesn't support allocating arrays.
   // This doesn't support allocating arrays.
   assert(n == 1);
   assert(n == 1);
@@ -30,7 +30,7 @@ allocate(typename pallocator_single<Type>::size_type n, typename std::allocator<
 
 
 template<class Type>
 template<class Type>
 INLINE void pallocator_single<Type>::
 INLINE void pallocator_single<Type>::
-deallocate(typename pallocator_single<Type>::pointer p, typename pallocator_single<Type>::size_type) {
+deallocate(Type *p, typename pallocator_single<Type>::size_type) {
   TAU_PROFILE("pallocator_single:deallocate()", " ", TAU_USER);
   TAU_PROFILE("pallocator_single:deallocate()", " ", TAU_USER);
   StaticDeletedChain<Type>::deallocate(p, _type_handle);
   StaticDeletedChain<Type>::deallocate(p, _type_handle);
 }
 }
@@ -44,13 +44,13 @@ pallocator_array(TypeHandle type_handle) noexcept :
 
 
 template<class Type>
 template<class Type>
 INLINE Type *pallocator_array<Type>::
 INLINE Type *pallocator_array<Type>::
-allocate(typename pallocator_array<Type>::size_type n, typename std::allocator<void>::const_pointer) {
-  return (typename pallocator_array<Type>::pointer)
+allocate(typename pallocator_array<Type>::size_type n, const void *) {
+  return (Type *)
     ASSUME_ALIGNED(_type_handle.allocate_array(n * sizeof(Type)), MEMORY_HOOK_ALIGNMENT);
     ASSUME_ALIGNED(_type_handle.allocate_array(n * sizeof(Type)), MEMORY_HOOK_ALIGNMENT);
 }
 }
 
 
 template<class Type>
 template<class Type>
 INLINE void pallocator_array<Type>::
 INLINE void pallocator_array<Type>::
-deallocate(typename pallocator_array<Type>::pointer p, typename pallocator_array<Type>::size_type) {
+deallocate(Type *p, typename pallocator_array<Type>::size_type) {
   _type_handle.deallocate_array((void *)p);
   _type_handle.deallocate_array((void *)p);
 }
 }

+ 12 - 12
dtool/src/dtoolbase/pallocator.h

@@ -46,10 +46,10 @@ class pallocator_single : public std::allocator<Type> {
 public:
 public:
   // Nowadays we cannot implicitly inherit typedefs from base classes in a
   // Nowadays we cannot implicitly inherit typedefs from base classes in a
   // template class; we must explicitly copy them here.
   // template class; we must explicitly copy them here.
-  typedef typename std::allocator<Type>::pointer pointer;
-  typedef typename std::allocator<Type>::reference reference;
-  typedef typename std::allocator<Type>::const_pointer const_pointer;
-  typedef typename std::allocator<Type>::const_reference const_reference;
+  typedef Type *pointer;
+  typedef Type &reference;
+  typedef const Type *const_pointer;
+  typedef const Type &const_reference;
   typedef typename std::allocator<Type>::size_type size_type;
   typedef typename std::allocator<Type>::size_type size_type;
 
 
   INLINE pallocator_single(TypeHandle type_handle) noexcept;
   INLINE pallocator_single(TypeHandle type_handle) noexcept;
@@ -59,9 +59,9 @@ public:
   INLINE pallocator_single(const pallocator_single<U> &copy) noexcept :
   INLINE pallocator_single(const pallocator_single<U> &copy) noexcept :
     _type_handle(copy._type_handle) { }
     _type_handle(copy._type_handle) { }
 
 
-  INLINE Type *allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
+  INLINE Type *allocate(size_type n, const void *hint = 0)
     RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT);
     RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT);
-  INLINE void deallocate(pointer p, size_type n);
+  INLINE void deallocate(Type *p, size_type n);
 
 
   template<class U> struct rebind {
   template<class U> struct rebind {
     typedef pallocator_single<U> other;
     typedef pallocator_single<U> other;
@@ -75,10 +75,10 @@ class pallocator_array : public std::allocator<Type> {
 public:
 public:
   // Nowadays we cannot implicitly inherit typedefs from base classes in a
   // Nowadays we cannot implicitly inherit typedefs from base classes in a
   // template class; we must explicitly copy them here.
   // template class; we must explicitly copy them here.
-  typedef typename std::allocator<Type>::pointer pointer;
-  typedef typename std::allocator<Type>::reference reference;
-  typedef typename std::allocator<Type>::const_pointer const_pointer;
-  typedef typename std::allocator<Type>::const_reference const_reference;
+  typedef Type *pointer;
+  typedef Type &reference;
+  typedef const Type *const_pointer;
+  typedef const Type &const_reference;
   typedef typename std::allocator<Type>::size_type size_type;
   typedef typename std::allocator<Type>::size_type size_type;
 
 
   INLINE pallocator_array(TypeHandle type_handle = TypeHandle::none()) noexcept;
   INLINE pallocator_array(TypeHandle type_handle = TypeHandle::none()) noexcept;
@@ -88,9 +88,9 @@ public:
   INLINE pallocator_array(const pallocator_array<U> &copy) noexcept :
   INLINE pallocator_array(const pallocator_array<U> &copy) noexcept :
     _type_handle(copy._type_handle) { }
     _type_handle(copy._type_handle) { }
 
 
-  INLINE Type *allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
+  INLINE Type *allocate(size_type n, const void *hint = 0)
     RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT);
     RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT);
-  INLINE void deallocate(pointer p, size_type n);
+  INLINE void deallocate(Type *p, size_type n);
 
 
   template<class U> struct rebind {
   template<class U> struct rebind {
     typedef pallocator_array<U> other;
     typedef pallocator_array<U> other;

+ 11 - 6
dtool/src/interrogate/functionRemap.cxx

@@ -738,6 +738,11 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
       _void_return = true;
       _void_return = true;
     }
     }
 
 
+  } else if (fname == "operator <=>") {
+    // This returns an opaque object that we must leave unchanged.
+    _return_type = new ParameterRemapUnchanged(rtype);
+    _void_return = false;
+
   } else {
   } else {
     // The normal case.
     // The normal case.
     _return_type = interface_maker->remap_parameter(_cpptype, rtype);
     _return_type = interface_maker->remap_parameter(_cpptype, rtype);
@@ -900,9 +905,9 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
 
 
     } else if (fname == "operator /") {
     } else if (fname == "operator /") {
       if (_has_this && _parameters.size() == 2 &&
       if (_has_this && _parameters.size() == 2 &&
-          TypeManager::is_float(_parameters[1]._remap->get_new_type())) {
-        // This division operator takes a single float argument.
-        _flags |= F_divide_float;
+          TypeManager::is_integer(_parameters[1]._remap->get_new_type())) {
+        // This division operator takes a single integer argument.
+        _flags |= F_divide_integer;
       }
       }
 
 
     } else if (fname == "get_key" || fname == "get_hash") {
     } else if (fname == "get_key" || fname == "get_hash") {
@@ -947,9 +952,9 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
   case T_assignment_method:
   case T_assignment_method:
     if (fname == "operator /=") {
     if (fname == "operator /=") {
       if (_has_this && _parameters.size() == 2 &&
       if (_has_this && _parameters.size() == 2 &&
-          TypeManager::is_float(_parameters[1]._remap->get_new_type())) {
-        // This division operator takes a single float argument.
-        _flags |= F_divide_float;
+          TypeManager::is_integer(_parameters[1]._remap->get_new_type())) {
+        // This division operator takes a single integer argument.
+        _flags |= F_divide_integer;
       }
       }
     }
     }
     break;
     break;

+ 1 - 1
dtool/src/interrogate/functionRemap.h

@@ -98,7 +98,7 @@ public:
     F_iter               = 0x0400,
     F_iter               = 0x0400,
     F_compare_to         = 0x0800,
     F_compare_to         = 0x0800,
     F_coerce_constructor = 0x1000,
     F_coerce_constructor = 0x1000,
-    F_divide_float       = 0x2000,
+    F_divide_integer     = 0x2000,
     F_hash               = 0x4000,
     F_hash               = 0x4000,
     F_explicit_args      = 0x8000,
     F_explicit_args      = 0x8000,
   };
   };

+ 114 - 30
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -70,6 +70,7 @@ RenameSet methodRenameDictionary[] = {
   { "operator >"    , "__gt__",                 0 },
   { "operator >"    , "__gt__",                 0 },
   { "operator <="   , "__le__",                 0 },
   { "operator <="   , "__le__",                 0 },
   { "operator >="   , "__ge__",                 0 },
   { "operator >="   , "__ge__",                 0 },
+  { "operator <=>"  , "__cmp__",                0 },
   { "operator ="    , "assign",                 0 },
   { "operator ="    , "assign",                 0 },
   { "operator ()"   , "__call__",               0 },
   { "operator ()"   , "__call__",               0 },
   { "operator []"   , "__getitem__",            0 },
   { "operator []"   , "__getitem__",            0 },
@@ -334,6 +335,18 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap,
     return true;
     return true;
   }
   }
 
 
+  if (method_name == "__truediv__") {
+    def._answer_location = "nb_true_divide";
+    def._wrapper_type = WT_binary_operator;
+    return true;
+  }
+
+  if (method_name == "__floordiv__") {
+    def._answer_location = "nb_floor_divide";
+    def._wrapper_type = WT_binary_operator;
+    return true;
+  }
+
   if (method_name == "operator %") {
   if (method_name == "operator %") {
     def._answer_location = "nb_remainder";
     def._answer_location = "nb_remainder";
     def._wrapper_type = WT_binary_operator;
     def._wrapper_type = WT_binary_operator;
@@ -406,6 +419,18 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap,
     return true;
     return true;
   }
   }
 
 
+  if (method_name == "__itruediv__") {
+    def._answer_location = "nb_inplace_true_divide";
+    def._wrapper_type = WT_binary_operator;
+    return true;
+  }
+
+  if (method_name == "__ifloordiv__") {
+    def._answer_location = "nb_inplace_floor_divide";
+    def._wrapper_type = WT_binary_operator;
+    return true;
+  }
+
   if (method_name == "operator %=") {
   if (method_name == "operator %=") {
     def._answer_location = "nb_inplace_remainder";
     def._answer_location = "nb_inplace_remainder";
     def._wrapper_type = WT_inplace_binary_operator;
     def._wrapper_type = WT_inplace_binary_operator;
@@ -1693,10 +1718,12 @@ write_module_class(ostream &out, Object *obj) {
 
 
         // Python 3 doesn't support nb_divide.  It has nb_true_divide and also
         // Python 3 doesn't support nb_divide.  It has nb_true_divide and also
         // nb_floor_divide, but they have different semantics than in C++.
         // nb_floor_divide, but they have different semantics than in C++.
-        // Ugh.  Make special slots to store the nb_divide members that take a
-        // float.  We'll use this to build up nb_true_divide, so that we can
-        // still properly divide float vector types.
-        if (remap->_flags & FunctionRemap::F_divide_float) {
+        // Ugh.  Make special slots to store the nb_divide members that don't
+        // take an int.  We'll use this to build up nb_true_divide, in the
+        // absence of a custom __truediv__, so that we can still properly divide
+        // float vector types.
+        if ((key == "nb_divide" || key == "nb_inplace_divide") &&
+            (remap->_flags & FunctionRemap::F_divide_integer) == 0) {
           string true_key;
           string true_key;
           if (key == "nb_inplace_divide") {
           if (key == "nb_inplace_divide") {
             true_key = "nb_inplace_true_divide";
             true_key = "nb_inplace_true_divide";
@@ -1727,7 +1754,8 @@ write_module_class(ostream &out, Object *obj) {
           fname == "operator ==" ||
           fname == "operator ==" ||
           fname == "operator !=" ||
           fname == "operator !=" ||
           fname == "operator >" ||
           fname == "operator >" ||
-          fname == "operator >=") {
+          fname == "operator >=" ||
+          fname == "operator <=>") {
         continue;
         continue;
       }
       }
 
 
@@ -2594,6 +2622,7 @@ write_module_class(ostream &out, Object *obj) {
     out << "    return nullptr;\n";
     out << "    return nullptr;\n";
     out << "  }\n\n";
     out << "  }\n\n";
 
 
+    std::set<FunctionRemap *> threeway_remaps;
     bool have_eq = false;
     bool have_eq = false;
     bool have_ne = false;
     bool have_ne = false;
     for (Function *func : obj->_methods) {
     for (Function *func : obj->_methods) {
@@ -2624,6 +2653,9 @@ write_module_class(ostream &out, Object *obj) {
         op_type = "Py_GT";
         op_type = "Py_GT";
       } else if (fname == "operator >=") {
       } else if (fname == "operator >=") {
         op_type = "Py_GE";
         op_type = "Py_GE";
+      } else if (fname == "operator <=>") {
+        threeway_remaps = std::move(remaps);
+        continue;
       } else {
       } else {
         continue;
         continue;
       }
       }
@@ -2644,7 +2676,15 @@ write_module_class(ostream &out, Object *obj) {
     }
     }
 
 
     if (has_local_richcompare) {
     if (has_local_richcompare) {
-      if (have_eq && !have_ne) {
+      if (!threeway_remaps.empty()) {
+        out << "  default:\n";
+        out << "    {\n";
+        string expected_params;
+        write_function_forset(out, threeway_remaps, 1, 1, expected_params, 6, true, false,
+                              AT_single_arg, RF_richcompare_zero | RF_err_null, false);
+        out << "    }\n";
+      }
+      else if (have_eq && !have_ne) {
         // Generate a not-equal function from the equal function.
         // Generate a not-equal function from the equal function.
         for (Function *func : obj->_methods) {
         for (Function *func : obj->_methods) {
           std::set<FunctionRemap*> remaps;
           std::set<FunctionRemap*> remaps;
@@ -2687,8 +2727,13 @@ write_module_class(ostream &out, Object *obj) {
       out << "    PyErr_Clear();\n";
       out << "    PyErr_Clear();\n";
       out << "  }\n\n";
       out << "  }\n\n";
     }
     }
+    else if (!threeway_remaps.empty()) {
+      string expected_params;
+      write_function_forset(out, threeway_remaps, 1, 1, expected_params, 2, true, false,
+                            AT_single_arg, RF_richcompare_zero | RF_err_null, false);
+    }
 
 
-    if (slots.count("tp_compare")) {
+    if (slots.count("tp_compare") && threeway_remaps.empty()) {
       // A lot of Panda code depends on comparisons being done via the
       // A lot of Panda code depends on comparisons being done via the
       // compare_to function, which is mapped to the tp_compare slot, which
       // compare_to function, which is mapped to the tp_compare slot, which
       // Python 3 no longer has.  So, we'll write code to fall back to that if
       // Python 3 no longer has.  So, we'll write code to fall back to that if
@@ -3510,7 +3555,8 @@ write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker
       fname == "operator ==" ||
       fname == "operator ==" ||
       fname == "operator !=" ||
       fname == "operator !=" ||
       fname == "operator >" ||
       fname == "operator >" ||
-      fname == "operator >=") {
+      fname == "operator >=" ||
+      fname == "operator <=>") {
     return;
     return;
   }
   }
 
 
@@ -4280,18 +4326,19 @@ int get_type_sort(CPPType *type) {
              TypeManager::is_struct(type)) {
              TypeManager::is_struct(type)) {
     answer = 20;
     answer = 20;
     int deepest = 0;
     int deepest = 0;
-    TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)), false);
-    InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
-    const InterrogateType &itype = idb->get_type(type_index);
 
 
-    if (itype.is_class() || itype.is_struct()) {
-      int num_derivations = itype.number_of_derivations();
-      for (int di = 0; di < num_derivations; di++) {
-        TypeIndex d_type_Index = itype.get_derivation(di);
-        const InterrogateType &d_itype = idb->get_type(d_type_Index);
-        int this_one = get_type_sort(d_itype._cpptype);
-        if (this_one > deepest) {
-          deepest = this_one;
+    // Sort such that more derived classes come first.
+    type = TypeManager::unwrap(TypeManager::resolve_type(type));
+    if (type != nullptr) {
+      CPPStructType *struct_type = type->as_struct_type();
+      if (struct_type != nullptr) {
+        for (const CPPStructType::Base &base : struct_type->_derivation) {
+          if (base._base != nullptr) {
+            int this_one = get_type_sort(base._base);
+            if (this_one > deepest) {
+              deepest = this_one;
+            }
+          }
         }
         }
       }
       }
     }
     }
@@ -4615,7 +4662,8 @@ write_function_instance(ostream &out, FunctionRemap *remap,
                         bool check_exceptions,
                         bool check_exceptions,
                         const string &first_pexpr) {
                         const string &first_pexpr) {
   string format_specifiers;
   string format_specifiers;
-  string keyword_list;
+  string keyword_list_old;
+  string keyword_list_new;
   string parameter_list;
   string parameter_list;
   string container;
   string container;
   string type_check;
   string type_check;
@@ -4773,14 +4821,20 @@ write_function_instance(ostream &out, FunctionRemap *remap,
     }
     }
 
 
     string reported_name = remap->_parameters[pn]._name;
     string reported_name = remap->_parameters[pn]._name;
-    if (!keyword_list.empty()) {
-      keyword_list += ", \"" + reported_name + "\"";
-    } else {
-      keyword_list = "\"" + reported_name + "\"";
+    if (!keyword_list_old.empty()) {
+      keyword_list_old += ", ";
+      keyword_list_new += ", ";
     }
     }
     if (remap->_parameters[pn]._has_name) {
     if (remap->_parameters[pn]._has_name) {
       has_keywords = true;
       has_keywords = true;
     }
     }
+    keyword_list_old += "\"" + reported_name + "\"";
+    if (has_keywords) {
+      keyword_list_new += "\"" + reported_name + "\"";
+    } else {
+      // Positional-only argument.
+      keyword_list_new += "\"\"";
+    }
 
 
     if (param->new_type_is_atomic_string()) {
     if (param->new_type_is_atomic_string()) {
 
 
@@ -5835,16 +5889,26 @@ write_function_instance(ostream &out, FunctionRemap *remap,
           // case we have implemented ourselves.
           // case we have implemented ourselves.
           if (min_num_args == 1) {
           if (min_num_args == 1) {
             indent(out, indent_level)
             indent(out, indent_level)
-              << "if (Dtool_ExtractArg(&" << param_name << ", args, kwds, " << keyword_list << ")) {\n";
+              << "if (Dtool_ExtractArg(&" << param_name << ", args, kwds, " << keyword_list_new << ")) {\n";
           } else {
           } else {
             indent(out, indent_level)
             indent(out, indent_level)
-              << "if (Dtool_ExtractOptionalArg(&" << param_name << ", args, kwds, " << keyword_list << ")) {\n";
+              << "if (Dtool_ExtractOptionalArg(&" << param_name << ", args, kwds, " << keyword_list_new << ")) {\n";
           }
           }
         } else {
         } else {
           // We have to use the more expensive PyArg_ParseTupleAndKeywords.
           // We have to use the more expensive PyArg_ParseTupleAndKeywords.
           clear_error = true;
           clear_error = true;
-          indent(out, indent_level)
-            << "static const char *keyword_list[] = {" << keyword_list << ", nullptr};\n";
+          if (keyword_list_new != keyword_list_old) {
+            out << "#if PY_VERSION_HEX >= 0x03060000\n";
+            indent(out, indent_level)
+              << "static const char *keyword_list[] = {" << keyword_list_new << ", nullptr};\n";
+            out << "#else\n";
+            indent(out, indent_level)
+              << "static const char *keyword_list[] = {" << keyword_list_old << ", nullptr};\n";
+            out << "#endif\n";
+          } else {
+            indent(out, indent_level)
+              << "static const char *keyword_list[] = {" << keyword_list_new << ", nullptr};\n";
+          }
           indent(out, indent_level)
           indent(out, indent_level)
             << "if (PyArg_ParseTupleAndKeywords(args, kwds, \""
             << "if (PyArg_ParseTupleAndKeywords(args, kwds, \""
             << format_specifiers << ":" << method_name
             << format_specifiers << ":" << method_name
@@ -6126,7 +6190,8 @@ write_function_instance(ostream &out, FunctionRemap *remap,
     // function call, so it should reduce the amount of code output while not
     // function call, so it should reduce the amount of code output while not
     // being any slower.
     // being any slower.
     bool return_null = (return_flags & RF_pyobject) != 0 &&
     bool return_null = (return_flags & RF_pyobject) != 0 &&
-                       (return_flags & RF_err_null) != 0;
+                       (return_flags & RF_err_null) != 0 &&
+                       (return_flags & RF_richcompare_zero) == 0;
     if (return_null && return_expr.empty()) {
     if (return_null && return_expr.empty()) {
       indent(out, indent_level)
       indent(out, indent_level)
         << "return Dtool_Return_None();\n";
         << "return Dtool_Return_None();\n";
@@ -6279,6 +6344,10 @@ write_function_instance(ostream &out, FunctionRemap *remap,
     indent(out, indent_level) << "Py_INCREF(self);\n";
     indent(out, indent_level) << "Py_INCREF(self);\n";
     indent(out, indent_level) << "return self;\n";
     indent(out, indent_level) << "return self;\n";
 
 
+  } else if (return_flags & RF_richcompare_zero) {
+    indent(out, indent_level)
+      << "Py_RETURN_RICHCOMPARE(" << return_expr << ", 0, op);\n";
+
   } else if (return_flags & RF_pyobject) {
   } else if (return_flags & RF_pyobject) {
     if (return_expr.empty()) {
     if (return_expr.empty()) {
       indent(out, indent_level) << "Py_INCREF(Py_None);\n";
       indent(out, indent_level) << "Py_INCREF(Py_None);\n";
@@ -7623,7 +7692,11 @@ is_remap_legal(FunctionRemap *remap) {
   if (!is_cpp_type_legal(remap->_return_type->get_orig_type())) {
   if (!is_cpp_type_legal(remap->_return_type->get_orig_type())) {
 // printf("  is_remap_legal Return Is Bad %s\n",remap->_return_type->get_orig_
 // printf("  is_remap_legal Return Is Bad %s\n",remap->_return_type->get_orig_
 // type()->get_fully_scoped_name().c_str());
 // type()->get_fully_scoped_name().c_str());
-    return false;
+    // Except if this is a spaceship operator, since we have special handling
+    // for its return type.
+    if (remap->_cppfunc->get_simple_name() != "operator <=>") {
+      return false;
+    }
   }
   }
 
 
   // We don't currently support returning pointers, but we accept them as
   // We don't currently support returning pointers, but we accept them as
@@ -8082,6 +8155,17 @@ NeedsARichCompareFunction(const InterrogateType &itype_class) {
     }
     }
   }
   }
 
 
+  if (itype_class._cpptype != nullptr) {
+    CPPStructType *struct_type = itype_class._cpptype->as_struct_type();
+    if (struct_type != nullptr) {
+      CPPScope *scope = struct_type->get_scope();
+      CPPScope::Functions::const_iterator it = scope->_functions.find("operator <=>");
+      if (it != scope->_functions.end()) {
+        return true;
+      }
+    }
+  }
+
   return false;
   return false;
 }
 }
 
 

+ 3 - 0
dtool/src/interrogate/interfaceMakerPythonNative.h

@@ -118,6 +118,9 @@ private:
 
 
     // Invert boolean return value.
     // Invert boolean return value.
     RF_invert_bool = 0x8000,
     RF_invert_bool = 0x8000,
+
+    // Used inside a rich comparison function.
+    RF_richcompare_zero = 0x10000,
   };
   };
 
 
   class SlottedFunctionDef {
   class SlottedFunctionDef {

+ 5 - 0
dtool/src/interrogate/interrogateBuilder.cxx

@@ -2430,6 +2430,11 @@ define_atomic_type(InterrogateType &itype, CPPSimpleType *cpptype) {
     itype._atomic_token = AT_int;
     itype._atomic_token = AT_int;
     break;
     break;
 
 
+  case CPPSimpleType::T_char8_t:
+    itype._flags |= InterrogateType::F_unsigned;
+    itype._atomic_token = AT_int;
+    break;
+
   case CPPSimpleType::T_char16_t:
   case CPPSimpleType::T_char16_t:
     itype._flags |= InterrogateType::F_unsigned;
     itype._flags |= InterrogateType::F_unsigned;
     itype._atomic_token = AT_int;
     itype._atomic_token = AT_int;

+ 3 - 1
dtool/src/interrogate/typeManager.cxx

@@ -1111,6 +1111,7 @@ is_integer(CPPType *type) {
           (simple_type->_type == CPPSimpleType::T_bool ||
           (simple_type->_type == CPPSimpleType::T_bool ||
            simple_type->_type == CPPSimpleType::T_char ||
            simple_type->_type == CPPSimpleType::T_char ||
            simple_type->_type == CPPSimpleType::T_wchar_t ||
            simple_type->_type == CPPSimpleType::T_wchar_t ||
+           simple_type->_type == CPPSimpleType::T_char8_t ||
            simple_type->_type == CPPSimpleType::T_char16_t ||
            simple_type->_type == CPPSimpleType::T_char16_t ||
            simple_type->_type == CPPSimpleType::T_char32_t ||
            simple_type->_type == CPPSimpleType::T_char32_t ||
            simple_type->_type == CPPSimpleType::T_int);
            simple_type->_type == CPPSimpleType::T_int);
@@ -1148,7 +1149,8 @@ is_unsigned_integer(CPPType *type) {
             simple_type->_type == CPPSimpleType::T_wchar_t ||
             simple_type->_type == CPPSimpleType::T_wchar_t ||
             simple_type->_type == CPPSimpleType::T_int) &&
             simple_type->_type == CPPSimpleType::T_int) &&
            (simple_type->_flags & CPPSimpleType::F_unsigned) != 0) ||
            (simple_type->_flags & CPPSimpleType::F_unsigned) != 0) ||
-           (simple_type->_type == CPPSimpleType::T_char16_t ||
+           (simple_type->_type == CPPSimpleType::T_char8_t ||
+            simple_type->_type == CPPSimpleType::T_char16_t ||
             simple_type->_type == CPPSimpleType::T_char32_t);
             simple_type->_type == CPPSimpleType::T_char32_t);
       }
       }
     }
     }

+ 5 - 0
dtool/src/interrogatedb/py_compat.h

@@ -137,6 +137,11 @@ typedef long Py_hash_t;
 #  endif
 #  endif
 #endif
 #endif
 
 
+/* Python 3.4 */
+#if PY_VERSION_HEX < 0x03040000
+#define PyGILState_Check() (PyGILState_GetThisThreadState() == _PyThreadState_Current)
+#endif
+
 /* Python 3.6 */
 /* Python 3.6 */
 
 
 #if PY_VERSION_HEX < 0x03080000 && !defined(_PyObject_CallNoArg)
 #if PY_VERSION_HEX < 0x03080000 && !defined(_PyObject_CallNoArg)

+ 10 - 2
dtool/src/interrogatedb/py_panda.cxx

@@ -785,7 +785,11 @@ bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds,
       *result = PyTuple_GET_ITEM(args, 0);
       *result = PyTuple_GET_ITEM(args, 0);
       return true;
       return true;
     }
     }
-  } else if (PyTuple_GET_SIZE(args) == 0) {
+  }
+  else if (!keyword || !keyword[0]) {
+    return false;
+  }
+  else if (PyTuple_GET_SIZE(args) == 0) {
     PyObject *key;
     PyObject *key;
     Py_ssize_t ppos = 0;
     Py_ssize_t ppos = 0;
     if (kwds != nullptr && PyDict_GET_SIZE(kwds) == 1 &&
     if (kwds != nullptr && PyDict_GET_SIZE(kwds) == 1 &&
@@ -831,7 +835,11 @@ bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds,
       *result = PyTuple_GET_ITEM(args, 0);
       *result = PyTuple_GET_ITEM(args, 0);
       return true;
       return true;
     }
     }
-  } else if (PyTuple_GET_SIZE(args) == 0) {
+  }
+  else if (!keyword || !keyword[0]) {
+    return (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0);
+  }
+  else if (PyTuple_GET_SIZE(args) == 0) {
     if (kwds != nullptr && PyDict_GET_SIZE(kwds) == 1) {
     if (kwds != nullptr && PyDict_GET_SIZE(kwds) == 1) {
       PyObject *key;
       PyObject *key;
       Py_ssize_t ppos = 0;
       Py_ssize_t ppos = 0;

+ 9 - 0
dtool/src/parser-inc/bit

@@ -0,0 +1,9 @@
+#pragma once
+
+namespace std {
+  enum class endian {
+    little,
+    big,
+    native,
+  };
+};

+ 23 - 0
dtool/src/parser-inc/compare

@@ -0,0 +1,23 @@
+#pragma once
+
+namespace std {
+  class partial_ordering {
+    static inline constexpr partial_ordering less;
+    static inline constexpr partial_ordering equivalent;
+    static inline constexpr partial_ordering greater;
+    static inline constexpr partial_ordering unordered;
+  };
+
+  class strong_ordering {
+    static inline constexpr strong_ordering less;
+    static inline constexpr strong_ordering equivalent;
+    static inline constexpr strong_ordering equal;
+    static inline constexpr strong_ordering greater;
+  };
+
+  class weak_ordering {
+    static inline constexpr weak_ordering less;
+    static inline constexpr weak_ordering equivalent;
+    static inline constexpr weak_ordering greater;
+  };
+};

+ 11 - 0
dtool/src/parser-inc/condition_variable

@@ -0,0 +1,11 @@
+#pragma once
+
+namespace std {
+  class condition_variable;
+  class condition_variable_any;
+
+  enum class cv_status {
+    no_timeout,
+    timeout,
+  };
+}

+ 31 - 0
dtool/src/parser-inc/coroutine

@@ -0,0 +1,31 @@
+#pragma once
+
+namespace std {
+  template<class R, class... ArgTypes>
+  struct coroutine_traits;
+
+  template<class Promise = void>
+  struct coroutine_handle;
+
+  template<class T> struct hash;
+  template<class P> struct hash<coroutine_handle<P>>;
+
+  struct noop_coroutine_promise;
+
+  template<> struct coroutine_handle<noop_coroutine_promise>;
+  using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
+
+  noop_coroutine_handle noop_coroutine() noexcept;
+
+  struct suspend_never {
+    constexpr bool await_ready() const noexcept { return true; }
+    constexpr void await_suspend(coroutine_handle<>) const noexcept {}
+    constexpr void await_resume() const noexcept {}
+  };
+
+  struct suspend_always {
+    constexpr bool await_ready() const noexcept { return false; }
+    constexpr void await_suspend(coroutine_handle<>) const noexcept {}
+    constexpr void await_resume() const noexcept {}
+  };
+}

+ 9 - 0
dtool/src/parser-inc/forward_list

@@ -0,0 +1,9 @@
+#pragma once
+
+#include <compare>
+#include <initializer_list>
+
+namespace std {
+  template<class T> struct allocator;
+  template<class T, class Allocator = allocator<T>> class forward_list;
+}

+ 27 - 0
dtool/src/parser-inc/future

@@ -0,0 +1,27 @@
+#pragma once
+
+namespace std {
+  enum class future_status {
+    ready,
+    timeout,
+    deferred
+  };
+
+  class future_error;
+
+  template<class R> class promise;
+  template<class R> class promise<R&>;
+  template<> class promise<void>;
+
+  template<class R> class future;
+  template<class R> class future<R&>;
+  template<> class future<void>;
+
+  template<class R> class shared_future;
+  template<class R> class shared_future<R&>;
+  template<> class shared_future<void>;
+
+  template<class> class packaged_task;
+  template<class R, class... ArgTypes>
+    class packaged_task<R(ArgTypes...)>;
+}

+ 1 - 0
dtool/src/parser-inc/mutex

@@ -22,6 +22,7 @@ namespace std {
   inline constexpr adopt_lock_t adopt_lock {};
   inline constexpr adopt_lock_t adopt_lock {};
 
 
   template<class Mutex> class lock_guard;
   template<class Mutex> class lock_guard;
+  template<class... MutexTypes> class scoped_lock;
   template<class Mutex> class unique_lock;
   template<class Mutex> class unique_lock;
 
 
   struct once_flag;
   struct once_flag;

+ 10 - 0
dtool/src/parser-inc/new

@@ -1,5 +1,7 @@
 #pragma once
 #pragma once
 
 
+#include <stdtypedefs.h>
+
 namespace std {
 namespace std {
   class bad_alloc;
   class bad_alloc;
   class bad_array_new_length;
   class bad_array_new_length;
@@ -10,4 +12,12 @@ namespace std {
   extern const nothrow_t nothrow;
   extern const nothrow_t nothrow;
 
 
   using new_handler = void (*)();
   using new_handler = void (*)();
+
+  inline constexpr size_t hardware_destructive_interference_size;
+  inline constexpr size_t hardware_constructive_interference_size;
+
+  struct destroying_delete_t {
+    explicit destroying_delete_t() = default;
+  };
+  inline constexpr destroying_delete_t destroying_delete {};
 }
 }

+ 7 - 0
dtool/src/parser-inc/shared_mutex

@@ -0,0 +1,7 @@
+#pragma once
+
+namespace std {
+  class shared_mutex;
+  class shared_timed_mutex;
+  template<class Mutex> class shared_lock;
+}

+ 21 - 0
dtool/src/parser-inc/thread

@@ -0,0 +1,21 @@
+#pragma once
+
+#include <compare>
+
+namespace std {
+  class thread {
+  public:
+    class id;
+  };
+
+  class jthread {
+  public:
+    using id = thread::id;
+  };
+
+  template<class T> struct hash;
+  template<> struct hash<thread::id>;
+
+  namespace this_thread {
+  }
+}

+ 19 - 0
dtool/src/parser-inc/type_traits

@@ -14,6 +14,9 @@ namespace std {
   typedef integral_constant<bool, true> true_type;
   typedef integral_constant<bool, true> true_type;
   typedef integral_constant<bool, false> false_type;
   typedef integral_constant<bool, false> false_type;
 
 
+  template<bool B>
+  using bool_constant = integral_constant<bool, B>;
+
   template<class T, class U>
   template<class T, class U>
   struct is_same : std::false_type {};
   struct is_same : std::false_type {};
 
 
@@ -86,6 +89,9 @@ namespace std {
   template<class T, class U>
   template<class T, class U>
   struct is_convertible : integral_constant<bool, __is_convertible_to(T, U)> {};
   struct is_convertible : integral_constant<bool, __is_convertible_to(T, U)> {};
 
 
+  template<class T>
+  struct is_aggregate;
+
   template<class T> struct add_cv { typedef const volatile T type; };
   template<class T> struct add_cv { typedef const volatile T type; };
   template<class T> struct add_const { typedef const T type; };
   template<class T> struct add_const { typedef const T type; };
   template<class T> struct add_volatile { typedef volatile T type; };
   template<class T> struct add_volatile { typedef volatile T type; };
@@ -93,4 +99,17 @@ namespace std {
   template<class T> struct add_lvalue_reference { typedef T &type; };
   template<class T> struct add_lvalue_reference { typedef T &type; };
   template<class T> struct add_rvalue_reference { typedef T &&type; };
   template<class T> struct add_rvalue_reference { typedef T &&type; };
   template<class T> struct add_pointer { typedef T *type; };
   template<class T> struct add_pointer { typedef T *type; };
+
+  constexpr bool is_constant_evaluated() noexcept;
+
+  template<class T>
+  struct type_identity {
+    using type = T;
+  };
+
+  template<class T>
+  using type_identity_t = typename type_identity<T>::type;
+
+  template<class...>
+  using void_t = void;
 }
 }

+ 4 - 0
dtool/src/prc/encryptStreamBuf.cxx

@@ -95,6 +95,10 @@ EncryptStreamBuf::
 ~EncryptStreamBuf() {
 ~EncryptStreamBuf() {
   close_read();
   close_read();
   close_write();
   close_write();
+
+#ifdef PHAVE_IOSTREAM
+  delete[] eback();
+#endif
 }
 }
 
 
 /**
 /**

+ 2 - 0
makepanda/installer.nsi

@@ -420,6 +420,7 @@ Section "Python ${INCLUDE_PYVER}" SecPython
     IfFileExists "$0\python.exe" AskRegPath RegPath
     IfFileExists "$0\python.exe" AskRegPath RegPath
 
 
     AskRegPath:
     AskRegPath:
+    IfSilent SkipRegPath
     MessageBox MB_YESNO|MB_ICONQUESTION \
     MessageBox MB_YESNO|MB_ICONQUESTION \
         "You already have a copy of Python ${INCLUDE_PYVER} installed in:$\r$\n$0$\r$\n$\r$\nPanda3D installs its own copy of Python ${INCLUDE_PYVER}, which will install alongside your existing copy.  Would you like to make Panda's copy the default Python for your user account?" \
         "You already have a copy of Python ${INCLUDE_PYVER} installed in:$\r$\n$0$\r$\n$\r$\nPanda3D installs its own copy of Python ${INCLUDE_PYVER}, which will install alongside your existing copy.  Would you like to make Panda's copy the default Python for your user account?" \
         IDNO SkipRegPath
         IDNO SkipRegPath
@@ -568,6 +569,7 @@ Function ConfirmPythonSelection
     ; No compatible Python version found (that wasn't shipped as part
     ; No compatible Python version found (that wasn't shipped as part
     ; of a different Panda3D build.)  Ask the user if he's sure about this.
     ; of a different Panda3D build.)  Ask the user if he's sure about this.
     AskConfirmation:
     AskConfirmation:
+    IfSilent SkipCheck
     MessageBox MB_YESNO|MB_ICONQUESTION \
     MessageBox MB_YESNO|MB_ICONQUESTION \
         "You do not appear to have a ${REGVIEW}-bit version of Python ${INCLUDE_PYVER} installed.  Are you sure you don't want Panda to install a compatible copy of Python?$\r$\n$\r$\nIf you choose Yes, you will not be able to do Python development with Panda3D until you install a ${REGVIEW}-bit version of Python and install the bindings for this version." \
         "You do not appear to have a ${REGVIEW}-bit version of Python ${INCLUDE_PYVER} installed.  Are you sure you don't want Panda to install a compatible copy of Python?$\r$\n$\r$\nIf you choose Yes, you will not be able to do Python development with Panda3D until you install a ${REGVIEW}-bit version of Python and install the bindings for this version." \
         IDYES SkipCheck
         IDYES SkipCheck

+ 9 - 8
makepanda/makepanda.py

@@ -2315,7 +2315,6 @@ DTOOL_CONFIG=[
     ("REPORT_OPENSSL_ERRORS",          '1',                      '1'),
     ("REPORT_OPENSSL_ERRORS",          '1',                      '1'),
     ("USE_PANDAFILESTREAM",            '1',                      '1'),
     ("USE_PANDAFILESTREAM",            '1',                      '1'),
     ("USE_DELETED_CHAIN",              '1',                      '1'),
     ("USE_DELETED_CHAIN",              '1',                      '1'),
-    ("HAVE_GLX",                       'UNDEF',                  '1'),
     ("HAVE_WGL",                       '1',                      'UNDEF'),
     ("HAVE_WGL",                       '1',                      'UNDEF'),
     ("HAVE_DX9",                       'UNDEF',                  'UNDEF'),
     ("HAVE_DX9",                       'UNDEF',                  'UNDEF'),
     ("HAVE_THREADS",                   '1',                      '1'),
     ("HAVE_THREADS",                   '1',                      '1'),
@@ -2478,15 +2477,11 @@ def WriteConfigSettings():
         dtool_config["PHAVE_SYS_MALLOC_H"] = '1'
         dtool_config["PHAVE_SYS_MALLOC_H"] = '1'
         dtool_config["HAVE_OPENAL_FRAMEWORK"] = '1'
         dtool_config["HAVE_OPENAL_FRAMEWORK"] = '1'
         dtool_config["HAVE_X11"] = 'UNDEF'  # We might have X11, but we don't need it.
         dtool_config["HAVE_X11"] = 'UNDEF'  # We might have X11, but we don't need it.
-        dtool_config["HAVE_GLX"] = 'UNDEF'
         dtool_config["IS_LINUX"] = 'UNDEF'
         dtool_config["IS_LINUX"] = 'UNDEF'
         dtool_config["HAVE_VIDEO4LINUX"] = 'UNDEF'
         dtool_config["HAVE_VIDEO4LINUX"] = 'UNDEF'
         dtool_config["PHAVE_LINUX_INPUT_H"] = 'UNDEF'
         dtool_config["PHAVE_LINUX_INPUT_H"] = 'UNDEF'
         dtool_config["IS_OSX"] = '1'
         dtool_config["IS_OSX"] = '1'
 
 
-    if PkgSkip("X11"):
-        dtool_config["HAVE_GLX"] = 'UNDEF'
-
     if (GetTarget() == "freebsd"):
     if (GetTarget() == "freebsd"):
         dtool_config["IS_LINUX"] = 'UNDEF'
         dtool_config["IS_LINUX"] = 'UNDEF'
         dtool_config["HAVE_VIDEO4LINUX"] = 'UNDEF'
         dtool_config["HAVE_VIDEO4LINUX"] = 'UNDEF'
@@ -2688,7 +2683,12 @@ def CreatePandaVersionFiles():
     if source_date:
     if source_date:
         # This matches the GCC / Clang format for __DATE__ __TIME__
         # This matches the GCC / Clang format for __DATE__ __TIME__
         source_date = time.gmtime(int(source_date))
         source_date = time.gmtime(int(source_date))
-        source_date = time.strftime('%b %e %Y %H:%M:%S', source_date)
+        try:
+            source_date = time.strftime('%b %e %Y %H:%M:%S', source_date)
+        except ValueError:
+            source_date = time.strftime('%b %d %Y %H:%M:%S', source_date)
+            if source_date[3:5] == ' 0':
+                source_date = source_date[:3] + '  ' + source_date[5:]
         pandaversion_h += "\n#define PANDA_BUILD_DATE_STR \"%s\"\n" % (source_date)
         pandaversion_h += "\n#define PANDA_BUILD_DATE_STR \"%s\"\n" % (source_date)
 
 
     checkpandaversion_cxx = CHECKPANDAVERSION_CXX.replace("$VERSION1",str(version1))
     checkpandaversion_cxx = CHECKPANDAVERSION_CXX.replace("$VERSION1",str(version1))
@@ -4622,9 +4622,10 @@ if GetTarget() not in ['windows', 'darwin', 'emscripten'] and not PkgSkip("X11")
 #
 #
 
 
 if GetTarget() not in ['windows', 'darwin', 'emscripten'] and not PkgSkip("GL") and not PkgSkip("X11"):
 if GetTarget() not in ['windows', 'darwin', 'emscripten'] and not PkgSkip("GL") and not PkgSkip("X11"):
-    OPTS=['DIR:panda/src/glxdisplay', 'BUILDING:PANDAGL', 'GL', 'NVIDIACG', 'CGGL']
+    DefSymbol('GLX', 'HAVE_GLX', '')
+    OPTS=['DIR:panda/src/glxdisplay', 'BUILDING:PANDAGL', 'GL', 'NVIDIACG', 'CGGL', 'GLX']
     TargetAdd('p3glxdisplay_composite1.obj', opts=OPTS, input='p3glxdisplay_composite1.cxx')
     TargetAdd('p3glxdisplay_composite1.obj', opts=OPTS, input='p3glxdisplay_composite1.cxx')
-    OPTS=['DIR:panda/metalibs/pandagl', 'BUILDING:PANDAGL', 'GL', 'NVIDIACG', 'CGGL']
+    OPTS=['DIR:panda/metalibs/pandagl', 'BUILDING:PANDAGL', 'GL', 'NVIDIACG', 'CGGL', 'GLX']
     TargetAdd('pandagl_pandagl.obj', opts=OPTS, input='pandagl.cxx')
     TargetAdd('pandagl_pandagl.obj', opts=OPTS, input='pandagl.cxx')
     TargetAdd('libpandagl.dll', input='p3x11display_composite1.obj')
     TargetAdd('libpandagl.dll', input='p3x11display_composite1.obj')
     TargetAdd('libpandagl.dll', input='pandagl_pandagl.obj')
     TargetAdd('libpandagl.dll', input='pandagl_pandagl.obj')

+ 4 - 0
makepanda/makepandacore.py

@@ -1361,6 +1361,8 @@ def GetThirdpartyDir():
             THIRDPARTYDIR = base + "/linux-libs-arm/"
             THIRDPARTYDIR = base + "/linux-libs-arm/"
         elif (target_arch in ("x86_64", "amd64")):
         elif (target_arch in ("x86_64", "amd64")):
             THIRDPARTYDIR = base + "/linux-libs-x64/"
             THIRDPARTYDIR = base + "/linux-libs-x64/"
+        elif target_arch == "aarch64":
+            THIRDPARTYDIR = base + "/linux-libs-aarch64/"
         else:
         else:
             THIRDPARTYDIR = base + "/linux-libs-a/"
             THIRDPARTYDIR = base + "/linux-libs-a/"
 
 
@@ -1369,6 +1371,8 @@ def GetThirdpartyDir():
             THIRDPARTYDIR = base + "/freebsd-libs-arm/"
             THIRDPARTYDIR = base + "/freebsd-libs-arm/"
         elif (target_arch in ("x86_64", "amd64")):
         elif (target_arch in ("x86_64", "amd64")):
             THIRDPARTYDIR = base + "/freebsd-libs-x64/"
             THIRDPARTYDIR = base + "/freebsd-libs-x64/"
+        elif target_arch == "aarch64":
+            THIRDPARTYDIR = base + "/freebsd-libs-aarch64/"
         else:
         else:
             THIRDPARTYDIR = base + "/freebsd-libs-a/"
             THIRDPARTYDIR = base + "/freebsd-libs-a/"
 
 

+ 2 - 3
panda/src/bullet/bullet_utils.cxx

@@ -118,10 +118,9 @@ btTransform LMatrix4_to_btTrans(const LMatrix4 &m) {
  */
  */
 LMatrix4 btTrans_to_LMatrix4(const btTransform &trans) {
 LMatrix4 btTrans_to_LMatrix4(const btTransform &trans) {
 
 
-  return TransformState::make_pos_quat_scale(
+  return TransformState::make_pos_quat(
     btVector3_to_LVector3(trans.getOrigin()),
     btVector3_to_LVector3(trans.getOrigin()),
-    btQuat_to_LQuaternion(trans.getRotation()),
-    LVector3(1.0f, 1.0f, 1.0f))->get_mat();
+    btQuat_to_LQuaternion(trans.getRotation()))->get_mat();
 }
 }
 
 
 /**
 /**

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

@@ -192,7 +192,7 @@ GraphicsEngine::
 
 
 /**
 /**
  * Specifies how future objects created via make_gsg(), make_buffer(), and
  * Specifies how future objects created via make_gsg(), make_buffer(), and
- * make_window() will be threaded.  This does not affect any already-created
+ * make_output() will be threaded.  This does not affect any already-created
  * objects.
  * objects.
  */
  */
 void GraphicsEngine::
 void GraphicsEngine::
@@ -2154,7 +2154,7 @@ do_draw(GraphicsOutput *win, GraphicsStateGuardian *gsg, DisplayRegion *dr, Thre
 }
 }
 
 
 /**
 /**
- * An internal function called by make_window() and make_buffer() and similar
+ * An internal function called by make_output() and make_buffer() and similar
  * functions to add the newly-created GraphicsOutput object to the engine's
  * functions to add the newly-created GraphicsOutput object to the engine's
  * list of windows, and to request that the window be opened.
  * list of windows, and to request that the window be opened.
  */
  */

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

@@ -63,7 +63,7 @@ static CubeFaceDef cube_faces[6] = {
 
 
 /**
 /**
  * Normally, the GraphicsOutput constructor is not called directly; these are
  * Normally, the GraphicsOutput constructor is not called directly; these are
- * created instead via the GraphicsEngine::make_window() function.
+ * created instead via the GraphicsEngine::make_output() function.
  */
  */
 GraphicsOutput::
 GraphicsOutput::
 GraphicsOutput(GraphicsEngine *engine, GraphicsPipe *pipe,
 GraphicsOutput(GraphicsEngine *engine, GraphicsPipe *pipe,

+ 2 - 2
panda/src/display/graphicsWindow.I

@@ -14,7 +14,7 @@
 /**
 /**
  * Returns true if the window has not yet been opened, or has been fully
  * Returns true if the window has not yet been opened, or has been fully
  * closed, false if it is open.  The window is not opened immediately after
  * closed, false if it is open.  The window is not opened immediately after
- * GraphicsEngine::make_window() is called; nor is it closed immediately after
+ * GraphicsEngine::make_output() is called; nor is it closed immediately after
  * GraphicsEngine::remove_window() is called.  Either operation may take a
  * GraphicsEngine::remove_window() is called.  Either operation may take a
  * frame or two.
  * frame or two.
  */
  */
@@ -34,7 +34,7 @@ is_fullscreen() const {
 
 
 /**
 /**
  * If this flag is false, the window is redrawn only after it has received a
  * If this flag is false, the window is redrawn only after it has received a
- * recent "unexpose" or "draw" event from the underlying windowing systme.  If
+ * recent "unexpose" or "draw" event from the underlying windowing system.  If
  * this flag is true, the window is redrawn every frame regardless.  Setting
  * this flag is true, the window is redrawn every frame regardless.  Setting
  * this false may prevent the window from redrawing unnecessarily when it is
  * this false may prevent the window from redrawing unnecessarily when it is
  * hidden, and may play nicer with other windows on the desktop, but may
  * hidden, and may play nicer with other windows on the desktop, but may

+ 4 - 4
panda/src/display/graphicsWindow.cxx

@@ -27,7 +27,7 @@ TypeHandle GraphicsWindow::_type_handle;
 
 
 /**
 /**
  * Normally, the GraphicsWindow constructor is not called directly; these are
  * Normally, the GraphicsWindow constructor is not called directly; these are
- * created instead via the GraphicsEngine::make_window() function.
+ * created instead via the GraphicsEngine::make_output() function.
  */
  */
 GraphicsWindow::
 GraphicsWindow::
 GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
 GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
@@ -354,9 +354,9 @@ disable_pointer_mode(int device) {
 }*/
 }*/
 
 
 /**
 /**
- * Returns the MouseData associated with the nth input device's pointer.  This
- * is deprecated; use get_pointer_device().get_pointer() instead, or for raw
- * mice, use the InputDeviceManager interface.
+ * Returns the MouseData associated with the nth input device's pointer.
+ * Using this to access raw mice (with an index other than 0) is deprecated,
+ * see the InputDeviceManager interface instead.
  */
  */
 MouseData GraphicsWindow::
 MouseData GraphicsWindow::
 get_pointer(int device) const {
 get_pointer(int device) const {

+ 1 - 1
panda/src/display/subprocessWindow.cxx

@@ -27,7 +27,7 @@ TypeHandle SubprocessWindow::_type_handle;
 
 
 /**
 /**
  * Normally, the SubprocessWindow constructor is not called directly; these
  * Normally, the SubprocessWindow constructor is not called directly; these
- * are created instead via the GraphicsEngine::make_window() function.
+ * are created instead via the GraphicsEngine::make_output() function.
  */
  */
 SubprocessWindow::
 SubprocessWindow::
 SubprocessWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
 SubprocessWindow(GraphicsEngine *engine, GraphicsPipe *pipe,

+ 3 - 0
panda/src/downloader/virtualFileHTTP.cxx

@@ -13,9 +13,12 @@
 
 
 #include "virtualFileHTTP.h"
 #include "virtualFileHTTP.h"
 #include "virtualFileMountHTTP.h"
 #include "virtualFileMountHTTP.h"
+#include "ramfile.h"
 #include "stringStream.h"
 #include "stringStream.h"
 #include "zStream.h"
 #include "zStream.h"
 
 
+#include <iterator>
+
 #ifdef HAVE_OPENSSL
 #ifdef HAVE_OPENSSL
 
 
 using std::istream;
 using std::istream;

+ 79 - 7
panda/src/egldisplay/eglGraphicsPipe.cxx

@@ -19,6 +19,8 @@
 #include "config_egldisplay.h"
 #include "config_egldisplay.h"
 #include "frameBufferProperties.h"
 #include "frameBufferProperties.h"
 
 
+#include <EGL/eglext.h>
+
 TypeHandle eglGraphicsPipe::_type_handle;
 TypeHandle eglGraphicsPipe::_type_handle;
 
 
 /**
 /**
@@ -26,6 +28,29 @@ TypeHandle eglGraphicsPipe::_type_handle;
  */
  */
 eglGraphicsPipe::
 eglGraphicsPipe::
 eglGraphicsPipe() {
 eglGraphicsPipe() {
+  // Check for client extensions.
+  vector_string extensions;
+  const char *ext_ptr = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+  if (ext_ptr != nullptr) {
+    extract_words(ext_ptr, extensions);
+
+    if (egldisplay_cat.is_debug()) {
+      std::ostream &out = egldisplay_cat.debug()
+        << "Supported EGL client extensions:\n";
+
+      for (const std::string &extension : extensions) {
+        out << "  " << extension << "\n";
+      }
+    }
+  }
+  else if (egldisplay_cat.is_debug()) {
+    eglGetError();
+    egldisplay_cat.debug()
+      << "EGL client extensions not supported.\n";
+  }
+
+  EGLint major, minor;
+
   //NB. if the X11 display failed to open, _display will be 0, which is a valid
   //NB. if the X11 display failed to open, _display will be 0, which is a valid
   // input to eglGetDisplay - it means to open the default display.
   // input to eglGetDisplay - it means to open the default display.
 #ifdef HAVE_X11
 #ifdef HAVE_X11
@@ -33,14 +58,62 @@ eglGraphicsPipe() {
 #else
 #else
   _egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
   _egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
 #endif
 #endif
-  if (!eglInitialize(_egl_display, nullptr, nullptr)) {
-    egldisplay_cat.error()
-      << "Couldn't initialize the EGL display: "
+  if (_egl_display && !eglInitialize(_egl_display, &major, &minor)) {
+    egldisplay_cat.warning()
+      << "Couldn't initialize the default EGL display: "
       << get_egl_error_string(eglGetError()) << "\n";
       << get_egl_error_string(eglGetError()) << "\n";
+    _egl_display = EGL_NO_DISPLAY;
+  }
+
+  if (!_egl_display &&
+      std::find(extensions.begin(), extensions.end(), "EGL_EXT_platform_device") != extensions.end() &&
+      std::find(extensions.begin(), extensions.end(), "EGL_EXT_device_enumeration") != extensions.end()) {
+
+    PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT =
+      (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT");
+
+    EGLint num_devices = 0;
+    if (eglQueryDevicesEXT != nullptr &&
+        eglQueryDevicesEXT(0, nullptr, &num_devices) &&
+        num_devices > 0) {
+      EGLDeviceEXT *devices = (EGLDeviceEXT *)alloca(sizeof(EGLDeviceEXT) * num_devices);
+      eglQueryDevicesEXT(num_devices, devices, &num_devices);
+
+      if (egldisplay_cat.is_debug()) {
+        egldisplay_cat.debug()
+          << "Found " << num_devices << " EGL devices.\n";
+      }
+
+      PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
+        (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
+
+      if (eglGetPlatformDisplayEXT != nullptr) {
+        for (EGLint i = 0; i < num_devices && !_egl_display; ++i) {
+          _egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, devices[i], nullptr);
+
+          if (_egl_display && !eglInitialize(_egl_display, &major, &minor)) {
+            egldisplay_cat.warning()
+              << "Couldn't initialize EGL platform display " << i << ": "
+              << get_egl_error_string(eglGetError()) << "\n";
+            _egl_display = EGL_NO_DISPLAY;
+          }
+        }
+      }
+    }
+  }
+
+  if (!_egl_display) {
+    egldisplay_cat.error()
+      << "Failed to find or initialize a suitable EGL display connection.\n";
     _is_valid = false;
     _is_valid = false;
     return;
     return;
   }
   }
 
 
+  if (egldisplay_cat.is_debug()) {
+    egldisplay_cat.debug()
+      << "Successfully initialized EGL display, got version " << major << "." << minor << "\n";
+  }
+
 #if defined(OPENGLES_1) || defined(OPENGLES_2)
 #if defined(OPENGLES_1) || defined(OPENGLES_2)
   if (!eglBindAPI(EGL_OPENGL_ES_API)) {
   if (!eglBindAPI(EGL_OPENGL_ES_API)) {
     egldisplay_cat.error()
     egldisplay_cat.error()
@@ -165,10 +238,9 @@ make_output(const std::string &name,
     // Early failure - if we are sure that this buffer WONT meet specs, we can
     // Early failure - if we are sure that this buffer WONT meet specs, we can
     // bail out early.
     // bail out early.
     if ((flags & BF_fb_props_optional)==0) {
     if ((flags & BF_fb_props_optional)==0) {
-      if ((fb_prop.get_indexed_color() > 0)||
-          (fb_prop.get_back_buffers() > 0)||
-          (fb_prop.get_accum_bits() > 0)||
-          (fb_prop.get_multisamples() > 0)) {
+      if (fb_prop.get_indexed_color() > 0 ||
+          fb_prop.get_back_buffers() > 0 ||
+          fb_prop.get_accum_bits() > 0) {
         return nullptr;
         return nullptr;
       }
       }
     }
     }

+ 6 - 6
panda/src/event/asyncFuture.I

@@ -93,11 +93,6 @@ set_result(std::nullptr_t) {
   set_result(nullptr, nullptr);
   set_result(nullptr, nullptr);
 }
 }
 
 
-INLINE void AsyncFuture::
-set_result(TypedObject *result) {
-  set_result(result, nullptr);
-}
-
 INLINE void AsyncFuture::
 INLINE void AsyncFuture::
 set_result(TypedReferenceCount *result) {
 set_result(TypedReferenceCount *result) {
   set_result(result, result);
   set_result(result, result);
@@ -110,7 +105,12 @@ set_result(TypedWritableReferenceCount *result) {
 
 
 INLINE void AsyncFuture::
 INLINE void AsyncFuture::
 set_result(const EventParameter &result) {
 set_result(const EventParameter &result) {
-  set_result(result.get_ptr(), result.get_ptr());
+  if (result.is_typed_ref_count()) {
+    set_result(result.get_typed_ref_count_value());
+  }
+  else {
+    set_result(result.get_ptr());
+  }
 }
 }
 
 
 /**
 /**

+ 16 - 0
panda/src/event/asyncFuture.cxx

@@ -176,6 +176,22 @@ notify_done(bool clean_exit) {
   }
   }
 }
 }
 
 
+/**
+ * Sets this future's result as a generic TypedObject.
+ */
+void AsyncFuture::
+set_result(TypedObject *result) {
+  if (result->is_of_type(TypedWritableReferenceCount::get_class_type())) {
+    set_result((TypedWritableReferenceCount *)result);
+  }
+  else if (result->is_of_type(TypedReferenceCount::get_class_type())) {
+    set_result((TypedReferenceCount *)result);
+  }
+  else {
+    set_result(result, nullptr);
+  }
+}
+
 /**
 /**
  * Sets this future's result.  Can only be done while the future is not done.
  * Sets this future's result.  Can only be done while the future is not done.
  * Calling this marks the future as done and schedules the done callbacks.
  * Calling this marks the future as done and schedules the done callbacks.

+ 4 - 3
panda/src/event/asyncFuture.h

@@ -84,13 +84,14 @@ PUBLISHED:
   BLOCKING void wait();
   BLOCKING void wait();
   BLOCKING void wait(double timeout);
   BLOCKING void wait(double timeout);
 
 
+  EXTENSION(void set_result(PyObject *));
+
+public:
   INLINE void set_result(std::nullptr_t);
   INLINE void set_result(std::nullptr_t);
-  INLINE void set_result(TypedObject *result);
   INLINE void set_result(TypedReferenceCount *result);
   INLINE void set_result(TypedReferenceCount *result);
   INLINE void set_result(TypedWritableReferenceCount *result);
   INLINE void set_result(TypedWritableReferenceCount *result);
   INLINE void set_result(const EventParameter &result);
   INLINE void set_result(const EventParameter &result);
-
-public:
+  void set_result(TypedObject *result);
   void set_result(TypedObject *ptr, ReferenceCount *ref_ptr);
   void set_result(TypedObject *ptr, ReferenceCount *ref_ptr);
 
 
   INLINE TypedObject *get_result() const;
   INLINE TypedObject *get_result() const;

+ 76 - 5
panda/src/event/asyncFuture_ext.cxx

@@ -13,9 +13,11 @@
 
 
 #include "asyncFuture_ext.h"
 #include "asyncFuture_ext.h"
 #include "asyncTaskSequence.h"
 #include "asyncTaskSequence.h"
-#include "eventParameter.h"
 #include "paramValue.h"
 #include "paramValue.h"
+#include "paramPyObject.h"
 #include "pythonTask.h"
 #include "pythonTask.h"
+#include "asyncTaskManager.h"
+#include "config_event.h"
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
 
 
@@ -23,6 +25,8 @@
 extern struct Dtool_PyTypedObject Dtool_AsyncFuture;
 extern struct Dtool_PyTypedObject Dtool_AsyncFuture;
 extern struct Dtool_PyTypedObject Dtool_ParamValueBase;
 extern struct Dtool_PyTypedObject Dtool_ParamValueBase;
 extern struct Dtool_PyTypedObject Dtool_TypedObject;
 extern struct Dtool_PyTypedObject Dtool_TypedObject;
+extern struct Dtool_PyTypedObject Dtool_TypedReferenceCount;
+extern struct Dtool_PyTypedObject Dtool_TypedWritableReferenceCount;
 #endif
 #endif
 
 
 /**
 /**
@@ -90,20 +94,24 @@ static PyObject *get_done_result(const AsyncFuture *future) {
         // EventStoreInt and Double are not exposed to Python for some reason.
         // EventStoreInt and Double are not exposed to Python for some reason.
         if (type == EventStoreInt::get_class_type()) {
         if (type == EventStoreInt::get_class_type()) {
           return Dtool_WrapValue(((EventStoreInt *)ptr)->get_value());
           return Dtool_WrapValue(((EventStoreInt *)ptr)->get_value());
-        } else if (type == EventStoreDouble::get_class_type()) {
+        }
+        else if (type == EventStoreDouble::get_class_type()) {
           return Dtool_WrapValue(((EventStoreDouble *)ptr)->get_value());
           return Dtool_WrapValue(((EventStoreDouble *)ptr)->get_value());
         }
         }
+        else if (type == ParamPyObject::get_class_type()) {
+          return ((ParamPyObject *)ptr)->get_value();
+        }
 
 
         ParamValueBase *value = (ParamValueBase *)ptr;
         ParamValueBase *value = (ParamValueBase *)ptr;
         PyObject *wrap = DTool_CreatePyInstanceTyped
         PyObject *wrap = DTool_CreatePyInstanceTyped
           ((void *)value, Dtool_ParamValueBase, false, false, type.get_index());
           ((void *)value, Dtool_ParamValueBase, false, false, type.get_index());
         if (wrap != nullptr) {
         if (wrap != nullptr) {
           PyObject *value = PyObject_GetAttrString(wrap, "value");
           PyObject *value = PyObject_GetAttrString(wrap, "value");
+          Py_DECREF(wrap);
           if (value != nullptr) {
           if (value != nullptr) {
             return value;
             return value;
           }
           }
           PyErr_Clear();
           PyErr_Clear();
-          Py_DECREF(wrap);
         }
         }
       }
       }
 
 
@@ -154,6 +162,58 @@ __await__(PyObject *self) {
   return Dtool_NewGenerator(self, &gen_next);
   return Dtool_NewGenerator(self, &gen_next);
 }
 }
 
 
+/**
+ * Sets this future's result.  Can only be called if done() returns false.
+ */
+void Extension<AsyncFuture>::
+set_result(PyObject *result) {
+  if (result == Py_None) {
+    _this->set_result(nullptr);
+    return;
+  }
+  else if (DtoolInstance_Check(result)) {
+    // If this is a Python subclass of a C++ type, fall through to below, since
+    // we don't want to lose that extra information.
+    if (Py_TYPE(result) == (PyTypeObject *)DtoolInstance_TYPE(result)) {
+      void *ptr;
+      if ((ptr = DtoolInstance_UPCAST(result, Dtool_TypedWritableReferenceCount))) {
+        _this->set_result((TypedWritableReferenceCount *)ptr);
+        return;
+      }
+      if ((ptr = DtoolInstance_UPCAST(result, Dtool_TypedReferenceCount))) {
+        _this->set_result((TypedReferenceCount *)ptr);
+        return;
+      }
+      if ((ptr = DtoolInstance_UPCAST(result, Dtool_TypedObject))) {
+        _this->set_result((TypedObject *)ptr);
+        return;
+      }
+    }
+  }
+  else if (PyUnicode_Check(result)) {
+    Py_ssize_t result_len;
+    wchar_t *result_str = PyUnicode_AsWideCharString(result, &result_len);
+    _this->set_result(new EventStoreWstring(std::wstring(result_str, result_len)));
+    PyMem_Free(result_str);
+    return;
+  }
+  else if (PyLongOrInt_Check(result)) {
+    long result_val = PyLongOrInt_AS_LONG(result);
+    if (result_val >= INT_MIN && result_val <= INT_MAX) {
+      _this->set_result(new EventStoreInt((int)result_val));
+      return;
+    }
+  }
+  else if (PyNumber_Check(result)) {
+    _this->set_result(new EventStoreDouble(PyFloat_AsDouble(result)));
+    return;
+  }
+
+  // If we don't recognize the type, store it as a generic PyObject pointer.
+  ParamPyObject::init_type();
+  _this->set_result(new ParamPyObject(result));
+}
+
 /**
 /**
  * Returns the result of this future, unless it was cancelled, in which case
  * Returns the result of this future, unless it was cancelled, in which case
  * it returns CancelledError.
  * it returns CancelledError.
@@ -285,8 +345,19 @@ gather(PyObject *args) {
       }
       }
     } else if (PyCoro_CheckExact(item)) {
     } else if (PyCoro_CheckExact(item)) {
       // We allow passing in a coroutine instead of a future.  This causes it
       // We allow passing in a coroutine instead of a future.  This causes it
-      // to be scheduled as a task.
-      futures.push_back(new PythonTask(item));
+      // to be scheduled as a task on the current task manager.
+      PT(AsyncTask) task = new PythonTask(item);
+      Thread *current_thread = Thread::get_current_thread();
+      AsyncTask *current_task = (AsyncTask *)current_thread->get_current_task();
+      if (current_task != nullptr) {
+        task->set_task_chain(current_task->get_task_chain());
+        current_task->get_manager()->add(task);
+      }
+      else {
+        event_cat.warning()
+          << "gather() with coroutine not called from within a task; not scheduling with task manager.\n";
+      }
+      futures.push_back(task);
       continue;
       continue;
     }
     }
     return Dtool_Raise_ArgTypeError(item, i, "gather", "coroutine, task or future");
     return Dtool_Raise_ArgTypeError(item, i, "gather", "coroutine, task or future");

+ 1 - 0
panda/src/event/asyncFuture_ext.h

@@ -29,6 +29,7 @@ public:
   static PyObject *__await__(PyObject *self);
   static PyObject *__await__(PyObject *self);
   static PyObject *__iter__(PyObject *self) { return __await__(self); }
   static PyObject *__iter__(PyObject *self) { return __await__(self); }
 
 
+  void set_result(PyObject *result);
   PyObject *result(PyObject *timeout = Py_None) const;
   PyObject *result(PyObject *timeout = Py_None) const;
 
 
   PyObject *add_done_callback(PyObject *self, PyObject *fn);
   PyObject *add_done_callback(PyObject *self, PyObject *fn);

+ 0 - 2
panda/src/event/pythonTask.cxx

@@ -84,7 +84,6 @@ PythonTask(PyObject *func_or_coro, const std::string &name) :
  */
  */
 PythonTask::
 PythonTask::
 ~PythonTask() {
 ~PythonTask() {
-#ifndef NDEBUG
   // If the coroutine threw an exception, and there was no opportunity to
   // If the coroutine threw an exception, and there was no opportunity to
   // handle it, let the user know.
   // handle it, let the user know.
   if (_exception != nullptr && !_retrieved_exception) {
   if (_exception != nullptr && !_retrieved_exception) {
@@ -97,7 +96,6 @@ PythonTask::
     _exc_value = nullptr;
     _exc_value = nullptr;
     _exc_traceback = nullptr;
     _exc_traceback = nullptr;
   }
   }
-#endif
 
 
   Py_XDECREF(_function);
   Py_XDECREF(_function);
   Py_DECREF(_args);
   Py_DECREF(_args);

+ 36 - 19
panda/src/express/patchfile.cxx

@@ -38,25 +38,39 @@ using std::string;
 // USE_MD5_FOR_HASHTABLE_INDEX_VALUES
 // USE_MD5_FOR_HASHTABLE_INDEX_VALUES
 
 
 /*
 /*
- * Patch File Format IF THIS CHANGES, UPDATE installerApplyPatch.cxx IN THE
- * INSTALLER [ HEADER ] 4 bytes  0xfeebfaac ("magic number") (older patch
- * files have a magic number 0xfeebfaab, indicating they are version number
- * 0.) 2 bytes  version number (if magic number == 0xfeebfaac) 4 bytes  length
- * of starting file (if version >= 1) 16 bytes  MD5 of starting file    (if
- * version >= 1) 4 bytes  length of resulting patched file 16 bytes  MD5 of
- * resultant patched file Note that MD5 hashes are written in the order
- * observed by HashVal::read_stream() and HashVal::write_stream(), which is
- * not the normal linear order.  (Each group of four bytes is reversed.)
+ * Patch File Format
+ * IF THIS CHANGES, UPDATE installerApplyPatch.cxx IN THE INSTALLER
+ *
+ * [ HEADER ]
+ *   4 bytes  0xfeebfaac ("magic number")
+ *            (older patch files have a magic number 0xfeebfaab,
+ *            indicating they are version number 0.)
+ *   2 bytes  version number (if magic number == 0xfeebfaac)
+ *   4 bytes  length of starting file (if version >= 1)
+ *  16 bytes  MD5 of starting file    (if version >= 1)
+ *   4 bytes  length of resulting patched file
+ *  16 bytes  MD5 of resultant patched file
+ *
+ * Note that MD5 hashes are written in the order observed by
+ * HashVal::read_stream() and HashVal::write_stream(), which is not
+ * the normal linear order.  (Each group of four bytes is reversed.)
  */
  */
 
 
 const int _v0_header_length = 4 + 4 + 16;
 const int _v0_header_length = 4 + 4 + 16;
 const int _v1_header_length = 4 + 2 + 4 + 16 + 4 + 16;
 const int _v1_header_length = 4 + 2 + 4 + 16 + 4 + 16;
 /*
 /*
- * [ ADDCOPY pairs; repeated N times ] 2 bytes  AL = ADD length AL bytes
- * bytes to add 2 bytes  CL = COPY length 4 bytes  offset of data to copy from
- * original file, if CL != 0. If version >= 2, offset is relative to end of
- * previous copy block; if version < 2, offset is relative to beginning of
- * file.  [ TERMINATOR ] 2 bytes  zero-length ADD 2 bytes  zero-length COPY
+ * [ ADD/COPY pairs; repeated N times ]
+ *   2 bytes  AL = ADD length
+ *  AL bytes  bytes to add
+ *   2 bytes  CL = COPY length
+ *   4 bytes  offset of data to copy from original file, if CL != 0.
+ *            If version >= 2, offset is relative to end of previous
+ *            copy block; if version < 2, offset is relative to
+ *            beginning of file.
+ *
+ * [ TERMINATOR ]
+ *   2 bytes  zero-length ADD
+ *   2 bytes  zero-length COPY
  */
  */
 
 
 // Defines
 // Defines
@@ -249,11 +263,14 @@ read_header(const Filename &patch_file) {
 }
 }
 
 
 /**
 /**
- * Perform one buffer's worth of patching Returns EU_ok while patching Returns
- * EU_success when done If error happens will return one of: EU_error_abort :
- * Patching has not been initiated EU_error_file_invalid : file is corrupted
- * EU_error_invalid_checksum : incompatible patch file
- * EU_error_write_file_rename : could not rename file
+ * Perform one buffer's worth of patching.
+ * Returns one of the following values:
+ * @li @c EU_ok : while patching
+ * @li @c EU_success : when done
+ * @li @c EU_error_abort : Patching has not been initiated
+ * @li @c EU_error_file_invalid : file is corrupted
+ * @li @c EU_error_invalid_checksum : incompatible patch file
+ * @li @c EU_error_write_file_rename : could not rename file
  */
  */
 int Patchfile::
 int Patchfile::
 run() {
 run() {

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

@@ -3054,6 +3054,37 @@ glsl_report_program_errors(GLuint program, bool fatal) {
     if (strcmp(info_log, "Success.\n") != 0 &&
     if (strcmp(info_log, "Success.\n") != 0 &&
         strcmp(info_log, "No errors.\n") != 0 &&
         strcmp(info_log, "No errors.\n") != 0 &&
         strcmp(info_log, "Validation successful.\n") != 0) {
         strcmp(info_log, "Validation successful.\n") != 0) {
+
+#ifdef __APPLE__
+      // Filter out these unhelpful warnings that Apple always generates.
+      while (true) {
+        if (info_log[0] == '\n') {
+          ++info_log;
+          continue;
+        }
+        if (info_log[0] == '\0') {
+          // We reached the end without finding anything interesting.
+          return;
+        }
+        int linelen = 0;
+        if ((sscanf(info_log, "WARNING: Could not find vertex shader attribute %*s to match BindAttributeLocation request.%*[\n]%n", &linelen) == 0 && linelen > 0) ||
+            (sscanf(info_log, "WARNING: Could not find fragment shader output %*s to match FragDataBinding request.%*[\n]%n", &linelen) == 0 && linelen > 0)) {
+          info_log += linelen;
+          continue;
+        }
+        else {
+          break;
+        }
+
+        info_log = strchr(info_log, '\n');
+        if (info_log == nullptr) {
+          // We reached the end without finding anything interesting.
+          return;
+        }
+        ++info_log;
+      }
+#endif
+
       if (!fatal) {
       if (!fatal) {
         GLCAT.warning()
         GLCAT.warning()
           << "Shader " << _shader->get_filename() << " produced the "
           << "Shader " << _shader->get_filename() << " produced the "

+ 72 - 5
panda/src/linmath/lvecBase2_ext_src.I

@@ -195,11 +195,78 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) {
 /**
 /**
  *
  *
  */
  */
-INLINE_LINMATH FLOATNAME(LVecBase2) Extension<FLOATNAME(LVecBase2)>::
-__pow__(FLOATTYPE exponent) const {
-  return FLOATNAME(LVecBase2)(
-    cpow(_this->_v(0), exponent),
-    cpow(_this->_v(1), exponent));
+INLINE_LINMATH PyObject *Extension<FLOATNAME(LVecBase2)>::
+__floordiv__(PyObject *self, FLOATTYPE scalar) const {
+  if (scalar == (FLOATTYPE)0) {
+    return PyErr_Format(PyExc_ZeroDivisionError, "floor division by zero");
+  }
+
+#ifndef CPPPARSER
+  extern struct Dtool_PyTypedObject FLOATNAME(Dtool_LVecBase2);
+#endif
+  PyObject *py_vec = PyObject_CallNoArgs((PyObject *)DtoolInstance_TYPE(self));
+  if (py_vec != nullptr) {
+    FLOATNAME(LVecBase2) *vec = (FLOATNAME(LVecBase2) *)DtoolInstance_UPCAST(py_vec, FLOATNAME(Dtool_LVecBase2));
+    nassertr(vec != nullptr, nullptr);
+
+#ifdef FLOATTYPE_IS_INT
+    if (scalar > 0) {
+      vec->_v(0) = (_this->_v(0) >= 0) ? _this->_v(0) / scalar : -1 - (-1 - _this->_v(0)) / scalar;
+      vec->_v(1) = (_this->_v(1) >= 0) ? _this->_v(1) / scalar : -1 - (-1 - _this->_v(1)) / scalar;
+    } else {
+      vec->_v(0) = (_this->_v(0) <= 0) ? _this->_v(0) / scalar : -1 + (1 - _this->_v(0)) / -scalar;
+      vec->_v(1) = (_this->_v(1) <= 0) ? _this->_v(1) / scalar : -1 + (1 - _this->_v(1)) / -scalar;
+    }
+#else
+    vec->_v(0) = std::floor(_this->_v(0) / scalar);
+    vec->_v(1) = std::floor(_this->_v(1) / scalar);
+#endif
+  }
+  return py_vec;
+}
+
+/**
+ *
+ */
+INLINE_LINMATH PyObject *Extension<FLOATNAME(LVecBase2)>::
+__ifloordiv__(PyObject *self, FLOATTYPE scalar) {
+  if (scalar == (FLOATTYPE)0) {
+    return PyErr_Format(PyExc_ZeroDivisionError, "floor division by zero");
+  }
+
+#ifdef FLOATTYPE_IS_INT
+  if (scalar > 0) {
+    _this->_v(0) = (_this->_v(0) >= 0) ? _this->_v(0) / scalar : -1 - (-1 - _this->_v(0)) / scalar;
+    _this->_v(1) = (_this->_v(1) >= 0) ? _this->_v(1) / scalar : -1 - (-1 - _this->_v(1)) / scalar;
+  } else {
+    _this->_v(0) = (_this->_v(0) <= 0) ? _this->_v(0) / scalar : -1 + (1 - _this->_v(0)) / -scalar;
+    _this->_v(1) = (_this->_v(1) <= 0) ? _this->_v(1) / scalar : -1 + (1 - _this->_v(1)) / -scalar;
+  }
+#else
+  _this->_v(0) = std::floor(_this->_v(0) / scalar);
+  _this->_v(1) = std::floor(_this->_v(1) / scalar);
+#endif
+  Py_INCREF(self);
+  return self;
+}
+
+/**
+ *
+ */
+INLINE_LINMATH PyObject *Extension<FLOATNAME(LVecBase2)>::
+__pow__(PyObject *self, FLOATTYPE exponent) const {
+#ifndef CPPPARSER
+  extern struct Dtool_PyTypedObject FLOATNAME(Dtool_LVecBase2);
+#endif
+  PyObject *py_vec = PyObject_CallNoArgs((PyObject *)DtoolInstance_TYPE(self));
+  if (py_vec != nullptr) {
+    FLOATNAME(LVecBase2) *vec = (FLOATNAME(LVecBase2) *)DtoolInstance_UPCAST(py_vec, FLOATNAME(Dtool_LVecBase2));
+    nassertr(vec != nullptr, nullptr);
+
+    vec->_v(0) = cpow(_this->_v(0), exponent);
+    vec->_v(1) = cpow(_this->_v(1), exponent);
+  }
+  return py_vec;
 }
 }
 
 
 /**
 /**

+ 4 - 1
panda/src/linmath/lvecBase2_ext_src.h

@@ -23,7 +23,10 @@ public:
   INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign);
   INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign);
   INLINE_LINMATH std::string __repr__() const;
   INLINE_LINMATH std::string __repr__() const;
 
 
-  INLINE_LINMATH FLOATNAME(LVecBase2) __pow__(FLOATTYPE exponent) const;
+  INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const;
+  INLINE_LINMATH PyObject *__ifloordiv__(PyObject *self, FLOATTYPE scalar);
+
+  INLINE_LINMATH PyObject *__pow__(PyObject *self, FLOATTYPE exponent) const;
   INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent);
   INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent);
 
 
   INLINE_LINMATH PyObject *__round__(PyObject *self) const;
   INLINE_LINMATH PyObject *__round__(PyObject *self) const;

+ 4 - 1
panda/src/linmath/lvecBase2_src.h

@@ -133,7 +133,10 @@ PUBLISHED:
 
 
   INLINE_LINMATH void componentwise_mult(const FLOATNAME(LVecBase2) &other);
   INLINE_LINMATH void componentwise_mult(const FLOATNAME(LVecBase2) &other);
 
 
-  EXTENSION(INLINE_LINMATH FLOATNAME(LVecBase2) __pow__(FLOATTYPE exponent) const);
+  EXTENSION(INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const);
+  EXTENSION(INLINE_LINMATH PyObject *__ifloordiv__(PyObject *self, FLOATTYPE scalar));
+
+  EXTENSION(INLINE_LINMATH PyObject *__pow__(PyObject *self, FLOATTYPE exponent) const);
   EXTENSION(INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent));
   EXTENSION(INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent));
 
 
   EXTENSION(INLINE_LINMATH PyObject *__round__(PyObject *self));
   EXTENSION(INLINE_LINMATH PyObject *__round__(PyObject *self));

+ 79 - 6
panda/src/linmath/lvecBase3_ext_src.I

@@ -196,12 +196,85 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) {
 /**
 /**
  *
  *
  */
  */
-INLINE_LINMATH FLOATNAME(LVecBase3) Extension<FLOATNAME(LVecBase3)>::
-__pow__(FLOATTYPE exponent) const {
-  return FLOATNAME(LVecBase3)(
-    cpow(_this->_v(0), exponent),
-    cpow(_this->_v(1), exponent),
-    cpow(_this->_v(2), exponent));
+INLINE_LINMATH PyObject *Extension<FLOATNAME(LVecBase3)>::
+__floordiv__(PyObject *self, FLOATTYPE scalar) const {
+  if (scalar == (FLOATTYPE)0) {
+    return PyErr_Format(PyExc_ZeroDivisionError, "floor division by zero");
+  }
+
+#ifndef CPPPARSER
+  extern struct Dtool_PyTypedObject FLOATNAME(Dtool_LVecBase3);
+#endif
+  PyObject *py_vec = PyObject_CallNoArgs((PyObject *)DtoolInstance_TYPE(self));
+  if (py_vec != nullptr) {
+    FLOATNAME(LVecBase3) *vec = (FLOATNAME(LVecBase3) *)DtoolInstance_UPCAST(py_vec, FLOATNAME(Dtool_LVecBase3));
+    nassertr(vec != nullptr, nullptr);
+
+#ifdef FLOATTYPE_IS_INT
+    if (scalar > 0) {
+      vec->_v(0) = (_this->_v(0) >= 0) ? _this->_v(0) / scalar : -1 - (-1 - _this->_v(0)) / scalar;
+      vec->_v(1) = (_this->_v(1) >= 0) ? _this->_v(1) / scalar : -1 - (-1 - _this->_v(1)) / scalar;
+      vec->_v(2) = (_this->_v(2) >= 0) ? _this->_v(2) / scalar : -1 - (-1 - _this->_v(2)) / scalar;
+    } else {
+      vec->_v(0) = (_this->_v(0) <= 0) ? _this->_v(0) / scalar : -1 + (1 - _this->_v(0)) / -scalar;
+      vec->_v(1) = (_this->_v(1) <= 0) ? _this->_v(1) / scalar : -1 + (1 - _this->_v(1)) / -scalar;
+      vec->_v(2) = (_this->_v(2) <= 0) ? _this->_v(2) / scalar : -1 + (1 - _this->_v(2)) / -scalar;
+    }
+#else
+    vec->_v(0) = std::floor(_this->_v(0) / scalar);
+    vec->_v(1) = std::floor(_this->_v(1) / scalar);
+    vec->_v(2) = std::floor(_this->_v(2) / scalar);
+#endif
+  }
+  return py_vec;
+}
+
+/**
+ *
+ */
+INLINE_LINMATH PyObject *Extension<FLOATNAME(LVecBase3)>::
+__ifloordiv__(PyObject *self, FLOATTYPE scalar) {
+  if (scalar == (FLOATTYPE)0) {
+    return PyErr_Format(PyExc_ZeroDivisionError, "floor division by zero");
+  }
+
+#ifdef FLOATTYPE_IS_INT
+  if (scalar > 0) {
+    _this->_v(0) = (_this->_v(0) >= 0) ? _this->_v(0) / scalar : -1 - (-1 - _this->_v(0)) / scalar;
+    _this->_v(1) = (_this->_v(1) >= 0) ? _this->_v(1) / scalar : -1 - (-1 - _this->_v(1)) / scalar;
+    _this->_v(2) = (_this->_v(2) >= 0) ? _this->_v(2) / scalar : -1 - (-1 - _this->_v(2)) / scalar;
+  } else {
+    _this->_v(0) = (_this->_v(0) <= 0) ? _this->_v(0) / scalar : -1 + (1 - _this->_v(0)) / -scalar;
+    _this->_v(1) = (_this->_v(1) <= 0) ? _this->_v(1) / scalar : -1 + (1 - _this->_v(1)) / -scalar;
+    _this->_v(2) = (_this->_v(2) <= 0) ? _this->_v(2) / scalar : -1 + (1 - _this->_v(2)) / -scalar;
+  }
+#else
+  _this->_v(0) = std::floor(_this->_v(0) / scalar);
+  _this->_v(1) = std::floor(_this->_v(1) / scalar);
+  _this->_v(2) = std::floor(_this->_v(2) / scalar);
+#endif
+  Py_INCREF(self);
+  return self;
+}
+
+/**
+ *
+ */
+INLINE_LINMATH PyObject *Extension<FLOATNAME(LVecBase3)>::
+__pow__(PyObject *self, FLOATTYPE exponent) const {
+#ifndef CPPPARSER
+  extern struct Dtool_PyTypedObject FLOATNAME(Dtool_LVecBase3);
+#endif
+  PyObject *py_vec = PyObject_CallNoArgs((PyObject *)DtoolInstance_TYPE(self));
+  if (py_vec != nullptr) {
+    FLOATNAME(LVecBase3) *vec = (FLOATNAME(LVecBase3) *)DtoolInstance_UPCAST(py_vec, FLOATNAME(Dtool_LVecBase3));
+    nassertr(vec != nullptr, nullptr);
+
+    vec->_v(0) = cpow(_this->_v(0), exponent);
+    vec->_v(1) = cpow(_this->_v(1), exponent);
+    vec->_v(2) = cpow(_this->_v(2), exponent);
+  }
+  return py_vec;
 }
 }
 
 
 /**
 /**

+ 4 - 1
panda/src/linmath/lvecBase3_ext_src.h

@@ -23,7 +23,10 @@ public:
   INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign);
   INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign);
   INLINE_LINMATH std::string __repr__() const;
   INLINE_LINMATH std::string __repr__() const;
 
 
-  INLINE_LINMATH FLOATNAME(LVecBase3) __pow__(FLOATTYPE exponent) const;
+  INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const;
+  INLINE_LINMATH PyObject *__ifloordiv__(PyObject *self, FLOATTYPE scalar);
+
+  INLINE_LINMATH PyObject *__pow__(PyObject *self, FLOATTYPE exponent) const;
   INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent);
   INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent);
 
 
   INLINE_LINMATH PyObject *__round__(PyObject *self) const;
   INLINE_LINMATH PyObject *__round__(PyObject *self) const;

+ 4 - 1
panda/src/linmath/lvecBase3_src.h

@@ -152,7 +152,10 @@ PUBLISHED:
 
 
   INLINE_LINMATH void componentwise_mult(const FLOATNAME(LVecBase3) &other);
   INLINE_LINMATH void componentwise_mult(const FLOATNAME(LVecBase3) &other);
 
 
-  EXTENSION(INLINE_LINMATH FLOATNAME(LVecBase3) __pow__(FLOATTYPE exponent) const);
+  EXTENSION(INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const);
+  EXTENSION(INLINE_LINMATH PyObject *__ifloordiv__(PyObject *self, FLOATTYPE scalar));
+
+  EXTENSION(INLINE_LINMATH PyObject *__pow__(PyObject *self, FLOATTYPE exponent) const);
   EXTENSION(INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent));
   EXTENSION(INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent));
 
 
   EXTENSION(INLINE_LINMATH PyObject *__round__(PyObject *self));
   EXTENSION(INLINE_LINMATH PyObject *__round__(PyObject *self));

+ 86 - 7
panda/src/linmath/lvecBase4_ext_src.I

@@ -202,13 +202,92 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) {
 /**
 /**
  *
  *
  */
  */
-INLINE_LINMATH FLOATNAME(LVecBase4) Extension<FLOATNAME(LVecBase4)>::
-__pow__(FLOATTYPE exponent) const {
-  return FLOATNAME(LVecBase4)(
-    cpow(_this->_v(0), exponent),
-    cpow(_this->_v(1), exponent),
-    cpow(_this->_v(2), exponent),
-    cpow(_this->_v(3), exponent));
+INLINE_LINMATH PyObject *Extension<FLOATNAME(LVecBase4)>::
+__floordiv__(PyObject *self, FLOATTYPE scalar) const {
+  if (scalar == (FLOATTYPE)0) {
+    return PyErr_Format(PyExc_ZeroDivisionError, "floor division by zero");
+  }
+
+#ifndef CPPPARSER
+  extern struct Dtool_PyTypedObject FLOATNAME(Dtool_LVecBase4);
+#endif
+  PyObject *py_vec = PyObject_CallNoArgs((PyObject *)DtoolInstance_TYPE(self));
+  if (py_vec != nullptr) {
+    FLOATNAME(LVecBase4) *vec = (FLOATNAME(LVecBase4) *)DtoolInstance_UPCAST(py_vec, FLOATNAME(Dtool_LVecBase4));
+    nassertr(vec != nullptr, nullptr);
+
+#ifdef FLOATTYPE_IS_INT
+    if (scalar > 0) {
+      vec->_v(0) = (_this->_v(0) >= 0) ? _this->_v(0) / scalar : -1 - (-1 - _this->_v(0)) / scalar;
+      vec->_v(1) = (_this->_v(1) >= 0) ? _this->_v(1) / scalar : -1 - (-1 - _this->_v(1)) / scalar;
+      vec->_v(2) = (_this->_v(2) >= 0) ? _this->_v(2) / scalar : -1 - (-1 - _this->_v(2)) / scalar;
+      vec->_v(3) = (_this->_v(3) >= 0) ? _this->_v(3) / scalar : -1 - (-1 - _this->_v(3)) / scalar;
+    } else {
+      vec->_v(0) = (_this->_v(0) <= 0) ? _this->_v(0) / scalar : -1 + (1 - _this->_v(0)) / -scalar;
+      vec->_v(1) = (_this->_v(1) <= 0) ? _this->_v(1) / scalar : -1 + (1 - _this->_v(1)) / -scalar;
+      vec->_v(2) = (_this->_v(2) <= 0) ? _this->_v(2) / scalar : -1 + (1 - _this->_v(2)) / -scalar;
+      vec->_v(3) = (_this->_v(3) <= 0) ? _this->_v(3) / scalar : -1 + (1 - _this->_v(3)) / -scalar;
+    }
+#else
+    vec->_v(0) = std::floor(_this->_v(0) / scalar);
+    vec->_v(1) = std::floor(_this->_v(1) / scalar);
+    vec->_v(2) = std::floor(_this->_v(2) / scalar);
+    vec->_v(3) = std::floor(_this->_v(3) / scalar);
+#endif
+  }
+  return py_vec;
+}
+
+/**
+ *
+ */
+INLINE_LINMATH PyObject *Extension<FLOATNAME(LVecBase4)>::
+__ifloordiv__(PyObject *self, FLOATTYPE scalar) {
+  if (scalar == (FLOATTYPE)0) {
+    return PyErr_Format(PyExc_ZeroDivisionError, "floor division by zero");
+  }
+
+#ifdef FLOATTYPE_IS_INT
+  if (scalar > 0) {
+    _this->_v(0) = (_this->_v(0) >= 0) ? _this->_v(0) / scalar : -1 - (-1 - _this->_v(0)) / scalar;
+    _this->_v(1) = (_this->_v(1) >= 0) ? _this->_v(1) / scalar : -1 - (-1 - _this->_v(1)) / scalar;
+    _this->_v(2) = (_this->_v(2) >= 0) ? _this->_v(2) / scalar : -1 - (-1 - _this->_v(2)) / scalar;
+    _this->_v(3) = (_this->_v(3) >= 0) ? _this->_v(3) / scalar : -1 - (-1 - _this->_v(3)) / scalar;
+  } else {
+    _this->_v(0) = (_this->_v(0) <= 0) ? _this->_v(0) / scalar : -1 + (1 - _this->_v(0)) / -scalar;
+    _this->_v(1) = (_this->_v(1) <= 0) ? _this->_v(1) / scalar : -1 + (1 - _this->_v(1)) / -scalar;
+    _this->_v(2) = (_this->_v(2) <= 0) ? _this->_v(2) / scalar : -1 + (1 - _this->_v(2)) / -scalar;
+    _this->_v(3) = (_this->_v(3) <= 0) ? _this->_v(3) / scalar : -1 + (1 - _this->_v(3)) / -scalar;
+  }
+#else
+  _this->_v(0) = std::floor(_this->_v(0) / scalar);
+  _this->_v(1) = std::floor(_this->_v(1) / scalar);
+  _this->_v(2) = std::floor(_this->_v(2) / scalar);
+  _this->_v(3) = std::floor(_this->_v(3) / scalar);
+#endif
+  Py_INCREF(self);
+  return self;
+}
+
+/**
+ *
+ */
+INLINE_LINMATH PyObject *Extension<FLOATNAME(LVecBase4)>::
+__pow__(PyObject *self, FLOATTYPE exponent) const {
+#ifndef CPPPARSER
+  extern struct Dtool_PyTypedObject FLOATNAME(Dtool_LVecBase4);
+#endif
+  PyObject *py_vec = PyObject_CallNoArgs((PyObject *)DtoolInstance_TYPE(self));
+  if (py_vec != nullptr) {
+    FLOATNAME(LVecBase4) *vec = (FLOATNAME(LVecBase4) *)DtoolInstance_UPCAST(py_vec, FLOATNAME(Dtool_LVecBase4));
+    nassertr(vec != nullptr, nullptr);
+
+    vec->_v(0) = cpow(_this->_v(0), exponent);
+    vec->_v(1) = cpow(_this->_v(1), exponent);
+    vec->_v(2) = cpow(_this->_v(2), exponent);
+    vec->_v(3) = cpow(_this->_v(3), exponent);
+  }
+  return py_vec;
 }
 }
 
 
 /**
 /**

+ 4 - 1
panda/src/linmath/lvecBase4_ext_src.h

@@ -23,7 +23,10 @@ public:
   INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign);
   INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign);
   INLINE_LINMATH std::string __repr__() const;
   INLINE_LINMATH std::string __repr__() const;
 
 
-  INLINE_LINMATH FLOATNAME(LVecBase4) __pow__(FLOATTYPE exponent) const;
+  INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const;
+  INLINE_LINMATH PyObject *__ifloordiv__(PyObject *self, FLOATTYPE scalar);
+
+  INLINE_LINMATH PyObject *__pow__(PyObject *self, FLOATTYPE exponent) const;
   INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent);
   INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent);
 
 
   INLINE_LINMATH PyObject *__round__(PyObject *self) const;
   INLINE_LINMATH PyObject *__round__(PyObject *self) const;

+ 4 - 1
panda/src/linmath/lvecBase4_src.h

@@ -160,7 +160,10 @@ PUBLISHED:
 
 
   INLINE_LINMATH void componentwise_mult(const FLOATNAME(LVecBase4) &other);
   INLINE_LINMATH void componentwise_mult(const FLOATNAME(LVecBase4) &other);
 
 
-  EXTENSION(INLINE_LINMATH FLOATNAME(LVecBase4) __pow__(FLOATTYPE exponent) const);
+  EXTENSION(INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const);
+  EXTENSION(INLINE_LINMATH PyObject *__ifloordiv__(PyObject *self, FLOATTYPE scalar));
+
+  EXTENSION(INLINE_LINMATH PyObject *__pow__(PyObject *self, FLOATTYPE exponent) const);
   EXTENSION(INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent));
   EXTENSION(INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent));
 
 
   EXTENSION(INLINE_LINMATH PyObject *__round__(PyObject *self));
   EXTENSION(INLINE_LINMATH PyObject *__round__(PyObject *self));

+ 1 - 1
panda/src/movies/config_movies.cxx

@@ -68,7 +68,7 @@ ConfigVariableBool vorbis_enable_seek
           "using the Ogg Vorbis decoder."));
           "using the Ogg Vorbis decoder."));
 
 
 ConfigVariableBool vorbis_seek_lap
 ConfigVariableBool vorbis_seek_lap
-("vorbis-seek-lap", true,
+("vorbis-seek-lap", false,
  PRC_DESC("If this is set to true, the Ogg Vorbis decoder will automatically "
  PRC_DESC("If this is set to true, the Ogg Vorbis decoder will automatically "
           "crosslap the transition from the previous playback position into "
           "crosslap the transition from the previous playback position into "
           "the new playback position when seeking in order to eliminate "
           "the new playback position when seeking in order to eliminate "

+ 18 - 8
panda/src/pgraph/nodePath.I

@@ -998,7 +998,8 @@ compose_color_scale(PN_stdfloat sr, PN_stdfloat sg, PN_stdfloat sb, PN_stdfloat
 }
 }
 
 
 /**
 /**
- * Sets the red scale component of the transform
+ * Sets the red component of the color scale.
+ * @see set_color_scale()
  */
  */
 INLINE void NodePath::
 INLINE void NodePath::
 set_sr(PN_stdfloat sr) {
 set_sr(PN_stdfloat sr) {
@@ -1009,7 +1010,8 @@ set_sr(PN_stdfloat sr) {
 }
 }
 
 
 /**
 /**
- * Sets the alpha scale component of the transform
+ * Sets the green component of the color scale.
+ * @see set_color_scale()
  */
  */
 INLINE void NodePath::
 INLINE void NodePath::
 set_sg(PN_stdfloat sg) {
 set_sg(PN_stdfloat sg) {
@@ -1020,7 +1022,8 @@ set_sg(PN_stdfloat sg) {
 }
 }
 
 
 /**
 /**
- * Sets the blue scale component of the transform
+ * Sets the blue component of the color scale.
+ * @see set_color_scale()
  */
  */
 INLINE void NodePath::
 INLINE void NodePath::
 set_sb(PN_stdfloat sb) {
 set_sb(PN_stdfloat sb) {
@@ -1031,7 +1034,8 @@ set_sb(PN_stdfloat sb) {
 }
 }
 
 
 /**
 /**
- * Sets the alpha scale component of the transform
+ * Sets the alpha component of the color scale.
+ * @see set_color_scale()
  */
  */
 INLINE void NodePath::
 INLINE void NodePath::
 set_sa(PN_stdfloat sa) {
 set_sa(PN_stdfloat sa) {
@@ -1042,7 +1046,8 @@ set_sa(PN_stdfloat sa) {
 }
 }
 
 
 /**
 /**
- * Gets the red scale component of the transform
+ * Gets the red component of the color scale.
+ * @see get_color_scale()
  */
  */
 INLINE PN_stdfloat NodePath::
 INLINE PN_stdfloat NodePath::
 get_sr() const {
 get_sr() const {
@@ -1050,7 +1055,8 @@ get_sr() const {
 }
 }
 
 
 /**
 /**
- * Gets the green scale component of the transform
+ * Gets the green component of the color scale.
+ * @see get_color_scale()
  */
  */
 INLINE PN_stdfloat NodePath::
 INLINE PN_stdfloat NodePath::
 get_sg() const {
 get_sg() const {
@@ -1058,7 +1064,8 @@ get_sg() const {
 }
 }
 
 
 /**
 /**
- * Gets the blue scale component of the transform
+ * Gets the blue component of the color scale.
+ * @see get_color_scale()
  */
  */
 INLINE PN_stdfloat NodePath::
 INLINE PN_stdfloat NodePath::
 get_sb() const {
 get_sb() const {
@@ -1066,7 +1073,8 @@ get_sb() const {
 }
 }
 
 
 /**
 /**
- * Gets the alpha scale component of the transform
+ * Gets the alpha component of the color scale.
+ * @see get_color_scale()
  */
  */
 INLINE PN_stdfloat NodePath::
 INLINE PN_stdfloat NodePath::
 get_sa() const {
 get_sa() const {
@@ -1838,6 +1846,8 @@ show_through(DrawMask camera_mask) {
  * invisible to all cameras.  It remains part of the scene graph, its bounding
  * invisible to all cameras.  It remains part of the scene graph, its bounding
  * volume still contributes to its parent's bounding volume, and it will still
  * volume still contributes to its parent's bounding volume, and it will still
  * be involved in collision tests.
  * be involved in collision tests.
+ *
+ * To undo this, call show().
  */
  */
 INLINE void NodePath::
 INLINE void NodePath::
 hide() {
 hide() {

+ 40 - 0
panda/src/pgraph/nodePath.cxx

@@ -959,6 +959,11 @@ set_pos(const LVecBase3 &pos) {
   node()->reset_prev_transform();
   node()->reset_prev_transform();
 }
 }
 
 
+/**
+ * Sets the X component of the position transform, leaving other components
+ * untouched.
+ * @see set_pos()
+ */
 void NodePath::
 void NodePath::
 set_x(PN_stdfloat x) {
 set_x(PN_stdfloat x) {
   nassertv_always(!is_empty());
   nassertv_always(!is_empty());
@@ -967,6 +972,11 @@ set_x(PN_stdfloat x) {
   set_pos(pos);
   set_pos(pos);
 }
 }
 
 
+/**
+ * Sets the Y component of the position transform, leaving other components
+ * untouched.
+ * @see set_pos()
+ */
 void NodePath::
 void NodePath::
 set_y(PN_stdfloat y) {
 set_y(PN_stdfloat y) {
   nassertv_always(!is_empty());
   nassertv_always(!is_empty());
@@ -975,6 +985,11 @@ set_y(PN_stdfloat y) {
   set_pos(pos);
   set_pos(pos);
 }
 }
 
 
+/**
+ * Sets the Z component of the position transform, leaving other components
+ * untouched.
+ * @see set_pos()
+ */
 void NodePath::
 void NodePath::
 set_z(PN_stdfloat z) {
 set_z(PN_stdfloat z) {
   nassertv_always(!is_empty());
   nassertv_always(!is_empty());
@@ -1127,6 +1142,11 @@ set_scale(const LVecBase3 &scale) {
   set_transform(transform->set_scale(scale));
   set_transform(transform->set_scale(scale));
 }
 }
 
 
+/**
+ * Sets the x-scale component of the transform, leaving other components
+ * untouched.
+ * @see set_scale()
+ */
 void NodePath::
 void NodePath::
 set_sx(PN_stdfloat sx) {
 set_sx(PN_stdfloat sx) {
   nassertv_always(!is_empty());
   nassertv_always(!is_empty());
@@ -1136,6 +1156,11 @@ set_sx(PN_stdfloat sx) {
   set_transform(transform->set_scale(scale));
   set_transform(transform->set_scale(scale));
 }
 }
 
 
+/**
+ * Sets the y-scale component of the transform, leaving other components
+ * untouched.
+ * @see set_scale()
+ */
 void NodePath::
 void NodePath::
 set_sy(PN_stdfloat sy) {
 set_sy(PN_stdfloat sy) {
   nassertv_always(!is_empty());
   nassertv_always(!is_empty());
@@ -1145,6 +1170,11 @@ set_sy(PN_stdfloat sy) {
   set_transform(transform->set_scale(scale));
   set_transform(transform->set_scale(scale));
 }
 }
 
 
+/**
+ * Sets the z-scale component of the transform, leaving other components
+ * untouched.
+ * @see set_scale()
+ */
 void NodePath::
 void NodePath::
 set_sz(PN_stdfloat sz) {
 set_sz(PN_stdfloat sz) {
   nassertv_always(!is_empty());
   nassertv_always(!is_empty());
@@ -4214,6 +4244,8 @@ get_material() const {
 /**
 /**
  * Recursively searches the scene graph for references to the given material,
  * Recursively searches the scene graph for references to the given material,
  * and replaces them with the new material.
  * and replaces them with the new material.
+ *
+ * @since 1.10.0
  */
  */
 void NodePath::
 void NodePath::
 replace_material(Material *mat, Material *new_mat) {
 replace_material(Material *mat, Material *new_mat) {
@@ -4936,6 +4968,8 @@ get_transparency() const {
  * Specifically sets or disables a logical operation on this particular node.
  * Specifically sets or disables a logical operation on this particular node.
  * If no other nodes override, this will cause geometry to be rendered without
  * If no other nodes override, this will cause geometry to be rendered without
  * color blending but instead using the given logical operator.
  * color blending but instead using the given logical operator.
+ *
+ * @since 1.10.0
  */
  */
 void NodePath::
 void NodePath::
 set_logic_op(LogicOpAttrib::Operation op, int priority) {
 set_logic_op(LogicOpAttrib::Operation op, int priority) {
@@ -4948,6 +4982,8 @@ set_logic_op(LogicOpAttrib::Operation op, int priority) {
  * Completely removes any logical operation that may have been set on this
  * Completely removes any logical operation that may have been set on this
  * node via set_logic_op(). The geometry at this level and below will
  * node via set_logic_op(). The geometry at this level and below will
  * subsequently be rendered using standard color blending.
  * subsequently be rendered using standard color blending.
+ *
+ * @since 1.10.0
  */
  */
 void NodePath::
 void NodePath::
 clear_logic_op() {
 clear_logic_op() {
@@ -4960,6 +4996,8 @@ clear_logic_op() {
  * particular node via set_logic_op().  If this returns true, then
  * particular node via set_logic_op().  If this returns true, then
  * get_logic_op() may be called to determine whether a logical operation has
  * get_logic_op() may be called to determine whether a logical operation has
  * been explicitly disabled for this node or set to particular operation.
  * been explicitly disabled for this node or set to particular operation.
+ *
+ * @since 1.10.0
  */
  */
 bool NodePath::
 bool NodePath::
 has_logic_op() const {
 has_logic_op() const {
@@ -4974,6 +5012,8 @@ has_logic_op() const {
  * has_logic_op().  This does not necessarily imply that the geometry will
  * has_logic_op().  This does not necessarily imply that the geometry will
  * or will not be rendered with the given logical operation, as there may be
  * or will not be rendered with the given logical operation, as there may be
  * other nodes that override.
  * other nodes that override.
+ *
+ * @since 1.10.0
  */
  */
 LogicOpAttrib::Operation NodePath::
 LogicOpAttrib::Operation NodePath::
 get_logic_op() const {
 get_logic_op() const {

+ 9 - 0
panda/src/pgraph/transformState.I

@@ -79,6 +79,15 @@ make_pos_hpr(const LVecBase3 &pos, const LVecBase3 &hpr) {
                             LVecBase3(1.0, 1.0f, 1.0f));
                             LVecBase3(1.0, 1.0f, 1.0f));
 }
 }
 
 
+/**
+ * Makes a new TransformState with the specified components.
+ */
+INLINE CPT(TransformState) TransformState::
+make_pos_quat(const LVecBase3 &pos, const LQuaternion &quat) {
+  return make_pos_quat_scale(pos, quat,
+                             LVecBase3(1.0, 1.0f, 1.0f));
+}
+
 /**
 /**
  * Makes a new TransformState with the specified components.
  * Makes a new TransformState with the specified components.
  */
  */

+ 2 - 0
panda/src/pgraph/transformState.h

@@ -76,6 +76,8 @@ PUBLISHED:
   INLINE static CPT(TransformState) make_quat(const LQuaternion &quat);
   INLINE static CPT(TransformState) make_quat(const LQuaternion &quat);
   INLINE static CPT(TransformState) make_pos_hpr(const LVecBase3 &pos,
   INLINE static CPT(TransformState) make_pos_hpr(const LVecBase3 &pos,
                                                  const LVecBase3 &hpr);
                                                  const LVecBase3 &hpr);
+  INLINE static CPT(TransformState) make_pos_quat(const LVecBase3 &pos,
+                                                  const LQuaternion &quat);
   INLINE static CPT(TransformState) make_scale(PN_stdfloat scale);
   INLINE static CPT(TransformState) make_scale(PN_stdfloat scale);
   INLINE static CPT(TransformState) make_scale(const LVecBase3 &scale);
   INLINE static CPT(TransformState) make_scale(const LVecBase3 &scale);
   INLINE static CPT(TransformState) make_shear(const LVecBase3 &shear);
   INLINE static CPT(TransformState) make_shear(const LVecBase3 &shear);

+ 72 - 37
panda/src/pgui/pgItem.cxx

@@ -30,6 +30,7 @@
 #include "boundingSphere.h"
 #include "boundingSphere.h"
 #include "boundingBox.h"
 #include "boundingBox.h"
 #include "config_mathutil.h"
 #include "config_mathutil.h"
+#include "pipeline.h"
 
 
 #ifdef HAVE_AUDIO
 #ifdef HAVE_AUDIO
 #include "audioSound.h"
 #include "audioSound.h"
@@ -124,6 +125,13 @@ PGItem(const PGItem &copy) :
     new_sd._frame_style = old_sd._frame_style;
     new_sd._frame_style = old_sd._frame_style;
 
 
     _state_defs.push_back(new_sd);
     _state_defs.push_back(new_sd);
+
+#ifdef THREADED_PIPELINE
+    if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
+      ((PGItem &)copy).update_frame((int)i);
+      update_frame((int)i);
+    }
+#endif
   }
   }
 }
 }
 
 
@@ -199,8 +207,10 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
     if (state >= 0 && (size_t)state < _state_defs.size()) {
     if (state >= 0 && (size_t)state < _state_defs.size()) {
       StateDef &state_def = _state_defs[state];
       StateDef &state_def = _state_defs[state];
       if (!state_def._root.is_empty()) {
       if (!state_def._root.is_empty()) {
-        if (state_def._frame_stale) {
-          update_frame(state);
+        if (Thread::get_current_pipeline_stage() == 0) {
+          if (state_def._frame_stale) {
+            update_frame(state);
+          }
         }
         }
 
 
         state_def_root = state_def._root.node();
         state_def_root = state_def._root.node();
@@ -347,9 +357,8 @@ void PGItem::
 r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
 r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
                 GeomTransformer &transformer, Thread *current_thread) {
                 GeomTransformer &transformer, Thread *current_thread) {
   LightReMutexHolder holder(_lock);
   LightReMutexHolder holder(_lock);
-  StateDefs::iterator di;
-  for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
-    NodePath &root = (*di)._root;
+  for (StateDef &def : _state_defs) {
+    NodePath &root = def._root;
     if (!root.is_empty()) {
     if (!root.is_empty()) {
       PandaNode *child = root.node();
       PandaNode *child = root.node();
       CPT(RenderState) child_state = node_state->compose(child->get_state());
       CPT(RenderState) child_state = node_state->compose(child->get_state());
@@ -375,9 +384,9 @@ xform(const LMatrix4 &mat) {
   _frame.set(ll[0], ur[0], ll[2], ur[2]);
   _frame.set(ll[0], ur[0], ll[2], ur[2]);
 
 
   // Transform the individual states and their frame styles.
   // Transform the individual states and their frame styles.
-  StateDefs::iterator di;
-  for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
-    NodePath &root = (*di)._root;
+  for (size_t state = 0; state < _state_defs.size(); ++state) {
+    StateDef &def = _state_defs[state];
+    NodePath &root = def._root;
     // Apply the matrix to the previous transform.
     // Apply the matrix to the previous transform.
     root.set_transform(root.get_transform()->compose(TransformState::make_mat(mat)));
     root.set_transform(root.get_transform()->compose(TransformState::make_mat(mat)));
 
 
@@ -386,8 +395,16 @@ xform(const LMatrix4 &mat) {
     gr.apply_attribs(root.node());
     gr.apply_attribs(root.node());
 
 
     // Transform the frame style too.
     // Transform the frame style too.
-    if ((*di)._frame_style.xform(mat)) {
-      (*di)._frame_stale = true;
+    if (def._frame_style.xform(mat)) {
+#ifdef THREADED_PIPELINE
+      if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
+        update_frame((int)state);
+      }
+      else
+#endif
+      {
+        def._frame_stale = true;
+      }
     }
     }
   }
   }
   mark_internal_bounds_stale();
   mark_internal_bounds_stale();
@@ -767,9 +784,7 @@ move(const MouseWatcherParameter &param) {
  */
  */
 void PGItem::
 void PGItem::
 background_press(const MouseWatcherParameter &param) {
 background_press(const MouseWatcherParameter &param) {
-  BackgroundFocus::const_iterator fi;
-  for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
-    PGItem *item = *fi;
+  for (PGItem *item : _background_focus) {
     if (!item->get_focus()) {
     if (!item->get_focus()) {
       item->press(param, true);
       item->press(param, true);
     }
     }
@@ -781,9 +796,7 @@ background_press(const MouseWatcherParameter &param) {
  */
  */
 void PGItem::
 void PGItem::
 background_release(const MouseWatcherParameter &param) {
 background_release(const MouseWatcherParameter &param) {
-  BackgroundFocus::const_iterator fi;
-  for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
-    PGItem *item = *fi;
+  for (PGItem *item : _background_focus) {
     if (!item->get_focus()) {
     if (!item->get_focus()) {
       item->release(param, true);
       item->release(param, true);
     }
     }
@@ -795,9 +808,7 @@ background_release(const MouseWatcherParameter &param) {
  */
  */
 void PGItem::
 void PGItem::
 background_keystroke(const MouseWatcherParameter &param) {
 background_keystroke(const MouseWatcherParameter &param) {
-  BackgroundFocus::const_iterator fi;
-  for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
-    PGItem *item = *fi;
+  for (PGItem *item : _background_focus) {
     if (!item->get_focus()) {
     if (!item->get_focus()) {
       item->keystroke(param, true);
       item->keystroke(param, true);
     }
     }
@@ -809,9 +820,7 @@ background_keystroke(const MouseWatcherParameter &param) {
  */
  */
 void PGItem::
 void PGItem::
 background_candidate(const MouseWatcherParameter &param) {
 background_candidate(const MouseWatcherParameter &param) {
-  BackgroundFocus::const_iterator fi;
-  for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
-    PGItem *item = *fi;
+  for (PGItem *item : _background_focus) {
     if (!item->get_focus()) {
     if (!item->get_focus()) {
       item->candidate(param, true);
       item->candidate(param, true);
     }
     }
@@ -952,6 +961,12 @@ clear_state_def(int state) {
   _state_defs[state]._frame_stale = true;
   _state_defs[state]._frame_stale = true;
 
 
   mark_internal_bounds_stale();
   mark_internal_bounds_stale();
+
+#ifdef THREADED_PIPELINE
+  if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
+    update_frame(state);
+  }
+#endif
 }
 }
 
 
 /**
 /**
@@ -991,15 +1006,24 @@ get_frame_style(int state) {
 void PGItem::
 void PGItem::
 set_frame_style(int state, const PGFrameStyle &style) {
 set_frame_style(int state, const PGFrameStyle &style) {
   LightReMutexHolder holder(_lock);
   LightReMutexHolder holder(_lock);
-  // Get the state def node, mainly to ensure that this state is slotted and
-  // listed as having been defined.
-  NodePath &root = do_get_state_def(state);
-  nassertv(!root.is_empty());
+
+  slot_state_def(state);
+
+  if (_state_defs[state]._root.is_empty()) {
+    // Create a new node.
+    _state_defs[state]._root = NodePath("state_" + format_string(state));
+  }
 
 
   _state_defs[state]._frame_style = style;
   _state_defs[state]._frame_style = style;
   _state_defs[state]._frame_stale = true;
   _state_defs[state]._frame_stale = true;
 
 
   mark_internal_bounds_stale();
   mark_internal_bounds_stale();
+
+#ifdef THREADED_PIPELINE
+  if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
+    update_frame(state);
+  }
+#endif
 }
 }
 
 
 #ifdef HAVE_AUDIO
 #ifdef HAVE_AUDIO
@@ -1165,11 +1189,10 @@ mouse_to_local(const LPoint2 &mouse_point) const {
 }
 }
 
 
 /**
 /**
- * Called when the user changes the frame size.
+ * Called when the user changes the frame size.  Assumes the lock is held.
  */
  */
 void PGItem::
 void PGItem::
 frame_changed() {
 frame_changed() {
-  LightReMutexHolder holder(_lock);
   mark_frames_stale();
   mark_frames_stale();
   if (_notify != nullptr) {
   if (_notify != nullptr) {
     _notify->item_frame_changed(this);
     _notify->item_frame_changed(this);
@@ -1202,6 +1225,7 @@ do_get_state_def(int state) {
 
 
 /**
 /**
  * Ensures there is a slot in the array for the given state definition.
  * Ensures there is a slot in the array for the given state definition.
+ * Assumes the lock is already held.
  */
  */
 void PGItem::
 void PGItem::
 slot_state_def(int state) {
 slot_state_def(int state) {
@@ -1212,6 +1236,7 @@ slot_state_def(int state) {
 
 
 /**
 /**
  * Generates a new instance of the frame geometry for the indicated state.
  * Generates a new instance of the frame geometry for the indicated state.
+ * Assumes the lock is already held.
  */
  */
 void PGItem::
 void PGItem::
 update_frame(int state) {
 update_frame(int state) {
@@ -1234,15 +1259,26 @@ update_frame(int state) {
 
 
 /**
 /**
  * Marks all the frames in all states stale, so that they will be regenerated
  * Marks all the frames in all states stale, so that they will be regenerated
- * the next time each state is requested.
+ * the next time each state is requested.  Assumes the lock is already held.
  */
  */
 void PGItem::
 void PGItem::
 mark_frames_stale() {
 mark_frames_stale() {
-  StateDefs::iterator di;
-  for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
-    // Remove the old frame, if any.
-    (*di)._frame.remove_node();
-    (*di)._frame_stale = true;
+#ifdef THREADED_PIPELINE
+  // If we are using the threaded pipeline, we must update the frame geometry
+  // immediately on the App thread, since this class isn't pipeline-cycled.
+  if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
+    for (int state = 0; state < (int)_state_defs.size(); ++state) {
+      update_frame(state);
+    }
+  }
+  else
+#endif
+  {
+    for (StateDef &def : _state_defs) {
+      // Remove the old frame, if any.
+      def._frame.remove_node();
+      def._frame_stale = true;
+    }
   }
   }
   mark_internal_bounds_stale();
   mark_internal_bounds_stale();
 }
 }
@@ -1294,9 +1330,8 @@ clip_frame(ClipPoints &source_points, const LPlane &plane) const {
   LPoint2 last_point(source_points.back());
   LPoint2 last_point(source_points.back());
   bool last_is_in = is_right(last_point - from2d, delta2d);
   bool last_is_in = is_right(last_point - from2d, delta2d);
   bool all_in = last_is_in;
   bool all_in = last_is_in;
-  ClipPoints::const_iterator pi;
-  for (pi = source_points.begin(); pi != source_points.end(); ++pi) {
-    LPoint2 this_point(*pi);
+
+  for (LPoint2 this_point : source_points) {
     bool this_is_in = is_right(this_point - from2d, delta2d);
     bool this_is_in = is_right(this_point - from2d, delta2d);
 
 
     // There appears to be a compiler bug in gcc 4.0: we need to extract this
     // There appears to be a compiler bug in gcc 4.0: we need to extract this

+ 1 - 2
panda/src/pgui/pgScrollFrame.cxx

@@ -284,11 +284,10 @@ remanage() {
 }
 }
 
 
 /**
 /**
- * Called when the user changes the frame size.
+ * Called when the user changes the frame size.  Assumes the lock is held.
  */
  */
 void PGScrollFrame::
 void PGScrollFrame::
 frame_changed() {
 frame_changed() {
-  LightReMutexHolder holder(_lock);
   PGVirtualFrame::frame_changed();
   PGVirtualFrame::frame_changed();
   _needs_remanage = true;
   _needs_remanage = true;
   _needs_recompute_clip = true;
   _needs_recompute_clip = true;

+ 1 - 2
panda/src/pgui/pgSliderBar.cxx

@@ -551,11 +551,10 @@ recompute() {
 }
 }
 
 
 /**
 /**
- * Called when the user changes the frame size.
+ * Called when the user changes the frame size.  Assumes the lock is held.
  */
  */
 void PGSliderBar::
 void PGSliderBar::
 frame_changed() {
 frame_changed() {
-  LightReMutexHolder holder(_lock);
   PGItem::frame_changed();
   PGItem::frame_changed();
   _needs_remanage = true;
   _needs_remanage = true;
   _needs_recompute = true;
   _needs_recompute = true;

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

@@ -258,7 +258,9 @@ call_python_func(PyObject *function, PyObject *args) {
 
 
       PyGILState_Release(gstate);
       PyGILState_Release(gstate);
 
 
-      PyErr_Restore(exc, val, tb);
+      if (PyGILState_Check()) {
+        PyErr_Restore(exc, val, tb);
+      }
     } else {
     } else {
       // No exception.  Restore the thread state normally.
       // No exception.  Restore the thread state normally.
       PyGILState_Release(gstate);
       PyGILState_Release(gstate);

+ 3 - 0
panda/src/putil/CMakeLists.txt

@@ -133,6 +133,9 @@ set(P3PUTIL_IGATEEXT
   callbackObject_ext.h
   callbackObject_ext.h
   doubleBitMask_ext.h
   doubleBitMask_ext.h
   doubleBitMask_ext.I
   doubleBitMask_ext.I
+  paramPyObject.cxx
+  paramPyObject.h
+  paramPyObject.I
   pythonCallbackObject.cxx
   pythonCallbackObject.cxx
   pythonCallbackObject.h
   pythonCallbackObject.h
   sparseArray_ext.cxx
   sparseArray_ext.cxx

+ 1 - 0
panda/src/putil/p3putil_ext_composite.cxx

@@ -1,5 +1,6 @@
 #include "bamReader_ext.cxx"
 #include "bamReader_ext.cxx"
 #include "bitArray_ext.cxx"
 #include "bitArray_ext.cxx"
+#include "paramPyObject.cxx"
 #include "pythonCallbackObject.cxx"
 #include "pythonCallbackObject.cxx"
 #include "sparseArray_ext.cxx"
 #include "sparseArray_ext.cxx"
 #include "typedWritable_ext.cxx"
 #include "typedWritable_ext.cxx"

+ 31 - 0
panda/src/putil/paramPyObject.I

@@ -0,0 +1,31 @@
+/**
+ * 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 paramPyObject.I
+ * @author rdb
+ * @date 2021-03-01
+ */
+
+#include "paramPyObject.h"
+
+/**
+ * Increments the reference count.  Assumes the GIL is held.
+ */
+INLINE ParamPyObject::
+ParamPyObject(PyObject *value) : _value(value) {
+  Py_INCREF(value);
+}
+
+/**
+ * Returns a new reference to the stored value.
+ */
+INLINE PyObject *ParamPyObject::
+get_value() const {
+  Py_INCREF(_value);
+  return _value;
+}

+ 42 - 0
panda/src/putil/paramPyObject.cxx

@@ -0,0 +1,42 @@
+/**
+ * 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 paramPyObject.cxx
+ * @author rdb
+ * @date 2021-03-01
+ */
+
+#include "paramPyObject.h"
+
+TypeHandle ParamPyObject::_type_handle;
+
+/**
+ * Decrements the reference count.
+ */
+ParamPyObject::
+~ParamPyObject() {
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  PyGILState_STATE gstate;
+  gstate = PyGILState_Ensure();
+#endif
+
+  Py_DECREF(_value);
+
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  PyGILState_Release(gstate);
+#endif
+}
+
+/**
+ *
+ */
+void ParamPyObject::
+output(std::ostream &out) const {
+  out << "<" << Py_TYPE(_value)->tp_name
+      << " object at " << (void *)_value << ">";
+}

+ 61 - 0
panda/src/putil/paramPyObject.h

@@ -0,0 +1,61 @@
+/**
+ * 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 paramPyObject.h
+ * @author rdb
+ * @date 2021-03-01
+ */
+
+#ifndef PARAMPYOBJECT_H
+#define PARAMPYOBJECT_H
+
+#include "pandabase.h"
+#include "paramValue.h"
+
+#ifdef HAVE_PYTHON
+
+#include "py_panda.h"
+
+/**
+ * A class object for storing an arbitrary Python object.
+ */
+class ParamPyObject final : public ParamValueBase {
+public:
+  INLINE ParamPyObject(PyObject *value);
+  virtual ~ParamPyObject();
+
+  INLINE PyObject *get_value() const;
+
+  void output(std::ostream &out) const override;
+
+public:
+  PyObject *_value;
+
+public:
+  virtual TypeHandle get_type() const override {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() override {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ParamValueBase::init_type();
+    register_type(_type_handle, "ParamPyObject",
+                  ParamValueBase::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "paramPyObject.I"
+
+#endif  // HAVE_PYTHON
+
+#endif

+ 1 - 1
pandatool/src/assimp/assimpLoader.cxx

@@ -1035,7 +1035,7 @@ load_light(const aiLight &light) {
     LPoint3 pos (light.mPosition.x, light.mPosition.y, light.mPosition.z);
     LPoint3 pos (light.mPosition.x, light.mPosition.y, light.mPosition.z);
     LQuaternion quat;
     LQuaternion quat;
     ::look_at(quat, LPoint3(vec.x, vec.y, vec.z), LVector3::up());
     ::look_at(quat, LPoint3(vec.x, vec.y, vec.z), LVector3::up());
-    plight->set_transform(TransformState::make_pos_quat_scale(pos, quat, LVecBase3(1, 1, 1)));
+    plight->set_transform(TransformState::make_pos_quat(pos, quat));
     break; }
     break; }
 
 
   // This is a somewhat recent addition to Assimp, so let's be kind to those
   // This is a somewhat recent addition to Assimp, so let's be kind to those

+ 65 - 0
tests/event/test_futures.py

@@ -163,6 +163,71 @@ def test_coro_exception():
         task.result()
         task.result()
 
 
 
 
+def test_future_result():
+    # Cancelled
+    fut = core.AsyncFuture()
+    assert not fut.done()
+    fut.cancel()
+    with pytest.raises(CancelledError):
+        fut.result()
+
+    # None
+    fut = core.AsyncFuture()
+    fut.set_result(None)
+    assert fut.done()
+    assert fut.result() is None
+
+    # Store int
+    fut = core.AsyncFuture()
+    fut.set_result(123)
+    assert fut.result() == 123
+
+    # Store string
+    fut = core.AsyncFuture()
+    fut.set_result("test\000\u1234")
+    assert fut.result() == "test\000\u1234"
+
+    # Store TypedWritableReferenceCount
+    tex = core.Texture()
+    rc = tex.get_ref_count()
+    fut = core.AsyncFuture()
+    fut.set_result(tex)
+    assert tex.get_ref_count() == rc + 1
+    assert fut.result() == tex
+    assert tex.get_ref_count() == rc + 1
+    assert fut.result() == tex
+    assert tex.get_ref_count() == rc + 1
+    fut = None
+    assert tex.get_ref_count() == rc
+
+    # Store EventParameter (no longer gets unwrapped)
+    ep = core.EventParameter(0.5)
+    fut = core.AsyncFuture()
+    fut.set_result(ep)
+    assert fut.result() is ep
+    assert fut.result() is ep
+
+    # Store TypedObject
+    dg = core.Datagram(b"test")
+    fut = core.AsyncFuture()
+    fut.set_result(dg)
+    assert fut.result() == dg
+    assert fut.result() == dg
+
+    # Store arbitrary Python object
+    obj = object()
+    rc = sys.getrefcount(obj)
+    fut = core.AsyncFuture()
+    fut.set_result(obj)
+    assert sys.getrefcount(obj) == rc + 1
+    assert fut.result() is obj
+    assert sys.getrefcount(obj) == rc + 1
+    assert fut.result() is obj
+    assert sys.getrefcount(obj) == rc + 1
+    fut = None
+    assert sys.getrefcount(obj) == rc
+
+
 def test_future_gather():
 def test_future_gather():
     fut1 = core.AsyncFuture()
     fut1 = core.AsyncFuture()
     fut2 = core.AsyncFuture()
     fut2 = core.AsyncFuture()

+ 20 - 0
tests/linmath/test_lvector2.py

@@ -1,6 +1,7 @@
 from math import floor, ceil
 from math import floor, ceil
 
 
 from panda3d.core import Vec2, Vec3, Vec4, Vec2F, Vec2D
 from panda3d.core import Vec2, Vec3, Vec4, Vec2F, Vec2D
+from panda3d import core
 import pytest
 import pytest
 
 
 
 
@@ -117,3 +118,22 @@ def test_vec2_nan():
     assert not Vec2D(inf, 0).is_nan()
     assert not Vec2D(inf, 0).is_nan()
     assert not Vec2D(inf, inf).is_nan()
     assert not Vec2D(inf, inf).is_nan()
     assert not Vec2D(-inf, 0).is_nan()
     assert not Vec2D(-inf, 0).is_nan()
+
+
[email protected]("type", (core.LVecBase2f, core.LVecBase2d, core.LVecBase2i))
+def test_vec4_floordiv(type):
+    with pytest.raises(ZeroDivisionError):
+        type(1, 2) // 0
+
+    for i in range(-11, 11):
+        for j in range(1, 11):
+            assert (type(i) // j).x == i // j
+            assert (type(i) // -j).x == i // -j
+
+            v = type(i)
+            v //= j
+            assert v.x == i // j
+
+            v = type(i)
+            v //= -j
+            assert v.x == i // -j

+ 20 - 0
tests/linmath/test_lvector3.py

@@ -1,6 +1,7 @@
 from math import floor, ceil
 from math import floor, ceil
 
 
 from panda3d.core import Vec2, Vec3, Vec3F, Vec3D
 from panda3d.core import Vec2, Vec3, Vec3F, Vec3D
+from panda3d import core
 import pytest
 import pytest
 
 
 
 
@@ -102,3 +103,22 @@ def test_vec3_compare():
     assert Vec3(0, 0, 1).compare_to(Vec3(1, 0, 0)) == -1
     assert Vec3(0, 0, 1).compare_to(Vec3(1, 0, 0)) == -1
     assert Vec3(0, 0, 1).compare_to(Vec3(0, 1, 0)) == -1
     assert Vec3(0, 0, 1).compare_to(Vec3(0, 1, 0)) == -1
     assert Vec3(0, 0, 1).compare_to(Vec3(0, 0, 1)) == 0
     assert Vec3(0, 0, 1).compare_to(Vec3(0, 0, 1)) == 0
+
+
[email protected]("type", (core.LVecBase3f, core.LVecBase3d, core.LVecBase3i))
+def test_vec3_floordiv(type):
+    with pytest.raises(ZeroDivisionError):
+        type(1, 2, 3) // 0
+
+    for i in range(-11, 11):
+        for j in range(1, 11):
+            assert (type(i) // j).x == i // j
+            assert (type(i) // -j).x == i // -j
+
+            v = type(i)
+            v //= j
+            assert v.x == i // j
+
+            v = type(i)
+            v //= -j
+            assert v.x == i // -j

+ 20 - 0
tests/linmath/test_lvector4.py

@@ -1,6 +1,7 @@
 from math import floor, ceil
 from math import floor, ceil
 
 
 from panda3d.core import Vec2, Vec3, Vec4, Vec4F, Vec4D
 from panda3d.core import Vec2, Vec3, Vec4, Vec4F, Vec4D
+from panda3d import core
 import pytest
 import pytest
 
 
 
 
@@ -118,3 +119,22 @@ def test_vec4_compare():
     assert Vec4(0, 0, 0, 1).compare_to(Vec4(0, 1, 0, 0)) == -1
     assert Vec4(0, 0, 0, 1).compare_to(Vec4(0, 1, 0, 0)) == -1
     assert Vec4(0, 0, 0, 1).compare_to(Vec4(0, 0, 1, 0)) == -1
     assert Vec4(0, 0, 0, 1).compare_to(Vec4(0, 0, 1, 0)) == -1
     assert Vec4(0, 0, 0, 1).compare_to(Vec4(0, 0, 0, 1)) == 0
     assert Vec4(0, 0, 0, 1).compare_to(Vec4(0, 0, 0, 1)) == 0
+
+
[email protected]("type", (core.LVecBase4f, core.LVecBase4d, core.LVecBase4i))
+def test_vec4_floordiv(type):
+    with pytest.raises(ZeroDivisionError):
+        type(1, 2, 3, 4) // 0
+
+    for i in range(-11, 11):
+        for j in range(1, 11):
+            assert (type(i) // j).x == i // j
+            assert (type(i) // -j).x == i // -j
+
+            v = type(i)
+            v //= j
+            assert v.x == i // j
+
+            v = type(i)
+            v //= -j
+            assert v.x == i // -j

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно