diff --git a/contrib/src/rplight/rpLight.I b/contrib/src/rplight/rpLight.I index c0e2422651..c405437906 100644 --- a/contrib/src/rplight/rpLight.I +++ b/contrib/src/rplight/rpLight.I @@ -148,7 +148,7 @@ inline void RPLight::assign_slot(int slot) { * changed. This will cause all shadow sources to be updated, emitting a * shadow update. Be careful when calling this method if you don't want all * sources to get updated. If you only have to invalidate a single shadow source, - * use get_shadow_source(n)->set_needs_update(true). + * use `get_shadow_source(n)->set_needs_update(true)`. */ inline void RPLight::invalidate_shadows() { for (size_t i = 0; i < _shadow_sources.size(); ++i) { @@ -296,14 +296,14 @@ inline bool RPLight::get_casts_shadows() const { } /** - * @brief Sets the lights shadow map resolution - * @details This sets the lights shadow map resolution. This has no effect + * @brief Sets the light's shadow map resolution + * @details This sets the light's shadow map resolution. This has no effect * when the light is not told to cast shadows (Use RPLight::set_casts_shadows). * - * When calling this on a light with multiple shadow sources (e.g. PointLight), - * this controls the resolution of each source. If the light has 6 shadow sources, - * and you use a resolution of 512x512, the lights shadow map will occur a - * space of 6 * 512x512 maps in the shadow atlas. + * When calling this on a light with multiple shadow sources (e.g. + * RPPointLight), this controls the resolution of each source. If the light + * has 6 shadow sources, and you use a resolution of 512x512, the light's + * shadow map will occupy a space of 6 * 512x512 maps in the shadow atlas. * * @param resolution Resolution of the shadow map in pixels */ @@ -326,14 +326,14 @@ inline size_t RPLight::get_shadow_map_resolution() const { } /** - * @brief Sets the ies profile + * @brief Sets the IES profile * @details This sets the ies profile of the light. The parameter should be a * handle previously returned by RenderPipeline.load_ies_profile. Using a * value of -1 indicates no ies profile. * - * Notice that for ies profiles which cover a whole range, you should use - * PointLights, whereas for ies profiles which only cover the lower hemisphere - * you should use SpotLights for the best performance. + * Notice that for IES profiles which cover a whole range, you should use an + * RPPointLight, whereas for ies profiles which only cover the lower + * hemisphere you should use an RPSpotLight for the best performance. * * @param profile IES Profile handle */ @@ -343,8 +343,8 @@ inline void RPLight::set_ies_profile(int profile) { } /** - * @brief Returns the lights ies profile - * @details This returns the ies profile of a light, previously set with + * @brief Returns the light's IES profile + * @details This returns the IES profile of a light, previously set with * RPLight::set_ies_profile. In case no ies profile was set, returns -1. * * @return IES Profile handle @@ -354,20 +354,20 @@ inline int RPLight::get_ies_profile() const { } /** - * @brief Returns whether the light has an ies profile assigned - * @details This returns whether the light has an ies profile assigned, + * @brief Returns whether the light has an IES profile assigned + * @details This returns whether the light has an IES profile assigned, * previously done with RPLight::set_ies_profile. * - * @return true if the light has an ies profile assigned, false otherwise + * @return true if the light has an IES profile assigned, false otherwise */ inline bool RPLight::has_ies_profile() const { return _ies_profile >= 0; } /** - * @brief Clears the ies profile - * @details This clears the ies profile of the light, telling it to no longer - * use an ies profile, and instead use the default attenuation. + * @brief Clears the IES profile + * @details This clears the IES profile of the light, telling it to no longer + * use an IES profile, and instead use the default attenuation. */ inline void RPLight::clear_ies_profile() { set_ies_profile(-1); @@ -377,7 +377,7 @@ inline void RPLight::clear_ies_profile() { * @brief Sets the near plane of the light * @details This sets the near plane of all shadow sources of the light. It has * no effects if the light does not cast shadows. This prevents artifacts from - * objects near to the light. It behaves like Lens::set_near_plane. + * objects near to the light. It behaves like Lens::set_near(). * * It can also help increasing shadow map precision, low near planes will * cause the precision to suffer. Try setting the near plane as big as possible. @@ -394,7 +394,7 @@ inline void RPLight::set_near_plane(float near_plane) { /** * @brief Returns the near plane of the light - * @details This returns the lights near plane, previously set with + * @details This returns the light's near plane, previously set with * RPLight::set_near_plane. If the light does not cast shadows, this value * is meaningless. * diff --git a/direct/src/actor/Actor.py b/direct/src/actor/Actor.py index 7aeed5d535..9cd05f13c7 100644 --- a/direct/src/actor/Actor.py +++ b/direct/src/actor/Actor.py @@ -1,4 +1,8 @@ -"""Actor module: contains the Actor class""" +"""Actor module: contains the Actor class. + +See the :ref:`models-and-actors` page in the Programming Guide to learn +more about loading models and animated actors. +""" __all__ = ['Actor'] diff --git a/direct/src/actor/__init__.py b/direct/src/actor/__init__.py index 8a080475bb..14d116777f 100644 --- a/direct/src/actor/__init__.py +++ b/direct/src/actor/__init__.py @@ -4,4 +4,7 @@ distributed variant thereof. Actor is a high-level interface around the lower-level :class:`panda3d.core.Character` implementation. It loads and controls an animated character and manages the animations playing on it. + +See the :ref:`models-and-actors` page in the Programming Guide to learn +more about loading models and animated actors. """ diff --git a/direct/src/directscripts/Doxyfile.cxx b/direct/src/directscripts/Doxyfile.cxx index db71c81b3b..11d5fc9bc2 100644 --- a/direct/src/directscripts/Doxyfile.cxx +++ b/direct/src/directscripts/Doxyfile.cxx @@ -789,6 +789,7 @@ RECURSIVE = YES EXCLUDE = dtool/src/parser-inc \ dtool/src/cppparser \ dtool/src/interrogate \ + direct/src/directscripts \ direct/src/plugin \ direct/src/plugin_standalone \ direct/src/plugin_npapi \ diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 48e2bd70b6..efa3a861c7 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -369,12 +369,13 @@ class build_apps(setuptools.Command): os.remove(os.path.join(whldir, whl)) pip_args = [ + '--disable-pip-version-check', 'download', '-d', whldir, '-r', self.requirements_path, '--only-binary', ':all:', '--platform', platform, - '--abi', abi_tag + '--abi', abi_tag, ] if self.use_optimized_wheels: diff --git a/direct/src/distributed/__init__.py b/direct/src/distributed/__init__.py index cf5b4bc502..e20792afad 100644 --- a/direct/src/distributed/__init__.py +++ b/direct/src/distributed/__init__.py @@ -2,4 +2,7 @@ This package contains an implementation of the Distributed Networking API, a high-level networking system that automatically propagates changes made on distributed objects to interested clients. + +See the :ref:`distributed-networking` section of the Programming Guide to +learn more about the distributed networking system. """ diff --git a/direct/src/filter/CommonFilters.py b/direct/src/filter/CommonFilters.py index 1ba9ec91d4..26601ced2e 100644 --- a/direct/src/filter/CommonFilters.py +++ b/direct/src/filter/CommonFilters.py @@ -1,7 +1,8 @@ """ Class CommonFilters implements certain common image -postprocessing filters. +postprocessing filters. See the :ref:`common-image-filters` page for +more information about how to use these filters. It is not ideal that these filters are all included in a single monolithic module. Unfortunately, when you want to apply two filters @@ -27,6 +28,7 @@ from panda3d.core import LVecBase4, LPoint2 from panda3d.core import Filename from panda3d.core import AuxBitplaneAttrib from panda3d.core import Texture, Shader, ATSNone +from panda3d.core import FrameBufferProperties import os CARTOON_BODY=""" @@ -177,7 +179,15 @@ class CommonFilters: self.textures[tex].setWrapU(Texture.WMClamp) self.textures[tex].setWrapV(Texture.WMClamp) - self.finalQuad = self.manager.renderSceneInto(textures = self.textures, auxbits=auxbits) + fbprops = None + clamping = None + if "HighDynamicRange" in configuration: + fbprops = FrameBufferProperties() + fbprops.setFloatColor(True) + fbprops.setSrgbColor(False) + clamping = False + + self.finalQuad = self.manager.renderSceneInto(textures = self.textures, auxbits=auxbits, fbprops=fbprops, clamping=clamping) if (self.finalQuad == None): self.cleanup() return False @@ -255,6 +265,17 @@ class CommonFilters: texcoordSets = list(enumerate(texcoordPadding.keys())) text = "//Cg\n" + if "HighDynamicRange" in configuration: + text += "static const float3x3 aces_input_mat = {\n" + text += " {0.59719, 0.35458, 0.04823},\n" + text += " {0.07600, 0.90834, 0.01566},\n" + text += " {0.02840, 0.13383, 0.83777},\n" + text += "};\n" + text += "static const float3x3 aces_output_mat = {\n" + text += " { 1.60475, -0.53108, -0.07367},\n" + text += " {-0.10208, 1.10813, -0.00605},\n" + text += " {-0.00327, -0.07276, 1.07602},\n" + text += "};\n" text += "void vshader(float4 vtx_position : POSITION,\n" text += " out float4 l_position : POSITION,\n" @@ -301,6 +322,10 @@ class CommonFilters: if ("VolumetricLighting" in configuration): text += " uniform float4 k_casterpos,\n" text += " uniform float4 k_vlparams,\n" + + if ("ExposureAdjust" in configuration): + text += " uniform float k_exposure,\n" + text += " out float4 o_color : COLOR)\n" text += "{\n" text += " o_color = tex2D(k_txcolor, %s);\n" % (texcoords["color"]) @@ -332,6 +357,14 @@ class CommonFilters: text += " }\n" text += " o_color += float4(vlcolor * k_vlparams.z, 1);\n" + if ("ExposureAdjust" in configuration): + text += " o_color.rgb *= k_exposure;\n" + + # With thanks to Stephen Hill! + if ("HighDynamicRange" in configuration): + text += " float3 aces_color = mul(aces_input_mat, o_color.rgb);\n" + text += " o_color.rgb = saturate(mul(aces_output_mat, (aces_color * (aces_color + 0.0245786f) - 0.000090537f) / (aces_color * (0.983729f * aces_color + 0.4329510f) + 0.238081f)));\n" + if ("GammaAdjust" in configuration): gamma = configuration["GammaAdjust"] if gamma == 0.5: @@ -341,6 +374,11 @@ class CommonFilters: elif gamma != 1.0: text += " o_color.rgb = pow(o_color.rgb, %ff);\n" % (gamma) + if ("SrgbEncode" in configuration): + text += " o_color.r = (o_color.r < 0.0031308) ? (o_color.r * 12.92) : (1.055 * pow(o_color.r, 0.41666) - 0.055);\n" + text += " o_color.g = (o_color.g < 0.0031308) ? (o_color.g * 12.92) : (1.055 * pow(o_color.g, 0.41666) - 0.055);\n" + text += " o_color.b = (o_color.b < 0.0031308) ? (o_color.b * 12.92) : (1.055 * pow(o_color.b, 0.41666) - 0.055);\n" + if ("Inverted" in configuration): text += " o_color = float4(1, 1, 1, 1) - o_color;\n" text += "}\n" @@ -386,6 +424,11 @@ class CommonFilters: self.ssao[0].setShaderInput("params1", config.numsamples, -float(config.amount) / config.numsamples, config.radius, 0) self.ssao[0].setShaderInput("params2", config.strength, config.falloff, 0, 0) + if (changed == "ExposureAdjust") or fullrebuild: + if ("ExposureAdjust" in configuration): + stops = configuration["ExposureAdjust"] + self.finalQuad.setShaderInput("exposure", 2 ** stops) + self.update() return True @@ -547,6 +590,73 @@ class CommonFilters: return self.reconfigure((old_gamma != 1.0), "GammaAdjust") return True + def setSrgbEncode(self, force=False): + """ Applies the inverse sRGB EOTF to the output, unless the window + already has an sRGB framebuffer, in which case this filter refuses to + apply, to prevent accidental double-application. + + Set the force argument to True to force it to be applied in all cases. + + .. versionadded:: 1.10.7 + """ + new_enable = force or not self.manager.win.getFbProperties().getSrgbColor() + old_enable = self.configuration.get("SrgbEncode", False) + if new_enable and not old_enable: + self.configuration["SrgbEncode"] = True + return self.reconfigure(True, "SrgbEncode") + elif not new_enable and old_enable: + del self.configuration["SrgbEncode"] + return new_enable + + def delSrgbEncode(self): + """ Reverses the effects of setSrgbEncode. """ + if ("SrgbEncode" in self.configuration): + old_enable = self.configuration["SrgbEncode"] + del self.configuration["SrgbEncode"] + return self.reconfigure(old_enable, "SrgbEncode") + return True + + def setHighDynamicRange(self): + """ Enables HDR rendering by using a floating-point framebuffer, + disabling color clamping on the main scene, and applying a tone map + operator (ACES). + + It may also be necessary to use setExposureAdjust to perform exposure + compensation on the scene, depending on the lighting intensity. + + .. versionadded:: 1.10.7 + """ + + fullrebuild = (("HighDynamicRange" in self.configuration) is False) + self.configuration["HighDynamicRange"] = 1 + return self.reconfigure(fullrebuild, "HighDynamicRange") + + def delHighDynamicRange(self): + if ("HighDynamicRange" in self.configuration): + del self.configuration["HighDynamicRange"] + return self.reconfigure(True, "HighDynamicRange") + return True + + def setExposureAdjust(self, stops): + """ Sets a relative exposure adjustment to multiply with the result of + rendering the scene, in stops. A value of 0 means no adjustment, a + positive value will result in a brighter image. Useful in conjunction + with HDR, see setHighDynamicRange. + + .. versionadded:: 1.10.7 + """ + old_stops = self.configuration.get("ExposureAdjust") + if old_stops != stops: + self.configuration["ExposureAdjust"] = stops + return self.reconfigure(old_stops is None, "ExposureAdjust") + return True + + def delExposureAdjust(self): + if ("ExposureAdjust" in self.configuration): + del self.configuration["ExposureAdjust"] + return self.reconfigure(True, "ExposureAdjust") + return True + #snake_case alias: del_cartoon_ink = delCartoonInk set_half_pixel_shift = setHalfPixelShift @@ -555,7 +665,6 @@ class CommonFilters: del_inverted = delInverted del_view_glow = delViewGlow set_volumetric_lighting = setVolumetricLighting - del_gamma_adjust = delGammaAdjust set_bloom = setBloom set_view_glow = setViewGlow set_ambient_occlusion = setAmbientOcclusion @@ -566,3 +675,10 @@ class CommonFilters: del_blur_sharpen = delBlurSharpen del_volumetric_lighting = delVolumetricLighting set_gamma_adjust = setGammaAdjust + del_gamma_adjust = delGammaAdjust + set_srgb_encode = setSrgbEncode + del_srgb_encode = delSrgbEncode + set_exposure_adjust = setExposureAdjust + del_exposure_adjust = delExposureAdjust + set_high_dynamic_range = setHighDynamicRange + del_high_dynamic_range = delHighDynamicRange diff --git a/direct/src/filter/FilterManager.py b/direct/src/filter/FilterManager.py index c20cbed139..fd3cda3921 100644 --- a/direct/src/filter/FilterManager.py +++ b/direct/src/filter/FilterManager.py @@ -3,6 +3,8 @@ The FilterManager is a convenience class that helps with the creation of render-to-texture buffers for image postprocessing applications. +See :ref:`generalized-image-filters` for information on how to use this class. + Still need to implement: * Make sure sort-order of buffers is correct. @@ -22,6 +24,7 @@ from panda3d.core import WindowProperties, FrameBufferProperties from panda3d.core import Camera from panda3d.core import OrthographicLens from panda3d.core import AuxBitplaneAttrib +from panda3d.core import LightRampAttrib from direct.directnotify.DirectNotifyGlobal import * from direct.showbase.DirectObject import DirectObject @@ -124,7 +127,7 @@ class FilterManager(DirectObject): return winx,winy - def renderSceneInto(self, depthtex=None, colortex=None, auxtex=None, auxbits=0, textures=None, fbprops=None): + def renderSceneInto(self, depthtex=None, colortex=None, auxtex=None, auxbits=0, textures=None, fbprops=None, clamping=None): """ Causes the scene to be rendered into the supplied textures instead of into the original window. Puts a fullscreen quad @@ -207,6 +210,9 @@ class FilterManager(DirectObject): #cs.setShaderAuto() if (auxbits): cs.setAttrib(AuxBitplaneAttrib.make(auxbits)) + if clamping is False: + # Disables clamping in the shader generator. + cs.setAttrib(LightRampAttrib.make_identity()) self.camera.node().setInitialState(cs.getState()) quadcamnode = Camera("filter-quad-cam") diff --git a/direct/src/filter/__init__.py b/direct/src/filter/__init__.py index 4acb30b438..52644c9e19 100644 --- a/direct/src/filter/__init__.py +++ b/direct/src/filter/__init__.py @@ -8,4 +8,7 @@ texture values as desired. The :class:`.CommonFilters` class contains various filters that are provided out of the box, whereas the :class:`.FilterManager` class is a lower-level class that allows you to set up your own filters. + +See the :ref:`render-to-texture-and-image-postprocessing` section of the +Programming Guide to learn more about image postprocessing in Panda3D. """ diff --git a/direct/src/showbase/__init__.py b/direct/src/showbase/__init__.py index e69de29bb2..0222b80817 100644 --- a/direct/src/showbase/__init__.py +++ b/direct/src/showbase/__init__.py @@ -0,0 +1,3 @@ +""" This package contains `.ShowBase`, an application framework responsible +for opening a graphical display, setting up input devices and creating +the scene graph. """ diff --git a/direct/src/stdpy/__init__.py b/direct/src/stdpy/__init__.py index 9e4739449c..d6c3be8322 100644 --- a/direct/src/stdpy/__init__.py +++ b/direct/src/stdpy/__init__.py @@ -1,5 +1,6 @@ """ This package contains various modules that provide a drop-in substitute for some of the built-in Python modules. These substitutes make better -use of Panda3D's virtual file system and threading system. +use of Panda3D's :ref:`Virtual File System ` and +:ref:`threading` system. """ diff --git a/direct/src/tkpanels/Inspector.py b/direct/src/tkpanels/Inspector.py index 4480332699..ddbbe30dc1 100644 --- a/direct/src/tkpanels/Inspector.py +++ b/direct/src/tkpanels/Inspector.py @@ -1,8 +1,15 @@ """Inspectors allow you to visually browse through the members of -various python objects. To open an inspector, import this module, and -execute inspector.inspect(anObject) I start IDLE with this command -line: idle.py -c "from inspector import inspect" -so that I can just type: inspect(anObject) any time.""" +various Python objects. To open an inspector, import this module, and +execute ``inspector.inspect(anObject)``. + +I start IDLE with this command line:: + + idle.py -c "from inspector import inspect" + +so that I can just type: ``inspect(anObject)`` any time. + +See :ref:`inspection-utilities` for more information. +""" __all__ = ['inspect', 'inspectorFor', 'Inspector', 'ModuleInspector', 'ClassInspector', 'InstanceInspector', 'FunctionInspector', 'InstanceMethodInspector', 'CodeInspector', 'ComplexInspector', 'DictionaryInspector', 'SequenceInspector', 'SliceInspector', 'InspectorWindow'] @@ -13,6 +20,9 @@ import Pmw ### public API def inspect(anObject): + """Opens up a window for visually inspecting the details of a given Python + object. See :ref:`inspection-utilities`. + """ inspector = inspectorFor(anObject) inspectorWindow = InspectorWindow(inspector) inspectorWindow.open() @@ -437,7 +447,3 @@ class InspectorWindow: label = item, command = lambda p = part, f = func: f(p)) return popupMenu - - - - diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 9df12dddfa..da19170534 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -6567,7 +6567,9 @@ write_make_seq(ostream &out, Object *obj, const std::string &ClassName, "\n"; if ((elem_getter->_args_type & AT_varargs) == AT_varargs) { + out << "#if defined(Py_TRACE_REFS) || PY_VERSION_HEX < 0x03090000\n"; out << " _Py_ForgetReference((PyObject *)&args);\n"; + out << "#endif\n"; } out << diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index 725e9bdedd..f83ea7a367 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -10,6 +10,9 @@ #ifdef HAVE_PYTHON +#define _STRINGIFY_VERSION(a, b) (#a "." #b) +#define STRINGIFY_VERSION(a, b) _STRINGIFY_VERSION(a, b) + using std::string; /** @@ -521,6 +524,8 @@ Dtool_TypeMap *Dtool_GetGlobalTypeMap() { } } +#define PY_MAJOR_VERSION_STR #PY_MAJOR_VERSION "." #PY_MINOR_VERSION + #if PY_MAJOR_VERSION >= 3 PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], PyModuleDef *module_def) { #else @@ -528,15 +533,18 @@ PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], const char *modulen #endif // Check the version so we can print a helpful error if it doesn't match. string version = Py_GetVersion(); + size_t version_len = version.find('.', 2); + if (version_len != string::npos) { + version.resize(version_len); + } - if (version[0] != '0' + PY_MAJOR_VERSION || - version[2] != '0' + PY_MINOR_VERSION) { + if (version != STRINGIFY_VERSION(PY_MAJOR_VERSION, PY_MINOR_VERSION)) { // Raise a helpful error message. We can safely do this because the // signature and behavior for PyErr_SetString has remained consistent. std::ostringstream errs; errs << "this module was compiled for Python " << PY_MAJOR_VERSION << "." << PY_MINOR_VERSION << ", which is " - << "incompatible with Python " << version.substr(0, 3); + << "incompatible with Python " << version; string error = errs.str(); PyErr_SetString(PyExc_ImportError, error.c_str()); return nullptr; diff --git a/dtool/src/prc/configPageManager.I b/dtool/src/prc/configPageManager.I index 30e6c64950..2d27874573 100644 --- a/dtool/src/prc/configPageManager.I +++ b/dtool/src/prc/configPageManager.I @@ -12,7 +12,7 @@ */ /** - * Returns true if the implicit *.prc files have already been loaded, false + * Returns true if the implicit `*.prc` files have already been loaded, false * otherwise. Normally this will only be false briefly before startup. */ INLINE bool ConfigPageManager:: @@ -21,7 +21,7 @@ loaded_implicit_pages() const { } /** - * Searches the PRC_DIR and/or PRC_PATH directories for *.prc files and loads + * Searches the PRC_DIR and/or PRC_PATH directories for `*.prc` files and loads * them in as pages. This is normally called automatically at startup time, * when the first variable's value is referenced. See also * reload_implicit_pages(). @@ -46,9 +46,9 @@ get_search_path() { } /** - * Returns the number of patterns, like "*.prc", that are compiled in that + * Returns the number of patterns, like `*.prc`, that are compiled in that * will be searched for as default config filenames. Normally there is only - * one pattern, and it is "*.prc", but others may be specified with the + * one pattern, and it is `*.prc`, but others may be specified with the * PRC_FILENAME variable in Config.pp. */ INLINE size_t ConfigPageManager:: @@ -67,7 +67,7 @@ get_prc_pattern(size_t n) const { } /** - * Returns the number of patterns, like "*.pre", that are compiled in that + * Returns the number of patterns, like `*.pre`, that are compiled in that * will be searched for as special config files that are understood to be * encrypted. */ @@ -87,7 +87,7 @@ get_prc_encrypted_pattern(size_t n) const { } /** - * Returns the number of patterns, like "*.exe", that are compiled in that + * Returns the number of patterns, like `*.exe`, that are compiled in that * will be searched for as special config files that are to be executed as a * program, and their output taken to be input. This is normally empty. */ diff --git a/dtool/src/prc/configVariableManager.cxx b/dtool/src/prc/configVariableManager.cxx index 774dc19f6b..86375764e4 100644 --- a/dtool/src/prc/configVariableManager.cxx +++ b/dtool/src/prc/configVariableManager.cxx @@ -82,7 +82,7 @@ make_variable(const string &name) { * Defines a variable "template" to match against dynamically-defined * variables that may or may not be created in the future. * - * The template consists of a glob pattern, e.g. "notify-level-*", which will + * The template consists of a glob pattern, e.g. `notify-level-*`, which will * be tested against any config variable passed to a future call to * make_variable(). If the pattern matches, the returned ConfigVariableCore * is copied to define the new variable, instead of creating a default, empty @@ -92,7 +92,7 @@ make_variable(const string &name) { * all have similar properties, and all may not be created at the same time. * It is especially useful to avoid cluttering up the list of available * variables with user-declared variables that have not been defined yet by - * the application (e.g. "egg-object-type-*"). + * the application (e.g. `egg-object-type-*`). * * This method basically pre-defines all variables that match the specified * glob pattern. diff --git a/dtool/src/prc/streamReader.h b/dtool/src/prc/streamReader.h index 4d32e0616e..e05e8ea0e2 100644 --- a/dtool/src/prc/streamReader.h +++ b/dtool/src/prc/streamReader.h @@ -68,10 +68,10 @@ PUBLISHED: BLOCKING void skip_bytes(size_t size); BLOCKING size_t extract_bytes(unsigned char *into, size_t size); - EXTENSION(BLOCKING PyObject *extract_bytes(size_t size)); + EXTENSION(PyObject *extract_bytes(size_t size)); - EXTENSION(BLOCKING PyObject *readline()); - EXTENSION(BLOCKING PyObject *readlines()); + EXTENSION(PyObject *readline()); + EXTENSION(PyObject *readlines()); public: BLOCKING vector_uchar extract_bytes(size_t size); diff --git a/dtool/src/prc/streamReader_ext.cxx b/dtool/src/prc/streamReader_ext.cxx index 767e5890eb..b64128aa25 100644 --- a/dtool/src/prc/streamReader_ext.cxx +++ b/dtool/src/prc/streamReader_ext.cxx @@ -15,6 +15,8 @@ #ifdef HAVE_PYTHON +#include "vector_string.h" + /** * Extracts the indicated number of bytes in the stream and returns them as a * string (or bytes, in Python 3). Returns empty string at end-of-file. @@ -23,13 +25,25 @@ PyObject *Extension:: extract_bytes(size_t size) { std::istream *in = _this->get_istream(); if (in->eof() || in->fail() || size == 0) { + // Note that this is only safe to call with size 0 while the GIL is held. return PyBytes_FromStringAndSize(nullptr, 0); } PyObject *bytes = PyBytes_FromStringAndSize(nullptr, size); - in->read(PyBytes_AS_STRING(bytes), size); + char *buffer = (char *)PyBytes_AS_STRING(bytes); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS +#endif // HAVE_THREADS && !SIMPLE_THREADS + + in->read(buffer, size); size_t read_bytes = in->gcount(); +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + Py_BLOCK_THREADS +#endif // HAVE_THREADS && !SIMPLE_THREADS + if (read_bytes == size || _PyBytes_Resize(&bytes, read_bytes) == 0) { return bytes; } else { @@ -47,6 +61,11 @@ extract_bytes(size_t size) { */ PyObject *Extension:: readline() { +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS +#endif // HAVE_THREADS && !SIMPLE_THREADS + std::istream *in = _this->get_istream(); std::string line; @@ -60,6 +79,10 @@ readline() { ch = in->get(); } +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + Py_BLOCK_THREADS +#endif // HAVE_THREADS && !SIMPLE_THREADS + return PyBytes_FromStringAndSize(line.data(), line.size()); } @@ -69,18 +92,46 @@ readline() { */ PyObject *Extension:: readlines() { - PyObject *lst = PyList_New(0); +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS +#endif // HAVE_THREADS && !SIMPLE_THREADS + + std::istream *in = _this->get_istream(); + vector_string lines; + + while (true) { + std::string line; + int ch = in->get(); + while (ch != EOF && !in->fail()) { + line += ch; + if (ch == '\n' || in->eof()) { + // Here's the newline character. + break; + } + ch = in->get(); + } + + if (line.empty()) { + break; + } + + lines.push_back(std::move(line)); + } + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + Py_BLOCK_THREADS +#endif // HAVE_THREADS && !SIMPLE_THREADS + + PyObject *lst = PyList_New(lines.size()); if (lst == nullptr) { return nullptr; } - PyObject *py_line = readline(); - - while (PyBytes_GET_SIZE(py_line) > 0) { - PyList_Append(lst, py_line); - Py_DECREF(py_line); - - py_line = readline(); + Py_ssize_t i = 0; + for (const std::string &line : lines) { + PyObject *py_line = PyBytes_FromStringAndSize(line.data(), line.size()); + PyList_SET_ITEM(lst, i++, py_line); } return lst; diff --git a/dtool/src/prc/streamReader_ext.h b/dtool/src/prc/streamReader_ext.h index fe74694f8b..a04cdf8fdd 100644 --- a/dtool/src/prc/streamReader_ext.h +++ b/dtool/src/prc/streamReader_ext.h @@ -29,9 +29,9 @@ template<> class Extension : public ExtensionBase { public: - BLOCKING PyObject *extract_bytes(size_t size); - BLOCKING PyObject *readline(); - BLOCKING PyObject *readlines(); + PyObject *extract_bytes(size_t size); + PyObject *readline(); + PyObject *readlines(); }; #endif // HAVE_PYTHON diff --git a/makepanda/makepackage.py b/makepanda/makepackage.py index ec6fa85d99..243ebc6fa7 100755 --- a/makepanda/makepackage.py +++ b/makepanda/makepackage.py @@ -158,10 +158,13 @@ def MakeInstallerNSIS(version, file, title, installdir, compressor="lzma", **kwa # Are we shipping a version of Python? if os.path.isfile(os.path.join(outputdir, "python", "python.exe")): py_dlls = glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9].dll")) \ - + glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9]_d.dll")) + + glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9]_d.dll")) \ + + glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9][0-9].dll")) \ + + glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9][0-9]_d.dll")) assert py_dlls py_dll = os.path.basename(py_dlls[0]) - pyver = py_dll[6] + "." + py_dll[7] + py_dllver = py_dll.strip(".DHLNOPTY_dhlnopty") + pyver = py_dllver[0] + '.' + py_dllver[1:] if GetTargetArch() != 'x64': pyver += '-32' @@ -723,7 +726,7 @@ def MakeInstallerFreeBSD(version, python_versions=[], **kwargs): oscmd("rm -f %s/tmp/python_dep" % outputdir) if "PYTHONVERSION" in SDK: - pyver_nodot = SDK["PYTHONVERSION"][6:9:2] + pyver_nodot = SDK["PYTHONVERSION"][6:].replace('.', '') else: pyver_nodot = "%d%d" % (sys.version_info[:2]) diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 25fab1437c..61a4332b07 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -2058,9 +2058,11 @@ def SdkLocatePython(prefer_thirdparty_python=False): # Determine which version it is by checking which dll is in the directory. if (GetOptimize() <= 2): - py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9]_d.dll") + py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9]_d.dll") + \ + glob.glob(SDK["PYTHON"] + "/python[0-9][0-9][0-9]_d.dll") else: - py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9].dll") + py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9].dll") + \ + glob.glob(SDK["PYTHON"] + "/python[0-9][0-9][0-9].dll") if len(py_dlls) == 0: exit("Could not find the Python dll in %s." % (SDK["PYTHON"])) @@ -2068,24 +2070,29 @@ def SdkLocatePython(prefer_thirdparty_python=False): exit("Found multiple Python dlls in %s." % (SDK["PYTHON"])) py_dll = os.path.basename(py_dlls[0]) - ver = py_dll[6] + "." + py_dll[7] + py_dllver = py_dll.strip(".DHLNOPTY_dhlnopty") + ver = py_dllver[0] + '.' + py_dllver[1:] SDK["PYTHONVERSION"] = "python" + ver os.environ["PYTHONHOME"] = SDK["PYTHON"] - if sys.version[:3] != ver: - Warn("running makepanda with Python %s, but building Panda3D with Python %s." % (sys.version[:3], ver)) + running_ver = '%d.%d' % sys.version_info[:2] + if ver != running_ver: + Warn("running makepanda with Python %s, but building Panda3D with Python %s." % (running_ver, ver)) elif CrossCompiling() or (prefer_thirdparty_python and os.path.isdir(os.path.join(GetThirdpartyDir(), "python"))): tp_python = os.path.join(GetThirdpartyDir(), "python") if GetTarget() == 'darwin': - py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].dylib") + py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].dylib") + \ + glob.glob(tp_python + "/lib/libpython[0-9].[0-9][0-9].dylib") else: - py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].so") + py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].so") + \ + glob.glob(tp_python + "/lib/libpython[0-9].[0-9][0-9].so") if len(py_libs) == 0: - py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].a") + py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].a") + \ + glob.glob(tp_python + "/lib/libpython[0-9].[0-9][0-9].a") if len(py_libs) == 0: exit("Could not find the Python library in %s." % (tp_python)) @@ -2093,7 +2100,8 @@ def SdkLocatePython(prefer_thirdparty_python=False): exit("Found multiple Python libraries in %s." % (tp_python)) py_lib = os.path.basename(py_libs[0]) - SDK["PYTHONVERSION"] = "python" + py_lib[9] + "." + py_lib[11] + py_libver = py_lib.strip('.abdhilnopsty') + SDK["PYTHONVERSION"] = "python" + py_libver SDK["PYTHONEXEC"] = tp_python + "/bin/" + SDK["PYTHONVERSION"] SDK["PYTHON"] = tp_python + "/include/" + SDK["PYTHONVERSION"] @@ -2139,9 +2147,9 @@ def SdkLocatePython(prefer_thirdparty_python=False): exit("Host Python version (%s) must be the same as target Python version (%s)!" % (host_version, SDK["PYTHONVERSION"])) if GetVerbose(): - print("Using Python %s build located at %s" % (SDK["PYTHONVERSION"][6:9], SDK["PYTHON"])) + print("Using Python %s build located at %s" % (SDK["PYTHONVERSION"][6:], SDK["PYTHON"])) else: - print("Using Python %s" % (SDK["PYTHONVERSION"][6:9])) + print("Using Python %s" % (SDK["PYTHONVERSION"][6:])) def SdkLocateVisualStudio(version=(10,0)): if (GetHost() != "windows"): return @@ -3352,7 +3360,7 @@ def GetCurrentPythonVersionInfo(): from distutils.sysconfig import get_python_lib return { - "version": SDK["PYTHONVERSION"][6:9], + "version": SDK["PYTHONVERSION"][6:], "soabi": GetPythonABI(), "ext_suffix": GetExtensionSuffix(), "executable": sys.executable, diff --git a/panda/src/audio/audioManager.h b/panda/src/audio/audioManager.h index bd35da2f5b..a94fd8f2b6 100644 --- a/panda/src/audio/audioManager.h +++ b/panda/src/audio/audioManager.h @@ -106,13 +106,11 @@ PUBLISHED: virtual void set_volume(PN_stdfloat volume) = 0; virtual PN_stdfloat get_volume() const = 0; -/* - * Turn the manager on or off. If you play a sound while the manager is - * inactive, it won't start. If you deactivate the manager while sounds are - * playing, they'll stop. If you activate the manager while looping sounds - * are playing (those that have a loop_count of zero), they will start playing - * from the beginning of their loop. inits to true. - */ + // Turn the manager on or off. If you play a sound while the manager is + // inactive, it won't start. If you deactivate the manager while sounds are + // playing, they'll stop. If you activate the manager while looping sounds + // are playing (those that have a loop_count of zero), they will start + // playing from the beginning of their loop. Defaults to true. virtual void set_active(bool flag) = 0; virtual bool get_active() const = 0; diff --git a/panda/src/audio/audioSound.cxx b/panda/src/audio/audioSound.cxx index aea4e4f14c..d1fa986909 100644 --- a/panda/src/audio/audioSound.cxx +++ b/panda/src/audio/audioSound.cxx @@ -71,8 +71,8 @@ get_3d_max_distance() const { */ PN_stdfloat AudioSound:: get_speaker_mix(int speaker) { - // intentionally blank - return 0.0; + // intentionally blank + return 0.0; } /** @@ -80,7 +80,7 @@ get_speaker_mix(int speaker) { */ void AudioSound:: set_speaker_mix(PN_stdfloat frontleft, PN_stdfloat frontright, PN_stdfloat center, PN_stdfloat sub, PN_stdfloat backleft, PN_stdfloat backright, PN_stdfloat sideleft, PN_stdfloat sideright) { - // intentionally blank + // intentionally blank } /** diff --git a/panda/src/audio/audioSound.h b/panda/src/audio/audioSound.h index 4f7116c080..b5ec6bcfc8 100644 --- a/panda/src/audio/audioSound.h +++ b/panda/src/audio/audioSound.h @@ -108,7 +108,7 @@ PUBLISHED: virtual void set_3d_max_distance(PN_stdfloat dist); virtual PN_stdfloat get_3d_max_distance() const; - // *_speaker_mix is for use with FMOD. + // speaker_mix is for use with FMOD. virtual PN_stdfloat get_speaker_mix(int speaker); virtual void set_speaker_mix(PN_stdfloat frontleft, PN_stdfloat frontright, PN_stdfloat center, PN_stdfloat sub, PN_stdfloat backleft, PN_stdfloat backright, PN_stdfloat sideleft, PN_stdfloat sideright); diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index 8ada87edaa..8fffdbefc5 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -1732,6 +1732,19 @@ handle_key_event(NSEvent *event) { return; } + // If UCKeyTranslate could not map the key into a valid unicode character or + // reserved symbol (See NSEvent.h), it returns 0x10 as translated character. + // This happens e.g.e with the combination Fn+F1.. keys. + // In that case, as fallback, we use charactersIgnoringModifiers to retrieve + // the character without modifiers. + if (c == 0x10) { + NSString *str = [event charactersIgnoringModifiers]; + if (str == nil || [str length] != 1) { + return; + } + c = [str characterAtIndex: 0]; + } + ButtonHandle button = map_key(c); if (button == ButtonHandle::none()) { diff --git a/panda/src/downloader/httpClient.cxx b/panda/src/downloader/httpClient.cxx index 1f80bf61fd..c451f13084 100644 --- a/panda/src/downloader/httpClient.cxx +++ b/panda/src/downloader/httpClient.cxx @@ -663,7 +663,7 @@ get_proxies_for_url(const URLSpec &url) const { * Specifies the username:password string corresponding to a particular server * and/or realm, when demanded by the server. Either or both of the server or * realm may be empty; if so, they match anything. Also, the server may be - * set to the special string "*proxy", which will match any proxy server. + * set to the special string `"*proxy"`, which will match any proxy server. * * If the username is set to the empty string, this clears the password for * the particular server/realm pair. diff --git a/panda/src/egg/eggTexture.cxx b/panda/src/egg/eggTexture.cxx index eb2cfa7342..a4fb06b62e 100644 --- a/panda/src/egg/eggTexture.cxx +++ b/panda/src/egg/eggTexture.cxx @@ -509,6 +509,7 @@ has_alpha_channel(int num_components) const { case F_rgb8: case F_rgb5: case F_rgb332: + case F_srgb: // These formats never use alpha, regardless of the number of components // we have. return false; @@ -525,6 +526,7 @@ has_alpha_channel(int num_components) const { case F_rgba8: case F_rgba4: case F_rgba5: + case F_srgb_alpha: case F_unspecified: // These formats use alpha if the image had alpha. return (num_components == 2 || num_components == 4); @@ -690,6 +692,8 @@ EggTexture::Format EggTexture:: string_format(const string &string) { if (cmp_nocase_uh(string, "rgba") == 0) { return F_rgba; + } else if (cmp_nocase_uh(string, "srgb_alpha") == 0) { + return F_srgb_alpha; } else if (cmp_nocase_uh(string, "rgbm") == 0) { return F_rgbm; } else if (cmp_nocase_uh(string, "rgba12") == 0) { @@ -701,6 +705,8 @@ string_format(const string &string) { } else if (cmp_nocase_uh(string, "rgb") == 0) { return F_rgb; + } else if (cmp_nocase_uh(string, "srgb") == 0) { + return F_srgb; } else if (cmp_nocase_uh(string, "rgb12") == 0) { return F_rgb12; } else if (cmp_nocase_uh(string, "rgb8") == 0) { @@ -1137,6 +1143,8 @@ ostream &operator << (ostream &out, EggTexture::Format format) { return out << "rgba8"; case EggTexture::F_rgba4: return out << "rgba4"; + case EggTexture::F_srgb_alpha: + return out << "srgb_alpha"; case EggTexture::F_rgb: return out << "rgb"; @@ -1150,6 +1158,8 @@ ostream &operator << (ostream &out, EggTexture::Format format) { return out << "rgba5"; case EggTexture::F_rgb332: return out << "rgb332"; + case EggTexture::F_srgb: + return out << "srgb"; case EggTexture::F_red: return out << "red"; diff --git a/panda/src/egg/eggTexture.h b/panda/src/egg/eggTexture.h index 55f56f4b3d..ac15293140 100644 --- a/panda/src/egg/eggTexture.h +++ b/panda/src/egg/eggTexture.h @@ -60,7 +60,10 @@ PUBLISHED: F_rgba, F_rgbm, F_rgba12, F_rgba8, F_rgba4, F_rgba5, F_rgb, F_rgb12, F_rgb8, F_rgb5, F_rgb332, F_red, F_green, F_blue, F_alpha, F_luminance, - F_luminance_alpha, F_luminance_alphamask + F_luminance_alpha, F_luminance_alphamask, + + // Only for compatibility with .bam, use is discouraged! + F_srgb, F_srgb_alpha }; enum CompressionMode { CM_default, CM_off, CM_on, diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index 66a9ea44c6..170cc6f7ae 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -900,6 +900,7 @@ load_texture(TextureDef &def, EggTexture *egg_tex) { case EggTexture::F_rgb8: case EggTexture::F_rgb5: case EggTexture::F_rgb332: + case EggTexture::F_srgb: wanted_channels = 3; wanted_alpha = false; break; @@ -910,6 +911,7 @@ load_texture(TextureDef &def, EggTexture *egg_tex) { case EggTexture::F_rgba8: case EggTexture::F_rgba4: case EggTexture::F_rgba5: + case EggTexture::F_srgb_alpha: wanted_channels = 4; wanted_alpha = true; break; @@ -1259,6 +1261,10 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) { case EggTexture::F_rgb332: tex->set_format(Texture::F_rgb332); break; + case EggTexture::F_srgb: + case EggTexture::F_srgb_alpha: + tex->set_format(Texture::F_srgb); + break; case EggTexture::F_unspecified: break; @@ -1296,6 +1302,9 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) { case EggTexture::F_rgba5: tex->set_format(Texture::F_rgba5); break; + case EggTexture::F_srgb_alpha: + tex->set_format(Texture::F_srgb_alpha); + break; case EggTexture::F_unspecified: break; diff --git a/panda/src/egg2pg/eggSaver.cxx b/panda/src/egg2pg/eggSaver.cxx index db88189d19..28d6e22d05 100644 --- a/panda/src/egg2pg/eggSaver.cxx +++ b/panda/src/egg2pg/eggSaver.cxx @@ -1411,6 +1411,12 @@ get_egg_texture(Texture *tex) { case Texture::F_luminance_alphamask: temp.set_format(EggTexture::F_luminance_alphamask); break; + case Texture::F_srgb: + temp.set_format(EggTexture::F_srgb); + break; + case Texture::F_srgb_alpha: + temp.set_format(EggTexture::F_srgb_alpha); + break; default: break; } diff --git a/panda/src/egldisplay/eglGraphicsStateGuardian.cxx b/panda/src/egldisplay/eglGraphicsStateGuardian.cxx index c6fb88989b..26f55f3b74 100644 --- a/panda/src/egldisplay/eglGraphicsStateGuardian.cxx +++ b/panda/src/egldisplay/eglGraphicsStateGuardian.cxx @@ -104,9 +104,9 @@ get_properties(FrameBufferProperties &properties, properties.set_depth_bits(depth_size); properties.set_multisamples(samples); - // Set both hardware and software bits, indicating not-yet-known. - properties.set_force_software(1); - properties.set_force_hardware(1); + // "slow" likely indicates no hardware acceleration. + properties.set_force_software(slow); + properties.set_force_hardware(!slow); } /** @@ -237,6 +237,20 @@ choose_pixel_format(const FrameBufferProperties &properties, if (!display || _visual) #endif { + // This is set during window creation, but for now we have to pretend + // that we can honor the request, if we support the extension. + if (properties.get_srgb_color()) { + const char *extensions = eglQueryString(_egl_display, EGL_EXTENSIONS); + if (extensions != nullptr) { + vector_string tokens; + extract_words(extensions, tokens); + + if (std::find(tokens.begin(), tokens.end(), "EGL_KHR_gl_colorspace") != tokens.end()) { + best_props.set_srgb_color(true); + } + } + } + _fbprops = best_props; delete[] configs; return; @@ -269,9 +283,6 @@ reset() { if (_gl_renderer == "Software Rasterizer") { _fbprops.set_force_software(1); _fbprops.set_force_hardware(0); - } else { - _fbprops.set_force_hardware(1); - _fbprops.set_force_software(0); } } diff --git a/panda/src/egldisplay/eglGraphicsWindow.cxx b/panda/src/egldisplay/eglGraphicsWindow.cxx index 8f74e1f47e..c85ff9d8df 100644 --- a/panda/src/egldisplay/eglGraphicsWindow.cxx +++ b/panda/src/egldisplay/eglGraphicsWindow.cxx @@ -30,6 +30,14 @@ #include "nativeWindowHandle.h" #include "get_x11.h" +#ifndef EGL_GL_COLORSPACE_KHR +#define EGL_GL_COLORSPACE_KHR 0x309D +#endif + +#ifndef EGL_GL_COLORSPACE_SRGB_KHR +#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 +#endif + TypeHandle eglGraphicsWindow::_type_handle; /** @@ -236,7 +244,16 @@ open_window() { return false; } - _egl_surface = eglCreateWindowSurface(_egl_display, eglgsg->_fbconfig, (NativeWindowType) _xwindow, nullptr); + EGLint attribs[4]; + EGLint *attribs_p = nullptr; + if (eglgsg->get_fb_properties().get_srgb_color()) { + attribs[0] = EGL_GL_COLORSPACE_KHR; + attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR; + attribs[2] = EGL_NONE; + attribs[3] = EGL_NONE; + attribs_p = attribs; + } + _egl_surface = eglCreateWindowSurface(_egl_display, eglgsg->_fbconfig, (NativeWindowType) _xwindow, attribs_p); if (eglGetError() != EGL_SUCCESS) { egldisplay_cat.error() << "Failed to create window surface.\n"; diff --git a/panda/src/gles2gsg/gles2gsg.h b/panda/src/gles2gsg/gles2gsg.h index 363baad0ca..729ee6e589 100644 --- a/panda/src/gles2gsg/gles2gsg.h +++ b/panda/src/gles2gsg/gles2gsg.h @@ -100,9 +100,7 @@ typedef char GLchar; #define GL_RGBA16F GL_RGBA16F_EXT #define GL_RGB32F GL_RGB32F_EXT #define GL_RGBA32F GL_RGBA32F_EXT -#define GL_SRGB GL_SRGB_EXT #define GL_SRGB_ALPHA GL_SRGB_ALPHA_EXT -#define GL_SRGB8_ALPHA8 GL_SRGB8_ALPHA8_EXT #define GL_RGBA8 GL_RGBA8_OES #define GL_R8 GL_R8_EXT #define GL_RG8 GL_RG8_EXT @@ -194,6 +192,9 @@ typedef char GLchar; #define GL_RGB9_E5 0x8C3D #define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB8_ALPHA8 0x8C43 #define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 #define GL_RED_INTEGER 0x8D94 #define GL_RGB_INTEGER 0x8D98 diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 33c69ad8ad..0c60dcf806 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -170,6 +170,7 @@ static const string default_vshader = "out vec2 texcoord;\n" "out vec4 color;\n" #else + "#version 100\n" "precision mediump float;\n" "attribute vec4 p3d_Vertex;\n" "attribute vec4 p3d_Color;\n" @@ -240,6 +241,7 @@ static const string default_fshader = "uniform sampler2D p3d_Texture0;\n" "uniform vec4 p3d_TexAlphaOnly;\n" #else + "#version 100\n" "precision mediump float;\n" "varying vec2 texcoord;\n" "varying lowp vec4 color;\n" @@ -3992,12 +3994,6 @@ prepare_lens() { } #endif -#ifndef OPENGLES_1 - if (_current_shader_context) { - _current_shader_context->issue_parameters(Shader::SSD_transform); - } -#endif - return true; } @@ -10512,11 +10508,9 @@ get_internal_image_format(Texture *tex, bool force_sized) const { #ifndef OPENGLES_1 case Texture::F_srgb: -#ifndef OPENGLES - return GL_SRGB8; -#endif + return _supports_texture_srgb ? GL_SRGB8 : GL_RGB8; case Texture::F_srgb_alpha: - return GL_SRGB8_ALPHA8; + return _supports_texture_srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8; #endif #ifndef OPENGLES case Texture::F_sluminance: @@ -11405,9 +11399,10 @@ set_state_and_transform(const RenderState *target, do_issue_transform(); } - if (target == _state_rs && (_state_mask | _inv_state_mask).is_all_on()) { - return; - } + //XXX the _inv_state_mask system does not appear to be used at the moment. + //if (target == _state_rs && (_state_mask | _inv_state_mask).is_all_on()) { + // return; + //} _target_rs = target; #ifndef OPENGLES_1 @@ -13005,11 +13000,20 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { int num_levels = 1; CPTA_uchar image = tex->get_ram_mipmap_image(mipmap_bias); + bool can_generate = _supports_generate_mipmap; +#if defined(OPENGLES) && !defined(OPENGLES_1) + // OpenGL ES doesn't support generating mipmaps for sRGB textures, so we + // have to do this on the CPU, unless we have a special extension. + if (internal_format == GL_SRGB8 || internal_format == GL_SRGB8_ALPHA8) { + can_generate = has_extension("GL_NV_generate_mipmap_sRGB"); + } +#endif + if (image.is_null()) { // We don't even have a RAM image, so we have no choice but to let // mipmaps be generated on the GPU. if (uses_mipmaps) { - if (_supports_generate_mipmap) { + if (can_generate) { num_levels = tex->get_expected_num_mipmap_levels() - mipmap_bias; gtc->_generate_mipmaps = true; } else { @@ -13025,7 +13029,7 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { if (num_levels <= 1) { // No RAM mipmap levels available. Should we generate some? - if (!_supports_generate_mipmap || !driver_generate_mipmaps || + if (!can_generate || !driver_generate_mipmaps || image_compression != Texture::CM_off) { // Yes, the GL can't or won't generate them, so we need to. Note // that some drivers (nVidia) will *corrupt memory* if you ask @@ -13038,7 +13042,7 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { if (num_levels <= 1) { // We don't have mipmap levels in RAM. Ask the GL to generate them // if it can. - if (_supports_generate_mipmap) { + if (can_generate) { num_levels = tex->get_expected_num_mipmap_levels() - mipmap_bias; gtc->_generate_mipmaps = true; } else { @@ -14304,9 +14308,7 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { #ifndef OPENGLES_1 case GL_SRGB: -#ifndef OPENGLES case GL_SRGB8: -#endif format = Texture::F_srgb; break; case GL_SRGB_ALPHA: diff --git a/panda/src/linmath/lmatrix4_src.I b/panda/src/linmath/lmatrix4_src.I index ca3ff8eed6..94515ce394 100644 --- a/panda/src/linmath/lmatrix4_src.I +++ b/panda/src/linmath/lmatrix4_src.I @@ -1325,7 +1325,7 @@ invert_in_place() { } /** - * Computes (*this) += other * weight. + * Computes `(*this) += other * weight`. */ INLINE_LINMATH void FLOATNAME(LMatrix4):: accumulate(const FLOATNAME(LMatrix4) &other, FLOATTYPE weight) { diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index fd6364cea5..51d97068a8 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -1045,17 +1045,25 @@ make_style(const WindowProperties &properties) { // Additionally, the window class attribute should not include the // CS_PARENTDC style. - DWORD window_style = WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + DWORD window_style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS; if (properties.get_fullscreen()) { - window_style |= WS_SYSMENU; - } else if (!properties.get_undecorated()) { - window_style |= (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX); - - if (!properties.get_fixed_size()) { - window_style |= (WS_SIZEBOX | WS_MAXIMIZEBOX); + window_style |= WS_POPUP | WS_SYSMENU; + } else { + if (_parent_window_handle) { + window_style |= WS_CHILD; } else { - window_style |= WS_BORDER; + window_style |= WS_POPUP; + } + + if (!properties.get_undecorated()) { + window_style |= (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX); + + if (!properties.get_fixed_size()) { + window_style |= (WS_SIZEBOX | WS_MAXIMIZEBOX); + } else { + window_style |= WS_BORDER; + } } } return window_style; diff --git a/pandatool/src/deploy-stub/deploy-stub.c b/pandatool/src/deploy-stub/deploy-stub.c index df02d0aa35..9b118c75b0 100644 --- a/pandatool/src/deploy-stub/deploy-stub.c +++ b/pandatool/src/deploy-stub/deploy-stub.c @@ -24,6 +24,8 @@ #include +#include "structmember.h" + /* Leave room for future expansion. We only read pointer 0, but there are other pointers that are being read by configPageManager.cxx. */ #define MAX_NUM_POINTERS 24 @@ -335,6 +337,49 @@ static int setup_logging(const char *path, int append) { #endif } +/** + * Sets the line_buffering property on a TextIOWrapper object. + */ +static int enable_line_buffering(PyObject *file) { +#if PY_VERSION_HEX >= 0x03070000 + /* Python 3.7 has a useful reconfigure() method. */ + PyObject *kwargs = _PyDict_NewPresized(1); + PyDict_SetItemString(kwargs, "line_buffering", Py_True); + PyObject *args = PyTuple_New(0); + + PyObject *method = PyObject_GetAttrString(file, "reconfigure"); + if (method != NULL) { + PyObject *result = PyObject_Call(method, args, kwargs); + Py_DECREF(method); + if (result != NULL) { + Py_DECREF(result); + } else { + PyErr_Clear(); + return 0; + } + } + Py_DECREF(kwargs); + Py_DECREF(args); +#else + /* Older versions just don't expose a way to reconfigure(), but it's still + safe to override the property; we just have to use a hack to do it, + because it's officially marked "readonly". */ + + PyTypeObject *type = Py_TYPE(file); + PyMemberDef *member = type->tp_members; + + while (member != NULL && member->name != NULL) { + if (strcmp(member->name, "line_buffering") == 0) { + *((char *)file + member->offset) = 1; + return 1; + } + ++member; + } + fflush(stdout); +#endif + return 1; +} + /* Main program */ #ifdef WIN_UNICODE @@ -345,8 +390,10 @@ int Py_FrozenMain(int argc, char **argv) { char *p; int n, sts = 1; - int inspect = 0; int unbuffered = 0; +#ifndef NDEBUG + int inspect = 0; +#endif #ifndef WIN_UNICODE int i; @@ -378,8 +425,10 @@ int Py_FrozenMain(int argc, char **argv) Py_NoSiteFlag = 0; Py_NoUserSiteDirectory = 1; +#ifndef NDEBUG if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') inspect = 1; +#endif if ((p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0') unbuffered = 1; @@ -422,6 +471,23 @@ int Py_FrozenMain(int argc, char **argv) PyWinFreeze_ExeInit(); #endif +#ifdef MS_WINDOWS + /* Ensure that line buffering is enabled on the output streams. */ + if (!unbuffered) { + PyObject *sys_stream; + sys_stream = PySys_GetObject("__stdout__"); + if (sys_stream && !enable_line_buffering(sys_stream)) { + fprintf(stderr, "Failed to enable line buffering on sys.stdout\n"); + fflush(stderr); + } + sys_stream = PySys_GetObject("__stderr__"); + if (sys_stream && !enable_line_buffering(sys_stream)) { + fprintf(stderr, "Failed to enable line buffering on sys.stderr\n"); + fflush(stderr); + } + } +#endif + if (Py_VerboseFlag) fprintf(stderr, "Python %s\n%s\n", Py_GetVersion(), Py_GetCopyright()); @@ -473,8 +539,10 @@ int Py_FrozenMain(int argc, char **argv) else sts = 0; +#ifndef NDEBUG if (inspect && isatty((int)fileno(stdin))) sts = PyRun_AnyFile(stdin, "") != 0; +#endif #ifdef MS_WINDOWS PyWinFreeze_ExeTerm(); diff --git a/pandatool/src/palettizer/textureProperties.cxx b/pandatool/src/palettizer/textureProperties.cxx index 573dd784b3..30d102f0a0 100644 --- a/pandatool/src/palettizer/textureProperties.cxx +++ b/pandatool/src/palettizer/textureProperties.cxx @@ -244,6 +244,7 @@ fully_define() { case EggTexture::F_rgba8: case EggTexture::F_rgba4: case EggTexture::F_rgba5: + case EggTexture::F_srgb_alpha: _num_channels = 4; break; @@ -253,6 +254,7 @@ fully_define() { case EggTexture::F_rgb8: case EggTexture::F_rgb5: case EggTexture::F_rgb332: + case EggTexture::F_srgb: _num_channels = 3; break;