diff --git a/.gitignore b/.gitignore index 44373e05fe..f99867f73a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ core core.* vgcore.* +*.core # Editor files/directories *.save @@ -22,6 +23,9 @@ vgcore.* *.o *.gch *.pch +/+DESC +/+MANIFEST +/pkg-plist # Produced installer/executables /*.exe @@ -31,6 +35,7 @@ vgcore.* /*.pkg /*.dmg /*.whl +/*.txz # CMake /build/ @@ -47,6 +52,10 @@ Thumbs.db ehthumbs.db # Python -__pycache__ +__pycache__/ *.pyc *.pyo + +# Test tool cache directories +.tox/ +.cache/ diff --git a/.travis.yml b/.travis.yml index c8176d293b..582c7fd62f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,15 +2,22 @@ language: cpp sudo: false matrix: include: - - compiler: gcc - env: PYTHONV=python2.7 FLAGS=--optimize=4 - compiler: clang env: PYTHONV=python3 FLAGS=--installer - compiler: clang env: PYTHONV=python2.7 FLAGS=--override=STDFLOAT_DOUBLE=1 + - compiler: gcc + env: PYTHONV=python2.7 FLAGS=--optimize=4 + before_install: + - export CC=gcc-4.7 + - export CXX=g++-4.7 addons: apt: + sources: + - ubuntu-toolchain-r-test packages: + - gcc-4.7 + - g++-4.7 - bison - flex - libfreetype6-dev @@ -27,8 +34,16 @@ addons: - nvidia-cg-toolkit - python-dev - python3-dev + - python-virtualenv - zlib1g-dev -script: $PYTHONV makepanda/makepanda.py --everything --git-commit $TRAVIS_COMMIT $FLAGS --threads 4 && LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV makepanda/test_imports.py + - fakeroot +install: + - virtualenv --python=$PYTHONV venv && source venv/bin/activate + - $PYTHONV -m pip install pytest +script: + - $PYTHONV makepanda/makepanda.py --everything --git-commit $TRAVIS_COMMIT $FLAGS --threads 4 + - LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV makepanda/test_imports.py + - LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV -m pytest tests notifications: irc: channels: diff --git a/README.md b/README.md index ceb23f7793..474ccedc57 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,23 @@ resources. If you get stuck, ask for help from our active Panda3D is licensed under the Modified BSD License. See the LICENSE file for more details. +Installing Panda3D +================== + +By far, the easiest way to install the latest development build of Panda3D +into an existing Python installation is using the following command: + +```bash +pip install --pre --extra-index-url https://archive.panda3d.org/ panda3d +``` + +If you prefer to install the full SDK with all tools, the latest development +builds can be obtained from this page: + +https://www.panda3d.org/download.php?sdk&version=devel + +These are automatically kept up-to-date with the latest GitHub version of Panda. + Building Panda3D ================ @@ -31,8 +48,8 @@ are included as part of the Windows 7.1 SDK. You will also need to have the third-party dependency libraries available for the build scripts to use. These are available from one of these two URLs, depending on whether you are on a 32-bit or 64-bit system: -https://www.panda3d.org/download/panda3d-1.9.2/panda3d-1.9.2-tools-win32.zip -https://www.panda3d.org/download/panda3d-1.9.2/panda3d-1.9.2-tools-win64.zip +https://www.panda3d.org/download/panda3d-1.9.4/panda3d-1.9.4-tools-win32.zip +https://www.panda3d.org/download/panda3d-1.9.4/panda3d-1.9.4-tools-win64.zip After acquiring these dependencies, you may simply build Panda3D from the command prompt using the following command: @@ -64,7 +81,7 @@ for you to install, depending on your distribution). The following command illustrates how to build Panda3D with some common options: ```bash -python2.7 makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-gles2 +python makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-gles2 --no-opencv ``` You will probably see some warnings saying that it's unable to find several @@ -93,11 +110,14 @@ may have to use the installpanda.py script instead, which will directly copy the files into the appropriate locations on your computer. You may have to run the `ldconfig` tool in order to update your library cache after installing Panda3D. -Mac OS X --------- +Alternatively, you can add the `--wheel` option, which will produce a .whl +file that can be installed into a Python installation using `pip`. -On Mac OS X, you will need to download a set of precompiled thirdparty packages in order to -compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.9.2/panda3d-1.9.2-tools-mac.tar.gz). +macOS +----- + +On macOS, you will need to download a set of precompiled thirdparty packages in order to +compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.9.4/panda3d-1.9.4-tools-mac.tar.gz). After placing the thirdparty directory inside the panda3d source directory, you may build Panda3D using a command like the following: @@ -107,20 +127,53 @@ python makepanda/makepanda.py --everything --installer ``` In order to make a universal build, pass the --universal flag. You may also -target a specific minimum Mac OS X version using the --osxtarget flag followed +target a specific minimum macOS version using the --osxtarget flag followed by the release number, eg. 10.6 or 10.7. If the build was successful, makepanda will have generated a .dmg file in the source directory containing the installer. Simply open it and run the package file in order to install the SDK onto your system. +FreeBSD +------- + +Building on FreeBSD is very similar to building on Linux. You will need to +install the requisite packages using the system package manager. To install +the recommended set of dependencies, you can use this command: + +```bash +pkg install pkgconf png jpeg-turbo tiff freetype2 eigen squish openal opusfile libvorbis libX11 libGL ode bullet assimp openexr +``` + +You will also need to choose which version of Python you want to use. +Install the appropriate package for it (such as `python2` or `python36`) and +run the makepanda script with your chosen Python version: + +```bash +python3.6 makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-gles2 +``` + +If successful, this will produce a .pkg file in the root of the source +directory which you can install using `pkg install`. + +Running Tests +============= + +Install [PyTest](https://docs.pytest.org/en/latest/getting-started.html#installation) +and run the `pytest` command. If you have not installed Panda3D, you will +need to configure your enviroment by pointing the `PYTHONPATH` variable at +the `built` directory. On Linux, you will also need to point the +`LD_LIBRARY_PATH` variable at the `built/lib` directory. + +As a convenience, you can alternatively pass the `--tests` option to makepanda. + Reporting Issues ================ If you encounter any bugs when using Panda3D, please report them in the bug tracker. This is hosted at: - https://bugs.launchpad.net/panda3d + https://github.com/panda3d/panda3d/issues Make sure to first use the search function to see if the bug has already been reported. When filling out a bug report, make sure that you include as much diff --git a/contrib/src/ai/aiCharacter.h b/contrib/src/ai/aiCharacter.h index 9fd0833a99..244f9bc45e 100644 --- a/contrib/src/ai/aiCharacter.h +++ b/contrib/src/ai/aiCharacter.h @@ -62,7 +62,7 @@ PUBLISHED: // This function is used to enable or disable the guides for path finding. void set_pf_guide(bool pf_guide); - AICharacter(string model_name, NodePath model_np, double mass, double movt_force, double max_force); + explicit AICharacter(string model_name, NodePath model_np, double mass, double movt_force, double max_force); ~AICharacter(); }; diff --git a/contrib/src/ai/aiNode.h b/contrib/src/ai/aiNode.h index ab0c48c1c7..f6a6ea737e 100644 --- a/contrib/src/ai/aiNode.h +++ b/contrib/src/ai/aiNode.h @@ -66,7 +66,7 @@ public: AINode *_next; PUBLISHED: - AINode(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h); + explicit AINode(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h); ~AINode(); bool contains(float x, float y); diff --git a/contrib/src/ai/flock.h b/contrib/src/ai/flock.h index 58595310d6..c5e508a837 100644 --- a/contrib/src/ai/flock.h +++ b/contrib/src/ai/flock.h @@ -44,7 +44,7 @@ public: AICharList _ai_char_list; PUBLISHED: - Flock(unsigned int flock_id, double vcone_angle, double vcone_radius, unsigned int separation_wt = 2, + explicit Flock(unsigned int flock_id, double vcone_angle, double vcone_radius, unsigned int separation_wt = 2, unsigned int cohesion_wt = 4, unsigned int alignment_wt = 1); ~Flock(); diff --git a/direct/metalibs/direct/direct.cxx b/direct/metalibs/direct/direct.cxx deleted file mode 100644 index 41594bf60d..0000000000 --- a/direct/metalibs/direct/direct.cxx +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @file direct.cxx - * @author drose - * @date 2000-05-18 - */ - -// This is a dummy file whose sole purpose is to give the compiler something -// to compile when making libdirect.so in NO_DEFER mode, which generates an -// empty library that itself links with all the other shared libraries that -// make up libdirect. diff --git a/direct/src/actor/Actor.py b/direct/src/actor/Actor.py index 29db4290bd..8a7ef093b0 100644 --- a/direct/src/actor/Actor.py +++ b/direct/src/actor/Actor.py @@ -50,6 +50,10 @@ class Actor(DirectObject, NodePath): def __repr__(self): return 'Actor.PartDef(%s, %s)' % (repr(self.partBundleNP), repr(self.partModel)) + + #snake_case alias: + get_bundle = getBundle + class AnimDef: """Instances of this class are stored within the @@ -72,6 +76,10 @@ class Actor(DirectObject, NodePath): def __repr__(self): return 'Actor.AnimDef(%s)' % (repr(self.filename)) + + #snake_case alias: + make_copy = makeCopy + class SubpartDef: """Instances of this class are stored within the SubpartDict @@ -889,7 +897,7 @@ class Actor(DirectObject, NodePath): return ((toFrame+1)-fromFrame) / animControl.getFrameRate() def getNumFrames(self, animName=None, partName=None): - lodName = next(iter(self.__animControlDict)) + #lodName = next(iter(self.__animControlDict)) controls = self.getAnimControls(animName, partName) if len(controls) == 0: return None @@ -2549,3 +2557,89 @@ class Actor(DirectObject, NodePath): for partBundleDict in self.__partBundleDict.values(): partDef = partBundleDict.get(subpartDef.truePartName) partDef.getBundle().setName(newBundleName) + + #snake_case alias: + control_joint = controlJoint + set_lod_animation = setLODAnimation + get_anim_control_dict = getAnimControlDict + get_actor_info = getActorInfo + clear_lod_animation = clearLODAnimation + reset_lod = resetLOD + fix_bounds = fixBounds + get_anim_filename = getAnimFilename + get_subparts_complete = getSubpartsComplete + verify_subparts_complete = verifySubpartsComplete + get_play_rate = getPlayRate + clear_python_data = clearPythonData + load_anims = loadAnims + set_subparts_complete = setSubpartsComplete + draw_in_front = drawInFront + get_lod_node = getLODNode + hide_part = hidePart + get_joint_transform_state = getJointTransformState + set_control_effect = setControlEffect + get_anim_controls = getAnimControls + release_joint = releaseJoint + print_anim_blends = printAnimBlends + get_lod = getLOD + disable_blend = disableBlend + show_part = showPart + get_joint_transform = getJointTransform + face_away_from_viewer = faceAwayFromViewer + set_lod = setLOD + osd_anim_blends = osdAnimBlends + get_current_frame = getCurrentFrame + set_play_rate = setPlayRate + bind_all_anims = bindAllAnims + unload_anims = unloadAnims + remove_part = removePart + use_lod = useLOD + get_anim_blends = getAnimBlends + get_lod_index = getLODIndex + get_num_frames = getNumFrames + post_flatten = postFlatten + get_lod_names = getLODNames + list_joints = listJoints + make_subpart = makeSubpart + get_anim_control = getAnimControl + get_part_bundle = getPartBundle + get_part_bundle_dict = getPartBundleDict + get_duration = getDuration + has_lod = hasLOD + print_lod = printLOD + fix_bounds_old = fixBounds_old + get_anim_names = getAnimNames + get_part_bundles = getPartBundles + anim_panel = animPanel + stop_joint = stopJoint + actor_interval = actorInterval + hide_all_bounds = hideAllBounds + show_all_bounds = showAllBounds + init_anims_on_all_lods = initAnimsOnAllLODs + get_part = getPart + add_lod = addLOD + show_all_parts = showAllParts + get_joints = getJoints + get_overlapping_joints = getOverlappingJoints + enable_blend = enableBlend + face_towards_viewer = faceTowardsViewer + bind_anim = bindAnim + set_blend = setBlend + get_frame_time = getFrameTime + remove_node = removeNode + wait_pending = waitPending + expose_joint = exposeJoint + set_lod_node = setLODNode + get_frame_rate = getFrameRate + get_current_anim = getCurrentAnim + get_part_names = getPartNames + freeze_joint = freezeJoint + set_center = setCenter + rename_part_bundles = renamePartBundles + get_geom_node = getGeomNode + set_geom_node = setGeomNode + load_model = loadModel + copy_actor = copyActor + get_base_frame_rate = getBaseFrameRate + remove_anim_control_dict = removeAnimControlDict + load_anims_on_all_lods = loadAnimsOnAllLODs diff --git a/direct/src/actor/__init__.py b/direct/src/actor/__init__.py index e69de29bb2..8a080475bb 100644 --- a/direct/src/actor/__init__.py +++ b/direct/src/actor/__init__.py @@ -0,0 +1,7 @@ +""" +This package contains the :class:`.Actor` class as well as a +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. +""" diff --git a/direct/src/controls/InputState.py b/direct/src/controls/InputState.py index 38b0d78892..68608e9bea 100755 --- a/direct/src/controls/InputState.py +++ b/direct/src/controls/InputState.py @@ -21,6 +21,9 @@ class InputStateToken: def __hash__(self): return self._hash + #snake_case alias: + is_valid = isValid + class InputStateWatchToken(InputStateToken, DirectObject.DirectObject): def release(self): self._inputState._ignore(self) @@ -39,6 +42,9 @@ class InputStateTokenGroup: token.release() self._tokens = [] + #snake_case alias: + add_token = addToken + class InputState(DirectObject.DirectObject): """ InputState is for tracking the on/off state of some events. @@ -235,3 +241,10 @@ class InputState(DirectObject.DirectObject): """for debugging""" return self.notify.debug( "%s (%s) %s"%(id(self), len(self._state), message)) + + #snake_case alias: + watch_with_modifiers = watchWithModifiers + is_set = isSet + get_event_name = getEventName + debug_print = debugPrint + release_inputs = releaseInputs diff --git a/direct/src/controls/__init__.py b/direct/src/controls/__init__.py index e69de29bb2..811657484c 100644 --- a/direct/src/controls/__init__.py +++ b/direct/src/controls/__init__.py @@ -0,0 +1,4 @@ +""" +This package contains various types of character controllers, handling basic +control mechanics and setting up collisions for them. +""" diff --git a/direct/src/dcparser/dcPacker.cxx b/direct/src/dcparser/dcPacker.cxx index fe3461a6c1..794d5e1690 100644 --- a/direct/src/dcparser/dcPacker.cxx +++ b/direct/src/dcparser/dcPacker.cxx @@ -708,7 +708,7 @@ pack_object(PyObject *object) { pack_int64(PyLong_AsLongLong(object)); #if PY_MAJOR_VERSION >= 3 } else if (PyUnicode_Check(object)) { - char *buffer; + const char *buffer; Py_ssize_t length; buffer = PyUnicode_AsUTF8AndSize(object, &length); if (buffer) { diff --git a/direct/src/directbase/DirectStart.py b/direct/src/directbase/DirectStart.py index 031dc85323..8b6b5b9539 100644 --- a/direct/src/directbase/DirectStart.py +++ b/direct/src/directbase/DirectStart.py @@ -1,4 +1,19 @@ -""" This is a deprecated module that creates a global instance of ShowBase. """ +""" +This is a shortcut that instantiates ShowBase automatically on import, +opening a graphical window and setting up the scene graph. +This example demonstrates its use: + + import direct.directbase.DirectStart + run() + +While it may be considered useful for quick prototyping in the interactive +Python shell, using it in applications is not considered good style. +As such, it has been deprecated starting with Panda3D 1.9. It is equivalent +to and may be replaced by the following code: + + from direct.showbase.ShowBase import ShowBase + base = ShowBase() +""" __all__ = [] diff --git a/direct/src/directbase/__init__.py b/direct/src/directbase/__init__.py index e69de29bb2..0d7c008a72 100644 --- a/direct/src/directbase/__init__.py +++ b/direct/src/directbase/__init__.py @@ -0,0 +1,12 @@ +""" +This package contains modules to quickly set up a Panda environment for +quick prototyping in the interactive Python shell. Merely importing +one of these modules will create a :class:`.ShowBase` instance, opening +a graphical window and setting up the scene graph. + +The most commonly used module from this package is :mod:`.DirectStart`, +importing which executes the following code:: + + from direct.showbase.ShowBase import ShowBase + base = ShowBase() +""" diff --git a/direct/src/directbase/ppython.cxx b/direct/src/directbase/ppython.cxx deleted file mode 100644 index e460de766c..0000000000 --- a/direct/src/directbase/ppython.cxx +++ /dev/null @@ -1,106 +0,0 @@ -// This is a little wrapper to make it easy to run a python program from the -// command line. Basically, it just interfaces to the Python API and imports -// the module that was specified by the IMPORT_MODULE preprocessor definition -// when it was compiled. - -#include "dtoolbase.h" - -#undef _POSIX_C_SOURCE -#undef _XOPEN_SOURCE -#include -#if PY_MAJOR_VERSION >= 3 -#include -#endif - -#ifndef IMPORT_MODULE -#error IMPORT_MODULE must be defined when compiling ppython.cxx ! -#endif - -#define _STRINGIFY(s) #s -#define STRINGIFY(s) _STRINGIFY(s) -#define IMPORT_MODULE_STR STRINGIFY(IMPORT_MODULE) - -#if defined(_WIN32) && PY_MAJOR_VERSION >= 3 -// As Py_SetProgramName expects a wchar_t*, it's easiest to just use the wmain -// entry point. -int wmain(int argc, wchar_t *argv[]) { - Py_SetProgramName(argv[0]); - -#elif PY_MAJOR_VERSION >= 3 -// Convert from UTF-8 to wchar_t*. -int main(int argc, char *mb_argv[]) { - wchar_t **argv = new wchar_t*[argc + 1]; - for (int i = 0; i < argc; ++i) { - size_t len = mbstowcs(NULL, mb_argv[i], 0); - argv[i] = new wchar_t[len + 1]; - mbstowcs(argv[i], mb_argv[i], len); - argv[i][len] = 0; - } - // Just for good measure - argv[argc] = NULL; - - Py_SetProgramName(argv[0]); - -#else -// Python 2. -int main(int argc, char *argv[]) { - Py_SetProgramName(argv[0]); -#endif - - // On Windows, we need to set pythonhome correctly. We'll try to find - // ppython.exe on the path and set pythonhome to its location. -#ifdef _WIN32 -#if PY_MAJOR_VERSION >= 3 - // Py_SetPythonHome expects a wchar_t in Python 3. - wchar_t *path = _wgetenv(L"PATH"); - wchar_t *result = wcstok(path, L";"); - while (result != NULL) { - struct _stat st; - wchar_t *ppython = (wchar_t*) malloc(wcslen(result) * 2 + 26); - wcscpy(ppython, result); - wcscat(ppython, L"\\python.exe"); - if (_wstat(ppython, &st) == 0) { - Py_SetPythonHome(result); - free(ppython); - break; - } - result = wcstok(NULL, L";"); - free(ppython); - } -#else - char *path = getenv("PATH"); - char *result = strtok(path, ";"); - while (result != NULL) { - struct stat st; - char *ppython = (char*) malloc(strlen(result) + 13); - strcpy(ppython, result); - strcat(ppython, "\\ppython.exe"); - if (stat(ppython, &st) == 0) { - Py_SetPythonHome(result); - free(ppython); - break; - } - result = strtok(NULL, ";"); - free(ppython); - } -#endif -#endif - - Py_Initialize(); - - if (Py_VerboseFlag) { - fprintf(stderr, "Python %s\\n%s\\n", Py_GetVersion(), Py_GetCopyright()); - } - - PySys_SetArgv(argc, argv); - - int sts = 0; - PyObject* m = PyImport_ImportModule(IMPORT_MODULE_STR); - if (m <= 0) { - PyErr_Print(); - sts = 1; - } - - Py_Finalize(); - return sts; -} diff --git a/direct/src/directdevices/DirectDeviceManager.py b/direct/src/directdevices/DirectDeviceManager.py index 78c8615bdf..76206f6c40 100644 --- a/direct/src/directdevices/DirectDeviceManager.py +++ b/direct/src/directdevices/DirectDeviceManager.py @@ -9,10 +9,6 @@ ANALOG_MAX = 0.95 ANALOG_DEADBAND = 0.125 ANALOG_CENTER = 0.0 -try: - myBase = base -except: - myBase = simbase class DirectDeviceManager(VrpnClient, DirectObject): def __init__(self, server = None): @@ -52,8 +48,13 @@ class DirectButtons(ButtonNode, DirectObject): ButtonNode.__init__(self, vrpnClient, device) # Create a unique name for this button object self.name = 'DirectButtons-' + repr(DirectButtons.buttonCount) + # Attach node to data graph - self.nodePath = myBase.dataRoot.attachNewNode(self) + try: + self._base = base + except: + self._base = simbase + self.nodePath = self._base.dataRoot.attachNewNode(self) def __getitem__(self, index): if (index < 0) or (index >= self.getNumButtons()): @@ -64,10 +65,10 @@ class DirectButtons(ButtonNode, DirectObject): return self.getNumButtons() def enable(self): - self.nodePath.reparentTo(myBase.dataRoot) + self.nodePath.reparentTo(self._base.dataRoot) def disable(self): - self.nodePath.reparentTo(myBase.dataUnused) + self.nodePath.reparentTo(self._base.dataUnused) def getName(self): return self.name @@ -83,6 +84,12 @@ class DirectButtons(ButtonNode, DirectObject): class DirectAnalogs(AnalogNode, DirectObject): analogCount = 0 + + _analogDeadband = ConfigVariableDouble('vrpn-analog-deadband', ANALOG_DEADBAND) + _analogMin = ConfigVariableDouble('vrpn-analog-min', ANALOG_MIN) + _analogMax = ConfigVariableDouble('vrpn-analog-max', ANALOG_MAX) + _analogCenter = ConfigVariableDouble('vrpn-analog-center', ANALOG_CENTER) + def __init__(self, vrpnClient, device): # Keep track of number of analogs created DirectAnalogs.analogCount += 1 @@ -90,19 +97,20 @@ class DirectAnalogs(AnalogNode, DirectObject): AnalogNode.__init__(self, vrpnClient, device) # Create a unique name for this analog object self.name = 'DirectAnalogs-' + repr(DirectAnalogs.analogCount) - # Attach node to data graph - self.nodePath = myBase.dataRoot.attachNewNode(self) - # See if any of the general analog parameters are dconfig'd - self.analogDeadband = myBase.config.GetFloat('vrpn-analog-deadband', - ANALOG_DEADBAND) - self.analogMin = myBase.config.GetFloat('vrpn-analog-min', - ANALOG_MIN) - self.analogMax = myBase.config.GetFloat('vrpn-analog-max', - ANALOG_MAX) - self.analogCenter = myBase.config.GetFloat('vrpn-analog-center', - ANALOG_CENTER) - self.analogRange = self.analogMax - self.analogMin + # Attach node to data graph + try: + self._base = base + except: + self._base = simbase + self.nodePath = self._base.dataRoot.attachNewNode(self) + + # See if any of the general analog parameters are dconfig'd + self.analogDeadband = self._analogDeadband.getValue() + self.analogMin = self._analogMin.getValue() + self.analogMax = self._analogMax.getValue() + self.analogCenter = self._analogCenter.getValue() + self.analogRange = self.analogMax - self.analogMin def __getitem__(self, index): if (index < 0) or (index >= self.getNumControls()): @@ -113,10 +121,10 @@ class DirectAnalogs(AnalogNode, DirectObject): return self.getNumControls() def enable(self): - self.nodePath.reparentTo(myBase.dataRoot) + self.nodePath.reparentTo(self._base.dataRoot) def disable(self): - self.nodePath.reparentTo(myBase.dataUnused) + self.nodePath.reparentTo(self._base.dataUnused) def normalizeWithoutCentering(self, val, minVal = -1, maxVal = 1): # @@ -186,14 +194,19 @@ class DirectTracker(TrackerNode, DirectObject): TrackerNode.__init__(self, vrpnClient, device) # Create a unique name for this tracker object self.name = 'DirectTracker-' + repr(DirectTracker.trackerCount) + # Attach node to data graph - self.nodePath = myBase.dataRoot.attachNewNode(self) + try: + self._base = base + except: + self._base = simbase + self.nodePath = self._base.dataRoot.attachNewNode(self) def enable(self): - self.nodePath.reparentTo(myBase.dataRoot) + self.nodePath.reparentTo(self._base.dataRoot) def disable(self): - self.nodePath.reparentTo(myBase.dataUnused) + self.nodePath.reparentTo(self._base.dataUnused) def getName(self): return self.name @@ -213,8 +226,13 @@ class DirectDials(DialNode, DirectObject): DialNode.__init__(self, vrpnClient, device) # Create a unique name for this dial object self.name = 'DirectDials-' + repr(DirectDials.dialCount) + # Attach node to data graph - self.nodePath = myBase.dataRoot.attachNewNode(self) + try: + self._base = base + except: + self._base = simbase + self.nodePath = self._base.dataRoot.attachNewNode(self) def __getitem__(self, index): """ @@ -227,10 +245,10 @@ class DirectDials(DialNode, DirectObject): return self.getNumDials() def enable(self): - self.nodePath.reparentTo(myBase.dataRoot) + self.nodePath.reparentTo(self._base.dataRoot) def disable(self): - self.nodePath.reparentTo(myBase.dataUnused) + self.nodePath.reparentTo(self._base.dataUnused) def getName(self): return self.name @@ -259,14 +277,19 @@ class DirectTimecodeReader(AnalogNode, DirectObject): self.seconds = 0 self.minutes = 0 self.hours = 0 + # Attach node to data graph - self.nodePath = myBase.dataRoot.attachNewNode(self) + try: + self._base = base + except: + self._base = simbase + self.nodePath = self._base.dataRoot.attachNewNode(self) def enable(self): - self.nodePath.reparentTo(myBase.dataRoot) + self.nodePath.reparentTo(self._base.dataRoot) def disable(self): - self.nodePath.reparentTo(myBase.dataUnused) + self.nodePath.reparentTo(self._base.dataUnused) def getName(self): return self.name diff --git a/direct/src/directdevices/DirectRadamec.py b/direct/src/directdevices/DirectRadamec.py index 2aa7b1933c..3f74162b9b 100644 --- a/direct/src/directdevices/DirectRadamec.py +++ b/direct/src/directdevices/DirectRadamec.py @@ -21,7 +21,7 @@ class DirectRadamec(DirectObject): radamecCount = 0 notify = DirectNotifyGlobal.directNotify.newCategory('DirectRadamec') - def __init__(self, device = 'Analog0', nodePath = base.direct.camera): + def __init__(self, device = 'Analog0', nodePath = None): # See if device manager has been initialized if base.direct.deviceManager == None: base.direct.deviceManager = DirectDeviceManager() diff --git a/direct/src/directdevices/__init__.py b/direct/src/directdevices/__init__.py index e69de29bb2..46728797fa 100644 --- a/direct/src/directdevices/__init__.py +++ b/direct/src/directdevices/__init__.py @@ -0,0 +1,5 @@ +""" +This package contains a high-level interface for VRPN devices. + +Also see the :mod:`panda3d.vprn` module. +""" diff --git a/direct/src/directnotify/__init__.py b/direct/src/directnotify/__init__.py index e69de29bb2..96706bb8ad 100644 --- a/direct/src/directnotify/__init__.py +++ b/direct/src/directnotify/__init__.py @@ -0,0 +1,3 @@ +""" +This package contains notification and logging utilities for Python code. +""" diff --git a/direct/src/directscripts/eggcacher.py b/direct/src/directscripts/eggcacher.py index ad11303fe0..6dcee8e2d3 100644 --- a/direct/src/directscripts/eggcacher.py +++ b/direct/src/directscripts/eggcacher.py @@ -87,8 +87,13 @@ class EggCacher: TexturePool.releaseAllTextures() progress += size -cacher = EggCacher(sys.argv[1:]) -# Dummy main function so this can be added to console_scripts. -def main(): +def main(args=None): + if args is None: + args = sys.argv[1:] + + cacher = EggCacher(args) return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/direct/src/directscripts/packpanda.nsi b/direct/src/directscripts/packpanda.nsi deleted file mode 100755 index 73c9011d9a..0000000000 --- a/direct/src/directscripts/packpanda.nsi +++ /dev/null @@ -1,831 +0,0 @@ -; Panda3D installation script for the Nullsoft Installation System (NSIS). -; Jon Parise -; with Ben Johnson -; with Jason Pratt -; mangled by Josh Yelon - -; Caller needs to define these variables: -; -; COMPRESSOR - either zlib or lzma -; NAME - name of what we're building (ie, "Panda3D" or "Airblade") -; SMDIRECTORY - where to put this in the start menu (ie, "Panda3D 1.1.0" or "Airblade 1.1.0") -; INSTALLDIR - where to install the program (ie, "C:\Program Files\Panda3D-1.1.0") -; OUTFILE - where to put the output file (ie, "..\nsis-output.exe") -; LICENSE - location of the license file (ie, "C:\Airblade\LICENSE.TXT") -; LANGUAGE - name of the Language file to use (ie, "English" or "Panda3DEnglish") -; RUNTEXT - text for run-box at end of installation (ie, "Run the Panda Greeting Card") -; IBITMAP - name of installer bitmap (ie, "C:\Airblade\Airblade.bmp") -; UBITMAP - name of uninstaller bitmap (ie, "C:\Airblade\Airblade.bmp") -; -; PANDA - location of panda install tree. -; PYVER - version of Python that Panda was built with (ie, "2.7") -; PANDACONF - name of panda config directory - usually $PANDA\etc -; PSOURCE - location of the panda source-tree if available, OR location of panda install tree. -; PYEXTRAS - directory containing python extras, if any. -; REGVIEW - either 32 or 64, depending on the build architecture. -; -; PPGAME - directory containing prepagaged game, if any (ie, "C:\My Games\Airblade") -; PPMAIN - python program containing prepackaged game, if any (ie, "Airblade.py") -; PPICON - file containing icon of prepackaged game. -; - -!include "MUI.nsh" -!include "WinMessages.nsh" -Name "${NAME}" -InstallDir "${INSTALLDIR}" -OutFile "${OUTFILE}" - -SetCompress auto -SetCompressor ${COMPRESSOR} - -!define MUI_WELCOMEFINISHPAGE_BITMAP "${IBITMAP}" -!define MUI_UNWELCOMEFINISHPAGE_BITMAP "${UBITMAP}" - -!define MUI_ABORTWARNING -!define MUI_FINISHPAGE_NOREBOOTSUPPORT -!define MUI_FINISHPAGE_RUN -!define MUI_FINISHPAGE_RUN_FUNCTION runFunction -!define MUI_FINISHPAGE_RUN_TEXT "${RUNTEXT}" - -!insertmacro MUI_PAGE_WELCOME -!insertmacro MUI_PAGE_LICENSE "${LICENSE}" -!insertmacro MUI_PAGE_DIRECTORY -!insertmacro MUI_PAGE_INSTFILES -!insertmacro MUI_PAGE_FINISH - -!insertmacro MUI_UNPAGE_WELCOME -!insertmacro MUI_UNPAGE_CONFIRM -!insertmacro MUI_UNPAGE_INSTFILES -!insertmacro MUI_UNPAGE_FINISH - -!insertmacro MUI_LANGUAGE "${LANGUAGE}" - -ShowInstDetails nevershow -ShowUninstDetails nevershow - -LicenseData "${LICENSE}" - -InstType "Typical" - -!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS - -var READABLE -var MANPAGE -var TUTNAME - -Function runFunction - !ifdef PPGAME - ExecShell "open" "$SMPROGRAMS\${SMDIRECTORY}\Play ${NAME}.lnk" - !else - ExecShell "open" "$SMPROGRAMS\${SMDIRECTORY}\Panda Manual.lnk" - !endif -FunctionEnd - -Section "${SMDIRECTORY}" SecCore - SectionIn 1 2 3 RO - - SetDetailsPrint none - SetOutPath $INSTDIR - SetOverwrite try - - SetOutPath $INSTDIR - File "${PANDA}\LICENSE" - SetOutPath $INSTDIR\bin - File /r "${PANDA}\bin\*.dll" - File /nonfatal /r "${PANDA}\bin\*.pyd" - File /nonfatal /r "${PANDA}\bin\Microsoft.*.manifest" - SetOutPath $INSTDIR\etc - File /r "${PANDACONF}\*" - SetOutPath $INSTDIR\direct\directscripts - File /r /x CVS /x Opt?-Win32 "${PANDA}\direct\directscripts\*" - SetOutPath $INSTDIR\direct\filter - File /r /x CVS /x Opt?-Win32 "${PANDA}\direct\filter\*.sha" - SetOutPath $INSTDIR\direct - File /r /x CVS /x Opt?-Win32 "${PANDA}\direct\*.py" - Delete "$INSTDIR\panda3d.py" - Delete "$INSTDIR\panda3d.pyc" - Delete "$INSTDIR\panda3d.pyo" - SetOutPath $INSTDIR\pandac - File /r "${PANDA}\pandac\*.py" - SetOutPath $INSTDIR\panda3d - File /r "${PANDA}\panda3d\*.py" - File /r "${PANDA}\panda3d\*.pyd" - SetOutPath $INSTDIR\python - File /r "${PANDA}\python\*" - RMDir /r "$SMPROGRAMS\${SMDIRECTORY}" - CreateDirectory "$SMPROGRAMS\${SMDIRECTORY}" - - !ifdef PPGAME - - SetOutPath $INSTDIR\models - File /r /x CVS "${PANDA}\models\*" - SetOutPath $INSTDIR\bin - File /r "${PANDA}\bin\eggcacher.exe" - SetOutpath $INSTDIR\game - File /r "${PPGAME}\*" - CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Play ${NAME}.lnk" "$INSTDIR\python\ppython.exe" "-E ${PPMAIN}" "$INSTDIR\${PPICON}" 0 SW_SHOWMINIMIZED "" "Play ${NAME}" - # Preload all EGG files into the model-cache - SetOutPath $INSTDIR - SetDetailsPrint textonly - SetDetailsView show - nsExec::ExecToLog '"$INSTDIR\bin\eggcacher.exe" --concise game' - SetDetailsPrint none - SetDetailsView hide - - !else - - SetOutPath $INSTDIR\plugins - File /nonfatal /r "${PANDA}\plugins\*.dle" - File /nonfatal /r "${PANDA}\plugins\*.dlo" - File /nonfatal /r "${PANDA}\plugins\*.mll" - File /nonfatal /r "${PANDA}\plugins\*.mel" - File /nonfatal /r "${PANDA}\plugins\*.ms" - File "${PSOURCE}\doc\INSTALLING-PLUGINS.TXT" - SetOutPath $INSTDIR\pandac\input - File /r "${PANDA}\pandac\input\*" - SetOutPath $INSTDIR\bin - File /r "${PANDA}\bin\*.exe" - File /nonfatal /r "${PANDA}\bin\*.p3d" - SetOutPath $INSTDIR\lib - File /r /x *.exp "${PANDA}\lib\*" - SetOutPath $INSTDIR\include - File /r /x *.exp "${PANDA}\include\*" - SetOutPath $INSTDIR\Pmw - File /r /x CVS "${PANDA}\Pmw\*" - SetOutPath $INSTDIR\NSIS - File /r /x CVS "${NSISDIR}\*" - SetOutPath $INSTDIR - File /r /x CVS "${PANDA}\ReleaseNotes" - !ifdef PYEXTRAS - SetOutPath $INSTDIR\python\lib - File /nonfatal /r "${PYEXTRAS}\*" - !endif - SetOutPath $INSTDIR\models - File /r /x CVS "${PANDA}\models\*" - SetOutPath $INSTDIR\samples - File /nonfatal /r /x CVS "${PSOURCE}\samples\*" - MessageBox MB_YESNO|MB_ICONQUESTION \ - "EGG caching is about to begin.$\nThis process may take a couple minute and approximately 270 MB of RAM.$\nWARNING : It might stuck if your computer resources are low.$\n$\nDo you really want to do this optional step ?" \ - IDNO SkipCaching - # Preload all EGG files into the model-cache - SetOutPath $INSTDIR - SetDetailsPrint both - SetDetailsView show - nsExec::ExecToLog '"$INSTDIR\bin\eggcacher.exe" --concise models samples' - SetDetailsPrint none - SetDetailsView hide - SkipCaching: - - SetOutPath $INSTDIR - WriteINIStr $INSTDIR\Website.url "InternetShortcut" "URL" "http://panda3d.org/" - WriteINIStr $INSTDIR\Manual.url "InternetShortcut" "URL" "http://panda3d.org/wiki/index.php" - WriteINIStr $INSTDIR\Samples.url "InternetShortcut" "URL" "http://panda3d.org/wiki/index.php/Sample_Programs_in_the_Distribution" - SetOutPath $INSTDIR - CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Panda Manual.lnk" "$INSTDIR\Manual.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Panda Manual" - CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Panda Website.lnk" "$INSTDIR\Website.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Panda Website" - CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Sample Program Manual.lnk" "$INSTDIR\Samples.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Sample Program Manual" - - FindFirst $0 $1 $INSTDIR\samples\* - loop: - StrCmp $1 "" done - StrCmp $1 "." next - StrCmp $1 ".." next - Push $1 - Push "-" - Push " " - Call StrRep - Pop $R0 - StrCpy $READABLE $R0 - Push $1 - Push "-" - Push "_" - Call StrRep - Pop $R0 - StrCpy $MANPAGE $R0 - CreateDirectory "$SMPROGRAMS\${SMDIRECTORY}\Sample Programs\$READABLE" - SetOutPath $INSTDIR\samples\$1 - WriteINIStr $INSTDIR\samples\$1\ManualPage.url "InternetShortcut" "URL" "http://panda3d.org/wiki/index.php/Sample_Programs:_$MANPAGE" - CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Sample Programs\$READABLE\Manual Page.lnk" "$INSTDIR\samples\$1\ManualPage.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Manual Entry on this Sample Program" - CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Sample Programs\$READABLE\View Source Code.lnk" "$INSTDIR\samples\$1" - FindFirst $2 $3 $INSTDIR\samples\$1\Tut-*.py - iloop: - StrCmp $3 "" idone - StrCpy $TUTNAME $3 -3 4 - Push $TUTNAME - Push "-" - Push " " - Call StrRep - Pop $R0 - StrCpy $TUTNAME $R0 - CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Sample Programs\$READABLE\Run $TUTNAME.lnk" "$INSTDIR\python\python.exe" "-E $3" "$INSTDIR\bin\eggcacher.exe" 0 SW_SHOWMINIMIZED "" "Run $TUTNAME" - CreateShortCut "$INSTDIR\samples\$1\Run $TUTNAME.lnk" "$INSTDIR\python\python.exe" "-E $3" "$INSTDIR\bin\eggcacher.exe" 0 SW_SHOWMINIMIZED "" "Run $TUTNAME" - FindNext $2 $3 - goto iloop - idone: - next: - FindNext $0 $1 - Goto loop - done: - - !endif - -SectionEnd - - -Section -post - - !ifdef REGVIEW - SetRegView ${REGVIEW} - !endif - - !ifndef PPGAME - - # Add the "bin" directory to the PATH. - Push "$INSTDIR\python" - Call RemoveFromPath - Push "$INSTDIR\python\Scripts" - Call RemoveFromPath - Push "$INSTDIR\bin" - Call RemoveFromPath - Push "$INSTDIR\python" - Call AddToPath - Push "$INSTDIR\python\Scripts" - Call AddToPath - Push "$INSTDIR\bin" - Call AddToPath - - ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" "" - StrCmp $0 "$INSTDIR\python" RegPath 0 - StrCmp $0 "" RegPath 0 - - MessageBox MB_YESNO|MB_ICONQUESTION \ - "Your system already has a copy of Python installed. Panda3D installs its own copy of Python ${PYVER}, which will install alongside your existing copy. Would you like to make Panda's copy the default Python? If you choose 'No', you will have to configure your existing copy of Python to use the Panda3D libraries, keeping in mind that this version of Panda3D can only run with Python ${PYVER}." \ - IDNO SkipRegPath - - RegPath: - DetailPrint "Adding registry keys for python..." - WriteRegStr HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" "" "$INSTDIR\python" - SkipRegPath: - !endif - - DetailPrint "Adding the uninstaller ..." - Delete "$INSTDIR\uninst.exe" - WriteUninstaller "$INSTDIR\uninst.exe" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${SMDIRECTORY}" "DisplayName" "${SMDIRECTORY}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${SMDIRECTORY}" "UninstallString" '"$INSTDIR\uninst.exe"' - SetOutPath $INSTDIR - CreateShortcut "$SMPROGRAMS\${SMDIRECTORY}\Uninstall ${NAME}.lnk" "$INSTDIR\uninst.exe" "" - -SectionEnd - -Section Uninstall - - !ifdef REGVIEW - SetRegView ${REGVIEW} - !endif - - !ifndef PPGAME - Push "$INSTDIR\python" - Call un.RemoveFromPath - Push "$INSTDIR\python\Scripts" - Call un.RemoveFromPath - Push "$INSTDIR\bin" - Call un.RemoveFromPath - - ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" "" - StrCmp $0 "$INSTDIR\python" 0 SkipUnReg - DeleteRegKey HKLM "Software\Python\PythonCore\${PYVER}" - SkipUnReg: - !endif - - Delete "$INSTDIR\uninst.exe" - RMDir /r "$SMPROGRAMS\${SMDIRECTORY}" - RMDir /r "$INSTDIR" - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${SMDIRECTORY}" - -SectionEnd - -# --[ Utility Functions ]------------------------------------------------------ - -; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91 -Function IsNT - Push $0 - ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion - StrCmp $0 "" 0 IsNT_yes - ; we are not NT. - Pop $0 - Push 0 - Return - IsNT_yes: - ; NT!!! - Pop $0 - Push 1 -FunctionEnd - -; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91 -Function un.IsNT - Push $0 - ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion - StrCmp $0 "" 0 unIsNT_yes - ; we are not NT. - Pop $0 - Push 0 - Return - unIsNT_yes: - ; NT!!! - Pop $0 - Push 1 -FunctionEnd - -; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91 -Function StrStr - Push $0 - Exch - Pop $0 ; $0 now have the string to find - Push $1 - Exch 2 - Pop $1 ; $1 now have the string to find in - Exch - Push $2 - Push $3 - Push $4 - Push $5 - StrCpy $2 -1 - StrLen $3 $0 - StrLen $4 $1 - IntOp $4 $4 - $3 - unStrStr_loop: - IntOp $2 $2 + 1 - IntCmp $2 $4 0 0 unStrStrReturn_notFound - StrCpy $5 $1 $3 $2 - StrCmp $5 $0 unStrStr_done unStrStr_loop - unStrStrReturn_notFound: - StrCpy $2 -1 - unStrStr_done: - Pop $5 - Pop $4 - Pop $3 - Exch $2 - Exch 2 - Pop $0 - Pop $1 -FunctionEnd - -; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91 -Function un.StrStr - Push $0 - Exch - Pop $0 ; $0 now have the string to find - Push $1 - Exch 2 - Pop $1 ; $1 now have the string to find in - Exch - Push $2 - Push $3 - Push $4 - Push $5 - StrCpy $2 -1 - StrLen $3 $0 - StrLen $4 $1 - IntOp $4 $4 - $3 - unStrStr_loop: - IntOp $2 $2 + 1 - IntCmp $2 $4 0 0 unStrStrReturn_notFound - StrCpy $5 $1 $3 $2 - StrCmp $5 $0 unStrStr_done unStrStr_loop - unStrStrReturn_notFound: - StrCpy $2 -1 - unStrStr_done: - Pop $5 - Pop $4 - Pop $3 - Exch $2 - Exch 2 - Pop $0 - Pop $1 -FunctionEnd - -; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91 -; Commentary and smarter ';' checking by Jon Parise -Function AddToPath - Exch $0 - Push $1 - Push $2 - Push $3 - Call IsNT - Pop $1 - - StrCmp $1 1 AddToPath_NT - ; We're not on NT, so modify the AUTOEXEC.BAT file. - StrCpy $1 $WINDIR 2 - FileOpen $1 "$1\autoexec.bat" a - FileSeek $1 0 END - GetFullPathName /SHORT $0 $0 - FileWrite $1 "$\r$\nSET PATH=%PATH%;$0$\r$\n" - FileClose $1 - Goto AddToPath_done - - AddToPath_NT: - ReadRegStr $1 HKCU "Environment" "PATH" - Call IsUserAdmin - Pop $3 - ; If this is an Admin user, use the System env. variable instead of the user's env. variable - StrCmp $3 1 0 +2 - ReadRegStr $1 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" - - ; If the PATH string is empty, jump over the mangling routines. - StrCmp $1 "" AddToPath_NTdoIt - - ; Pull off the last character of the PATH string. If it's a semicolon, - ; we don't need to add another one, so jump to the section where we - ; append the new PATH component(s). - StrCpy $2 $1 1 -1 - StrCmp $2 ";" AddToPath_NTAddPath AddToPath_NTAddSemi - - AddToPath_NTAddSemi: - StrCpy $1 "$1;" - Goto AddToPath_NTAddPath - AddToPath_NTAddPath: - StrCpy $0 "$1$0" - Goto AddToPath_NTdoIt - AddToPath_NTdoIt: - Call IsUserAdmin - Pop $3 - StrCmp $3 1 0 NotAdmin - WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $0 - Goto AddToPath_done - - NotAdmin: - WriteRegExpandStr HKCU "Environment" "PATH" $0 - AddToPath_done: - SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 - Pop $3 - Pop $2 - Pop $1 - Pop $0 -FunctionEnd - -; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91 -Function RemoveFromPath - Exch $0 - Push $1 - Push $2 - Push $3 - Push $4 - Push $5 - Call IsNT - Pop $1 - StrCmp $1 1 unRemoveFromPath_NT - ; Not on NT - StrCpy $1 $WINDIR 2 - FileOpen $1 "$1\autoexec.bat" r - GetTempFileName $4 - FileOpen $2 $4 w - GetFullPathName /SHORT $0 $0 - StrCpy $0 "SET PATH=%PATH%;$0" - SetRebootFlag true - Goto unRemoveFromPath_dosLoop - - unRemoveFromPath_dosLoop: - FileRead $1 $3 - StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoop - StrCmp $3 "$0$\n" unRemoveFromPath_dosLoop - StrCmp $3 "$0" unRemoveFromPath_dosLoop - StrCmp $3 "" unRemoveFromPath_dosLoopEnd - FileWrite $2 $3 - Goto unRemoveFromPath_dosLoop - - unRemoveFromPath_dosLoopEnd: - FileClose $2 - FileClose $1 - StrCpy $1 $WINDIR 2 - Delete "$1\autoexec.bat" - CopyFiles /SILENT $4 "$1\autoexec.bat" - Delete $4 - Goto unRemoveFromPath_done - - unRemoveFromPath_NT: - StrLen $2 $0 - Call IsUserAdmin - Pop $5 - StrCmp $5 1 0 NotAdmin - ReadRegStr $1 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" - Push $1 - Push $0 - Call StrStr ; Find $0 in $1 - Pop $0 ; pos of our dir - IntCmp $0 -1 unRemoveFromPath_done - ; else, it is in path - StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir - IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';') - IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon. - StrLen $0 $1 - StrCpy $1 $1 $0 $2 - StrCpy $3 "$3$1" - WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $3 - SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 - Goto unRemoveFromPath_done - - - NotAdmin: - ReadRegStr $1 HKCU "Environment" "PATH" - Push $1 - Push $0 - Call StrStr ; Find $0 in $1 - Pop $0 ; pos of our dir - IntCmp $0 -1 unRemoveFromPath_done - ; else, it is in path - StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir - IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';') - IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon. - StrLen $0 $1 - StrCpy $1 $1 $0 $2 - StrCpy $3 "$3$1" - WriteRegExpandStr HKCU "Environment" "PATH" $3 - SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 - - unRemoveFromPath_done: - Pop $5 - Pop $4 - Pop $3 - Pop $2 - Pop $1 - Pop $0 -FunctionEnd - -; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91 -Function un.RemoveFromPath - Exch $0 - Push $1 - Push $2 - Push $3 - Push $4 - Push $5 - Call un.IsNT - Pop $1 - StrCmp $1 1 unRemoveFromPath_NT - ; Not on NT - StrCpy $1 $WINDIR 2 - FileOpen $1 "$1\autoexec.bat" r - GetTempFileName $4 - FileOpen $2 $4 w - GetFullPathName /SHORT $0 $0 - StrCpy $0 "SET PATH=%PATH%;$0" - SetRebootFlag true - Goto unRemoveFromPath_dosLoop - - unRemoveFromPath_dosLoop: - FileRead $1 $3 - StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoop - StrCmp $3 "$0$\n" unRemoveFromPath_dosLoop - StrCmp $3 "$0" unRemoveFromPath_dosLoop - StrCmp $3 "" unRemoveFromPath_dosLoopEnd - FileWrite $2 $3 - Goto unRemoveFromPath_dosLoop - - unRemoveFromPath_dosLoopEnd: - FileClose $2 - FileClose $1 - StrCpy $1 $WINDIR 2 - Delete "$1\autoexec.bat" - CopyFiles /SILENT $4 "$1\autoexec.bat" - Delete $4 - Goto unRemoveFromPath_done - - unRemoveFromPath_NT: - StrLen $2 $0 - Call un.IsUserAdmin - Pop $5 - StrCmp $5 1 0 NotAdmin - ReadRegStr $1 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" - Push $1 - Push $0 - Call un.StrStr ; Find $0 in $1 - Pop $0 ; pos of our dir - IntCmp $0 -1 unRemoveFromPath_done - ; else, it is in path - StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir - IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';') - IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon. - StrLen $0 $1 - StrCpy $1 $1 $0 $2 - StrCpy $3 "$3$1" - WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $3 - SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 - Goto unRemoveFromPath_done - - - NotAdmin: - ReadRegStr $1 HKCU "Environment" "PATH" - Push $1 - Push $0 - Call un.StrStr ; Find $0 in $1 - Pop $0 ; pos of our dir - IntCmp $0 -1 unRemoveFromPath_done - ; else, it is in path - StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir - IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';') - IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon. - StrLen $0 $1 - StrCpy $1 $1 $0 $2 - StrCpy $3 "$3$1" - WriteRegExpandStr HKCU "Environment" "PATH" $3 - SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 - - unRemoveFromPath_done: - Pop $5 - Pop $4 - Pop $3 - Pop $2 - Pop $1 - Pop $0 -FunctionEnd - -; From: http://nsis.sourceforge.net/archive/nsisweb.php?page=329&instances=0,11 -; Localized by Ben Johnson (bkj@andrew.cmu.edu) -Function IsUserAdmin - Push $0 - Push $1 - Push $2 - Push $3 - Call IsNT - Pop $1 - - ClearErrors - UserInfo::GetName - ;IfErrors Win9x - Pop $2 - UserInfo::GetAccountType - Pop $3 - - ; Compare results of IsNT with "1" - StrCmp $1 1 0 NotNT - ;This is NT - - - StrCmp $3 "Admin" 0 NotAdmin - ; Observation: I get here when running Win98SE. (Lilla) - ; The functions UserInfo.dll looks for are there on Win98 too, - ; but just don't work. So UserInfo.dll, knowing that admin isn't required - ; on Win98, returns admin anyway. (per kichik) - ; MessageBox MB_OK 'User "$R1" is in the Administrators group' - Pop $3 - Pop $2 - Pop $1 - Pop $0 - - Push 1 - Return - - NotAdmin: - ; You should still check for an empty string because the functions - ; UserInfo.dll looks for may not be present on Windows 95. (per kichik) - - #StrCmp $2 "" Win9x - #StrCpy $0 0 - ;MessageBox MB_OK 'User "$2" is in the "$3" group' - Pop $3 - Pop $2 - Pop $1 - Pop $0 - - Push 0 - Return - - ;Because we use IsNT, this is redundant. - #Win9x: - # ; comment/message below is by UserInfo.nsi author: - # ; This one means you don't need to care about admin or - # ; not admin because Windows 9x doesn't either - # ;MessageBox MB_OK "Error! This DLL can't run under Windows 9x!" - # StrCpy $0 0 - - NotNT: - ;We are not NT - ;Win9x doesn't have "admin" users. - ;Let the user do whatever. - Pop $3 - Pop $2 - Pop $1 - Pop $0 - - Push 1 - -FunctionEnd - -Function un.IsUserAdmin - Push $0 - Push $1 - Push $2 - Push $3 - Call un.IsNT - Pop $1 - - ClearErrors - UserInfo::GetName - ;IfErrors Win9x - Pop $2 - UserInfo::GetAccountType - Pop $3 - - ; Compare results of IsNT with "1" - StrCmp $1 1 0 NotNT - ;This is NT - - - StrCmp $3 "Admin" 0 NotAdmin - ; Observation: I get here when running Win98SE. (Lilla) - ; The functions UserInfo.dll looks for are there on Win98 too, - ; but just don't work. So UserInfo.dll, knowing that admin isn't required - ; on Win98, returns admin anyway. (per kichik) - ; MessageBox MB_OK 'User "$R1" is in the Administrators group' - Pop $3 - Pop $2 - Pop $1 - Pop $0 - - Push 1 - Return - - NotAdmin: - ; You should still check for an empty string because the functions - ; UserInfo.dll looks for may not be present on Windows 95. (per kichik) - - #StrCmp $2 "" Win9x - #StrCpy $0 0 - ;MessageBox MB_OK 'User "$2" is in the "$3" group' - Pop $3 - Pop $2 - Pop $1 - Pop $0 - - Push 0 - Return - - ;Because we use IsNT, this is redundant. - #Win9x: - # ; comment/message below is by UserInfo.nsi author: - # ; This one means you don't need to care about admin or - # ; not admin because Windows 9x doesn't either - # ;MessageBox MB_OK "Error! This DLL can't run under Windows 9x!" - # StrCpy $0 0 - - NotNT: - ;We are not NT - ;Win9x doesn't have "admin" users. - ;Let the user do whatever. - Pop $3 - Pop $2 - Pop $1 - Pop $0 - - Push 1 - -FunctionEnd - -Function StrRep - - ;Written by dirtydingus 2003-02-20 04:30:09 - ; USAGE - ;Push String to do replacement in (haystack) - ;Push String to replace (needle) - ;Push Replacement - ;Call StrRep - ;Pop $R0 result - ;StrCpy $Result STR $R0 - - Exch $R4 ; $R4 = Replacement String - Exch - Exch $R3 ; $R3 = String to replace (needle) - Exch 2 - Exch $R1 ; $R1 = String to do replacement in (haystack) - Push $R2 ; Replaced haystack - Push $R5 ; Len (needle) - Push $R6 ; len (haystack) - Push $R7 ; Scratch reg - StrCpy $R2 "" - StrLen $R5 $R3 - StrLen $R6 $R1 -loop: - StrCpy $R7 $R1 $R5 - StrCmp $R7 $R3 found - StrCpy $R7 $R1 1 ; - optimization can be removed if U know len needle=1 - StrCpy $R2 "$R2$R7" - StrCpy $R1 $R1 $R6 1 - StrCmp $R1 "" done loop -found: - StrCpy $R2 "$R2$R4" - StrCpy $R1 $R1 $R6 $R5 - StrCmp $R1 "" done loop -done: - StrCpy $R3 $R2 - Pop $R7 - Pop $R6 - Pop $R5 - Pop $R2 - Pop $R1 - Pop $R4 - Exch $R3 - -FunctionEnd - diff --git a/direct/src/directscripts/packpanda.py b/direct/src/directscripts/packpanda.py deleted file mode 100755 index c195017f04..0000000000 --- a/direct/src/directscripts/packpanda.py +++ /dev/null @@ -1,424 +0,0 @@ -############################################################################# -# -# packpanda - this is a tool that packages up a panda game into a -# convenient, easily-downloaded windows executable. Packpanda runs on linux -# and windows - on linux, it builds .debs and .rpms, on windows it relies on -# NSIS, the nullsoft scriptable install system, to do the hard work. -# -# This is intentionally a very simplistic game-packer with very -# limited options. The goal is simplicity, not feature richness. -# There are dozens of complex, powerful packaging tools already out -# there. This one is for people who just want to do it quick and -# easy. -# -############################################################################## - -import sys, os, getopt, shutil, py_compile, subprocess - -OPTIONLIST = [ -("dir", 1, "Name of directory containing game"), -("name", 1, "Human-readable name of the game"), -("version", 1, "Version number to add to game name"), -("rmdir", 2, "Delete all directories with given name"), -("rmext", 2, "Delete all files with given extension"), -("fast", 0, "Use fast compression instead of good compression"), -("bam", 0, "Generate BAM files, change default-model-extension to BAM"), -("pyc", 0, "Generate PYC files"), -] - -def ParseFailure(): - print("") - print("packpanda usage:") - print("") - for (opt, hasval, explanation) in OPTIONLIST: - if (hasval): - print(" --%-10s %s"%(opt+" x", explanation)) - else: - print(" --%-10s %s"%(opt+" ", explanation)) - sys.exit(1) - -def ParseOptions(args): - try: - options = {} - longopts = [] - for (opt, hasval, explanation) in OPTIONLIST: - if (hasval==2): - longopts.append(opt+"=") - options[opt] = [] - elif (hasval==1): - longopts.append(opt+"=") - options[opt] = "" - else: - longopts.append(opt) - options[opt] = 0 - opts, extras = getopt.getopt(args, "", longopts) - for option, value in opts: - for (opt, hasval, explanation) in OPTIONLIST: - if (option == "--"+opt): - if (hasval==2): options[opt].append(value) - elif (hasval==1): options[opt] = value - else: options[opt] = 1 - return options - except: ParseFailure(); - -OPTIONS = ParseOptions(sys.argv[1:]) - -############################################################################## -# -# Locate the relevant trees. -# -############################################################################## - -PANDA=None -for dir in sys.path: - if (dir != "") and os.path.exists(os.path.join(dir,"direct")) and os.path.exists(os.path.join(dir,"pandac")): - PANDA=os.path.abspath(dir) -if (PANDA is None): - sys.exit("Cannot locate the panda root directory in the python path (cannot locate directory containing direct and pandac).") -print("PANDA located at "+PANDA) - -if (os.path.exists(os.path.join(PANDA,"..","makepanda","makepanda.py"))) and (sys.platform != "win32" or os.path.exists(os.path.join(PANDA,"..","thirdparty","win-nsis","makensis.exe"))): - PSOURCE=os.path.abspath(os.path.join(PANDA,"..")) - if (sys.platform == "win32"): - NSIS=os.path.abspath(os.path.join(PANDA,"..","thirdparty","win-nsis")) -else: - PSOURCE=PANDA - if (sys.platform == "win32"): - NSIS=os.path.join(PANDA,"nsis") - -############################################################################## -# -# Identify the main parts of the game: DIR, NAME, MAIN, ICON, BITMAP, etc -# -############################################################################## - -VER=OPTIONS["version"] -DIR=OPTIONS["dir"] -if (DIR==""): - print("You must specify the --dir option.") - ParseFailure() -DIR=os.path.abspath(DIR) -MYDIR=os.path.abspath(os.getcwd()) -BASENAME=os.path.basename(DIR) -if (OPTIONS["name"] != ""): - NAME=OPTIONS["name"] -else: - NAME=BASENAME -SMDIRECTORY=NAME -if (VER!=""): SMDIRECTORY=SMDIRECTORY+" "+VER -PYTHONV="python"+sys.version[:3] -LICENSE=os.path.join(DIR, "license.txt") -OUTFILE=os.path.basename(DIR) -if (VER!=""): OUTFILE=OUTFILE+"-"+VER -if (sys.platform == "win32"): - ICON=os.path.join(DIR, "icon.ico") - BITMAP=os.path.join(DIR, "installer.bmp") - OUTFILE=os.path.abspath(OUTFILE+".exe") - INSTALLDIR='C:\\'+os.path.basename(DIR) - if (VER!=""): INSTALLDIR=INSTALLDIR+"-"+VER - COMPRESS="lzma" - if (OPTIONS["fast"]): COMPRESS="zlib" -if (OPTIONS["pyc"]): MAIN="main.pyc" -else: MAIN="main.py" - -def PrintFileStatus(label, file): - if (os.path.exists(file)): - print("%-15s: %s"%(label, file)) - else: - print("%-15s: %s (MISSING)"%(label, file)) - -PrintFileStatus("Dir", DIR) -print("%-15s: %s"%("Name", NAME)) -print("%-15s: %s"%("Start Menu", SMDIRECTORY)) -PrintFileStatus("Main", os.path.join(DIR, MAIN)) -if (sys.platform == "win32"): - PrintFileStatus("Icon", ICON) - PrintFileStatus("Bitmap", BITMAP) -PrintFileStatus("License", LICENSE) -print("%-15s: %s"%("Output", OUTFILE)) -if (sys.platform == "win32"): - print("%-15s: %s"%("Install Dir", INSTALLDIR)) - -if (os.path.isdir(DIR)==0): - sys.exit("Difficulty reading "+DIR+". Cannot continue.") - -if (os.path.isfile(os.path.join(DIR, "main.py"))==0): - sys.exit("Difficulty reading main.py. Cannot continue.") - -if (os.path.isfile(LICENSE)==0): - LICENSE=os.path.join(PANDA,"LICENSE") - -if (sys.platform == "win32") and (os.path.isfile(BITMAP)==0): - BITMAP=os.path.join(NSIS,"Contrib","Graphics","Wizard","nsis.bmp") - -if (sys.platform == "win32"): - if (os.path.isfile(ICON)==0): - PPICON="bin\\ppython.exe" - else: - PPICON="game\\icon.ico" - -############################################################################## -# -# Copy the game to a temporary directory, so we can modify it safely. -# -############################################################################## - -def limitedCopyTree(src, dst, rmdir): - if (os.path.isdir(src)): - if (os.path.basename(src) in rmdir): - return - if (not os.path.isdir(dst)): os.mkdir(dst) - for x in os.listdir(src): - limitedCopyTree(os.path.join(src,x), os.path.join(dst,x), rmdir) - else: - shutil.copyfile(src, dst) - - -TMPDIR=os.path.abspath("packpanda-TMP") -if (sys.platform == "win32"): - TMPGAME=os.path.join(TMPDIR,"game") - TMPETC=os.path.join(TMPDIR,"etc") -else: - TMPGAME=os.path.join(TMPDIR,"usr","share","games",BASENAME,"game") - TMPETC=os.path.join(TMPDIR,"usr","share","games",BASENAME,"etc") -print("") -print("Copying the game to "+TMPDIR+"...") -if (os.path.exists(TMPDIR)): - try: shutil.rmtree(TMPDIR) - except: sys.exit("Cannot delete "+TMPDIR) -try: - os.mkdir(TMPDIR) - rmdir = {} - for x in OPTIONS["rmdir"]: - rmdir[x] = 1 - if not os.path.isdir( TMPGAME ): - os.makedirs(TMPGAME) - limitedCopyTree(DIR, TMPGAME, rmdir) - if not os.path.isdir( TMPETC ): - os.makedirs(TMPETC) - if sys.platform == "win32": - limitedCopyTree(os.path.join(PANDA, "etc"), TMPETC, {}) - else: - shutil.copyfile("/etc/Config.prc", os.path.join(TMPETC, "Config.prc")) - shutil.copyfile("/etc/Confauto.prc", os.path.join(TMPETC, "Confauto.prc")) -except: sys.exit("Cannot copy game to "+TMPDIR) - -############################################################################## -# -# If --bam requested, change default-model-extension .egg to bam. -# -############################################################################## - -def ReadFile(wfile): - try: - srchandle = open(wfile, "rb") - data = srchandle.read() - srchandle.close() - return data - except: exit("Cannot read "+wfile) - -def WriteFile(wfile,data): - try: - dsthandle = open(wfile, "wb") - dsthandle.write(data) - dsthandle.close() - except: exit("Cannot write "+wfile) - -if OPTIONS["bam"]: - CONF=ReadFile(os.path.join(TMPETC,"Confauto.prc")) - CONF=CONF.replace("default-model-extension .egg","default-model-extension .bam") - WriteFile(os.path.join(TMPETC,"Confauto.prc"), CONF) - -############################################################################## -# -# Compile all py files, convert all egg files. -# -# We do this as a sanity check, even if the user -# hasn't requested that his files be compiled. -# -############################################################################## - -if (sys.platform == "win32"): - EGG2BAM=os.path.join(PANDA,"bin","egg2bam.exe") -else: - EGG2BAM=os.path.join(PANDA,"bin","egg2bam") - -def egg2bam(file,bam): - present = os.path.exists(bam) - if (present): bam = "packpanda-TMP.bam"; - cmd = 'egg2bam -noabs -ps rel -pd . "'+file+'" -o "'+bam+'"' - print("Executing: "+cmd) - if (sys.platform == "win32"): - res = os.spawnl(os.P_WAIT, EGG2BAM, cmd) - else: - res = os.system(cmd) - if (res != 0): sys.exit("Problem in egg file: "+file) - if (present) or (OPTIONS["bam"]==0): - os.unlink(bam) - -def py2pyc(file): - print("Compiling python "+file) - pyc = file[:-3]+'.pyc' - pyo = file[:-3]+'.pyo' - if (os.path.exists(pyc)): os.unlink(pyc) - if (os.path.exists(pyo)): os.unlink(pyo) - try: py_compile.compile(file) - except: sys.exit("Cannot compile "+file) - if (OPTIONS["pyc"]==0): - if (os.path.exists(pyc)): - os.unlink(pyc) - if (os.path.exists(pyo)): - os.unlink(pyo) - -def CompileFiles(file): - if (os.path.isfile(file)): - if (file.endswith(".egg")): - egg2bam(file, file[:-4]+'.bam') - elif (file.endswith(".egg.pz") or file.endswith(".egg.gz")): - egg2bam(file, file[:-7]+'.bam') - elif (file.endswith(".py")): - py2pyc(file) - else: pass - elif (os.path.isdir(file)): - for x in os.listdir(file): - CompileFiles(os.path.join(file, x)) - -def DeleteFiles(file): - base = os.path.basename(file).lower() - if (os.path.isdir(file)): - for pattern in OPTIONS["rmdir"]: - if pattern.lower() == base: - print("Deleting "+file) - shutil.rmtree(file) - return - for x in os.listdir(file): - DeleteFiles(os.path.join(file, x)) - else: - for ext in OPTIONS["rmext"]: - if base[-(len(ext) + 1):] == ("." + ext).lower(): - print("Deleting "+file) - os.unlink(file) - return - -print("") -print("Compiling BAM and PYC files...") -os.chdir(TMPGAME) -CompileFiles(".") -DeleteFiles(".") - -############################################################################## -# -# Now make the installer. Yay! -# -############################################################################## - -INSTALLER_DEB_FILE=""" -Package: BASENAME -Version: VERSION -Section: games -Priority: optional -Architecture: ARCH -Essential: no -Depends: PYTHONV -Provides: BASENAME -Description: NAME -Maintainer: Unknown -""" - -INSTALLER_SPEC_FILE=""" -Summary: NAME -Name: BASENAME -Version: VERSION -Release: 1 -Group: Amusement/Games -License: See license file -BuildRoot: TMPDIR -BuildRequires: PYTHONV -%description -NAME -%files -%defattr(-,root,root) -/usr/bin/BASENAME -/usr/lib/games/BASENAME -/usr/share/games/BASENAME -""" - -RUN_SCRIPT=""" -#!/bin/sh -cd /usr/share/games/BASENAME/game -PYTHONPATH=/usr/lib/games/BASENAME:/usr/share/games/BASENAME -LD_LIBRARY_PATH=/usr/lib/games/BASENAME -PYTHONV MAIN -""" - -if (sys.platform == "win32"): - CMD="\""+NSIS+"\\makensis.exe\" /V2 " - CMD=CMD+'/DCOMPRESSOR="'+COMPRESS+'" ' - CMD=CMD+'/DNAME="'+NAME+'" ' - CMD=CMD+'/DSMDIRECTORY="'+SMDIRECTORY+'" ' - CMD=CMD+'/DINSTALLDIR="'+INSTALLDIR+'" ' - CMD=CMD+'/DOUTFILE="'+OUTFILE+'" ' - CMD=CMD+'/DLICENSE="'+LICENSE+'" ' - CMD=CMD+'/DLANGUAGE="English" ' - CMD=CMD+'/DRUNTEXT="Play '+NAME+'" ' - CMD=CMD+'/DIBITMAP="'+BITMAP+'" ' - CMD=CMD+'/DUBITMAP="'+BITMAP+'" ' - CMD=CMD+'/DPANDA="'+PANDA+'" ' - CMD=CMD+'/DPANDACONF="'+TMPETC+'" ' - CMD=CMD+'/DPSOURCE="'+PSOURCE+'" ' - CMD=CMD+'/DPPGAME="'+TMPGAME+'" ' - CMD=CMD+'/DPPMAIN="'+MAIN+'" ' - CMD=CMD+'/DPPICON="'+PPICON+'" ' - CMD=CMD+'"'+PSOURCE+'\\direct\\directscripts\\packpanda.nsi"' - - print("") - print(CMD) - print("packing...") - subprocess.call(CMD) -else: - os.chdir(MYDIR) - os.system("mkdir -p %s/usr/bin" % TMPDIR) - os.system("mkdir -p %s/usr/share/games/%s" % (TMPDIR, BASENAME)) - os.system("mkdir -p %s/usr/lib/games/%s" % (TMPDIR, BASENAME)) - os.system("cp --recursive %s/direct %s/usr/share/games/%s/direct" % (PANDA, TMPDIR, BASENAME)) - os.system("cp --recursive %s/pandac %s/usr/share/games/%s/pandac" % (PANDA, TMPDIR, BASENAME)) - os.system("cp --recursive %s/models %s/usr/share/games/%s/models" % (PANDA, TMPDIR, BASENAME)) - os.system("cp --recursive %s/Pmw %s/usr/share/games/%s/Pmw" % (PANDA, TMPDIR, BASENAME)) - os.system("cp %s %s/usr/share/games/%s/LICENSE" % (LICENSE, TMPDIR, BASENAME)) - os.system("cp --recursive /usr/lib/panda3d/* %s/usr/lib/games/%s/" % (TMPDIR, BASENAME)) - - # Make the script to run the game - txt = RUN_SCRIPT[1:].replace("BASENAME",BASENAME).replace("PYTHONV",PYTHONV).replace("MAIN",MAIN) - WriteFile(TMPDIR+"/usr/bin/"+BASENAME, txt) - os.system("chmod +x "+TMPDIR+"/usr/bin/"+BASENAME) - - if (os.path.exists("/usr/bin/rpmbuild")): - os.system("rm -rf %s/DEBIAN" % TMPDIR) - os.system("rpm -E '%_target_cpu' > packpanda-TMP.txt") - ARCH=ReadFile("packpanda-TMP.txt").strip() - os.remove("packpanda-TMP.txt") - txt = INSTALLER_SPEC_FILE[1:].replace("VERSION",VER).replace("TMPDIR",TMPDIR) - txt = txt.replace("BASENAME",BASENAME).replace("NAME",NAME).replace("PYTHONV",PYTHONV) - WriteFile("packpanda-TMP.spec", txt) - os.system("rpmbuild --define '_rpmdir "+TMPDIR+"' -bb packpanda-TMP.spec") - os.system("mv "+ARCH+"/"+BASENAME+"-"+VER+"-1."+ARCH+".rpm .") - os.rmdir(ARCH) - os.remove("packpanda-TMP.spec") - - if (os.path.exists("/usr/bin/dpkg-deb")): - os.system("dpkg --print-architecture > packpanda-TMP.txt") - ARCH=ReadFile("packpanda-TMP.txt").strip() - os.remove("packpanda-TMP.txt") - txt = INSTALLER_DEB_FILE[1:].replace("VERSION",str(VER)).replace("PYTHONV",PYTHONV) - txt = txt.replace("BASENAME",BASENAME).replace("NAME",NAME).replace("ARCH",ARCH) - os.system("mkdir -p %s/DEBIAN" % TMPDIR) - os.system("cd %s ; (find usr -type f -exec md5sum {} \;) > DEBIAN/md5sums" % TMPDIR) - WriteFile(TMPDIR+"/DEBIAN/control",txt) - os.system("dpkg-deb -b "+TMPDIR+" "+BASENAME+"_"+VER+"_"+ARCH+".deb") - - if not(os.path.exists("/usr/bin/rpmbuild") or os.path.exists("/usr/bin/dpkg-deb")): - exit("To build an installer, either rpmbuild or dpkg-deb must be present on your system!") - -# Dummy main function so this can be added to console_scripts. -def main(): - return 0 diff --git a/direct/src/directtools/DirectCameraControl.py b/direct/src/directtools/DirectCameraControl.py index 2913260c57..7870fb09f5 100644 --- a/direct/src/directtools/DirectCameraControl.py +++ b/direct/src/directtools/DirectCameraControl.py @@ -413,7 +413,7 @@ class DirectCameraControl(DirectObject): np = NodePath('temp') np.setPos(base.direct.camera, hitPt) self.coaMarkerPos = np.getPos() - np.remove() + np.removeNode() self.coaMarker.setPos(self.coaMarkerPos) iRay.collisionNodePath.removeNode() diff --git a/direct/src/directtools/DirectManipulation.py b/direct/src/directtools/DirectManipulation.py index 3ebc697400..d7aeb1ee3b 100644 --- a/direct/src/directtools/DirectManipulation.py +++ b/direct/src/directtools/DirectManipulation.py @@ -186,7 +186,7 @@ class DirectManipulationControl(DirectObject): def drawMarquee(self, startX, startY): if self.marquee: - self.marquee.remove() + self.marquee.removeNode() self.marquee = None if base.direct.cameraControl.useMayaCamControls and base.direct.fAlt: @@ -228,7 +228,7 @@ class DirectManipulationControl(DirectObject): skipFlags |= SKIP_CAMERA * (1 - base.getControl()) if self.marquee: - self.marquee.remove() + self.marquee.removeNode() self.marquee = None base.direct.deselectAll() @@ -1691,7 +1691,7 @@ class ObjectHandles(NodePath, DirectObject): np.setPos(base.direct.camera, hitPt) resultPt = Point3(0) resultPt.assign(np.getPos()) - np.remove() + np.removeNode() del iRay return resultPt diff --git a/direct/src/directtools/DirectSession.py b/direct/src/directtools/DirectSession.py index 96f8bdb610..367b753064 100644 --- a/direct/src/directtools/DirectSession.py +++ b/direct/src/directtools/DirectSession.py @@ -900,7 +900,7 @@ class DirectSession(DirectObject): # If nothing specified, try selected node path nodePath = self.selected.last if nodePath: - nodePath.remove() + nodePath.removeNode() def removeAllSelected(self): self.selected.removeAll() diff --git a/direct/src/directtools/__init__.py b/direct/src/directtools/__init__.py index e69de29bb2..14c68c31bf 100644 --- a/direct/src/directtools/__init__.py +++ b/direct/src/directtools/__init__.py @@ -0,0 +1,8 @@ +""" +This package contains the DIRECT tools, a set of tkinter tools for exploring +and manipulating the Panda3D scene graph. By default, these are disabled, +but they can be explicitly enabled using the following PRC configuration:: + + want-directtools true + want-tk true +""" diff --git a/direct/src/directutil/__init__.py b/direct/src/directutil/__init__.py index e69de29bb2..b242b7639e 100644 --- a/direct/src/directutil/__init__.py +++ b/direct/src/directutil/__init__.py @@ -0,0 +1,3 @@ +""" +This package contains assorted utility classes. +""" diff --git a/direct/src/distributed/DistributedObject.py b/direct/src/distributed/DistributedObject.py index 0ed3e90a54..64fd3ecc20 100644 --- a/direct/src/distributed/DistributedObject.py +++ b/direct/src/distributed/DistributedObject.py @@ -4,7 +4,6 @@ from panda3d.core import * from panda3d.direct import * from direct.directnotify.DirectNotifyGlobal import directNotify from direct.distributed.DistributedObjectBase import DistributedObjectBase -from direct.showbase.PythonUtil import StackTrace #from PyDatagram import PyDatagram #from PyDatagramIterator import PyDatagramIterator @@ -259,7 +258,10 @@ class DistributedObject(DistributedObjectBase): def _destroyDO(self): # after this is called, the object is no longer a DistributedObject # but may still be used as a DelayDeleted object - self.destroyDoStackTrace = StackTrace() + if __debug__: + # StackTrace is omitted in packed versions + from direct.showbase.PythonUtil import StackTrace + self.destroyDoStackTrace = StackTrace() # check for leftover cached data that was not retrieved or flushed by this object # this will catch typos in the data name in calls to get/setCachedData if hasattr(self, '_cachedData'): diff --git a/direct/src/distributed/DistributedSmoothNode.py b/direct/src/distributed/DistributedSmoothNode.py index 1d6aef43fb..9bc75c1da1 100644 --- a/direct/src/distributed/DistributedSmoothNode.py +++ b/direct/src/distributed/DistributedSmoothNode.py @@ -7,19 +7,21 @@ from . import DistributedNode from . import DistributedSmoothNodeBase from direct.task.Task import cont +config = get_config_showbase() + # This number defines our tolerance for out-of-sync telemetry packets. # If a packet appears to have originated from more than MaxFuture # seconds in the future, assume we're out of sync with the other # avatar and suggest a resync for both. -MaxFuture = base.config.GetFloat("smooth-max-future", 0.2) +MaxFuture = config.GetFloat("smooth-max-future", 0.2) # How frequently can we suggest a resynchronize with another client? -MinSuggestResync = base.config.GetFloat("smooth-min-suggest-resync", 15) +MinSuggestResync = config.GetFloat("smooth-min-suggest-resync", 15) # These flags indicate whether global smoothing and/or prediction is # allowed or disallowed. -EnableSmoothing = base.config.GetBool("smooth-enable-smoothing", 1) -EnablePrediction = base.config.GetBool("smooth-enable-prediction", 1) +EnableSmoothing = config.GetBool("smooth-enable-smoothing", 1) +EnablePrediction = config.GetBool("smooth-enable-prediction", 1) # These values represent the amount of time, in seconds, to delay the # apparent position of other avatars, when non-predictive and @@ -27,8 +29,8 @@ EnablePrediction = base.config.GetBool("smooth-enable-prediction", 1) # addition to the automatic delay of the observed average latency from # each avatar, which is intended to compensate for relative clock # skew. -Lag = base.config.GetDouble("smooth-lag", 0.2) -PredictionLag = base.config.GetDouble("smooth-prediction-lag", 0.0) +Lag = config.GetDouble("smooth-lag", 0.2) +PredictionLag = config.GetDouble("smooth-prediction-lag", 0.0) GlobalSmoothing = 0 diff --git a/direct/src/distributed/DoCollectionManager.py b/direct/src/distributed/DoCollectionManager.py index 16bf345f4e..fcff96e218 100755 --- a/direct/src/distributed/DoCollectionManager.py +++ b/direct/src/distributed/DoCollectionManager.py @@ -312,7 +312,6 @@ class DoCollectionManager: else: self.notify.warning('handleSetLocation: object %s not present' % self.getMsgChannel()) - @exceptionLogged() def storeObjectLocation(self, object, parentId, zoneId): oldParentId = object.parentId oldZoneId = object.zoneId diff --git a/direct/src/distributed/StagedObject.py b/direct/src/distributed/StagedObject.py index 7692dc3684..3127bd6e06 100755 --- a/direct/src/distributed/StagedObject.py +++ b/direct/src/distributed/StagedObject.py @@ -17,7 +17,6 @@ class StagedObject: call any "handle" functions. """ self.__state = initState - pass def goOnStage(self, *args, **kw): """ @@ -29,8 +28,6 @@ class StagedObject: if not self.isOnStage(): self.handleOnStage(*args, **kw) - pass - pass def handleOnStage(self): """ @@ -39,7 +36,6 @@ class StagedObject: Don't forget to call down to this one, though. """ self.__state = StagedObject.ON - pass def goOffStage(self, *args, **kw): """ @@ -51,8 +47,6 @@ class StagedObject: if not self.isOffStage(): self.handleOffStage(*args, **kw) - pass - pass def handleOffStage(self): """ @@ -61,7 +55,6 @@ class StagedObject: Don't forget to call down to this one, though. """ self.__state = StagedObject.OFF - pass def isOnStage(self): return self.__state == StagedObject.ON diff --git a/direct/src/distributed/__init__.py b/direct/src/distributed/__init__.py index e69de29bb2..cf5b4bc502 100644 --- a/direct/src/distributed/__init__.py +++ b/direct/src/distributed/__init__.py @@ -0,0 +1,5 @@ +""" +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. +""" diff --git a/direct/src/distributed/cConnectionRepository.h b/direct/src/distributed/cConnectionRepository.h index 8d62c21d86..fbc494423e 100644 --- a/direct/src/distributed/cConnectionRepository.h +++ b/direct/src/distributed/cConnectionRepository.h @@ -55,8 +55,8 @@ class SocketStream; */ class EXPCL_DIRECT CConnectionRepository { PUBLISHED: - CConnectionRepository(bool has_owner_view = false, - bool threaded_net = false); + explicit CConnectionRepository(bool has_owner_view = false, + bool threaded_net = false); ~CConnectionRepository(); /* diff --git a/direct/src/extensions_native/__init__.py b/direct/src/extensions_native/__init__.py index 8b13789179..093c05df15 100644 --- a/direct/src/extensions_native/__init__.py +++ b/direct/src/extensions_native/__init__.py @@ -1 +1,4 @@ - +""" +This package contains various Python methods that extend some of Panda's +underlying C++ classes. +""" diff --git a/direct/src/filter/CommonFilters.py b/direct/src/filter/CommonFilters.py index 9cfe72d987..493f2d9794 100644 --- a/direct/src/filter/CommonFilters.py +++ b/direct/src/filter/CommonFilters.py @@ -536,3 +536,24 @@ class CommonFilters: del self.configuration["GammaAdjust"] return self.reconfigure((old_gamma != 1.0), "GammaAdjust") return True + + #snake_case alias: + del_cartoon_ink = delCartoonInk + set_half_pixel_shift = setHalfPixelShift + del_half_pixel_shift = delHalfPixelShift + set_inverted = setInverted + 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 + set_cartoon_ink = setCartoonInk + del_bloom = delBloom + del_ambient_occlusion = delAmbientOcclusion + load_shader = loadShader + set_blur_sharpen = setBlurSharpen + del_blur_sharpen = delBlurSharpen + del_volumetric_lighting = delVolumetricLighting + set_gamma_adjust = setGammaAdjust diff --git a/direct/src/filter/FilterManager.py b/direct/src/filter/FilterManager.py index bf35abf3c9..1de63c702d 100644 --- a/direct/src/filter/FilterManager.py +++ b/direct/src/filter/FilterManager.py @@ -349,3 +349,15 @@ class FilterManager(DirectObject): self.nextsort = self.win.getSort() - 1000 self.basex = 0 self.basey = 0 + + #snake_case alias: + is_fullscreen = isFullscreen + resize_buffers = resizeBuffers + set_stacked_clears = setStackedClears + render_scene_into = renderSceneInto + get_scaled_size = getScaledSize + render_quad_into = renderQuadInto + get_clears = getClears + set_clears = setClears + create_buffer = createBuffer + window_event = windowEvent diff --git a/direct/src/filter/__init__.py b/direct/src/filter/__init__.py index e69de29bb2..4acb30b438 100644 --- a/direct/src/filter/__init__.py +++ b/direct/src/filter/__init__.py @@ -0,0 +1,11 @@ +""" +This package contains functionality for applying post-processing +filters to the result of rendering a 3-D scene. This is done by +rendering the scene to an off-screen buffer, and then applying this to +a full-screen card that has a shader applied which manipulates the +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. +""" diff --git a/direct/src/filter/filter-bloomx.sha b/direct/src/filter/filter-bloomx.sha index 6819936fc4..300ffeff2e 100644 --- a/direct/src/filter/filter-bloomx.sha +++ b/direct/src/filter/filter-bloomx.sha @@ -12,8 +12,9 @@ void vshader(float4 vtx_position : POSITION, l_position=mul(mat_modelproj, vtx_position); float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy; float offset = texpix_src.x; - l_texcoord0 = float4(c.x-offset* -4, c.x-offset* -3, c.x-offset* -2, c.y); - l_texcoord1 = float4(c.x-offset* -1, c.x-offset* 0, c.x-offset* 1, c.y); + float pad = texpad_src.x * 2; + l_texcoord0 = float4(min(c.x-offset* -4, pad), min(c.x-offset* -3, pad), min(c.x-offset* -2, pad), c.y); + l_texcoord1 = float4(min(c.x-offset* -1, pad), c.x-offset* 0, c.x-offset* 1, c.y); l_texcoord2 = float4(c.x-offset* 2, c.x-offset* 3, c.x-offset* 4, c.y); } diff --git a/direct/src/filter/filter-bloomy.sha b/direct/src/filter/filter-bloomy.sha index 2351c70dd3..51912b8b60 100644 --- a/direct/src/filter/filter-bloomy.sha +++ b/direct/src/filter/filter-bloomy.sha @@ -12,8 +12,9 @@ void vshader(float4 vtx_position : POSITION, l_position=mul(mat_modelproj, vtx_position); float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy; float offset = texpix_src.y; - l_texcoord0 = float4(c.y-offset* -4, c.y-offset* -3, c.y-offset* -2, c.x); - l_texcoord1 = float4(c.y-offset* -1, c.y-offset* 0, c.y-offset* 1, c.x); + float pad = texpad_src.y * 2; + l_texcoord0 = float4(min(c.y-offset* -4, pad), min(c.y-offset* -3, pad), min(c.y-offset* -2, pad), c.x); + l_texcoord1 = float4(min(c.y-offset* -1, pad), c.y-offset* 0, c.y-offset* 1, c.x); l_texcoord2 = float4(c.y-offset* 2, c.y-offset* 3, c.y-offset* 4, c.x); } diff --git a/direct/src/filter/filter-blurx.sha b/direct/src/filter/filter-blurx.sha index 2cb6c1512d..47a2217af3 100644 --- a/direct/src/filter/filter-blurx.sha +++ b/direct/src/filter/filter-blurx.sha @@ -2,7 +2,7 @@ // //Cg profile arbvp1 arbfp1 -void vshader(float4 vtx_position : POSITION, +void vshader(float4 vtx_position : POSITION, float2 vtx_texcoord0 : TEXCOORD0, out float4 l_position : POSITION, out float2 l_texcoord0 : TEXCOORD0, @@ -17,16 +17,18 @@ void vshader(float4 vtx_position : POSITION, void fshader(float2 l_texcoord0 : TEXCOORD0, out float4 o_color : COLOR, uniform float2 texpix_src, + uniform float4 texpad_src, uniform sampler2D k_src : TEXUNIT0) { + float pad = texpad_src.x * 2; float3 offset = float3(1.0*texpix_src.x, 2.0*texpix_src.x, 3.0*texpix_src.x); o_color = tex2D(k_src, l_texcoord0); o_color += tex2D(k_src, float2(l_texcoord0.x - offset.z, l_texcoord0.y)); o_color += tex2D(k_src, float2(l_texcoord0.x - offset.y, l_texcoord0.y)); o_color += tex2D(k_src, float2(l_texcoord0.x - offset.x, l_texcoord0.y)); - o_color += tex2D(k_src, float2(l_texcoord0.x + offset.x, l_texcoord0.y)); - o_color += tex2D(k_src, float2(l_texcoord0.x + offset.y, l_texcoord0.y)); - o_color += tex2D(k_src, float2(l_texcoord0.x + offset.z, l_texcoord0.y)); + o_color += tex2D(k_src, float2(min(l_texcoord0.x + offset.x, pad), l_texcoord0.y)); + o_color += tex2D(k_src, float2(min(l_texcoord0.x + offset.y, pad), l_texcoord0.y)); + o_color += tex2D(k_src, float2(min(l_texcoord0.x + offset.z, pad), l_texcoord0.y)); o_color /= 7; o_color.w = 1; } diff --git a/direct/src/filter/filter-blury.sha b/direct/src/filter/filter-blury.sha index b927cf0329..ab135c363a 100644 --- a/direct/src/filter/filter-blury.sha +++ b/direct/src/filter/filter-blury.sha @@ -2,7 +2,7 @@ // //Cg profile arbvp1 arbfp1 -void vshader(float4 vtx_position : POSITION, +void vshader(float4 vtx_position : POSITION, float2 vtx_texcoord0 : TEXCOORD0, out float4 l_position : POSITION, out float2 l_texcoord0 : TEXCOORD0, @@ -17,16 +17,18 @@ void vshader(float4 vtx_position : POSITION, void fshader(float2 l_texcoord0 : TEXCOORD0, out float4 o_color : COLOR, uniform float2 texpix_src, + uniform float4 texpad_src, uniform sampler2D k_src : TEXUNIT0) { + float pad = texpad_src.y * 2; float3 offset = float3(1.0*texpix_src.y, 2.0*texpix_src.y, 3.0*texpix_src.y); o_color = tex2D(k_src, l_texcoord0); o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y - offset.z)); o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y - offset.y)); o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y - offset.x)); - o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y + offset.x)); - o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y + offset.y)); - o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y + offset.z)); + o_color += tex2D(k_src, float2(l_texcoord0.x, min(l_texcoord0.y + offset.x, pad))); + o_color += tex2D(k_src, float2(l_texcoord0.x, min(l_texcoord0.y + offset.y, pad))); + o_color += tex2D(k_src, float2(l_texcoord0.x, min(l_texcoord0.y + offset.z, pad))); o_color /= 7; o_color.w = 1; } diff --git a/direct/src/fsm/ClassicFSM.py b/direct/src/fsm/ClassicFSM.py index 847cf290db..c954718e4d 100644 --- a/direct/src/fsm/ClassicFSM.py +++ b/direct/src/fsm/ClassicFSM.py @@ -1,13 +1,13 @@ -"""Undocumented Module""" - -__all__ = ['ClassicFSM'] - """Finite State Machine module: contains the ClassicFSM class. -This module and class exist only for backward compatibility with -existing code. New code should use the FSM module instead. +.. note:: + + This module and class exist only for backward compatibility with + existing code. New code should use the :mod:`.FSM` module instead. """ +__all__ = ['ClassicFSM'] + from direct.directnotify.DirectNotifyGlobal import directNotify from direct.showbase.DirectObject import DirectObject import weakref diff --git a/direct/src/fsm/FSM.py b/direct/src/fsm/FSM.py index 3aa583f007..6af064e3d7 100644 --- a/direct/src/fsm/FSM.py +++ b/direct/src/fsm/FSM.py @@ -1,5 +1,5 @@ """The new Finite State Machine module. This replaces the module -previously called FSM.py (now called ClassicFSM.py). +previously called FSM (now called :mod:`.ClassicFSM`). """ __all__ = ['FSMException', 'FSM'] diff --git a/direct/src/fsm/FourState.py b/direct/src/fsm/FourState.py index df653825f9..5188284baf 100755 --- a/direct/src/fsm/FourState.py +++ b/direct/src/fsm/FourState.py @@ -1,9 +1,7 @@ -"""Undocumented Module""" +"""Contains the FourState class.""" __all__ = ['FourState'] - - from direct.directnotify import DirectNotifyGlobal #import DistributedObject from . import ClassicFSM @@ -20,7 +18,7 @@ class FourState: Inherit from FourStateFSM and pass in your states. Two of the states should be oposites of each other and the other two should be the transition states between the first two. - E.g. + E.g:: +--------+ -->| closed | -- @@ -90,7 +88,8 @@ class FourState: off (and so is state 2 which is oposite of 4 and therefore oposite of 'on'). """ - assert self.debugPrint("FourState(names=%s)"%(names)) + self.stateIndex = 0 + assert self.__debugPrint("FourState(names=%s)"%(names)) self.track = None self.stateTime = 0.0 self.names = names @@ -120,7 +119,6 @@ class FourState: self.exitState4, [names[1]]), } - self.stateIndex = 0 self.fsm = ClassicFSM.ClassicFSM('FourState', list(self.states.values()), # Initial State @@ -131,7 +129,7 @@ class FourState: self.fsm.enterInitialState() def setTrack(self, track): - assert self.debugPrint("setTrack(track=%s)"%(track,)) + assert self.__debugPrint("setTrack(track=%s)"%(track,)) if self.track is not None: self.track.pause() self.track = None @@ -147,27 +145,27 @@ class FourState: # If the client wants the state changed it needs to # send a request to the AI. #def setIsOn(self, isOn): - # assert self.debugPrint("setIsOn(isOn=%s)"%(isOn,)) + # assert self.__debugPrint("setIsOn(isOn=%s)"%(isOn,)) # pass def isOn(self): - assert self.debugPrint("isOn() returning %s (stateIndex=%s)"%(self.stateIndex==4, self.stateIndex)) + assert self.__debugPrint("isOn() returning %s (stateIndex=%s)"%(self.stateIndex==4, self.stateIndex)) return self.stateIndex==4 def changedOnState(self, isOn): """ Allow derived classes to overide this. """ - assert self.debugPrint("changedOnState(isOn=%s)"%(isOn,)) + assert self.__debugPrint("changedOnState(isOn=%s)"%(isOn,)) ##### state 0 ##### def enterState0(self): - assert self.debugPrint("enter0()") + assert self.__debugPrint("enter0()") self.enterStateN(0) def exitState0(self): - assert self.debugPrint("exit0()") + assert self.__debugPrint("exit0()") # It's important for FourStates to broadcast their state # when they are generated on the client. Before I put this in, # if a door was generated and went directly to an 'open' state, @@ -177,43 +175,43 @@ class FourState: ##### state 1 ##### def enterState1(self): - assert self.debugPrint("enterState1()") + assert self.__debugPrint("enterState1()") self.enterStateN(1) def exitState1(self): - assert self.debugPrint("exitState1()") + assert self.__debugPrint("exitState1()") ##### state 2 ##### def enterState2(self): - assert self.debugPrint("enterState2()") + assert self.__debugPrint("enterState2()") self.enterStateN(2) def exitState2(self): - assert self.debugPrint("exitState2()") + assert self.__debugPrint("exitState2()") ##### state 3 ##### def enterState3(self): - assert self.debugPrint("enterState3()") + assert self.__debugPrint("enterState3()") self.enterStateN(3) def exitState3(self): - assert self.debugPrint("exitState3()") + assert self.__debugPrint("exitState3()") ##### state 4 ##### def enterState4(self): - assert self.debugPrint("enterState4()") + assert self.__debugPrint("enterState4()") self.enterStateN(4) self.changedOnState(1) def exitState4(self): - assert self.debugPrint("exitState4()") + assert self.__debugPrint("exitState4()") self.changedOnState(0) if __debug__: - def debugPrint(self, message): + def __debugPrint(self, message): """for debugging""" return self.notify.debug("%d (%d) %s"%( id(self), self.stateIndex==4, message)) diff --git a/direct/src/fsm/FourStateAI.py b/direct/src/fsm/FourStateAI.py index 6f5ff8d966..a17b9e4c62 100755 --- a/direct/src/fsm/FourStateAI.py +++ b/direct/src/fsm/FourStateAI.py @@ -1,9 +1,7 @@ -"""Undocumented Module""" +"""Contains the FourStateAI class. See also :mod:`.FourState`.""" __all__ = ['FourStateAI'] - - from direct.directnotify import DirectNotifyGlobal #import DistributedObjectAI from . import ClassicFSM @@ -21,7 +19,7 @@ class FourStateAI: Inherit from FourStateFSM and pass in your states. Two of the states should be oposites of each other and the other two should be the transition states between the first two. - E.g. + E.g:: +--------+ -->| closed | -- @@ -93,11 +91,11 @@ class FourStateAI: off (and so is state 2 which is oposite of state 4 and therefore oposite of 'on'). """ - assert self.debugPrint( + self.stateIndex = 0 + assert self.__debugPrint( "FourStateAI(names=%s, durations=%s)" %(names, durations)) self.doLaterTask = None - self.stateIndex = 0 assert len(names) == 5 assert len(names) == len(durations) self.names = names @@ -137,7 +135,7 @@ class FourStateAI: self.fsm.enterInitialState() def delete(self): - assert self.debugPrint("delete()") + assert self.__debugPrint("delete()") if self.doLaterTask is not None: self.doLaterTask.remove() del self.doLaterTask @@ -145,15 +143,15 @@ class FourStateAI: del self.fsm def getState(self): - assert self.debugPrint("getState() returning %s"%(self.stateIndex,)) + assert self.__debugPrint("getState() returning %s"%(self.stateIndex,)) return [self.stateIndex] def sendState(self): - assert self.debugPrint("sendState()") + assert self.__debugPrint("sendState()") self.sendUpdate('setState', self.getState()) def setIsOn(self, isOn): - assert self.debugPrint("setIsOn(isOn=%s)"%(isOn,)) + assert self.__debugPrint("setIsOn(isOn=%s)"%(isOn,)) if isOn: if self.stateIndex != 4: # ...if it's not On; request turning on: @@ -170,7 +168,7 @@ class FourStateAI: # self.fsm.request(self.states[nextState]) def isOn(self): - assert self.debugPrint("isOn() returning %s (stateIndex=%s)"%(self.stateIndex==4, self.stateIndex)) + assert self.__debugPrint("isOn() returning %s (stateIndex=%s)"%(self.stateIndex==4, self.stateIndex)) return self.stateIndex==4 def changedOnState(self, isOn): @@ -179,12 +177,12 @@ class FourStateAI: The self.isOn value has toggled. Call getIsOn() to get the current state. """ - assert self.debugPrint("changedOnState(isOn=%s)"%(isOn,)) + assert self.__debugPrint("changedOnState(isOn=%s)"%(isOn,)) ##### states ##### def switchToNextStateTask(self, task): - assert self.debugPrint("switchToNextStateTask()") + assert self.__debugPrint("switchToNextStateTask()") self.fsm.request(self.states[self.nextStateIndex]) return Task.done @@ -193,11 +191,11 @@ class FourStateAI: This function is intentionaly simple so that derived classes may easily alter the network message. """ - assert self.debugPrint("distributeStateChange()") + assert self.__debugPrint("distributeStateChange()") self.sendState() def enterStateN(self, stateIndex, nextStateIndex): - assert self.debugPrint( + assert self.__debugPrint( "enterStateN(stateIndex=%s, nextStateIndex=%s)"% (stateIndex, nextStateIndex)) self.stateIndex = stateIndex @@ -211,7 +209,7 @@ class FourStateAI: "enterStateN-timer-%s"%id(self)) def exitStateN(self): - assert self.debugPrint("exitStateN()") + assert self.__debugPrint("exitStateN()") if self.doLaterTask: taskMgr.remove(self.doLaterTask) self.doLaterTask=None @@ -219,56 +217,56 @@ class FourStateAI: ##### state 0 ##### def enterState0(self): - assert self.debugPrint("enter0()") + assert self.__debugPrint("enter0()") self.enterStateN(0, 0) def exitState0(self): - assert self.debugPrint("exit0()") + assert self.__debugPrint("exit0()") ##### state 1 ##### def enterState1(self): - #assert self.debugPrint("enterState1()") + #assert self.__debugPrint("enterState1()") self.enterStateN(1, 2) def exitState1(self): - assert self.debugPrint("exitState1()") + assert self.__debugPrint("exitState1()") self.exitStateN() ##### state 2 ##### def enterState2(self): - #assert self.debugPrint("enterState2()") + #assert self.__debugPrint("enterState2()") self.enterStateN(2, 3) def exitState2(self): - assert self.debugPrint("exitState2()") + assert self.__debugPrint("exitState2()") self.exitStateN() ##### state 3 ##### def enterState3(self): - #assert self.debugPrint("enterState3()") + #assert self.__debugPrint("enterState3()") self.enterStateN(3, 4) def exitState3(self): - assert self.debugPrint("exitState3()") + assert self.__debugPrint("exitState3()") self.exitStateN() ##### state 4 ##### def enterState4(self): - assert self.debugPrint("enterState4()") + assert self.__debugPrint("enterState4()") self.enterStateN(4, 1) self.changedOnState(1) def exitState4(self): - assert self.debugPrint("exitState4()") + assert self.__debugPrint("exitState4()") self.exitStateN() self.changedOnState(0) if __debug__: - def debugPrint(self, message): + def __debugPrint(self, message): """for debugging""" return self.notify.debug("%d (%d) %s"%( id(self), self.stateIndex==4, message)) diff --git a/direct/src/fsm/__init__.py b/direct/src/fsm/__init__.py index e69de29bb2..365e539c4e 100644 --- a/direct/src/fsm/__init__.py +++ b/direct/src/fsm/__init__.py @@ -0,0 +1,6 @@ +""" +This package contains implementations of a Finite State Machine, an +abstract construct that holds a particular state and can transition +between several defined states. These are useful for a range of logic +programming tasks. +""" diff --git a/direct/src/gui/DirectButton.py b/direct/src/gui/DirectButton.py index 87077d20a0..e6111af710 100644 --- a/direct/src/gui/DirectButton.py +++ b/direct/src/gui/DirectButton.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""This module contains the DirectButton class.""" __all__ = ['DirectButton'] diff --git a/direct/src/gui/DirectCheckButton.py b/direct/src/gui/DirectCheckButton.py index 3df28a0730..f353f3d989 100644 --- a/direct/src/gui/DirectCheckButton.py +++ b/direct/src/gui/DirectCheckButton.py @@ -1,4 +1,6 @@ -"""Undocumented Module""" +"""A DirectCheckButton is a type of button that toggles between two states +when clicked. It also has a separate indicator that can be modified +separately.""" __all__ = ['DirectCheckButton'] diff --git a/direct/src/gui/DirectDialog.py b/direct/src/gui/DirectDialog.py index 039092c511..33852dc504 100644 --- a/direct/src/gui/DirectDialog.py +++ b/direct/src/gui/DirectDialog.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""This module defines various dialog windows for the DirectGUI system.""" __all__ = ['findDialog', 'cleanupDialog', 'DirectDialog', 'OkDialog', 'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog'] diff --git a/direct/src/gui/DirectEntry.py b/direct/src/gui/DirectEntry.py index 45733b7c35..2eb7607400 100644 --- a/direct/src/gui/DirectEntry.py +++ b/direct/src/gui/DirectEntry.py @@ -1,4 +1,5 @@ -"""Undocumented Module""" +"""Contains the DirectEntry class, a type of DirectGUI widget that accepts +text entered using the keyboard.""" __all__ = ['DirectEntry'] diff --git a/direct/src/gui/DirectFrame.py b/direct/src/gui/DirectFrame.py index 9c164038b9..e078b28021 100644 --- a/direct/src/gui/DirectFrame.py +++ b/direct/src/gui/DirectFrame.py @@ -1,4 +1,17 @@ -"""Undocumented Module""" +"""A DirectFrame is a basic DirectGUI component that acts as the base +class for various other components, and can also serve as a basic +container to hold other DirectGUI components. + +A DirectFrame can have: + +* A background texture (pass in path to image, or Texture Card) +* A midground geometry item (pass in geometry) +* A foreground text Node (pass in text string or OnscreenText) + +Each of these has 1 or more states. The same object can be used for +all states or each state can have a different text/geom/image (for +radio button and check button indicators, for example). +""" __all__ = ['DirectFrame'] @@ -19,14 +32,6 @@ class DirectFrame(DirectGuiWidget): DefDynGroups = ('text', 'geom', 'image') def __init__(self, parent = None, **kw): # Inherits from DirectGuiWidget - # A Direct Frame can have: - # - A background texture (pass in path to image, or Texture Card) - # - A midground geometry item (pass in geometry) - # - A foreground text Node (pass in text string or Onscreen Text) - # Each of these has 1 or more states - # The same object can be used for all states or each - # state can have a different text/geom/image (for radio button - # and check button indicators, for example). optiondefs = ( # Define type of DirectGuiWidget ('pgFunc', PGItem, None), diff --git a/direct/src/gui/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py index 833bf8ebcb..6836f7465f 100644 --- a/direct/src/gui/DirectGuiBase.py +++ b/direct/src/gui/DirectGuiBase.py @@ -1,32 +1,7 @@ -"""Undocumented Module""" - -__all__ = ['DirectGuiBase', 'DirectGuiWidget'] - - -from panda3d.core import * -from panda3d.direct import get_config_showbase -from . import DirectGuiGlobals as DGG -from .OnscreenText import * -from .OnscreenGeom import * -from .OnscreenImage import * -from direct.directtools.DirectUtil import ROUND_TO -from direct.showbase import DirectObject -from direct.task import Task -import sys - -if sys.version_info >= (3, 0): - stringType = str -else: - stringType = basestring - -guiObjectCollector = PStatCollector("Client::GuiObjects") - """ Base class for all Direct Gui items. Handles composite widgets and command line argument parsing. -""" -""" Code Overview: 1 Each widget defines a set of options (optiondefs) as a list of tuples @@ -101,7 +76,31 @@ Code Overview: are left unused. If so, an error is raised. """ +__all__ = ['DirectGuiBase', 'DirectGuiWidget'] + + +from panda3d.core import * +from panda3d.direct import get_config_showbase +from . import DirectGuiGlobals as DGG +from .OnscreenText import * +from .OnscreenGeom import * +from .OnscreenImage import * +from direct.directtools.DirectUtil import ROUND_TO +from direct.showbase import DirectObject +from direct.task import Task +import sys + +if sys.version_info >= (3, 0): + stringType = str +else: + stringType = basestring + +guiObjectCollector = PStatCollector("Client::GuiObjects") + + class DirectGuiBase(DirectObject.DirectObject): + """Base class of all DirectGUI widgets.""" + def __init__(self): # Default id of all gui object, subclasses should override this self.guiId = 'guiObject' diff --git a/direct/src/gui/DirectGuiGlobals.py b/direct/src/gui/DirectGuiGlobals.py index d71c97593d..96519fd9fc 100644 --- a/direct/src/gui/DirectGuiGlobals.py +++ b/direct/src/gui/DirectGuiGlobals.py @@ -1,12 +1,10 @@ -"""Undocumented Module""" - -__all__ = [] - - """ Global definitions used by Direct Gui Classes and handy constants that can be used during widget construction """ + +__all__ = [] + from panda3d.core import * defaultFont = None diff --git a/direct/src/gui/DirectLabel.py b/direct/src/gui/DirectLabel.py index 5cb2932900..bc1328ded7 100644 --- a/direct/src/gui/DirectLabel.py +++ b/direct/src/gui/DirectLabel.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the DirectLabel class.""" __all__ = ['DirectLabel'] diff --git a/direct/src/gui/DirectOptionMenu.py b/direct/src/gui/DirectOptionMenu.py index e2432a5512..40d48f7672 100644 --- a/direct/src/gui/DirectOptionMenu.py +++ b/direct/src/gui/DirectOptionMenu.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Implements a pop-up menu containing multiple clickable options.""" __all__ = ['DirectOptionMenu'] diff --git a/direct/src/gui/DirectRadioButton.py b/direct/src/gui/DirectRadioButton.py index 8044e81295..f6543d1e8f 100755 --- a/direct/src/gui/DirectRadioButton.py +++ b/direct/src/gui/DirectRadioButton.py @@ -1,4 +1,7 @@ -"""Undocumented Module""" +"""A DirectRadioButton is a type of button that, similar to a +DirectCheckButton, has a separate indicator and can be toggled between +two states. However, only one DirectRadioButton in a group can be enabled +at a particular time.""" __all__ = ['DirectRadioButton'] diff --git a/direct/src/gui/DirectScrollBar.py b/direct/src/gui/DirectScrollBar.py index 8493547e0d..d56385163b 100644 --- a/direct/src/gui/DirectScrollBar.py +++ b/direct/src/gui/DirectScrollBar.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Defines the DirectScrollBar class.""" __all__ = ['DirectScrollBar'] diff --git a/direct/src/gui/DirectScrolledFrame.py b/direct/src/gui/DirectScrolledFrame.py index 6a7be7cabf..c44bc7a0fb 100644 --- a/direct/src/gui/DirectScrolledFrame.py +++ b/direct/src/gui/DirectScrolledFrame.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the DirectScrolledFrame class.""" __all__ = ['DirectScrolledFrame'] diff --git a/direct/src/gui/DirectScrolledList.py b/direct/src/gui/DirectScrolledList.py index d17023d545..a89b691576 100644 --- a/direct/src/gui/DirectScrolledList.py +++ b/direct/src/gui/DirectScrolledList.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the DirectScrolledList class.""" __all__ = ['DirectScrolledListItem', 'DirectScrolledList'] diff --git a/direct/src/gui/DirectSlider.py b/direct/src/gui/DirectSlider.py index 73f5410e49..507c13b5a8 100644 --- a/direct/src/gui/DirectSlider.py +++ b/direct/src/gui/DirectSlider.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Defines the DirectSlider class.""" __all__ = ['DirectSlider'] diff --git a/direct/src/gui/DirectWaitBar.py b/direct/src/gui/DirectWaitBar.py index a8eb9a56cc..ff3f554fee 100644 --- a/direct/src/gui/DirectWaitBar.py +++ b/direct/src/gui/DirectWaitBar.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the DirectWaitBar class, a progress bar widget.""" __all__ = ['DirectWaitBar'] diff --git a/direct/src/gui/OnscreenText.py b/direct/src/gui/OnscreenText.py index 1af714c44a..c3accf191d 100644 --- a/direct/src/gui/OnscreenText.py +++ b/direct/src/gui/OnscreenText.py @@ -35,7 +35,8 @@ class OnscreenText(NodePath): font = None, parent = None, sort = 0, - mayChange = True): + mayChange = True, + direction = None): """ Make a text node from string, put it into the 2d sg and set it up with all the indicated parameters. @@ -95,6 +96,9 @@ class OnscreenText(NodePath): mayChange: pass true if the text or its properties may need to be changed at runtime, false if it is static once created (which leads to better memory optimization). + + direction: this can be set to 'ltr' or 'rtl' to override the + direction of the text. """ if parent == None: parent = aspect2d @@ -192,6 +196,17 @@ class OnscreenText(NodePath): textNode.setFrameColor(frame[0], frame[1], frame[2], frame[3]) textNode.setFrameAsMargin(0.1, 0.1, 0.1, 0.1) + if direction is not None: + if isinstance(direction, str): + direction = direction.lower() + if direction == 'rtl': + direction = TextProperties.D_rtl + elif direction == 'ltr': + direction = TextProperties.D_ltr + else: + raise ValueError('invalid direction') + textNode.setDirection(direction) + # Create a transform for the text for our scale and position. # We'd rather do it here, on the text itself, rather than on # our NodePath, so we have one fewer transforms in the scene diff --git a/direct/src/gui/__init__.py b/direct/src/gui/__init__.py index e69de29bb2..5cc1895b62 100644 --- a/direct/src/gui/__init__.py +++ b/direct/src/gui/__init__.py @@ -0,0 +1,12 @@ +""" +This package contains the DirectGui system, a set of classes +responsible for drawing graphical widgets to the 2-D scene graph. + +It is based on the lower-level PGui system, which is implemented in +C++. + +For convenience, all of the DirectGui widgets may be imported from a +single module as follows:: + + from direct.gui.DirectGui import * +""" diff --git a/direct/src/interval/ActorInterval.py b/direct/src/interval/ActorInterval.py index 5d8b557ae6..6ac026497b 100644 --- a/direct/src/interval/ActorInterval.py +++ b/direct/src/interval/ActorInterval.py @@ -83,7 +83,7 @@ class ActorInterval(Interval.Interval): if startTime == None: startTime = float(self.startFrame) / float(self.frameRate) endTime = startTime + duration - self.endFrame = duration * self.frameRate + self.endFrame = endTime * self.frameRate else: # No end frame specified. Choose the maximum of all # of the controls' numbers of frames. diff --git a/direct/src/interval/Interval.py b/direct/src/interval/Interval.py index 21a79f016a..02a5f4bc00 100644 --- a/direct/src/interval/Interval.py +++ b/direct/src/interval/Interval.py @@ -116,8 +116,9 @@ class Interval(DirectObject): return self.currT def start(self, startT = 0.0, endT = -1.0, playRate = 1.0): + """ Starts the interval. Returns an awaitable. """ self.setupPlay(startT, endT, playRate, 0) - self.__spawnTask() + return self.__spawnTask() def loop(self, startT = 0.0, endT = -1.0, playRate = 1.0): self.setupPlay(startT, endT, playRate, 1) @@ -427,6 +428,7 @@ class Interval(DirectObject): task = Task(self.__playTask) task.interval = self taskMgr.add(task, taskName) + return task def __removeTask(self): # Kill old task(s), including those from a similarly-named but diff --git a/direct/src/interval/IntervalGlobal.py b/direct/src/interval/IntervalGlobal.py index 903f92d14f..41ee6444f7 100644 --- a/direct/src/interval/IntervalGlobal.py +++ b/direct/src/interval/IntervalGlobal.py @@ -1,4 +1,7 @@ -"""IntervalGlobal module""" +""" +This module imports all of the other interval modules, to provide a +single convenient module from which all interval types can be imported. +""" # In this unusual case, I'm not going to declare __all__, # since the purpose of this module is to add up the contributions diff --git a/direct/src/interval/IntervalManager.py b/direct/src/interval/IntervalManager.py index 0e909ceeb8..a9a44a3bf0 100644 --- a/direct/src/interval/IntervalManager.py +++ b/direct/src/interval/IntervalManager.py @@ -1,4 +1,5 @@ -"""Undocumented Module""" +"""Defines the IntervalManager class as well as the global instance of +this class, ivalMgr.""" __all__ = ['IntervalManager', 'ivalMgr'] @@ -136,6 +137,5 @@ class IntervalManager(CIntervalManager): assert self.ivals[index] == None or self.ivals[index] == interval self.ivals[index] = interval -# The global IntervalManager object. +#: The global IntervalManager object. ivalMgr = IntervalManager(1) - diff --git a/direct/src/interval/MetaInterval.py b/direct/src/interval/MetaInterval.py index dc83f3587c..fd379280a7 100644 --- a/direct/src/interval/MetaInterval.py +++ b/direct/src/interval/MetaInterval.py @@ -1,4 +1,7 @@ -"""Undocumented Module""" +""" +This module defines the various "meta intervals", which execute other +intervals either in parallel or in a specified sequential order. +""" __all__ = ['MetaInterval', 'Sequence', 'Parallel', 'ParallelEndTogether', 'Track'] diff --git a/direct/src/interval/ParticleInterval.py b/direct/src/interval/ParticleInterval.py index c373af7542..b5a3b09b73 100644 --- a/direct/src/interval/ParticleInterval.py +++ b/direct/src/interval/ParticleInterval.py @@ -1,11 +1,9 @@ -"""Undocumented Module""" - -__all__ = ['ParticleInterval'] - """ Contains the ParticleInterval class """ +__all__ = ['ParticleInterval'] + from panda3d.core import * from panda3d.direct import * from direct.directnotify.DirectNotifyGlobal import directNotify diff --git a/direct/src/interval/TestInterval.py b/direct/src/interval/TestInterval.py index 82a04b604e..6a13cce4f2 100755 --- a/direct/src/interval/TestInterval.py +++ b/direct/src/interval/TestInterval.py @@ -1,11 +1,9 @@ -"""Undocumented Module""" +""" +Contains the TestInterval class +""" __all__ = ['TestInterval'] -""" -Contains the ParticleInterval class -""" - from panda3d.core import * from panda3d.direct import * from direct.directnotify.DirectNotifyGlobal import directNotify diff --git a/direct/src/interval/__init__.py b/direct/src/interval/__init__.py index e69de29bb2..5f3e8e4332 100644 --- a/direct/src/interval/__init__.py +++ b/direct/src/interval/__init__.py @@ -0,0 +1,12 @@ +""" +This package contains the Python implementation of the interval system, +which is a mechanism for playing back scripted actions. A range of +interval types has been defined to automate motion, animation, sounds, +color, function calls, as well as other intervals and arbitrary +properties. + +All interval types can be conveniently imported from the +:mod:`.IntervalGlobal` module:: + + from direct.interval.IntervalGlobal import * +""" diff --git a/direct/src/interval/cConstrainHprInterval.h b/direct/src/interval/cConstrainHprInterval.h index 42c52df558..a618b98641 100644 --- a/direct/src/interval/cConstrainHprInterval.h +++ b/direct/src/interval/cConstrainHprInterval.h @@ -26,9 +26,9 @@ */ class EXPCL_DIRECT CConstrainHprInterval : public CConstraintInterval { PUBLISHED: - CConstrainHprInterval(const string &name, double duration, - const NodePath &node, const NodePath &target, - bool wrt, const LVecBase3 hprOffset=LVector3::zero()); + explicit CConstrainHprInterval(const string &name, double duration, + const NodePath &node, const NodePath &target, + bool wrt, const LVecBase3 hprOffset=LVector3::zero()); INLINE const NodePath &get_node() const; INLINE const NodePath &get_target() const; diff --git a/direct/src/interval/cConstrainPosHprInterval.h b/direct/src/interval/cConstrainPosHprInterval.h index fcdcd39c32..d3ce5efa4f 100644 --- a/direct/src/interval/cConstrainPosHprInterval.h +++ b/direct/src/interval/cConstrainPosHprInterval.h @@ -26,10 +26,10 @@ */ class EXPCL_DIRECT CConstrainPosHprInterval : public CConstraintInterval { PUBLISHED: - CConstrainPosHprInterval(const string &name, double duration, - const NodePath &node, const NodePath &target, - bool wrt, const LVecBase3 posOffset=LVector3::zero(), - const LVecBase3 hprOffset=LVector3::zero()); + explicit CConstrainPosHprInterval(const string &name, double duration, + const NodePath &node, const NodePath &target, + bool wrt, const LVecBase3 posOffset=LVector3::zero(), + const LVecBase3 hprOffset=LVector3::zero()); INLINE const NodePath &get_node() const; INLINE const NodePath &get_target() const; diff --git a/direct/src/interval/cConstrainPosInterval.h b/direct/src/interval/cConstrainPosInterval.h index aa05a0f427..92055cd0a4 100644 --- a/direct/src/interval/cConstrainPosInterval.h +++ b/direct/src/interval/cConstrainPosInterval.h @@ -25,9 +25,9 @@ */ class EXPCL_DIRECT CConstrainPosInterval : public CConstraintInterval { PUBLISHED: - CConstrainPosInterval(const string &name, double duration, - const NodePath &node, const NodePath &target, - bool wrt, const LVecBase3 posOffset=LVector3::zero()); + explicit CConstrainPosInterval(const string &name, double duration, + const NodePath &node, const NodePath &target, + bool wrt, const LVecBase3 posOffset=LVector3::zero()); INLINE const NodePath &get_node() const; INLINE const NodePath &get_target() const; diff --git a/direct/src/interval/cConstrainTransformInterval.h b/direct/src/interval/cConstrainTransformInterval.h index b8bfc2c06f..897f12aea3 100644 --- a/direct/src/interval/cConstrainTransformInterval.h +++ b/direct/src/interval/cConstrainTransformInterval.h @@ -24,9 +24,9 @@ */ class EXPCL_DIRECT CConstrainTransformInterval : public CConstraintInterval { PUBLISHED: - CConstrainTransformInterval(const string &name, double duration, - const NodePath &node, const NodePath &target, - bool wrt); + explicit CConstrainTransformInterval(const string &name, double duration, + const NodePath &node, + const NodePath &target, bool wrt); INLINE const NodePath &get_node() const; INLINE const NodePath &get_target() const; diff --git a/direct/src/interval/cLerpAnimEffectInterval.h b/direct/src/interval/cLerpAnimEffectInterval.h index 8a4148baef..ef84b19df0 100644 --- a/direct/src/interval/cLerpAnimEffectInterval.h +++ b/direct/src/interval/cLerpAnimEffectInterval.h @@ -31,8 +31,8 @@ */ class EXPCL_DIRECT CLerpAnimEffectInterval : public CLerpInterval { PUBLISHED: - INLINE CLerpAnimEffectInterval(const string &name, double duration, - BlendType blend_type); + INLINE explicit CLerpAnimEffectInterval(const string &name, double duration, + BlendType blend_type); INLINE void add_control(AnimControl *control, const string &name, float begin_effect, float end_effect); diff --git a/direct/src/interval/cLerpNodePathInterval.h b/direct/src/interval/cLerpNodePathInterval.h index 06b09deb3b..9ac5ae46dc 100644 --- a/direct/src/interval/cLerpNodePathInterval.h +++ b/direct/src/interval/cLerpNodePathInterval.h @@ -25,10 +25,10 @@ */ class EXPCL_DIRECT CLerpNodePathInterval : public CLerpInterval { PUBLISHED: - CLerpNodePathInterval(const string &name, double duration, - BlendType blend_type, bool bake_in_start, - bool fluid, - const NodePath &node, const NodePath &other); + explicit CLerpNodePathInterval(const string &name, double duration, + BlendType blend_type, bool bake_in_start, + bool fluid, + const NodePath &node, const NodePath &other); INLINE const NodePath &get_node() const; INLINE const NodePath &get_other() const; diff --git a/direct/src/interval/cMetaInterval.h b/direct/src/interval/cMetaInterval.h index 4f4e246045..ea44225842 100644 --- a/direct/src/interval/cMetaInterval.h +++ b/direct/src/interval/cMetaInterval.h @@ -31,7 +31,7 @@ */ class EXPCL_DIRECT CMetaInterval : public CInterval { PUBLISHED: - CMetaInterval(const string &name); + explicit CMetaInterval(const string &name); virtual ~CMetaInterval(); enum RelativeStart { diff --git a/direct/src/interval/hideInterval.h b/direct/src/interval/hideInterval.h index 6b57ef7237..f2a53307b1 100644 --- a/direct/src/interval/hideInterval.h +++ b/direct/src/interval/hideInterval.h @@ -23,7 +23,7 @@ */ class EXPCL_DIRECT HideInterval : public CInterval { PUBLISHED: - HideInterval(const NodePath &node, const string &name = string()); + explicit HideInterval(const NodePath &node, const string &name = string()); virtual void priv_instant(); virtual void priv_reverse_instant(); diff --git a/direct/src/interval/showInterval.h b/direct/src/interval/showInterval.h index 3ed7d55649..5365581339 100644 --- a/direct/src/interval/showInterval.h +++ b/direct/src/interval/showInterval.h @@ -23,7 +23,7 @@ */ class EXPCL_DIRECT ShowInterval : public CInterval { PUBLISHED: - ShowInterval(const NodePath &node, const string &name = string()); + explicit ShowInterval(const NodePath &node, const string &name = string()); virtual void priv_instant(); virtual void priv_reverse_instant(); diff --git a/direct/src/interval/waitInterval.h b/direct/src/interval/waitInterval.h index a1fd57793b..dbf0757843 100644 --- a/direct/src/interval/waitInterval.h +++ b/direct/src/interval/waitInterval.h @@ -23,7 +23,7 @@ */ class EXPCL_DIRECT WaitInterval : public CInterval { PUBLISHED: - INLINE WaitInterval(double duration); + INLINE explicit WaitInterval(double duration); virtual void priv_step(double t); diff --git a/direct/src/motiontrail/__init__.py b/direct/src/motiontrail/__init__.py index e69de29bb2..d50bc43be3 100644 --- a/direct/src/motiontrail/__init__.py +++ b/direct/src/motiontrail/__init__.py @@ -0,0 +1,3 @@ +""" +This package contains only the :class:`.MotionTrail` class. +""" diff --git a/direct/src/p3d/AppRunner.py b/direct/src/p3d/AppRunner.py index 4a59e20de6..aae03754e6 100644 --- a/direct/src/p3d/AppRunner.py +++ b/direct/src/p3d/AppRunner.py @@ -1,12 +1,15 @@ - """ - This module is intended to be compiled into the Panda3D runtime distributable, to execute a packaged p3d application, but it can also be run directly via the Python interpreter (if the current Panda3D and Python versions match the version expected by the application). See runp3d.py for a command-line tool to invoke this module. +The global AppRunner instance may be imported as follows:: + + from direct.showbase.AppRunnerGlobal import appRunner + +This will be None if Panda was not run from the runtime environment. """ __all__ = ["AppRunner", "dummyAppRunner", "ArgumentError"] diff --git a/direct/src/p3d/__init__.py b/direct/src/p3d/__init__.py index e69de29bb2..e6ab93b09d 100644 --- a/direct/src/p3d/__init__.py +++ b/direct/src/p3d/__init__.py @@ -0,0 +1,4 @@ +""" +This package provides the Python interface to functionality relating to +the Panda3D Runtime environment. +""" diff --git a/direct/src/particles/__init__.py b/direct/src/particles/__init__.py index e69de29bb2..d3525ee464 100644 --- a/direct/src/particles/__init__.py +++ b/direct/src/particles/__init__.py @@ -0,0 +1,7 @@ +""" +This package contains the high-level Python interface to the particle +system. + +Also see the :mod:`panda3d.physics` module, which contains the C++ +implementation of the particle system. +""" diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index b3f96b3f9c..0eabdaafd5 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -3450,8 +3450,8 @@ paint_window_osx_port() { int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size()); size_t rowsize = _swbuffer->get_row_size(); - Rect src_rect = {0, 0, y_size, x_size}; - Rect ddrc_rect = {0, 0, y_size, x_size}; + Rect src_rect = {0, 0, (short)y_size, (short)x_size}; + Rect ddrc_rect = {0, 0, (short)y_size, (short)x_size}; QDErr err; @@ -3502,7 +3502,7 @@ paint_window_osx_cgcontext(CGContextRef context) { int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size()); if (_buffer_image != NULL) { - CGRect region = { { 0, 0 }, { x_size, y_size } }; + CGRect region = { { 0, 0 }, { (CGFloat)x_size, (CGFloat)y_size } }; CGContextDrawImage(context, region, _buffer_image); } } @@ -3538,7 +3538,7 @@ handle_event_osx_event_record(const P3D_event_data &event) { // First, convert the coordinates from screen coordinates to browser // window coordinates. WindowRef window = handle._handle._osx_cgcontext._window; - CGPoint cgpt = { pt.h, pt.v }; + CGPoint cgpt = { (CGFloat)pt.h, (CGFloat)pt.v }; HIPointConvert(&cgpt, kHICoordSpaceScreenPixel, NULL, kHICoordSpaceWindow, window); diff --git a/direct/src/plugin/p3dOsxSplashWindow.cxx b/direct/src/plugin/p3dOsxSplashWindow.cxx index 2014251149..c8fc969679 100644 --- a/direct/src/plugin/p3dOsxSplashWindow.cxx +++ b/direct/src/plugin/p3dOsxSplashWindow.cxx @@ -267,7 +267,7 @@ refresh() { return; } if (_toplevel_window != NULL) { - Rect r = { 0, 0, _win_height, _win_width }; + Rect r = { 0, 0, (short)_win_height, (short)_win_width }; InvalWindowRect(_toplevel_window, &r); } else { @@ -345,7 +345,7 @@ paint_window_osx_cgcontext(CGContextRef context) { CGColorSpaceRef rgb_space = CGColorSpaceCreateDeviceRGB(); CGColorRef bg = CGColorCreate(rgb_space, bg_components); - CGRect region = { { 0, 0 }, { _win_width, _win_height } }; + CGRect region = { { 0, 0 }, { (CGFloat)_win_width, (CGFloat)_win_height } }; CGContextSetFillColorWithColor(context, bg); CGContextFillRect(context, region); @@ -407,7 +407,7 @@ handle_event_osx_event_record(const P3D_event_data &event) { // First, convert the coordinates from screen coordinates to browser // window coordinates. WindowRef window = handle._handle._osx_cgcontext._window; - CGPoint cgpt = { pt.h, pt.v }; + CGPoint cgpt = { (CGFloat)pt.h, (CGFloat)pt.v }; HIPointConvert(&cgpt, kHICoordSpaceScreenPixel, NULL, kHICoordSpaceWindow, window); @@ -617,7 +617,7 @@ paint_progress_bar(CGContextRef context) { int bar_x, bar_y, bar_width, bar_height; get_bar_placement(bar_x, bar_y, bar_width, bar_height); - CGRect bar_rect = { { bar_x, bar_y }, { bar_width, bar_height } }; + CGRect bar_rect = { { (CGFloat)bar_x, (CGFloat)bar_y }, { (CGFloat)bar_width, (CGFloat)bar_height } }; // Clear the entire progress bar to white (or the background color). CGContextSetFillColorWithColor(context, bar_bg); @@ -627,7 +627,7 @@ paint_progress_bar(CGContextRef context) { if (_progress_known) { int progress_width = (int)(bar_width * _install_progress + 0.5); if (progress_width != 0) { - CGRect prog = { { bar_x, bar_y }, { progress_width, bar_height } }; + CGRect prog = { { (CGFloat)bar_x, (CGFloat)bar_y }, { (CGFloat)progress_width, (CGFloat)bar_height } }; CGContextSetFillColorWithColor(context, bar); CGContextFillRect(context, prog); } @@ -642,7 +642,7 @@ paint_progress_bar(CGContextRef context) { progress = block_travel * 2 - progress; } - CGRect prog = { { bar_x + progress, bar_y }, { block_width, bar_height } }; + CGRect prog = { { (CGFloat)(bar_x + progress), (CGFloat)bar_y }, { (CGFloat)block_width, (CGFloat)bar_height } }; CGContextSetFillColorWithColor(context, bar); CGContextFillRect(context, prog); } @@ -652,8 +652,8 @@ paint_progress_bar(CGContextRef context) { // We offset the border by half a pixel, so we'll be drawing the one-pixel // line through the middle of a pixel, and it won't try to antialias // itself into a half-black two-pixel line. - CGRect border_rect = { { bar_x - 0.5, bar_y - 0.5 }, - { bar_width + 1, bar_height + 1 } }; + CGRect border_rect = { { (CGFloat)(bar_x - 0.5), (CGFloat)(bar_y - 0.5) }, + { (CGFloat)(bar_width + 1), (CGFloat)(bar_height + 1) } }; CGContextBeginPath(context); CGContextSetLineWidth(context, 1); diff --git a/direct/src/showbase/AppRunnerGlobal.py b/direct/src/showbase/AppRunnerGlobal.py index 9e4b083f25..2ab5c0ccf9 100644 --- a/direct/src/showbase/AppRunnerGlobal.py +++ b/direct/src/showbase/AppRunnerGlobal.py @@ -6,4 +6,6 @@ This is needed for apps that start themselves by importing DirectStart; it provides a place for these apps to look for the AppRunner at startup. """ +#: Contains the global AppRunner instance, or None if this application +#: was not run from the runtime environment. appRunner = None diff --git a/direct/src/showbase/Audio3DManager.py b/direct/src/showbase/Audio3DManager.py index e9cdb59eb8..463d3b5bf0 100644 --- a/direct/src/showbase/Audio3DManager.py +++ b/direct/src/showbase/Audio3DManager.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the Audio3DManager class.""" __all__ = ['Audio3DManager'] @@ -289,3 +289,27 @@ class Audio3DManager: for sound in self.sound_dict[object]: self.detachSound(sound) + #snake_case alias: + get_doppler_factor = getDopplerFactor + set_listener_velocity_auto = setListenerVelocityAuto + attach_listener = attachListener + set_distance_factor = setDistanceFactor + attach_sound_to_object = attachSoundToObject + get_drop_off_factor = getDropOffFactor + set_doppler_factor = setDopplerFactor + get_sounds_on_object = getSoundsOnObject + set_sound_velocity_auto = setSoundVelocityAuto + get_sound_max_distance = getSoundMaxDistance + load_sfx = loadSfx + get_distance_factor = getDistanceFactor + set_listener_velocity = setListenerVelocity + set_sound_max_distance = setSoundMaxDistance + get_sound_velocity = getSoundVelocity + get_listener_velocity = getListenerVelocity + set_sound_velocity = setSoundVelocity + set_sound_min_distance = setSoundMinDistance + get_sound_min_distance = getSoundMinDistance + detach_listener = detachListener + set_drop_off_factor = setDropOffFactor + detach_sound = detachSound + diff --git a/direct/src/showbase/BufferViewer.py b/direct/src/showbase/BufferViewer.py index c2a7f86049..48dff3a1ea 100644 --- a/direct/src/showbase/BufferViewer.py +++ b/direct/src/showbase/BufferViewer.py @@ -1,4 +1,6 @@ -"""Undocumented Module""" +"""Contains the BufferViewer class, which is used as a debugging aid +when debugging render-to-texture effects. It shows different views at +the bottom of the screen showing the various render targets.""" __all__ = ['BufferViewer'] diff --git a/direct/src/showbase/BulletinBoard.py b/direct/src/showbase/BulletinBoard.py index c46eb0ff0e..5b1cdd71a0 100755 --- a/direct/src/showbase/BulletinBoard.py +++ b/direct/src/showbase/BulletinBoard.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the BulletinBoard class.""" __all__ = ['BulletinBoard'] diff --git a/direct/src/showbase/BulletinBoardWatcher.py b/direct/src/showbase/BulletinBoardWatcher.py index da2cb8dd63..efc75b43ac 100755 --- a/direct/src/showbase/BulletinBoardWatcher.py +++ b/direct/src/showbase/BulletinBoardWatcher.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the BulletinBoardWatcher class.""" __all__ = ['BulletinBoardWatcher'] diff --git a/direct/src/showbase/DirectObject.py b/direct/src/showbase/DirectObject.py index 22d846bc3e..778328ea50 100644 --- a/direct/src/showbase/DirectObject.py +++ b/direct/src/showbase/DirectObject.py @@ -1,4 +1,5 @@ -"""Undocumented Module""" +"""Defines the DirectObject class, a convenient class to inherit from if the +object needs to be able to respond to events.""" __all__ = ['DirectObject'] @@ -100,3 +101,15 @@ class DirectObject: func = choice(getRepository()._crashOnProactiveLeakDetect, self.notify.error, self.notify.warning) func('destroyed %s instance is still %s%s' % (self.__class__.__name__, estr, tstr)) + + #snake_case alias: + add_task = addTask + do_method_later = doMethodLater + detect_leaks = detectLeaks + accept_once = acceptOnce + ignore_all = ignoreAll + get_all_accepting = getAllAccepting + is_ignoring = isIgnoring + remove_all_tasks = removeAllTasks + remove_task = removeTask + is_accepting = isAccepting diff --git a/direct/src/showbase/EventGroup.py b/direct/src/showbase/EventGroup.py index baf26193f4..6385761ca4 100755 --- a/direct/src/showbase/EventGroup.py +++ b/direct/src/showbase/EventGroup.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""This module defines the EventGroup class.""" __all__ = ['EventGroup'] diff --git a/direct/src/showbase/EventManager.py b/direct/src/showbase/EventManager.py index 35616f6002..af8bd1c91a 100644 --- a/direct/src/showbase/EventManager.py +++ b/direct/src/showbase/EventManager.py @@ -1,4 +1,5 @@ -"""Undocumented Module""" +"""Contains the EventManager class. See :mod:`.EventManagerGlobal` for the +global eventMgr instance.""" __all__ = ['EventManager'] diff --git a/direct/src/showbase/EventManagerGlobal.py b/direct/src/showbase/EventManagerGlobal.py index 9e7000ed51..73a35b8ba5 100644 --- a/direct/src/showbase/EventManagerGlobal.py +++ b/direct/src/showbase/EventManagerGlobal.py @@ -1,7 +1,8 @@ -"""Undocumented Module""" +"""Contains the global :class:`.EventManager` instance.""" __all__ = ['eventMgr'] from . import EventManager +#: The global event manager. eventMgr = EventManager.EventManager() diff --git a/direct/src/showbase/Factory.py b/direct/src/showbase/Factory.py index bff33dd931..0abd217c07 100755 --- a/direct/src/showbase/Factory.py +++ b/direct/src/showbase/Factory.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the Factory class.""" __all__ = ['Factory'] diff --git a/direct/src/showbase/FindCtaPaths.py b/direct/src/showbase/FindCtaPaths.py index a530778715..4832400b46 100755 --- a/direct/src/showbase/FindCtaPaths.py +++ b/direct/src/showbase/FindCtaPaths.py @@ -1,7 +1,3 @@ -"""Undocumented Module""" - -__all__ = ['deCygwinify', 'getPaths'] - """This module is used only by the VR Studio programmers who are using the ctattach tools. It is imported before any other package, and its job is to figure out the correct paths to each of the packages. @@ -10,6 +6,8 @@ This module is not needed if you are not using ctattach; in this case all of the Panda packages will be collected under a common directory, which you will presumably have already on your PYTHONPATH. """ +__all__ = ['deCygwinify', 'getPaths'] + import os import sys diff --git a/direct/src/showbase/Finder.py b/direct/src/showbase/Finder.py index f7c619da91..129bcb4dc6 100644 --- a/direct/src/showbase/Finder.py +++ b/direct/src/showbase/Finder.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains various utility functions.""" __all__ = ['findClass', 'rebindClass', 'copyFuncs', 'replaceMessengerFunc', 'replaceTaskMgrFunc', 'replaceStateFunc', 'replaceCRFunc', 'replaceAIRFunc', 'replaceIvalFunc'] diff --git a/direct/src/showbase/GarbageReport.py b/direct/src/showbase/GarbageReport.py index db91df0c47..7d268bf450 100755 --- a/direct/src/showbase/GarbageReport.py +++ b/direct/src/showbase/GarbageReport.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains utility classes for debugging memory leaks.""" __all__ = ['FakeObject', '_createGarbage', 'GarbageReport', 'GarbageLogger'] diff --git a/direct/src/showbase/Loader.py b/direct/src/showbase/Loader.py index 9edde2a9b7..17c9cea25e 100644 --- a/direct/src/showbase/Loader.py +++ b/direct/src/showbase/Loader.py @@ -21,31 +21,105 @@ class Loader(DirectObject): loaderIndex = 0 class Callback: - def __init__(self, numObjects, gotList, callback, extraArgs): + """Returned by loadModel when used asynchronously. This class is + modelled after Future, and can be awaited.""" + + # This indicates that this class behaves like a Future. + _asyncio_future_blocking = False + + def __init__(self, loader, numObjects, gotList, callback, extraArgs): + self._loader = loader self.objects = [None] * numObjects self.gotList = gotList self.callback = callback self.extraArgs = extraArgs - self.numRemaining = numObjects - self.cancelled = False self.requests = set() + self.requestList = [] def gotObject(self, index, object): self.objects[index] = object - self.numRemaining -= 1 - if self.numRemaining == 0: - if self.gotList: - self.callback(self.objects, *self.extraArgs) - else: - self.callback(*(self.objects + self.extraArgs)) + if not self.requests: + self._loader = None + if self.callback: + if self.gotList: + self.callback(self.objects, *self.extraArgs) + else: + self.callback(*(self.objects + self.extraArgs)) + + def cancel(self): + "Cancels the request. Callback won't be called." + if self._loader: + for request in self.requests: + self._loader.loader.remove(request) + del self._loader._requests[request] + self._loader = None + self.requests = None + self.requestList = None + + def cancelled(self): + "Returns true if the request was cancelled." + return self.requestList is None + + def done(self): + "Returns true if all the requests were finished or cancelled." + return not self.requests + + def result(self): + "Returns the results, suspending the thread to wait if necessary." + for r in list(self.requests): + r.wait() + if self.gotList: + return self.objects + else: + return self.objects[0] + + def exception(self): + assert self.done() and not self.cancelled() + return None + + def __await__(self): + """ Returns a generator that raises StopIteration when the loading + is complete. This allows this class to be used with 'await'.""" + if self.requests: + self._asyncio_future_blocking = True + yield self + + # This should be a simple return, but older versions of Python + # don't allow return statements with arguments. + result = self.result() + exc = StopIteration(result) + exc.value = result + raise exc + + def __aiter__(self): + """ This allows using `async for` to iterate asynchronously over + the results of this class. It does guarantee to return the + results in order, though, even though they may not be loaded in + that order. """ + requestList = self.requestList + assert requestList is not None, "Request was cancelled." + + class AsyncIter: + index = 0 + def __anext__(self): + if self.index < len(requestList): + i = self.index + self.index = i + 1 + return requestList[i] + else: + raise StopAsyncIteration + + iter = AsyncIter() + iter.objects = self.objects + return iter # special methods def __init__(self, base): self.base = base self.loader = PandaLoader.getGlobalPtr() - self.__requests = {} + self._requests = {} self.hook = "async_loader_%s" % (Loader.loaderIndex) Loader.loaderIndex += 1 @@ -60,7 +134,8 @@ class Loader(DirectObject): # model loading funcs def loadModel(self, modelPath, loaderOptions = None, noCache = None, allowInstance = False, okMissing = None, - callback = None, extraArgs = [], priority = None): + callback = None, extraArgs = [], priority = None, + blocking = None): """ Attempts to load a model or models from one or more relative pathnames. If the input modelPath is a string (a single model @@ -97,10 +172,10 @@ class Loader(DirectObject): If callback is not None, then the model load will be performed asynchronously. In this case, loadModel() will initiate a background load and return immediately. The return value will - be an object that may later be passed to - loader.cancelRequest() to cancel the asynchronous request. At - some later point, when the requested model(s) have finished - loading, the callback function will be invoked with the n + be an object that can be used to check the status, cancel the + request, or use it in an `await` expression. Unless callback + is the special value True, when the requested model(s) have + finished loading, it will be invoked with the n loaded models passed as its parameter list. It is possible that the callback will be invoked immediately, even before loadModel() returns. If you use callback, you may also @@ -152,7 +227,10 @@ class Loader(DirectObject): modelList = modelPath gotList = True - if callback is None: + if blocking is None: + blocking = callback is None + + if blocking: # We got no callback, so it's a synchronous load. result = [] @@ -180,7 +258,7 @@ class Loader(DirectObject): # requested models have been loaded, we'll invoke the # callback (passing it the models on the parameter list). - cb = Loader.Callback(len(modelList), gotList, callback, extraArgs) + cb = Loader.Callback(self, len(modelList), gotList, callback, extraArgs) i = 0 for modelPath in modelList: request = self.loader.makeAsyncRequest(Filename(modelPath), loaderOptions) @@ -189,26 +267,26 @@ class Loader(DirectObject): request.setDoneEvent(self.hook) self.loader.loadAsync(request) cb.requests.add(request) - self.__requests[request] = (cb, i) + cb.requestList.append(request) + self._requests[request] = (cb, i) i += 1 return cb def cancelRequest(self, cb): """Cancels an aysynchronous loading or flatten request issued earlier. The callback associated with the request will not be - called after cancelRequest() has been performed. """ + called after cancelRequest() has been performed. - if not cb.cancelled: - cb.cancelled = True - for request in cb.requests: - self.loader.remove(request) - del self.__requests[request] - cb.requests = None + This is now deprecated: call cb.cancel() instead. """ + + cb.cancel() def isRequestPending(self, cb): """ Returns true if an asynchronous loading or flatten request issued earlier is still pending, or false if it has completed or - been cancelled. """ + been cancelled. + + This is now deprecated: call cb.done() instead. """ return bool(cb.requests) @@ -290,7 +368,8 @@ class Loader(DirectObject): ModelPool.releaseModel(modelNode) def saveModel(self, modelPath, node, loaderOptions = None, - callback = None, extraArgs = [], priority = None): + callback = None, extraArgs = [], priority = None, + blocking = None): """ Saves the model (a NodePath or PandaNode) to the indicated filename path. Returns true on success, false on failure. If a callback is used, the model is saved asynchronously, and the @@ -325,7 +404,10 @@ class Loader(DirectObject): # From here on, we deal with a list of (filename, node) pairs. modelList = list(zip(modelList, nodeList)) - if callback is None: + if blocking is None: + blocking = callback is None + + if blocking: # We got no callback, so it's a synchronous save. result = [] @@ -344,7 +426,7 @@ class Loader(DirectObject): # requested models have been saved, we'll invoke the # callback (passing it the models on the parameter list). - cb = Loader.Callback(len(modelList), gotList, callback, extraArgs) + cb = Loader.Callback(self, len(modelList), gotList, callback, extraArgs) i = 0 for modelPath, node in modelList: request = self.loader.makeAsyncSaveRequest(Filename(modelPath), loaderOptions, node) @@ -353,7 +435,8 @@ class Loader(DirectObject): request.setDoneEvent(self.hook) self.loader.saveAsync(request) cb.requests.add(request) - self.__requests[request] = (cb, i) + cb.requestList.append(request) + self._requests[request] = (cb, i) i += 1 return cb @@ -880,13 +963,14 @@ class Loader(DirectObject): # requested sounds have been loaded, we'll invoke the # callback (passing it the sounds on the parameter list). - cb = Loader.Callback(len(soundList), gotList, callback, extraArgs) + cb = Loader.Callback(self, len(soundList), gotList, callback, extraArgs) for i, soundPath in enumerate(soundList): request = AudioLoadRequest(manager, soundPath, positional) request.setDoneEvent(self.hook) self.loader.loadAsync(request) cb.requests.add(request) - self.__requests[request] = (cb, i) + cb.requestList.append(request) + self._requests[request] = (cb, i) return cb def unloadSfx(self, sfx): @@ -944,14 +1028,15 @@ class Loader(DirectObject): callback = self.__asyncFlattenDone gotList = True - cb = Loader.Callback(len(modelList), gotList, callback, extraArgs) + cb = Loader.Callback(self, len(modelList), gotList, callback, extraArgs) i = 0 for model in modelList: request = ModelFlattenRequest(model.node()) request.setDoneEvent(self.hook) self.loader.loadAsync(request) cb.requests.add(request) - self.__requests[request] = (cb, i) + cb.requestList.append(request) + self._requests[request] = (cb, i) i += 1 return cb @@ -980,36 +1065,26 @@ class Loader(DirectObject): of loaded objects, and call the appropriate callback when it's time.""" - if request not in self.__requests: + if request not in self._requests: return - cb, i = self.__requests[request] - if cb.cancelled: + cb, i = self._requests[request] + if cb.cancelled() or request.cancelled(): # Shouldn't be here. - del self.__requests[request] + del self._requests[request] return cb.requests.discard(request) if not cb.requests: - del self.__requests[request] + del self._requests[request] - object = None - if hasattr(request, "getModel"): - node = request.getModel() - if node is not None: - object = NodePath(node) + result = request.result() + if isinstance(result, PandaNode): + result = NodePath(result) - elif hasattr(request, "getSound"): - object = request.getSound() - - elif hasattr(request, "getSuccess"): - object = request.getSuccess() - - cb.gotObject(i, object) + cb.gotObject(i, result) load_model = loadModel - cancel_request = cancelRequest - is_request_pending = isRequestPending unload_model = unloadModel save_model = saveModel load_font = loadFont diff --git a/direct/src/showbase/Messenger.py b/direct/src/showbase/Messenger.py index 20c02ce17a..4ac1b96f56 100644 --- a/direct/src/showbase/Messenger.py +++ b/direct/src/showbase/Messenger.py @@ -1,4 +1,5 @@ -"""Undocumented Module""" +"""This defines the Messenger class, which is responsible for most of the +event handling that happens on the Python side.""" __all__ = ['Messenger'] @@ -108,6 +109,12 @@ class Messenger: if record[0] <= 0: del self._id2object[id] + def future(self, event): + """ Returns a future that is triggered by the given event name. This + will function only once. """ + + return eventMgr.eventHandler.get_future(event) + def accept(self, event, object, method, extraArgs=[], persistent=1): """ accept(self, string, DirectObject, Function, List, Boolean) @@ -408,10 +415,14 @@ class Messenger: # Release the lock temporarily while we call the method. self.lock.release() try: - method (*(extraArgs + sentArgs)) + result = method (*(extraArgs + sentArgs)) finally: self.lock.acquire() + if hasattr(result, 'cr_await'): + # It's a coroutine, so schedule it with the task manager. + taskMgr.add(result) + def clear(self): """ Start fresh with a clear dict @@ -531,7 +542,6 @@ class Messenger: keys.sort() for event in keys: if repr(event).find(needle) >= 0: - print(self.__eventRepr(event)) return {event: self.__callbacks[event]} def findAll(self, needle, limit=None): @@ -545,7 +555,6 @@ class Messenger: keys.sort() for event in keys: if repr(event).find(needle) >= 0: - print(self.__eventRepr(event)) matches[event] = self.__callbacks[event] # if the limit is not None, decrement and # check for break: @@ -636,3 +645,16 @@ class Messenger: str = str + '='*50 + '\n' return str + #snake_case alias: + get_events = getEvents + is_ignoring = isIgnoring + who_accepts = whoAccepts + find_all = findAll + replace_method = replaceMethod + ignore_all = ignoreAll + is_accepting = isAccepting + is_empty = isEmpty + detailed_repr = detailedRepr + get_all_accepting = getAllAccepting + toggle_verbose = toggleVerbose + diff --git a/direct/src/showbase/MirrorDemo.py b/direct/src/showbase/MirrorDemo.py index e2a77dbf82..0c5090cbc4 100755 --- a/direct/src/showbase/MirrorDemo.py +++ b/direct/src/showbase/MirrorDemo.py @@ -1,7 +1,3 @@ -"""Undocumented Module""" - -__all__ = ['setupMirror', 'showFrustum'] - """This file demonstrates one way to create a mirror effect in Panda. Call setupMirror() to create a mirror in the world that reflects everything in front of it. @@ -23,10 +19,13 @@ surface are possible, like a funhouse mirror. However, the reflection itself is always basically planar; for more accurate convex reflections, you will need to use a sphere map or a cube map.""" +__all__ = ['setupMirror', 'showFrustum'] + from panda3d.core import * from direct.task import Task -def setupMirror(name, width, height, rootCamera = None): +def setupMirror(name, width, height, rootCamera = None, + bufferSize = 256, clearColor = None): # The return value is a NodePath that contains a rectangle that # reflects render. You can reparent, reposition, and rotate it # anywhere you like. @@ -51,9 +50,12 @@ def setupMirror(name, width, height, rootCamera = None): # Now create an offscreen buffer for rendering the mirror's point # of view. The parameters here control the resolution of the # texture. - buffer = base.win.makeTextureBuffer(name, 256, 256) - #buffer.setClearColor(base.win.getClearColor()) - buffer.setClearColor(VBase4(0, 0, 1, 1)) + buffer = base.win.makeTextureBuffer(name, bufferSize, bufferSize) + if clearColor is None: + buffer.setClearColor(base.win.getClearColor()) + #buffer.setClearColor(VBase4(0, 0, 1, 1)) + else: + buffer.setClearColor(clearColor) # Set up a display region on this buffer, and create a camera. dr = buffer.makeDisplayRegion() @@ -87,6 +89,10 @@ def setupMirror(name, width, height, rootCamera = None): # Set the camera to the mirror-image position of the main camera. cameraNP.setMat(rootCamera.getMat(planeNP) * plane.getReflectionMat()) + # Set the cameras roll to the roll of the mirror. Otherwise + # mirrored objects will be moved unexpectedly + cameraNP.setR(planeNP.getR()-180) + # And reset the frustum to exactly frame the mirror's corners. # This is a minor detail, but it helps to provide a realistic # reflection and keep the subject centered. @@ -94,6 +100,18 @@ def setupMirror(name, width, height, rootCamera = None): ur = cameraNP.getRelativePoint(card, Point3(width / 2.0, 0, height / 2.0)) ll = cameraNP.getRelativePoint(card, Point3(-width / 2.0, 0, -height / 2.0)) lr = cameraNP.getRelativePoint(card, Point3(width / 2.0, 0, -height / 2.0)) + + # get the distance from the mirrors camera to the mirror plane + camvec = planeNP.getPos() - cameraNP.getPos() + camdist = camvec.length() + + # set the discance on the mirrors corners so it will keep correct + # sizes of the mirrored objects + ul.setY(camdist) + ur.setY(camdist) + ll.setY(camdist) + lr.setY(camdist) + lens.setFrustumFromCorners(ul, ur, ll, lr, Lens.FCCameraPlane | Lens.FCOffAxis | Lens.FCAspectRatio) return Task.cont @@ -118,3 +136,21 @@ def showFrustum(np): geomNode.addGeom(lens.makeGeometry()) cameraNP.attachNewNode(geomNode) +if __name__ == "__main__": + from direct.showbase.ShowBase import ShowBase + base = ShowBase() + + panda = loader.loadModel("panda") + panda.setH(180) + panda.setPos(0, 10, -2.5) + panda.setScale(0.5) + panda.reparentTo(render) + + myMirror = setupMirror("mirror", 10, 10, bufferSize=1024, clearColor=(0, 0, 1, 1)) + myMirror.setPos(0, 15, 2.5) + myMirror.setH(180) + + # Uncomment this to show the frustum of the camera in the mirror + #showFrustum(render) + + base.run() diff --git a/direct/src/showbase/ObjectPool.py b/direct/src/showbase/ObjectPool.py index 4b0c0a0731..e5970803ab 100755 --- a/direct/src/showbase/ObjectPool.py +++ b/direct/src/showbase/ObjectPool.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the ObjectPool utility class.""" __all__ = ['Diff', 'ObjectPool'] diff --git a/direct/src/showbase/ObjectReport.py b/direct/src/showbase/ObjectReport.py index b860932d2d..a0fcce7168 100755 --- a/direct/src/showbase/ObjectReport.py +++ b/direct/src/showbase/ObjectReport.py @@ -1,4 +1,13 @@ -"""Undocumented Module""" +""" +>>> from direct.showbase import ObjectReport + +>>> o=ObjectReport.ObjectReport('baseline') +>>> run() +... + +>>> o2=ObjectReport.ObjectReport('') +>>> o.diff(o2) +""" __all__ = ['ExclusiveObjectPool', 'ObjectReport'] @@ -13,17 +22,6 @@ if sys.version_info >= (3, 0): else: import __builtin__ as builtins -""" ->>> from direct.showbase import ObjectReport - ->>> o=ObjectReport.ObjectReport('baseline') ->>> run() -... - ->>> o2=ObjectReport.ObjectReport('') ->>> o.diff(o2) -""" - class ExclusiveObjectPool(DirectObject.DirectObject): # ObjectPool specialization that excludes particular objects # IDs of objects to globally exclude from reporting diff --git a/direct/src/showbase/OnScreenDebug.py b/direct/src/showbase/OnScreenDebug.py index ba24b0637a..1a5756c078 100755 --- a/direct/src/showbase/OnScreenDebug.py +++ b/direct/src/showbase/OnScreenDebug.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the OnScreenDebug class.""" __all__ = ['OnScreenDebug'] diff --git a/direct/src/showbase/Pool.py b/direct/src/showbase/Pool.py index ddcc80a32d..5c1782d629 100755 --- a/direct/src/showbase/Pool.py +++ b/direct/src/showbase/Pool.py @@ -1,9 +1,4 @@ -"""Undocumented Module""" - -__all__ = ['Pool'] - """ - Pool is a collection of python objects that you can checkin and checkout. This is useful for a cache of objects that are expensive to load and can be reused over and over, like splashes on cannonballs, or @@ -12,12 +7,16 @@ or be the same type. Internally the pool is implemented with 2 lists, free items and used items. -p = Pool([1, 2, 3, 4, 5]) -x = p.checkout() -p.checkin(x) +Example:: + + p = Pool([1, 2, 3, 4, 5]) + x = p.checkout() + p.checkin(x) """ +__all__ = ['Pool'] + from direct.directnotify import DirectNotifyGlobal @@ -116,5 +115,3 @@ class Pool: def __repr__(self): return "free = %s\nused = %s" % (self.__free, self.__used) - - diff --git a/direct/src/showbase/SfxPlayer.py b/direct/src/showbase/SfxPlayer.py index 2fb97eae32..4f139a89c9 100644 --- a/direct/src/showbase/SfxPlayer.py +++ b/direct/src/showbase/SfxPlayer.py @@ -1,4 +1,5 @@ -"""Undocumented Module""" +"""Contains the SfxPlayer class, a thin utility class for playing sounds at +a particular location.""" __all__ = ['SfxPlayer'] @@ -95,6 +96,3 @@ class SfxPlayer: if node is not None: finalVolume *= node.getNetAudioVolume() sfx.setVolume(finalVolume) - - - diff --git a/direct/src/showbase/ShadowDemo.py b/direct/src/showbase/ShadowDemo.py index 78f9d33dd0..daadd58c11 100755 --- a/direct/src/showbase/ShadowDemo.py +++ b/direct/src/showbase/ShadowDemo.py @@ -1,8 +1,3 @@ -"""Undocumented Module""" - -__all__ = ['ShadowCaster', 'avatarShadow', 'piratesAvatarShadow', 'arbitraryShadow'] - - """Create a cheesy shadow effect by rendering the view of an object (e.g. the local avatar) from a special camera as seen from above (as if from the sun), using a solid gray foreground and a @@ -14,6 +9,8 @@ multitexture rendering techniques. It's not a particularly great way to do shadows. """ +__all__ = ['ShadowCaster', 'avatarShadow', 'piratesAvatarShadow', 'arbitraryShadow'] + from panda3d.core import * from direct.task import Task diff --git a/direct/src/showbase/ShadowPlacer.py b/direct/src/showbase/ShadowPlacer.py index 80826926f7..845bb5e66e 100755 --- a/direct/src/showbase/ShadowPlacer.py +++ b/direct/src/showbase/ShadowPlacer.py @@ -1,7 +1,3 @@ -"""Undocumented Module""" - -__all__ = ['ShadowPlacer'] - """ ShadowPlacer.py places a shadow. @@ -10,6 +6,8 @@ Or it may do that later, right now it puts a node on the surface under the its parent node. """ +__all__ = ['ShadowPlacer'] + from direct.controls.ControlManager import CollisionHandlerRayStart from direct.directnotify import DirectNotifyGlobal from panda3d.core import * diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index e83583e3dd..467b9c96f5 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -980,7 +980,9 @@ class ShowBase(DirectObject.DirectObject): if isinstance(self.win, GraphicsWindow): self.setupMouse(self.win) self.makeCamera2d(self.win) - self.makeCamera2dp(self.win) + + if self.wantRender2dp: + self.makeCamera2dp(self.win) if oldLens != None: # Restore the previous lens properties. @@ -1270,9 +1272,9 @@ class ShowBase(DirectObject.DirectObject): if win == None: win = self.win - if win != None and win.hasSize() and win.getSbsLeftYSize() != 0: + if win != None and win.getSideBySideStereo() and \ + win.hasSize() and win.getSbsLeftYSize() != 0: aspectRatio = float(win.getSbsLeftXSize()) / float(win.getSbsLeftYSize()) - else: if win == None or not hasattr(win, "getRequestedProperties"): props = WindowProperties.getDefault() @@ -1307,8 +1309,7 @@ class ShowBase(DirectObject.DirectObject): if not props.hasSize(): props = WindowProperties.getDefault() - if props.hasSize(): - return props.getXSize(), props.getYSize() + return props.getXSize(), props.getYSize() def makeCamera(self, win, sort = 0, scene = None, displayRegion = (0, 1, 0, 1), stereo = None, @@ -1572,9 +1573,11 @@ class ShowBase(DirectObject.DirectObject): # Tell the gui system about our new mouse watcher. self.aspect2d.node().setMouseWatcher(mw.node()) - self.aspect2dp.node().setMouseWatcher(mw.node()) self.pixel2d.node().setMouseWatcher(mw.node()) - self.pixel2dp.node().setMouseWatcher(mw.node()) + if self.wantRender2dp: + self.aspect2dp.node().setMouseWatcher(mw.node()) + self.pixel2dp.node().setMouseWatcher(mw.node()) + mw.node().addRegion(PGMouseWatcherBackground()) return self.buttonThrowers[0] @@ -2813,13 +2816,15 @@ class ShowBase(DirectObject.DirectObject): # changed and update the camera lenses and aspect2d parameters self.adjustWindowAspectRatio(self.getAspectRatio()) - # Temporary hasattr for old Pandas - if not hasattr(win, 'getSbsLeftXSize'): - self.pixel2d.setScale(2.0 / win.getXSize(), 1.0, 2.0 / win.getYSize()) - self.pixel2dp.setScale(2.0 / win.getXSize(), 1.0, 2.0 / win.getYSize()) - else: + if win.getSideBySideStereo() and win.hasSize() and win.getSbsLeftYSize() != 0: self.pixel2d.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize()) self.pixel2dp.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize()) + else: + xsize, ysize = self.getSize() + if xsize > 0 and ysize > 0: + self.pixel2d.setScale(2.0 / xsize, 1.0, 2.0 / ysize) + if self.wantRender2dp: + self.pixel2dp.setScale(2.0 / xsize, 1.0, 2.0 / ysize) def adjustWindowAspectRatio(self, aspectRatio): """ This function is normally called internally by @@ -2843,11 +2848,12 @@ class ShowBase(DirectObject.DirectObject): self.a2dLeft = -1 self.a2dRight = 1.0 # Don't forget 2dp - self.aspect2dp.setScale(1.0, aspectRatio, aspectRatio) - self.a2dpTop = 1.0 / aspectRatio - self.a2dpBottom = - 1.0 / aspectRatio - self.a2dpLeft = -1 - self.a2dpRight = 1.0 + if self.wantRender2dp: + self.aspect2dp.setScale(1.0, aspectRatio, aspectRatio) + self.a2dpTop = 1.0 / aspectRatio + self.a2dpBottom = - 1.0 / aspectRatio + self.a2dpLeft = -1 + self.a2dpRight = 1.0 else: # If the window is WIDE, lets expand the left and right @@ -2857,41 +2863,43 @@ class ShowBase(DirectObject.DirectObject): self.a2dLeft = -aspectRatio self.a2dRight = aspectRatio # Don't forget 2dp - self.aspect2dp.setScale(1.0 / aspectRatio, 1.0, 1.0) - self.a2dpTop = 1.0 - self.a2dpBottom = -1.0 - self.a2dpLeft = -aspectRatio - self.a2dpRight = aspectRatio + if self.wantRender2dp: + self.aspect2dp.setScale(1.0 / aspectRatio, 1.0, 1.0) + self.a2dpTop = 1.0 + self.a2dpBottom = -1.0 + self.a2dpLeft = -aspectRatio + self.a2dpRight = aspectRatio # Reposition the aspect2d marker nodes - self.a2dTopCenter.setPos(0, self.a2dTop, self.a2dTop) - self.a2dBottomCenter.setPos(0, self.a2dBottom, self.a2dBottom) + self.a2dTopCenter.setPos(0, 0, self.a2dTop) + self.a2dTopCenterNs.setPos(0, 0, self.a2dTop) + self.a2dBottomCenter.setPos(0, 0, self.a2dBottom) + self.a2dBottomCenterNs.setPos(0, 0, self.a2dBottom) self.a2dLeftCenter.setPos(self.a2dLeft, 0, 0) - self.a2dRightCenter.setPos(self.a2dRight, 0, 0) - self.a2dTopLeft.setPos(self.a2dLeft, self.a2dTop, self.a2dTop) - self.a2dTopRight.setPos(self.a2dRight, self.a2dTop, self.a2dTop) - self.a2dBottomLeft.setPos(self.a2dLeft, self.a2dBottom, self.a2dBottom) - self.a2dBottomRight.setPos(self.a2dRight, self.a2dBottom, self.a2dBottom) - - # Reposition the aspect2d marker nodes - self.a2dTopCenterNs.setPos(0, self.a2dTop, self.a2dTop) - self.a2dBottomCenterNs.setPos(0, self.a2dBottom, self.a2dBottom) self.a2dLeftCenterNs.setPos(self.a2dLeft, 0, 0) + self.a2dRightCenter.setPos(self.a2dRight, 0, 0) self.a2dRightCenterNs.setPos(self.a2dRight, 0, 0) - self.a2dTopLeftNs.setPos(self.a2dLeft, self.a2dTop, self.a2dTop) - self.a2dTopRightNs.setPos(self.a2dRight, self.a2dTop, self.a2dTop) - self.a2dBottomLeftNs.setPos(self.a2dLeft, self.a2dBottom, self.a2dBottom) - self.a2dBottomRightNs.setPos(self.a2dRight, self.a2dBottom, self.a2dBottom) + + self.a2dTopLeft.setPos(self.a2dLeft, 0, self.a2dTop) + self.a2dTopLeftNs.setPos(self.a2dLeft, 0, self.a2dTop) + self.a2dTopRight.setPos(self.a2dRight, 0, self.a2dTop) + self.a2dTopRightNs.setPos(self.a2dRight, 0, self.a2dTop) + self.a2dBottomLeft.setPos(self.a2dLeft, 0, self.a2dBottom) + self.a2dBottomLeftNs.setPos(self.a2dLeft, 0, self.a2dBottom) + self.a2dBottomRight.setPos(self.a2dRight, 0, self.a2dBottom) + self.a2dBottomRightNs.setPos(self.a2dRight, 0, self.a2dBottom) # Reposition the aspect2dp marker nodes - self.a2dpTopCenter.setPos(0, self.a2dpTop, self.a2dpTop) - self.a2dpBottomCenter.setPos(0, self.a2dpBottom, self.a2dpBottom) - self.a2dpLeftCenter.setPos(self.a2dpLeft, 0, 0) - self.a2dpRightCenter.setPos(self.a2dpRight, 0, 0) - self.a2dpTopLeft.setPos(self.a2dpLeft, self.a2dpTop, self.a2dpTop) - self.a2dpTopRight.setPos(self.a2dpRight, self.a2dpTop, self.a2dpTop) - self.a2dpBottomLeft.setPos(self.a2dpLeft, self.a2dpBottom, self.a2dpBottom) - self.a2dpBottomRight.setPos(self.a2dpRight, self.a2dpBottom, self.a2dpBottom) + if self.wantRender2dp: + self.a2dpTopCenter.setPos(0, 0, self.a2dpTop) + self.a2dpBottomCenter.setPos(0, 0, self.a2dpBottom) + self.a2dpLeftCenter.setPos(self.a2dpLeft, 0, 0) + self.a2dpRightCenter.setPos(self.a2dpRight, 0, 0) + + self.a2dpTopLeft.setPos(self.a2dpLeft, 0, self.a2dpTop) + self.a2dpTopRight.setPos(self.a2dpRight, 0, self.a2dpTop) + self.a2dpBottomLeft.setPos(self.a2dpLeft, 0, self.a2dpBottom) + self.a2dpBottomRight.setPos(self.a2dpRight, 0, self.a2dpBottom) # If anybody needs to update their GUI, put a callback on this event messenger.send("aspectRatioChanged") diff --git a/direct/src/showbase/TaskThreaded.py b/direct/src/showbase/TaskThreaded.py index b320e84453..a6634cd751 100755 --- a/direct/src/showbase/TaskThreaded.py +++ b/direct/src/showbase/TaskThreaded.py @@ -1,10 +1,13 @@ -"""Undocumented Module""" +"""Contains the TaskThreaded and TaskThread classes.""" __all__ = ['TaskThreaded', 'TaskThread'] from direct.directnotify.DirectNotifyGlobal import directNotify from direct.task import Task +from .PythonUtil import SerialNumGen + + class TaskThreaded: """ derive from this if you need to do a bunch of CPU-intensive processing and you don't want to hang up the show. Lets you break diff --git a/direct/src/showbase/ThreeUpShow.py b/direct/src/showbase/ThreeUpShow.py index 6449033c50..7e042c7f39 100644 --- a/direct/src/showbase/ThreeUpShow.py +++ b/direct/src/showbase/ThreeUpShow.py @@ -1,4 +1,5 @@ -"""Undocumented Module""" +"""ThreeUpShow is a variant of ShowBase that defines three cameras covering +different parts of the window.""" __all__ = ['ThreeUpShow'] diff --git a/direct/src/showbase/Transitions.py b/direct/src/showbase/Transitions.py index c60ac8c09e..e26d0e93ec 100644 --- a/direct/src/showbase/Transitions.py +++ b/direct/src/showbase/Transitions.py @@ -1,4 +1,6 @@ -"""Undocumented Module""" +"""This module defines various transition effects that can be used to +graphically transition between two scenes, such as by fading the screen to +a particular color.""" __all__ = ['Transitions'] @@ -345,7 +347,7 @@ class Transitions: frameColor = (0, 0, 0, 1), borderWidth = (0, 0), frameSize = (-1, 1, 0, 0.2), - pos = (0, 0, 0.8), + pos = (0, 0, 1.0), image = barImage, image_scale = (2.25,1,.5), image_pos = (0,0,.1), @@ -360,7 +362,7 @@ class Transitions: frameColor = (0, 0, 0, 1), borderWidth = (0, 0), frameSize = (-1, 1, 0, 0.2), - pos = (0, 0, -1), + pos = (0, 0, -1.2), image = barImage, image_scale = (2.25,1,.5), image_pos = (0,0,.1), diff --git a/direct/src/showbase/VerboseImport.py b/direct/src/showbase/VerboseImport.py index 56efc33b23..9c61783ea5 100644 --- a/direct/src/showbase/VerboseImport.py +++ b/direct/src/showbase/VerboseImport.py @@ -1,4 +1,7 @@ -"""Undocumented Module""" +""" +This module hooks into Python's import mechanism to print out all imports to +the standard output as they happen. +""" __all__ = [] diff --git a/direct/src/showutil/pfreeze.py b/direct/src/showutil/pfreeze.py index 04397d31d9..2c2827e051 100755 --- a/direct/src/showutil/pfreeze.py +++ b/direct/src/showutil/pfreeze.py @@ -13,11 +13,11 @@ Python code into a standalone executable. It also uses Python's built-in modulefinder module, which it uses to find all of the modules imported directly or indirectly by the original startfile.py. -Usage: +Usage:: pfreeze.py [opts] [startfile] -Options: +Options:: -o output Specifies the name of the resulting executable file to produce. @@ -67,88 +67,95 @@ def usage(code, msg = ''): sys.stderr.write(str(msg) + '\n') sys.exit(code) -# We're not protecting the next part under a __name__ == __main__ -# check, just so we can import this file directly in ppython.cxx. -freezer = FreezeTool.Freezer() +def main(args=None): + if args is None: + args = sys.argv[1:] -basename = None -addStartupModules = False + freezer = FreezeTool.Freezer() -try: - opts, args = getopt.getopt(sys.argv[1:], 'o:i:x:p:P:slkh') -except getopt.error as msg: - usage(1, msg) + basename = None + addStartupModules = False -for opt, arg in opts: - if opt == '-o': - basename = arg - elif opt == '-i': - for module in arg.split(','): - freezer.addModule(module) - elif opt == '-x': - for module in arg.split(','): - freezer.excludeModule(module) - elif opt == '-p': - for module in arg.split(','): - freezer.handleCustomPath(module) - elif opt == '-P': - sys.path.append(arg) - elif opt == '-s': - addStartupModules = True - elif opt == '-l': - freezer.linkExtensionModules = True - elif opt == '-k': - freezer.keepTemporaryFiles = True - elif opt == '-h': - usage(0) + try: + opts, args = getopt.getopt(args, 'o:i:x:p:P:slkh') + except getopt.error as msg: + usage(1, msg) + + for opt, arg in opts: + if opt == '-o': + basename = arg + elif opt == '-i': + for module in arg.split(','): + freezer.addModule(module) + elif opt == '-x': + for module in arg.split(','): + freezer.excludeModule(module) + elif opt == '-p': + for module in arg.split(','): + freezer.handleCustomPath(module) + elif opt == '-P': + sys.path.append(arg) + elif opt == '-s': + addStartupModules = True + elif opt == '-l': + freezer.linkExtensionModules = True + elif opt == '-k': + freezer.keepTemporaryFiles = True + elif opt == '-h': + usage(0) + else: + print('illegal option: ' + flag) + sys.exit(1) + + if not basename: + usage(1, 'You did not specify an output file.') + + if len(args) > 1: + usage(1, 'Only one main file may be specified.') + + outputType = 'exe' + bl = basename.lower() + if bl.endswith('.mf'): + outputType = 'mf' + elif bl.endswith('.c'): + outputType = 'c' + elif bl.endswith('.dll') or bl.endswith('.pyd') or bl.endswith('.so'): + basename = os.path.splitext(basename)[0] + outputType = 'dll' + elif bl.endswith('.exe'): + basename = os.path.splitext(basename)[0] + + compileToExe = False + if args: + startfile = args[0] + startmod = startfile + if startfile.endswith('.py') or startfile.endswith('.pyw') or \ + startfile.endswith('.pyc') or startfile.endswith('.pyo'): + startmod = os.path.splitext(startfile)[0] + + if outputType == 'dll' or outputType == 'c': + freezer.addModule(startmod, filename = startfile) + else: + freezer.addModule('__main__', filename = startfile) + compileToExe = True + addStartupModules = True + + elif outputType == 'exe': + # We must have a main module when making an executable. + usage(1, 'A main file needs to be specified when creating an executable.') + + freezer.done(addStartupModules = addStartupModules) + + if outputType == 'mf': + freezer.writeMultifile(basename) + elif outputType == 'c': + freezer.writeCode(basename) else: - print('illegal option: ' + flag) - sys.exit(1) + freezer.generateCode(basename, compileToExe = compileToExe) -if not basename: - usage(1, 'You did not specify an output file.') + return 0 -if len(args) > 1: - usage(1, 'Only one main file may be specified.') - -outputType = 'exe' -bl = basename.lower() -if bl.endswith('.mf'): - outputType = 'mf' -elif bl.endswith('.c'): - outputType = 'c' -elif bl.endswith('.dll') or bl.endswith('.pyd') or bl.endswith('.so'): - basename = os.path.splitext(basename)[0] - outputType = 'dll' -elif bl.endswith('.exe'): - basename = os.path.splitext(basename)[0] - -compileToExe = False -if args: - startfile = args[0] - startmod = startfile - if startfile.endswith('.py') or startfile.endswith('.pyw') or \ - startfile.endswith('.pyc') or startfile.endswith('.pyo'): - startmod = os.path.splitext(startfile)[0] - - if outputType == 'dll' or outputType == 'c': - freezer.addModule(startmod, filename = startfile) - else: - freezer.addModule('__main__', filename = startfile) - compileToExe = True - addStartupModules = True - -elif outputType == 'exe': - # We must have a main module when making an executable. - usage(1, 'A main file needs to be specified when creating an executable.') - -freezer.done(addStartupModules = addStartupModules) - -if outputType == 'mf': - freezer.writeMultifile(basename) -elif outputType == 'c': - freezer.writeCode(basename) -else: - freezer.generateCode(basename, compileToExe = compileToExe) +if __name__ == '__main__': + sys.exit(main()) diff --git a/direct/src/stdpy/__init__.py b/direct/src/stdpy/__init__.py index e69de29bb2..9e4739449c 100644 --- a/direct/src/stdpy/__init__.py +++ b/direct/src/stdpy/__init__.py @@ -0,0 +1,5 @@ +""" +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. +""" diff --git a/direct/src/stdpy/thread.py b/direct/src/stdpy/thread.py index 05090e65d8..c10675dd4c 100644 --- a/direct/src/stdpy/thread.py +++ b/direct/src/stdpy/thread.py @@ -11,18 +11,32 @@ __all__ = [ 'interrupt_main', 'exit', 'allocate_lock', 'get_ident', 'stack_size', + 'force_yield', 'consider_yield', 'forceYield', 'considerYield', + 'TIMEOUT_MAX' ] from panda3d import core +import sys + +if sys.platform == "win32": + TIMEOUT_MAX = float(0xffffffff // 1000) +else: + TIMEOUT_MAX = float(0x7fffffffffffffff // 1000000000) # These methods are defined in Panda, and are particularly useful if # you may be running in Panda's SIMPLE_THREADS compilation mode. -forceYield = core.Thread.forceYield -considerYield = core.Thread.considerYield +force_yield = core.Thread.force_yield +consider_yield = core.Thread.consider_yield -class error(Exception): - pass +forceYield = force_yield +considerYield = consider_yield + +if sys.version_info >= (3, 3): + error = RuntimeError +else: + class error(Exception): + pass class LockType: """ Implements a mutex lock. Instead of directly subclassing @@ -36,13 +50,18 @@ class LockType: self.__cvar = core.ConditionVar(self.__lock) self.__locked = False - def acquire(self, waitflag = 1): + def acquire(self, waitflag = 1, timeout = -1): self.__lock.acquire() try: if self.__locked and not waitflag: return False - while self.__locked: - self.__cvar.wait() + + if timeout >= 0: + while self.__locked: + self.__cvar.wait(timeout) + else: + while self.__locked: + self.__cvar.wait() self.__locked = True return True @@ -202,12 +221,17 @@ def _get_thread_locals(thread, i): def _remove_thread_id(threadId): """ Removes the thread with the indicated ID from the thread list. """ + # On interpreter shutdown, Python may set module globals to None. + if _threadsLock is None or _threads is None: + return + _threadsLock.acquire() try: - thread, locals, wrapper = _threads[threadId] - assert thread.getPythonIndex() == threadId - del _threads[threadId] - thread.setPythonIndex(-1) + if threadId in _threads: + thread, locals, wrapper = _threads[threadId] + assert thread.getPythonIndex() == threadId + del _threads[threadId] + thread.setPythonIndex(-1) finally: _threadsLock.release() diff --git a/direct/src/stdpy/threading.py b/direct/src/stdpy/threading.py index 5c8af3cc62..b4cb6d9228 100644 --- a/direct/src/stdpy/threading.py +++ b/direct/src/stdpy/threading.py @@ -35,11 +35,15 @@ __all__ = [ 'Event', 'Timer', 'local', - 'current_thread', 'currentThread', - 'enumerate', 'active_count', 'activeCount', + 'current_thread', + 'main_thread', + 'enumerate', 'active_count', 'settrace', 'setprofile', 'stack_size', + 'TIMEOUT_MAX', ] +TIMEOUT_MAX = _thread.TIMEOUT_MAX + local = _thread._local _newname = _thread._newname @@ -98,7 +102,15 @@ class Thread(ThreadBase): self.__dict__['daemon'] = current.daemon self.__dict__['name'] = name - self.__thread = core.PythonThread(self.run, None, name, name) + def call_run(): + # As soon as the thread is done, break the circular reference. + try: + self.run() + finally: + self.__thread = None + _thread._remove_thread_id(self.ident) + + self.__thread = core.PythonThread(call_run, None, name, name) threadId = _thread._add_thread(self.__thread, weakref.proxy(self)) self.__dict__['ident'] = threadId @@ -109,16 +121,17 @@ class Thread(ThreadBase): _thread._remove_thread_id(self.ident) def is_alive(self): - return self.__thread.isStarted() + thread = self.__thread + return thread is not None and thread.is_started() - def isAlive(self): - return self.__thread.isStarted() + isAlive = is_alive def start(self): - if self.__thread.isStarted(): + thread = self.__thread + if thread is None or thread.is_started(): raise RuntimeError - if not self.__thread.start(core.TPNormal, True): + if not thread.start(core.TPNormal, True): raise RuntimeError def run(self): @@ -132,8 +145,12 @@ class Thread(ThreadBase): def join(self, timeout = None): # We don't support a timed join here, sorry. assert timeout is None - self.__thread.join() - self.__thread = None + thread = self.__thread + if thread is not None: + thread.join() + # Clear the circular reference. + self.__thread = None + _thread._remove_thread_id(self.ident) def setName(self, name): self.__dict__['name'] = name @@ -379,6 +396,10 @@ def current_thread(): t = core.Thread.getCurrentThread() return _thread._get_thread_wrapper(t, _create_thread_wrapper) +def main_thread(): + t = core.Thread.getMainThread() + return _thread._get_thread_wrapper(t, _create_thread_wrapper) + currentThread = current_thread def enumerate(): @@ -386,7 +407,7 @@ def enumerate(): _thread._threadsLock.acquire() try: for thread, locals, wrapper in list(_thread._threads.values()): - if wrapper and thread.isStarted(): + if wrapper and wrapper.is_alive(): tlist.append(wrapper) return tlist finally: @@ -394,6 +415,7 @@ def enumerate(): def active_count(): return len(enumerate()) + activeCount = active_count _settrace_func = None diff --git a/direct/src/stdpy/threading2.py b/direct/src/stdpy/threading2.py index 9beee770c0..6e25f5208c 100644 --- a/direct/src/stdpy/threading2.py +++ b/direct/src/stdpy/threading2.py @@ -15,7 +15,7 @@ implementation. """ import sys as _sys -from direct.stdpy import thread +from direct.stdpy import thread as _thread from direct.stdpy.thread import stack_size, _newname, _local as local from panda3d import core _sleep = core.Thread.sleep @@ -23,16 +23,19 @@ _sleep = core.Thread.sleep from time import time as _time from traceback import format_exc as _format_exc -# Rename some stuff so "from threading import *" is safe -__all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event', - 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread', - 'Timer', 'setprofile', 'settrace', 'local', 'stack_size'] +__all__ = ['get_ident', 'active_count', 'Condition', 'current_thread', + 'enumerate', 'main_thread', 'TIMEOUT_MAX', + 'Event', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread', + 'Timer', 'ThreadError', + 'setprofile', 'settrace', 'local', 'stack_size'] -_start_new_thread = thread.start_new_thread -_allocate_lock = thread.allocate_lock -_get_ident = thread.get_ident -ThreadError = thread.error -del thread +# Rename some stuff so "from threading import *" is safe +_start_new_thread = _thread.start_new_thread +_allocate_lock = _thread.allocate_lock +get_ident = _thread.get_ident +ThreadError = _thread.error +TIMEOUT_MAX = _thread.TIMEOUT_MAX +del _thread # Debug support (adapted from ihooks.py). @@ -446,7 +449,7 @@ class Thread(_Verbose): try: self.__started = True _active_limbo_lock.acquire() - _active[_get_ident()] = self + _active[get_ident()] = self del _limbo[self] _active_limbo_lock.release() if __debug__: @@ -537,7 +540,7 @@ class Thread(_Verbose): _active_limbo_lock.acquire() try: try: - del _active[_get_ident()] + del _active[get_ident()] except KeyError: if 'dummy_threading' not in _sys.modules: raise @@ -581,10 +584,12 @@ class Thread(_Verbose): assert self.__initialized, "Thread.__init__() not called" self.__name = str(name) - def isAlive(self): + def is_alive(self): assert self.__initialized, "Thread.__init__() not called" return self.__started and not self.__stopped + isAlive = is_alive + def isDaemon(self): assert self.__initialized, "Thread.__init__() not called" return self.__daemonic @@ -634,7 +639,7 @@ class _MainThread(Thread): Thread.__init__(self, name="MainThread") self._Thread__started = True _active_limbo_lock.acquire() - _active[_get_ident()] = self + _active[get_ident()] = self _active_limbo_lock.release() def _set_daemon(self): @@ -653,12 +658,6 @@ class _MainThread(Thread): self._note("%s: exiting", self) self._Thread__delete() -def _pickSomeNonDaemonThread(): - for t in enumerate(): - if not t.isDaemon() and t.isAlive(): - return t - return None - # Dummy thread class to represent threads not started here. # These aren't garbage collected when they die, nor can they be waited for. @@ -680,7 +679,7 @@ class _DummyThread(Thread): self._Thread__started = True _active_limbo_lock.acquire() - _active[_get_ident()] = self + _active[get_ident()] = self _active_limbo_lock.release() def _set_daemon(self): @@ -692,19 +691,23 @@ class _DummyThread(Thread): # Global API functions -def currentThread(): +def current_thread(): try: - return _active[_get_ident()] + return _active[get_ident()] except KeyError: - ##print "currentThread(): no current thread for", _get_ident() + ##print "current_thread(): no current thread for", get_ident() return _DummyThread() -def activeCount(): +currentThread = current_thread + +def active_count(): _active_limbo_lock.acquire() count = len(_active) + len(_limbo) _active_limbo_lock.release() return count +activeCount = active_count + def enumerate(): _active_limbo_lock.acquire() active = list(_active.values()) + list(_limbo.values()) @@ -717,7 +720,21 @@ def enumerate(): # and make it available for the interpreter # (Py_Main) as threading._shutdown. -_shutdown = _MainThread()._exitfunc +_main_thread = _MainThread() +_shutdown = _main_thread._exitfunc + +def _pickSomeNonDaemonThread(): + for t in enumerate(): + if not t.isDaemon() and t.isAlive(): + return t + return None + +def main_thread(): + """Return the main thread object. + In normal conditions, the main thread is the thread from which the + Python interpreter was started. + """ + return _main_thread # get thread-local implementation, either from the thread # module, or from the python fallback diff --git a/direct/src/task/Task.py b/direct/src/task/Task.py index 215b240d16..8181638264 100644 --- a/direct/src/task/Task.py +++ b/direct/src/task/Task.py @@ -74,7 +74,9 @@ Task = PythonTask # Copy the module-level enums above into the class level. This funny # syntax is necessary because it's a C++-wrapped extension type, not a # true Python class. -Task.DtoolClassDict['done'] = done +# We can't override 'done', which is already a known method. We have a +# special check in PythonTask for when the method is being returned. +#Task.DtoolClassDict['done'] = done Task.DtoolClassDict['cont'] = cont Task.DtoolClassDict['again'] = again Task.DtoolClassDict['pickup'] = pickup @@ -333,6 +335,7 @@ class TaskManager: funcOrTask - either an existing Task object (not already added to the task manager), or a callable function object. If this is a function, a new Task object will be created and returned. + You may also pass in a coroutine object. name - the name to assign to the Task. Required, unless you are passing in a Task object that already has a name. @@ -385,6 +388,15 @@ class TaskManager: task = funcOrTask elif hasattr(funcOrTask, '__call__'): task = PythonTask(funcOrTask) + if name is None: + name = getattr(funcOrTask, '__qualname__', None) or \ + getattr(funcOrTask, '__name__', None) + elif hasattr(funcOrTask, 'cr_await') or type(funcOrTask) == types.GeneratorType: + # It's a coroutine, or something emulating one. + task = PythonTask(funcOrTask) + if name is None: + name = getattr(funcOrTask, '__qualname__', None) or \ + getattr(funcOrTask, '__name__', None) else: self.notify.error( 'add: Tried to add a task that was not a Task or a func') diff --git a/direct/src/task/TaskManagerGlobal.py b/direct/src/task/TaskManagerGlobal.py index 60587d7a4f..792938cfc4 100644 --- a/direct/src/task/TaskManagerGlobal.py +++ b/direct/src/task/TaskManagerGlobal.py @@ -4,4 +4,5 @@ __all__ = ['taskMgr'] from . import Task +#: The global task manager. taskMgr = Task.TaskManager() diff --git a/direct/src/task/TaskTester.py b/direct/src/task/TaskTester.py index 25437f9b6b..cbe8cd0bd7 100755 --- a/direct/src/task/TaskTester.py +++ b/direct/src/task/TaskTester.py @@ -24,7 +24,8 @@ def taskCallback(task): spawnNewTask() return Task.done -taskMgr.removeTasksMatching("taskTester*") +if __name__ == '__main__': + taskMgr.removeTasksMatching("taskTester*") -for i in range(numTasks): - spawnNewTask() + for i in range(numTasks): + spawnNewTask() diff --git a/direct/src/task/Timer.py b/direct/src/task/Timer.py index be7957183c..47d5041de9 100644 --- a/direct/src/task/Timer.py +++ b/direct/src/task/Timer.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the Timer class.""" __all__ = ['Timer'] diff --git a/direct/src/task/__init__.py b/direct/src/task/__init__.py index e69de29bb2..40a5246131 100644 --- a/direct/src/task/__init__.py +++ b/direct/src/task/__init__.py @@ -0,0 +1,8 @@ +""" +This package contains the Python interface to the task system, which +manages scheduled functions that are executed at designated intervals. + +The global task manager object can be imported as a singleton:: + + from direct.task.TaskManagerGlobal import taskMgr +""" diff --git a/direct/src/tkpanels/DirectSessionPanel.py b/direct/src/tkpanels/DirectSessionPanel.py index 65a262eda3..b40583cda3 100644 --- a/direct/src/tkpanels/DirectSessionPanel.py +++ b/direct/src/tkpanels/DirectSessionPanel.py @@ -177,8 +177,8 @@ class DirectSessionPanel(AppShell): sgeFrame, nodePath = render, scrolledCanvas_hull_width = 250, scrolledCanvas_hull_height = 300) - self.SGE.pack(fill = BOTH, expand = 0) - sgeFrame.pack(side = LEFT, fill = 'both', expand = 0) + self.SGE.pack(fill = BOTH, expand = 1) + sgeFrame.pack(side = LEFT, fill = 'both', expand = 1) # Create the notebook pages notebook = Pmw.NoteBook(notebookFrame) diff --git a/direct/src/tkpanels/NotifyPanel.py b/direct/src/tkpanels/NotifyPanel.py index 9712903f7a..f13e7d92c6 100644 --- a/direct/src/tkpanels/NotifyPanel.py +++ b/direct/src/tkpanels/NotifyPanel.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the NotifyPanel class.""" __all__ = ['NotifyPanel'] diff --git a/direct/src/tkwidgets/MemoryExplorer.py b/direct/src/tkwidgets/MemoryExplorer.py index 62e74a6202..04e81875a0 100755 --- a/direct/src/tkwidgets/MemoryExplorer.py +++ b/direct/src/tkwidgets/MemoryExplorer.py @@ -284,7 +284,7 @@ class MemoryExplorerItem: def getNumChildren(self): return len(self.children) - def getChildrenAsList(self): + def getChildren(self): return self.children def getName(self): diff --git a/direct/src/tkwidgets/SceneGraphExplorer.py b/direct/src/tkwidgets/SceneGraphExplorer.py index 7e39dbeac1..256f226f46 100644 --- a/direct/src/tkwidgets/SceneGraphExplorer.py +++ b/direct/src/tkwidgets/SceneGraphExplorer.py @@ -1,4 +1,7 @@ -"""Undocumented Module""" +"""This module defines a widget used to display a graphical overview of the +scene graph using the tkinter GUI system. + +Requires Pmw.""" __all__ = ['SceneGraphExplorer', 'SceneGraphExplorerItem', 'explore'] diff --git a/direct/src/tkwidgets/Tree.py b/direct/src/tkwidgets/Tree.py index e3e49b925f..ff8a273965 100644 --- a/direct/src/tkwidgets/Tree.py +++ b/direct/src/tkwidgets/Tree.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Defines tree widgets for the tkinter GUI system.""" __all__ = ['TreeNode', 'TreeItem'] diff --git a/direct/src/tkwidgets/WidgetPropertiesDialog.py b/direct/src/tkwidgets/WidgetPropertiesDialog.py index 0693a885d1..584e45620c 100644 --- a/direct/src/tkwidgets/WidgetPropertiesDialog.py +++ b/direct/src/tkwidgets/WidgetPropertiesDialog.py @@ -1,4 +1,4 @@ -"""Undocumented Module""" +"""Contains the WidgetPropertiesDialog class.""" __all__ = ['WidgetPropertiesDialog'] diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index d3e22a1fc3..4a8a13ee64 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -1,3 +1,18 @@ +------------------------ RELEASE 1.9.4 ------------------------ + +One of the bugfixes in the last 1.9.3 release introduced a regression, +therefore it was decided to make another 1.9.x release. + +* Fix 1.9.3 regression with generating geometry in threaded pipeline +* Various compile warning fixes +* Fix occasional crash in PNMImage::quick_filter_from() +* Fix issue taking screenshots from an OpenGL FBO buffer +* Fix various issues with MeshDrawer +* Fix issue with collision sphere generation in bam2egg +* Fix compile errors with more obscure Python configurations +* Fix assert when using Texture.load_sub_image to load whole image +* Fix fsm FourState + ------------------------ RELEASE 1.9.3 ------------------------ This issue fixes several bugs that were still found in 1.9.2. diff --git a/dtool/src/cppparser/cppBison.cxx.prebuilt b/dtool/src/cppparser/cppBison.cxx.prebuilt index 877a9636e1..2167d06578 100644 --- a/dtool/src/cppparser/cppBison.cxx.prebuilt +++ b/dtool/src/cppparser/cppBison.cxx.prebuilt @@ -73,6 +73,7 @@ #include "cppBisonDefs.h" #include "cppParser.h" +#include "cppClosureType.h" #include "cppExpression.h" #include "cppSimpleType.h" #include "cppExtensionType.h" @@ -109,6 +110,7 @@ static CPPEnumType *current_enum = NULL; static int current_storage_class = 0; static CPPType *current_type = NULL; static CPPExpression *current_expr = NULL; +static CPPClosureType *current_closure = NULL; static int publish_nest_level = 0; static CPPVisibility publish_previous; static YYLTYPE publish_loc; @@ -262,7 +264,7 @@ pop_struct() { } -#line 266 "built/tmp/cppBison.yxx.c" /* yacc.c:339 */ +#line 268 "built/tmp/cppBison.yxx.c" /* yacc.c:339 */ # ifndef YY_NULLPTR # if defined __cplusplus && 201103L <= __cplusplus @@ -341,103 +343,106 @@ extern int cppyydebug; XOREQUAL = 299, LSHIFTEQUAL = 300, RSHIFTEQUAL = 301, - KW_ALIGNAS = 302, - KW_ALIGNOF = 303, - KW_AUTO = 304, - KW_BEGIN_PUBLISH = 305, - KW_BLOCKING = 306, - KW_BOOL = 307, - KW_CATCH = 308, - KW_CHAR = 309, - KW_CHAR16_T = 310, - KW_CHAR32_T = 311, - KW_CLASS = 312, - KW_CONST = 313, - KW_CONSTEXPR = 314, - KW_CONST_CAST = 315, - KW_DECLTYPE = 316, - KW_DEFAULT = 317, - KW_DELETE = 318, - KW_DOUBLE = 319, - KW_DYNAMIC_CAST = 320, - KW_ELSE = 321, - KW_END_PUBLISH = 322, - KW_ENUM = 323, - KW_EXTENSION = 324, - KW_EXTERN = 325, - KW_EXPLICIT = 326, - KW_PUBLISHED = 327, - KW_FALSE = 328, - KW_FINAL = 329, - KW_FLOAT = 330, - KW_FRIEND = 331, - KW_FOR = 332, - KW_GOTO = 333, - KW_HAS_VIRTUAL_DESTRUCTOR = 334, - KW_IF = 335, - KW_INLINE = 336, - KW_INT = 337, - KW_IS_ABSTRACT = 338, - KW_IS_BASE_OF = 339, - KW_IS_CLASS = 340, - KW_IS_CONSTRUCTIBLE = 341, - KW_IS_CONVERTIBLE_TO = 342, - KW_IS_DESTRUCTIBLE = 343, - KW_IS_EMPTY = 344, - KW_IS_ENUM = 345, - KW_IS_FINAL = 346, - KW_IS_FUNDAMENTAL = 347, - KW_IS_POD = 348, - KW_IS_POLYMORPHIC = 349, - KW_IS_STANDARD_LAYOUT = 350, - KW_IS_TRIVIAL = 351, - KW_IS_UNION = 352, - KW_LONG = 353, - KW_MAKE_MAP_PROPERTY = 354, - KW_MAKE_PROPERTY = 355, - KW_MAKE_PROPERTY2 = 356, - KW_MAKE_SEQ = 357, - KW_MAKE_SEQ_PROPERTY = 358, - KW_MUTABLE = 359, - KW_NAMESPACE = 360, - KW_NEW = 361, - KW_NOEXCEPT = 362, - KW_NULLPTR = 363, - KW_OPERATOR = 364, - KW_OVERRIDE = 365, - KW_PRIVATE = 366, - KW_PROTECTED = 367, - KW_PUBLIC = 368, - KW_REGISTER = 369, - KW_REINTERPRET_CAST = 370, - KW_RETURN = 371, - KW_SHORT = 372, - KW_SIGNED = 373, - KW_SIZEOF = 374, - KW_STATIC = 375, - KW_STATIC_ASSERT = 376, - KW_STATIC_CAST = 377, - KW_STRUCT = 378, - KW_TEMPLATE = 379, - KW_THREAD_LOCAL = 380, - KW_THROW = 381, - KW_TRUE = 382, - KW_TRY = 383, - KW_TYPEDEF = 384, - KW_TYPEID = 385, - KW_TYPENAME = 386, - KW_UNDERLYING_TYPE = 387, - KW_UNION = 388, - KW_UNSIGNED = 389, - KW_USING = 390, - KW_VIRTUAL = 391, - KW_VOID = 392, - KW_VOLATILE = 393, - KW_WCHAR_T = 394, - KW_WHILE = 395, - START_CPP = 396, - START_CONST_EXPR = 397, - START_TYPE = 398 + 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 }; #endif /* Tokens. */ @@ -485,103 +490,106 @@ extern int cppyydebug; #define XOREQUAL 299 #define LSHIFTEQUAL 300 #define RSHIFTEQUAL 301 -#define KW_ALIGNAS 302 -#define KW_ALIGNOF 303 -#define KW_AUTO 304 -#define KW_BEGIN_PUBLISH 305 -#define KW_BLOCKING 306 -#define KW_BOOL 307 -#define KW_CATCH 308 -#define KW_CHAR 309 -#define KW_CHAR16_T 310 -#define KW_CHAR32_T 311 -#define KW_CLASS 312 -#define KW_CONST 313 -#define KW_CONSTEXPR 314 -#define KW_CONST_CAST 315 -#define KW_DECLTYPE 316 -#define KW_DEFAULT 317 -#define KW_DELETE 318 -#define KW_DOUBLE 319 -#define KW_DYNAMIC_CAST 320 -#define KW_ELSE 321 -#define KW_END_PUBLISH 322 -#define KW_ENUM 323 -#define KW_EXTENSION 324 -#define KW_EXTERN 325 -#define KW_EXPLICIT 326 -#define KW_PUBLISHED 327 -#define KW_FALSE 328 -#define KW_FINAL 329 -#define KW_FLOAT 330 -#define KW_FRIEND 331 -#define KW_FOR 332 -#define KW_GOTO 333 -#define KW_HAS_VIRTUAL_DESTRUCTOR 334 -#define KW_IF 335 -#define KW_INLINE 336 -#define KW_INT 337 -#define KW_IS_ABSTRACT 338 -#define KW_IS_BASE_OF 339 -#define KW_IS_CLASS 340 -#define KW_IS_CONSTRUCTIBLE 341 -#define KW_IS_CONVERTIBLE_TO 342 -#define KW_IS_DESTRUCTIBLE 343 -#define KW_IS_EMPTY 344 -#define KW_IS_ENUM 345 -#define KW_IS_FINAL 346 -#define KW_IS_FUNDAMENTAL 347 -#define KW_IS_POD 348 -#define KW_IS_POLYMORPHIC 349 -#define KW_IS_STANDARD_LAYOUT 350 -#define KW_IS_TRIVIAL 351 -#define KW_IS_UNION 352 -#define KW_LONG 353 -#define KW_MAKE_MAP_PROPERTY 354 -#define KW_MAKE_PROPERTY 355 -#define KW_MAKE_PROPERTY2 356 -#define KW_MAKE_SEQ 357 -#define KW_MAKE_SEQ_PROPERTY 358 -#define KW_MUTABLE 359 -#define KW_NAMESPACE 360 -#define KW_NEW 361 -#define KW_NOEXCEPT 362 -#define KW_NULLPTR 363 -#define KW_OPERATOR 364 -#define KW_OVERRIDE 365 -#define KW_PRIVATE 366 -#define KW_PROTECTED 367 -#define KW_PUBLIC 368 -#define KW_REGISTER 369 -#define KW_REINTERPRET_CAST 370 -#define KW_RETURN 371 -#define KW_SHORT 372 -#define KW_SIGNED 373 -#define KW_SIZEOF 374 -#define KW_STATIC 375 -#define KW_STATIC_ASSERT 376 -#define KW_STATIC_CAST 377 -#define KW_STRUCT 378 -#define KW_TEMPLATE 379 -#define KW_THREAD_LOCAL 380 -#define KW_THROW 381 -#define KW_TRUE 382 -#define KW_TRY 383 -#define KW_TYPEDEF 384 -#define KW_TYPEID 385 -#define KW_TYPENAME 386 -#define KW_UNDERLYING_TYPE 387 -#define KW_UNION 388 -#define KW_UNSIGNED 389 -#define KW_USING 390 -#define KW_VIRTUAL 391 -#define KW_VOID 392 -#define KW_VOLATILE 393 -#define KW_WCHAR_T 394 -#define KW_WHILE 395 -#define START_CPP 396 -#define START_CONST_EXPR 397 -#define START_TYPE 398 +#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 /* Value type. */ @@ -607,7 +615,7 @@ int cppyyparse (void); /* Copy the second part of user declarations. */ -#line 611 "built/tmp/cppBison.yxx.c" /* yacc.c:358 */ +#line 619 "built/tmp/cppBison.yxx.c" /* yacc.c:358 */ #ifdef short # undef short @@ -851,21 +859,21 @@ union yyalloc /* YYFINAL -- State number of the termination state. */ #define YYFINAL 104 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 6206 +#define YYLAST 7127 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 168 +#define YYNTOKENS 171 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 107 +#define YYNNTS 110 /* YYNRULES -- Number of rules. */ -#define YYNRULES 733 +#define YYNRULES 763 /* YYNSTATES -- Number of states. */ -#define YYNSTATES 1482 +#define YYNSTATES 1570 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 398 +#define YYMAXUTOK 401 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -877,16 +885,16 @@ static const yytype_uint8 yytranslate[] = 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 167, 2, 2, 2, 159, 152, 2, - 162, 164, 157, 155, 145, 156, 161, 158, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 147, 146, - 153, 148, 154, 149, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 169, 2, 2, 2, 162, 155, 2, + 165, 167, 160, 158, 148, 159, 164, 161, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 150, 149, + 156, 151, 157, 152, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 163, 2, 166, 151, 2, 2, 2, 2, 2, + 2, 166, 2, 170, 154, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 144, 150, 165, 160, 2, 2, 2, + 2, 2, 2, 147, 153, 168, 163, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -913,87 +921,91 @@ static const yytype_uint8 yytranslate[] = 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, - 135, 136, 137, 138, 139, 140, 141, 142, 143 + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 442, 442, 443, 447, 454, 455, 456, 460, 461, - 465, 469, 473, 486, 485, 497, 498, 499, 500, 501, - 502, 503, 516, 525, 529, 537, 541, 545, 556, 577, - 607, 624, 652, 689, 711, 744, 766, 777, 791, 790, - 805, 809, 814, 818, 829, 833, 837, 841, 845, 849, - 853, 857, 861, 865, 869, 873, 881, 882, 886, 887, - 892, 891, 907, 916, 924, 932, 940, 951, 967, 966, - 981, 996, 1005, 1020, 1019, 1044, 1043, 1071, 1070, 1101, - 1100, 1119, 1118, 1139, 1138, 1170, 1169, 1195, 1208, 1212, - 1216, 1220, 1224, 1228, 1232, 1236, 1240, 1245, 1249, 1253, - 1264, 1268, 1272, 1276, 1280, 1284, 1288, 1292, 1296, 1300, - 1304, 1308, 1312, 1316, 1320, 1324, 1328, 1332, 1336, 1340, - 1344, 1348, 1352, 1356, 1360, 1364, 1368, 1372, 1376, 1380, - 1384, 1388, 1392, 1396, 1400, 1404, 1408, 1412, 1416, 1423, - 1424, 1429, 1428, 1436, 1440, 1441, 1445, 1451, 1460, 1461, - 1465, 1469, 1473, 1477, 1483, 1489, 1495, 1502, 1507, 1516, - 1520, 1525, 1533, 1545, 1549, 1563, 1578, 1583, 1588, 1593, - 1598, 1603, 1608, 1613, 1619, 1618, 1640, 1650, 1660, 1664, - 1668, 1677, 1681, 1686, 1690, 1695, 1703, 1708, 1716, 1720, - 1725, 1729, 1734, 1742, 1747, 1755, 1759, 1766, 1770, 1777, - 1781, 1785, 1789, 1793, 1800, 1804, 1808, 1812, 1816, 1820, - 1827, 1828, 1829, 1833, 1836, 1837, 1838, 1842, 1847, 1853, - 1859, 1864, 1870, 1876, 1887, 1891, 1901, 1905, 1909, 1914, - 1919, 1924, 1929, 1934, 1939, 1947, 1951, 1955, 1960, 1965, - 1970, 1975, 1980, 1985, 1990, 1996, 2004, 2009, 2014, 2019, - 2024, 2029, 2034, 2039, 2044, 2049, 2055, 2063, 2067, 2072, - 2077, 2082, 2087, 2092, 2097, 2102, 2107, 2115, 2119, 2124, - 2129, 2134, 2139, 2144, 2149, 2154, 2159, 2164, 2170, 2177, - 2184, 2194, 2198, 2206, 2210, 2214, 2218, 2222, 2238, 2254, - 2263, 2273, 2280, 2291, 2295, 2303, 2307, 2311, 2315, 2319, - 2335, 2351, 2369, 2378, 2388, 2395, 2399, 2407, 2411, 2427, - 2443, 2452, 2462, 2469, 2473, 2481, 2485, 2490, 2494, 2503, - 2502, 2527, 2526, 2556, 2557, 2564, 2565, 2569, 2570, 2574, - 2578, 2582, 2586, 2590, 2594, 2598, 2602, 2606, 2610, 2617, - 2625, 2629, 2633, 2638, 2646, 2650, 2657, 2658, 2663, 2670, - 2671, 2676, 2684, 2688, 2692, 2699, 2703, 2707, 2715, 2714, - 2737, 2736, 2759, 2760, 2764, 2770, 2777, 2786, 2787, 2788, - 2792, 2796, 2800, 2804, 2808, 2812, 2817, 2822, 2827, 2832, - 2836, 2841, 2850, 2855, 2863, 2867, 2871, 2879, 2889, 2889, - 2899, 2900, 2904, 2905, 2906, 2907, 2908, 2909, 2910, 2911, - 2912, 2913, 2914, 2915, 2915, 2915, 2916, 2916, 2916, 2916, - 2917, 2917, 2917, 2917, 2917, 2918, 2918, 2918, 2919, 2919, - 2919, 2919, 2919, 2920, 2920, 2920, 2920, 2920, 2921, 2921, - 2921, 2921, 2921, 2922, 2922, 2922, 2922, 2922, 2923, 2923, - 2923, 2923, 2924, 2924, 2924, 2924, 2924, 2925, 2925, 2925, - 2925, 2925, 2926, 2926, 2926, 2926, 2926, 2926, 2927, 2927, - 2927, 2927, 2927, 2928, 2928, 2928, 2928, 2929, 2929, 2929, - 2929, 2930, 2930, 2930, 2930, 2930, 2931, 2931, 2931, 2931, - 2932, 2932, 2932, 2932, 2932, 2933, 2933, 2933, 2933, 2934, - 2934, 2934, 2934, 2934, 2935, 2935, 2938, 2938, 2938, 2938, - 2938, 2938, 2938, 2938, 2938, 2938, 2938, 2939, 2939, 2939, - 2939, 2939, 2939, 2939, 2939, 2939, 2939, 2940, 2940, 2944, - 2948, 2955, 2959, 2966, 2970, 2977, 2981, 2985, 2989, 2993, - 2997, 3001, 3005, 3009, 3013, 3017, 3021, 3025, 3029, 3033, - 3037, 3041, 3045, 3049, 3053, 3057, 3061, 3065, 3069, 3073, - 3077, 3081, 3085, 3089, 3093, 3097, 3101, 3105, 3109, 3113, - 3117, 3121, 3125, 3133, 3137, 3141, 3145, 3149, 3153, 3157, - 3167, 3173, 3179, 3185, 3191, 3197, 3203, 3210, 3217, 3224, - 3231, 3237, 3243, 3247, 3251, 3255, 3259, 3263, 3274, 3285, - 3289, 3293, 3297, 3301, 3305, 3309, 3313, 3317, 3321, 3325, - 3329, 3333, 3337, 3341, 3345, 3349, 3353, 3357, 3361, 3365, - 3369, 3373, 3377, 3381, 3385, 3389, 3393, 3397, 3401, 3405, - 3412, 3416, 3420, 3424, 3428, 3432, 3436, 3440, 3444, 3450, - 3456, 3460, 3464, 3468, 3472, 3476, 3480, 3484, 3488, 3492, - 3496, 3500, 3504, 3508, 3512, 3516, 3520, 3524, 3528, 3532, - 3546, 3550, 3554, 3558, 3562, 3566, 3570, 3574, 3578, 3582, - 3586, 3590, 3601, 3612, 3616, 3620, 3624, 3628, 3632, 3636, - 3640, 3644, 3648, 3652, 3656, 3660, 3664, 3668, 3672, 3676, - 3680, 3684, 3688, 3692, 3696, 3700, 3704, 3708, 3712, 3716, - 3720, 3724, 3728, 3735, 3739, 3743, 3747, 3751, 3755, 3759, - 3763, 3767, 3773, 3779, 3787, 3788, 3789, 3793, 3794, 3795, - 3796, 3800, 3808, 3816, 3820, 3850, 3854, 3858, 3862, 3866, - 3870, 3876, 3880, 3884, 3888, 3899, 3903, 3907, 3911, 3919, - 3923, 3927, 3933, 3944 + 0, 450, 450, 451, 455, 462, 463, 464, 468, 469, + 473, 477, 481, 494, 493, 505, 506, 507, 508, 509, + 510, 511, 524, 533, 537, 545, 549, 553, 574, 601, + 622, 651, 687, 730, 742, 763, 799, 833, 855, 891, + 913, 924, 938, 937, 952, 956, 961, 965, 976, 980, + 984, 988, 992, 996, 1000, 1004, 1008, 1012, 1016, 1020, + 1025, 1029, 1036, 1037, 1041, 1042, 1043, 1048, 1047, 1063, + 1073, 1072, 1089, 1097, 1105, 1116, 1132, 1131, 1146, 1161, + 1170, 1185, 1184, 1209, 1208, 1236, 1235, 1266, 1265, 1284, + 1283, 1304, 1303, 1335, 1334, 1360, 1373, 1377, 1381, 1385, + 1398, 1402, 1406, 1410, 1414, 1419, 1424, 1428, 1432, 1436, + 1443, 1447, 1451, 1455, 1459, 1463, 1467, 1471, 1475, 1479, + 1483, 1487, 1491, 1495, 1499, 1503, 1507, 1511, 1515, 1519, + 1523, 1527, 1531, 1535, 1539, 1543, 1547, 1551, 1555, 1559, + 1563, 1567, 1571, 1575, 1579, 1583, 1587, 1591, 1595, 1602, + 1603, 1604, 1608, 1610, 1609, 1617, 1618, 1622, 1623, 1627, + 1633, 1642, 1643, 1647, 1651, 1655, 1659, 1665, 1671, 1677, + 1684, 1689, 1698, 1702, 1707, 1715, 1727, 1731, 1745, 1760, + 1765, 1770, 1775, 1780, 1785, 1790, 1795, 1801, 1800, 1831, + 1841, 1851, 1855, 1859, 1868, 1872, 1880, 1884, 1889, 1893, + 1898, 1906, 1911, 1919, 1923, 1928, 1932, 1937, 1945, 1950, + 1958, 1962, 1969, 1973, 1980, 1984, 1988, 1992, 1996, 2003, + 2007, 2011, 2015, 2019, 2023, 2030, 2031, 2032, 2036, 2039, + 2040, 2041, 2045, 2050, 2056, 2062, 2067, 2073, 2079, 2083, + 2094, 2098, 2108, 2112, 2116, 2121, 2126, 2131, 2136, 2141, + 2146, 2154, 2158, 2162, 2167, 2172, 2177, 2182, 2187, 2192, + 2197, 2203, 2211, 2216, 2221, 2226, 2231, 2236, 2241, 2246, + 2251, 2256, 2262, 2270, 2274, 2279, 2284, 2289, 2294, 2299, + 2304, 2309, 2314, 2322, 2326, 2331, 2336, 2341, 2346, 2351, + 2356, 2361, 2366, 2371, 2377, 2384, 2391, 2401, 2405, 2413, + 2417, 2421, 2425, 2429, 2445, 2461, 2470, 2474, 2484, 2491, + 2502, 2506, 2514, 2518, 2522, 2526, 2530, 2546, 2562, 2580, + 2589, 2593, 2603, 2610, 2614, 2622, 2626, 2642, 2658, 2667, + 2677, 2684, 2688, 2696, 2700, 2705, 2709, 2717, 2718, 2719, + 2720, 2725, 2724, 2749, 2748, 2778, 2779, 2786, 2787, 2791, + 2792, 2796, 2800, 2804, 2808, 2812, 2816, 2820, 2824, 2828, + 2832, 2839, 2847, 2851, 2855, 2860, 2868, 2872, 2879, 2880, + 2885, 2892, 2893, 2898, 2906, 2910, 2914, 2921, 2925, 2929, + 2937, 2936, 2959, 2958, 2981, 2982, 2986, 2992, 2999, 3008, + 3009, 3010, 3014, 3018, 3022, 3026, 3030, 3034, 3039, 3044, + 3049, 3054, 3058, 3063, 3072, 3077, 3085, 3089, 3093, 3101, + 3111, 3111, 3121, 3122, 3126, 3127, 3128, 3129, 3130, 3131, + 3132, 3133, 3134, 3135, 3136, 3137, 3137, 3137, 3138, 3138, + 3138, 3138, 3139, 3139, 3139, 3139, 3139, 3140, 3140, 3140, + 3141, 3141, 3141, 3141, 3141, 3142, 3142, 3142, 3142, 3142, + 3143, 3143, 3144, 3144, 3144, 3144, 3144, 3145, 3145, 3145, + 3145, 3145, 3146, 3146, 3146, 3146, 3147, 3147, 3147, 3147, + 3147, 3148, 3148, 3148, 3148, 3148, 3149, 3149, 3149, 3149, + 3149, 3149, 3150, 3150, 3150, 3150, 3150, 3151, 3151, 3151, + 3151, 3152, 3152, 3152, 3152, 3153, 3153, 3153, 3153, 3153, + 3154, 3154, 3154, 3154, 3155, 3155, 3155, 3155, 3155, 3156, + 3156, 3156, 3156, 3157, 3157, 3157, 3157, 3157, 3158, 3158, + 3161, 3161, 3161, 3161, 3161, 3161, 3161, 3161, 3161, 3161, + 3161, 3162, 3162, 3162, 3162, 3162, 3162, 3162, 3162, 3162, + 3162, 3163, 3163, 3167, 3171, 3178, 3182, 3189, 3193, 3200, + 3204, 3208, 3212, 3216, 3220, 3224, 3228, 3240, 3244, 3248, + 3252, 3256, 3260, 3264, 3268, 3272, 3276, 3280, 3284, 3288, + 3292, 3296, 3300, 3304, 3308, 3312, 3316, 3320, 3324, 3328, + 3332, 3336, 3340, 3344, 3348, 3352, 3356, 3360, 3368, 3372, + 3376, 3380, 3384, 3388, 3392, 3402, 3412, 3418, 3424, 3430, + 3436, 3442, 3448, 3455, 3462, 3469, 3476, 3482, 3488, 3492, + 3504, 3508, 3512, 3516, 3520, 3531, 3542, 3546, 3550, 3554, + 3558, 3562, 3566, 3570, 3574, 3578, 3582, 3586, 3590, 3594, + 3598, 3602, 3606, 3610, 3614, 3618, 3622, 3626, 3630, 3634, + 3638, 3642, 3646, 3650, 3654, 3658, 3662, 3669, 3673, 3677, + 3681, 3685, 3689, 3693, 3697, 3701, 3707, 3713, 3717, 3723, + 3730, 3734, 3738, 3742, 3746, 3750, 3754, 3758, 3762, 3766, + 3770, 3774, 3778, 3782, 3786, 3790, 3794, 3808, 3812, 3816, + 3820, 3824, 3828, 3832, 3836, 3848, 3852, 3856, 3860, 3864, + 3875, 3886, 3890, 3894, 3898, 3902, 3906, 3910, 3914, 3918, + 3922, 3926, 3930, 3934, 3938, 3942, 3946, 3950, 3954, 3958, + 3962, 3966, 3970, 3974, 3978, 3982, 3986, 3990, 3994, 3998, + 4002, 4009, 4013, 4017, 4021, 4025, 4029, 4033, 4037, 4041, + 4047, 4053, 4061, 4065, 4069, 4073, 4080, 4090, 4096, 4102, + 4112, 4124, 4132, 4136, 4166, 4170, 4174, 4178, 4182, 4186, + 4192, 4196, 4200, 4204, 4208, 4219, 4223, 4227, 4231, 4239, + 4243, 4247, 4253, 4264 }; #endif @@ -1011,18 +1023,19 @@ static const char *const yytname[] = "UNARY_PLUS", "UNARY_STAR", "UNARY_REF", "POINTSAT", "SCOPE", "PLUSPLUS", "MINUSMINUS", "TIMESEQUAL", "DIVIDEEQUAL", "MODEQUAL", "PLUSEQUAL", "MINUSEQUAL", "OREQUAL", "ANDEQUAL", "XOREQUAL", "LSHIFTEQUAL", - "RSHIFTEQUAL", "KW_ALIGNAS", "KW_ALIGNOF", "KW_AUTO", "KW_BEGIN_PUBLISH", - "KW_BLOCKING", "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_END_PUBLISH", "KW_ENUM", "KW_EXTENSION", "KW_EXTERN", - "KW_EXPLICIT", "KW_PUBLISHED", "KW_FALSE", "KW_FINAL", "KW_FLOAT", - "KW_FRIEND", "KW_FOR", "KW_GOTO", "KW_HAS_VIRTUAL_DESTRUCTOR", "KW_IF", - "KW_INLINE", "KW_INT", "KW_IS_ABSTRACT", "KW_IS_BASE_OF", "KW_IS_CLASS", - "KW_IS_CONSTRUCTIBLE", "KW_IS_CONVERTIBLE_TO", "KW_IS_DESTRUCTIBLE", - "KW_IS_EMPTY", "KW_IS_ENUM", "KW_IS_FINAL", "KW_IS_FUNDAMENTAL", - "KW_IS_POD", "KW_IS_POLYMORPHIC", "KW_IS_STANDARD_LAYOUT", - "KW_IS_TRIVIAL", "KW_IS_UNION", "KW_LONG", "KW_MAKE_MAP_PROPERTY", + "RSHIFTEQUAL", "ATTR_LEFT", "ATTR_RIGHT", "KW_ALIGNAS", "KW_ALIGNOF", + "KW_AUTO", "KW_BEGIN_PUBLISH", "KW_BLOCKING", "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_END_PUBLISH", + "KW_ENUM", "KW_EXTENSION", "KW_EXTERN", "KW_EXPLICIT", "KW_PUBLISHED", + "KW_FALSE", "KW_FINAL", "KW_FLOAT", "KW_FRIEND", "KW_FOR", "KW_GOTO", + "KW_HAS_VIRTUAL_DESTRUCTOR", "KW_IF", "KW_INLINE", "KW_INT", + "KW_IS_ABSTRACT", "KW_IS_BASE_OF", "KW_IS_CLASS", "KW_IS_CONSTRUCTIBLE", + "KW_IS_CONVERTIBLE_TO", "KW_IS_DESTRUCTIBLE", "KW_IS_EMPTY", + "KW_IS_ENUM", "KW_IS_FINAL", "KW_IS_FUNDAMENTAL", "KW_IS_POD", + "KW_IS_POLYMORPHIC", "KW_IS_STANDARD_LAYOUT", "KW_IS_TRIVIAL", + "KW_IS_UNION", "KW_LONG", "KW_MAKE_MAP_KEYS_SEQ", "KW_MAKE_MAP_PROPERTY", "KW_MAKE_PROPERTY", "KW_MAKE_PROPERTY2", "KW_MAKE_SEQ", "KW_MAKE_SEQ_PROPERTY", "KW_MUTABLE", "KW_NAMESPACE", "KW_NEW", "KW_NOEXCEPT", "KW_NULLPTR", "KW_OPERATOR", "KW_OVERRIDE", "KW_PRIVATE", @@ -1035,21 +1048,22 @@ static const char *const yytname[] = "KW_WCHAR_T", "KW_WHILE", "START_CPP", "START_CONST_EXPR", "START_TYPE", "'{'", "','", "';'", "':'", "'='", "'?'", "'|'", "'^'", "'&'", "'<'", "'>'", "'+'", "'-'", "'*'", "'/'", "'%'", "'~'", "'.'", "'('", "'['", - "')'", "'}'", "']'", "'!'", "$accept", "grammar", "cpp", + "')'", "'}'", "'!'", "']'", "$accept", "grammar", "cpp", "constructor_inits", "constructor_init", "extern_c", "$@1", "declaration", "friend_declaration", "$@2", "storage_class", "attribute_specifiers", "attribute_specifier", "type_like_declaration", - "$@3", "multiple_instance_identifiers", "typedef_declaration", "$@4", - "typedef_instance_identifiers", "constructor_prototype", "$@5", "$@6", - "function_prototype", "$@7", "$@8", "$@9", "$@10", "$@11", + "$@3", "$@4", "multiple_instance_identifiers", "typedef_declaration", + "$@5", "typedef_instance_identifiers", "constructor_prototype", "$@6", + "$@7", "function_prototype", "$@8", "$@9", "$@10", "$@11", "$@12", "function_post", "function_operator", "more_template_declaration", - "template_declaration", "$@12", "template_formal_parameters", + "template_declaration", "$@13", "template_formal_parameters", "template_nonempty_formal_parameters", "typename_keyword", "template_formal_parameter", "template_formal_parameter_type", - "instance_identifier", "$@13", + "instance_identifier", "$@14", "instance_identifier_and_maybe_trailing_return_type", - "maybe_trailing_return_type", "function_parameter_list", - "function_parameters", "formal_parameter_list", "formal_parameters", + "maybe_trailing_return_type", "maybe_comma_identifier", + "function_parameter_list", "function_parameters", + "formal_parameter_list", "formal_parameters", "template_parameter_maybe_initialize", "maybe_initialize", "maybe_initialize_or_constructor_body", "maybe_initialize_or_function_body", "structure_init", @@ -1057,13 +1071,13 @@ static const char *const yytname[] = "not_paren_formal_parameter_identifier", "formal_parameter_identifier", "parameter_pack_identifier", "not_paren_empty_instance_identifier", "empty_instance_identifier", "type", "type_pack", "type_decl", - "predefined_type", "var_type_decl", "full_type", "anonymous_struct", - "$@14", "named_struct", "$@15", "maybe_final", "maybe_class_derivation", - "class_derivation", "base_specification", "enum", "enum_decl", - "enum_element_type", "enum_body_trailing_comma", "enum_body", - "enum_keyword", "struct_keyword", "namespace_declaration", "$@16", - "$@17", "using_declaration", "simple_type", "simple_int_type", - "simple_float_type", "simple_void_type", "code", "$@18", "code_block", + "predefined_type", "var_type_decl", "full_type", "struct_attributes", + "anonymous_struct", "$@15", "named_struct", "$@16", "maybe_final", + "maybe_class_derivation", "class_derivation", "base_specification", + "enum", "enum_decl", "enum_element_type", "enum_body_trailing_comma", + "enum_body", "enum_keyword", "struct_keyword", "namespace_declaration", + "$@17", "$@18", "using_declaration", "simple_type", "simple_int_type", + "simple_float_type", "simple_void_type", "code", "$@19", "code_block", "element", "optional_const_expr", "optional_const_expr_comma", "const_expr_comma", "no_angle_bracket_const_expr", "const_expr", "const_operand", "formal_const_expr", "formal_const_operand", @@ -1091,18 +1105,19 @@ static const yytype_uint16 yytoknum[] = 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, - 395, 396, 397, 398, 123, 44, 59, 58, 61, 63, - 124, 94, 38, 60, 62, 43, 45, 42, 47, 37, - 126, 46, 40, 91, 41, 125, 93, 33 + 395, 396, 397, 398, 399, 400, 401, 123, 44, 59, + 58, 61, 63, 124, 94, 38, 60, 62, 43, 45, + 42, 47, 37, 126, 46, 40, 91, 41, 125, 33, + 93 }; # endif -#define YYPACT_NINF -868 +#define YYPACT_NINF -926 #define yypact_value_is_default(Yystate) \ - (!!((Yystate) == (-868))) + (!!((Yystate) == (-926))) -#define YYTABLE_NINF -729 +#define YYTABLE_NINF -759 #define yytable_value_is_error(Yytable_value) \ 0 @@ -1111,155 +1126,163 @@ static const yytype_uint16 yytoknum[] = STATE-NUM. */ static const yytype_int16 yypact[] = { - 347, -868, 2997, 4826, 28, 3474, -868, -868, -868, -868, - -868, -868, -868, -868, -138, -102, -81, -45, -31, -27, - 9, 23, 52, -868, -868, 66, 89, 100, 114, 117, - 123, 131, 134, 144, 172, 199, 210, 213, 239, 243, - 252, 254, 266, 274, 5078, -868, -868, 60, 292, 293, - 7, 164, -868, 304, 305, 308, 2997, 2997, 2997, 2997, - 2997, 2040, 1052, 2997, 2753, -868, 185, -868, -868, -868, - -868, -868, -868, -868, -868, 4939, 310, -868, 27, -868, - -868, 1717, 4467, 4467, -868, 2604, 311, -868, 4467, -868, - -868, 16, 16, -868, -868, -868, -868, 334, 276, 464, - -868, -868, -868, -868, -868, -868, 5981, 5981, 5981, -868, - 5981, 3092, 5981, 152, -868, 5972, 317, 321, 329, 332, - 5981, 2208, 357, 358, 360, 5981, 5981, 336, 5884, 5981, - 5981, 2823, 5981, 5981, -868, 345, -868, -868, -868, 3672, - -868, -868, -868, -868, -868, 2997, 4826, 2997, 2997, 2997, - 2997, 4826, 2997, 4826, 2997, 4826, 2997, 4826, 4826, 4826, - 4826, 4826, 4826, 4826, 4826, 4826, 4826, 4826, 4826, 4826, - 4826, 4826, 2997, -868, -868, 353, 2604, 355, 370, 2604, - 2604, -868, 4826, 2997, 2997, 372, 4826, 4826, 2040, 2997, - 2997, 73, 73, 73, 73, 73, -138, -81, -45, -31, - -27, 23, 66, 100, 6011, 4837, 5211, 6044, 308, 373, - -93, 2753, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, 2604, 356, 374, -868, -868, 73, 2997, - 2997, 2997, 2997, 2997, 2997, 2997, 2997, 2997, 2997, 2997, - 2997, 2997, 2997, 2997, 2997, 2997, 2997, 2997, 2997, 2997, - 2337, 2997, -868, -868, 16, 16, 2997, -868, -868, -868, - 4467, -868, -868, -868, -868, 4826, -868, 379, 560, 354, - 16, 16, 354, 354, 4375, 377, -868, 378, -868, -868, - -868, -868, -868, -868, 3578, 400, -26, -10, 33, 130, - -868, -868, -868, 5981, -868, -868, -868, -868, 5981, -868, - -868, -868, 5858, 2295, -868, 2604, 2604, 2604, 2604, -868, - -868, 406, -868, -868, -868, -868, -868, 2997, 3763, -868, - 396, -868, 3803, -868, 2604, 83, -868, -868, 2604, 338, - 389, -868, 390, 4968, 2604, 391, -868, 2604, -6, 314, - 409, -868, -868, -868, -868, 2065, 464, -868, 394, 414, - -868, 397, 398, 399, 401, 403, 425, 404, 426, 419, - 428, 429, 430, 415, 432, -82, 452, 434, 436, 437, - 441, 442, 445, 446, 453, 455, 458, 466, 2997, -868, - 4826, 2997, -868, -868, 477, 468, 473, 2604, 475, 487, - 478, 3948, 481, 482, 2997, 2997, -868, -868, 486, 1052, - 1325, 4243, 3013, 3013, 1096, 1096, 579, 579, -868, 4038, - 1056, 4259, 3516, 1096, 1096, 233, 233, 73, 73, 73, - -868, -868, -79, 1807, -868, -868, 3966, 492, 354, 498, - 493, 2604, 354, 354, 354, 354, 354, 502, -868, 377, - -868, 377, -868, 502, 502, -868, 354, 4850, 4737, 354, - 354, 503, 31, -868, 821, 85, -868, 2997, 2604, 501, - -868, -868, -868, -868, 3578, -868, 595, -868, -868, -868, - 530, 531, 532, 534, 537, 5354, -868, 1433, 4571, 315, - 513, 314, -868, -868, 539, -868, 4826, 520, 546, 533, - -868, 4, 2997, 5164, 247, -868, 4826, -868, 541, -868, - -868, 2604, -22, -868, -868, -868, 2205, -868, -868, 371, - -868, 557, -868, -868, -868, -868, -868, -868, -868, 544, - -868, 545, -868, -868, -868, -868, 4826, -868, 4826, -868, - 4826, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, 3984, 529, 548, 547, -868, -868, 549, -868, - 552, -868, -868, -868, -868, 73, 2753, 4619, 3321, -868, - -868, 2997, -868, -868, -868, -868, 502, 354, -868, 502, - 502, 502, 502, 502, 2997, -68, 4939, 821, 85, -868, - 34, 75, -868, -868, 4708, 551, 821, 821, 821, 821, - 821, 821, -44, -868, -868, 553, 2604, 85, 85, 85, - 85, 85, 85, -18, 554, 2753, -868, -40, -868, 578, - 5426, -868, 581, -868, 5498, -868, 715, 716, 719, 720, - -868, -868, 405, 593, -868, -868, -868, -868, 3877, -868, - 590, 601, 2472, -868, 647, -868, -868, 4, -868, 371, - -868, 602, 583, 2604, 3129, 4619, 603, 371, 4619, 4002, - 247, 604, 247, 247, 247, 247, 247, 93, -868, -868, - 599, 5570, -868, 582, -868, 179, -868, -61, 613, 618, - 610, 620, 621, 2469, 2225, 624, 371, 371, 3605, 371, - 371, 371, 371, -868, 151, 351, -868, 3578, 2997, 2997, - 614, 619, 623, -868, -868, -868, 2997, -868, 2997, 631, - -868, 5054, -868, -868, -868, -868, -868, 622, -868, -868, - 638, -868, 2753, 502, 636, 635, 821, 85, -44, -18, - 641, 644, 3321, -868, -868, 821, 645, 645, 645, 645, - 645, 262, 2997, 2997, -868, -868, 85, -868, 648, 648, - 648, 648, 648, 275, 2997, -868, 649, -868, 2997, -868, - 5194, 672, 673, -868, -868, -868, -67, 676, 677, 681, - 5642, 15, -868, 647, 5944, 4571, 2604, 669, 666, 647, - 647, 647, 647, 647, 647, 39, 648, -868, 351, -868, - 5981, -868, -868, -868, -868, -868, -868, -868, 667, 675, - 682, -868, -868, 5078, -868, -868, 688, 10, 691, -868, - 683, 2997, 2997, 2997, 2997, 2040, 2997, 685, 36, -868, - -868, 4204, -868, 185, -868, 686, 371, 202, 689, -868, - 284, 247, 684, 684, 684, 684, 684, -868, 2997, -868, - -868, 4619, -868, 1873, -868, -868, 2604, 2997, 2997, -868, - -868, -868, -868, -868, 2469, 687, 701, 2753, -868, -868, - 371, 288, 288, 844, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - 692, 693, -868, -868, 288, 288, 288, 350, 854, -868, - 2997, -868, 2205, 718, -868, 578, -42, -12, -868, -868, - -868, 51, 58, -868, 5078, 16, 678, -868, -868, 4619, - -44, -18, -868, -868, 4619, 4619, -868, 645, 698, 2753, - 700, 648, 702, 703, 2489, 5245, 5264, 5278, 2604, 413, - -868, 2604, -868, 852, -868, 5194, 5714, 858, 725, 862, - 863, 864, -868, 729, 39, 648, -868, -868, -868, -868, - -868, 4826, 647, 3404, -868, -868, 735, -868, -868, 4826, - 4826, 4826, 723, 4826, 724, 4826, 4826, 2040, 73, 73, - 73, 73, 717, 82, 73, -868, -868, 3267, 2997, 2997, - 2997, 2997, 2997, 2997, 2997, 2997, 2997, 2997, 2997, 2997, - 2997, 2997, 2997, 2997, 2997, 2997, 2997, 2997, 2997, 2601, - 2997, -868, 367, 726, -868, -868, 684, 4619, 721, 727, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, 728, -868, - 731, 733, 736, -868, 2733, 288, -868, -868, -868, -868, - -868, 3129, 732, 2225, 371, -868, -868, -868, -868, 3321, - 16, -868, -868, 11, 738, 740, -868, -868, 741, 742, - 4619, -868, 4619, -868, -868, 4456, -868, 4456, -868, 4456, - -868, -868, 4456, 4456, 4456, -868, -868, 5786, -868, -868, - 116, -868, 137, 751, 138, -868, -868, -868, 730, 737, - 763, 768, 24, 769, 3404, 3404, 3404, 3404, 3404, 2040, - 3404, 3685, -868, 371, 762, 773, 774, 2997, 775, 2604, - 766, 778, 770, 4020, 2997, -868, -868, -868, 1325, 4243, - 3013, 3013, 1096, 1096, 579, 579, -868, 4186, 1056, 4259, - 3516, 1096, 1096, 233, 233, 73, 73, 73, -868, -868, - 139, 3287, 2124, 771, -868, 2124, 4619, 772, -868, -868, - 1596, -868, -868, 867, -868, 2469, 2753, 780, -868, -868, - 794, -868, 783, -868, -868, -868, -868, -868, 784, 786, - -868, -868, -868, -868, -868, -868, -868, 942, 808, 946, - 814, 817, 954, 819, 4826, 4826, 4826, 4826, 804, 4826, - 4826, 87, 87, 87, 87, 87, 803, 146, 87, 3404, - 3404, 3404, 3404, 3404, 3404, 3404, 3404, 3404, 3404, 3404, - 3404, 3404, 3404, 3404, 3404, 3404, 3404, 3404, 2865, 2997, - -868, -868, 812, 813, 806, 815, 818, -868, 822, -868, - -868, 73, 2997, -868, -868, -868, 4619, 824, -868, 2124, - -868, -868, 816, -868, -868, -868, 3321, 3321, 3321, -868, - -868, 825, -868, 840, -868, -868, 155, -868, 829, 832, - 841, 842, 2604, 830, 845, 3404, -868, 3201, 4290, 1525, - 1525, 1338, 1338, 1374, 1374, -868, 4222, 4441, 4457, 4482, - 975, 975, 87, 87, 87, -868, -868, 171, 3927, 2997, - 2997, -868, 2997, -868, 2997, 2753, 4619, 837, -868, 2124, - -868, 2124, 838, -868, -868, -868, 2124, 2124, 856, 994, - 995, 860, -868, 849, 850, 853, 855, -868, 861, 87, - 3404, -868, -868, 214, 215, 223, 229, 868, -868, 2124, - -868, -868, 870, 872, -868, 2997, 2997, 2997, -868, 2997, - 3685, -868, -868, -868, -868, -868, 3321, 871, 874, 244, - 255, 257, 258, 3321, -868, -868, -868, -868, -868, -868, - -868, -868 + 162, -926, 3677, 5743, 39, 4864, -926, -926, -926, -926, + -926, -926, -926, -926, -36, -99, -70, -37, -33, -11, + -69, -5, 24, -926, -926, 19, 33, 44, 57, 64, + 67, 78, 92, 96, 102, 144, 171, 174, 179, 185, + 191, 197, 211, 216, 6057, -926, -926, 108, 220, 222, + 20, 246, -926, 243, 245, 256, 3677, 3677, 3677, 3677, + 3677, 1793, 1173, 3677, 3886, -926, 158, -926, -926, -926, + -926, -926, -926, -926, -926, 5854, 264, -926, -23, -926, + -926, 2240, 2171, 2171, -926, 4505, 272, -926, 2171, -926, + -926, 305, 305, -926, -926, -926, -926, 152, 95, -926, + -926, -926, -926, -926, -926, 6164, 280, -926, 6986, 6986, + 6986, -926, 6986, 5233, 6986, 297, -926, 6971, 296, 298, + 307, 314, 319, 324, 6986, 1206, 336, 342, 345, 6986, + 6986, 331, 6778, 6986, 6986, 5065, 6986, 6986, -926, -926, + -926, -926, 2195, -926, -926, -926, -926, -926, 3677, 3677, + 5743, 3677, 3677, 3677, 3677, 5743, 3677, 5743, 3677, 5743, + 3677, 5743, 5743, 5743, 5743, 5743, 5743, 5743, 5743, 5743, + 5743, 5743, 5743, 5743, 5743, 5743, 3677, -926, -926, 332, + 4505, 337, 338, 4505, -926, -926, 5743, 3677, 3677, 339, + 5336, 5743, 1793, 3677, 3677, 88, 88, 88, 88, 88, + -36, -70, -37, -33, -11, -5, 19, 44, 6195, 5244, + 5695, 6068, 256, 334, -78, 3886, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, 4505, 4505, + -85, 358, -926, -926, 88, 3677, 3677, 3677, 3677, 3677, + 3677, 3677, 3677, 3677, 3677, 3677, 3677, 3677, 3677, 3677, + 3677, 3677, 3677, 3677, 3677, 3677, 2732, 3677, -926, -926, + 305, 305, 2867, -926, -926, -926, 2171, -926, -926, -926, + -926, 5743, -926, 353, 261, 150, 305, 305, 150, 150, + 4978, 349, -926, 350, -926, -926, -926, -926, -926, -926, + 1338, 368, 5160, -926, 4505, 472, 371, 356, 1936, 5250, + 6986, -926, -926, -926, -926, 6986, -926, -926, -926, -926, + 6871, 1355, -926, 4505, 4505, 4505, 4505, 4505, 4505, -926, + -926, 375, -926, -926, -926, -926, -926, 3677, -926, 4316, + -926, 367, -926, 4408, -926, 4505, 74, -926, -926, -63, + 364, -926, 365, 5941, 4505, 366, -926, 4505, -926, 239, + 383, -926, -926, -926, -926, 792, -926, -926, 372, 386, + -926, 369, 374, 382, 384, 390, 391, 393, 395, 396, + 407, 408, 410, 412, 415, 414, -75, 424, 416, 419, + 428, 429, 431, 432, 435, 436, 437, 438, 439, 3677, + -926, 5743, 3677, -926, 6759, 425, 443, 446, 4505, 450, + 451, 434, 452, 4177, 462, 463, 3677, 3677, -926, 524, + -926, 1294, 467, 3677, -926, -926, 1444, 4862, 1629, 1629, + 908, 908, 1658, 1658, -926, 2750, 1066, 4908, 5061, 908, + 908, 166, 166, 88, 88, 88, -926, -926, -74, 1595, + -926, -926, 468, 4477, 473, 150, 476, 479, 4505, 150, + 150, 150, 150, 150, 477, -926, 349, -926, 349, -926, + 477, 477, -926, 150, 6164, 5830, 5714, 150, 150, 481, + 53, -926, 672, 720, -926, 3677, 4505, 484, -926, -926, + -926, -926, 1338, -16, -14, -4, 6164, 489, 143, -926, + -926, -926, 494, 6986, 6164, 3812, -36, 482, 4535, -926, + -926, -926, 509, 511, 513, 515, 522, 525, 526, 6239, + -926, 3831, 5486, 249, 492, 239, -926, -926, 523, -926, + 5743, -926, 46, 3002, 6081, 916, -926, 5743, -926, 510, + 202, -926, -926, 2079, -926, -926, 413, -926, 531, 5160, + -926, -926, -926, -926, -926, -926, -926, 521, -926, 533, + -926, -926, -926, -926, 5743, -926, 5743, -926, 5743, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + 4556, 529, 536, -926, 535, -926, -926, 537, -926, -926, + 540, -926, -926, -926, -926, 88, 3886, -926, 4505, 358, + 5593, 2226, -926, 3886, 3677, -926, -926, -926, -926, -926, + 477, 150, -926, 477, 477, 477, 477, 477, 3677, -121, + 658, 5854, 672, 720, -926, -29, 10, -926, -926, 5622, + 555, 672, 672, 672, 672, 672, 672, -57, -926, -926, + 558, 4505, 720, 720, 720, 720, 720, 720, 99, 551, + 3886, -926, -52, -926, 582, 683, 1936, -926, 660, 6164, + -926, -926, -926, -926, -926, -926, -926, -926, 569, 584, + 589, -926, -926, 6057, -926, -926, 590, 35, 594, -926, + 573, 3677, 3677, 3677, 3677, 1793, 3677, 585, 54, -926, + -926, 4647, -926, 158, -926, 6986, 6986, 6312, -926, 741, + 743, 744, 748, 754, 755, -926, -926, 290, 617, -926, + -926, -926, -926, 5518, -926, 610, 620, 6753, -926, 495, + -926, -926, 46, -926, 413, -926, 621, 5593, 609, 413, + 5593, 612, 4574, 916, 616, 916, 916, 916, 916, 916, + 266, -926, -926, 614, 6385, -926, -926, -926, 4505, 248, + -926, 615, -926, 637, 638, 3137, 3024, 628, 413, 413, + 4248, 413, 413, 413, 413, -926, 8, 250, -926, 1338, + -926, 3677, 3677, 624, 626, 629, -926, -926, -926, 3677, + -926, 3677, -926, 630, -926, 5970, 6164, -926, -926, -926, + -926, -926, -926, 634, -926, -926, 653, -926, 3886, 477, + 635, 641, 5714, 672, 720, -57, 99, 642, 644, 2226, + -926, -926, 672, 645, 645, 645, 645, 645, 212, 3677, + -926, 720, -926, 649, 649, 649, 649, 649, 275, 3677, + -926, 650, -926, 3677, -926, 643, 4592, 6458, -926, 669, + -926, -926, 5743, 5743, 5743, 655, 5743, 659, 5365, 5743, + 1793, 88, 88, 88, 88, 656, -66, 88, -926, -926, + 3953, 3677, 3677, 3677, 3677, 3677, 3677, 3677, 3677, 3677, + 3677, 3677, 3677, 3677, 3677, 3677, 3677, 3677, 3677, 3677, + 3677, 3677, 3272, 3677, -926, -926, -926, -926, 678, -48, + 685, 686, 690, 691, 6531, 23, -926, 495, 6944, 5486, + 4505, 680, 681, 495, 495, 495, 495, 495, 495, 134, + 649, -926, 250, -926, 675, 413, 240, 676, -926, -926, + 288, 916, 679, 679, 679, 679, 679, -926, 3677, -926, + -926, 5593, 687, 321, -926, -25, 697, 698, -926, 2564, + -926, -926, -926, 3137, 692, 700, 3886, -926, -926, 413, + 311, 311, 839, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, 684, + 688, -926, -926, 311, 311, 311, 247, 849, -926, 3677, + -926, 2079, 706, -926, 582, -44, -40, -926, -926, -926, + -28, -22, -926, 6057, 305, 811, 471, -926, -926, 5593, + -926, -57, 99, -926, -926, 5593, 5593, -926, 645, 699, + 693, 649, 701, 695, 3294, -926, -926, -926, 5324, 723, + 714, -926, 704, 710, 716, 3677, 724, 4505, 717, 721, + 725, 722, 4629, 3677, -926, -926, -926, 1444, 4862, 1629, + 1629, 908, 908, 1658, 1658, -926, 3564, 1066, 4908, 5061, + 908, 908, 166, 166, 88, 88, 88, -926, -926, -6, + 1958, 6604, 867, 873, 738, 880, 733, -926, 892, 893, + 894, -926, 760, 134, 649, -926, -926, -926, -926, -926, + -926, 5743, 495, 4093, -926, -926, 764, -926, -926, 269, + 749, -926, -926, 679, 5593, 746, 751, -926, -926, 4505, + 3677, 3677, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, 771, -926, 3407, 311, -926, -926, + -926, -926, -926, 3812, 753, 3024, 413, -926, -926, -926, + -926, 2226, 305, -926, -926, -926, 15, 776, 752, -926, + -926, 778, 779, 5593, -926, 5593, -926, -926, 5705, 6090, + 6150, 4505, 343, -926, -926, 932, -926, 5324, -926, 783, + 784, 812, 813, 814, -926, -926, 815, -926, -926, 88, + 3677, -926, -926, -926, 816, 1, -926, 834, 835, 14, + 819, 59, -926, -926, -926, 822, 833, 837, 838, 38, + 840, 4093, 4093, 4093, 4093, 4093, 1793, 4093, 4798, -926, + 413, 4016, 825, -926, 4016, 5593, 828, -926, -926, 829, + -926, 830, 842, 2396, -926, 3137, 3886, 845, -926, -926, + 853, -926, 850, -926, -926, -926, -926, -926, 851, 852, + 5929, -926, 5929, -926, 5929, -926, -926, 5929, 5929, 5929, + -926, 6677, -926, 3677, 3677, -926, 3677, -926, 3677, 3886, + 855, 992, 858, 1006, -926, 1010, 874, 875, 1012, 876, + 5743, 5743, 5743, 5743, 861, 5457, 5743, 123, 123, 123, + 123, 123, 860, 101, 123, 4093, 4093, 4093, 4093, 4093, + 4093, 4093, 4093, 4093, 4093, 4093, 4093, 4093, 4093, 4093, + 4093, 4093, 4093, 4093, 3542, 3677, -926, -926, 5593, 863, + -926, 4016, -926, -926, 1013, -926, 864, -926, -926, -926, + 2226, 2226, 2226, -926, -926, -926, -926, -926, -926, -926, + -926, -926, 114, 147, 168, 175, -926, 885, -926, 868, + 888, -926, -926, 176, -926, 872, 883, 884, 886, 4505, + 877, 887, 895, 4093, -926, 4144, 5038, 1181, 1181, 1608, + 1608, 2116, 2116, -926, 4744, 5088, 5104, 1648, 405, 405, + 123, 123, 123, -926, -926, 181, 2329, 5593, 889, -926, + 4016, -926, -926, 4016, 882, -926, -926, -926, 4016, 4016, + -926, -926, -926, -926, 1043, 891, 906, 1049, 1050, 912, + -926, 897, 899, 900, 910, -926, -926, 913, 123, 4093, + -926, -926, 914, -926, 4016, -926, -926, 926, -926, 923, + 192, -926, 3677, 3677, 3677, -926, 3677, 4798, -926, 2226, + -926, 930, 1070, 934, 193, 199, 206, 213, 2226, -926, + -926, 925, -926, -926, -926, -926, -926, -926, 944, -926 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. @@ -1267,187 +1290,195 @@ static const yytype_int16 yypact[] = means the default is an error. */ static const yytype_uint16 yydefact[] = { - 0, 733, 0, 0, 0, 733, 5, 624, 620, 623, - 729, 730, 626, 627, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 622, 628, 0, 0, 0, 0, 0, + 0, 763, 0, 0, 0, 763, 5, 651, 647, 650, + 759, 760, 653, 654, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 649, 655, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 630, 629, 0, 0, 0, - 0, 0, 621, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 733, 0, 3, 563, 625, 282, 292, 291, - 370, 371, 373, 374, 355, 0, 0, 385, 352, 384, - 379, 376, 375, 378, 356, 0, 0, 357, 377, 387, - 372, 733, 733, 4, 284, 285, 286, 0, 341, 0, - 281, 367, 368, 369, 1, 21, 733, 733, 733, 22, - 733, 733, 733, 0, 38, 733, 0, 0, 0, 0, - 733, 0, 0, 0, 0, 733, 733, 0, 733, 733, - 733, 0, 733, 733, 6, 0, 17, 7, 19, 0, - 15, 16, 18, 65, 40, 733, 0, 733, 733, 733, - 733, 0, 733, 0, 733, 0, 733, 0, 0, 0, + 0, 0, 0, 0, 0, 657, 656, 0, 0, 0, + 0, 0, 648, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 763, 0, 3, 588, 652, 298, 309, 308, + 392, 393, 395, 396, 377, 0, 0, 407, 374, 406, + 401, 398, 397, 400, 378, 0, 0, 379, 399, 409, + 394, 763, 763, 4, 300, 301, 302, 0, 363, 763, + 297, 389, 390, 391, 1, 0, 0, 21, 763, 763, + 763, 22, 763, 763, 763, 0, 42, 763, 0, 0, + 0, 0, 0, 0, 763, 0, 0, 0, 0, 763, + 763, 0, 763, 763, 763, 0, 763, 763, 6, 17, + 7, 19, 0, 15, 16, 18, 73, 44, 763, 763, + 0, 763, 763, 763, 763, 0, 763, 0, 763, 0, + 763, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 763, 324, 330, 0, + 0, 0, 612, 0, 763, 323, 0, 763, 763, 0, + 0, 0, 0, 763, 763, 621, 619, 618, 620, 617, + 298, 392, 393, 395, 396, 407, 406, 401, 398, 397, + 400, 399, 394, 0, 0, 547, 744, 745, 746, 754, + 747, 750, 748, 752, 751, 749, 753, 733, 734, 0, + 0, 763, 739, 732, 616, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 733, 306, 312, 0, 0, 0, 585, 0, - 0, 305, 0, 733, 733, 0, 0, 0, 0, 733, - 733, 594, 592, 591, 593, 590, 282, 370, 371, 373, - 374, 385, 384, 379, 376, 375, 378, 377, 372, 0, - 0, 523, 715, 716, 717, 718, 721, 719, 723, 722, - 720, 724, 708, 707, 0, 705, 710, 704, 589, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 761, 762, + 763, 763, 0, 375, 376, 408, 398, 403, 402, 405, + 299, 0, 404, 0, 284, 763, 763, 763, 763, 763, + 763, 0, 333, 283, 335, 763, 755, 756, 757, 758, + 0, 365, 0, 337, 0, 0, 62, 64, 0, 763, + 763, 56, 45, 55, 57, 763, 46, 152, 51, 23, + 763, 0, 49, 0, 0, 0, 0, 0, 0, 54, + 763, 0, 26, 25, 24, 52, 48, 0, 156, 0, + 155, 0, 58, 0, 20, 0, 0, 50, 53, 332, + 311, 322, 0, 0, 0, 0, 13, 0, 70, 0, + 331, 67, 313, 314, 315, 363, 763, 310, 0, 546, + 545, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 731, 732, 733, 733, 0, 353, 354, 386, - 376, 381, 380, 383, 283, 0, 382, 0, 268, 733, - 733, 733, 733, 733, 733, 0, 315, 267, 317, 733, - 725, 726, 727, 728, 0, 343, 715, 716, 717, 719, - 319, 287, 321, 733, 52, 41, 51, 53, 733, 42, - 47, 23, 733, 0, 45, 0, 0, 0, 0, 50, - 733, 0, 26, 25, 24, 48, 44, 0, 0, 143, - 0, 54, 0, 20, 0, 0, 46, 49, 0, 314, - 294, 304, 0, 0, 0, 0, 13, 0, 0, 0, - 313, 60, 296, 297, 298, 341, 0, 293, 0, 522, - 521, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 307, - 0, 733, 309, 308, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 619, 709, 733, 733, - 603, 604, 605, 606, 607, 608, 611, 612, 618, 0, - 600, 601, 602, 609, 610, 598, 599, 595, 596, 597, - 617, 616, 0, 0, 316, 318, 0, 0, 733, 269, - 0, 258, 733, 733, 733, 733, 733, 274, 257, 0, - 270, 0, 271, 273, 272, 182, 733, 0, 0, 733, - 733, 0, 183, 186, 733, 0, 181, 733, 349, 0, - 346, 345, 340, 344, 0, 733, 733, 43, 39, 733, - 0, 0, 0, 0, 0, 733, 358, 0, 733, 314, - 294, 0, 313, 68, 0, 364, 0, 0, 56, 58, - 73, 75, 0, 0, 733, 295, 0, 733, 0, 388, - 199, 0, 0, 63, 388, 204, 0, 64, 62, 0, - 301, 343, 299, 569, 584, 575, 571, 573, 574, 0, - 581, 0, 580, 633, 570, 634, 0, 636, 0, 637, - 0, 640, 641, 642, 643, 644, 645, 646, 647, 648, - 649, 577, 0, 0, 0, 0, 576, 579, 0, 582, - 0, 587, 588, 578, 572, 564, 524, 733, 733, 88, - 706, 0, 615, 614, 289, 290, 275, 733, 259, 264, - 260, 261, 263, 262, 733, 0, 0, 733, 0, 223, - 0, 0, 733, 185, 0, 0, 733, 733, 733, 733, - 733, 733, 733, 236, 235, 0, 246, 0, 0, 0, - 0, 0, 0, 733, 0, 520, 519, 350, 339, 288, - 733, 324, 733, 323, 733, 360, 0, 0, 0, 0, - 362, 733, 0, 0, 160, 161, 162, 148, 0, 149, - 0, 145, 150, 146, 733, 159, 144, 0, 70, 0, - 366, 0, 0, 0, 733, 733, 0, 0, 733, 0, - 733, 0, 733, 733, 733, 733, 733, 0, 227, 226, - 0, 733, 77, 0, 733, 0, 8, 0, 0, 0, - 0, 0, 0, 733, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 61, 733, 733, 163, 0, 0, 0, - 0, 0, 0, 310, 311, 586, 0, 583, 0, 0, - 95, 0, 89, 92, 96, 91, 93, 0, 90, 94, - 0, 178, 613, 265, 0, 0, 733, 0, 733, 733, - 0, 0, 733, 184, 187, 733, 241, 237, 238, 240, - 239, 0, 0, 733, 217, 197, 0, 247, 252, 248, - 249, 251, 250, 0, 733, 220, 276, 347, 0, 320, - 0, 0, 326, 325, 363, 733, 0, 0, 0, 0, - 733, 0, 37, 733, 733, 0, 153, 151, 0, 733, - 733, 733, 733, 733, 733, 733, 157, 69, 733, 365, - 733, 57, 697, 693, 696, 699, 700, 189, 0, 0, - 0, 695, 701, 0, 703, 702, 0, 0, 0, 694, - 0, 0, 0, 0, 0, 0, 0, 0, 190, 224, - 193, 225, 650, 698, 188, 0, 0, 0, 0, 302, - 0, 733, 232, 228, 229, 231, 230, 83, 733, 303, - 14, 733, 200, 389, 390, 388, 0, 733, 733, 202, - 203, 205, 207, 208, 733, 0, 211, 213, 210, 206, - 0, 170, 166, 0, 110, 111, 112, 113, 114, 115, - 118, 119, 134, 122, 123, 124, 125, 126, 127, 128, - 129, 130, 131, 132, 133, 138, 137, 121, 120, 107, - 109, 108, 116, 117, 105, 106, 102, 103, 104, 101, - 0, 0, 100, 164, 167, 169, 168, 0, 0, 174, - 733, 176, 0, 0, 66, 300, 0, 0, 635, 638, - 639, 0, 0, 733, 0, 733, 0, 388, 266, 733, - 733, 733, 218, 221, 733, 733, 277, 242, 245, 198, - 0, 253, 256, 0, 351, 723, 0, 722, 0, 0, - 327, 0, 329, 711, 733, 0, 733, 0, 0, 0, - 0, 0, 359, 0, 733, 158, 139, 142, 140, 147, - 154, 0, 733, 0, 155, 195, 0, 71, 55, 0, - 0, 0, 659, 0, 0, 0, 0, 0, 667, 666, - 665, 664, 0, 0, 663, 59, 192, 0, 0, 0, + 325, 0, 763, 327, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 646, 737, + 740, 0, 763, 0, 735, 212, 630, 631, 632, 633, + 634, 635, 638, 639, 645, 0, 627, 628, 629, 636, + 637, 625, 626, 622, 623, 624, 644, 643, 0, 0, + 334, 336, 0, 0, 0, 763, 285, 0, 274, 763, + 763, 763, 763, 763, 290, 273, 0, 286, 0, 287, + 289, 288, 197, 763, 0, 0, 0, 763, 763, 0, + 198, 201, 763, 0, 196, 763, 371, 0, 368, 367, + 362, 366, 0, 744, 745, 746, 0, 0, 748, 341, + 303, 343, 0, 763, 0, 763, 311, 0, 0, 47, + 43, 763, 0, 0, 0, 0, 0, 0, 0, 763, + 380, 0, 763, 332, 311, 0, 331, 76, 0, 386, + 0, 81, 83, 0, 0, 763, 312, 0, 763, 0, + 0, 410, 219, 0, 72, 69, 0, 318, 365, 0, + 595, 594, 611, 601, 597, 599, 600, 0, 607, 0, + 606, 660, 596, 661, 0, 663, 0, 664, 0, 667, + 668, 669, 670, 671, 672, 673, 674, 675, 676, 603, + 0, 0, 0, 326, 0, 602, 605, 0, 609, 608, + 0, 614, 615, 604, 598, 589, 548, 738, 0, 763, + 763, 763, 96, 213, 0, 642, 641, 306, 305, 307, + 291, 763, 275, 280, 276, 277, 279, 278, 763, 0, + 0, 0, 763, 0, 238, 0, 0, 763, 200, 0, + 0, 763, 763, 763, 763, 763, 763, 763, 252, 251, + 0, 262, 0, 0, 0, 0, 0, 0, 763, 0, + 544, 543, 372, 361, 304, 0, 0, 763, 763, 0, + 59, 63, 725, 721, 724, 727, 728, 204, 0, 0, + 0, 723, 729, 0, 731, 730, 0, 0, 0, 722, + 0, 0, 0, 0, 0, 0, 0, 0, 205, 240, + 208, 241, 677, 726, 203, 763, 763, 763, 382, 0, + 0, 0, 0, 0, 0, 384, 763, 0, 0, 173, + 174, 175, 161, 0, 162, 0, 158, 163, 159, 763, + 172, 157, 0, 78, 0, 388, 0, 763, 0, 0, + 763, 0, 0, 763, 0, 763, 763, 763, 763, 763, + 0, 243, 242, 0, 763, 85, 410, 214, 0, 0, + 71, 0, 763, 0, 0, 763, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 68, 763, 763, 176, 0, + 316, 0, 0, 0, 0, 0, 328, 329, 613, 0, + 610, 0, 736, 0, 103, 0, 0, 97, 105, 100, + 104, 99, 101, 0, 98, 102, 0, 191, 640, 281, + 0, 0, 0, 763, 0, 763, 763, 0, 0, 763, + 199, 202, 763, 257, 253, 254, 256, 255, 0, 763, + 232, 0, 263, 268, 264, 265, 267, 266, 0, 763, + 235, 292, 369, 0, 338, 0, 0, 763, 346, 763, + 345, 66, 0, 0, 0, 687, 0, 0, 0, 0, + 0, 695, 694, 693, 692, 0, 0, 691, 65, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 733, 0, 0, 733, 85, 233, 733, 0, 0, - 392, 393, 397, 394, 402, 395, 396, 398, 399, 400, - 401, 403, 404, 405, 406, 407, 408, 409, 410, 411, - 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, - 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, - 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, - 442, 443, 444, 445, 446, 447, 448, 468, 449, 450, - 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, - 461, 462, 463, 464, 465, 466, 467, 469, 470, 471, - 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, - 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, - 492, 493, 494, 495, 733, 512, 513, 514, 505, 517, - 501, 502, 500, 507, 508, 496, 497, 498, 499, 506, - 504, 511, 509, 515, 510, 516, 503, 391, 0, 9, - 0, 0, 0, 209, 212, 171, 165, 136, 135, 173, - 177, 733, 0, 198, 0, 567, 566, 568, 565, 733, - 733, 179, 97, 0, 0, 0, 219, 222, 0, 0, - 733, 243, 733, 254, 348, 0, 332, 0, 331, 0, - 330, 713, 0, 0, 0, 712, 714, 733, 328, 361, - 0, 27, 0, 0, 0, 36, 156, 152, 0, 0, + 0, 0, 0, 0, 61, 60, 385, 763, 0, 0, + 763, 0, 0, 0, 763, 0, 41, 763, 763, 0, + 166, 164, 0, 763, 763, 763, 763, 763, 763, 763, + 170, 77, 763, 387, 0, 0, 0, 0, 320, 319, + 0, 763, 248, 244, 245, 247, 246, 91, 763, 321, + 14, 763, 0, 0, 8, 0, 0, 0, 220, 411, + 412, 222, 223, 763, 0, 226, 228, 225, 221, 0, + 183, 179, 0, 120, 121, 122, 123, 124, 125, 128, + 129, 144, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 148, 147, 131, 130, 117, 119, + 118, 126, 127, 115, 116, 112, 113, 114, 111, 0, + 0, 110, 177, 180, 182, 181, 0, 0, 187, 763, + 189, 0, 0, 74, 317, 0, 0, 662, 665, 666, + 0, 0, 763, 0, 763, 0, 0, 410, 282, 763, + 239, 763, 763, 233, 236, 763, 763, 293, 258, 261, + 0, 269, 272, 0, 373, 340, 339, 342, 0, 0, + 348, 347, 0, 0, 0, 763, 0, 0, 0, 0, + 0, 0, 0, 0, 720, 206, 209, 704, 705, 706, + 707, 708, 709, 712, 713, 719, 0, 701, 702, 703, + 710, 711, 699, 700, 696, 697, 698, 718, 717, 0, + 0, 763, 0, 0, 0, 0, 0, 194, 0, 0, + 0, 381, 0, 763, 171, 151, 149, 154, 150, 160, + 167, 0, 763, 0, 168, 210, 0, 79, 763, 0, + 0, 763, 93, 249, 763, 0, 0, 215, 410, 0, + 763, 763, 217, 218, 414, 415, 419, 416, 424, 417, + 418, 420, 421, 422, 423, 425, 426, 427, 428, 429, + 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, + 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, + 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, + 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, + 470, 471, 472, 492, 473, 474, 475, 476, 477, 478, + 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, + 489, 490, 491, 493, 494, 495, 496, 497, 498, 499, + 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, + 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, + 763, 536, 537, 538, 529, 541, 525, 526, 524, 531, + 532, 520, 521, 522, 523, 530, 528, 535, 533, 539, + 534, 527, 540, 413, 0, 224, 227, 184, 178, 146, + 145, 186, 190, 763, 0, 213, 0, 592, 591, 593, + 590, 763, 763, 192, 109, 106, 0, 0, 0, 234, + 237, 0, 0, 763, 259, 763, 270, 370, 752, 0, + 751, 0, 0, 349, 351, 741, 763, 0, 686, 0, + 0, 0, 0, 0, 684, 683, 0, 689, 690, 678, + 0, 716, 715, 383, 0, 0, 33, 195, 0, 0, + 0, 0, 40, 169, 165, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 211, 549, + 0, 82, 0, 87, 84, 763, 0, 250, 763, 0, + 9, 0, 0, 0, 229, 763, 230, 0, 185, 75, + 0, 193, 0, 107, 658, 763, 763, 763, 0, 0, + 0, 354, 0, 353, 0, 352, 742, 0, 0, 0, + 743, 763, 350, 0, 0, 688, 0, 685, 0, 714, + 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 564, 562, 561, + 563, 560, 0, 0, 559, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 196, 525, 0, 0, 0, 0, 733, 0, 0, - 0, 0, 0, 0, 0, 692, 191, 194, 676, 677, - 678, 679, 680, 681, 684, 685, 691, 0, 673, 674, - 675, 682, 683, 671, 672, 668, 669, 670, 690, 689, - 0, 0, 74, 0, 79, 76, 733, 0, 234, 733, - 0, 201, 12, 10, 214, 733, 215, 0, 172, 67, - 0, 180, 0, 98, 631, 733, 733, 733, 0, 0, - 338, 337, 336, 335, 334, 333, 322, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 539, 537, 536, 538, 535, 0, 0, 534, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 72, 658, 0, 0, 0, 0, 0, 656, 0, 661, - 662, 651, 0, 688, 687, 81, 733, 0, 733, 78, - 518, 11, 0, 733, 388, 99, 733, 733, 733, 733, - 733, 0, 28, 0, 33, 35, 0, 30, 0, 0, - 0, 0, 0, 0, 0, 0, 562, 548, 549, 550, - 551, 552, 553, 554, 555, 561, 0, 545, 546, 547, - 543, 544, 540, 541, 542, 560, 559, 0, 0, 0, - 0, 660, 0, 657, 0, 686, 733, 0, 733, 84, - 216, 175, 0, 280, 279, 278, 244, 255, 0, 0, - 0, 0, 533, 0, 0, 0, 0, 531, 0, 526, - 0, 558, 557, 0, 0, 0, 0, 0, 733, 86, - 632, 29, 0, 0, 31, 0, 0, 0, 532, 0, - 556, 654, 653, 655, 652, 733, 733, 0, 0, 0, - 0, 0, 0, 733, 80, 34, 32, 529, 528, 530, - 527, 82 + 0, 0, 0, 0, 0, 0, 80, 89, 763, 0, + 763, 86, 216, 12, 10, 542, 0, 763, 410, 108, + 763, 763, 763, 763, 763, 360, 359, 358, 357, 356, + 355, 344, 0, 0, 0, 0, 36, 763, 34, 0, + 0, 37, 39, 0, 29, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 587, 573, 574, 575, 576, 577, + 578, 579, 580, 586, 0, 570, 571, 572, 568, 569, + 565, 566, 567, 585, 584, 0, 0, 763, 0, 763, + 92, 11, 231, 188, 0, 296, 295, 294, 260, 271, + 681, 680, 682, 679, 0, 0, 0, 0, 0, 0, + 558, 0, 0, 0, 0, 556, 555, 0, 550, 0, + 583, 582, 0, 763, 94, 659, 195, 0, 28, 0, + 0, 30, 0, 0, 0, 557, 0, 581, 763, 763, + 35, 0, 0, 0, 0, 0, 0, 0, 763, 88, + 38, 0, 31, 553, 552, 554, 551, 90, 0, 32 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -868, -868, -245, -868, 178, -868, -868, 722, -868, -868, - -53, 375, -868, -124, -868, -142, -868, -868, -192, -868, - -868, -868, 734, -868, -868, -868, -868, -868, -493, -868, - -868, 294, -868, -868, -868, -868, 273, 412, -553, -868, - -628, -666, -100, -868, -101, -868, 103, -511, -868, -466, - -838, -868, -445, 72, -540, -147, -442, 756, -54, -41, - -30, 739, -682, 746, 884, -104, -868, -97, -868, -868, - -868, -868, 124, -92, -868, -444, -868, -868, -14, 55, - -868, -868, -868, -868, -7, 5, -868, -868, -502, -868, - -51, -868, -538, -140, -60, 188, 107, 104, -868, -868, - 694, -868, -867, 634, 652, -617, -1 + -926, -926, -313, -926, -13, -926, -926, 785, -128, -926, + 104, -430, 449, -126, -926, -926, -155, -926, -926, -228, + -926, -926, -926, 773, -926, -926, -926, -926, -926, -608, + -926, -926, -111, -926, -926, -926, -926, 218, 409, -636, + -926, -703, -728, -349, -576, -926, -142, -926, 30, -513, + -926, -510, -925, -926, -428, 265, -648, 312, -218, 93, + -82, -10, 22, -279, -640, 786, 1002, -159, -109, -926, + -94, -926, -926, -926, -926, -171, -87, -926, -455, -926, + -926, -18, -12, -926, -926, -926, -926, -32, -39, -926, + -926, -715, -926, -103, -926, -561, -136, -60, 302, 715, + -276, -926, -926, -926, 707, 13, 1196, -61, -492, -1 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 4, 5, 665, 666, 136, 497, 137, 138, 302, - 139, 487, 488, 140, 509, 683, 323, 639, 777, 338, - 645, 648, 339, 831, 1356, 1416, 1017, 1266, 558, 893, - 957, 141, 320, 630, 631, 632, 633, 634, 684, 1161, - 685, 710, 451, 452, 807, 808, 964, 734, 503, 507, - 845, 846, 453, 810, 657, 726, 738, 275, 276, 91, - 92, 340, 178, 341, 93, 94, 465, 95, 466, 612, - 751, 752, 940, 96, 97, 462, 458, 459, 98, 99, - 142, 621, 755, 143, 100, 101, 102, 103, 663, 664, - 833, 1147, 604, 348, 349, 1221, 211, 65, 811, 812, - 224, 225, 942, 943, 593, 66, 144 + -1, 4, 5, 923, 924, 139, 528, 140, 141, 310, + 142, 295, 296, 143, 536, 530, 755, 334, 714, 901, + 348, 717, 720, 349, 921, 1428, 1497, 1104, 1335, 591, + 982, 1087, 144, 331, 705, 706, 707, 708, 709, 756, + 1253, 757, 786, 1076, 469, 470, 677, 678, 1094, 414, + 740, 534, 934, 935, 471, 680, 730, 803, 813, 281, + 282, 91, 92, 350, 182, 351, 93, 292, 94, 647, + 95, 648, 829, 1029, 1030, 1283, 96, 97, 480, 476, + 477, 98, 99, 145, 696, 877, 146, 100, 101, 102, + 103, 741, 742, 929, 1243, 639, 358, 359, 1328, 215, + 65, 681, 682, 230, 231, 1284, 1285, 628, 66, 147 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If @@ -1455,1486 +1486,1681 @@ static const yytype_int16 yydefgoto[] = number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int16 yytable[] = { - 6, 210, 670, 579, 319, 761, 1152, 352, 353, 354, - 355, 778, 357, 603, 359, 638, 361, 646, 901, 915, - 609, 252, 185, 253, 145, 974, 1282, 813, 104, 267, - 179, 268, 377, 269, 254, 342, 714, 181, 278, 1308, - 668, 669, 343, 385, 386, 255, 583, 344, -725, 392, - 393, 986, 395, 294, 295, 296, 926, 297, 299, 300, - 146, 227, 304, 528, -726, 475, 395, 309, 1186, 1188, - 1190, 396, 315, 316, 270, 318, 321, 322, 947, 326, - 327, 147, 529, 837, 257, 562, 261, 262, 263, 722, - 277, 277, 745, 266, 817, 574, 715, 948, 595, 180, - 596, 838, 597, 395, 732, 747, 237, -727, 748, 64, - 820, 972, 822, 823, 824, 825, 826, 148, -725, 733, - 1327, -725, 1165, 851, 852, 345, 894, 895, 896, 897, - 732, 149, 347, 395, -726, 150, 719, -726, 499, 724, - 500, 501, 502, 598, 350, 744, 350, 350, 350, 350, - 258, 350, 1166, 350, 271, 350, 739, 740, 741, 742, - 743, 647, 151, 191, 192, 193, 194, 195, 272, 186, - 228, 350, 975, 273, 903, 1283, 584, -727, 274, 953, - -727, 987, 350, 350, 701, 152, 1309, 963, 350, 350, - 422, 252, 776, 253, 346, 930, 395, 574, 720, 809, - 424, 425, 733, 395, -728, 153, 933, 922, 923, 261, - 262, 263, 266, 182, 342, 1167, 440, 442, 342, 904, - 610, 343, 1168, 599, 614, 343, 344, 395, 154, 485, - 344, 486, 1170, 454, 249, 250, 251, 600, 574, 721, - 304, 544, 601, 905, 455, 467, 1235, 602, 1337, 1338, - 1339, 155, 661, 277, 277, 827, 828, 280, 281, 282, - 651, 1297, 156, 1012, 652, 261, 237, 966, 438, 277, - 277, 438, 438, 456, -728, 921, 157, -728, 460, 158, - 1298, 1016, 1299, 1302, 395, 159, 280, 281, 282, 463, - 1018, 395, 494, 160, 931, 391, 161, 1155, 898, 301, - 1430, 1300, 1303, 1353, 345, 653, 162, 592, 345, 6, - 1386, 347, 967, 899, 900, 347, 395, 187, 1290, 1431, - 1291, 955, 1292, 835, 836, 1293, 1294, 1295, 739, 740, - 741, 742, 743, 1148, 163, 1441, 400, 401, 402, 403, - 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, - 414, 415, 416, 417, 418, 419, 420, 283, 423, 395, - 395, 164, 1162, 426, 899, 900, 1013, 430, 395, 431, - 813, 432, 165, 346, 395, 166, 760, 346, 1461, 1462, - 350, 280, 281, 282, 675, 654, 283, 1463, 676, 395, - 246, 247, 248, 1464, 249, 250, 251, 559, 227, 655, - 395, 167, 395, 395, 656, 168, 577, 454, 1477, 1176, - 1177, 10, 433, 11, 169, 1174, 170, 578, 455, 1478, - 1169, 1479, 1480, 284, 477, 733, 928, 438, 171, 677, - 718, 438, 438, 438, 438, 438, 172, 1362, 744, 932, - 727, 728, 729, 730, 731, 438, 1015, 828, 438, 438, - 899, 900, 650, 594, 183, 184, 606, 699, 504, -87, - 505, -87, 506, -87, 6, 613, 188, 189, 6, 463, - 190, 635, 256, 265, 286, 287, 288, 636, 279, 305, - 678, 283, -87, 306, -87, 542, -87, 775, 1, 2, - 3, 307, 434, 659, 308, 504, 6, 505, 317, 902, - 490, 555, 556, 1280, 312, 313, 435, 314, 328, 679, - 946, 436, 899, 900, 1159, 378, 454, 380, 1262, 399, - 931, 1265, 398, 680, 1192, 1193, 1194, 455, 681, 899, - 900, 1263, 381, 682, 387, 716, 428, 394, 215, 216, - 457, -257, 809, 454, 813, 815, 717, 464, 818, 478, - 476, 491, 492, 496, 455, 508, 456, 711, 513, 395, - 526, 514, 515, 516, 605, 517, 438, 518, 520, 920, - 212, 213, 214, 606, 289, 218, 594, 219, 927, 519, - 521, 559, 220, 522, 221, 594, 594, 594, 594, 594, - 594, 735, 523, 524, 525, 778, 527, 530, 531, 649, - 532, 533, 735, 454, 454, 534, 535, 454, 290, 536, - 537, 753, 237, 674, 455, 455, 954, 538, 455, 539, - 6, 635, 540, 727, 728, 729, 730, 731, 906, 907, - 541, 545, 546, 594, 215, 216, 911, 547, 912, 549, - 956, 550, 551, 814, 456, 553, 554, 456, 557, 659, - 567, 659, 659, 659, 659, 659, 565, 280, 281, 282, - 768, -259, 596, 834, 769, 574, 608, 582, 712, 611, - 217, 218, 848, 219, 615, 637, 616, 617, 220, 618, - 221, 605, 619, 711, 735, 640, 642, 179, 212, 213, - 214, 643, 463, 694, 181, 644, 226, 1150, 1151, 1197, - 1423, 1424, 1425, 662, 687, 770, 688, 689, 725, 696, - 736, 318, 695, 697, 698, 594, 809, 735, 735, 264, - 746, 711, -342, -258, 594, 756, 757, 968, 750, 758, - 759, 1019, 606, 291, 244, 245, 246, 247, 248, 762, - 249, 250, 251, 606, 764, 983, 765, 832, 779, 780, - 285, 292, 215, 216, 6, 311, 180, 283, 635, 839, - 816, 821, 594, 829, 840, 325, 842, 843, 594, 594, - 594, 594, 594, 594, 965, 841, 1359, 735, 908, 179, - 847, 850, 917, 909, 916, 771, 181, 910, 217, 218, - 454, 219, 1366, 1367, 1368, 913, 220, 919, 221, 772, - 1474, 455, 918, 924, 773, 941, 925, 1481, 733, 774, - 379, 744, -266, 382, 383, 927, 944, 961, 945, 1175, - 659, 949, 950, 962, 1178, 1179, 951, 606, 970, 969, - 456, 280, 281, 282, 585, 971, 350, 350, 586, 929, - 605, 973, 1172, 848, 976, 977, 1154, 828, 180, 985, - 1011, 605, 1153, 1014, 1156, 934, 1157, 397, 1160, 1158, - 1180, 1171, 1422, 1164, 1182, 1419, 1181, 1196, 1200, 1183, - 1421, 1201, 1202, 1203, 1204, 1205, 1426, 1427, 454, 587, - 1223, 1234, 1361, 454, 454, 1227, 1229, 1268, 1264, 455, - 1305, 1269, 1304, 1271, 455, 455, 1272, 1273, 1278, 606, - 179, 1274, 429, 1284, 1285, 1286, 1287, 181, 978, 979, - 980, 981, 559, 984, 277, 1301, 1306, 1267, 456, 735, - 735, 1307, 1310, 456, 456, 1449, 1341, 1342, 1343, 1345, - 1347, 283, 1348, 1355, 1349, 605, 1358, 470, 1364, 471, - 472, 473, 474, 6, 1363, 209, 454, 1365, 1369, 1260, - 1370, 847, 1371, 965, 1372, 1466, 1373, 455, 484, 588, - 1374, 594, 489, 1375, 1376, 1377, 1382, 1385, 495, 180, - 1411, 498, 1473, 589, 1409, 1410, 454, 1412, 590, 510, - 512, 1420, 1413, 591, 1414, 1429, 1433, 455, 1418, 1428, - 941, 941, 941, 1432, 1437, 1434, 1435, 511, 292, 1438, - 941, 1448, 1451, 1450, 1452, 1453, 1454, 605, 1327, 1163, - 559, 1455, 1456, 559, 1149, 1457, 456, 1475, 781, 1458, - 1476, 548, 1279, 1459, 468, 437, 439, 441, 443, 444, - 351, 1340, 1465, 226, 1467, 356, 1468, 358, 959, 360, - 763, 362, 363, 364, 365, 366, 367, 368, 369, 370, - 371, 372, 373, 374, 375, 376, 481, 1206, 958, 1237, - 1277, 482, 212, 213, 214, 568, 384, 1222, 483, 1198, - 388, 389, 390, 1270, 231, 232, 233, 234, 235, 236, - 1288, 0, 1289, 0, 1233, 0, 0, 1344, 0, 237, - 0, 0, 607, 560, 0, 1238, 1239, 1240, 1241, 1242, - 1243, 1244, 1245, 1246, 1247, 1248, 1249, 1250, 1251, 1252, - 1253, 1254, 1255, 1256, 1257, 1258, 1281, 1261, 235, 236, - 454, 0, 0, 834, 0, 0, 215, 216, 0, 237, - 0, 455, 1334, 1335, 1336, 667, 1337, 1338, 1339, 454, - 0, 454, 0, 0, 0, 0, 658, 0, 0, 427, - 455, 0, 455, 0, 0, 0, 0, 0, 0, 1317, - 814, 686, 217, 218, 0, 219, 1357, 0, 711, 277, - 220, 0, 221, 0, 0, 0, 0, 0, 0, 456, - 0, 456, 0, 0, 566, 0, 0, 0, 569, 570, - 571, 572, 573, 0, 0, 0, 0, 0, 0, 0, - 222, 0, 575, 0, 223, 580, 581, 240, 241, 242, - 243, 244, 245, 246, 247, 248, 0, 249, 250, 251, - 0, 0, 0, 0, 0, 454, 350, 0, 0, 0, - 737, 0, 0, 0, 0, 0, 455, 0, 0, 0, - 941, 0, 941, 0, 941, 0, 0, 941, 941, 941, - 0, 244, 245, 246, 247, 248, 1417, 249, 250, 251, - 0, 1276, 0, 0, 543, 456, 767, 0, 559, 0, - 0, 0, 0, 0, 848, 0, 0, 489, 1407, 0, - 0, 0, 0, 0, 559, 559, 559, 0, 0, 0, - 0, 686, 0, 0, 0, 0, 0, 0, 0, 686, - 0, 0, 658, 0, 658, 658, 658, 658, 658, 0, - 0, 0, 0, 0, 0, 454, 1447, 0, 1222, 1222, - 1222, 1222, 1222, 713, 1222, 0, 455, 0, 686, 686, - 0, 686, 686, 686, 686, 0, 0, 0, 0, 0, - 0, 1351, 230, 231, 232, 233, 234, 235, 236, 1443, - 1444, 0, 1445, 0, 1446, 456, 0, 559, 237, 0, - 1325, 1326, 559, 0, 0, 711, 711, 711, 559, 559, - 641, 1327, 0, 0, 0, 454, 0, 0, 0, 0, - 660, 0, 847, 0, 0, 0, 455, 0, 0, 0, - 0, 0, 0, 0, 0, 1469, 1470, 1471, 0, 1472, - 960, 0, 1311, 1312, 1313, 1314, 1315, 1327, 1318, 0, - 690, 0, 691, 0, 692, 456, 0, 559, 0, 0, - 0, 0, 0, 1222, 1222, 1222, 1222, 1222, 1222, 1222, - 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, - 1222, 1222, 0, 0, 0, 0, 1408, 559, 0, 229, - 230, 231, 232, 233, 234, 235, 236, 0, 0, 1415, - 0, 0, 0, 0, 559, 711, 237, 0, 686, 0, - 667, 0, 711, 658, 0, 239, 240, 241, 242, 243, - 244, 245, 246, 247, 248, 0, 249, 250, 251, 1222, - 0, 0, 0, 1332, 1333, 1334, 1335, 1336, 0, 1337, - 1338, 1339, 686, 0, 0, 0, 0, 1387, 1388, 1389, - 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, - 1400, 1401, 1402, 1403, 1404, 1405, 0, 0, 0, 1332, - 1333, 1334, 1335, 1336, 0, 1337, 1338, 1339, 0, 0, - 0, 0, 0, 0, 1222, 1323, 1324, 1325, 1326, 0, - 1173, 0, 0, 0, 0, 0, 0, 0, 1327, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1191, 1439, 0, 1195, 0, 0, 622, 0, - 0, 0, 238, 239, 240, 241, 242, 243, 244, 245, - 246, 247, 248, 0, 249, 250, 251, 623, 0, 1020, - 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, - 0, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, - 1040, 1041, 0, 0, 0, 0, 0, 0, 1460, 1042, - 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, - 1053, 1054, 1055, 1056, 1057, 1058, 0, 0, 1059, 1060, - 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, - 1071, 1072, 1073, 0, 1074, 0, 1075, 1076, 1077, 1078, - 1079, 1080, 1081, 1082, 1083, 0, 1084, 1085, 1086, 0, - 1332, 1333, 1334, 1335, 1336, 0, 1337, 1338, 1339, 982, - 0, 0, 0, 0, 1087, 0, 0, 0, 0, 0, - 1088, 1089, 1090, 0, 1091, 1092, 1093, 1094, 1095, 1096, - 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, - 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, - 1117, 1118, 1119, 1120, 1121, 1122, 1123, 0, 0, 0, - 1124, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133, - 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, - 1144, 1360, 1145, 1146, 0, 0, 0, 0, 0, 70, - 0, 71, 72, 73, 0, 0, 0, 0, 0, 0, - 0, 259, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 260, 686, 0, 0, 0, - 0, 0, 0, 229, 230, 231, 232, 233, 234, 235, - 236, 0, 0, 0, 82, 83, 0, 0, 0, 0, - 237, 0, 0, 0, 0, 1207, 0, 0, 0, 0, - 0, 88, 0, 1224, 1225, 1226, 90, 1228, 0, 1230, - 1231, 1232, 0, 1346, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 686, 1020, 1021, 1022, 1023, - 1024, 1025, 1026, 1027, 1028, 1029, 1030, 0, 1031, 1032, - 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 0, - 0, 0, 0, 0, 0, 0, 1042, 1043, 1044, 1045, - 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, - 1056, 1057, 1058, 0, 0, 1059, 1060, 1061, 1062, 1063, - 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, - 0, 1074, 0, 1075, 1076, 1077, 1078, 1079, 1080, 1081, - 1082, 1083, 0, 1084, 1085, 1086, 238, 239, 240, 241, - 242, 243, 244, 245, 246, 247, 248, 0, 249, 250, - 251, 1087, 0, 563, 0, 0, 0, 1088, 1089, 1090, - 0, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, - 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, - 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, - 1120, 1121, 1122, 1123, 0, 0, 1436, 1124, 1125, 1126, - 1127, 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, 1136, - 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 0, 1145, - 1146, 0, 0, 7, 8, 9, 10, 0, 11, 12, - 13, 196, 68, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 286, 287, 288, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 15, 69, - 0, 0, 197, 0, 198, 199, 200, 74, 75, 0, - 20, 76, 0, 1316, 201, 22, 0, 0, 78, 0, - 0, 0, 0, 23, 24, 202, 0, 0, 0, 26, - 0, 0, 203, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 204, 215, - 216, 700, 0, 0, 0, 0, 44, 0, 45, 0, - 46, 0, 0, 0, 0, 47, 0, 205, 206, 50, - 0, 0, 51, 84, 0, 0, 0, 52, 0, 0, - 53, 85, 86, 87, 207, 289, 218, 89, 219, 208, - 0, 0, 702, 220, 0, 221, 0, 0, 1378, 1379, - 1380, 1381, 56, 1383, 1384, 57, 58, 59, 703, 0, - 60, 0, 61, 62, 0, 0, 0, 63, 7, 8, - 9, 10, 284, 11, 12, 13, 14, 0, 212, 213, - 214, 0, 0, 0, 0, 0, 0, 0, 704, 0, - 0, 705, 0, 0, 706, 0, 0, 0, 0, 0, - 0, 229, 230, 231, 232, 233, 234, 235, 236, 0, - 707, 0, 0, 15, 0, 0, 0, 16, 237, 17, - 18, 19, 708, 0, 0, 20, 0, 671, 672, 21, - 22, 0, 0, 0, 0, 0, 709, 0, 23, 24, - 25, 0, 215, 216, 26, 0, 0, 27, 28, 29, + 6, 214, 307, 683, 328, 713, 330, 509, 1244, 799, + 284, 902, 185, 361, 773, 363, 364, 365, 366, 497, + 368, 922, 370, 835, 372, 394, 183, 644, 990, 258, + 1352, 259, 184, 352, 610, 189, 263, 291, 614, 104, + 388, 775, 267, 268, 269, 608, 791, 790, 353, 272, + 837, 396, 397, 1394, 516, 354, 645, 404, 405, 718, + -755, 233, -756, 411, 651, 260, 150, 679, 618, 849, + 407, 1017, -757, 556, 407, 910, 772, 912, 913, 914, + 915, 916, 407, 906, -95, 412, -95, 155, -95, 408, + 283, 283, 557, 595, 413, 151, 822, 261, 293, 823, + 1073, 1044, 521, 264, 407, 286, 287, 288, 407, 809, + 357, 148, 940, 941, 810, 983, 984, 985, 986, 1074, + 407, 243, 1110, 1257, 355, 820, 407, 1258, 152, 149, + 356, -755, 153, -756, -755, 1004, -756, 608, 797, 1259, + 1111, 904, 407, -757, 907, 1260, -757, 360, 360, 1381, + 360, 360, 360, 360, 154, 360, 1413, 360, 987, 360, + 156, 1301, 1385, 447, 258, 448, 259, 449, 1382, 267, + 268, 269, 272, 988, 989, 360, 608, 798, 440, 441, + 157, 1386, 1353, 293, 158, 190, 360, 360, 687, 352, + 1082, 801, 360, 360, 457, 459, 438, 539, 159, 243, + 838, 619, 850, 1395, 353, 885, 719, 1388, 289, 160, + 450, 354, 301, 302, 303, 734, 304, 306, 308, -758, + 352, 312, 161, 519, 352, 520, 1389, 267, 319, 162, + 415, 491, 163, 325, 326, 353, 329, 332, 333, 353, + 337, 338, 354, 164, 992, 290, 354, 993, 1020, 407, + 413, 481, 255, 256, 257, 638, 572, 165, 1023, 283, + 283, 166, 407, 1103, 186, 819, 357, 167, 1474, 1099, + 472, 216, 217, 218, 455, 283, 283, 455, 455, 474, + 355, 1510, 1013, 1014, 478, 1093, 356, 1423, 1424, 1425, + -758, 451, 1267, -758, 538, 407, 10, 357, 11, 285, + 809, 357, 473, 1247, 994, 452, 1, 2, 3, 168, + 453, 355, 926, 927, 1511, 355, 407, 356, 273, 6, + 274, 356, 275, 407, 1518, 219, 252, 253, 254, 407, + 255, 256, 257, 525, 827, 1512, 169, 220, 221, 170, + 1552, 407, 1513, 1519, 171, 1106, 1005, 407, 1530, 736, + 172, 737, 738, 739, 407, 293, 173, 1105, 683, 1553, + 1563, 407, 174, 1262, 1010, 276, 1564, 825, 454, 456, + 458, 460, 461, 1565, 222, 223, 175, 224, 809, 1019, + 1566, 176, 225, 884, 226, 187, 531, 188, 532, 1096, + 533, 360, 1097, 1339, 1261, 796, -95, 531, -95, 532, + -95, 991, 191, 306, 312, 988, 989, 1100, 192, 499, + 193, 592, 988, 989, 1251, 814, 815, 816, 817, 818, + 1436, 194, 679, 286, 287, 288, 747, -274, 1254, 262, + 748, 917, 918, 1268, 988, 989, 1332, 271, 1413, 1271, + 1272, 819, 1022, 481, 455, 298, 277, 309, 455, 455, + 455, 455, 455, 1102, 918, 612, 472, 1367, 1368, 1369, + 278, 313, 455, 314, 731, 279, 455, 455, 1108, 1109, + 280, 629, 315, 749, 641, 758, 988, 989, 491, 316, + 710, 216, 217, 218, 317, 472, 322, 613, 473, 318, + 1331, 900, 323, 1334, 684, 324, 327, 389, 1269, 1270, + 6, 406, 391, 392, 398, 286, 287, 288, 892, 413, + 631, 711, 893, 445, 723, 475, -273, 473, 482, 494, + 493, 495, 510, 512, 732, 750, 289, 6, 1336, 522, + 523, 527, 535, 1350, 407, 219, 541, 352, 600, 587, + 540, 542, 603, 604, 605, 606, 607, 220, 221, 543, + 547, 544, 353, 549, 751, 894, 609, 545, 546, 354, + 615, 616, 548, 554, 1071, 1420, 1421, 1422, 752, 1423, + 1424, 1425, 558, 753, 550, 551, 1012, 552, 754, 553, + 472, 555, 574, 559, 222, 223, 560, 224, 415, 474, + 787, 580, 225, 1021, 226, 561, 562, 650, 563, 564, + 455, 793, 565, 566, 567, 568, 569, 641, 289, 472, + 575, 629, 473, 576, 357, 846, 592, 578, 579, 581, + 629, 629, 629, 629, 629, 629, 415, 902, 355, 583, + 584, 185, 590, 794, 356, 597, 895, 415, 1265, 601, + 599, 473, -275, 608, 649, 183, 6, 830, 617, 685, + 896, 184, 643, 758, 646, 897, 688, 712, 758, 689, + 898, 690, 731, 691, 731, 731, 731, 731, 731, 1084, + 692, 710, 715, 693, 694, 735, 814, 815, 816, 817, + 818, 759, 286, 287, 288, 620, 761, 758, 758, 621, + 758, 758, 758, 758, 789, 6, 767, 1358, 762, 1359, + 769, 995, 996, 768, 770, 771, 792, 472, 629, 1000, + 472, 1001, 1505, 1506, 1507, 802, 474, 64, 811, 474, + 481, 821, 732, 1504, 732, 732, 732, 732, 732, -364, + 1431, 824, 622, 630, 832, 631, 828, 632, 840, 473, + 833, 930, 473, 185, 937, 834, 836, 1440, 1441, 1442, + 839, 878, 848, 879, 880, 787, 415, 183, 881, 1429, + 1085, 683, 1086, 184, 882, 883, 886, 888, 889, 905, + 903, 195, 196, 197, 198, 199, 911, 1088, 234, 908, + 633, 919, 472, 928, 627, 289, 931, 932, 939, 874, + 875, 997, 629, 998, 415, 415, 999, 1002, 787, 1006, + 1007, 629, 483, 484, 485, 1008, 1009, 1015, 641, 1016, + 1025, 809, 1069, 623, 473, 819, -282, 1329, 641, 1028, + 1035, 1559, 1500, 1043, 1037, 679, 1072, 624, 1031, 1503, + 1567, 1091, 625, 1075, 1078, 1508, 1509, 626, 1079, 1080, + 472, 1092, 1098, 1101, 758, 918, 1112, 1113, 1246, 1248, + 731, 1249, 1498, 1252, 1256, 1107, 219, 710, 1250, 1264, + 1245, 634, 1287, 1274, 1273, 1276, 1275, 1289, 220, 221, + 1286, 1288, 473, 1290, 1021, 635, 6, 1304, 758, 1077, + 636, 1292, 1296, 1305, 1294, 637, 629, 1306, 1295, 1297, + 1307, 1534, 629, 629, 629, 629, 629, 629, 1095, 1291, + 1308, 415, 1309, 1310, 1311, 488, 223, 403, 224, 1312, + 732, 472, 1330, 225, 1333, 226, 1337, 641, 1338, 1355, + 474, 1532, 1263, 1348, 795, 1549, 286, 287, 288, 724, + 241, 242, 937, 725, 804, 805, 806, 807, 808, 1344, + 1558, 243, 290, 473, 1354, 1356, 1357, 1370, 1373, 1374, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, + 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, + 436, 185, 439, 1371, 1341, 1342, 726, 443, 1376, 1375, + 1378, 1377, 1383, 1380, 1384, 183, 1387, 1390, 641, 1391, + 1427, 184, 329, 1392, 1393, 1430, 1396, 1432, 1433, 472, + 1438, 592, 1457, 283, 1456, 472, 472, 1458, 474, 1434, + 415, 415, 1437, 498, 474, 474, 1459, 1439, 1443, 1444, + 1460, 899, 1463, 1461, 1462, 1464, 1469, 1473, 1501, 289, + 1499, 473, 1502, 1514, 360, 1516, 1517, 473, 473, 1520, + 1521, 1522, 511, 1523, 1525, 1329, 1329, 1329, 1329, 1329, + 1535, 1329, 1527, 1536, 1526, 1538, 1533, 727, 1537, 1539, + 1540, 1541, 1542, 213, 1543, 1544, 250, 251, 252, 253, + 254, 728, 255, 256, 257, 1550, 729, 1545, 1546, 1560, + 1561, 1548, 1095, 1562, 237, 238, 239, 240, 241, 242, + 1551, 629, 1568, 1569, 472, 500, 1340, 592, 831, 243, + 592, 1349, 1426, 474, 570, 1011, 515, 1089, 1515, 360, + 360, 1347, 887, 1313, 1018, 1046, 1372, 1343, 589, 517, + 0, 585, 586, 0, 0, 0, 473, 0, 593, 1329, + 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, + 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 0, 0, + 0, 0, 362, 0, 0, 0, 0, 367, 0, 369, + 0, 371, 0, 373, 374, 375, 376, 377, 378, 379, + 380, 381, 382, 383, 384, 385, 386, 387, 0, 0, + 1351, 0, 0, 216, 217, 218, 0, 0, 395, 0, + 640, 0, 400, 401, 402, 758, 0, 1329, 0, 1083, + 0, 1409, 1410, 1411, 1412, 0, 804, 805, 806, 807, + 808, 0, 0, 0, 1413, 0, 216, 217, 218, 930, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 0, + 255, 256, 257, 0, 0, 0, 0, 219, 722, 0, + 0, 0, 0, 472, 0, 0, 0, 0, 746, 220, + 221, 0, 684, 1329, 0, 0, 0, 0, 232, 0, + 787, 283, 0, 472, 0, 472, 1403, 0, 0, 758, + 219, 0, 474, 444, 474, 473, 0, 0, 0, 0, + 0, 270, 220, 221, 0, 6, 222, 223, 0, 224, + 0, 1361, 1363, 1365, 225, 473, 226, 473, 0, 0, + 0, 297, 0, 0, 216, 217, 218, 0, 0, 788, + 0, 0, 0, 1452, 1453, 0, 1454, 0, 1455, 222, + 223, 321, 224, 640, 227, 472, 0, 225, 228, 226, + 0, 336, 0, 229, 474, 0, 0, 592, 0, 1418, + 1419, 1420, 1421, 1422, 937, 1423, 1424, 1425, 0, 479, + 0, 0, 0, 320, 592, 592, 592, 473, 219, 0, + 0, 826, 0, 0, 1495, 216, 217, 218, 0, 0, + 220, 221, 0, 1445, 0, 1446, 390, 1447, 0, 393, + 1448, 1449, 1450, 0, 0, 0, 841, 842, 843, 844, + 0, 847, 70, 571, 71, 72, 73, 0, 0, 0, + 0, 0, 0, 0, 1018, 0, 0, 222, 223, 0, + 224, 0, 0, 0, 0, 225, 0, 226, 472, 219, + 0, 0, 80, 0, 409, 410, 0, 474, 0, 592, + 0, 220, 221, 0, 0, 0, 592, 0, 266, 787, + 787, 787, 592, 592, 0, 0, 0, 0, 0, 588, + 473, 0, 0, 0, 229, 0, 1077, 0, 82, 83, + 936, 236, 237, 238, 239, 240, 241, 242, 222, 223, + 446, 224, 0, 0, 0, 88, 225, 243, 226, 0, + 90, 0, 1554, 1555, 1556, 0, 1557, 472, 490, 0, + 492, 0, 0, 0, 0, 0, 474, 0, 592, 0, + 0, 0, 501, 0, 0, 0, 0, 502, 0, 503, + 504, 505, 506, 507, 508, 0, 0, 0, 0, 473, + 0, 0, 716, 0, 640, 0, 0, 0, 0, 733, + 0, 518, 592, 0, 640, 0, 0, 0, 1024, 0, + 526, 0, 0, 529, 0, 0, 0, 592, 787, 0, + 0, 537, 0, 0, 0, 1042, 763, 787, 764, 0, + 765, 0, 0, 0, 0, 0, 1047, 1048, 1049, 1050, + 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, + 1061, 1062, 1063, 1064, 1065, 1066, 1067, 0, 1070, 0, + 573, 0, 0, 0, 577, 0, 0, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 232, 255, 256, + 257, 235, 236, 237, 238, 239, 240, 241, 242, 0, + 0, 0, 0, 1397, 1398, 1399, 1400, 1401, 243, 1404, + 1411, 1412, 0, 640, 0, 0, 0, 0, 0, 0, + 0, 1413, 0, 0, 602, 0, 0, 0, 936, 239, + 240, 241, 242, 0, 0, 0, 0, 0, 0, 0, + 297, 0, 243, 0, 0, 0, 1407, 1408, 1409, 1410, + 1411, 1412, 642, 0, 0, 0, 0, 845, 0, 0, + 0, 1413, 297, 0, 0, 0, 0, 0, 0, 0, + 297, 243, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 640, 0, 1255, 1475, 1476, 1477, + 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1485, 1486, 1487, + 1488, 1489, 1490, 1491, 1492, 1493, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 760, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 1299, 255, + 256, 257, 0, 0, 0, 596, 1418, 1419, 1420, 1421, + 1422, 0, 1423, 1424, 1425, 1528, 0, 0, 0, 0, + 0, 0, 0, 0, 409, 248, 249, 250, 251, 252, + 253, 254, 0, 255, 256, 257, 7, 8, 9, 10, + 0, 11, 12, 13, 200, 68, 1418, 1419, 1420, 1421, + 1422, 0, 1423, 1424, 1425, 0, 250, 251, 252, 253, + 254, 0, 255, 256, 257, 0, 0, 812, 0, 0, + 0, 1547, 0, 0, 1032, 1033, 1034, 0, 1036, 0, + 1039, 1040, 1041, 15, 69, 297, 0, 201, 0, 202, + 203, 204, 74, 75, 0, 20, 76, 0, 0, 205, + 22, 0, 0, 78, 0, 0, 0, 0, 23, 24, + 206, 0, 0, 0, 26, 0, 0, 207, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 0, 212, 213, 214, 0, 0, - 0, 44, 0, 45, 0, 46, 0, 0, 217, 218, - 47, 219, 48, 49, 50, 0, 220, 51, 221, 0, - 0, 0, 52, 0, 0, 53, 0, 0, 0, 54, - 7, 8, 9, 10, 55, 11, 12, 13, 14, 673, - 0, 0, 310, 0, 0, 0, 0, 56, 0, 0, - 57, 58, 59, 0, 0, 60, 0, 61, 62, 215, - 216, 849, 63, 0, 238, 239, 240, 241, 242, 243, - 244, 245, 246, 247, 248, 15, 249, 250, 251, 16, - 0, 17, 18, 19, 0, 0, 0, 20, 0, 0, - 0, 21, 22, 0, 0, 217, 218, 0, 219, 0, - 23, 24, 25, 220, 0, 221, 26, 0, 0, 27, - 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, - 38, 39, 40, 41, 42, 43, 0, 0, 0, 469, - 0, 0, 0, 44, 0, 45, 0, 46, 0, 0, - 0, 0, 47, 0, 48, 49, 50, 0, 0, 51, - 0, 0, 0, 0, 52, 0, 0, 53, 0, 0, - 0, 54, 7, 8, 9, 10, 55, 11, 12, 13, - 14, 0, 212, 213, 214, 0, 0, 766, 0, 56, - 0, 0, 57, 58, 59, 0, 0, 60, 0, 61, - 62, 421, 0, 0, 63, 229, 230, 231, 232, 233, - 234, 235, 236, 0, 0, 0, 0, 15, 0, 0, - 0, 16, 237, 17, 18, 19, 0, 0, 0, 20, - 0, 0, 0, 21, 22, 0, 0, 0, 0, 0, - 0, 0, 23, 24, 25, 0, 215, 216, 26, 0, - 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, 0, 0, + 40, 41, 42, 208, 0, 0, 0, 0, 0, 0, + 0, 0, 44, 891, 45, 0, 46, 0, 0, 0, + 0, 47, 0, 209, 210, 50, 0, 0, 51, 84, + 0, 0, 0, 52, 0, 0, 53, 85, 86, 87, + 211, 0, 0, 89, 925, 212, 0, 0, 0, 7, + 8, 9, 10, 0, 11, 12, 13, 496, 56, 0, + 0, 57, 58, 59, 0, 0, 60, 0, 61, 62, + 0, 1346, 63, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 297, 0, 235, 236, 237, 238, 239, 240, + 241, 242, 0, 0, 0, 0, 15, 341, 0, 0, + 201, 243, 202, 203, 204, 74, 0, 0, 20, 342, + 0, 0, 205, 22, 0, 0, 78, 0, 0, 0, + 0, 23, 24, 206, 0, 1379, 0, 26, 0, 0, + 207, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 208, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 45, 0, 46, - 0, 0, 217, 218, 47, 219, 48, 49, 50, 0, - 220, 51, 221, 0, 0, 0, 52, 0, 0, 53, - 0, 0, 0, 54, 7, 8, 9, 10, 55, 11, - 12, 13, 14, 844, 212, 213, 214, 0, 0, 0, - 0, 56, 0, 0, 57, 58, 59, 0, 0, 60, - 0, 61, 62, 0, 1184, 0, 63, 0, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, 248, 15, - 249, 250, 251, 16, 0, 17, 18, 19, 0, 0, - 0, 20, 0, 0, 0, 21, 22, 0, 0, 0, - 0, 0, 0, 0, 23, 24, 25, 0, 215, 216, + 0, 0, 0, 0, 47, 0, 209, 210, 50, 0, + 936, 51, 84, 0, 0, 0, 52, 0, 0, 53, + 344, 345, 87, 211, 0, 0, 89, 0, 212, 0, + 0, 0, 7, 8, 9, 10, 1090, 11, 12, 13, + 14, 56, 0, 1314, 57, 58, 59, 0, 0, 60, + 0, 61, 62, 0, 0, 63, 0, 0, 0, 0, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 0, 255, 256, 257, 0, 0, 0, 1302, 15, + 0, 0, 0, 16, 0, 17, 18, 19, 0, 0, + 1496, 20, 0, 743, 744, 21, 22, 0, 0, 1413, + 0, 0, 0, 0, 23, 24, 25, 0, 0, 0, 26, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, - 0, 0, 0, 0, 0, 0, 0, 44, 0, 45, - 0, 46, 0, 0, 217, 218, 47, 219, 48, 49, - 50, 0, 220, 51, 221, 0, 0, 0, 52, 0, - 0, 53, 0, 0, 0, 54, 7, 8, 9, 10, - 55, 11, 12, 13, 14, 0, 0, 0, 0, 0, - 0, 0, 0, 56, 0, 0, 57, 58, 59, 0, - 0, 60, 0, 61, 62, 1259, 0, 0, 63, 229, - 230, 231, 232, 233, 234, 235, 236, 0, 0, 0, - 0, 15, 0, 0, 0, 16, 237, 17, 18, 19, - 0, 0, 0, 20, 0, 0, 0, 21, 22, 0, - 0, 0, 0, 0, 0, 0, 23, 24, 25, 0, - 0, 0, 26, 0, 0, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 0, 212, 213, 214, 0, 0, 0, 44, - 0, 45, 0, 46, 0, 0, 0, 0, 47, 0, - 48, 49, 50, 0, 0, 51, 0, 0, 0, 0, - 52, 0, 0, 53, 0, 0, 0, 54, 7, 8, - 9, 10, 55, 11, 12, 13, 14, 1275, 0, 0, - 0, 0, 0, 0, 0, 56, 0, 0, 57, 58, - 59, 0, 0, 60, 0, 61, 62, 215, 216, 0, - 63, 0, 238, 239, 240, 241, 242, 243, 244, 245, - 246, 247, 248, 15, 249, 250, 251, 16, 0, 17, - 18, 19, 0, 0, 0, 20, 0, 0, 324, 21, - 22, 0, 0, 217, 218, 0, 219, 0, 23, 24, - 25, 220, 0, 221, 26, 0, 0, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, + 45, 0, 46, 0, 0, 0, 0, 47, 0, 48, + 49, 50, 1266, 0, 51, 339, 340, 0, 0, 52, + 0, 0, 53, 0, 0, 0, 54, 0, 0, 0, + 0, 55, 0, 0, 0, 70, 745, 71, 72, 73, + 0, 0, 0, 1293, 56, 0, 0, 57, 58, 59, + 0, 0, 60, 774, 61, 62, 341, 0, 63, 70, + 0, 71, 72, 73, 74, 80, 0, 0, 342, 775, + 0, 77, 0, 0, 0, 78, 0, 0, 0, 0, + 0, 266, 79, 776, 1418, 1419, 1420, 1421, 1422, 80, + 1423, 1424, 1425, 0, 0, 0, 777, 778, 0, 0, + 0, 82, 83, 0, 70, 81, 71, 72, 73, 0, + 0, 0, 779, 0, 0, 925, 265, 343, 88, 0, + 0, 0, 0, 90, 0, 82, 83, 0, 0, 0, + 0, 84, 0, 0, 80, 0, 0, 0, 1402, 344, + 345, 87, 88, 780, 0, 89, 781, 90, 0, 782, + 266, 0, 346, 0, 0, 235, 236, 237, 238, 239, + 240, 241, 242, 0, 0, 783, 0, 0, 347, 0, + 82, 83, 243, 0, 0, 0, 0, 784, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 88, 0, 0, + 0, 785, 90, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1465, 1466, 1467, 1468, 0, 1471, 1472, 1114, + 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, + 0, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133, + 1134, 1135, 0, 0, 0, 0, 0, 0, 0, 1136, + 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1145, 1146, + 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 0, 0, + 1155, 1156, 1157, 1158, 1159, 1160, 1161, 1162, 1163, 1164, + 1165, 1166, 1167, 1168, 1169, 0, 1170, 0, 1171, 1172, + 1173, 1174, 1175, 1176, 1177, 1178, 1179, 1366, 1180, 1181, + 1182, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 0, 255, 256, 257, 1183, 0, 0, 1531, + 0, 0, 0, 1184, 1185, 1186, 0, 1187, 1188, 1189, + 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, + 1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, + 1210, 1211, 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, + 0, 0, 0, 1220, 1221, 1222, 1223, 1224, 1225, 1226, + 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236, + 1237, 1238, 1239, 1240, 1435, 1241, 1242, 1114, 1115, 1116, + 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 0, 1125, + 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, + 0, 0, 0, 0, 0, 0, 0, 1136, 1137, 1138, + 1139, 1140, 1141, 1142, 1143, 1144, 1145, 1146, 1147, 1148, + 1149, 1150, 1151, 1152, 1153, 1154, 0, 0, 1155, 1156, + 1157, 1158, 1159, 1160, 1161, 1162, 1163, 1164, 1165, 1166, + 1167, 1168, 1169, 0, 1170, 0, 1171, 1172, 1173, 1174, + 1175, 1176, 1177, 1178, 1179, 0, 1180, 1181, 1182, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1183, 1524, 0, 0, 0, 0, + 0, 1184, 1185, 1186, 0, 1187, 1188, 1189, 1190, 1191, + 1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, 1200, 1201, + 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, + 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 0, 0, + 0, 1220, 1221, 1222, 1223, 1224, 1225, 1226, 1227, 1228, + 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236, 1237, 1238, + 1239, 1240, 0, 1241, 1242, 7, 8, 9, 10, 0, + 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 235, 236, 237, 238, + 239, 240, 241, 242, 0, 0, 0, 0, 0, 0, + 0, 0, 15, 243, 0, 0, 16, 0, 17, 18, + 19, 0, 0, 0, 20, 0, 0, 0, 21, 22, + 0, 0, 0, 0, 0, 0, 0, 23, 24, 25, + 0, 0, 0, 26, 0, 0, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 45, 0, 46, 0, 0, 0, 0, 47, 0, 48, 49, 50, 0, 0, 51, 0, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, 54, 7, 8, 9, 10, 55, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, - 57, 58, 59, 0, 0, 60, 0, 61, 62, 1406, - 0, 0, 63, 233, 234, 235, 236, 0, 0, 0, - 0, 0, 0, 0, 0, 15, 237, 0, 0, 16, - 0, 17, 18, 19, 0, 0, 0, 20, 0, 0, - 0, 21, 22, 0, 0, 0, 0, 0, 0, 0, - 23, 24, 25, 0, 0, 0, 26, 0, 0, 27, - 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, - 38, 39, 40, 41, 42, 43, 0, 0, 298, 0, - 0, 0, 0, 44, 0, 45, 0, 46, 0, 0, - 0, 0, 47, 0, 48, 49, 50, 0, 0, 51, - 0, 0, 0, 0, 52, 0, 0, 53, 0, 0, - 0, 54, 782, 783, 784, 10, 55, 11, 785, 786, - 67, 68, 0, 106, 787, 0, 0, 0, 0, 56, - 107, 108, 57, 58, 59, 0, 0, 60, 0, 61, - 62, 110, 111, 112, 63, 0, 242, 243, 244, 245, - 246, 247, 248, 293, 249, 250, 251, 788, 69, 0, - 0, 70, 0, 71, 72, 73, 74, 447, 0, 789, - 76, 0, 0, 77, 790, 0, 120, 78, 0, 0, - 0, 0, 791, 792, 79, 0, 125, 0, 0, 0, - 0, 80, 126, 0, 0, 0, 0, 129, 1320, 1321, - 1322, 1323, 1324, 1325, 1326, 0, 0, 81, 132, 0, - 133, 0, 0, 0, 1327, 793, 0, 794, 0, 795, - 0, 0, 0, 448, 796, 0, 82, 83, 797, 0, - 0, 798, 84, 0, 0, 135, 799, 0, 0, 800, - 85, 86, 87, 88, 0, 0, 89, 0, 90, 0, - 782, 783, 784, 10, 0, 11, 785, 786, 67, 68, - 0, 801, 1236, 0, 802, 803, 0, 0, 0, 804, - 0, 805, 0, 0, 0, 0, 806, 0, 0, 0, - 0, 0, 0, 229, 230, 231, 232, 233, 234, 235, - 236, 0, 0, 0, 0, 788, 69, 0, 0, 70, - 237, 71, 72, 73, 74, 447, 0, 789, 76, 0, - 0, 77, 790, 0, 0, 78, 0, 0, 700, 0, - 791, 792, 79, 0, 0, 0, 0, 0, 0, 80, - 0, 1329, 1330, 1331, 701, 0, 1332, 1333, 1334, 1335, - 1336, 0, 1337, 1338, 1339, 81, 0, 0, 0, 0, - 0, 0, 0, 793, 0, 794, 0, 795, 0, 702, - 0, 448, 796, 0, 82, 83, 797, 0, 0, 798, - 84, 0, 0, 0, 799, 703, 0, 800, 85, 86, - 87, 88, 0, 0, 89, 0, 90, 7, 8, 9, - 10, 0, 11, 12, 13, 0, 0, 0, 0, 801, - 0, 0, 802, 803, 0, 704, 0, 804, 705, 805, - 0, 706, 0, 0, 806, 0, 238, 239, 240, 241, - 242, 243, 244, 245, 246, 247, 248, 707, 249, 250, - 251, 0, 1208, 1354, 0, 0, 0, 0, 0, 708, - 0, 0, 0, 0, 1209, 0, 0, 0, 0, 1210, - 0, 0, 0, 709, -2, 0, 0, 23, 24, 0, - 0, 0, 0, 26, 0, 0, 0, 28, 29, 30, + 57, 58, 59, 0, 0, 60, 0, 61, 62, 437, + 594, 63, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 0, 255, 256, 257, 15, 442, 0, + 0, 16, 0, 17, 18, 19, 0, 0, 0, 20, + 0, 0, 0, 21, 22, 0, 0, 0, 0, 0, + 0, 0, 23, 24, 25, 0, 0, 0, 26, 0, + 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 0, 0, + 0, 0, 0, 0, 0, 0, 44, 0, 45, 0, + 46, 0, 0, 0, 0, 47, 0, 48, 49, 50, + 0, 0, 51, 0, 0, 0, 0, 52, 0, 0, + 53, 0, 0, 0, 54, 7, 8, 9, 10, 55, + 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, + 0, 0, 56, 0, 0, 57, 58, 59, 0, 0, + 60, 0, 61, 62, 0, 0, 63, 0, 0, 0, + 235, 236, 237, 238, 239, 240, 241, 242, 0, 0, + 0, 0, 15, 721, 0, 0, 16, 243, 17, 18, + 19, 0, 0, 0, 20, 0, 0, 0, 21, 22, + 0, 0, 0, 0, 0, 0, 0, 23, 24, 25, + 0, 0, 0, 26, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 45, 0, 46, 0, 0, 0, 0, 1211, - 0, 0, 0, 1212, 105, 106, 1213, 0, 0, 0, - 0, 52, 107, 108, 231, 232, 233, 234, 235, 236, - 0, 109, 0, 110, 111, 112, 113, 0, 0, 237, - 114, 0, 0, 0, 0, 115, 1214, 0, 0, 1215, - 1216, 1217, 0, 0, 1218, 0, 1219, 62, 0, 0, - 0, 1220, 0, 0, 116, 117, 118, 119, 120, 121, - 0, 0, 0, 0, 0, 122, 123, 124, 125, 461, - 0, 0, 0, 0, 126, 127, 0, 0, 128, 129, - 0, 0, 0, 130, 0, 0, 0, 0, 0, 131, - 132, 853, 133, 0, 0, 0, 0, 0, 0, 0, - 134, 854, 855, 856, 857, 858, 859, 860, 861, 0, - 70, 0, 71, 72, 73, 0, 0, 135, 862, 0, - 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, - 873, 874, 0, 0, 0, 0, 0, 0, 0, 0, - 80, 0, 0, 0, 0, 0, 0, 0, 875, 242, - 243, 244, 245, 246, 247, 248, 260, 249, 250, 251, - 0, 0, 329, 330, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 82, 83, 0, 0, 0, - 0, 1319, 1320, 1321, 1322, 1323, 1324, 1325, 1326, 0, - 0, 876, 88, 0, 0, 0, 0, 90, 1327, 0, - 0, 331, 0, 0, 70, 0, 71, 72, 73, 74, - 0, 0, 0, 332, 0, 0, 77, 0, 0, 0, - 78, 0, 0, 0, 0, 0, 0, 79, 0, 0, - 877, 0, 0, 878, 80, 879, 880, 881, 882, 883, - 884, 885, 886, 887, 888, 889, 0, 890, 891, 0, - 81, 0, 892, 329, 330, 0, 0, 0, 0, 0, - 0, 333, 0, 0, 0, 0, 0, 0, 0, 82, - 83, 0, 0, 0, 0, 84, 0, 0, 0, 0, - 0, 0, 0, 334, 335, 87, 88, 0, 0, 89, - 0, 90, 331, 479, 480, 70, 336, 71, 72, 73, - 74, 0, 0, 0, 332, 0, 0, 77, 0, 0, - 0, 78, 337, 0, 1328, 1329, 1330, 1331, 79, 0, - 1332, 1333, 1334, 1335, 1336, 80, 1337, 1338, 1339, 0, - 0, 0, 331, 0, 0, 70, 0, 71, 72, 73, - 74, 81, 0, 0, 332, 0, 0, 77, 0, 0, - 0, 78, 333, 0, 0, 0, 0, 0, 79, 0, - 82, 83, 0, 0, 0, 80, 84, 624, 625, 626, - 0, 0, 0, 0, 334, 335, 87, 88, 0, 0, - 89, 81, 90, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 333, 0, 0, 0, 0, 0, 0, 0, - 82, 83, 0, 337, 0, 0, 84, 0, 0, 70, - 0, 71, 72, 73, 334, 335, 87, 88, 0, 0, - 89, 77, 90, 229, 230, 231, 232, 233, 234, 235, - 236, 0, 79, 0, 0, 0, 0, 0, 0, 80, - 237, 0, 0, 337, 229, 230, 231, 232, 233, 234, - 235, 236, 0, 0, 0, 81, 0, 0, 0, 0, - 0, 237, 229, 230, 231, 232, 233, 234, 235, 236, - 0, 0, 0, 0, 82, 83, 0, 0, 0, 237, - 229, 230, 231, 232, 233, 234, 235, 236, 0, 0, - 0, 88, 0, 0, 89, 0, 90, 237, 229, 230, - 231, 232, 233, 234, 235, 236, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 237, 229, 230, 231, 232, - 233, 234, 235, 236, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 237, 229, 230, 231, 232, 233, 234, - 235, 236, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 237, 0, 0, 0, 0, 238, 239, 240, 241, - 242, 243, 244, 245, 246, 247, 248, 0, 249, 250, - 251, 0, 0, 1442, 0, 0, 0, 238, 239, 240, - 241, 242, 243, 244, 245, 246, 247, 248, 0, 249, - 250, 251, 552, 0, 0, 238, 239, 240, 241, 242, - 243, 244, 245, 246, 247, 248, 0, 249, 250, 251, - 564, 0, 0, 238, 239, 240, 241, 242, 243, 244, - 245, 246, 247, 248, 0, 249, 250, 251, 693, 0, - 0, 238, 239, 240, 241, 242, 243, 244, 245, 246, - 247, 248, 0, 249, 250, 251, 819, 0, 0, 238, - 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, - 0, 249, 250, 251, 1350, 561, 0, 238, 239, 240, - 241, 242, 243, 244, 245, 246, 247, 248, 0, 249, - 250, 251, 229, 230, 231, 232, 233, 234, 235, 236, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, - 988, 989, 990, 991, 992, 993, 994, 995, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 996, 1319, 1320, - 1321, 1322, 1323, 1324, 1325, 1326, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1327, 0, 0, 0, 0, - 0, 231, 232, 233, 234, 235, 236, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 237, 231, 232, 233, - 234, 235, 236, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 237, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1321, 1322, - 1323, 1324, 1325, 1326, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1327, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1352, 0, 238, 239, 240, 241, 242, - 243, 244, 245, 246, 247, 248, 0, 249, 250, 251, - 0, 0, 0, 997, 998, 999, 1000, 1001, 1002, 1003, - 1004, 1005, 1006, 1007, 0, 1008, 1009, 1010, 0, 1440, - 0, 1328, 1329, 1330, 1331, 0, 0, 1332, 1333, 1334, - 1335, 1336, 0, 1337, 1338, 1339, 67, 68, 0, 0, - 445, 0, 446, 239, 240, 241, 242, 243, 244, 245, - 246, 247, 248, 0, 249, 250, 251, 0, 0, 0, - 0, 241, 242, 243, 244, 245, 246, 247, 248, 0, - 249, 250, 251, 0, 69, 0, 0, 70, 0, 71, - 72, 73, 74, 447, 0, 0, 76, 0, 0, 77, - 1329, 1330, 1331, 78, 0, 1332, 1333, 1334, 1335, 1336, - 79, 1337, 1338, 1339, 0, 0, 0, 80, 0, 1321, - 1322, 1323, 1324, 1325, 1326, 0, 212, 213, 214, 0, - 0, 0, 0, 81, 1327, 1321, 1322, 1323, 1324, 1325, - 1326, 0, 0, 0, 0, 0, 0, 0, 0, 448, - 1327, 0, 82, 83, 0, 0, 0, 0, 84, 0, - 1321, 1322, 1323, 1324, 1325, 1326, 85, 86, 87, 88, - 0, 0, 89, 74, 90, 1327, 0, 0, 0, 70, - 0, 71, 72, 73, 0, 0, 0, 449, 0, 0, - 215, 216, 450, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 260, 217, 218, 0, 219, - 0, 0, 0, 0, 220, 0, 221, 0, 0, 84, - 0, 624, 625, 626, 82, 83, 0, 938, 0, 87, - 0, 0, 1330, 1331, 0, 0, 1332, 1333, 1334, 1335, - 1336, 88, 1337, 1338, 1339, 0, 90, 0, 0, 1331, - 0, 0, 1332, 1333, 1334, 1335, 1336, 0, 1337, 1338, - 1339, 0, 0, 70, 0, 71, 72, 73, 627, 628, - 67, 68, 0, 0, 445, 77, 0, 1332, 1333, 1334, - 1335, 1336, 0, 1337, 1338, 1339, 79, 0, 0, 0, - 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 69, 81, - 0, 70, 0, 71, 72, 73, 74, 447, 0, 0, - 76, 0, 0, 77, 0, 0, 0, 78, 82, 83, - 0, 0, 0, 0, 79, 0, 0, 0, 0, 0, - 0, 80, 629, 0, 0, 88, 0, 0, 89, 0, - 90, 0, 0, 0, 0, 0, 0, 81, 0, 67, - 68, 0, 0, 723, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 448, 0, 0, 82, 83, 0, 0, - 0, 0, 84, 0, 0, 0, 0, 0, 67, 68, - 85, 86, 87, 88, 0, 0, 89, 69, 90, 0, - 70, 0, 71, 72, 73, 74, 447, 0, 0, 76, - 0, 0, 77, 0, 0, 0, 78, 0, 0, 0, - 0, 0, 0, 79, 0, 0, 69, 0, 0, 70, - 80, 71, 72, 73, 74, 447, 0, 0, 76, 0, - 0, 77, 0, 0, 0, 78, 81, 0, 0, 0, - 0, 0, 79, 0, 0, 0, 0, 0, 0, 80, - 0, 0, 448, 0, 0, 82, 83, 0, 0, 0, - 0, 84, 0, 0, 0, 81, 0, 67, 68, 85, - 86, 87, 88, 0, 0, 89, 0, 90, 0, 0, - 0, 448, 0, 0, 82, 83, 0, 0, 0, 0, - 84, 67, 68, 0, 0, 0, 0, 0, 85, 86, - 87, 88, 0, 0, 89, 69, 90, 0, 70, 0, - 71, 72, 73, 74, 75, 0, 0, 76, 0, 70, - 77, 71, 72, 73, 78, 0, 0, 0, 0, 69, - 0, 79, 70, 0, 71, 72, 73, 74, 80, 0, - 0, 76, 0, 0, 77, 0, 0, 0, 78, 80, - 0, 0, 0, 0, 81, 79, 0, 0, 0, 0, - 0, 0, 80, 0, 0, 260, 0, 0, 0, 0, - 0, 0, 0, 82, 83, 0, 0, 0, 81, 84, - 67, 68, 0, 0, 82, 83, 0, 85, 86, 87, - 88, 0, 0, 89, 576, 90, 0, 82, 83, 0, - 0, 88, 0, 84, 0, 0, 90, 0, 0, 67, - 0, 85, 86, 87, 88, 0, 0, 89, 69, 90, - 0, 70, 0, 71, 72, 73, 74, 0, 0, 183, - 76, 0, 0, 77, 0, 0, 0, 78, 0, 0, - 0, 0, 0, 0, 79, 0, 0, 69, 0, 0, - 70, 80, 71, 72, 73, 74, 493, 0, 0, 76, - 0, 0, 77, 0, 0, 0, 78, 81, 0, 0, - 0, 0, 0, 79, 0, 0, 0, 0, 0, 0, - 80, 0, 0, 0, 0, 0, 82, 83, 0, 0, - 0, 0, 84, 0, 0, 173, 81, 0, 0, 0, - 85, 86, 87, 88, 0, 0, 89, 0, 90, 0, - 0, 0, 0, 0, 0, 82, 83, 0, 0, 173, - 0, 84, 0, 0, 0, 0, 0, 0, 0, 85, - 86, 87, 88, 174, 0, 89, 70, 90, 71, 72, - 73, 74, 914, 0, 0, 175, 0, 0, 77, 0, - 0, 0, 78, 0, 0, 0, 0, 174, 0, 79, - 70, 0, 71, 72, 73, 74, 80, 0, 0, 175, - 0, 0, 77, 0, 0, 0, 78, 0, 0, 0, - 0, 0, 81, 79, 0, 0, 0, 0, 0, 0, - 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 83, 0, 0, 67, 81, 84, 0, 0, - 0, 0, 0, 0, 0, 176, 177, 87, 88, 0, - 0, 89, 0, 90, 0, 82, 83, 0, 0, 0, - 0, 84, 0, 0, 212, 213, 214, 0, 0, 176, - 177, 87, 88, 69, 0, 89, 70, 90, 71, 72, - 73, 74, 0, 0, 0, 76, 0, 0, 77, 0, - 0, 0, 78, 0, 0, 0, 0, 0, 0, 79, + 41, 42, 43, 0, 0, 0, 0, 0, 0, 0, + 0, 44, 0, 45, 0, 46, 0, 0, 0, 0, + 47, 0, 48, 49, 50, 0, 0, 51, 0, 0, + 0, 0, 52, 0, 0, 53, 0, 0, 0, 54, + 7, 8, 9, 10, 55, 11, 12, 13, 14, 0, + 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, + 57, 58, 59, 0, 0, 60, 0, 61, 62, 0, + 0, 63, 0, 938, 0, 0, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 15, 255, 256, + 257, 16, 0, 17, 18, 19, 0, 0, 0, 20, + 0, 0, 0, 21, 22, 0, 0, 0, 0, 0, + 0, 0, 23, 24, 25, 0, 0, 0, 26, 0, + 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 0, 0, + 0, 0, 0, 0, 0, 0, 44, 0, 45, 0, + 46, 0, 0, 0, 0, 47, 0, 48, 49, 50, + 0, 0, 51, 0, 0, 0, 0, 52, 0, 0, + 53, 0, 0, 0, 54, 7, 8, 9, 10, 55, + 11, 12, 13, 14, 933, 0, 0, 0, 0, 0, + 0, 0, 56, 0, 0, 57, 58, 59, 0, 0, + 60, 0, 61, 62, 0, 0, 63, 0, 0, 0, + 235, 236, 237, 238, 239, 240, 241, 242, 0, 0, + 0, 0, 15, 0, 0, 0, 16, 243, 17, 18, + 19, 0, 0, 0, 20, 0, 0, 0, 21, 22, + 0, 0, 0, 0, 0, 0, 0, 23, 24, 25, + 0, 0, 0, 26, 0, 0, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 0, 0, 0, 0, 0, 0, 0, + 0, 44, 0, 45, 0, 46, 0, 0, 0, 0, + 47, 0, 48, 49, 50, 0, 0, 51, 0, 0, + 0, 0, 52, 0, 0, 53, 0, 0, 0, 54, + 7, 8, 9, 10, 55, 11, 12, 13, 14, 0, + 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, + 57, 58, 59, 0, 0, 60, 0, 61, 62, 1068, + 0, 63, 1277, 0, 0, 0, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 15, 255, 256, + 257, 16, 0, 17, 18, 19, 0, 0, 0, 20, + 0, 0, 0, 21, 22, 0, 0, 0, 0, 0, + 0, 0, 23, 24, 25, 0, 0, 0, 26, 0, + 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 0, 0, + 0, 0, 0, 0, 0, 0, 44, 0, 45, 0, + 46, 0, 0, 0, 0, 47, 0, 48, 49, 50, + 0, 0, 51, 0, 0, 0, 0, 52, 0, 0, + 53, 0, 0, 0, 54, 7, 8, 9, 10, 55, + 11, 12, 13, 14, 1345, 0, 0, 0, 0, 0, + 0, 0, 56, 0, 0, 57, 58, 59, 0, 0, + 60, 0, 61, 62, 0, 0, 63, 0, 0, 0, + 235, 236, 237, 238, 239, 240, 241, 242, 0, 0, + 0, 0, 15, 0, 0, 0, 16, 243, 17, 18, + 19, 0, 0, 0, 20, 0, 0, 0, 21, 22, + 0, 0, 0, 0, 0, 0, 0, 23, 24, 25, + 0, 0, 0, 26, 0, 0, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 0, 0, 0, 0, 0, 0, 0, + 0, 44, 0, 45, 0, 46, 0, 0, 0, 0, + 47, 0, 48, 49, 50, 0, 0, 51, 0, 0, + 0, 0, 52, 0, 0, 53, 0, 0, 0, 54, + 7, 8, 9, 10, 55, 11, 12, 13, 14, 0, + 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, + 57, 58, 59, 0, 0, 60, 0, 61, 62, 1494, + 0, 63, 0, 0, 1300, 0, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 15, 255, 256, + 257, 16, 0, 17, 18, 19, 0, 0, 0, 20, + 0, 0, 0, 21, 22, 0, 0, 0, 0, 0, + 0, 0, 23, 24, 25, 0, 0, 0, 26, 0, + 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 0, 0, + 0, 0, 0, 0, 0, 0, 44, 0, 45, 0, + 46, 0, 0, 0, 0, 47, 0, 48, 49, 50, + 0, 0, 51, 0, 0, 0, 0, 52, 0, 0, + 53, 0, 0, 0, 54, 652, 653, 654, 10, 55, + 11, 655, 656, 67, 68, 0, 0, 657, 0, 0, + 0, 0, 56, 0, 0, 57, 58, 59, 0, 0, + 60, 0, 61, 62, 0, 0, 63, 235, 236, 237, + 238, 239, 240, 241, 242, 0, 0, 0, 0, 464, + 0, 0, 658, 69, 243, 0, 70, 0, 71, 72, + 73, 74, 465, 0, 659, 76, 0, 0, 77, 660, + 0, 0, 78, 0, 0, 0, 0, 661, 662, 79, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, - 0, 74, 0, 0, 0, 212, 213, 214, 0, 0, - 0, 0, 81, 70, 0, 71, 72, 73, 215, 216, - 0, 0, 0, 0, 212, 213, 214, 0, 0, 0, - 0, 82, 83, 0, 0, 0, 0, 84, 212, 213, - 214, 0, 0, 80, 0, 85, 86, 87, 88, 0, - 0, 89, 74, 90, 217, 935, 936, 937, 0, 260, - 0, 0, 220, 0, 221, 0, 0, 84, 0, 215, - 216, 74, 0, 0, 0, 938, 0, 87, 82, 83, - 939, 0, 0, 0, 0, 74, 0, 0, 215, 216, - 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, - 90, 0, 215, 216, 0, 217, 218, 0, 219, 0, - 0, 0, 0, 220, 0, 221, 0, 0, 84, 0, - 0, 0, 0, 184, 217, 218, 938, 219, 87, 0, - 0, 1185, 220, 0, 221, 0, 0, 84, 217, 218, - 0, 219, 0, 0, 0, 938, 220, 87, 221, 0, - 1187, 84, 0, 0, 105, 106, 0, 0, 0, 938, - 0, 87, 107, 108, 1189, 0, 0, 0, 0, 0, - 0, 109, 0, 110, 111, 112, 113, 0, 0, 0, - 114, 0, 0, 0, 0, 115, 0, 0, 0, 0, + 0, 0, 235, 236, 237, 238, 239, 240, 241, 242, + 0, 0, 81, 0, 0, 0, 0, 0, 0, 243, + 0, 663, 0, 664, 0, 665, 0, 0, 0, 466, + 666, 0, 82, 83, 667, 0, 0, 668, 84, 0, + 0, 0, 669, 0, 0, 670, 85, 86, 87, 88, + 0, 0, 89, 0, 90, 0, 652, 653, 654, 10, + 0, 11, 655, 656, 67, 68, 0, 671, 1045, 0, + 672, 673, 0, 0, 0, 674, 0, 675, 0, 697, + 0, 676, 0, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 0, 255, 256, 257, 698, 0, + 464, 0, 0, 658, 69, 0, 0, 70, 0, 71, + 72, 73, 74, 465, 0, 659, 76, 0, 0, 77, + 660, 0, 0, 78, 0, 0, 0, 0, 661, 662, + 79, 0, 0, 774, 0, 0, 0, 80, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 0, + 255, 256, 257, 81, 0, 0, 0, 0, 0, 0, + 0, 0, 663, 776, 664, 0, 665, 0, 0, 0, + 466, 666, 0, 82, 83, 667, 777, 778, 668, 84, + 0, 0, 0, 669, 0, 0, 670, 85, 86, 87, + 88, 0, 779, 89, 0, 90, 7, 8, 9, 10, + 0, 11, 12, 13, 0, 0, 0, 0, 671, 0, + 0, 672, 673, 0, 0, 0, 674, 0, 675, 0, + 0, 0, 676, 780, 0, 0, 781, 0, 0, 782, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 116, 117, 118, 119, 120, 121, - 0, 0, 0, 0, 0, 122, 123, 124, 125, 0, - 0, 0, 0, 0, 126, 127, 105, 106, 128, 129, - 0, 0, 0, 130, 107, 108, 0, 0, 0, 131, - 132, 0, 133, 109, 0, 110, 111, 112, 113, 0, - 134, 0, 114, 0, 0, 0, 0, 115, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 135, 0, 620, - 0, 0, 0, 0, 0, 0, 116, 117, 118, 119, - 120, 121, 0, 0, 0, 0, 0, 122, 123, 124, - 125, 0, 0, 0, 0, 0, 126, 127, 105, 106, - 128, 129, 0, 0, 0, 130, 107, 108, 0, 0, - 0, 131, 132, 0, 133, 109, 0, 110, 111, 112, - 113, 0, 134, 0, 114, 0, 0, 0, 0, 115, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, - 0, 749, 0, 0, 0, 0, 0, 0, 116, 117, - 118, 119, 120, 121, 0, 0, 0, 0, 0, 122, - 123, 124, 125, 0, 0, 0, 0, 0, 126, 127, - 105, 106, 128, 129, 0, 0, 0, 130, 107, 108, - 0, 0, 0, 131, 132, 0, 133, 109, 0, 110, - 111, 112, 113, 0, 134, 0, 114, 0, 0, 0, - 0, 115, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 135, 0, 754, 0, 0, 0, 0, 0, 0, - 116, 117, 118, 119, 120, 121, 0, 0, 0, 0, - 0, 122, 123, 124, 125, 0, 0, 0, 0, 0, - 126, 127, 105, 106, 128, 129, 0, 0, 0, 130, - 107, 108, 0, 0, 0, 131, 132, 0, 133, 109, - 0, 110, 111, 112, 113, 0, 134, 0, 114, 0, - 0, 0, 0, 115, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 135, 0, 830, 0, 0, 0, 0, - 0, 0, 116, 117, 118, 119, 120, 121, 0, 0, - 0, 0, 0, 122, 123, 124, 125, 0, 0, 0, - 0, 0, 126, 127, 105, 106, 128, 129, 0, 0, - 0, 130, 107, 108, 0, 0, 0, 131, 132, 0, - 133, 109, 0, 110, 111, 112, 113, 0, 134, 0, - 114, 0, 0, 0, 0, 115, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 135, 0, 952, 0, 0, - 0, 0, 0, 0, 116, 117, 118, 119, 120, 121, - 0, 0, 0, 0, 0, 122, 123, 124, 125, 0, - 0, 0, 0, 0, 126, 127, 105, 106, 128, 129, - 0, 0, 0, 130, 107, 108, 0, 0, 0, 131, - 132, 0, 133, 109, 0, 110, 111, 112, 113, 0, - 134, 0, 114, 0, 0, 0, 0, 115, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 135, 0, 1199, - 0, 0, 0, 0, 0, 0, 116, 117, 118, 119, - 120, 121, 0, 0, 0, 0, 0, 122, 123, 124, - 125, 0, 0, 0, 0, 0, 126, 127, 105, 106, - 128, 129, 0, 0, 0, 130, 107, 108, 0, 0, - 0, 131, 132, 0, 133, 109, 0, 110, 111, 112, - 113, 0, 134, 0, 114, 106, 0, 0, 0, 115, - 0, 0, 107, 108, 0, 0, 0, 0, 0, 135, - 0, 1296, 0, 110, 111, 112, 0, 0, 116, 117, - 118, 119, 120, 121, 0, 293, 0, 0, 0, 122, - 123, 124, 125, 0, 0, 0, 0, 0, 126, 127, - 0, 0, 128, 129, 0, 0, 0, 130, 120, 0, - 0, 0, 0, 131, 132, 106, 133, 0, 125, 0, - 0, 0, 107, 108, 126, 0, 0, 0, 0, 129, - 0, 0, 0, 110, 111, 112, 0, 0, 0, 131, - 132, 135, 133, 106, 0, 293, 0, 0, 0, 0, - 107, 108, 106, 0, 0, 0, 0, -141, 0, 107, - 108, 110, 111, 112, 0, 0, 0, 135, 120, 0, - 110, 111, 112, 293, 0, 0, 0, 0, 125, 0, - 0, 0, 293, 70, 126, 71, 72, 73, 128, 129, - 0, 0, 0, 0, 0, 259, 120, 303, 0, 131, - 132, 0, 133, 0, 0, 120, 125, 0, 0, 0, - 0, 0, 126, 80, 0, 125, 70, 129, 71, 72, - 73, 126, 0, 0, 0, 0, 129, 135, 132, 260, - 133, 0, 0, 0, 0, 0, 0, 132, 0, 133, - 0, 0, 0, 0, 0, 0, 80, 0, 82, 83, - 0, 0, 0, 0, 0, 135, 0, 0, 0, 0, - 0, 0, 260, 0, 135, 88, 0, 0, 0, 0, - 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 83, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 172, 0, 0, 0, 0, 88, 0, - 0, 0, 0, 90, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1315, 0, 783, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1316, 0, 784, 0, 0, + 1317, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 23, 24, + 0, 785, 0, 0, 26, 0, 0, 1413, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 235, 236, 237, 238, 239, 240, 241, + 242, 0, 0, 0, 45, 0, 46, 0, 0, 0, + 243, 1318, 0, 0, 0, 1319, 0, 0, 1320, 0, + 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 189 + 0, 0, 0, 0, 0, 0, 0, 0, 1321, 0, + 0, 1322, 1323, 1324, 942, 0, 1325, 0, 1326, 62, + 0, 0, 1327, 0, 943, 944, 945, 946, 947, 948, + 949, 950, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 951, 0, 952, 953, 954, 955, 956, 957, 958, + 959, 960, 961, 962, 963, 0, 0, 1415, 1416, 1417, + 0, 0, 1418, 1419, 1420, 1421, 1422, 0, 1423, 1424, + 1425, 0, 0, 964, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 339, 340, 0, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 0, 255, 256, 257, 582, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 965, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 341, 0, 0, + 70, 0, 71, 72, 73, 74, 0, 0, 0, 342, + 0, 0, 77, 0, 0, 0, 78, 0, 0, 0, + 0, 0, 0, 79, 0, 0, 966, 0, 0, 967, + 80, 968, 969, 970, 971, 972, 973, 974, 975, 976, + 977, 978, 0, 979, 980, 0, 81, 981, 513, 514, + 0, 0, 0, 0, 0, 0, 0, 0, 343, 0, + 0, 0, 0, 0, 0, 0, 82, 83, 0, 0, + 0, 0, 84, 0, 0, 0, 0, 0, 0, 0, + 344, 345, 87, 88, 0, 0, 89, 0, 90, 341, + 0, 0, 70, 0, 71, 72, 73, 74, 0, 0, + 0, 342, 0, 0, 77, 0, 0, 0, 78, 347, + 0, 0, 0, 0, 0, 79, 0, 0, 0, 0, + 0, 0, 80, 235, 236, 237, 238, 239, 240, 241, + 242, 0, 0, 0, 0, 0, 0, 0, 81, 0, + 243, 0, 0, 0, 0, 216, 217, 218, 0, 0, + 343, 0, 0, 0, 0, 0, 0, 0, 82, 83, + 0, 0, 0, 0, 84, 0, 0, 0, 0, 0, + 0, 0, 344, 345, 87, 88, 0, 0, 89, 0, + 90, 235, 236, 237, 238, 239, 240, 241, 242, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 243, 219, + 0, 347, 235, 236, 237, 238, 239, 240, 241, 242, + 0, 220, 221, 0, 0, 0, 0, 0, 0, 243, + 235, 236, 237, 238, 239, 240, 241, 242, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 243, 235, 236, + 237, 238, 239, 240, 241, 242, 0, 0, 222, 223, + 0, 224, 0, 0, 0, 243, 225, 0, 226, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 0, 255, 256, 257, 598, 235, 236, 237, 238, 239, + 240, 241, 242, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 243, 851, 852, 853, 854, 855, 856, 857, + 858, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 859, 0, 0, 0, 0, 0, 0, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 0, 255, + 256, 257, 686, 0, 0, 0, 0, 0, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 0, + 255, 256, 257, 766, 0, 0, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 0, 255, 256, + 257, 909, 0, 0, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 0, 255, 256, 257, 1026, + 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1413, 0, 0, + 0, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 0, 255, 256, 257, 1298, 0, 0, 860, + 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, + 0, 871, 872, 873, 1405, 1406, 1407, 1408, 1409, 1410, + 1411, 1412, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1413, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 237, 238, 239, 240, 241, 242, 0, 0, 0, 0, + 0, 0, 0, 0, 1529, 243, 1414, 1415, 1416, 1417, + 0, 0, 1418, 1419, 1420, 1421, 1422, 0, 1423, 1424, + 1425, 105, 0, 106, 0, 0, 107, 108, 0, 0, + 0, 0, 0, 0, 109, 110, 237, 238, 239, 240, + 241, 242, 0, 111, 0, 112, 113, 114, 115, 0, + 0, 243, 116, 0, 0, 0, 0, 117, 0, 0, + 1414, 1415, 1416, 1417, 0, 0, 1418, 1419, 1420, 1421, + 1422, 0, 1423, 1424, 1425, 118, 119, 120, 121, 122, + 123, 124, 125, 0, 0, 0, 0, 0, 126, 127, + 128, 129, 0, 0, 0, 0, 0, 130, 131, 67, + 68, 132, 133, 462, 0, 463, 134, 0, 0, 0, + 0, 0, 135, 136, 0, 137, 0, 0, 0, 0, + 0, 0, 0, 138, 0, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 464, 255, 256, 257, 69, + 0, 0, 70, 0, 71, 72, 73, 74, 465, 0, + 0, 76, 0, 0, 77, 0, 0, 0, 78, 0, + 0, 0, 0, 0, 0, 79, 1407, 1408, 1409, 1410, + 1411, 1412, 80, 247, 248, 249, 250, 251, 252, 253, + 254, 1413, 255, 256, 257, 216, 217, 218, 81, 237, + 238, 239, 240, 241, 242, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 243, 466, 0, 0, 82, 83, + 0, 0, 0, 0, 84, 0, 1407, 1408, 1409, 1410, + 1411, 1412, 85, 86, 87, 88, 0, 0, 89, 0, + 90, 1413, 1407, 1408, 1409, 1410, 1411, 1412, 0, 219, + 0, 0, 0, 467, 0, 0, 0, 1413, 468, 0, + 0, 220, 221, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 483, 484, 485, 335, 0, 0, 0, 0, 222, 223, + 0, 224, 0, 0, 0, 0, 225, 0, 226, 0, + 0, 1415, 1416, 1417, 0, 0, 1418, 1419, 1420, 1421, + 1422, 0, 1423, 1424, 1425, 0, 0, 486, 0, 487, + 0, 0, 0, 0, 0, 0, 0, 248, 249, 250, + 251, 252, 253, 254, 219, 255, 256, 257, 0, 0, + 0, 0, 0, 0, 0, 0, 220, 221, 0, 305, + 0, 0, 1416, 1417, 0, 0, 1418, 1419, 1420, 1421, + 1422, 0, 1423, 1424, 1425, 0, 305, 0, 0, 1417, + 0, 0, 1418, 1419, 1420, 1421, 1422, 0, 1423, 1424, + 1425, 0, 0, 488, 223, 0, 224, 0, 0, 0, + 105, 225, 106, 226, 0, 0, 108, 0, 0, 0, + 0, 0, 0, 109, 110, 0, 0, 105, 70, 106, + 71, 72, 73, 108, 112, 113, 114, 489, 0, 0, + 109, 110, 0, 0, 0, 0, 300, 0, 0, 0, + 0, 112, 299, 114, 0, 0, 0, 0, 80, 0, + 0, 0, 0, 300, 216, 217, 218, 0, 0, 0, + 124, 0, 0, 0, 266, 0, 399, 67, 68, 0, + 129, 0, 0, 0, 0, 0, 130, 124, 0, 0, + 132, 133, 0, 0, 82, 83, 0, 129, 0, 0, + 0, 0, 136, 130, 137, 1038, 67, 68, 133, 0, + 0, 88, 0, 0, 0, 0, 90, 69, 219, 136, + 70, 137, 71, 72, 73, 74, 75, 0, 0, 76, + 220, 221, 77, 0, 0, 0, 78, 0, 0, 187, + 0, 0, 0, 79, 0, 0, 69, 0, 0, 70, + 80, 71, 72, 73, 74, 75, 0, 0, 76, 0, + 0, 77, 0, 0, 0, 78, 81, 222, 1278, 1279, + 1280, 0, 79, 0, 0, 225, 0, 226, 0, 80, + 0, 0, 0, 0, 0, 0, 82, 83, 1281, 0, + 0, 0, 84, 1282, 0, 81, 0, 1470, 67, 68, + 85, 86, 87, 88, 0, 0, 89, 0, 90, 0, + 0, 0, 0, 0, 0, 82, 83, 0, 0, 0, + 0, 84, 0, 0, 0, 0, 699, 700, 701, 85, + 86, 87, 88, 0, 0, 89, 0, 90, 69, 0, + 0, 70, 0, 71, 72, 73, 74, 75, 0, 0, + 76, 0, 0, 77, 0, 0, 0, 78, 699, 700, + 701, 0, 0, 0, 79, 0, 0, 0, 0, 0, + 70, 80, 71, 72, 73, 702, 703, 0, 0, 0, + 0, 0, 77, 0, 0, 0, 0, 81, 0, 0, + 0, 0, 0, 79, 0, 0, 0, 0, 0, 0, + 80, 0, 70, 0, 71, 72, 73, 82, 83, 0, + 0, 0, 0, 84, 77, 0, 81, 0, 0, 0, + 0, 85, 86, 87, 88, 79, 0, 89, 0, 90, + 0, 0, 80, 0, 67, 68, 82, 83, 462, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 81, 0, + 704, 0, 0, 88, 0, 0, 89, 0, 90, 0, + 0, 0, 0, 67, 68, 0, 0, 800, 82, 83, + 464, 0, 0, 0, 69, 0, 0, 70, 0, 71, + 72, 73, 74, 465, 0, 88, 76, 0, 89, 77, + 90, 0, 0, 78, 0, 0, 0, 0, 0, 464, + 79, 0, 0, 69, 0, 0, 70, 80, 71, 72, + 73, 74, 465, 0, 0, 76, 0, 0, 77, 0, + 0, 0, 78, 81, 0, 0, 0, 0, 0, 79, + 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, + 466, 0, 0, 82, 83, 216, 217, 218, 0, 84, + 0, 0, 81, 0, 0, 67, 68, 85, 86, 87, + 88, 0, 0, 89, 0, 90, 0, 0, 0, 466, + 0, 0, 82, 83, 0, 0, 0, 0, 84, 70, + 0, 71, 72, 73, 67, 68, 85, 86, 87, 88, + 0, 464, 89, 0, 90, 69, 0, 0, 70, 219, + 71, 72, 73, 74, 465, 0, 0, 76, 0, 80, + 77, 220, 221, 0, 78, 0, 0, 0, 0, 0, + 0, 79, 0, 0, 69, 266, 0, 70, 80, 71, + 72, 73, 74, 75, 0, 0, 76, 0, 0, 77, + 0, 0, 0, 78, 81, 82, 83, 0, 222, 223, + 79, 224, 0, 0, 0, 0, 225, 80, 226, 0, + 0, 466, 88, 0, 82, 83, 0, 90, 0, 1281, + 84, 67, 68, 81, 1360, 0, 0, 0, 85, 86, + 87, 88, 0, 0, 89, 0, 90, 0, 0, 0, + 188, 0, 0, 82, 83, 67, 68, 0, 0, 84, + 0, 0, 0, 0, 0, 0, 0, 85, 86, 87, + 88, 69, 0, 89, 70, 90, 71, 72, 73, 74, + 0, 0, 0, 76, 0, 0, 77, 0, 0, 0, + 78, 0, 0, 0, 0, 69, 0, 79, 70, 0, + 71, 72, 73, 74, 80, 0, 0, 76, 0, 0, + 77, 0, 0, 0, 78, 0, 0, 0, 0, 0, + 81, 79, 0, 0, 0, 0, 0, 0, 80, 216, + 217, 218, 0, 0, 0, 0, 0, 611, 0, 0, + 82, 83, 67, 0, 81, 0, 84, 0, 0, 0, + 0, 0, 0, 0, 85, 86, 87, 88, 0, 0, + 89, 0, 90, 0, 82, 83, 0, 0, 0, 0, + 84, 177, 0, 0, 0, 0, 0, 0, 85, 86, + 87, 88, 69, 219, 89, 70, 90, 71, 72, 73, + 74, 524, 0, 0, 76, 220, 221, 77, 0, 0, + 0, 78, 0, 0, 0, 0, 0, 0, 79, 0, + 0, 178, 0, 0, 70, 80, 71, 72, 73, 74, + 1003, 0, 0, 179, 0, 0, 77, 0, 0, 0, + 78, 81, 222, 223, 0, 224, 0, 79, 0, 0, + 225, 0, 226, 0, 80, 0, 0, 0, 0, 0, + 0, 82, 83, 1281, 0, 0, 0, 84, 177, 0, + 81, 0, 0, 0, 0, 85, 86, 87, 88, 0, + 0, 89, 0, 90, 0, 0, 0, 0, 0, 0, + 82, 83, 67, 0, 0, 0, 84, 0, 0, 0, + 216, 217, 218, 0, 180, 181, 87, 88, 178, 0, + 89, 70, 90, 71, 72, 73, 74, 0, 0, 0, + 179, 0, 70, 77, 71, 72, 73, 78, 0, 0, + 0, 0, 69, 0, 79, 70, 0, 71, 72, 73, + 74, 80, 0, 0, 76, 0, 0, 77, 0, 0, + 0, 78, 80, 0, 219, 0, 0, 81, 79, 0, + 216, 217, 218, 0, 0, 80, 220, 221, 266, 0, + 0, 0, 0, 0, 216, 217, 218, 82, 83, 0, + 0, 81, 0, 84, 0, 0, 0, 0, 82, 83, + 0, 180, 181, 87, 88, 0, 0, 89, 0, 90, + 0, 82, 83, 222, 223, 88, 224, 84, 0, 0, + 90, 225, 0, 226, 219, 85, 86, 87, 88, 0, + 0, 89, 0, 90, 1281, 0, 220, 221, 219, 1362, + 0, 0, 0, 193, 0, 0, 0, 0, 0, 0, + 220, 221, 0, 0, 0, 0, 0, 0, 0, 70, + 0, 71, 72, 73, 0, 0, 0, 0, 0, 0, + 0, 265, 0, 222, 223, 0, 224, 0, 0, 0, + 0, 225, 0, 226, 0, 0, 0, 222, 223, 80, + 224, 0, 0, 0, 1281, 225, 105, 226, 106, 1364, + 0, 107, 108, 0, 0, 266, 0, 0, 0, 109, + 110, 0, 294, 0, 0, 0, 0, 0, 111, 0, + 112, 113, 114, 115, 0, 82, 83, 116, 0, 0, + 0, 0, 117, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 88, 0, 0, 0, 0, 90, 0, 0, + 118, 119, 120, 121, 122, 123, 124, 125, 0, 0, + 0, 0, 0, 126, 127, 128, 129, 0, 0, 105, + 176, 106, 130, 131, 107, 108, 132, 133, 0, 0, + 0, 134, 109, 110, 0, 0, 0, 135, 136, 0, + 137, 111, 0, 112, 113, 114, 115, 0, 138, 0, + 116, 0, 0, 0, 0, 117, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 695, 0, 0, + 0, 0, 0, 118, 119, 120, 121, 122, 123, 124, + 125, 0, 0, 0, 0, 0, 126, 127, 128, 129, + 0, 0, 105, 0, 106, 130, 131, 107, 108, 132, + 133, 0, 0, 0, 134, 109, 110, 0, 0, 0, + 135, 136, 0, 137, 111, 0, 112, 113, 114, 115, + 0, 138, 0, 116, 0, 0, 0, 0, 117, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 876, 0, 0, 0, 0, 0, 118, 119, 120, 121, + 122, 123, 124, 125, 0, 0, 0, 0, 0, 126, + 127, 128, 129, 0, 0, 105, 0, 106, 130, 131, + 107, 108, 132, 133, 0, 0, 0, 134, 109, 110, + 0, 0, 0, 135, 136, 0, 137, 111, 0, 112, + 113, 114, 115, 0, 138, 0, 116, 0, 0, 0, + 0, 117, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 920, 0, 0, 0, 0, 0, 118, + 119, 120, 121, 122, 123, 124, 125, 0, 0, 0, + 0, 0, 126, 127, 128, 129, 0, 0, 105, 0, + 106, 130, 131, 107, 108, 132, 133, 0, 0, 0, + 134, 109, 110, 0, 0, 0, 135, 136, 0, 137, + 111, 0, 112, 113, 114, 115, 0, 138, 0, 116, + 0, 0, 0, 0, 117, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1027, 0, 0, 0, + 0, 0, 118, 119, 120, 121, 122, 123, 124, 125, + 0, 0, 0, 0, 0, 126, 127, 128, 129, 0, + 0, 105, 0, 106, 130, 131, 107, 108, 132, 133, + 0, 0, 0, 134, 109, 110, 0, 0, 0, 135, + 136, 0, 137, 111, 0, 112, 113, 114, 115, 0, + 138, 0, 116, 0, 0, 0, 0, 117, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1081, + 0, 0, 0, 0, 0, 118, 119, 120, 121, 122, + 123, 124, 125, 0, 0, 0, 0, 0, 126, 127, + 128, 129, 0, 0, 105, 0, 106, 130, 131, 107, + 108, 132, 133, 0, 0, 0, 134, 109, 110, 0, + 0, 0, 135, 136, 0, 137, 111, 0, 112, 113, + 114, 115, 0, 138, 0, 116, 0, 0, 0, 0, + 117, 0, 0, 216, 217, 218, 0, 0, 890, 216, + 217, 218, 1303, 0, 0, 0, 0, 0, 118, 119, + 120, 121, 122, 123, 124, 125, 0, 0, 0, 0, + 0, 126, 127, 128, 129, 0, 0, 0, 0, 0, + 130, 131, 0, 0, 132, 133, 486, 0, 487, 134, + 0, 0, 0, 0, 0, 135, 136, 219, 137, 0, + 0, 0, 0, 219, 0, 105, 138, 106, 0, 220, + 221, 108, 0, 0, 0, 220, 221, 0, 109, 110, + 0, 0, 0, 0, 0, 1451, 0, 0, 0, 112, + 299, 114, 0, 0, 0, 0, 116, 0, 0, 0, + 0, 300, 0, 0, 0, 0, 222, 223, 0, 224, + 0, 0, 222, 223, 225, 224, 226, 0, 0, 0, + 225, 0, 226, 0, 0, 124, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 129, 0, 0, 0, 0, + 0, 130, 0, 0, 0, 0, 133, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 135, 136, 105, 137, + 106, 0, 0, 107, 108, 0, 0, 0, 0, 0, + 0, 109, 110, 0, -153, 0, 0, 0, 0, 0, + 111, 0, 112, 113, 114, 115, 0, 0, 0, 116, + 0, 0, 0, 0, 117, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 118, 119, 120, 121, 122, 123, 124, 125, + 0, 0, 0, 0, 0, 126, 127, 128, 129, 0, + 0, 105, 0, 106, 130, 131, 0, 108, 132, 133, + 0, 0, 0, 134, 109, 110, 0, 0, 0, 135, + 136, 0, 137, 0, 0, 112, 113, 114, 105, 0, + 106, 0, 116, 0, 108, 0, 0, 300, 0, 0, + 0, 109, 110, 105, 0, 106, 0, 0, 0, 108, + 0, 0, 112, 299, 114, 0, 109, 110, 0, 0, + 0, 124, 0, 0, 300, 0, 0, 112, 299, 114, + 0, 129, 0, 0, 0, 0, 0, 130, 0, 300, + 0, 132, 133, 0, 0, 0, 0, 0, 124, 311, + 0, 0, 135, 136, 0, 137, 0, 0, 129, 0, + 0, 0, 0, 124, 130, 0, 0, 0, 0, 133, + 0, 0, 0, 129, 0, 0, 0, 0, 0, 130, + 136, 0, 137, 0, 133, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 136, 0, 137 }; static const yytype_int16 yycheck[] = { - 1, 61, 504, 448, 128, 622, 844, 147, 148, 149, - 150, 639, 152, 455, 154, 481, 156, 13, 684, 701, - 464, 6, 15, 8, 162, 15, 15, 644, 0, 13, - 44, 15, 172, 17, 75, 139, 574, 44, 92, 15, - 62, 63, 139, 183, 184, 75, 15, 139, 74, 189, - 190, 15, 145, 106, 107, 108, 722, 110, 111, 112, - 162, 62, 115, 145, 74, 310, 145, 120, 935, 936, - 937, 164, 125, 126, 58, 128, 129, 130, 145, 132, - 133, 162, 164, 144, 57, 164, 81, 82, 83, 582, - 91, 92, 603, 88, 647, 163, 164, 164, 13, 44, - 15, 162, 17, 145, 148, 145, 33, 74, 148, 2, - 650, 793, 652, 653, 654, 655, 656, 162, 144, 163, - 33, 147, 164, 676, 677, 139, 679, 680, 681, 682, - 148, 162, 139, 145, 144, 162, 578, 147, 144, 584, - 146, 147, 148, 58, 145, 163, 147, 148, 149, 150, - 123, 152, 164, 154, 138, 156, 598, 599, 600, 601, - 602, 157, 153, 56, 57, 58, 59, 60, 152, 162, - 63, 172, 162, 157, 685, 164, 145, 144, 162, 164, - 147, 145, 183, 184, 33, 162, 162, 148, 189, 190, - 250, 6, 634, 8, 139, 733, 145, 163, 164, 644, - 254, 255, 163, 145, 74, 153, 744, 718, 719, 204, - 205, 206, 207, 153, 318, 164, 270, 271, 322, 685, - 465, 318, 164, 138, 469, 322, 318, 145, 162, 146, - 322, 148, 914, 274, 161, 162, 163, 152, 163, 164, - 293, 381, 157, 687, 274, 298, 164, 162, 161, 162, - 163, 162, 497, 254, 255, 162, 163, 10, 11, 12, - 13, 145, 162, 816, 17, 260, 33, 778, 269, 270, - 271, 272, 273, 274, 144, 717, 162, 147, 279, 162, - 164, 821, 145, 145, 145, 162, 10, 11, 12, 284, - 828, 145, 333, 162, 736, 188, 162, 850, 147, 147, - 145, 164, 164, 164, 318, 58, 162, 454, 322, 310, - 164, 318, 778, 162, 163, 322, 145, 153, 1185, 164, - 1187, 763, 1189, 144, 145, 1192, 1193, 1194, 770, 771, - 772, 773, 774, 835, 162, 164, 229, 230, 231, 232, - 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, - 243, 244, 245, 246, 247, 248, 249, 110, 251, 145, - 145, 162, 900, 256, 162, 163, 164, 13, 145, 15, - 987, 17, 162, 318, 145, 162, 621, 322, 164, 164, - 381, 10, 11, 12, 13, 138, 110, 164, 17, 145, - 157, 158, 159, 164, 161, 162, 163, 398, 399, 152, - 145, 162, 145, 145, 157, 162, 447, 448, 164, 920, - 921, 6, 58, 8, 162, 917, 162, 447, 448, 164, - 913, 164, 164, 147, 317, 163, 164, 428, 162, 58, - 577, 432, 433, 434, 435, 436, 162, 1275, 163, 164, - 587, 588, 589, 590, 591, 446, 162, 163, 449, 450, - 162, 163, 493, 454, 162, 162, 457, 557, 144, 144, - 146, 146, 148, 148, 465, 466, 162, 162, 469, 464, - 162, 478, 162, 162, 10, 11, 12, 478, 144, 162, - 109, 110, 144, 162, 146, 378, 148, 634, 141, 142, - 143, 162, 138, 494, 162, 144, 497, 146, 162, 148, - 162, 394, 395, 1169, 147, 147, 152, 147, 163, 138, - 755, 157, 162, 163, 164, 162, 557, 162, 1011, 145, - 962, 1014, 166, 152, 111, 112, 113, 557, 157, 162, - 163, 164, 162, 162, 162, 576, 157, 164, 74, 75, - 163, 163, 987, 584, 1161, 645, 576, 147, 648, 153, - 144, 162, 162, 162, 584, 146, 557, 558, 164, 145, - 145, 164, 164, 164, 457, 164, 567, 164, 164, 716, - 10, 11, 12, 574, 110, 111, 577, 113, 725, 154, - 154, 582, 118, 164, 120, 586, 587, 588, 589, 590, - 591, 592, 164, 164, 164, 1223, 164, 145, 164, 492, - 164, 164, 603, 644, 645, 164, 164, 648, 144, 164, - 164, 612, 33, 506, 644, 645, 763, 164, 648, 164, - 621, 628, 164, 770, 771, 772, 773, 774, 688, 689, - 164, 154, 164, 634, 74, 75, 696, 164, 698, 164, - 764, 154, 164, 644, 645, 164, 164, 648, 162, 650, - 157, 652, 653, 654, 655, 656, 164, 10, 11, 12, - 13, 163, 15, 664, 17, 163, 165, 164, 561, 74, - 110, 111, 673, 113, 144, 162, 145, 145, 118, 145, - 120, 574, 145, 684, 685, 146, 166, 701, 10, 11, - 12, 145, 687, 164, 701, 162, 62, 837, 838, 944, - 1366, 1367, 1368, 162, 147, 58, 162, 162, 157, 162, - 157, 764, 164, 164, 162, 716, 1161, 718, 719, 85, - 166, 722, 144, 163, 725, 10, 10, 780, 147, 10, - 10, 831, 733, 99, 155, 156, 157, 158, 159, 146, - 161, 162, 163, 744, 154, 805, 145, 165, 146, 166, - 98, 99, 74, 75, 755, 121, 701, 110, 765, 146, - 157, 157, 763, 164, 146, 131, 146, 146, 769, 770, - 771, 772, 773, 774, 775, 165, 1269, 778, 164, 793, - 673, 157, 144, 164, 162, 138, 793, 164, 110, 111, - 831, 113, 1285, 1286, 1287, 164, 118, 162, 120, 152, - 1466, 831, 166, 162, 157, 750, 162, 1473, 163, 162, - 176, 163, 163, 179, 180, 962, 144, 148, 145, 919, - 821, 145, 145, 157, 924, 925, 145, 828, 153, 162, - 831, 10, 11, 12, 13, 153, 837, 838, 17, 732, - 733, 153, 164, 844, 153, 162, 145, 163, 793, 164, - 164, 744, 165, 164, 10, 748, 164, 223, 4, 166, - 162, 915, 1364, 145, 162, 1358, 166, 15, 10, 166, - 1363, 146, 10, 10, 10, 146, 1369, 1370, 919, 58, - 145, 164, 15, 924, 925, 162, 162, 166, 162, 919, - 153, 164, 162, 165, 924, 925, 165, 164, 166, 900, - 914, 165, 268, 165, 164, 164, 164, 914, 801, 802, - 803, 804, 913, 806, 915, 164, 153, 1017, 919, 920, - 921, 153, 153, 924, 925, 1418, 164, 154, 154, 154, - 164, 110, 154, 162, 164, 828, 164, 303, 144, 305, - 306, 307, 308, 944, 164, 61, 987, 164, 164, 1009, - 164, 844, 10, 954, 146, 1448, 10, 987, 324, 138, - 146, 962, 328, 146, 10, 146, 162, 164, 334, 914, - 164, 337, 1465, 152, 162, 162, 1017, 162, 157, 345, - 346, 165, 164, 162, 162, 145, 154, 1017, 164, 164, - 935, 936, 937, 164, 164, 154, 154, 345, 346, 154, - 945, 164, 146, 165, 10, 10, 146, 900, 33, 902, - 1011, 162, 162, 1014, 836, 162, 1017, 146, 643, 164, - 146, 387, 1164, 162, 302, 269, 270, 271, 272, 273, - 146, 1223, 164, 399, 164, 151, 164, 153, 765, 155, - 628, 157, 158, 159, 160, 161, 162, 163, 164, 165, - 166, 167, 168, 169, 170, 171, 322, 954, 764, 987, - 1161, 322, 10, 11, 12, 431, 182, 963, 322, 945, - 186, 187, 188, 1124, 18, 19, 20, 21, 22, 23, - 1180, -1, 1182, -1, 977, -1, -1, 1227, -1, 33, - -1, -1, 458, 399, -1, 988, 989, 990, 991, 992, - 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, - 1003, 1004, 1005, 1006, 1007, 1008, 1170, 1010, 22, 23, - 1161, -1, -1, 1124, -1, -1, 74, 75, -1, 33, - -1, 1161, 157, 158, 159, 501, 161, 162, 163, 1180, - -1, 1182, -1, -1, -1, -1, 494, -1, -1, 265, - 1180, -1, 1182, -1, -1, -1, -1, -1, -1, 1219, - 1161, 509, 110, 111, -1, 113, 1266, -1, 1169, 1170, - 118, -1, 120, -1, -1, -1, -1, -1, -1, 1180, - -1, 1182, -1, -1, 428, -1, -1, -1, 432, 433, - 434, 435, 436, -1, -1, -1, -1, -1, -1, -1, - 148, -1, 446, -1, 152, 449, 450, 151, 152, 153, - 154, 155, 156, 157, 158, 159, -1, 161, 162, 163, - -1, -1, -1, -1, -1, 1266, 1227, -1, -1, -1, - 596, -1, -1, -1, -1, -1, 1266, -1, -1, -1, - 1185, -1, 1187, -1, 1189, -1, -1, 1192, 1193, 1194, - -1, 155, 156, 157, 158, 159, 1356, 161, 162, 163, - -1, 1154, -1, -1, 380, 1266, 632, -1, 1269, -1, - -1, -1, -1, -1, 1275, -1, -1, 643, 1338, -1, - -1, -1, -1, -1, 1285, 1286, 1287, -1, -1, -1, - -1, 639, -1, -1, -1, -1, -1, -1, -1, 647, - -1, -1, 650, -1, 652, 653, 654, 655, 656, -1, - -1, -1, -1, -1, -1, 1356, 1416, -1, 1214, 1215, - 1216, 1217, 1218, 567, 1220, -1, 1356, -1, 676, 677, - -1, 679, 680, 681, 682, -1, -1, -1, -1, -1, - -1, 1234, 17, 18, 19, 20, 21, 22, 23, 1409, - 1410, -1, 1412, -1, 1414, 1356, -1, 1358, 33, -1, - 22, 23, 1363, -1, -1, 1366, 1367, 1368, 1369, 1370, - 486, 33, -1, -1, -1, 1416, -1, -1, -1, -1, - 496, -1, 1275, -1, -1, -1, 1416, -1, -1, -1, - -1, -1, -1, -1, -1, 1455, 1456, 1457, -1, 1459, - 766, -1, 1214, 1215, 1216, 1217, 1218, 33, 1220, -1, - 526, -1, 528, -1, 530, 1416, -1, 1418, -1, -1, - -1, -1, -1, 1319, 1320, 1321, 1322, 1323, 1324, 1325, - 1326, 1327, 1328, 1329, 1330, 1331, 1332, 1333, 1334, 1335, - 1336, 1337, -1, -1, -1, -1, 1339, 1448, -1, 16, - 17, 18, 19, 20, 21, 22, 23, -1, -1, 1352, - -1, -1, -1, -1, 1465, 1466, 33, -1, 816, -1, - 836, -1, 1473, 821, -1, 150, 151, 152, 153, 154, - 155, 156, 157, 158, 159, -1, 161, 162, 163, 1385, - -1, -1, -1, 155, 156, 157, 158, 159, -1, 161, - 162, 163, 850, -1, -1, -1, -1, 1319, 1320, 1321, - 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1330, 1331, - 1332, 1333, 1334, 1335, 1336, 1337, -1, -1, -1, 155, - 156, 157, 158, 159, -1, 161, 162, 163, -1, -1, - -1, -1, -1, -1, 1440, 20, 21, 22, 23, -1, - 916, -1, -1, -1, -1, -1, -1, -1, 33, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 938, 1385, -1, 941, -1, -1, 145, -1, - -1, -1, 149, 150, 151, 152, 153, 154, 155, 156, - 157, 158, 159, -1, 161, 162, 163, 164, -1, 3, + 1, 61, 113, 495, 132, 515, 132, 320, 933, 617, + 92, 714, 44, 149, 590, 151, 152, 153, 154, 298, + 156, 736, 158, 663, 160, 184, 44, 482, 756, 6, + 15, 8, 44, 142, 464, 15, 59, 98, 466, 0, + 176, 33, 81, 82, 83, 166, 167, 608, 142, 88, + 15, 187, 188, 15, 333, 142, 486, 193, 194, 13, + 76, 62, 76, 148, 494, 75, 165, 495, 15, 15, + 148, 799, 76, 148, 148, 723, 589, 725, 726, 727, + 728, 729, 148, 719, 147, 170, 149, 156, 151, 167, + 91, 92, 167, 167, 151, 165, 148, 75, 99, 151, + 148, 167, 165, 126, 148, 10, 11, 12, 148, 166, + 142, 147, 748, 749, 627, 751, 752, 753, 754, 167, + 148, 33, 147, 167, 142, 638, 148, 167, 165, 165, + 142, 147, 165, 147, 150, 775, 150, 166, 167, 167, + 165, 717, 148, 147, 720, 167, 150, 148, 149, 148, + 151, 152, 153, 154, 165, 156, 33, 158, 150, 160, + 165, 167, 148, 13, 6, 15, 8, 17, 167, 208, + 209, 210, 211, 165, 166, 176, 166, 167, 260, 261, + 156, 167, 167, 184, 165, 165, 187, 188, 501, 298, + 167, 619, 193, 194, 276, 277, 256, 356, 165, 33, + 165, 148, 148, 165, 298, 697, 160, 148, 113, 165, + 60, 298, 108, 109, 110, 528, 112, 113, 114, 76, + 329, 117, 165, 149, 333, 151, 167, 266, 124, 165, + 231, 292, 165, 129, 130, 329, 132, 133, 134, 333, + 136, 137, 329, 165, 757, 150, 333, 757, 809, 148, + 151, 290, 164, 165, 166, 473, 392, 165, 819, 260, + 261, 165, 148, 911, 156, 166, 298, 165, 167, 905, + 280, 10, 11, 12, 275, 276, 277, 278, 279, 280, + 298, 167, 795, 796, 285, 151, 298, 164, 165, 166, + 147, 141, 1007, 150, 355, 148, 6, 329, 8, 147, + 166, 333, 280, 939, 759, 155, 144, 145, 146, 165, + 160, 329, 64, 65, 167, 333, 148, 329, 13, 320, + 15, 333, 17, 148, 148, 64, 160, 161, 162, 148, + 164, 165, 166, 343, 647, 167, 165, 76, 77, 165, + 148, 148, 167, 167, 165, 921, 776, 148, 167, 147, + 165, 149, 150, 151, 148, 356, 165, 918, 850, 167, + 167, 148, 165, 1003, 792, 60, 167, 646, 275, 276, + 277, 278, 279, 167, 113, 114, 165, 116, 166, 167, + 167, 165, 121, 696, 123, 165, 147, 165, 149, 902, + 151, 392, 902, 1108, 1002, 613, 147, 147, 149, 149, + 151, 151, 156, 299, 300, 165, 166, 167, 165, 305, + 165, 412, 165, 166, 167, 633, 634, 635, 636, 637, + 1345, 165, 850, 10, 11, 12, 13, 166, 989, 165, + 17, 165, 166, 1009, 165, 166, 167, 165, 33, 1015, + 1016, 166, 167, 482, 445, 165, 141, 150, 449, 450, + 451, 452, 453, 165, 166, 465, 466, 114, 115, 116, + 155, 165, 463, 165, 525, 160, 467, 468, 147, 148, + 165, 472, 165, 60, 475, 536, 165, 166, 539, 165, + 512, 10, 11, 12, 165, 495, 150, 465, 466, 165, + 1098, 709, 150, 1101, 495, 150, 165, 165, 1011, 1012, + 501, 167, 165, 165, 165, 10, 11, 12, 13, 151, + 15, 512, 17, 160, 524, 166, 166, 495, 150, 148, + 48, 165, 147, 156, 525, 112, 113, 528, 1104, 165, + 165, 165, 149, 1261, 148, 64, 167, 646, 445, 15, + 168, 167, 449, 450, 451, 452, 453, 76, 77, 167, + 157, 167, 646, 157, 141, 60, 463, 167, 167, 646, + 467, 468, 167, 148, 877, 160, 161, 162, 155, 164, + 165, 166, 148, 160, 167, 167, 794, 167, 165, 167, + 590, 167, 157, 167, 113, 114, 167, 116, 589, 590, + 591, 157, 121, 811, 123, 167, 167, 493, 167, 167, + 601, 611, 167, 167, 167, 167, 167, 608, 113, 619, + 167, 612, 590, 167, 646, 675, 617, 167, 167, 167, + 621, 622, 623, 624, 625, 626, 627, 1330, 646, 167, + 167, 663, 165, 611, 646, 167, 141, 638, 167, 160, + 167, 619, 166, 166, 150, 663, 647, 648, 167, 167, + 155, 663, 168, 714, 165, 160, 147, 165, 719, 148, + 165, 148, 723, 148, 725, 726, 727, 728, 729, 887, + 148, 703, 149, 148, 148, 165, 894, 895, 896, 897, + 898, 150, 10, 11, 12, 13, 165, 748, 749, 17, + 751, 752, 753, 754, 601, 696, 167, 1273, 165, 1275, + 165, 761, 762, 167, 167, 165, 48, 717, 709, 769, + 720, 771, 1440, 1441, 1442, 160, 717, 2, 160, 720, + 759, 170, 723, 1438, 725, 726, 727, 728, 729, 147, + 1338, 48, 60, 13, 165, 15, 76, 17, 165, 717, + 156, 742, 720, 775, 745, 156, 156, 1355, 1356, 1357, + 156, 10, 167, 10, 10, 756, 757, 775, 10, 1335, + 888, 1253, 888, 775, 10, 10, 149, 157, 148, 160, + 149, 56, 57, 58, 59, 60, 160, 888, 63, 167, + 60, 167, 792, 168, 472, 113, 149, 149, 160, 685, + 686, 167, 793, 167, 795, 796, 167, 167, 799, 165, + 147, 802, 10, 11, 12, 170, 165, 165, 809, 165, + 167, 166, 872, 141, 792, 166, 166, 1093, 819, 150, + 165, 1549, 1430, 167, 165, 1253, 148, 155, 829, 1437, + 1558, 151, 160, 148, 148, 1443, 1444, 165, 148, 148, + 850, 160, 167, 167, 905, 166, 149, 149, 148, 10, + 911, 167, 1428, 4, 148, 168, 64, 889, 170, 48, + 168, 141, 148, 170, 165, 170, 165, 157, 76, 77, + 147, 167, 850, 157, 1092, 155, 877, 10, 939, 880, + 160, 157, 157, 10, 167, 165, 887, 149, 167, 167, + 10, 1499, 893, 894, 895, 896, 897, 898, 899, 1035, + 167, 902, 10, 10, 10, 113, 114, 192, 116, 149, + 911, 921, 148, 121, 165, 123, 170, 918, 167, 167, + 921, 1497, 1004, 170, 612, 1533, 10, 11, 12, 13, + 22, 23, 933, 17, 622, 623, 624, 625, 626, 168, + 1548, 33, 150, 921, 168, 167, 167, 15, 165, 165, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 1003, 257, 1286, 1110, 1111, 60, 262, 165, 167, + 165, 167, 148, 167, 149, 1003, 167, 165, 989, 156, + 165, 1003, 888, 156, 156, 167, 156, 168, 168, 1009, + 147, 1002, 10, 1004, 149, 1015, 1016, 149, 1009, 167, + 1011, 1012, 167, 298, 1015, 1016, 10, 167, 167, 167, + 10, 709, 10, 149, 149, 149, 165, 167, 15, 113, + 167, 1009, 168, 148, 1035, 167, 148, 1015, 1016, 167, + 157, 157, 327, 157, 167, 1321, 1322, 1323, 1324, 1325, + 168, 1327, 157, 10, 167, 149, 167, 141, 167, 10, + 10, 149, 165, 61, 165, 165, 158, 159, 160, 161, + 162, 155, 164, 165, 166, 149, 160, 167, 165, 149, + 10, 167, 1083, 149, 18, 19, 20, 21, 22, 23, + 167, 1092, 167, 149, 1104, 310, 1109, 1098, 649, 33, + 1101, 1256, 1330, 1104, 389, 793, 333, 889, 1457, 1110, + 1111, 1253, 703, 1083, 802, 850, 1287, 1220, 411, 333, + -1, 406, 407, -1, -1, -1, 1104, -1, 413, 1405, + 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1415, + 1416, 1417, 1418, 1419, 1420, 1421, 1422, 1423, -1, -1, + -1, -1, 150, -1, -1, -1, -1, 155, -1, 157, + -1, 159, -1, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, -1, -1, + 1262, -1, -1, 10, 11, 12, -1, -1, 186, -1, + 475, -1, 190, 191, 192, 1256, -1, 1473, -1, 887, + -1, 20, 21, 22, 23, -1, 894, 895, 896, 897, + 898, -1, -1, -1, 33, -1, 10, 11, 12, 1220, + 154, 155, 156, 157, 158, 159, 160, 161, 162, -1, + 164, 165, 166, -1, -1, -1, -1, 64, 523, -1, + -1, -1, -1, 1253, -1, -1, -1, -1, 533, 76, + 77, -1, 1253, 1529, -1, -1, -1, -1, 62, -1, + 1261, 1262, -1, 1273, -1, 1275, 1326, -1, -1, 1330, + 64, -1, 1273, 271, 1275, 1253, -1, -1, -1, -1, + -1, 85, 76, 77, -1, 1286, 113, 114, -1, 116, + -1, 1278, 1279, 1280, 121, 1273, 123, 1275, -1, -1, + -1, 105, -1, -1, 10, 11, 12, -1, -1, 594, + -1, -1, -1, 1373, 1374, -1, 1376, -1, 1378, 113, + 114, 125, 116, 608, 151, 1335, -1, 121, 155, 123, + -1, 135, -1, 160, 1335, -1, -1, 1338, -1, 158, + 159, 160, 161, 162, 1345, 164, 165, 166, -1, 11, + -1, -1, -1, 147, 1355, 1356, 1357, 1335, 64, -1, + -1, 646, -1, -1, 1424, 10, 11, 12, -1, -1, + 76, 77, -1, 1360, -1, 1362, 180, 1364, -1, 183, + 1367, 1368, 1369, -1, -1, -1, 671, 672, 673, 674, + -1, 676, 54, 391, 56, 57, 58, -1, -1, -1, + -1, -1, -1, -1, 1092, -1, -1, 113, 114, -1, + 116, -1, -1, -1, -1, 121, -1, 123, 1428, 64, + -1, -1, 84, -1, 228, 229, -1, 1428, -1, 1430, + -1, 76, 77, -1, -1, -1, 1437, -1, 100, 1440, + 1441, 1442, 1443, 1444, -1, -1, -1, -1, -1, 155, + 1428, -1, -1, -1, 160, -1, 1457, -1, 120, 121, + 745, 17, 18, 19, 20, 21, 22, 23, 113, 114, + 274, 116, -1, -1, -1, 137, 121, 33, 123, -1, + 142, -1, 1542, 1543, 1544, -1, 1546, 1497, 292, -1, + 294, -1, -1, -1, -1, -1, 1497, -1, 1499, -1, + -1, -1, 147, -1, -1, -1, -1, 311, -1, 313, + 314, 315, 316, 317, 318, -1, -1, -1, -1, 1497, + -1, -1, 520, -1, 809, -1, -1, -1, -1, 527, + -1, 335, 1533, -1, 819, -1, -1, -1, 823, -1, + 344, -1, -1, 347, -1, -1, -1, 1548, 1549, -1, + -1, 355, -1, -1, -1, 840, 554, 1558, 556, -1, + 558, -1, -1, -1, -1, -1, 851, 852, 853, 854, + 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, + 865, 866, 867, 868, 869, 870, 871, -1, 873, -1, + 394, -1, -1, -1, 398, -1, -1, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 411, 164, 165, + 166, 16, 17, 18, 19, 20, 21, 22, 23, -1, + -1, -1, -1, 1321, 1322, 1323, 1324, 1325, 33, 1327, + 22, 23, -1, 918, -1, -1, -1, -1, -1, -1, + -1, 33, -1, -1, 448, -1, -1, -1, 933, 20, + 21, 22, 23, -1, -1, -1, -1, -1, -1, -1, + 464, -1, 33, -1, -1, -1, 18, 19, 20, 21, + 22, 23, 476, -1, -1, -1, -1, 675, -1, -1, + -1, 33, 486, -1, -1, -1, -1, -1, -1, -1, + 494, 33, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 989, -1, 991, 1405, 1406, 1407, + 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1415, 1416, 1417, + 1418, 1419, 1420, 1421, 1422, 1423, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 539, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 1043, 164, + 165, 166, -1, -1, -1, 170, 158, 159, 160, 161, + 162, -1, 164, 165, 166, 1473, -1, -1, -1, -1, + -1, -1, -1, -1, 588, 156, 157, 158, 159, 160, + 161, 162, -1, 164, 165, 166, 3, 4, 5, 6, + -1, 8, 9, 10, 11, 12, 158, 159, 160, 161, + 162, -1, 164, 165, 166, -1, 158, 159, 160, 161, + 162, -1, 164, 165, 166, -1, -1, 631, -1, -1, + -1, 1529, -1, -1, 832, 833, 834, -1, 836, -1, + 838, 839, 840, 50, 51, 649, -1, 54, -1, 56, + 57, 58, 59, 60, -1, 62, 63, -1, -1, 66, + 67, -1, -1, 70, -1, -1, -1, -1, 75, 76, + 77, -1, -1, -1, 81, -1, -1, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, -1, -1, -1, -1, -1, -1, + -1, -1, 109, 707, 111, -1, 113, -1, -1, -1, + -1, 118, -1, 120, 121, 122, -1, -1, 125, 126, + -1, -1, -1, 130, -1, -1, 133, 134, 135, 136, + 137, -1, -1, 140, 738, 142, -1, -1, -1, 3, + 4, 5, 6, -1, 8, 9, 10, 11, 155, -1, + -1, 158, 159, 160, -1, -1, 163, -1, 165, 166, + -1, 1246, 169, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 776, -1, 16, 17, 18, 19, 20, 21, + 22, 23, -1, -1, -1, -1, 50, 51, -1, -1, + 54, 33, 56, 57, 58, 59, -1, -1, 62, 63, + -1, -1, 66, 67, -1, -1, 70, -1, -1, -1, + -1, 75, 76, 77, -1, 1300, -1, 81, -1, -1, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, -1, -1, -1, + -1, -1, -1, -1, -1, 109, -1, 111, -1, 113, + -1, -1, -1, -1, 118, -1, 120, 121, 122, -1, + 1345, 125, 126, -1, -1, -1, 130, -1, -1, 133, + 134, 135, 136, 137, -1, -1, 140, -1, 142, -1, + -1, -1, 3, 4, 5, 6, 890, 8, 9, 10, + 11, 155, -1, 1091, 158, 159, 160, -1, -1, 163, + -1, 165, 166, -1, -1, 169, -1, -1, -1, -1, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, -1, 164, 165, 166, -1, -1, -1, 170, 50, + -1, -1, -1, 54, -1, 56, 57, 58, -1, -1, + 1425, 62, -1, 64, 65, 66, 67, -1, -1, 33, + -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, + 81, -1, -1, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + -1, -1, -1, -1, -1, -1, -1, -1, 109, -1, + 111, -1, 113, -1, -1, -1, -1, 118, -1, 120, + 121, 122, 1006, -1, 125, 10, 11, -1, -1, 130, + -1, -1, 133, -1, -1, -1, 137, -1, -1, -1, + -1, 142, -1, -1, -1, 54, 147, 56, 57, 58, + -1, -1, -1, 1037, 155, -1, -1, 158, 159, 160, + -1, -1, 163, 17, 165, 166, 51, -1, 169, 54, + -1, 56, 57, 58, 59, 84, -1, -1, 63, 33, + -1, 66, -1, -1, -1, 70, -1, -1, -1, -1, + -1, 100, 77, 47, 158, 159, 160, 161, 162, 84, + 164, 165, 166, -1, -1, -1, 60, 61, -1, -1, + -1, 120, 121, -1, 54, 100, 56, 57, 58, -1, + -1, -1, 76, -1, -1, 1109, 66, 112, 137, -1, + -1, -1, -1, 142, -1, 120, 121, -1, -1, -1, + -1, 126, -1, -1, 84, -1, -1, -1, 1326, 134, + 135, 136, 137, 107, -1, 140, 110, 142, -1, 113, + 100, -1, 147, -1, -1, 16, 17, 18, 19, 20, + 21, 22, 23, -1, -1, 129, -1, -1, 163, -1, + 120, 121, 33, -1, -1, -1, -1, 141, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 137, -1, -1, + -1, 155, 142, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1390, 1391, 1392, 1393, -1, 1395, 1396, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, -1, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, -1, -1, -1, -1, -1, -1, 1440, 33, + 24, 25, -1, -1, -1, -1, -1, -1, -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 49, -1, -1, 52, 53, + 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, -1, 68, -1, 70, 71, 72, 73, - 74, 75, 76, 77, 78, -1, 80, 81, 82, -1, - 155, 156, 157, 158, 159, -1, 161, 162, 163, 805, - -1, -1, -1, -1, 98, -1, -1, -1, -1, -1, - 104, 105, 106, -1, 108, 109, 110, 111, 112, 113, + 64, 65, 66, 67, 68, -1, 70, -1, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 1281, 82, 83, + 84, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, -1, 164, 165, 166, 100, -1, -1, 170, + -1, -1, -1, 107, 108, 109, -1, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, - 134, 135, 136, 137, 138, 139, 140, -1, -1, -1, - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + -1, -1, -1, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, - 164, 165, 166, 167, -1, -1, -1, -1, -1, 52, - -1, 54, 55, 56, -1, -1, -1, -1, -1, -1, - -1, 64, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, + 164, 165, 166, 167, 168, 169, 170, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, -1, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + -1, -1, -1, -1, -1, -1, -1, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, -1, -1, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, -1, 70, -1, 72, 73, 74, 75, + 76, 77, 78, 79, 80, -1, 82, 83, 84, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 98, 1164, -1, -1, -1, - -1, -1, -1, 16, 17, 18, 19, 20, 21, 22, - 23, -1, -1, -1, 117, 118, -1, -1, -1, -1, - 33, -1, -1, -1, -1, 961, -1, -1, -1, -1, - -1, 134, -1, 969, 970, 971, 139, 973, -1, 975, - 976, 977, -1, 1229, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 1223, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, -1, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, - -1, -1, -1, -1, -1, -1, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, - 47, 48, 49, -1, -1, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, - -1, 68, -1, 70, 71, 72, 73, 74, 75, 76, - 77, 78, -1, 80, 81, 82, 149, 150, 151, 152, - 153, 154, 155, 156, 157, 158, 159, -1, 161, 162, - 163, 98, -1, 166, -1, -1, -1, 104, 105, 106, - -1, 108, 109, 110, 111, 112, 113, 114, 115, 116, - 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, - 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, - 137, 138, 139, 140, -1, -1, 1382, 144, 145, 146, - 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, - 157, 158, 159, 160, 161, 162, 163, 164, -1, 166, - 167, -1, -1, 3, 4, 5, 6, -1, 8, 9, - 10, 11, 12, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 100, 1469, -1, -1, -1, -1, + -1, 107, 108, 109, -1, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, -1, -1, + -1, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, -1, 169, 170, 3, 4, 5, 6, -1, + 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 10, 11, 12, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, - -1, -1, 52, -1, 54, 55, 56, 57, 58, -1, - 60, 61, -1, 1219, 64, 65, -1, -1, 68, -1, - -1, -1, -1, 73, 74, 75, -1, -1, -1, 79, - -1, -1, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, 94, 95, 96, 97, 98, 74, - 75, 17, -1, -1, -1, -1, 106, -1, 108, -1, - 110, -1, -1, -1, -1, 115, -1, 117, 118, 119, - -1, -1, 122, 123, -1, -1, -1, 127, -1, -1, - 130, 131, 132, 133, 134, 110, 111, 137, 113, 139, - -1, -1, 58, 118, -1, 120, -1, -1, 1304, 1305, - 1306, 1307, 152, 1309, 1310, 155, 156, 157, 74, -1, - 160, -1, 162, 163, -1, -1, -1, 167, 3, 4, - 5, 6, 147, 8, 9, 10, 11, -1, 10, 11, - 12, -1, -1, -1, -1, -1, -1, -1, 104, -1, - -1, 107, -1, -1, 110, -1, -1, -1, -1, -1, - -1, 16, 17, 18, 19, 20, 21, 22, 23, -1, - 126, -1, -1, 48, -1, -1, -1, 52, 33, 54, - 55, 56, 138, -1, -1, 60, -1, 62, 63, 64, - 65, -1, -1, -1, -1, -1, 152, -1, 73, 74, - 75, -1, 74, 75, 79, -1, -1, 82, 83, 84, - 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, - 95, 96, 97, 98, -1, 10, 11, 12, -1, -1, - -1, 106, -1, 108, -1, 110, -1, -1, 110, 111, - 115, 113, 117, 118, 119, -1, 118, 122, 120, -1, - -1, -1, 127, -1, -1, 130, -1, -1, -1, 134, - 3, 4, 5, 6, 139, 8, 9, 10, 11, 144, - -1, -1, 144, -1, -1, -1, -1, 152, -1, -1, - 155, 156, 157, -1, -1, 160, -1, 162, 163, 74, - 75, 146, 167, -1, 149, 150, 151, 152, 153, 154, - 155, 156, 157, 158, 159, 48, 161, 162, 163, 52, - -1, 54, 55, 56, -1, -1, -1, 60, -1, -1, - -1, 64, 65, -1, -1, 110, 111, -1, 113, -1, - 73, 74, 75, 118, -1, 120, 79, -1, -1, 82, - 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - 93, 94, 95, 96, 97, 98, -1, -1, -1, 144, - -1, -1, -1, 106, -1, 108, -1, 110, -1, -1, - -1, -1, 115, -1, 117, 118, 119, -1, -1, 122, - -1, -1, -1, -1, 127, -1, -1, 130, -1, -1, - -1, 134, 3, 4, 5, 6, 139, 8, 9, 10, - 11, -1, 10, 11, 12, -1, -1, 15, -1, 152, - -1, -1, 155, 156, 157, -1, -1, 160, -1, 162, - 163, 164, -1, -1, 167, 16, 17, 18, 19, 20, - 21, 22, 23, -1, -1, -1, -1, 48, -1, -1, - -1, 52, 33, 54, 55, 56, -1, -1, -1, 60, - -1, -1, -1, 64, 65, -1, -1, -1, -1, -1, - -1, -1, 73, 74, 75, -1, 74, 75, 79, -1, - -1, 82, 83, 84, 85, 86, 87, 88, 89, 90, - 91, 92, 93, 94, 95, 96, 97, 98, -1, -1, - -1, -1, -1, -1, -1, 106, -1, 108, -1, 110, - -1, -1, 110, 111, 115, 113, 117, 118, 119, -1, - 118, 122, 120, -1, -1, -1, 127, -1, -1, 130, - -1, -1, -1, 134, 3, 4, 5, 6, 139, 8, - 9, 10, 11, 144, 10, 11, 12, -1, -1, -1, - -1, 152, -1, -1, 155, 156, 157, -1, -1, 160, - -1, 162, 163, -1, 145, -1, 167, -1, 149, 150, - 151, 152, 153, 154, 155, 156, 157, 158, 159, 48, - 161, 162, 163, 52, -1, 54, 55, 56, -1, -1, - -1, 60, -1, -1, -1, 64, 65, -1, -1, -1, - -1, -1, -1, -1, 73, 74, 75, -1, 74, 75, - 79, -1, -1, 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, - -1, -1, -1, -1, -1, -1, -1, 106, -1, 108, - -1, 110, -1, -1, 110, 111, 115, 113, 117, 118, - 119, -1, 118, 122, 120, -1, -1, -1, 127, -1, - -1, 130, -1, -1, -1, 134, 3, 4, 5, 6, - 139, 8, 9, 10, 11, -1, -1, -1, -1, -1, - -1, -1, -1, 152, -1, -1, 155, 156, 157, -1, - -1, 160, -1, 162, 163, 164, -1, -1, 167, 16, - 17, 18, 19, 20, 21, 22, 23, -1, -1, -1, - -1, 48, -1, -1, -1, 52, 33, 54, 55, 56, - -1, -1, -1, 60, -1, -1, -1, 64, 65, -1, - -1, -1, -1, -1, -1, -1, 73, 74, 75, -1, - -1, -1, 79, -1, -1, 82, 83, 84, 85, 86, - 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, - 97, 98, -1, 10, 11, 12, -1, -1, -1, 106, - -1, 108, -1, 110, -1, -1, -1, -1, 115, -1, - 117, 118, 119, -1, -1, 122, -1, -1, -1, -1, - 127, -1, -1, 130, -1, -1, -1, 134, 3, 4, - 5, 6, 139, 8, 9, 10, 11, 144, -1, -1, - -1, -1, -1, -1, -1, 152, -1, -1, 155, 156, - 157, -1, -1, 160, -1, 162, 163, 74, 75, -1, - 167, -1, 149, 150, 151, 152, 153, 154, 155, 156, - 157, 158, 159, 48, 161, 162, 163, 52, -1, 54, - 55, 56, -1, -1, -1, 60, -1, -1, 105, 64, - 65, -1, -1, 110, 111, -1, 113, -1, 73, 74, - 75, 118, -1, 120, 79, -1, -1, 82, 83, 84, - 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, - 95, 96, 97, 98, -1, -1, -1, -1, -1, -1, - -1, 106, -1, 108, -1, 110, -1, -1, -1, -1, - 115, -1, 117, 118, 119, -1, -1, 122, -1, -1, - -1, -1, 127, -1, -1, 130, -1, -1, -1, 134, - 3, 4, 5, 6, 139, 8, 9, 10, 11, -1, - -1, -1, -1, -1, -1, -1, -1, 152, -1, -1, - 155, 156, 157, -1, -1, 160, -1, 162, 163, 164, - -1, -1, 167, 20, 21, 22, 23, -1, -1, -1, - -1, -1, -1, -1, -1, 48, 33, -1, -1, 52, - -1, 54, 55, 56, -1, -1, -1, 60, -1, -1, - -1, 64, 65, -1, -1, -1, -1, -1, -1, -1, - 73, 74, 75, -1, -1, -1, 79, -1, -1, 82, - 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - 93, 94, 95, 96, 97, 98, -1, -1, 6, -1, - -1, -1, -1, 106, -1, 108, -1, 110, -1, -1, - -1, -1, 115, -1, 117, 118, 119, -1, -1, 122, - -1, -1, -1, -1, 127, -1, -1, 130, -1, -1, - -1, 134, 3, 4, 5, 6, 139, 8, 9, 10, - 11, 12, -1, 51, 15, -1, -1, -1, -1, 152, - 58, 59, 155, 156, 157, -1, -1, 160, -1, 162, - 163, 69, 70, 71, 167, -1, 153, 154, 155, 156, - 157, 158, 159, 81, 161, 162, 163, 48, 49, -1, - -1, 52, -1, 54, 55, 56, 57, 58, -1, 60, - 61, -1, -1, 64, 65, -1, 104, 68, -1, -1, - -1, -1, 73, 74, 75, -1, 114, -1, -1, -1, - -1, 82, 120, -1, -1, -1, -1, 125, 17, 18, - 19, 20, 21, 22, 23, -1, -1, 98, 136, -1, - 138, -1, -1, -1, 33, 106, -1, 108, -1, 110, - -1, -1, -1, 114, 115, -1, 117, 118, 119, -1, - -1, 122, 123, -1, -1, 163, 127, -1, -1, 130, - 131, 132, 133, 134, -1, -1, 137, -1, 139, -1, - 3, 4, 5, 6, -1, 8, 9, 10, 11, 12, - -1, 152, 15, -1, 155, 156, -1, -1, -1, 160, - -1, 162, -1, -1, -1, -1, 167, -1, -1, -1, - -1, -1, -1, 16, 17, 18, 19, 20, 21, 22, - 23, -1, -1, -1, -1, 48, 49, -1, -1, 52, - 33, 54, 55, 56, 57, 58, -1, 60, 61, -1, - -1, 64, 65, -1, -1, 68, -1, -1, 17, -1, - 73, 74, 75, -1, -1, -1, -1, -1, -1, 82, - -1, 150, 151, 152, 33, -1, 155, 156, 157, 158, - 159, -1, 161, 162, 163, 98, -1, -1, -1, -1, - -1, -1, -1, 106, -1, 108, -1, 110, -1, 58, - -1, 114, 115, -1, 117, 118, 119, -1, -1, 122, - 123, -1, -1, -1, 127, 74, -1, 130, 131, 132, - 133, 134, -1, -1, 137, -1, 139, 3, 4, 5, - 6, -1, 8, 9, 10, -1, -1, -1, -1, 152, - -1, -1, 155, 156, -1, 104, -1, 160, 107, 162, - -1, 110, -1, -1, 167, -1, 149, 150, 151, 152, - 153, 154, 155, 156, 157, 158, 159, 126, 161, 162, - 163, -1, 48, 166, -1, -1, -1, -1, -1, 138, - -1, -1, -1, -1, 60, -1, -1, -1, -1, 65, - -1, -1, -1, 152, 0, -1, -1, 73, 74, -1, - -1, -1, -1, 79, -1, -1, -1, 83, 84, 85, - 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 108, -1, 110, -1, -1, -1, -1, 115, - -1, -1, -1, 119, 50, 51, 122, -1, -1, -1, - -1, 127, 58, 59, 18, 19, 20, 21, 22, 23, - -1, 67, -1, 69, 70, 71, 72, -1, -1, 33, - 76, -1, -1, -1, -1, 81, 152, -1, -1, 155, - 156, 157, -1, -1, 160, -1, 162, 163, -1, -1, - -1, 167, -1, -1, 100, 101, 102, 103, 104, 105, - -1, -1, -1, -1, -1, 111, 112, 113, 114, 11, - -1, -1, -1, -1, 120, 121, -1, -1, 124, 125, - -1, -1, -1, 129, -1, -1, -1, -1, -1, 135, - 136, 6, 138, -1, -1, -1, -1, -1, -1, -1, - 146, 16, 17, 18, 19, 20, 21, 22, 23, -1, - 52, -1, 54, 55, 56, -1, -1, 163, 33, -1, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, -1, -1, -1, -1, -1, -1, -1, -1, - 82, -1, -1, -1, -1, -1, -1, -1, 63, 153, - 154, 155, 156, 157, 158, 159, 98, 161, 162, 163, - -1, -1, 10, 11, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 117, 118, -1, -1, -1, - -1, 16, 17, 18, 19, 20, 21, 22, 23, -1, - -1, 106, 134, -1, -1, -1, -1, 139, 33, -1, - -1, 49, -1, -1, 52, -1, 54, 55, 56, 57, - -1, -1, -1, 61, -1, -1, 64, -1, -1, -1, - 68, -1, -1, -1, -1, -1, -1, 75, -1, -1, - 145, -1, -1, 148, 82, 150, 151, 152, 153, 154, - 155, 156, 157, 158, 159, 160, -1, 162, 163, -1, - 98, -1, 167, 10, 11, -1, -1, -1, -1, -1, - -1, 109, -1, -1, -1, -1, -1, -1, -1, 117, - 118, -1, -1, -1, -1, 123, -1, -1, -1, -1, - -1, -1, -1, 131, 132, 133, 134, -1, -1, 137, - -1, 139, 49, 10, 11, 52, 144, 54, 55, 56, - 57, -1, -1, -1, 61, -1, -1, 64, -1, -1, - -1, 68, 160, -1, 149, 150, 151, 152, 75, -1, - 155, 156, 157, 158, 159, 82, 161, 162, 163, -1, - -1, -1, 49, -1, -1, 52, -1, 54, 55, 56, - 57, 98, -1, -1, 61, -1, -1, 64, -1, -1, - -1, 68, 109, -1, -1, -1, -1, -1, 75, -1, - 117, 118, -1, -1, -1, 82, 123, 10, 11, 12, - -1, -1, -1, -1, 131, 132, 133, 134, -1, -1, - 137, 98, 139, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 109, -1, -1, -1, -1, -1, -1, -1, - 117, 118, -1, 160, -1, -1, 123, -1, -1, 52, - -1, 54, 55, 56, 131, 132, 133, 134, -1, -1, - 137, 64, 139, 16, 17, 18, 19, 20, 21, 22, - 23, -1, 75, -1, -1, -1, -1, -1, -1, 82, - 33, -1, -1, 160, 16, 17, 18, 19, 20, 21, - 22, 23, -1, -1, -1, 98, -1, -1, -1, -1, - -1, 33, 16, 17, 18, 19, 20, 21, 22, 23, - -1, -1, -1, -1, 117, 118, -1, -1, -1, 33, - 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, - -1, 134, -1, -1, 137, -1, 139, 33, 16, 17, - 18, 19, 20, 21, 22, 23, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 33, 16, 17, 18, 19, + -1, -1, -1, -1, -1, -1, 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 33, 16, 17, 18, 19, 20, 21, + -1, -1, 50, 33, -1, -1, 54, -1, 56, 57, + 58, -1, -1, -1, 62, -1, -1, -1, 66, 67, + -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, + -1, -1, -1, 81, -1, -1, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, -1, -1, -1, -1, -1, -1, -1, + -1, 109, -1, 111, -1, 113, -1, -1, -1, -1, + 118, -1, 120, 121, 122, -1, -1, 125, -1, -1, + -1, -1, 130, -1, -1, 133, -1, -1, -1, 137, + 3, 4, 5, 6, 142, 8, 9, 10, 11, -1, + -1, -1, -1, -1, -1, -1, -1, 155, -1, -1, + 158, 159, 160, -1, -1, 163, -1, 165, 166, 167, + 150, 169, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, -1, 164, 165, 166, 50, 51, -1, + -1, 54, -1, 56, 57, 58, -1, -1, -1, 62, + -1, -1, -1, 66, 67, -1, -1, -1, -1, -1, + -1, -1, 75, 76, 77, -1, -1, -1, 81, -1, + -1, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, -1, -1, + -1, -1, -1, -1, -1, -1, 109, -1, 111, -1, + 113, -1, -1, -1, -1, 118, -1, 120, 121, 122, + -1, -1, 125, -1, -1, -1, -1, 130, -1, -1, + 133, -1, -1, -1, 137, 3, 4, 5, 6, 142, + 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, + -1, -1, 155, -1, -1, 158, 159, 160, -1, -1, + 163, -1, 165, 166, -1, -1, 169, -1, -1, -1, + 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, + -1, -1, 50, 51, -1, -1, 54, 33, 56, 57, + 58, -1, -1, -1, 62, -1, -1, -1, 66, 67, + -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, + -1, -1, -1, 81, -1, -1, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, -1, -1, -1, -1, -1, -1, -1, + -1, 109, -1, 111, -1, 113, -1, -1, -1, -1, + 118, -1, 120, 121, 122, -1, -1, 125, -1, -1, + -1, -1, 130, -1, -1, 133, -1, -1, -1, 137, + 3, 4, 5, 6, 142, 8, 9, 10, 11, -1, + -1, -1, -1, -1, -1, -1, -1, 155, -1, -1, + 158, 159, 160, -1, -1, 163, -1, 165, 166, -1, + -1, 169, -1, 149, -1, -1, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 50, 164, 165, + 166, 54, -1, 56, 57, 58, -1, -1, -1, 62, + -1, -1, -1, 66, 67, -1, -1, -1, -1, -1, + -1, -1, 75, 76, 77, -1, -1, -1, 81, -1, + -1, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, -1, -1, + -1, -1, -1, -1, -1, -1, 109, -1, 111, -1, + 113, -1, -1, -1, -1, 118, -1, 120, 121, 122, + -1, -1, 125, -1, -1, -1, -1, 130, -1, -1, + 133, -1, -1, -1, 137, 3, 4, 5, 6, 142, + 8, 9, 10, 11, 147, -1, -1, -1, -1, -1, + -1, -1, 155, -1, -1, 158, 159, 160, -1, -1, + 163, -1, 165, 166, -1, -1, 169, -1, -1, -1, + 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, + -1, -1, 50, -1, -1, -1, 54, 33, 56, 57, + 58, -1, -1, -1, 62, -1, -1, -1, 66, 67, + -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, + -1, -1, -1, 81, -1, -1, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, -1, -1, -1, -1, -1, -1, -1, + -1, 109, -1, 111, -1, 113, -1, -1, -1, -1, + 118, -1, 120, 121, 122, -1, -1, 125, -1, -1, + -1, -1, 130, -1, -1, 133, -1, -1, -1, 137, + 3, 4, 5, 6, 142, 8, 9, 10, 11, -1, + -1, -1, -1, -1, -1, -1, -1, 155, -1, -1, + 158, 159, 160, -1, -1, 163, -1, 165, 166, 167, + -1, 169, 148, -1, -1, -1, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 50, 164, 165, + 166, 54, -1, 56, 57, 58, -1, -1, -1, 62, + -1, -1, -1, 66, 67, -1, -1, -1, -1, -1, + -1, -1, 75, 76, 77, -1, -1, -1, 81, -1, + -1, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, -1, -1, + -1, -1, -1, -1, -1, -1, 109, -1, 111, -1, + 113, -1, -1, -1, -1, 118, -1, 120, 121, 122, + -1, -1, 125, -1, -1, -1, -1, 130, -1, -1, + 133, -1, -1, -1, 137, 3, 4, 5, 6, 142, + 8, 9, 10, 11, 147, -1, -1, -1, -1, -1, + -1, -1, 155, -1, -1, 158, 159, 160, -1, -1, + 163, -1, 165, 166, -1, -1, 169, -1, -1, -1, + 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, + -1, -1, 50, -1, -1, -1, 54, 33, 56, 57, + 58, -1, -1, -1, 62, -1, -1, -1, 66, 67, + -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, + -1, -1, -1, 81, -1, -1, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, -1, -1, -1, -1, -1, -1, -1, + -1, 109, -1, 111, -1, 113, -1, -1, -1, -1, + 118, -1, 120, 121, 122, -1, -1, 125, -1, -1, + -1, -1, 130, -1, -1, 133, -1, -1, -1, 137, + 3, 4, 5, 6, 142, 8, 9, 10, 11, -1, + -1, -1, -1, -1, -1, -1, -1, 155, -1, -1, + 158, 159, 160, -1, -1, 163, -1, 165, 166, 167, + -1, 169, -1, -1, 150, -1, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 50, 164, 165, + 166, 54, -1, 56, 57, 58, -1, -1, -1, 62, + -1, -1, -1, 66, 67, -1, -1, -1, -1, -1, + -1, -1, 75, 76, 77, -1, -1, -1, 81, -1, + -1, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, -1, -1, + -1, -1, -1, -1, -1, -1, 109, -1, 111, -1, + 113, -1, -1, -1, -1, 118, -1, 120, 121, 122, + -1, -1, 125, -1, -1, -1, -1, 130, -1, -1, + 133, -1, -1, -1, 137, 3, 4, 5, 6, 142, + 8, 9, 10, 11, 12, -1, -1, 15, -1, -1, + -1, -1, 155, -1, -1, 158, 159, 160, -1, -1, + 163, -1, 165, 166, -1, -1, 169, 16, 17, 18, + 19, 20, 21, 22, 23, -1, -1, -1, -1, 47, + -1, -1, 50, 51, 33, -1, 54, -1, 56, 57, + 58, 59, 60, -1, 62, 63, -1, -1, 66, 67, + -1, -1, 70, -1, -1, -1, -1, 75, 76, 77, + -1, -1, -1, -1, -1, -1, 84, -1, -1, -1, + -1, -1, 16, 17, 18, 19, 20, 21, 22, 23, + -1, -1, 100, -1, -1, -1, -1, -1, -1, 33, + -1, 109, -1, 111, -1, 113, -1, -1, -1, 117, + 118, -1, 120, 121, 122, -1, -1, 125, 126, -1, + -1, -1, 130, -1, -1, 133, 134, 135, 136, 137, + -1, -1, 140, -1, 142, -1, 3, 4, 5, 6, + -1, 8, 9, 10, 11, 12, -1, 155, 15, -1, + 158, 159, -1, -1, -1, 163, -1, 165, -1, 148, + -1, 169, -1, 152, 153, 154, 155, 156, 157, 158, + 159, 160, 161, 162, -1, 164, 165, 166, 167, -1, + 47, -1, -1, 50, 51, -1, -1, 54, -1, 56, + 57, 58, 59, 60, -1, 62, 63, -1, -1, 66, + 67, -1, -1, 70, -1, -1, -1, -1, 75, 76, + 77, -1, -1, 17, -1, -1, -1, 84, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, -1, + 164, 165, 166, 100, -1, -1, -1, -1, -1, -1, + -1, -1, 109, 47, 111, -1, 113, -1, -1, -1, + 117, 118, -1, 120, 121, 122, 60, 61, 125, 126, + -1, -1, -1, 130, -1, -1, 133, 134, 135, 136, + 137, -1, 76, 140, -1, 142, 3, 4, 5, 6, + -1, 8, 9, 10, -1, -1, -1, -1, 155, -1, + -1, 158, 159, -1, -1, -1, 163, -1, 165, -1, + -1, -1, 169, 107, -1, -1, 110, -1, -1, 113, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 50, -1, 129, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 62, -1, 141, -1, -1, + 67, 17, 18, 19, 20, 21, 22, 23, 75, 76, + -1, 155, -1, -1, 81, -1, -1, 33, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 16, 17, 18, 19, 20, 21, 22, + 23, -1, -1, -1, 111, -1, 113, -1, -1, -1, + 33, 118, -1, -1, -1, 122, -1, -1, 125, -1, + -1, -1, -1, 130, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 155, -1, + -1, 158, 159, 160, 6, -1, 163, -1, 165, 166, + -1, -1, 169, -1, 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 33, -1, -1, -1, -1, 149, 150, 151, 152, - 153, 154, 155, 156, 157, 158, 159, -1, 161, 162, - 163, -1, -1, 166, -1, -1, -1, 149, 150, 151, - 152, 153, 154, 155, 156, 157, 158, 159, -1, 161, - 162, 163, 164, -1, -1, 149, 150, 151, 152, 153, - 154, 155, 156, 157, 158, 159, -1, 161, 162, 163, - 164, -1, -1, 149, 150, 151, 152, 153, 154, 155, - 156, 157, 158, 159, -1, 161, 162, 163, 164, -1, - -1, 149, 150, 151, 152, 153, 154, 155, 156, 157, - 158, 159, -1, 161, 162, 163, 164, -1, -1, 149, - 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - -1, 161, 162, 163, 164, 147, -1, 149, 150, 151, - 152, 153, 154, 155, 156, 157, 158, 159, -1, 161, - 162, 163, 16, 17, 18, 19, 20, 21, 22, 23, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, + -1, 33, -1, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, -1, -1, 153, 154, 155, + -1, -1, 158, 159, 160, 161, 162, -1, 164, 165, + 166, -1, -1, 65, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 10, 11, -1, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + -1, 164, 165, 166, 167, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 109, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 51, -1, -1, + 54, -1, 56, 57, 58, 59, -1, -1, -1, 63, + -1, -1, 66, -1, -1, -1, 70, -1, -1, -1, + -1, -1, -1, 77, -1, -1, 148, -1, -1, 151, + 84, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, -1, 165, 166, -1, 100, 169, 10, 11, + -1, -1, -1, -1, -1, -1, -1, -1, 112, -1, + -1, -1, -1, -1, -1, -1, 120, 121, -1, -1, + -1, -1, 126, -1, -1, -1, -1, -1, -1, -1, + 134, 135, 136, 137, -1, -1, 140, -1, 142, 51, + -1, -1, 54, -1, 56, 57, 58, 59, -1, -1, + -1, 63, -1, -1, 66, -1, -1, -1, 70, 163, + -1, -1, -1, -1, -1, 77, -1, -1, -1, -1, + -1, -1, 84, 16, 17, 18, 19, 20, 21, 22, + 23, -1, -1, -1, -1, -1, -1, -1, 100, -1, + 33, -1, -1, -1, -1, 10, 11, 12, -1, -1, + 112, -1, -1, -1, -1, -1, -1, -1, 120, 121, + -1, -1, -1, -1, 126, -1, -1, -1, -1, -1, + -1, -1, 134, 135, 136, 137, -1, -1, 140, -1, + 142, 16, 17, 18, 19, 20, 21, 22, 23, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 33, 64, + -1, 163, 16, 17, 18, 19, 20, 21, 22, 23, + -1, 76, 77, -1, -1, -1, -1, -1, -1, 33, 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, 16, 17, - 18, 19, 20, 21, 22, 23, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 33, -1, -1, -1, -1, - -1, 18, 19, 20, 21, 22, 23, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 33, 18, 19, 20, + 18, 19, 20, 21, 22, 23, -1, -1, 113, 114, + -1, 116, -1, -1, -1, 33, 121, -1, 123, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + -1, 164, 165, 166, 167, 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 33, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 18, 19, - 20, 21, 22, 23, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 33, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 147, -1, 149, 150, 151, 152, 153, - 154, 155, 156, 157, 158, 159, -1, 161, 162, 163, - -1, -1, -1, 149, 150, 151, 152, 153, 154, 155, - 156, 157, 158, 159, -1, 161, 162, 163, -1, 147, - -1, 149, 150, 151, 152, -1, -1, 155, 156, 157, - 158, 159, -1, 161, 162, 163, 11, 12, -1, -1, - 15, -1, 17, 150, 151, 152, 153, 154, 155, 156, - 157, 158, 159, -1, 161, 162, 163, -1, -1, -1, - -1, 152, 153, 154, 155, 156, 157, 158, 159, -1, - 161, 162, 163, -1, 49, -1, -1, 52, -1, 54, - 55, 56, 57, 58, -1, -1, 61, -1, -1, 64, - 150, 151, 152, 68, -1, 155, 156, 157, 158, 159, - 75, 161, 162, 163, -1, -1, -1, 82, -1, 18, - 19, 20, 21, 22, 23, -1, 10, 11, 12, -1, - -1, -1, -1, 98, 33, 18, 19, 20, 21, 22, - 23, -1, -1, -1, -1, -1, -1, -1, -1, 114, - 33, -1, 117, 118, -1, -1, -1, -1, 123, -1, - 18, 19, 20, 21, 22, 23, 131, 132, 133, 134, - -1, -1, 137, 57, 139, 33, -1, -1, -1, 52, - -1, 54, 55, 56, -1, -1, -1, 152, -1, -1, - 74, 75, 157, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, + -1, -1, 33, 16, 17, 18, 19, 20, 21, 22, + 23, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 33, -1, -1, -1, -1, -1, -1, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, -1, 164, + 165, 166, 167, -1, -1, -1, -1, -1, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, -1, + 164, 165, 166, 167, -1, -1, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, -1, 164, 165, + 166, 167, -1, -1, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, -1, 164, 165, 166, 167, + 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 33, -1, -1, + -1, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, -1, 164, 165, 166, 167, -1, -1, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + -1, 164, 165, 166, 16, 17, 18, 19, 20, 21, + 22, 23, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 98, 110, 111, -1, 113, - -1, -1, -1, -1, 118, -1, 120, -1, -1, 123, - -1, 10, 11, 12, 117, 118, -1, 131, -1, 133, - -1, -1, 151, 152, -1, -1, 155, 156, 157, 158, - 159, 134, 161, 162, 163, -1, 139, -1, -1, 152, - -1, -1, 155, 156, 157, 158, 159, -1, 161, 162, - 163, -1, -1, 52, -1, 54, 55, 56, 57, 58, - 11, 12, -1, -1, 15, 64, -1, 155, 156, 157, - 158, 159, -1, 161, 162, 163, 75, -1, -1, -1, - -1, -1, -1, 82, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 49, 98, - -1, 52, -1, 54, 55, 56, 57, 58, -1, -1, - 61, -1, -1, 64, -1, -1, -1, 68, 117, 118, - -1, -1, -1, -1, 75, -1, -1, -1, -1, -1, - -1, 82, 131, -1, -1, 134, -1, -1, 137, -1, - 139, -1, -1, -1, -1, -1, -1, 98, -1, 11, - 12, -1, -1, 15, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 114, -1, -1, 117, 118, -1, -1, - -1, -1, 123, -1, -1, -1, -1, -1, 11, 12, - 131, 132, 133, 134, -1, -1, 137, 49, 139, -1, - 52, -1, 54, 55, 56, 57, 58, -1, -1, 61, - -1, -1, 64, -1, -1, -1, 68, -1, -1, -1, - -1, -1, -1, 75, -1, -1, 49, -1, -1, 52, - 82, 54, 55, 56, 57, 58, -1, -1, 61, -1, - -1, 64, -1, -1, -1, 68, 98, -1, -1, -1, - -1, -1, 75, -1, -1, -1, -1, -1, -1, 82, - -1, -1, 114, -1, -1, 117, 118, -1, -1, -1, - -1, 123, -1, -1, -1, 98, -1, 11, 12, 131, - 132, 133, 134, -1, -1, 137, -1, 139, -1, -1, - -1, 114, -1, -1, 117, 118, -1, -1, -1, -1, - 123, 11, 12, -1, -1, -1, -1, -1, 131, 132, - 133, 134, -1, -1, 137, 49, 139, -1, 52, -1, - 54, 55, 56, 57, 58, -1, -1, 61, -1, 52, - 64, 54, 55, 56, 68, -1, -1, -1, -1, 49, - -1, 75, 52, -1, 54, 55, 56, 57, 82, -1, - -1, 61, -1, -1, 64, -1, -1, -1, 68, 82, - -1, -1, -1, -1, 98, 75, -1, -1, -1, -1, - -1, -1, 82, -1, -1, 98, -1, -1, -1, -1, - -1, -1, -1, 117, 118, -1, -1, -1, 98, 123, - 11, 12, -1, -1, 117, 118, -1, 131, 132, 133, - 134, -1, -1, 137, 114, 139, -1, 117, 118, -1, - -1, 134, -1, 123, -1, -1, 139, -1, -1, 11, - -1, 131, 132, 133, 134, -1, -1, 137, 49, 139, - -1, 52, -1, 54, 55, 56, 57, -1, -1, 162, - 61, -1, -1, 64, -1, -1, -1, 68, -1, -1, - -1, -1, -1, -1, 75, -1, -1, 49, -1, -1, - 52, 82, 54, 55, 56, 57, 58, -1, -1, 61, - -1, -1, 64, -1, -1, -1, 68, 98, -1, -1, - -1, -1, -1, 75, -1, -1, -1, -1, -1, -1, - 82, -1, -1, -1, -1, -1, 117, 118, -1, -1, - -1, -1, 123, -1, -1, 11, 98, -1, -1, -1, - 131, 132, 133, 134, -1, -1, 137, -1, 139, -1, - -1, -1, -1, -1, -1, 117, 118, -1, -1, 11, - -1, 123, -1, -1, -1, -1, -1, -1, -1, 131, - 132, 133, 134, 49, -1, 137, 52, 139, 54, 55, - 56, 57, 58, -1, -1, 61, -1, -1, 64, -1, - -1, -1, 68, -1, -1, -1, -1, 49, -1, 75, - 52, -1, 54, 55, 56, 57, 82, -1, -1, 61, - -1, -1, 64, -1, -1, -1, 68, -1, -1, -1, - -1, -1, 98, 75, -1, -1, -1, -1, -1, -1, - 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 117, 118, -1, -1, 11, 98, 123, -1, -1, - -1, -1, -1, -1, -1, 131, 132, 133, 134, -1, - -1, 137, -1, 139, -1, 117, 118, -1, -1, -1, - -1, 123, -1, -1, 10, 11, 12, -1, -1, 131, - 132, 133, 134, 49, -1, 137, 52, 139, 54, 55, - 56, 57, -1, -1, -1, 61, -1, -1, 64, -1, - -1, -1, 68, -1, -1, -1, -1, -1, -1, 75, - -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, - -1, 57, -1, -1, -1, 10, 11, 12, -1, -1, - -1, -1, 98, 52, -1, 54, 55, 56, 74, 75, - -1, -1, -1, -1, 10, 11, 12, -1, -1, -1, - -1, 117, 118, -1, -1, -1, -1, 123, 10, 11, - 12, -1, -1, 82, -1, 131, 132, 133, 134, -1, - -1, 137, 57, 139, 110, 111, 112, 113, -1, 98, - -1, -1, 118, -1, 120, -1, -1, 123, -1, 74, - 75, 57, -1, -1, -1, 131, -1, 133, 117, 118, - 136, -1, -1, -1, -1, 57, -1, -1, 74, 75, - -1, -1, -1, -1, -1, 134, -1, -1, -1, -1, - 139, -1, 74, 75, -1, 110, 111, -1, 113, -1, - -1, -1, -1, 118, -1, 120, -1, -1, 123, -1, - -1, -1, -1, 162, 110, 111, 131, 113, 133, -1, - -1, 136, 118, -1, 120, -1, -1, 123, 110, 111, - -1, 113, -1, -1, -1, 131, 118, 133, 120, -1, - 136, 123, -1, -1, 50, 51, -1, -1, -1, 131, - -1, 133, 58, 59, 136, -1, -1, -1, -1, -1, - -1, 67, -1, 69, 70, 71, 72, -1, -1, -1, - 76, -1, -1, -1, -1, 81, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 100, 101, 102, 103, 104, 105, - -1, -1, -1, -1, -1, 111, 112, 113, 114, -1, - -1, -1, -1, -1, 120, 121, 50, 51, 124, 125, - -1, -1, -1, 129, 58, 59, -1, -1, -1, 135, - 136, -1, 138, 67, -1, 69, 70, 71, 72, -1, - 146, -1, 76, -1, -1, -1, -1, 81, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, - -1, -1, -1, -1, -1, -1, 100, 101, 102, 103, - 104, 105, -1, -1, -1, -1, -1, 111, 112, 113, - 114, -1, -1, -1, -1, -1, 120, 121, 50, 51, - 124, 125, -1, -1, -1, 129, 58, 59, -1, -1, - -1, 135, 136, -1, 138, 67, -1, 69, 70, 71, - 72, -1, 146, -1, 76, -1, -1, -1, -1, 81, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, - -1, 165, -1, -1, -1, -1, -1, -1, 100, 101, - 102, 103, 104, 105, -1, -1, -1, -1, -1, 111, - 112, 113, 114, -1, -1, -1, -1, -1, 120, 121, - 50, 51, 124, 125, -1, -1, -1, 129, 58, 59, - -1, -1, -1, 135, 136, -1, 138, 67, -1, 69, - 70, 71, 72, -1, 146, -1, 76, -1, -1, -1, - -1, 81, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 163, -1, 165, -1, -1, -1, -1, -1, -1, - 100, 101, 102, 103, 104, 105, -1, -1, -1, -1, - -1, 111, 112, 113, 114, -1, -1, -1, -1, -1, - 120, 121, 50, 51, 124, 125, -1, -1, -1, 129, - 58, 59, -1, -1, -1, 135, 136, -1, 138, 67, - -1, 69, 70, 71, 72, -1, 146, -1, 76, -1, - -1, -1, -1, 81, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, - -1, -1, 100, 101, 102, 103, 104, 105, -1, -1, - -1, -1, -1, 111, 112, 113, 114, -1, -1, -1, - -1, -1, 120, 121, 50, 51, 124, 125, -1, -1, - -1, 129, 58, 59, -1, -1, -1, 135, 136, -1, - 138, 67, -1, 69, 70, 71, 72, -1, 146, -1, - 76, -1, -1, -1, -1, 81, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, - -1, -1, -1, -1, 100, 101, 102, 103, 104, 105, - -1, -1, -1, -1, -1, 111, 112, 113, 114, -1, - -1, -1, -1, -1, 120, 121, 50, 51, 124, 125, - -1, -1, -1, 129, 58, 59, -1, -1, -1, 135, - 136, -1, 138, 67, -1, 69, 70, 71, 72, -1, - 146, -1, 76, -1, -1, -1, -1, 81, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, - -1, -1, -1, -1, -1, -1, 100, 101, 102, 103, - 104, 105, -1, -1, -1, -1, -1, 111, 112, 113, - 114, -1, -1, -1, -1, -1, 120, 121, 50, 51, - 124, 125, -1, -1, -1, 129, 58, 59, -1, -1, - -1, 135, 136, -1, 138, 67, -1, 69, 70, 71, - 72, -1, 146, -1, 76, 51, -1, -1, -1, 81, - -1, -1, 58, 59, -1, -1, -1, -1, -1, 163, - -1, 165, -1, 69, 70, 71, -1, -1, 100, 101, - 102, 103, 104, 105, -1, 81, -1, -1, -1, 111, - 112, 113, 114, -1, -1, -1, -1, -1, 120, 121, - -1, -1, 124, 125, -1, -1, -1, 129, 104, -1, - -1, -1, -1, 135, 136, 51, 138, -1, 114, -1, - -1, -1, 58, 59, 120, -1, -1, -1, -1, 125, - -1, -1, -1, 69, 70, 71, -1, -1, -1, 135, - 136, 163, 138, 51, -1, 81, -1, -1, -1, -1, - 58, 59, 51, -1, -1, -1, -1, 153, -1, 58, - 59, 69, 70, 71, -1, -1, -1, 163, 104, -1, - 69, 70, 71, 81, -1, -1, -1, -1, 114, -1, - -1, -1, 81, 52, 120, 54, 55, 56, 124, 125, - -1, -1, -1, -1, -1, 64, 104, 105, -1, 135, - 136, -1, 138, -1, -1, 104, 114, -1, -1, -1, - -1, -1, 120, 82, -1, 114, 52, 125, 54, 55, - 56, 120, -1, -1, -1, -1, 125, 163, 136, 98, - 138, -1, -1, -1, -1, -1, -1, 136, -1, 138, - -1, -1, -1, -1, -1, -1, 82, -1, 117, 118, - -1, -1, -1, -1, -1, 163, -1, -1, -1, -1, - -1, -1, 98, -1, 163, 134, -1, -1, -1, -1, - 139, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 117, 118, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 162, -1, -1, -1, -1, 134, -1, - -1, -1, -1, 139, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 162 + 18, 19, 20, 21, 22, 23, -1, -1, -1, -1, + -1, -1, -1, -1, 150, 33, 152, 153, 154, 155, + -1, -1, 158, 159, 160, 161, 162, -1, 164, 165, + 166, 47, -1, 49, -1, -1, 52, 53, -1, -1, + -1, -1, -1, -1, 60, 61, 18, 19, 20, 21, + 22, 23, -1, 69, -1, 71, 72, 73, 74, -1, + -1, 33, 78, -1, -1, -1, -1, 83, -1, -1, + 152, 153, 154, 155, -1, -1, 158, 159, 160, 161, + 162, -1, 164, 165, 166, 101, 102, 103, 104, 105, + 106, 107, 108, -1, -1, -1, -1, -1, 114, 115, + 116, 117, -1, -1, -1, -1, -1, 123, 124, 11, + 12, 127, 128, 15, -1, 17, 132, -1, -1, -1, + -1, -1, 138, 139, -1, 141, -1, -1, -1, -1, + -1, -1, -1, 149, -1, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 47, 164, 165, 166, 51, + -1, -1, 54, -1, 56, 57, 58, 59, 60, -1, + -1, 63, -1, -1, 66, -1, -1, -1, 70, -1, + -1, -1, -1, -1, -1, 77, 18, 19, 20, 21, + 22, 23, 84, 155, 156, 157, 158, 159, 160, 161, + 162, 33, 164, 165, 166, 10, 11, 12, 100, 18, + 19, 20, 21, 22, 23, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 33, 117, -1, -1, 120, 121, + -1, -1, -1, -1, 126, -1, 18, 19, 20, 21, + 22, 23, 134, 135, 136, 137, -1, -1, 140, -1, + 142, 33, 18, 19, 20, 21, 22, 23, -1, 64, + -1, -1, -1, 155, -1, -1, -1, 33, 160, -1, + -1, 76, 77, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 10, 11, 12, 108, -1, -1, -1, -1, 113, 114, + -1, 116, -1, -1, -1, -1, 121, -1, 123, -1, + -1, 153, 154, 155, -1, -1, 158, 159, 160, 161, + 162, -1, 164, 165, 166, -1, -1, 47, -1, 49, + -1, -1, -1, -1, -1, -1, -1, 156, 157, 158, + 159, 160, 161, 162, 64, 164, 165, 166, -1, -1, + -1, -1, -1, -1, -1, -1, 76, 77, -1, 6, + -1, -1, 154, 155, -1, -1, 158, 159, 160, 161, + 162, -1, 164, 165, 166, -1, 6, -1, -1, 155, + -1, -1, 158, 159, 160, 161, 162, -1, 164, 165, + 166, -1, -1, 113, 114, -1, 116, -1, -1, -1, + 47, 121, 49, 123, -1, -1, 53, -1, -1, -1, + -1, -1, -1, 60, 61, -1, -1, 47, 54, 49, + 56, 57, 58, 53, 71, 72, 73, 147, -1, -1, + 60, 61, -1, -1, -1, -1, 83, -1, -1, -1, + -1, 71, 72, 73, -1, -1, -1, -1, 84, -1, + -1, -1, -1, 83, 10, 11, 12, -1, -1, -1, + 107, -1, -1, -1, 100, -1, 10, 11, 12, -1, + 117, -1, -1, -1, -1, -1, 123, 107, -1, -1, + 127, 128, -1, -1, 120, 121, -1, 117, -1, -1, + -1, -1, 139, 123, 141, 10, 11, 12, 128, -1, + -1, 137, -1, -1, -1, -1, 142, 51, 64, 139, + 54, 141, 56, 57, 58, 59, 60, -1, -1, 63, + 76, 77, 66, -1, -1, -1, 70, -1, -1, 165, + -1, -1, -1, 77, -1, -1, 51, -1, -1, 54, + 84, 56, 57, 58, 59, 60, -1, -1, 63, -1, + -1, 66, -1, -1, -1, 70, 100, 113, 114, 115, + 116, -1, 77, -1, -1, 121, -1, 123, -1, 84, + -1, -1, -1, -1, -1, -1, 120, 121, 134, -1, + -1, -1, 126, 139, -1, 100, -1, 10, 11, 12, + 134, 135, 136, 137, -1, -1, 140, -1, 142, -1, + -1, -1, -1, -1, -1, 120, 121, -1, -1, -1, + -1, 126, -1, -1, -1, -1, 10, 11, 12, 134, + 135, 136, 137, -1, -1, 140, -1, 142, 51, -1, + -1, 54, -1, 56, 57, 58, 59, 60, -1, -1, + 63, -1, -1, 66, -1, -1, -1, 70, 10, 11, + 12, -1, -1, -1, 77, -1, -1, -1, -1, -1, + 54, 84, 56, 57, 58, 59, 60, -1, -1, -1, + -1, -1, 66, -1, -1, -1, -1, 100, -1, -1, + -1, -1, -1, 77, -1, -1, -1, -1, -1, -1, + 84, -1, 54, -1, 56, 57, 58, 120, 121, -1, + -1, -1, -1, 126, 66, -1, 100, -1, -1, -1, + -1, 134, 135, 136, 137, 77, -1, 140, -1, 142, + -1, -1, 84, -1, 11, 12, 120, 121, 15, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 100, -1, + 134, -1, -1, 137, -1, -1, 140, -1, 142, -1, + -1, -1, -1, 11, 12, -1, -1, 15, 120, 121, + 47, -1, -1, -1, 51, -1, -1, 54, -1, 56, + 57, 58, 59, 60, -1, 137, 63, -1, 140, 66, + 142, -1, -1, 70, -1, -1, -1, -1, -1, 47, + 77, -1, -1, 51, -1, -1, 54, 84, 56, 57, + 58, 59, 60, -1, -1, 63, -1, -1, 66, -1, + -1, -1, 70, 100, -1, -1, -1, -1, -1, 77, + -1, -1, -1, -1, -1, -1, 84, -1, -1, -1, + 117, -1, -1, 120, 121, 10, 11, 12, -1, 126, + -1, -1, 100, -1, -1, 11, 12, 134, 135, 136, + 137, -1, -1, 140, -1, 142, -1, -1, -1, 117, + -1, -1, 120, 121, -1, -1, -1, -1, 126, 54, + -1, 56, 57, 58, 11, 12, 134, 135, 136, 137, + -1, 47, 140, -1, 142, 51, -1, -1, 54, 64, + 56, 57, 58, 59, 60, -1, -1, 63, -1, 84, + 66, 76, 77, -1, 70, -1, -1, -1, -1, -1, + -1, 77, -1, -1, 51, 100, -1, 54, 84, 56, + 57, 58, 59, 60, -1, -1, 63, -1, -1, 66, + -1, -1, -1, 70, 100, 120, 121, -1, 113, 114, + 77, 116, -1, -1, -1, -1, 121, 84, 123, -1, + -1, 117, 137, -1, 120, 121, -1, 142, -1, 134, + 126, 11, 12, 100, 139, -1, -1, -1, 134, 135, + 136, 137, -1, -1, 140, -1, 142, -1, -1, -1, + 165, -1, -1, 120, 121, 11, 12, -1, -1, 126, + -1, -1, -1, -1, -1, -1, -1, 134, 135, 136, + 137, 51, -1, 140, 54, 142, 56, 57, 58, 59, + -1, -1, -1, 63, -1, -1, 66, -1, -1, -1, + 70, -1, -1, -1, -1, 51, -1, 77, 54, -1, + 56, 57, 58, 59, 84, -1, -1, 63, -1, -1, + 66, -1, -1, -1, 70, -1, -1, -1, -1, -1, + 100, 77, -1, -1, -1, -1, -1, -1, 84, 10, + 11, 12, -1, -1, -1, -1, -1, 117, -1, -1, + 120, 121, 11, -1, 100, -1, 126, -1, -1, -1, + -1, -1, -1, -1, 134, 135, 136, 137, -1, -1, + 140, -1, 142, -1, 120, 121, -1, -1, -1, -1, + 126, 11, -1, -1, -1, -1, -1, -1, 134, 135, + 136, 137, 51, 64, 140, 54, 142, 56, 57, 58, + 59, 60, -1, -1, 63, 76, 77, 66, -1, -1, + -1, 70, -1, -1, -1, -1, -1, -1, 77, -1, + -1, 51, -1, -1, 54, 84, 56, 57, 58, 59, + 60, -1, -1, 63, -1, -1, 66, -1, -1, -1, + 70, 100, 113, 114, -1, 116, -1, 77, -1, -1, + 121, -1, 123, -1, 84, -1, -1, -1, -1, -1, + -1, 120, 121, 134, -1, -1, -1, 126, 11, -1, + 100, -1, -1, -1, -1, 134, 135, 136, 137, -1, + -1, 140, -1, 142, -1, -1, -1, -1, -1, -1, + 120, 121, 11, -1, -1, -1, 126, -1, -1, -1, + 10, 11, 12, -1, 134, 135, 136, 137, 51, -1, + 140, 54, 142, 56, 57, 58, 59, -1, -1, -1, + 63, -1, 54, 66, 56, 57, 58, 70, -1, -1, + -1, -1, 51, -1, 77, 54, -1, 56, 57, 58, + 59, 84, -1, -1, 63, -1, -1, 66, -1, -1, + -1, 70, 84, -1, 64, -1, -1, 100, 77, -1, + 10, 11, 12, -1, -1, 84, 76, 77, 100, -1, + -1, -1, -1, -1, 10, 11, 12, 120, 121, -1, + -1, 100, -1, 126, -1, -1, -1, -1, 120, 121, + -1, 134, 135, 136, 137, -1, -1, 140, -1, 142, + -1, 120, 121, 113, 114, 137, 116, 126, -1, -1, + 142, 121, -1, 123, 64, 134, 135, 136, 137, -1, + -1, 140, -1, 142, 134, -1, 76, 77, 64, 139, + -1, -1, -1, 165, -1, -1, -1, -1, -1, -1, + 76, 77, -1, -1, -1, -1, -1, -1, -1, 54, + -1, 56, 57, 58, -1, -1, -1, -1, -1, -1, + -1, 66, -1, 113, 114, -1, 116, -1, -1, -1, + -1, 121, -1, 123, -1, -1, -1, 113, 114, 84, + 116, -1, -1, -1, 134, 121, 47, 123, 49, 139, + -1, 52, 53, -1, -1, 100, -1, -1, -1, 60, + 61, -1, 138, -1, -1, -1, -1, -1, 69, -1, + 71, 72, 73, 74, -1, 120, 121, 78, -1, -1, + -1, -1, 83, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 137, -1, -1, -1, -1, 142, -1, -1, + 101, 102, 103, 104, 105, 106, 107, 108, -1, -1, + -1, -1, -1, 114, 115, 116, 117, -1, -1, 47, + 165, 49, 123, 124, 52, 53, 127, 128, -1, -1, + -1, 132, 60, 61, -1, -1, -1, 138, 139, -1, + 141, 69, -1, 71, 72, 73, 74, -1, 149, -1, + 78, -1, -1, -1, -1, 83, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 168, -1, -1, + -1, -1, -1, 101, 102, 103, 104, 105, 106, 107, + 108, -1, -1, -1, -1, -1, 114, 115, 116, 117, + -1, -1, 47, -1, 49, 123, 124, 52, 53, 127, + 128, -1, -1, -1, 132, 60, 61, -1, -1, -1, + 138, 139, -1, 141, 69, -1, 71, 72, 73, 74, + -1, 149, -1, 78, -1, -1, -1, -1, 83, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 168, -1, -1, -1, -1, -1, 101, 102, 103, 104, + 105, 106, 107, 108, -1, -1, -1, -1, -1, 114, + 115, 116, 117, -1, -1, 47, -1, 49, 123, 124, + 52, 53, 127, 128, -1, -1, -1, 132, 60, 61, + -1, -1, -1, 138, 139, -1, 141, 69, -1, 71, + 72, 73, 74, -1, 149, -1, 78, -1, -1, -1, + -1, 83, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 168, -1, -1, -1, -1, -1, 101, + 102, 103, 104, 105, 106, 107, 108, -1, -1, -1, + -1, -1, 114, 115, 116, 117, -1, -1, 47, -1, + 49, 123, 124, 52, 53, 127, 128, -1, -1, -1, + 132, 60, 61, -1, -1, -1, 138, 139, -1, 141, + 69, -1, 71, 72, 73, 74, -1, 149, -1, 78, + -1, -1, -1, -1, 83, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 168, -1, -1, -1, + -1, -1, 101, 102, 103, 104, 105, 106, 107, 108, + -1, -1, -1, -1, -1, 114, 115, 116, 117, -1, + -1, 47, -1, 49, 123, 124, 52, 53, 127, 128, + -1, -1, -1, 132, 60, 61, -1, -1, -1, 138, + 139, -1, 141, 69, -1, 71, 72, 73, 74, -1, + 149, -1, 78, -1, -1, -1, -1, 83, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 168, + -1, -1, -1, -1, -1, 101, 102, 103, 104, 105, + 106, 107, 108, -1, -1, -1, -1, -1, 114, 115, + 116, 117, -1, -1, 47, -1, 49, 123, 124, 52, + 53, 127, 128, -1, -1, -1, 132, 60, 61, -1, + -1, -1, 138, 139, -1, 141, 69, -1, 71, 72, + 73, 74, -1, 149, -1, 78, -1, -1, -1, -1, + 83, -1, -1, 10, 11, 12, -1, -1, 15, 10, + 11, 12, 168, -1, -1, -1, -1, -1, 101, 102, + 103, 104, 105, 106, 107, 108, -1, -1, -1, -1, + -1, 114, 115, 116, 117, -1, -1, -1, -1, -1, + 123, 124, -1, -1, 127, 128, 47, -1, 49, 132, + -1, -1, -1, -1, -1, 138, 139, 64, 141, -1, + -1, -1, -1, 64, -1, 47, 149, 49, -1, 76, + 77, 53, -1, -1, -1, 76, 77, -1, 60, 61, + -1, -1, -1, -1, -1, 168, -1, -1, -1, 71, + 72, 73, -1, -1, -1, -1, 78, -1, -1, -1, + -1, 83, -1, -1, -1, -1, 113, 114, -1, 116, + -1, -1, 113, 114, 121, 116, 123, -1, -1, -1, + 121, -1, 123, -1, -1, 107, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 117, -1, -1, -1, -1, + -1, 123, -1, -1, -1, -1, 128, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 138, 139, 47, 141, + 49, -1, -1, 52, 53, -1, -1, -1, -1, -1, + -1, 60, 61, -1, 156, -1, -1, -1, -1, -1, + 69, -1, 71, 72, 73, 74, -1, -1, -1, 78, + -1, -1, -1, -1, 83, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 101, 102, 103, 104, 105, 106, 107, 108, + -1, -1, -1, -1, -1, 114, 115, 116, 117, -1, + -1, 47, -1, 49, 123, 124, -1, 53, 127, 128, + -1, -1, -1, 132, 60, 61, -1, -1, -1, 138, + 139, -1, 141, -1, -1, 71, 72, 73, 47, -1, + 49, -1, 78, -1, 53, -1, -1, 83, -1, -1, + -1, 60, 61, 47, -1, 49, -1, -1, -1, 53, + -1, -1, 71, 72, 73, -1, 60, 61, -1, -1, + -1, 107, -1, -1, 83, -1, -1, 71, 72, 73, + -1, 117, -1, -1, -1, -1, -1, 123, -1, 83, + -1, 127, 128, -1, -1, -1, -1, -1, 107, 108, + -1, -1, 138, 139, -1, 141, -1, -1, 117, -1, + -1, -1, -1, 107, 123, -1, -1, -1, -1, 128, + -1, -1, -1, 117, -1, -1, -1, -1, -1, 123, + 139, -1, 141, -1, 128, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 139, -1, 141 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint16 yystos[] = { - 0, 141, 142, 143, 169, 170, 274, 3, 4, 5, - 6, 8, 9, 10, 11, 48, 52, 54, 55, 56, - 60, 64, 65, 73, 74, 75, 79, 82, 83, 84, - 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, - 95, 96, 97, 98, 106, 108, 110, 115, 117, 118, - 119, 122, 127, 130, 134, 139, 152, 155, 156, 157, - 160, 162, 163, 167, 264, 265, 273, 11, 12, 49, - 52, 54, 55, 56, 57, 58, 61, 64, 68, 75, - 82, 98, 117, 118, 123, 131, 132, 133, 134, 137, - 139, 227, 228, 232, 233, 235, 241, 242, 246, 247, - 252, 253, 254, 255, 0, 50, 51, 58, 59, 67, - 69, 70, 71, 72, 76, 81, 100, 101, 102, 103, - 104, 105, 111, 112, 113, 114, 120, 121, 124, 125, - 129, 135, 136, 138, 146, 163, 173, 175, 176, 178, - 181, 199, 248, 251, 274, 162, 162, 162, 162, 162, - 162, 153, 162, 153, 162, 162, 162, 162, 162, 162, - 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, - 162, 162, 162, 11, 49, 61, 131, 132, 230, 246, - 247, 252, 153, 162, 162, 15, 162, 153, 162, 162, - 162, 264, 264, 264, 264, 264, 11, 52, 54, 55, - 56, 64, 75, 82, 98, 117, 118, 134, 139, 232, - 262, 264, 10, 11, 12, 74, 75, 110, 111, 113, - 118, 120, 148, 152, 268, 269, 271, 274, 264, 16, - 17, 18, 19, 20, 21, 22, 23, 33, 149, 150, - 151, 152, 153, 154, 155, 156, 157, 158, 159, 161, - 162, 163, 6, 8, 227, 228, 162, 57, 123, 64, - 98, 253, 253, 253, 271, 162, 253, 13, 15, 17, - 58, 138, 152, 157, 162, 225, 226, 274, 226, 144, - 10, 11, 12, 110, 147, 272, 10, 11, 12, 110, - 144, 271, 272, 81, 178, 178, 178, 178, 6, 178, - 178, 147, 177, 105, 178, 162, 162, 162, 162, 178, - 144, 271, 147, 147, 147, 178, 178, 162, 178, 181, - 200, 178, 178, 184, 105, 271, 178, 178, 163, 10, - 11, 49, 61, 109, 131, 132, 144, 160, 187, 190, - 229, 231, 233, 235, 241, 246, 247, 252, 261, 262, - 274, 232, 261, 261, 261, 261, 232, 261, 232, 261, - 232, 261, 232, 232, 232, 232, 232, 232, 232, 232, - 232, 232, 232, 232, 232, 232, 232, 261, 162, 271, - 162, 162, 271, 271, 232, 261, 261, 162, 232, 232, - 232, 264, 261, 261, 164, 145, 164, 271, 166, 145, - 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, - 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, - 264, 164, 262, 264, 226, 226, 264, 232, 157, 271, - 13, 15, 17, 58, 138, 152, 157, 225, 274, 225, - 226, 225, 226, 225, 225, 15, 17, 58, 114, 152, - 157, 210, 211, 220, 227, 228, 274, 163, 244, 245, - 274, 11, 243, 253, 147, 234, 236, 178, 175, 144, - 271, 271, 271, 271, 271, 170, 144, 264, 153, 10, - 11, 190, 229, 231, 271, 146, 148, 179, 180, 271, - 162, 162, 162, 58, 227, 271, 162, 174, 271, 144, - 146, 147, 148, 216, 144, 146, 148, 217, 146, 182, - 271, 272, 271, 164, 164, 164, 164, 164, 164, 154, - 164, 154, 164, 164, 164, 164, 145, 164, 145, 164, - 145, 164, 164, 164, 164, 164, 164, 164, 164, 164, - 164, 164, 264, 232, 261, 154, 164, 164, 271, 164, - 154, 164, 164, 164, 164, 264, 264, 162, 196, 274, - 268, 147, 164, 166, 164, 164, 225, 157, 271, 225, - 225, 225, 225, 225, 163, 225, 114, 227, 228, 220, - 225, 225, 164, 15, 145, 13, 17, 58, 138, 152, - 157, 162, 223, 272, 274, 13, 15, 17, 58, 138, - 152, 157, 162, 224, 260, 264, 274, 271, 165, 243, - 170, 74, 237, 274, 170, 144, 145, 145, 145, 145, - 165, 249, 145, 164, 10, 11, 12, 57, 58, 131, - 201, 202, 203, 204, 205, 252, 274, 162, 217, 185, - 146, 232, 166, 145, 162, 188, 13, 157, 189, 264, - 227, 13, 17, 58, 138, 152, 157, 222, 272, 274, - 232, 170, 162, 256, 257, 171, 172, 271, 62, 63, - 256, 62, 63, 144, 264, 13, 17, 58, 109, 138, - 152, 157, 162, 183, 206, 208, 272, 147, 162, 162, - 232, 232, 232, 164, 164, 164, 162, 164, 162, 210, - 17, 33, 58, 74, 104, 107, 110, 126, 138, 152, - 209, 274, 264, 225, 260, 164, 227, 228, 223, 224, - 164, 164, 196, 15, 220, 157, 223, 223, 223, 223, - 223, 223, 148, 163, 215, 274, 157, 271, 224, 224, - 224, 224, 224, 224, 163, 215, 166, 145, 148, 165, - 147, 238, 239, 274, 165, 250, 10, 10, 10, 10, - 170, 273, 146, 205, 154, 145, 15, 271, 13, 17, - 58, 138, 152, 157, 162, 223, 224, 186, 208, 146, - 166, 179, 3, 4, 5, 9, 10, 15, 48, 60, - 65, 73, 74, 106, 108, 110, 115, 119, 122, 127, - 130, 152, 155, 156, 160, 162, 167, 212, 213, 220, - 221, 266, 267, 273, 274, 210, 157, 206, 210, 164, - 222, 157, 222, 222, 222, 222, 222, 162, 163, 164, - 165, 191, 165, 258, 274, 144, 145, 144, 162, 146, - 146, 165, 146, 146, 144, 218, 219, 264, 274, 146, - 157, 206, 206, 6, 16, 17, 18, 19, 20, 21, - 22, 23, 33, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 63, 106, 145, 148, 150, - 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, - 162, 163, 167, 197, 206, 206, 206, 206, 147, 162, - 163, 209, 148, 215, 217, 243, 262, 262, 164, 164, - 164, 262, 262, 164, 58, 230, 162, 144, 166, 162, - 223, 224, 215, 215, 162, 162, 209, 223, 164, 264, - 260, 224, 164, 260, 264, 111, 112, 113, 131, 136, - 240, 247, 270, 271, 144, 145, 170, 145, 164, 145, - 145, 145, 165, 164, 223, 224, 181, 198, 199, 204, - 271, 148, 157, 148, 214, 274, 215, 217, 178, 162, - 153, 153, 230, 153, 15, 162, 153, 162, 264, 264, - 264, 264, 232, 262, 264, 164, 15, 145, 16, 17, - 18, 19, 20, 21, 22, 23, 33, 149, 150, 151, - 152, 153, 154, 155, 156, 157, 158, 159, 161, 162, - 163, 164, 206, 164, 164, 162, 222, 194, 260, 210, - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 52, - 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, - 63, 64, 65, 66, 68, 70, 71, 72, 73, 74, - 75, 76, 77, 78, 80, 81, 82, 98, 104, 105, - 106, 108, 109, 110, 111, 112, 113, 114, 115, 116, - 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, - 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, - 137, 138, 139, 140, 144, 145, 146, 147, 148, 149, - 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 166, 167, 259, 256, 172, - 261, 261, 218, 165, 145, 206, 10, 164, 166, 164, - 4, 207, 260, 264, 145, 164, 164, 164, 164, 196, - 230, 226, 164, 271, 256, 210, 215, 215, 210, 210, - 162, 166, 162, 166, 145, 136, 270, 136, 270, 136, - 270, 271, 111, 112, 113, 271, 15, 170, 240, 165, - 10, 146, 10, 10, 10, 146, 214, 232, 48, 60, - 65, 115, 119, 122, 152, 155, 156, 157, 160, 162, - 167, 263, 265, 145, 232, 232, 232, 162, 232, 162, - 232, 232, 232, 264, 164, 164, 15, 221, 264, 264, - 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, - 264, 264, 264, 264, 264, 264, 264, 264, 264, 164, - 262, 264, 196, 164, 162, 196, 195, 210, 166, 164, - 258, 165, 165, 164, 165, 144, 264, 212, 166, 183, - 209, 226, 15, 164, 165, 164, 164, 164, 210, 210, - 270, 270, 270, 270, 270, 270, 165, 145, 164, 145, - 164, 164, 145, 164, 162, 153, 153, 153, 15, 162, - 153, 263, 263, 263, 263, 263, 232, 262, 263, 16, - 17, 18, 19, 20, 21, 22, 23, 33, 149, 150, - 151, 152, 155, 156, 157, 158, 159, 161, 162, 163, - 186, 164, 154, 154, 261, 154, 271, 164, 154, 164, - 164, 264, 147, 164, 166, 162, 192, 210, 164, 196, - 165, 15, 218, 164, 144, 164, 196, 196, 196, 164, - 164, 10, 146, 10, 146, 146, 10, 146, 232, 232, - 232, 232, 162, 232, 232, 164, 164, 263, 263, 263, - 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, - 263, 263, 263, 263, 263, 263, 164, 262, 264, 162, - 162, 164, 162, 164, 162, 264, 193, 210, 164, 196, - 165, 196, 256, 209, 209, 209, 196, 196, 164, 145, - 145, 164, 164, 154, 154, 154, 271, 164, 154, 263, - 147, 164, 166, 262, 262, 262, 262, 210, 164, 196, - 165, 146, 10, 10, 146, 162, 162, 162, 164, 162, - 263, 164, 164, 164, 164, 164, 196, 164, 164, 262, - 262, 262, 262, 196, 209, 146, 146, 164, 164, 164, - 164, 209 + 0, 144, 145, 146, 172, 173, 280, 3, 4, 5, + 6, 8, 9, 10, 11, 50, 54, 56, 57, 58, + 62, 66, 67, 75, 76, 77, 81, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 109, 111, 113, 118, 120, 121, + 122, 125, 130, 133, 137, 142, 155, 158, 159, 160, + 163, 165, 166, 169, 270, 271, 279, 11, 12, 51, + 54, 56, 57, 58, 59, 60, 63, 66, 70, 77, + 84, 100, 120, 121, 126, 134, 135, 136, 137, 140, + 142, 232, 233, 237, 239, 241, 247, 248, 252, 253, + 258, 259, 260, 261, 0, 47, 49, 52, 53, 60, + 61, 69, 71, 72, 73, 74, 78, 83, 101, 102, + 103, 104, 105, 106, 107, 108, 114, 115, 116, 117, + 123, 124, 127, 128, 132, 138, 139, 141, 149, 176, + 178, 179, 181, 184, 203, 254, 257, 280, 147, 165, + 165, 165, 165, 165, 165, 156, 165, 156, 165, 165, + 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, + 165, 165, 165, 165, 165, 165, 165, 11, 51, 63, + 134, 135, 235, 252, 253, 258, 156, 165, 165, 15, + 165, 156, 165, 165, 165, 270, 270, 270, 270, 270, + 11, 54, 56, 57, 58, 66, 77, 84, 100, 120, + 121, 137, 142, 237, 268, 270, 10, 11, 12, 64, + 76, 77, 113, 114, 116, 121, 123, 151, 155, 160, + 274, 275, 277, 280, 270, 16, 17, 18, 19, 20, + 21, 22, 23, 33, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 164, 165, 166, 6, 8, + 232, 233, 165, 59, 126, 66, 100, 259, 259, 259, + 277, 165, 259, 13, 15, 17, 60, 141, 155, 160, + 165, 230, 231, 280, 231, 147, 10, 11, 12, 113, + 150, 278, 238, 280, 138, 182, 183, 277, 165, 72, + 83, 181, 181, 181, 181, 6, 181, 203, 181, 150, + 180, 108, 181, 165, 165, 165, 165, 165, 165, 181, + 147, 277, 150, 150, 150, 181, 181, 165, 179, 181, + 184, 204, 181, 181, 188, 108, 277, 181, 181, 10, + 11, 51, 63, 112, 134, 135, 147, 163, 191, 194, + 234, 236, 239, 241, 247, 252, 253, 258, 267, 268, + 280, 267, 237, 267, 267, 267, 267, 237, 267, 237, + 267, 237, 267, 237, 237, 237, 237, 237, 237, 237, + 237, 237, 237, 237, 237, 237, 237, 237, 267, 165, + 277, 165, 165, 277, 238, 237, 267, 267, 165, 10, + 237, 237, 237, 270, 267, 267, 167, 148, 167, 277, + 277, 148, 170, 151, 220, 280, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 167, 268, 270, + 231, 231, 51, 270, 237, 160, 277, 13, 15, 17, + 60, 141, 155, 160, 230, 280, 230, 231, 230, 231, + 230, 230, 15, 17, 47, 60, 117, 155, 160, 215, + 216, 225, 232, 233, 280, 166, 250, 251, 280, 11, + 249, 259, 150, 10, 11, 12, 47, 49, 113, 147, + 277, 278, 277, 48, 148, 165, 11, 234, 270, 181, + 178, 147, 277, 277, 277, 277, 277, 277, 277, 173, + 147, 270, 156, 10, 11, 194, 234, 236, 277, 149, + 151, 165, 165, 165, 60, 232, 277, 165, 177, 277, + 186, 147, 149, 151, 222, 149, 185, 277, 278, 238, + 168, 167, 167, 167, 167, 167, 167, 157, 167, 157, + 167, 167, 167, 167, 148, 167, 148, 167, 148, 167, + 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, + 270, 237, 267, 277, 157, 167, 167, 277, 167, 167, + 157, 167, 167, 167, 167, 270, 270, 15, 155, 275, + 165, 200, 280, 270, 150, 167, 170, 167, 167, 167, + 230, 160, 277, 230, 230, 230, 230, 230, 166, 230, + 182, 117, 232, 233, 225, 230, 230, 167, 15, 148, + 13, 17, 60, 141, 155, 160, 165, 228, 278, 280, + 13, 15, 17, 60, 141, 155, 160, 165, 229, 266, + 270, 280, 277, 168, 249, 182, 165, 240, 242, 150, + 181, 182, 3, 4, 5, 9, 10, 15, 50, 62, + 67, 75, 76, 109, 111, 113, 118, 122, 125, 130, + 133, 155, 158, 159, 163, 165, 169, 217, 218, 225, + 226, 272, 273, 279, 280, 167, 167, 173, 147, 148, + 148, 148, 148, 148, 148, 168, 255, 148, 167, 10, + 11, 12, 59, 60, 134, 205, 206, 207, 208, 209, + 258, 280, 165, 222, 189, 149, 237, 192, 13, 160, + 193, 51, 270, 232, 13, 17, 60, 141, 155, 160, + 227, 278, 280, 237, 173, 165, 147, 149, 150, 151, + 221, 262, 263, 64, 65, 147, 270, 13, 17, 60, + 112, 141, 155, 160, 165, 187, 210, 212, 278, 150, + 277, 165, 165, 237, 237, 237, 167, 167, 167, 165, + 167, 165, 220, 215, 17, 33, 47, 60, 61, 76, + 107, 110, 113, 129, 141, 155, 213, 280, 270, 230, + 266, 167, 48, 232, 233, 228, 229, 167, 167, 200, + 15, 225, 160, 228, 228, 228, 228, 228, 228, 166, + 220, 160, 277, 229, 229, 229, 229, 229, 229, 166, + 220, 170, 148, 151, 48, 234, 270, 173, 76, 243, + 280, 183, 165, 156, 156, 235, 156, 15, 165, 156, + 165, 270, 270, 270, 270, 237, 268, 270, 167, 15, + 148, 16, 17, 18, 19, 20, 21, 22, 23, 33, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 164, 165, 166, 181, 181, 168, 256, 10, 10, + 10, 10, 10, 10, 173, 279, 149, 209, 157, 148, + 15, 277, 13, 17, 60, 141, 155, 160, 165, 228, + 229, 190, 212, 149, 215, 160, 210, 215, 167, 167, + 227, 160, 227, 227, 227, 227, 227, 165, 166, 167, + 168, 195, 262, 174, 175, 277, 64, 65, 168, 264, + 280, 149, 149, 147, 223, 224, 270, 280, 149, 160, + 210, 210, 6, 16, 17, 18, 19, 20, 21, 22, + 23, 33, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 65, 109, 148, 151, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 165, + 166, 169, 201, 210, 210, 210, 210, 150, 165, 166, + 213, 151, 220, 222, 249, 268, 268, 167, 167, 167, + 268, 268, 167, 60, 235, 182, 165, 147, 170, 165, + 225, 228, 229, 220, 220, 165, 165, 213, 228, 167, + 266, 229, 167, 266, 270, 167, 167, 168, 150, 244, + 245, 280, 237, 237, 237, 165, 237, 165, 10, 237, + 237, 237, 270, 167, 167, 15, 226, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 167, 268, + 270, 173, 148, 148, 167, 148, 214, 280, 148, 148, + 148, 168, 167, 228, 229, 179, 184, 202, 203, 208, + 277, 151, 160, 151, 219, 280, 220, 222, 167, 210, + 167, 167, 165, 227, 198, 266, 215, 168, 147, 148, + 147, 165, 149, 149, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 70, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 82, 83, 84, 100, 107, 108, 109, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 169, 170, 265, 223, 168, 148, 210, 10, 167, + 170, 167, 4, 211, 266, 270, 148, 167, 167, 167, + 167, 200, 235, 231, 48, 167, 277, 262, 215, 220, + 220, 215, 215, 165, 170, 165, 170, 148, 114, 115, + 116, 134, 139, 246, 276, 277, 147, 148, 167, 157, + 157, 267, 157, 277, 167, 167, 157, 167, 167, 270, + 150, 167, 170, 168, 10, 10, 149, 10, 167, 10, + 10, 10, 149, 219, 237, 50, 62, 67, 118, 122, + 125, 155, 158, 159, 160, 163, 165, 169, 269, 271, + 148, 200, 167, 165, 200, 199, 215, 170, 167, 262, + 175, 267, 267, 264, 168, 147, 270, 217, 170, 187, + 213, 231, 15, 167, 168, 167, 167, 167, 215, 215, + 139, 276, 139, 276, 139, 276, 277, 114, 115, 116, + 15, 173, 246, 165, 165, 167, 165, 167, 165, 270, + 167, 148, 167, 148, 149, 148, 167, 167, 148, 167, + 165, 156, 156, 156, 15, 165, 156, 269, 269, 269, + 269, 269, 237, 268, 269, 16, 17, 18, 19, 20, + 21, 22, 23, 33, 152, 153, 154, 155, 158, 159, + 160, 161, 162, 164, 165, 166, 190, 165, 196, 215, + 167, 200, 168, 168, 167, 168, 223, 167, 147, 167, + 200, 200, 200, 167, 167, 276, 276, 276, 276, 276, + 276, 168, 268, 268, 268, 268, 149, 10, 149, 10, + 10, 149, 149, 10, 149, 237, 237, 237, 237, 165, + 10, 237, 237, 167, 167, 269, 269, 269, 269, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, + 269, 269, 269, 269, 167, 268, 270, 197, 215, 167, + 200, 15, 168, 200, 262, 213, 213, 213, 200, 200, + 167, 167, 167, 167, 148, 214, 167, 148, 148, 167, + 167, 157, 157, 157, 277, 167, 167, 157, 269, 150, + 167, 170, 215, 167, 200, 168, 10, 167, 149, 10, + 10, 149, 165, 165, 165, 167, 165, 269, 167, 200, + 149, 167, 148, 167, 268, 268, 268, 268, 200, 213, + 149, 10, 149, 167, 167, 167, 167, 213, 167, 149 }; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint16 yyr1[] = { - 0, 168, 169, 169, 169, 170, 170, 170, 171, 171, - 172, 172, 172, 174, 173, 175, 175, 175, 175, 175, - 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, - 175, 175, 175, 175, 175, 175, 175, 175, 177, 176, + 0, 171, 172, 172, 172, 173, 173, 173, 174, 174, + 175, 175, 175, 177, 176, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, - 178, 178, 178, 178, 178, 178, 179, 179, 180, 180, - 182, 181, 181, 181, 181, 181, 183, 183, 185, 184, - 184, 186, 186, 188, 187, 189, 187, 191, 190, 192, - 190, 193, 190, 194, 190, 195, 190, 190, 196, 196, - 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, - 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, - 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, - 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, - 197, 197, 197, 197, 197, 197, 197, 197, 197, 198, - 198, 200, 199, 199, 201, 201, 202, 202, 203, 203, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 205, - 205, 205, 205, 206, 206, 206, 206, 206, 206, 206, - 206, 206, 206, 206, 207, 206, 208, 208, 209, 209, - 209, 210, 210, 210, 210, 210, 211, 211, 212, 212, - 212, 212, 212, 213, 213, 214, 214, 215, 215, 216, - 216, 216, 216, 216, 217, 217, 217, 217, 217, 217, - 218, 218, 218, 219, 219, 219, 219, 220, 220, 220, - 220, 220, 220, 220, 221, 221, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 223, 223, 223, 223, 223, - 223, 223, 223, 223, 223, 223, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 225, 225, 225, - 225, 225, 225, 225, 225, 225, 225, 226, 226, 226, - 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, - 226, 227, 227, 227, 227, 227, 227, 227, 227, 227, - 227, 227, 228, 229, 229, 229, 229, 229, 229, 229, - 229, 229, 229, 229, 229, 230, 230, 230, 230, 230, - 230, 230, 230, 231, 231, 232, 232, 232, 232, 234, - 233, 236, 235, 237, 237, 238, 238, 239, 239, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 241, - 242, 242, 242, 242, 243, 243, 244, 244, 244, 245, - 245, 245, 246, 246, 246, 247, 247, 247, 249, 248, - 250, 248, 248, 248, 251, 251, 251, 252, 252, 252, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 254, 254, 254, 255, 257, 256, + 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, + 178, 178, 180, 179, 181, 181, 181, 181, 181, 181, + 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, + 181, 181, 182, 182, 183, 183, 183, 185, 184, 184, + 186, 184, 184, 184, 187, 187, 189, 188, 188, 190, + 190, 192, 191, 193, 191, 195, 194, 196, 194, 197, + 194, 198, 194, 199, 194, 194, 200, 200, 200, 200, + 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, + 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, + 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, + 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, + 201, 201, 201, 201, 201, 201, 201, 201, 201, 202, + 202, 202, 203, 204, 203, 203, 203, 205, 205, 206, + 206, 207, 207, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 209, 209, 209, 209, 210, 210, 210, 210, + 210, 210, 210, 210, 210, 210, 210, 211, 210, 212, + 212, 213, 213, 213, 214, 214, 215, 215, 215, 215, + 215, 216, 216, 217, 217, 217, 217, 217, 218, 218, + 219, 219, 220, 220, 221, 221, 221, 221, 221, 222, + 222, 222, 222, 222, 222, 223, 223, 223, 224, 224, + 224, 224, 225, 225, 225, 225, 225, 225, 225, 225, + 226, 226, 227, 227, 227, 227, 227, 227, 227, 227, + 227, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 229, 229, 230, 230, 230, 230, 230, 230, 230, + 230, 230, 230, 231, 231, 231, 231, 231, 231, 231, + 231, 231, 231, 231, 231, 231, 231, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 233, + 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, + 234, 234, 234, 235, 235, 235, 235, 235, 235, 235, + 235, 236, 236, 237, 237, 237, 237, 238, 238, 238, + 238, 240, 239, 242, 241, 243, 243, 244, 244, 245, + 245, 246, 246, 246, 246, 246, 246, 246, 246, 246, + 246, 247, 248, 248, 248, 248, 249, 249, 250, 250, + 250, 251, 251, 251, 252, 252, 252, 253, 253, 253, + 255, 254, 256, 254, 254, 254, 257, 257, 257, 258, 258, 258, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 260, - 260, 261, 261, 262, 262, 263, 263, 263, 263, 263, - 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, - 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, - 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, - 263, 263, 263, 264, 264, 264, 264, 264, 264, 264, - 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, - 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, - 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, - 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, - 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, + 259, 259, 259, 259, 259, 259, 260, 260, 260, 261, + 263, 262, 264, 264, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, - 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, - 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, - 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, - 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, - 266, 266, 266, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 268, 268, 268, 269, 269, 269, - 269, 270, 270, 270, 270, 271, 271, 271, 271, 271, - 271, 271, 271, 271, 271, 272, 272, 272, 272, 273, - 273, 273, 273, 274 + 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, + 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, + 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, + 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, + 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, + 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, + 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, + 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, + 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, + 265, 265, 265, 266, 266, 267, 267, 268, 268, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 272, 272, 272, + 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, + 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, + 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, + 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, + 272, 273, 273, 273, 273, 273, 273, 273, 273, 273, + 273, 273, 274, 274, 274, 274, 274, 275, 275, 275, + 275, 276, 276, 276, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 278, 278, 278, 278, 279, + 279, 279, 279, 280 }; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ @@ -2942,44 +3168,46 @@ static const yytype_uint8 yyr2[] = { 0, 2, 2, 2, 2, 1, 2, 2, 1, 3, 4, 5, 4, 0, 5, 1, 1, 1, 1, 1, - 2, 1, 1, 2, 2, 2, 2, 7, 9, 11, - 9, 11, 13, 9, 13, 9, 7, 5, 0, 3, - 1, 2, 2, 3, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 6, 1, 3, 1, 4, - 0, 4, 3, 3, 3, 1, 2, 4, 0, 4, - 3, 2, 4, 0, 6, 0, 6, 0, 7, 0, - 11, 0, 12, 0, 8, 0, 9, 1, 1, 2, - 2, 2, 2, 2, 2, 2, 2, 4, 5, 6, + 2, 1, 1, 2, 2, 2, 2, 8, 11, 9, + 11, 13, 15, 7, 9, 12, 9, 9, 13, 9, + 7, 5, 0, 3, 1, 2, 2, 3, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, + 5, 5, 1, 3, 1, 4, 4, 0, 4, 3, + 0, 4, 3, 1, 2, 4, 0, 4, 3, 2, + 4, 0, 6, 0, 6, 0, 7, 0, 11, 0, + 12, 0, 8, 0, 9, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 4, 5, 6, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, - 1, 0, 6, 2, 1, 1, 1, 3, 1, 1, - 1, 2, 4, 2, 3, 3, 4, 2, 3, 1, - 1, 1, 1, 1, 2, 3, 2, 2, 2, 2, - 2, 3, 4, 3, 0, 6, 2, 3, 1, 3, - 4, 1, 1, 1, 3, 2, 1, 3, 1, 1, - 1, 3, 2, 1, 3, 1, 2, 1, 2, 1, - 3, 5, 3, 3, 1, 3, 3, 3, 3, 4, - 1, 1, 2, 1, 3, 3, 5, 3, 4, 5, - 3, 4, 5, 2, 1, 1, 1, 1, 2, 2, - 2, 2, 2, 3, 4, 1, 1, 2, 2, 2, - 2, 2, 3, 4, 7, 3, 1, 2, 2, 2, - 2, 2, 2, 3, 4, 7, 3, 1, 1, 2, - 2, 2, 2, 2, 2, 3, 4, 1, 1, 2, - 2, 2, 2, 2, 2, 3, 4, 5, 9, 9, - 9, 1, 1, 2, 1, 1, 1, 2, 4, 4, - 4, 1, 1, 1, 1, 2, 1, 1, 1, 2, - 4, 2, 4, 4, 1, 1, 1, 2, 2, 2, - 4, 4, 1, 1, 1, 2, 3, 2, 3, 0, - 5, 0, 8, 1, 1, 1, 1, 2, 3, 1, - 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, - 3, 1, 4, 2, 1, 1, 1, 3, 5, 1, - 2, 4, 1, 2, 2, 1, 1, 1, 0, 6, - 0, 7, 4, 5, 3, 5, 4, 1, 1, 1, + 1, 1, 2, 0, 6, 2, 2, 1, 1, 1, + 3, 1, 1, 1, 2, 4, 2, 3, 3, 4, + 2, 3, 1, 1, 1, 1, 1, 2, 3, 2, + 2, 2, 2, 2, 3, 4, 3, 0, 6, 2, + 3, 1, 3, 4, 1, 2, 1, 1, 1, 3, + 2, 1, 3, 1, 1, 1, 3, 2, 1, 3, + 1, 2, 1, 2, 1, 3, 5, 3, 3, 1, + 3, 3, 3, 3, 4, 1, 1, 2, 1, 3, + 3, 5, 3, 4, 5, 3, 4, 5, 2, 4, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, + 4, 1, 1, 2, 2, 2, 2, 2, 3, 4, + 7, 3, 1, 2, 2, 2, 2, 2, 2, 3, + 4, 7, 3, 1, 1, 2, 2, 2, 2, 2, + 2, 3, 4, 1, 1, 2, 2, 2, 2, 2, + 2, 3, 4, 5, 9, 9, 9, 1, 1, 2, + 1, 1, 1, 3, 4, 4, 4, 4, 1, 1, + 1, 1, 2, 1, 1, 1, 3, 4, 2, 4, + 4, 4, 1, 1, 1, 2, 3, 2, 4, 4, + 1, 1, 1, 2, 3, 2, 3, 1, 4, 5, + 5, 0, 6, 0, 9, 1, 1, 1, 1, 2, + 3, 1, 2, 2, 2, 3, 3, 3, 3, 3, + 3, 4, 3, 1, 4, 2, 1, 1, 1, 3, + 5, 1, 2, 4, 1, 2, 2, 1, 1, 1, + 0, 6, 0, 7, 4, 5, 3, 5, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 1, 1, 2, 1, 0, 2, - 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 1, 1, 2, 1, + 0, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -2991,27 +3219,28 @@ static const yytype_uint8 yyr2[] = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, - 1, 1, 1, 1, 3, 1, 4, 7, 7, 7, - 7, 4, 5, 4, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 5, 4, 4, 3, - 3, 3, 3, 1, 4, 7, 7, 7, 7, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 5, 4, 2, 5, 4, 4, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, + 4, 7, 7, 7, 7, 4, 4, 5, 4, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 5, 4, 4, 3, 3, 3, 3, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 8, 11, 4, 4, 6, 4, 4, 6, 6, + 3, 5, 4, 4, 3, 3, 3, 3, 1, 4, + 7, 7, 7, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 1, 4, 7, 7, 7, 7, 4, 5, 4, 2, - 5, 4, 4, 2, 2, 2, 2, 2, 3, 3, + 5, 4, 2, 5, 4, 4, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 5, 4, 4, 3, - 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, - 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, + 5, 4, 4, 3, 3, 3, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 8, 11, + 4, 4, 6, 4, 4, 6, 6, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 1, 4, 7, + 7, 7, 7, 4, 4, 5, 4, 2, 5, 4, + 4, 2, 2, 2, 2, 2, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 5, 4, 4, 3, 3, 3, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 4, 2, 3, 1, + 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0 }; @@ -3789,64 +4018,64 @@ yyreduce: switch (yyn) { case 3: -#line 444 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 452 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_expr = (yyvsp[0].u.expr); } -#line 3797 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4026 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 4: -#line 448 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 456 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_type = (yyvsp[0].u.type); } -#line 3805 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4034 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 10: -#line 466 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - delete (yyvsp[-1].u.expr); -} -#line 3813 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 11: -#line 470 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - delete (yyvsp[-2].u.expr); -} -#line 3821 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 12: #line 474 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { delete (yyvsp[-1].u.expr); } -#line 3829 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4042 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 11: +#line 478 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + delete (yyvsp[-2].u.expr); +} +#line 4050 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 12: +#line 482 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + delete (yyvsp[-1].u.expr); +} +#line 4058 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 13: -#line 486 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 494 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { push_storage_class((current_storage_class & ~CPPInstance::SC_c_binding) | ((yyvsp[-1].u.integer) & CPPInstance::SC_c_binding)); } -#line 3838 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4067 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 14: -#line 491 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 499 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_storage_class(); } -#line 3846 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4075 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 21: -#line 504 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 512 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if (publish_nest_level != 0) { yyerror("Unclosed __begin_publish", publish_loc); @@ -3859,11 +4088,11 @@ yyreduce: publish_nest_level++; current_scope->set_current_vis(V_published); } -#line 3863 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4092 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 22: -#line 517 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 525 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if (publish_nest_level != 1) { yyerror("Unmatched __end_publish", (yylsp[0])); @@ -3872,19 +4101,19 @@ yyreduce: } publish_nest_level = 0; } -#line 3876 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4105 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 23: -#line 526 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 534 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_scope->set_current_vis(V_published); } -#line 3884 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4113 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 24: -#line 530 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 538 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if (publish_nest_level > 0) { current_scope->set_current_vis(V_published); @@ -3892,258 +4121,413 @@ yyreduce: current_scope->set_current_vis(V_public); } } -#line 3896 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4125 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 25: -#line 538 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 546 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_scope->set_current_vis(V_protected); } -#line 3904 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4133 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 26: -#line 542 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 550 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_scope->set_current_vis(V_private); } -#line 3912 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4141 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 27: -#line 546 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 554 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { + CPPDeclaration *getter = (yyvsp[-3].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-3].u.identifier)->get_fully_scoped_name(), (yylsp[-3])); + } else { + CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-5].u.identifier), CPPMakeProperty::T_normal, current_scope, (yylsp[-7]).file); + make_property->_get_function = getter->as_function_group(); - CPPDeclaration *getter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); + if ((yyvsp[-2].u.identifier) != nullptr) { + CPPDeclaration *setter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid setter: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); + } else { + make_property->_set_function = setter->as_function_group(); + } + } + + current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-7])); } - - CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-4].u.identifier), getter->as_function_group(), NULL, current_scope, (yylsp[-6]).file); - current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-6])); } -#line 3927 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4166 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 28: -#line 557 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - CPPDeclaration *getter = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-4].u.identifier)->get_fully_scoped_name(), (yylsp[-4])); - - } else { - CPPDeclaration *setter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - CPPFunctionGroup *setter_func = NULL; - - if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid setter: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); - } else { - setter_func = setter->as_function_group(); - } - - CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-6].u.identifier), getter->as_function_group(), - setter_func, current_scope, (yylsp[-8]).file); - current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-8])); - } -} -#line 3952 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 29: -#line 578 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 575 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPDeclaration *getter = (yyvsp[-6].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-6].u.identifier)->get_fully_scoped_name(), (yylsp[-6])); } else { - CPPDeclaration *setter = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - CPPFunctionGroup *setter_func = NULL; + CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-8].u.identifier), CPPMakeProperty::T_normal, current_scope, (yylsp[-10]).file); + make_property->_get_function = getter->as_function_group(); - if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { + CPPDeclaration *setter = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid setter: " + (yyvsp[-4].u.identifier)->get_fully_scoped_name(), (yylsp[-4])); } else { - setter_func = setter->as_function_group(); + make_property->_set_function = setter->as_function_group(); } CPPDeclaration *deleter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (deleter == (CPPDeclaration *)NULL || deleter->get_subtype() != CPPDeclaration::ST_function_group) { + if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("reference to non-existent or invalid delete method: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); - deleter = NULL; - } - - CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-8].u.identifier), getter->as_function_group(), - setter_func, current_scope, (yylsp[-10]).file); - if (deleter) { + } else { make_property->_del_function = deleter->as_function_group(); } + current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-10])); } } -#line 3986 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4197 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 30: -#line 608 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 29: +#line 602 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPDeclaration *length_getter = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("reference to non-existent or invalid length method: " + (yyvsp[-4].u.identifier)->get_fully_scoped_name(), (yylsp[-4])); - length_getter = NULL; + length_getter = nullptr; } CPPDeclaration *getter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); + getter = nullptr; } - CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-6].u.identifier), getter->as_function_group(), NULL, current_scope, (yylsp[-8]).file); - make_property->_length_function = length_getter->as_function_group(); - current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-8])); + if (getter != nullptr && length_getter != nullptr) { + CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-6].u.identifier), CPPMakeProperty::T_sequence, current_scope, (yylsp[-8]).file); + make_property->_get_function = getter->as_function_group(); + make_property->_length_function = length_getter->as_function_group(); + current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-8])); + } } -#line 4007 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4222 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 31: -#line 625 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 30: +#line 623 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPDeclaration *length_getter = (yyvsp[-6].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("reference to non-existent or invalid length method: " + (yyvsp[-6].u.identifier)->get_fully_scoped_name(), (yylsp[-6])); - length_getter = NULL; + length_getter = nullptr; } CPPDeclaration *getter = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-4].u.identifier)->get_fully_scoped_name(), (yylsp[-4])); + getter = nullptr; + } + + if (getter != nullptr && length_getter != nullptr) { + CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-8].u.identifier), CPPMakeProperty::T_sequence, current_scope, (yylsp[-10]).file); + make_property->_get_function = getter->as_function_group(); + make_property->_length_function = length_getter->as_function_group(); - } else { CPPDeclaration *setter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - CPPFunctionGroup *setter_func = NULL; - - if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid setter: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); } else { - setter_func = setter->as_function_group(); + make_property->_set_function = setter->as_function_group(); } - CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-8].u.identifier), getter->as_function_group(), - setter_func, current_scope, (yylsp[-10]).file); - make_property->_length_function = length_getter->as_function_group(); current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-10])); } } -#line 4039 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4255 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 32: -#line 653 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 31: +#line 652 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPDeclaration *length_getter = (yyvsp[-8].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("reference to non-existent or invalid length method: " + (yyvsp[-8].u.identifier)->get_fully_scoped_name(), (yylsp[-8])); length_getter = NULL; } CPPDeclaration *getter = (yyvsp[-6].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-6].u.identifier)->get_fully_scoped_name(), (yylsp[-6])); + getter = nullptr; + } + + if (getter != nullptr && length_getter != nullptr) { + CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-10].u.identifier), CPPMakeProperty::T_sequence, current_scope, (yylsp[-12]).file); + make_property->_get_function = getter->as_function_group(); + make_property->_length_function = length_getter->as_function_group(); - } else { CPPDeclaration *setter = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - CPPFunctionGroup *setter_func = NULL; - - if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid setter: " + (yyvsp[-4].u.identifier)->get_fully_scoped_name(), (yylsp[-4])); } else { - setter_func = setter->as_function_group(); + make_property->_set_function = setter->as_function_group(); } CPPDeclaration *deleter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (deleter == (CPPDeclaration *)NULL || deleter->get_subtype() != CPPDeclaration::ST_function_group) { + if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("reference to non-existent or invalid delete method: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); - deleter = NULL; - } - - CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-10].u.identifier), getter->as_function_group(), - setter_func, current_scope, (yylsp[-12]).file); - make_property->_length_function = length_getter->as_function_group(); - if (deleter) { + } else { make_property->_del_function = deleter->as_function_group(); } + current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-12])); } } -#line 4080 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4295 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 32: +#line 688 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPDeclaration *length_getter = (yyvsp[-10].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid length method: " + (yyvsp[-10].u.identifier)->get_fully_scoped_name(), (yylsp[-10])); + length_getter = NULL; + } + + CPPDeclaration *getter = (yyvsp[-8].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-8].u.identifier)->get_fully_scoped_name(), (yylsp[-8])); + getter = nullptr; + } + + if (getter != nullptr && length_getter != nullptr) { + CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-12].u.identifier), CPPMakeProperty::T_sequence, current_scope, (yylsp[-14]).file); + make_property->_get_function = getter->as_function_group(); + make_property->_length_function = length_getter->as_function_group(); + + CPPDeclaration *setter = (yyvsp[-6].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid setter: " + (yyvsp[-6].u.identifier)->get_fully_scoped_name(), (yylsp[-6])); + } else { + make_property->_set_function = setter->as_function_group(); + } + + CPPDeclaration *deleter = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid delete method: " + (yyvsp[-4].u.identifier)->get_fully_scoped_name(), (yylsp[-4])); + } else { + make_property->_del_function = deleter->as_function_group(); + } + + CPPDeclaration *inserter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (inserter == nullptr || inserter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid append method: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); + } else { + make_property->_insert_function = inserter->as_function_group(); + } + + current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-14])); + } +} +#line 4342 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 33: -#line 690 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 731 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - CPPDeclaration *hasser = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (hasser == (CPPDeclaration *)NULL || hasser->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid has-function: " + (yyvsp[-4].u.identifier)->get_fully_scoped_name(), (yylsp[-4])); - } - CPPDeclaration *getter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); - } + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid item getter method: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); - if (hasser && getter) { - CPPMakeProperty *make_property; - make_property = new CPPMakeProperty((yyvsp[-6].u.identifier), - hasser->as_function_group(), - getter->as_function_group(), - NULL, NULL, - current_scope, (yylsp[-8]).file); - current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-8])); + } else { + CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-4].u.identifier), CPPMakeProperty::T_mapping, current_scope, (yylsp[-6]).file); + make_property->_get_function = getter->as_function_group(); + current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-6])); } } -#line 4106 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4358 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 34: -#line 712 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 743 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - CPPDeclaration *hasser = (yyvsp[-8].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (hasser == (CPPDeclaration *)NULL || hasser->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid has-function: " + (yyvsp[-8].u.identifier)->get_fully_scoped_name(), (yylsp[-8])); - } + CPPDeclaration *getter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); - CPPDeclaration *getter = (yyvsp[-6].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-6].u.identifier)->get_fully_scoped_name(), (yylsp[-6])); - } - - CPPDeclaration *setter = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid setter: " + (yyvsp[-4].u.identifier)->get_fully_scoped_name(), (yylsp[-4])); - } - - CPPDeclaration *clearer = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); - if (clearer == (CPPDeclaration *)NULL || clearer->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid clear-function: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); - } - - if (hasser && getter && setter && clearer) { + } else { CPPMakeProperty *make_property; - make_property = new CPPMakeProperty((yyvsp[-10].u.identifier), - hasser->as_function_group(), - getter->as_function_group(), - setter->as_function_group(), - clearer->as_function_group(), - current_scope, (yylsp[-12]).file); - current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-12])); + make_property = new CPPMakeProperty((yyvsp[-6].u.identifier), CPPMakeProperty::T_mapping, current_scope, (yylsp[-8]).file); + make_property->_get_function = getter->as_function_group(); + + CPPDeclaration *hasser = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid has/find method: " + (yyvsp[-4].u.identifier)->get_fully_scoped_name(), (yylsp[-4])); + } else { + make_property->_has_function = hasser->as_function_group(); + } + + current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-8])); } } -#line 4143 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4383 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 35: -#line 745 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 764 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPDeclaration *getter = (yyvsp[-5].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-5].u.identifier)->get_fully_scoped_name(), (yylsp[-5])); + + } else { + CPPMakeProperty *make_property = new CPPMakeProperty((yyvsp[-9].u.identifier), CPPMakeProperty::T_mapping, current_scope, (yylsp[-11]).file); + make_property->_get_function = getter->as_function_group(); + + CPPDeclaration *hasser = (yyvsp[-7].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid has/find method: " + (yyvsp[-7].u.identifier)->get_fully_scoped_name(), (yylsp[-7])); + } else { + make_property->_has_function = hasser->as_function_group(); + } + + CPPDeclaration *setter = (yyvsp[-3].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid setter: " + (yyvsp[-3].u.identifier)->get_fully_scoped_name(), (yylsp[-3])); + } else { + make_property->_set_function = setter->as_function_group(); + } + + if ((yyvsp[-2].u.identifier) != nullptr) { + CPPDeclaration *deleter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid delete method: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); + } else { + make_property->_del_function = deleter->as_function_group(); + } + } + + current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-11])); + } +} +#line 4423 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 36: +#line 800 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPDeclaration *length_getter = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid length method: " + (yyvsp[-4].u.identifier)->get_fully_scoped_name(), (yylsp[-4])); + length_getter = nullptr; + } + + CPPDeclaration *getter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid getter: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); + getter = nullptr; + } + + if (getter != nullptr && length_getter != nullptr) { + CPPMakeProperty *make_property = nullptr; + for (size_t i = 0; i < current_scope->_declarations.size(); ++i) { + make_property = current_scope->_declarations[i]->as_make_property(); + if (make_property != nullptr) { + if (make_property->get_fully_scoped_name() == (yyvsp[-6].u.identifier)->get_fully_scoped_name()) { + break; + } else { + make_property = nullptr; + } + } + } + if (make_property != nullptr) { + make_property->_get_key_function = getter->as_function_group(); + make_property->_length_function = length_getter->as_function_group(); + } else { + yyerror("reference to non-existent MAKE_MAP_PROPERTY: " + (yyvsp[-6].u.identifier)->get_fully_scoped_name(), (yylsp[-6])); + } + } +} +#line 4461 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 37: +#line 834 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPDeclaration *getter = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); + + } else { + CPPMakeProperty *make_property; + make_property = new CPPMakeProperty((yyvsp[-6].u.identifier), CPPMakeProperty::T_normal, + current_scope, (yylsp[-8]).file); + make_property->_get_function = getter->as_function_group(); + + CPPDeclaration *hasser = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid has/find method: " + (yyvsp[-4].u.identifier)->get_fully_scoped_name(), (yylsp[-4])); + } else { + make_property->_has_function = hasser->as_function_group(); + } + + current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-8])); + } +} +#line 4487 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 38: +#line 856 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPDeclaration *getter = (yyvsp[-6].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid getter: " + (yyvsp[-6].u.identifier)->get_fully_scoped_name(), (yylsp[-6])); + + } else { + CPPMakeProperty *make_property; + make_property = new CPPMakeProperty((yyvsp[-10].u.identifier), CPPMakeProperty::T_normal, + current_scope, (yylsp[-12]).file); + make_property->_get_function = getter->as_function_group(); + + CPPDeclaration *hasser = (yyvsp[-8].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid has/find method: " + (yyvsp[-8].u.identifier)->get_fully_scoped_name(), (yylsp[-8])); + } else { + make_property->_has_function = hasser->as_function_group(); + } + + CPPDeclaration *setter = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid setter: " + (yyvsp[-4].u.identifier)->get_fully_scoped_name(), (yylsp[-4])); + } else { + make_property->_set_function = setter->as_function_group(); + } + + CPPDeclaration *clearer = (yyvsp[-2].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (clearer == nullptr || clearer->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid clear method: " + (yyvsp[-2].u.identifier)->get_fully_scoped_name(), (yylsp[-2])); + } else { + make_property->_clear_function = clearer->as_function_group(); + } + + current_scope->add_declaration(make_property, global_scope, current_lexer, (yylsp[-12])); + } +} +#line 4527 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 39: +#line 892 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPDeclaration *length_getter = (yyvsp[-4].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { @@ -4165,11 +4549,11 @@ yyreduce: current_scope->add_declaration(make_seq, global_scope, current_lexer, (yylsp[-8])); } } -#line 4169 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4553 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 36: -#line 767 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 40: +#line 914 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPExpression::Result result = (yyvsp[-4].u.expr)->evaluate(); if (result._type == CPPExpression::RT_error) { @@ -4180,11 +4564,11 @@ yyreduce: yywarning("static_assert failed: " + str.str(), (yylsp[-4])); } } -#line 4184 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4568 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 37: -#line 778 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 41: +#line 925 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // This alternative version of static_assert was introduced in C++17. CPPExpression::Result result = (yyvsp[-2].u.expr)->evaluate(); @@ -4194,55 +4578,55 @@ yyreduce: yywarning("static_assert failed", (yylsp[-2])); } } -#line 4198 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4582 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 38: -#line 791 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 42: +#line 938 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPScope *new_scope = new CPPScope(current_scope, CPPNameComponent("temp"), V_public); push_scope(new_scope); } -#line 4208 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4592 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 39: -#line 797 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 43: +#line 944 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { delete current_scope; pop_scope(); } -#line 4217 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4601 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 40: -#line 806 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 44: +#line 953 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.integer) = 0; } -#line 4225 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4609 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 41: -#line 810 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 45: +#line 957 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // This isn't really a storage class, but it helps with parsing. (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_const; } -#line 4234 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4618 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 42: -#line 815 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 46: +#line 962 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_extern; } -#line 4242 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4626 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 43: -#line 819 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 47: +#line 966 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_extern; if ((yyvsp[-1].str) == "C") { @@ -4253,108 +4637,124 @@ yyreduce: yywarning("Ignoring unknown linkage type \"" + (yyvsp[-1].str) + "\"", (yylsp[-1])); } } -#line 4257 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 44: -#line 830 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_static; -} -#line 4265 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 45: -#line 834 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_inline; -} -#line 4273 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 46: -#line 838 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_virtual; -} -#line 4281 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 47: -#line 842 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_explicit; -} -#line 4289 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4641 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 48: -#line 846 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 977 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_register; + (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_static; } -#line 4297 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4649 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 49: -#line 850 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 981 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_volatile; + (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_inline; } -#line 4305 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4657 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 50: -#line 854 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 985 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_mutable; + (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_virtual; } -#line 4313 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4665 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 51: -#line 858 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 989 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_constexpr; + (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_explicit; } -#line 4321 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4673 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 52: -#line 862 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 993 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_blocking; + (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_register; } -#line 4329 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4681 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 53: -#line 866 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 997 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_extension; + (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_volatile; } -#line 4337 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4689 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 54: -#line 870 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1001 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_thread_local; + (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_mutable; } -#line 4345 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4697 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 55: -#line 874 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1005 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_constexpr; +} +#line 4705 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 56: +#line 1009 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_blocking; +} +#line 4713 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 57: +#line 1013 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_extension; +} +#line 4721 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 58: +#line 1017 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_thread_local; +} +#line 4729 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 59: +#line 1021 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // Ignore attribute specifiers for now. (yyval.u.integer) = (yyvsp[0].u.integer); } -#line 4354 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4738 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 60: -#line 892 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1026 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[0].u.integer); +} +#line 4746 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 61: +#line 1030 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[0].u.integer); +} +#line 4754 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 67: +#line 1048 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // We don't need to push/pop type, because we can't nest // type_like_declaration. @@ -4365,19 +4765,19 @@ yyreduce: } push_storage_class((yyvsp[-1].u.integer)); } -#line 4369 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4769 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 61: -#line 903 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 68: +#line 1059 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_storage_class(); } -#line 4377 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4777 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 62: -#line 908 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 69: +#line 1064 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // We don't really care about the storage class here. In fact, it's // not actually legal to define a class or struct using a particular @@ -4386,11 +4786,36 @@ yyreduce: current_scope->add_declaration((yyvsp[-1].u.decl), global_scope, current_lexer, (yylsp[-1])); } -#line 4390 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4790 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 63: -#line 917 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 70: +#line 1073 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + if ((yyvsp[0].u.instance) != (CPPInstance *)NULL) { + // Push the scope so that the initializers can make use of things defined + // in the class body. + push_scope((yyvsp[0].u.instance)->get_scope(current_scope, global_scope)); + (yyvsp[0].u.instance)->_storage_class |= (current_storage_class | (yyvsp[-1].u.integer)); + } +} +#line 4803 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 71: +#line 1082 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + if ((yyvsp[-2].u.instance) != (CPPInstance *)NULL) { + pop_scope(); + current_scope->add_declaration((yyvsp[-2].u.instance), global_scope, current_lexer, (yylsp[-2])); + (yyvsp[-2].u.instance)->set_initializer((yyvsp[0].u.expr)); + } +} +#line 4815 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 72: +#line 1090 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if ((yyvsp[-1].u.instance) != (CPPInstance *)NULL) { (yyvsp[-1].u.instance)->_storage_class |= (current_storage_class | (yyvsp[-2].u.integer)); @@ -4398,23 +4823,11 @@ yyreduce: (yyvsp[-1].u.instance)->set_initializer((yyvsp[0].u.expr)); } } -#line 4402 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4827 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 64: -#line 925 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - if ((yyvsp[-1].u.instance) != (CPPInstance *)NULL) { - (yyvsp[-1].u.instance)->_storage_class |= (current_storage_class | (yyvsp[-2].u.integer)); - current_scope->add_declaration((yyvsp[-1].u.instance), global_scope, current_lexer, (yylsp[-1])); - (yyvsp[-1].u.instance)->set_initializer((yyvsp[0].u.expr)); - } -} -#line 4414 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 66: -#line 941 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 74: +#line 1106 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if (current_storage_class & CPPInstance::SC_const) { (yyvsp[-1].u.inst_ident)->add_modifier(IIT_const); @@ -4425,11 +4838,11 @@ yyreduce: inst->set_initializer((yyvsp[0].u.expr)); current_scope->add_declaration(inst, global_scope, current_lexer, (yylsp[-1])); } -#line 4429 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4842 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 67: -#line 952 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 75: +#line 1117 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if (current_storage_class & CPPInstance::SC_const) { (yyvsp[-3].u.inst_ident)->add_modifier(IIT_const); @@ -4440,11 +4853,11 @@ yyreduce: inst->set_initializer((yyvsp[-2].u.expr)); current_scope->add_declaration(inst, global_scope, current_lexer, (yylsp[-3])); } -#line 4444 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4857 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 68: -#line 967 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 76: +#line 1132 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // We don't need to push/pop type, because we can't nest // multiple_var_declarations. @@ -4455,19 +4868,19 @@ yyreduce: } push_storage_class((yyvsp[-1].u.integer)); } -#line 4459 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4872 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 69: -#line 978 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 77: +#line 1143 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_storage_class(); } -#line 4467 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4880 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 70: -#line 982 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 78: +#line 1147 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if ((yyvsp[-1].u.instance) != (CPPDeclaration *)NULL) { CPPInstance *inst = (yyvsp[-1].u.instance)->as_instance(); @@ -4479,11 +4892,11 @@ yyreduce: } } } -#line 4483 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4896 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 71: -#line 997 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 79: +#line 1162 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if (current_storage_class & CPPInstance::SC_const) { (yyvsp[-1].u.inst_ident)->add_modifier(IIT_const); @@ -4492,11 +4905,11 @@ yyreduce: CPPTypedefType *typedef_type = new CPPTypedefType(target_type, (yyvsp[-1].u.inst_ident), current_scope, (yylsp[-1]).file); current_scope->add_declaration(CPPType::new_type(typedef_type), global_scope, current_lexer, (yylsp[-1])); } -#line 4496 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4909 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 72: -#line 1006 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 80: +#line 1171 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if (current_storage_class & CPPInstance::SC_const) { (yyvsp[-3].u.inst_ident)->add_modifier(IIT_const); @@ -4505,19 +4918,19 @@ yyreduce: CPPTypedefType *typedef_type = new CPPTypedefType(target_type, (yyvsp[-3].u.inst_ident), current_scope, (yylsp[-3]).file); current_scope->add_declaration(CPPType::new_type(typedef_type), global_scope, current_lexer, (yylsp[-3])); } -#line 4509 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4922 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 73: -#line 1020 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 81: +#line 1185 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { push_scope((yyvsp[-1].u.identifier)->get_scope(current_scope, global_scope)); } -#line 4517 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4930 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 74: -#line 1024 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 82: +#line 1189 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type; if ((yyvsp[-5].u.identifier)->get_simple_name() == current_scope->get_simple_name() || @@ -4537,19 +4950,19 @@ yyreduce: (yyval.u.instance) = new CPPInstance(type, ii, 0, (yylsp[-5]).file); } -#line 4541 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4954 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 75: -#line 1044 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 83: +#line 1209 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { push_scope((yyvsp[-1].u.identifier)->get_scope(current_scope, global_scope)); } -#line 4549 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4962 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 76: -#line 1048 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 84: +#line 1213 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_scope(); CPPType *type; @@ -4567,19 +4980,19 @@ yyreduce: (yyval.u.instance) = new CPPInstance(type, ii, 0, (yylsp[-5]).file); } -#line 4571 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4984 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 77: -#line 1071 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 85: +#line 1236 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { push_scope((yyvsp[-1].u.identifier)->get_scope(current_scope, global_scope)); } -#line 4579 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4992 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 78: -#line 1075 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 86: +#line 1240 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_scope(); if ((yyvsp[-5].u.identifier)->is_scoped()) { @@ -4598,19 +5011,19 @@ yyreduce: (yyval.u.instance) = new CPPInstance(type, ii, 0, (yylsp[-5]).file); } } -#line 4602 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5015 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 79: -#line 1101 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 87: +#line 1266 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { push_scope((yyvsp[-2].u.inst_ident)->get_scope(current_scope, global_scope)); } -#line 4610 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5023 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 80: -#line 1105 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 88: +#line 1270 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_scope(); CPPType *type = (yyvsp[-10].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); @@ -4624,19 +5037,19 @@ yyreduce: ii->add_func_modifier((yyvsp[-3].u.param_list), (yyvsp[-1].u.integer)); (yyval.u.instance) = new CPPInstance(type, ii, 0, (yylsp[-10]).file); } -#line 4628 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5041 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 81: -#line 1119 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 89: +#line 1284 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { push_scope((yyvsp[-2].u.inst_ident)->get_scope(current_scope, global_scope)); } -#line 4636 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5049 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 82: -#line 1123 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 90: +#line 1288 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_scope(); CPPType *type = (yyvsp[-11].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); @@ -4650,21 +5063,21 @@ yyreduce: ii->add_func_modifier((yyvsp[-3].u.param_list), (yyvsp[-1].u.integer)); (yyval.u.instance) = new CPPInstance(type, ii, 0, (yylsp[-11]).file); } -#line 4654 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5067 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 83: -#line 1139 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 91: +#line 1304 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if ((yyvsp[-3].u.identifier) != NULL) { push_scope((yyvsp[-3].u.identifier)->get_scope(current_scope, global_scope)); } } -#line 4664 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5077 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 84: -#line 1145 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 92: +#line 1310 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if ((yyvsp[-7].u.identifier) != NULL) { pop_scope(); @@ -4689,21 +5102,21 @@ yyreduce: (yyval.u.instance) = CPPInstance::make_typecast_function (new CPPInstance((yyvsp[-6].u.type), (yyvsp[-5].u.inst_ident), 0, (yylsp[-5]).file), ident, (yyvsp[-2].u.param_list), (yyvsp[0].u.integer)); } -#line 4693 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5106 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 85: -#line 1170 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 93: +#line 1335 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if ((yyvsp[-4].u.identifier) != NULL) { push_scope((yyvsp[-4].u.identifier)->get_scope(current_scope, global_scope)); } } -#line 4703 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5116 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 86: -#line 1176 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 94: +#line 1341 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { if ((yyvsp[-8].u.identifier) != NULL) { pop_scope(); @@ -4719,11 +5132,11 @@ yyreduce: (yyval.u.instance) = CPPInstance::make_typecast_function (new CPPInstance((yyvsp[-6].u.type), (yyvsp[-5].u.inst_ident), 0, (yylsp[-5]).file), ident, (yyvsp[-2].u.param_list), (yyvsp[0].u.integer)); } -#line 4723 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5136 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 87: -#line 1196 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 95: +#line 1361 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPDeclaration *decl = (yyvsp[0].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); @@ -4733,557 +5146,574 @@ yyreduce: (yyval.u.instance) = (CPPInstance *)NULL; } } -#line 4737 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 88: -#line 1209 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = 0; -} -#line 4745 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 89: -#line 1213 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_const_method; -} -#line 4753 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 90: -#line 1217 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_volatile_method; -} -#line 4761 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 91: -#line 1221 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_noexcept; -} -#line 4769 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 92: -#line 1225 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_final; -} -#line 4777 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 93: -#line 1229 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_override; -} -#line 4785 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 94: -#line 1233 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_lvalue_method; -} -#line 4793 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 95: -#line 1237 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_rvalue_method; -} -#line 4801 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5150 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 96: -#line 1241 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1374 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - // Used for lambdas, currently ignored. - (yyval.u.integer) = (yyvsp[-1].u.integer); + (yyval.u.integer) = 0; } -#line 4810 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5158 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 97: -#line 1246 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1378 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[-3].u.integer); + (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_const_method; } -#line 4818 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5166 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 98: -#line 1250 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1382 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[-4].u.integer); + (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_volatile_method; } -#line 4826 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5174 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 99: -#line 1254 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1386 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[-5].u.integer); -} -#line 4834 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 100: -#line 1265 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "!"; -} -#line 4842 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 101: -#line 1269 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "~"; -} -#line 4850 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 102: -#line 1273 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "*"; -} -#line 4858 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 103: -#line 1277 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "/"; -} -#line 4866 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 104: -#line 1281 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "%"; -} -#line 4874 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 105: -#line 1285 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "+"; -} -#line 4882 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 106: -#line 1289 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "-"; -} -#line 4890 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 107: -#line 1293 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "|"; -} -#line 4898 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 108: -#line 1297 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "&"; -} -#line 4906 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 109: -#line 1301 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "^"; -} -#line 4914 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 110: -#line 1305 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "||"; -} -#line 4922 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 111: -#line 1309 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "&&"; -} -#line 4930 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 112: -#line 1313 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "=="; -} -#line 4938 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 113: -#line 1317 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "!="; -} -#line 4946 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 114: -#line 1321 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "<="; -} -#line 4954 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 115: -#line 1325 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = ">="; -} -#line 4962 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 116: -#line 1329 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "<"; -} -#line 4970 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 117: -#line 1333 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = ">"; -} -#line 4978 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 118: -#line 1337 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "<<"; -} -#line 4986 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 119: -#line 1341 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = ">>"; -} -#line 4994 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 120: -#line 1345 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "="; -} -#line 5002 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 121: -#line 1349 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = ","; -} -#line 5010 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 122: -#line 1353 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "++"; -} -#line 5018 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 123: -#line 1357 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "--"; -} -#line 5026 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 124: -#line 1361 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "*="; -} -#line 5034 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 125: -#line 1365 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "/="; -} -#line 5042 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 126: -#line 1369 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "%="; -} -#line 5050 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 127: -#line 1373 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "+="; -} -#line 5058 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 128: -#line 1377 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "-="; -} -#line 5066 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 129: -#line 1381 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "|="; -} -#line 5074 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 130: -#line 1385 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "&="; -} -#line 5082 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 131: -#line 1389 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "^="; -} -#line 5090 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 132: -#line 1393 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "<<="; -} -#line 5098 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 133: -#line 1397 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = ">>="; -} -#line 5106 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 134: -#line 1401 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "->"; -} -#line 5114 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 135: -#line 1405 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "[]"; -} -#line 5122 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 136: -#line 1409 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "()"; -} -#line 5130 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 137: -#line 1413 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "new"; -} -#line 5138 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 138: -#line 1417 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "delete"; -} -#line 5146 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 141: -#line 1429 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - push_scope(new CPPTemplateScope(current_scope)); -} -#line 5154 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 142: -#line 1433 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - pop_scope(); -} -#line 5162 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 146: -#line 1446 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - CPPTemplateScope *ts = current_scope->as_template_scope(); - assert(ts != NULL); - ts->add_template_parameter((yyvsp[0].u.decl)); -} -#line 5172 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 147: -#line 1452 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - CPPTemplateScope *ts = current_scope->as_template_scope(); - assert(ts != NULL); - ts->add_template_parameter((yyvsp[0].u.decl)); + (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_noexcept; } #line 5182 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 150: -#line 1466 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 100: +#line 1399 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.decl) = CPPType::new_type(new CPPClassTemplateParameter((CPPIdentifier *)NULL)); + (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_final; } #line 5190 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 151: -#line 1470 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 101: +#line 1403 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.decl) = CPPType::new_type(new CPPClassTemplateParameter((yyvsp[0].u.identifier))); + (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_override; } #line 5198 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 152: -#line 1474 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 102: +#line 1407 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.decl) = CPPType::new_type(new CPPClassTemplateParameter((yyvsp[-2].u.identifier), (yyvsp[0].u.type))); + (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_lvalue_method; } #line 5206 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; + case 103: +#line 1411 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_rvalue_method; +} +#line 5214 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 104: +#line 1415 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + // Used for lambdas, currently ignored. + (yyval.u.integer) = (yyvsp[-1].u.integer); +} +#line 5223 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 105: +#line 1420 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + // Used for lambdas in C++17, currently ignored. + (yyval.u.integer) = (yyvsp[-1].u.integer); +} +#line 5232 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 106: +#line 1425 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[-3].u.integer); +} +#line 5240 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 107: +#line 1429 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[-4].u.integer); +} +#line 5248 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 108: +#line 1433 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[-5].u.integer); +} +#line 5256 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 109: +#line 1437 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[-3].u.integer); +} +#line 5264 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 110: +#line 1444 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "!"; +} +#line 5272 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 111: +#line 1448 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "~"; +} +#line 5280 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 112: +#line 1452 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "*"; +} +#line 5288 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 113: +#line 1456 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "/"; +} +#line 5296 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 114: +#line 1460 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "%"; +} +#line 5304 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 115: +#line 1464 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "+"; +} +#line 5312 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 116: +#line 1468 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "-"; +} +#line 5320 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 117: +#line 1472 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "|"; +} +#line 5328 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 118: +#line 1476 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "&"; +} +#line 5336 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 119: +#line 1480 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "^"; +} +#line 5344 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 120: +#line 1484 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "||"; +} +#line 5352 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 121: +#line 1488 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "&&"; +} +#line 5360 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 122: +#line 1492 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "=="; +} +#line 5368 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 123: +#line 1496 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "!="; +} +#line 5376 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 124: +#line 1500 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "<="; +} +#line 5384 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 125: +#line 1504 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = ">="; +} +#line 5392 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 126: +#line 1508 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "<"; +} +#line 5400 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 127: +#line 1512 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = ">"; +} +#line 5408 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 128: +#line 1516 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "<<"; +} +#line 5416 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 129: +#line 1520 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = ">>"; +} +#line 5424 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 130: +#line 1524 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "="; +} +#line 5432 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 131: +#line 1528 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = ","; +} +#line 5440 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 132: +#line 1532 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "++"; +} +#line 5448 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 133: +#line 1536 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "--"; +} +#line 5456 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 134: +#line 1540 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "*="; +} +#line 5464 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 135: +#line 1544 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "/="; +} +#line 5472 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 136: +#line 1548 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "%="; +} +#line 5480 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 137: +#line 1552 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "+="; +} +#line 5488 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 138: +#line 1556 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "-="; +} +#line 5496 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 139: +#line 1560 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "|="; +} +#line 5504 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 140: +#line 1564 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "&="; +} +#line 5512 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 141: +#line 1568 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "^="; +} +#line 5520 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 142: +#line 1572 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "<<="; +} +#line 5528 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 143: +#line 1576 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = ">>="; +} +#line 5536 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 144: +#line 1580 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "->"; +} +#line 5544 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 145: +#line 1584 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "[]"; +} +#line 5552 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 146: +#line 1588 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "()"; +} +#line 5560 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 147: +#line 1592 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "new"; +} +#line 5568 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 148: +#line 1596 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.str) = "delete"; +} +#line 5576 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + case 153: -#line 1478 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1610 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + push_scope(new CPPTemplateScope(current_scope)); +} +#line 5584 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 154: +#line 1614 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + pop_scope(); +} +#line 5592 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 159: +#line 1628 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPTemplateScope *ts = current_scope->as_template_scope(); + assert(ts != NULL); + ts->add_template_parameter((yyvsp[0].u.decl)); +} +#line 5602 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 160: +#line 1634 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPTemplateScope *ts = current_scope->as_template_scope(); + assert(ts != NULL); + ts->add_template_parameter((yyvsp[0].u.decl)); +} +#line 5612 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 163: +#line 1648 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.decl) = CPPType::new_type(new CPPClassTemplateParameter((CPPIdentifier *)NULL)); +} +#line 5620 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 164: +#line 1652 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.decl) = CPPType::new_type(new CPPClassTemplateParameter((yyvsp[0].u.identifier))); +} +#line 5628 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 165: +#line 1656 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.decl) = CPPType::new_type(new CPPClassTemplateParameter((yyvsp[-2].u.identifier), (yyvsp[0].u.type))); +} +#line 5636 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 166: +#line 1660 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPClassTemplateParameter *ctp = new CPPClassTemplateParameter((CPPIdentifier *)NULL); ctp->_packed = true; (yyval.u.decl) = CPPType::new_type(ctp); } -#line 5216 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5646 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 154: -#line 1484 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 167: +#line 1666 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPClassTemplateParameter *ctp = new CPPClassTemplateParameter((yyvsp[0].u.identifier)); ctp->_packed = true; (yyval.u.decl) = CPPType::new_type(ctp); } -#line 5226 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5656 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 155: -#line 1490 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 168: +#line 1672 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPInstance *inst = new CPPInstance((yyvsp[-2].u.type), (yyvsp[-1].u.inst_ident), 0, (yylsp[-1]).file); inst->set_initializer((yyvsp[0].u.expr)); (yyval.u.decl) = inst; } -#line 5236 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5666 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 156: -#line 1496 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 169: +#line 1678 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyvsp[-1].u.inst_ident)->add_modifier(IIT_const); CPPInstance *inst = new CPPInstance((yyvsp[-2].u.type), (yyvsp[-1].u.inst_ident), 0, (yylsp[-1]).file); inst->set_initializer((yyvsp[0].u.expr)); (yyval.u.decl) = inst; } -#line 5247 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5677 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 157: -#line 1503 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 170: +#line 1685 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPInstance *inst = new CPPInstance((yyvsp[-1].u.type), (yyvsp[0].u.inst_ident), 0, (yylsp[0]).file); (yyval.u.decl) = inst; } -#line 5256 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5686 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 158: -#line 1508 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 171: +#line 1690 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyvsp[0].u.inst_ident)->add_modifier(IIT_const); CPPInstance *inst = new CPPInstance((yyvsp[-1].u.type), (yyvsp[0].u.inst_ident), 0, (yylsp[0]).file); (yyval.u.decl) = inst; } -#line 5266 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5696 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 159: -#line 1517 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 172: +#line 1699 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = CPPType::new_type((yyvsp[0].u.simple_type)); } -#line 5274 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5704 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 160: -#line 1521 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 173: +#line 1703 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { yywarning("Not a type: " + (yyvsp[0].u.identifier)->get_fully_scoped_name(), (yylsp[0])); (yyval.u.type) = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_unknown)); } -#line 5283 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5713 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 161: -#line 1526 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 174: +#line 1708 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); if ((yyval.u.type) == NULL) { @@ -5291,11 +5721,11 @@ yyreduce: } assert((yyval.u.type) != NULL); } -#line 5295 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5725 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 162: -#line 1534 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 175: +#line 1716 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); if ((yyval.u.type) == NULL) { @@ -5303,19 +5733,19 @@ yyreduce: } assert((yyval.u.type) != NULL); } -#line 5307 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5737 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 163: -#line 1546 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 176: +#line 1728 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); } -#line 5315 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5745 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 164: -#line 1550 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 177: +#line 1732 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // For an operator function. We implement this simply by building a // ficticious name for the function; in other respects it's just @@ -5329,11 +5759,11 @@ yyreduce: (yyval.u.inst_ident) = new CPPInstanceIdentifier(ident); } -#line 5333 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5763 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 165: -#line 1564 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 178: +#line 1746 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // A C++11 literal operator. if (!(yyvsp[-1].str).empty()) { @@ -5348,83 +5778,83 @@ yyreduce: (yyval.u.inst_ident) = new CPPInstanceIdentifier(ident); } -#line 5352 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5782 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 166: -#line 1579 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 179: +#line 1761 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); (yyval.u.inst_ident)->add_modifier(IIT_const); } -#line 5361 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5791 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 167: -#line 1584 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 180: +#line 1766 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); (yyval.u.inst_ident)->add_modifier(IIT_volatile); } -#line 5370 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5800 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 168: -#line 1589 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 181: +#line 1771 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); (yyval.u.inst_ident)->add_modifier(IIT_pointer); } -#line 5379 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5809 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 169: -#line 1594 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 182: +#line 1776 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); (yyval.u.inst_ident)->add_modifier(IIT_reference); } -#line 5388 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5818 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 170: -#line 1599 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 183: +#line 1781 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); } -#line 5397 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5827 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 171: -#line 1604 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 184: +#line 1786 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); (yyval.u.inst_ident)->add_scoped_pointer_modifier((yyvsp[-2].u.identifier)); } -#line 5406 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5836 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 172: -#line 1609 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 185: +#line 1791 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.inst_ident) = (yyvsp[-3].u.inst_ident); (yyval.u.inst_ident)->add_array_modifier((yyvsp[-1].u.expr)); } -#line 5415 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5845 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 173: -#line 1614 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 186: +#line 1796 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.inst_ident) = (yyvsp[-1].u.inst_ident); (yyval.u.inst_ident)->add_modifier(IIT_paren); } -#line 5424 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5854 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 174: -#line 1619 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 187: +#line 1801 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // Create a scope for this function (in case it is a function) CPPScope *scope = new CPPScope((yyvsp[-1].u.inst_ident)->get_scope(current_scope, global_scope), @@ -5437,11 +5867,11 @@ yyreduce: push_scope(scope); } -#line 5432 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5871 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 175: -#line 1623 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 188: +#line 1814 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_scope(); (yyval.u.inst_ident) = (yyvsp[-5].u.inst_ident); @@ -5455,11 +5885,11 @@ yyreduce: (yyval.u.inst_ident)->add_func_modifier((yyvsp[-2].u.param_list), (yyvsp[0].u.integer)); } } -#line 5450 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5889 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 176: -#line 1641 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 189: +#line 1832 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // This is handled a bit awkwardly right now. Ideally it'd be wrapped // up in the instance_identifier rule, but then more needs to happen in @@ -5469,886 +5899,910 @@ yyreduce: } (yyval.u.inst_ident) = (yyvsp[-1].u.inst_ident); } -#line 5464 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5903 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 177: -#line 1651 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 190: +#line 1842 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // Bitfield definition. (yyvsp[-2].u.inst_ident)->_bit_width = (yyvsp[0].u.integer); (yyval.u.inst_ident) = (yyvsp[-2].u.inst_ident); } -#line 5474 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5913 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 178: -#line 1661 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 191: +#line 1852 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = NULL; } -#line 5482 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5921 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 179: -#line 1665 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 192: +#line 1856 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = (yyvsp[0].u.inst_ident)->unroll_type((yyvsp[-1].u.type)); } -#line 5490 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5929 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 180: -#line 1669 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 193: +#line 1860 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyvsp[0].u.inst_ident)->add_modifier(IIT_const); (yyval.u.type) = (yyvsp[0].u.inst_ident)->unroll_type((yyvsp[-1].u.type)); } -#line 5499 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 181: -#line 1678 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = new CPPParameterList; -} -#line 5507 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 182: -#line 1682 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = new CPPParameterList; - (yyval.u.param_list)->_includes_ellipsis = true; -} -#line 5516 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 183: -#line 1687 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = (yyvsp[0].u.param_list); -} -#line 5524 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 184: -#line 1691 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = (yyvsp[-2].u.param_list); - (yyval.u.param_list)->_includes_ellipsis = true; -} -#line 5533 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 185: -#line 1696 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = (yyvsp[-1].u.param_list); - (yyval.u.param_list)->_includes_ellipsis = true; -} -#line 5542 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 186: -#line 1704 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = new CPPParameterList; - (yyval.u.param_list)->_parameters.push_back((yyvsp[0].u.instance)); -} -#line 5551 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 187: -#line 1709 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = (yyvsp[-2].u.param_list); - (yyval.u.param_list)->_parameters.push_back((yyvsp[0].u.instance)); -} -#line 5560 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 188: -#line 1717 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = new CPPParameterList; -} -#line 5568 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 189: -#line 1721 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = new CPPParameterList; - (yyval.u.param_list)->_includes_ellipsis = true; -} -#line 5577 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 190: -#line 1726 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = (yyvsp[0].u.param_list); -} -#line 5585 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 191: -#line 1730 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = (yyvsp[-2].u.param_list); - (yyval.u.param_list)->_includes_ellipsis = true; -} -#line 5594 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 192: -#line 1735 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = (yyvsp[-1].u.param_list); - (yyval.u.param_list)->_includes_ellipsis = true; -} -#line 5603 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 193: -#line 1743 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = new CPPParameterList; - (yyval.u.param_list)->_parameters.push_back((yyvsp[0].u.instance)); -} -#line 5612 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5938 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 194: -#line 1748 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1869 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.identifier) = NULL; +} +#line 5946 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 195: +#line 1873 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.identifier) = (yyvsp[0].u.identifier); +} +#line 5954 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 196: +#line 1881 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.param_list) = new CPPParameterList; +} +#line 5962 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 197: +#line 1885 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.param_list) = new CPPParameterList; + (yyval.u.param_list)->_includes_ellipsis = true; +} +#line 5971 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 198: +#line 1890 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.param_list) = (yyvsp[0].u.param_list); +} +#line 5979 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 199: +#line 1894 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.param_list) = (yyvsp[-2].u.param_list); + (yyval.u.param_list)->_includes_ellipsis = true; +} +#line 5988 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 200: +#line 1899 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.param_list) = (yyvsp[-1].u.param_list); + (yyval.u.param_list)->_includes_ellipsis = true; +} +#line 5997 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 201: +#line 1907 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.param_list) = new CPPParameterList; + (yyval.u.param_list)->_parameters.push_back((yyvsp[0].u.instance)); +} +#line 6006 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 202: +#line 1912 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.param_list) = (yyvsp[-2].u.param_list); (yyval.u.param_list)->_parameters.push_back((yyvsp[0].u.instance)); } -#line 5621 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 195: -#line 1756 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5629 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 196: -#line 1760 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 5637 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 197: -#line 1767 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5645 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 198: -#line 1771 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 5653 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 199: -#line 1778 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5661 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 200: -#line 1782 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5669 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 201: -#line 1786 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5677 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 202: -#line 1790 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::get_default()); -} -#line 5685 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6015 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 203: -#line 1794 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1920 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::get_delete()); + (yyval.u.param_list) = new CPPParameterList; } -#line 5693 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6023 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 204: -#line 1801 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1924 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (CPPExpression *)NULL; + (yyval.u.param_list) = new CPPParameterList; + (yyval.u.param_list)->_includes_ellipsis = true; } -#line 5701 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6032 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 205: -#line 1805 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1929 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (CPPExpression *)NULL; + (yyval.u.param_list) = (yyvsp[0].u.param_list); } -#line 5709 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6040 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 206: -#line 1809 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1933 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (yyvsp[-1].u.expr); + (yyval.u.param_list) = (yyvsp[-2].u.param_list); + (yyval.u.param_list)->_includes_ellipsis = true; } -#line 5717 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6049 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 207: -#line 1813 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1938 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::get_default()); + (yyval.u.param_list) = (yyvsp[-1].u.param_list); + (yyval.u.param_list)->_includes_ellipsis = true; } -#line 5725 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6058 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 208: -#line 1817 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1946 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::get_delete()); + (yyval.u.param_list) = new CPPParameterList; + (yyval.u.param_list)->_parameters.push_back((yyvsp[0].u.instance)); } -#line 5733 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6067 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 209: -#line 1821 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1951 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.param_list) = (yyvsp[-2].u.param_list); + (yyval.u.param_list)->_parameters.push_back((yyvsp[0].u.instance)); +} +#line 6076 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 210: +#line 1959 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = (CPPExpression *)NULL; } -#line 5741 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6084 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 211: +#line 1963 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[0].u.expr); +} +#line 6092 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 212: +#line 1970 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (CPPExpression *)NULL; +} +#line 6100 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 213: -#line 1834 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1974 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { + (yyval.u.expr) = (yyvsp[0].u.expr); } -#line 5748 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6108 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 214: +#line 1981 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (CPPExpression *)NULL; +} +#line 6116 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 215: +#line 1985 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (CPPExpression *)NULL; +} +#line 6124 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 216: +#line 1989 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (CPPExpression *)NULL; +} +#line 6132 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 217: -#line 1843 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1993 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.instance) = new CPPInstance((yyvsp[-2].u.type), (yyvsp[-1].u.inst_ident), 0, (yylsp[-1]).file); - (yyval.u.instance)->set_initializer((yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(CPPExpression::get_default()); } -#line 5757 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6140 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 218: -#line 1848 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1997 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyvsp[-1].u.inst_ident)->add_modifier(IIT_const); - (yyval.u.instance) = new CPPInstance((yyvsp[-2].u.type), (yyvsp[-1].u.inst_ident), 0, (yylsp[-1]).file); - (yyval.u.instance)->set_initializer((yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(CPPExpression::get_delete()); } -#line 5767 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6148 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 219: -#line 1854 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2004 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyvsp[-1].u.inst_ident)->add_modifier(IIT_const); - (yyval.u.instance) = new CPPInstance((yyvsp[-2].u.type), (yyvsp[-1].u.inst_ident), 0, (yylsp[-2]).file); - (yyval.u.instance)->set_initializer((yyvsp[0].u.expr)); + (yyval.u.expr) = (CPPExpression *)NULL; } -#line 5777 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6156 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 220: -#line 1860 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2008 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (CPPExpression *)NULL; +} +#line 6164 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 221: +#line 2012 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[-1].u.expr); +} +#line 6172 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 222: +#line 2016 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::get_default()); +} +#line 6180 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 223: +#line 2020 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::get_delete()); +} +#line 6188 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 224: +#line 2024 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (CPPExpression *)NULL; +} +#line 6196 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 228: +#line 2037 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { +} +#line 6203 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 232: +#line 2046 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.instance) = new CPPInstance((yyvsp[-2].u.type), (yyvsp[-1].u.inst_ident), 0, (yylsp[-1]).file); (yyval.u.instance)->set_initializer((yyvsp[0].u.expr)); } -#line 5786 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6212 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 221: -#line 1865 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 233: +#line 2051 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyvsp[-1].u.inst_ident)->add_modifier(IIT_const); (yyval.u.instance) = new CPPInstance((yyvsp[-2].u.type), (yyvsp[-1].u.inst_ident), 0, (yylsp[-1]).file); (yyval.u.instance)->set_initializer((yyvsp[0].u.expr)); } -#line 5796 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6222 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 222: -#line 1871 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 234: +#line 2057 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyvsp[-1].u.inst_ident)->add_modifier(IIT_const); (yyval.u.instance) = new CPPInstance((yyvsp[-2].u.type), (yyvsp[-1].u.inst_ident), 0, (yylsp[-2]).file); (yyval.u.instance)->set_initializer((yyvsp[0].u.expr)); } -#line 5806 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6232 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 223: -#line 1877 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 235: +#line 2063 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.instance) = new CPPInstance((yyvsp[-2].u.type), (yyvsp[-1].u.inst_ident), 0, (yylsp[-1]).file); + (yyval.u.instance)->set_initializer((yyvsp[0].u.expr)); +} +#line 6241 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 236: +#line 2068 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyvsp[-1].u.inst_ident)->add_modifier(IIT_const); + (yyval.u.instance) = new CPPInstance((yyvsp[-2].u.type), (yyvsp[-1].u.inst_ident), 0, (yylsp[-1]).file); + (yyval.u.instance)->set_initializer((yyvsp[0].u.expr)); +} +#line 6251 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 237: +#line 2074 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyvsp[-1].u.inst_ident)->add_modifier(IIT_const); + (yyval.u.instance) = new CPPInstance((yyvsp[-2].u.type), (yyvsp[-1].u.inst_ident), 0, (yylsp[-2]).file); + (yyval.u.instance)->set_initializer((yyvsp[0].u.expr)); +} +#line 6261 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 238: +#line 2080 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.instance) = (yyvsp[0].u.instance); } -#line 5814 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6269 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 224: -#line 1888 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 239: +#line 2084 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.instance) = (yyvsp[0].u.instance); } -#line 5822 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6277 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 225: -#line 1892 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 240: +#line 2095 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.instance) = (yyvsp[0].u.instance); +} +#line 6285 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 241: +#line 2099 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_parameter)); (yyval.u.instance) = new CPPInstance(type, "expr"); (yyval.u.instance)->set_initializer((yyvsp[0].u.expr)); } -#line 5833 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 226: -#line 1902 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); -} -#line 5841 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 227: -#line 1906 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); -} -#line 5849 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 228: -#line 1910 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_const); -} -#line 5858 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 229: -#line 1915 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_volatile); -} -#line 5867 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 230: -#line 1920 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_pointer); -} -#line 5876 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 231: -#line 1925 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_reference); -} -#line 5885 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 232: -#line 1930 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); -} -#line 5894 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 233: -#line 1935 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_scoped_pointer_modifier((yyvsp[-2].u.identifier)); -} -#line 5903 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 234: -#line 1940 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[-3].u.inst_ident); - (yyval.u.inst_ident)->add_array_modifier((yyvsp[-1].u.expr)); -} -#line 5912 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 235: -#line 1948 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); -} -#line 5920 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 236: -#line 1952 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); -} -#line 5928 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 237: -#line 1956 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_const); -} -#line 5937 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 238: -#line 1961 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_volatile); -} -#line 5946 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 239: -#line 1966 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_pointer); -} -#line 5955 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 240: -#line 1971 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_reference); -} -#line 5964 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 241: -#line 1976 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); -} -#line 5973 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6296 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 242: -#line 1981 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2109 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_scoped_pointer_modifier((yyvsp[-2].u.identifier)); + (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); } -#line 5982 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6304 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 243: -#line 1986 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2113 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.inst_ident) = (yyvsp[-3].u.inst_ident); - (yyval.u.inst_ident)->add_array_modifier((yyvsp[-1].u.expr)); + (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); } -#line 5991 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6312 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 244: -#line 1991 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2117 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.inst_ident) = (yyvsp[-5].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_paren); - (yyval.u.inst_ident)->add_func_modifier((yyvsp[-2].u.param_list), (yyvsp[0].u.integer)); + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_const); } -#line 6001 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6321 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 245: -#line 1997 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2122 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.inst_ident) = (yyvsp[-1].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_paren); + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_volatile); } -#line 6010 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6330 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 246: -#line 2005 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); - (yyval.u.inst_ident)->_packed = true; -} -#line 6019 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 247: -#line 2010 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); - (yyval.u.inst_ident)->_packed = true; -} -#line 6028 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 248: -#line 2015 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_const); -} -#line 6037 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 249: -#line 2020 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_volatile); -} -#line 6046 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 250: -#line 2025 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2127 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); (yyval.u.inst_ident)->add_modifier(IIT_pointer); } -#line 6055 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 251: -#line 2030 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_reference); -} -#line 6064 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 252: -#line 2035 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); -} -#line 6073 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 253: -#line 2040 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_scoped_pointer_modifier((yyvsp[-2].u.identifier)); -} -#line 6082 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 254: -#line 2045 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[-3].u.inst_ident); - (yyval.u.inst_ident)->add_array_modifier((yyvsp[-1].u.expr)); -} -#line 6091 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 255: -#line 2050 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[-5].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_paren); - (yyval.u.inst_ident)->add_func_modifier((yyvsp[-2].u.param_list), (yyvsp[0].u.integer)); -} -#line 6101 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 256: -#line 2056 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[-1].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_paren); -} -#line 6110 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 257: -#line 2064 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); -} -#line 6118 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 258: -#line 2068 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); - (yyval.u.inst_ident)->_packed = true; -} -#line 6127 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 259: -#line 2073 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); - (yyval.u.inst_ident)->_packed = true; -} -#line 6136 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 260: -#line 2078 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_const); -} -#line 6145 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 261: -#line 2083 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_volatile); -} -#line 6154 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 262: -#line 2088 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_pointer); -} -#line 6163 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 263: -#line 2093 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_reference); -} -#line 6172 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 264: -#line 2098 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); -} -#line 6181 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 265: -#line 2103 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_scoped_pointer_modifier((yyvsp[-2].u.identifier)); -} -#line 6190 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 266: -#line 2108 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[-3].u.inst_ident); - (yyval.u.inst_ident)->add_array_modifier((yyvsp[-1].u.expr)); -} -#line 6199 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 267: -#line 2116 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); -} -#line 6207 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 268: -#line 2120 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); - (yyval.u.inst_ident)->_packed = true; -} -#line 6216 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 269: -#line 2125 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); - (yyval.u.inst_ident)->_packed = true; -} -#line 6225 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 270: -#line 2130 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_const); -} -#line 6234 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 271: -#line 2135 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_volatile); -} -#line 6243 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 272: -#line 2140 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_pointer); -} -#line 6252 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 273: -#line 2145 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_reference); -} -#line 6261 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 274: -#line 2150 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); -} -#line 6270 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 275: -#line 2155 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); - (yyval.u.inst_ident)->add_scoped_pointer_modifier((yyvsp[-2].u.identifier)); -} -#line 6279 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 276: -#line 2160 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[-3].u.inst_ident); - (yyval.u.inst_ident)->add_array_modifier((yyvsp[-1].u.expr)); -} -#line 6288 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 277: -#line 2165 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); - (yyval.u.inst_ident)->add_modifier(IIT_paren); - (yyval.u.inst_ident)->add_func_modifier((yyvsp[-3].u.param_list), (yyvsp[-1].u.integer), (yyvsp[0].u.type)); -} -#line 6298 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 278: -#line 2171 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[-6].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_pointer); - (yyval.u.inst_ident)->add_modifier(IIT_paren); - (yyval.u.inst_ident)->add_func_modifier((yyvsp[-3].u.param_list), (yyvsp[-1].u.integer), (yyvsp[0].u.type)); -} -#line 6309 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 279: -#line 2178 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[-6].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_reference); - (yyval.u.inst_ident)->add_modifier(IIT_paren); - (yyval.u.inst_ident)->add_func_modifier((yyvsp[-3].u.param_list), (yyvsp[-1].u.integer), (yyvsp[0].u.type)); -} -#line 6320 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 280: -#line 2185 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = (yyvsp[-6].u.inst_ident); - (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); - (yyval.u.inst_ident)->add_modifier(IIT_paren); - (yyval.u.inst_ident)->add_func_modifier((yyvsp[-3].u.param_list), (yyvsp[-1].u.integer), (yyvsp[0].u.type)); -} -#line 6331 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 281: -#line 2195 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.type) = CPPType::new_type((yyvsp[0].u.simple_type)); -} #line 6339 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; + case 247: +#line 2132 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_reference); +} +#line 6348 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 248: +#line 2137 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); +} +#line 6357 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 249: +#line 2142 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_scoped_pointer_modifier((yyvsp[-2].u.identifier)); +} +#line 6366 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 250: +#line 2147 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[-3].u.inst_ident); + (yyval.u.inst_ident)->add_array_modifier((yyvsp[-1].u.expr)); +} +#line 6375 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 251: +#line 2155 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); +} +#line 6383 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 252: +#line 2159 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); +} +#line 6391 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 253: +#line 2163 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_const); +} +#line 6400 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 254: +#line 2168 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_volatile); +} +#line 6409 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 255: +#line 2173 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_pointer); +} +#line 6418 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 256: +#line 2178 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_reference); +} +#line 6427 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 257: +#line 2183 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); +} +#line 6436 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 258: +#line 2188 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_scoped_pointer_modifier((yyvsp[-2].u.identifier)); +} +#line 6445 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 259: +#line 2193 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[-3].u.inst_ident); + (yyval.u.inst_ident)->add_array_modifier((yyvsp[-1].u.expr)); +} +#line 6454 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 260: +#line 2198 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[-5].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_paren); + (yyval.u.inst_ident)->add_func_modifier((yyvsp[-2].u.param_list), (yyvsp[0].u.integer)); +} +#line 6464 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 261: +#line 2204 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[-1].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_paren); +} +#line 6473 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 262: +#line 2212 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); + (yyval.u.inst_ident)->_packed = true; +} +#line 6482 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 263: +#line 2217 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); + (yyval.u.inst_ident)->_packed = true; +} +#line 6491 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 264: +#line 2222 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_const); +} +#line 6500 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 265: +#line 2227 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_volatile); +} +#line 6509 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 266: +#line 2232 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_pointer); +} +#line 6518 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 267: +#line 2237 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_reference); +} +#line 6527 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 268: +#line 2242 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); +} +#line 6536 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 269: +#line 2247 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_scoped_pointer_modifier((yyvsp[-2].u.identifier)); +} +#line 6545 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 270: +#line 2252 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[-3].u.inst_ident); + (yyval.u.inst_ident)->add_array_modifier((yyvsp[-1].u.expr)); +} +#line 6554 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 271: +#line 2257 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[-5].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_paren); + (yyval.u.inst_ident)->add_func_modifier((yyvsp[-2].u.param_list), (yyvsp[0].u.integer)); +} +#line 6564 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 272: +#line 2263 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[-1].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_paren); +} +#line 6573 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 273: +#line 2271 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); +} +#line 6581 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 274: +#line 2275 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); + (yyval.u.inst_ident)->_packed = true; +} +#line 6590 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 275: +#line 2280 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); + (yyval.u.inst_ident)->_packed = true; +} +#line 6599 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 276: +#line 2285 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_const); +} +#line 6608 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 277: +#line 2290 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_volatile); +} +#line 6617 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 278: +#line 2295 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_pointer); +} +#line 6626 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 279: +#line 2300 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_reference); +} +#line 6635 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 280: +#line 2305 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); +} +#line 6644 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 281: +#line 2310 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_scoped_pointer_modifier((yyvsp[-2].u.identifier)); +} +#line 6653 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + case 282: -#line 2199 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2315 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[-3].u.inst_ident); + (yyval.u.inst_ident)->add_array_modifier((yyvsp[-1].u.expr)); +} +#line 6662 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 283: +#line 2323 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); +} +#line 6670 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 284: +#line 2327 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); + (yyval.u.inst_ident)->_packed = true; +} +#line 6679 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 285: +#line 2332 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); + (yyval.u.inst_ident)->_packed = true; +} +#line 6688 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 286: +#line 2337 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_const); +} +#line 6697 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 287: +#line 2342 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_volatile); +} +#line 6706 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 288: +#line 2347 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_pointer); +} +#line 6715 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 289: +#line 2352 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_reference); +} +#line 6724 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 290: +#line 2357 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); +} +#line 6733 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 291: +#line 2362 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[0].u.inst_ident); + (yyval.u.inst_ident)->add_scoped_pointer_modifier((yyvsp[-2].u.identifier)); +} +#line 6742 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 292: +#line 2367 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[-3].u.inst_ident); + (yyval.u.inst_ident)->add_array_modifier((yyvsp[-1].u.expr)); +} +#line 6751 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 293: +#line 2372 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); + (yyval.u.inst_ident)->add_modifier(IIT_paren); + (yyval.u.inst_ident)->add_func_modifier((yyvsp[-3].u.param_list), (yyvsp[-1].u.integer), (yyvsp[0].u.type)); +} +#line 6761 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 294: +#line 2378 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[-6].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_pointer); + (yyval.u.inst_ident)->add_modifier(IIT_paren); + (yyval.u.inst_ident)->add_func_modifier((yyvsp[-3].u.param_list), (yyvsp[-1].u.integer), (yyvsp[0].u.type)); +} +#line 6772 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 295: +#line 2385 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[-6].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_reference); + (yyval.u.inst_ident)->add_modifier(IIT_paren); + (yyval.u.inst_ident)->add_func_modifier((yyvsp[-3].u.param_list), (yyvsp[-1].u.integer), (yyvsp[0].u.type)); +} +#line 6783 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 296: +#line 2392 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = (yyvsp[-6].u.inst_ident); + (yyval.u.inst_ident)->add_modifier(IIT_rvalue_reference); + (yyval.u.inst_ident)->add_modifier(IIT_paren); + (yyval.u.inst_ident)->add_func_modifier((yyvsp[-3].u.param_list), (yyvsp[-1].u.integer), (yyvsp[0].u.type)); +} +#line 6794 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 297: +#line 2402 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.type) = CPPType::new_type((yyvsp[0].u.simple_type)); +} +#line 6802 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 298: +#line 2406 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); if ((yyval.u.type) == NULL) { @@ -6356,50 +6810,50 @@ yyreduce: } assert((yyval.u.type) != NULL); } -#line 6351 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6814 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 283: -#line 2207 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 299: +#line 2414 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = CPPType::new_type(new CPPTBDType((yyvsp[0].u.identifier))); } -#line 6359 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6822 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 284: -#line 2211 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 300: +#line 2418 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = CPPType::new_type((yyvsp[0].u.struct_type)); } -#line 6367 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6830 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 285: -#line 2215 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 301: +#line 2422 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = CPPType::new_type((yyvsp[0].u.struct_type)); } -#line 6375 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6838 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 286: -#line 2219 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 302: +#line 2426 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = CPPType::new_type((yyvsp[0].u.enum_type)); } -#line 6383 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6846 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 287: -#line 2223 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 303: +#line 2430 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); if (type != NULL) { (yyval.u.type) = type; } else { CPPExtensionType *et = - CPPType::new_type(new CPPExtensionType((yyvsp[-1].u.extension_enum), (yyvsp[0].u.identifier), current_scope, (yylsp[-1]).file)) + CPPType::new_type(new CPPExtensionType((yyvsp[-2].u.extension_enum), (yyvsp[0].u.identifier), current_scope, (yylsp[-2]).file)) ->as_extension_type(); CPPScope *scope = (yyvsp[0].u.identifier)->get_scope(current_scope, global_scope); if (scope != NULL) { @@ -6408,11 +6862,11 @@ yyreduce: (yyval.u.type) = et; } } -#line 6403 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6866 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 288: -#line 2239 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 304: +#line 2446 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = (yyvsp[-2].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); if (type != NULL) { @@ -6428,11 +6882,11 @@ yyreduce: (yyval.u.type) = et; } } -#line 6423 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6886 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 289: -#line 2255 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 305: +#line 2462 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = (yyvsp[-1].u.expr)->determine_type(); if ((yyval.u.type) == (CPPType *)NULL) { @@ -6441,11 +6895,19 @@ yyreduce: yyerror("could not determine type of " + str.str(), (yylsp[-1])); } } -#line 6436 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6899 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 290: -#line 2264 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 306: +#line 2471 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.type) = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_auto)); +} +#line 6907 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 307: +#line 2475 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPEnumType *enum_type = (yyvsp[-1].u.type)->as_enum_type(); if (enum_type == NULL) { @@ -6455,19 +6917,19 @@ yyreduce: (yyval.u.type) = enum_type->get_underlying_type(); } } -#line 6450 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6921 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 291: -#line 2274 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 308: +#line 2485 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_auto)); } -#line 6458 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6929 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 292: -#line 2281 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 309: +#line 2492 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); if ((yyval.u.type) == NULL) { @@ -6475,19 +6937,19 @@ yyreduce: } assert((yyval.u.type) != NULL); } -#line 6470 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6941 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 293: -#line 2292 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 310: +#line 2503 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.decl) = CPPType::new_type((yyvsp[0].u.simple_type)); } -#line 6478 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6949 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 294: -#line 2296 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 311: +#line 2507 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.decl) = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); if ((yyval.u.decl) == NULL) { @@ -6495,50 +6957,50 @@ yyreduce: } assert((yyval.u.decl) != NULL); } -#line 6490 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6961 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 295: -#line 2304 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 312: +#line 2515 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.decl) = CPPType::new_type(new CPPTBDType((yyvsp[0].u.identifier))); } -#line 6498 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6969 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 296: -#line 2308 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 313: +#line 2519 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.decl) = CPPType::new_type((yyvsp[0].u.struct_type)); } -#line 6506 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6977 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 297: -#line 2312 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 314: +#line 2523 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.decl) = new CPPTypeDeclaration(CPPType::new_type((yyvsp[0].u.struct_type))); } -#line 6514 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6985 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 298: -#line 2316 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 315: +#line 2527 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.decl) = new CPPTypeDeclaration(CPPType::new_type((yyvsp[0].u.enum_type))); } -#line 6522 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6993 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 299: -#line 2320 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 316: +#line 2531 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); if (type != NULL) { (yyval.u.decl) = type; } else { CPPExtensionType *et = - CPPType::new_type(new CPPExtensionType((yyvsp[-1].u.extension_enum), (yyvsp[0].u.identifier), current_scope, (yylsp[-1]).file)) + CPPType::new_type(new CPPExtensionType((yyvsp[-2].u.extension_enum), (yyvsp[0].u.identifier), current_scope, (yylsp[-2]).file)) ->as_extension_type(); CPPScope *scope = (yyvsp[0].u.identifier)->get_scope(current_scope, global_scope); if (scope != NULL) { @@ -6547,11 +7009,11 @@ yyreduce: (yyval.u.decl) = et; } } -#line 6542 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7013 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 300: -#line 2336 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 317: +#line 2547 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = (yyvsp[-2].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); if (type != NULL) { @@ -6567,11 +7029,11 @@ yyreduce: (yyval.u.decl) = et; } } -#line 6562 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7033 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 301: -#line 2352 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 318: +#line 2563 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { yywarning(string("C++ does not permit forward declaration of untyped enum ") + (yyvsp[0].u.identifier)->get_fully_scoped_name(), (yylsp[-1])); @@ -6589,11 +7051,11 @@ yyreduce: (yyval.u.decl) = et; } } -#line 6584 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7055 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 302: -#line 2370 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 319: +#line 2581 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.decl) = (yyvsp[-1].u.expr)->determine_type(); if ((yyval.u.decl) == (CPPType *)NULL) { @@ -6602,11 +7064,19 @@ yyreduce: yyerror("could not determine type of " + str.str(), (yylsp[-1])); } } -#line 6597 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7068 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 303: -#line 2379 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 320: +#line 2590 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.decl) = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_auto)); +} +#line 7076 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 321: +#line 2594 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPEnumType *enum_type = (yyvsp[-1].u.type)->as_enum_type(); if (enum_type == NULL) { @@ -6616,27 +7086,27 @@ yyreduce: (yyval.u.decl) = enum_type->get_underlying_type(); } } -#line 6611 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7090 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 304: -#line 2389 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 322: +#line 2604 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.decl) = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_auto)); } -#line 6619 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7098 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 305: -#line 2396 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 323: +#line 2611 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = CPPType::new_type((yyvsp[0].u.simple_type)); } -#line 6627 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7106 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 306: -#line 2400 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 324: +#line 2615 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); if ((yyval.u.type) == NULL) { @@ -6644,19 +7114,39 @@ yyreduce: } assert((yyval.u.type) != NULL); } -#line 6639 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7118 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 307: -#line 2408 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 325: +#line 2623 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = CPPType::new_type(new CPPTBDType((yyvsp[0].u.identifier))); } -#line 6647 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7126 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 308: -#line 2412 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 326: +#line 2627 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPType *type = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); + if (type != NULL) { + (yyval.u.type) = type; + } else { + CPPExtensionType *et = + CPPType::new_type(new CPPExtensionType((yyvsp[-2].u.extension_enum), (yyvsp[0].u.identifier), current_scope, (yylsp[-2]).file)) + ->as_extension_type(); + CPPScope *scope = (yyvsp[0].u.identifier)->get_scope(current_scope, global_scope); + if (scope != NULL) { + scope->define_extension_type(et); + } + (yyval.u.type) = et; + } +} +#line 7146 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 327: +#line 2643 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); if (type != NULL) { @@ -6672,31 +7162,11 @@ yyreduce: (yyval.u.type) = et; } } -#line 6667 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7166 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 309: -#line 2428 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - CPPType *type = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); - if (type != NULL) { - (yyval.u.type) = type; - } else { - CPPExtensionType *et = - CPPType::new_type(new CPPExtensionType((yyvsp[-1].u.extension_enum), (yyvsp[0].u.identifier), current_scope, (yylsp[-1]).file)) - ->as_extension_type(); - CPPScope *scope = (yyvsp[0].u.identifier)->get_scope(current_scope, global_scope); - if (scope != NULL) { - scope->define_extension_type(et); - } - (yyval.u.type) = et; - } -} -#line 6687 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 310: -#line 2444 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 328: +#line 2659 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = (yyvsp[-1].u.expr)->determine_type(); if ((yyval.u.type) == (CPPType *)NULL) { @@ -6705,11 +7175,11 @@ yyreduce: yyerror("could not determine type of " + str.str(), (yylsp[-1])); } } -#line 6700 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7179 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 311: -#line 2453 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 329: +#line 2668 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPEnumType *enum_type = (yyvsp[-1].u.type)->as_enum_type(); if (enum_type == NULL) { @@ -6719,103 +7189,103 @@ yyreduce: (yyval.u.type) = enum_type->get_underlying_type(); } } -#line 6714 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7193 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 312: -#line 2463 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 330: +#line 2678 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_auto)); } -#line 6722 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7201 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 313: -#line 2470 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 331: +#line 2685 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.decl) = (yyvsp[0].u.decl); } -#line 6730 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7209 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 314: -#line 2474 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 332: +#line 2689 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { yyerror(string("unknown type '") + (yyvsp[0].u.identifier)->get_fully_scoped_name() + "'", (yylsp[0])); (yyval.u.decl) = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_unknown)); } -#line 6740 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7219 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 315: -#line 2482 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 333: +#line 2697 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = (yyvsp[0].u.inst_ident)->unroll_type((yyvsp[-1].u.type)); } -#line 6748 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7227 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 316: -#line 2486 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 334: +#line 2701 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyvsp[0].u.inst_ident)->add_modifier(IIT_const); (yyval.u.type) = (yyvsp[0].u.inst_ident)->unroll_type((yyvsp[-1].u.type)); } -#line 6757 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7236 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 317: -#line 2491 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 335: +#line 2706 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = (yyvsp[0].u.inst_ident)->unroll_type((yyvsp[-1].u.type)); } -#line 6765 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7244 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 318: -#line 2495 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 336: +#line 2710 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyvsp[0].u.inst_ident)->add_modifier(IIT_const); (yyval.u.type) = (yyvsp[0].u.inst_ident)->unroll_type((yyvsp[-1].u.type)); } -#line 6774 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7253 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 319: -#line 2503 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 341: +#line 2725 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPVisibility starting_vis = - ((yyvsp[-1].u.extension_enum) == CPPExtensionType::T_class) ? V_private : V_public; + ((yyvsp[-2].u.extension_enum) == CPPExtensionType::T_class) ? V_private : V_public; CPPScope *new_scope = new CPPScope(current_scope, CPPNameComponent("anon"), starting_vis); - CPPStructType *st = new CPPStructType((yyvsp[-1].u.extension_enum), NULL, current_scope, - new_scope, (yylsp[-1]).file); + CPPStructType *st = new CPPStructType((yyvsp[-2].u.extension_enum), NULL, current_scope, + new_scope, (yylsp[-2]).file); new_scope->set_struct_type(st); push_scope(new_scope); push_struct(st); } -#line 6792 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7271 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 320: -#line 2517 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 342: +#line 2739 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.struct_type) = current_struct; current_struct->_incomplete = false; pop_struct(); pop_scope(); } -#line 6803 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7282 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 321: -#line 2527 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 343: +#line 2749 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPVisibility starting_vis = - ((yyvsp[-1].u.extension_enum) == CPPExtensionType::T_class) ? V_private : V_public; + ((yyvsp[-2].u.extension_enum) == CPPExtensionType::T_class) ? V_private : V_public; CPPScope *scope = (yyvsp[0].u.identifier)->get_scope(current_scope, global_scope, current_lexer); if (scope == NULL) { @@ -6824,261 +7294,261 @@ yyreduce: CPPScope *new_scope = new CPPScope(scope, (yyvsp[0].u.identifier)->_names.back(), starting_vis); - CPPStructType *st = new CPPStructType((yyvsp[-1].u.extension_enum), (yyvsp[0].u.identifier), current_scope, - new_scope, (yylsp[-1]).file); + CPPStructType *st = new CPPStructType((yyvsp[-2].u.extension_enum), (yyvsp[0].u.identifier), current_scope, + new_scope, (yylsp[-2]).file); new_scope->set_struct_type(st); current_scope->define_extension_type(st); push_scope(new_scope); push_struct(st); } -#line 6827 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7306 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 322: -#line 2547 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 344: +#line 2769 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.struct_type) = current_struct; current_struct->_incomplete = false; pop_struct(); pop_scope(); } -#line 6838 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7317 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 324: -#line 2558 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 346: +#line 2780 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_struct->_final = true; } -#line 6846 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7325 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 329: -#line 2575 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 351: +#line 2797 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_struct->append_derivation((yyvsp[0].u.type), V_unknown, false); } -#line 6854 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7333 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 330: -#line 2579 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 352: +#line 2801 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_struct->append_derivation((yyvsp[0].u.type), V_public, false); } -#line 6862 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7341 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 331: -#line 2583 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 353: +#line 2805 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_struct->append_derivation((yyvsp[0].u.type), V_protected, false); } -#line 6870 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7349 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 332: -#line 2587 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 354: +#line 2809 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_struct->append_derivation((yyvsp[0].u.type), V_private, false); } -#line 6878 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7357 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 333: -#line 2591 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 355: +#line 2813 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_struct->append_derivation((yyvsp[0].u.type), V_public, true); } -#line 6886 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7365 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 334: -#line 2595 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 356: +#line 2817 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_struct->append_derivation((yyvsp[0].u.type), V_protected, true); } -#line 6894 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7373 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 335: -#line 2599 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 357: +#line 2821 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_struct->append_derivation((yyvsp[0].u.type), V_private, true); } -#line 6902 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7381 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 336: -#line 2603 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 358: +#line 2825 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_struct->append_derivation((yyvsp[0].u.type), V_public, true); } -#line 6910 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7389 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 337: -#line 2607 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 359: +#line 2829 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_struct->append_derivation((yyvsp[0].u.type), V_protected, true); } -#line 6918 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7397 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 338: -#line 2611 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 360: +#line 2833 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_struct->append_derivation((yyvsp[0].u.type), V_private, true); } -#line 6926 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7405 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 339: -#line 2618 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 361: +#line 2840 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.enum_type) = current_enum; current_enum = NULL; } -#line 6935 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7414 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 340: -#line 2626 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 362: +#line 2848 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_enum = new CPPEnumType((yyvsp[-2].u.extension_enum), NULL, (yyvsp[0].u.type), current_scope, NULL, (yylsp[-2]).file); } -#line 6943 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7422 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 341: -#line 2630 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 363: +#line 2852 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_enum = new CPPEnumType((yyvsp[0].u.extension_enum), NULL, current_scope, NULL, (yylsp[0]).file); } -#line 6951 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7430 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 342: -#line 2634 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 364: +#line 2856 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPScope *new_scope = new CPPScope(current_scope, (yyvsp[-2].u.identifier)->_names.back(), V_public); current_enum = new CPPEnumType((yyvsp[-3].u.extension_enum), (yyvsp[-2].u.identifier), (yyvsp[0].u.type), current_scope, new_scope, (yylsp[-3]).file); } -#line 6960 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7439 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 343: -#line 2639 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 365: +#line 2861 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPScope *new_scope = new CPPScope(current_scope, (yyvsp[0].u.identifier)->_names.back(), V_public); current_enum = new CPPEnumType((yyvsp[-1].u.extension_enum), (yyvsp[0].u.identifier), current_scope, new_scope, (yylsp[-1]).file); } -#line 6969 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7448 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 344: -#line 2647 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 366: +#line 2869 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = CPPType::new_type((yyvsp[0].u.simple_type)); } -#line 6977 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7456 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 345: -#line 2651 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 367: +#line 2873 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); } -#line 6985 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7464 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 347: -#line 2659 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 369: +#line 2881 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { assert(current_enum != NULL); current_enum->add_element((yyvsp[-1].u.identifier)->get_simple_name(), NULL, current_lexer, (yylsp[-1])); } -#line 6994 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7473 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 348: -#line 2664 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 370: +#line 2886 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { assert(current_enum != NULL); current_enum->add_element((yyvsp[-3].u.identifier)->get_simple_name(), (yyvsp[-1].u.expr), current_lexer, (yylsp[-3])); } -#line 7003 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7482 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 350: -#line 2672 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 372: +#line 2894 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { assert(current_enum != NULL); current_enum->add_element((yyvsp[0].u.identifier)->get_simple_name(), NULL, current_lexer, (yylsp[0])); } -#line 7012 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7491 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 351: -#line 2677 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 373: +#line 2899 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { assert(current_enum != NULL); current_enum->add_element((yyvsp[-2].u.identifier)->get_simple_name(), (yyvsp[0].u.expr), current_lexer, (yylsp[-2])); } -#line 7021 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7500 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 352: -#line 2685 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 374: +#line 2907 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.extension_enum) = CPPExtensionType::T_enum; } -#line 7029 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7508 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 353: -#line 2689 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 375: +#line 2911 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.extension_enum) = CPPExtensionType::T_enum_class; } -#line 7037 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7516 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 354: -#line 2693 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 376: +#line 2915 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.extension_enum) = CPPExtensionType::T_enum_struct; } -#line 7045 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7524 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 355: -#line 2700 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 377: +#line 2922 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.extension_enum) = CPPExtensionType::T_class; } -#line 7053 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7532 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 356: -#line 2704 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 378: +#line 2926 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.extension_enum) = CPPExtensionType::T_struct; } -#line 7061 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7540 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 357: -#line 2708 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 379: +#line 2930 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.extension_enum) = CPPExtensionType::T_union; } -#line 7069 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7548 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 358: -#line 2715 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 380: +#line 2937 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPScope *scope = (yyvsp[-1].u.identifier)->find_scope(current_scope, global_scope, current_lexer); if (scope == NULL) { @@ -7096,19 +7566,19 @@ yyreduce: current_scope->define_namespace(nspace); push_scope(scope); } -#line 7091 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7570 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 359: -#line 2733 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 381: +#line 2955 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_scope(); } -#line 7099 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7578 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 360: -#line 2737 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 382: +#line 2959 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPScope *scope = (yyvsp[-1].u.identifier)->find_scope(current_scope, global_scope, current_lexer); if (scope == NULL) { @@ -7127,143 +7597,143 @@ yyreduce: current_scope->define_namespace(nspace); push_scope(scope); } -#line 7122 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7601 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 361: -#line 2756 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 383: +#line 2978 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_scope(); } -#line 7130 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7609 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 364: -#line 2765 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 386: +#line 2987 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPUsing *using_decl = new CPPUsing((yyvsp[-1].u.identifier), false, (yylsp[-2]).file); current_scope->add_declaration(using_decl, global_scope, current_lexer, (yylsp[-2])); current_scope->add_using(using_decl, global_scope, current_lexer); } -#line 7140 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7619 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 365: -#line 2771 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 387: +#line 2993 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // This is really just an alternative way to declare a typedef. CPPTypedefType *typedef_type = new CPPTypedefType((yyvsp[-1].u.type), (yyvsp[-3].u.identifier), current_scope); typedef_type->_using = true; current_scope->add_declaration(CPPType::new_type(typedef_type), global_scope, current_lexer, (yylsp[-4])); } -#line 7151 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7630 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 366: -#line 2778 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 388: +#line 3000 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPUsing *using_decl = new CPPUsing((yyvsp[-1].u.identifier), true, (yylsp[-3]).file); current_scope->add_declaration(using_decl, global_scope, current_lexer, (yylsp[-3])); current_scope->add_using(using_decl, global_scope, current_lexer); } -#line 7161 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7640 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 370: -#line 2793 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 392: +#line 3015 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_bool); } -#line 7169 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7648 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 371: -#line 2797 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 393: +#line 3019 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_char); } -#line 7177 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7656 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 372: -#line 2801 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 394: +#line 3023 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_wchar_t); } -#line 7185 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7664 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 373: -#line 2805 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 395: +#line 3027 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_char16_t); } -#line 7193 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7672 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 374: -#line 2809 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 396: +#line 3031 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_char32_t); } -#line 7201 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7680 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 375: -#line 2813 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 397: +#line 3035 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_int, CPPSimpleType::F_short); } -#line 7210 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7689 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 376: -#line 2818 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 398: +#line 3040 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_int, CPPSimpleType::F_long); } -#line 7219 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7698 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 377: -#line 2823 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 399: +#line 3045 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_int, CPPSimpleType::F_unsigned); } -#line 7228 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7707 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 378: -#line 2828 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 400: +#line 3050 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_int, CPPSimpleType::F_signed); } -#line 7237 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7716 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 379: -#line 2833 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 401: +#line 3055 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_int); } -#line 7245 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7724 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 380: -#line 2837 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 402: +#line 3059 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = (yyvsp[0].u.simple_type); (yyval.u.simple_type)->_flags |= CPPSimpleType::F_short; } -#line 7254 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7733 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 381: -#line 2842 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 403: +#line 3064 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = (yyvsp[0].u.simple_type); if ((yyval.u.simple_type)->_flags & CPPSimpleType::F_long) { @@ -7272,485 +7742,501 @@ yyreduce: (yyval.u.simple_type)->_flags |= CPPSimpleType::F_long; } } -#line 7267 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7746 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 382: -#line 2851 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 404: +#line 3073 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = (yyvsp[0].u.simple_type); (yyval.u.simple_type)->_flags |= CPPSimpleType::F_unsigned; } -#line 7276 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7755 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 383: -#line 2856 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 405: +#line 3078 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = (yyvsp[0].u.simple_type); (yyval.u.simple_type)->_flags |= CPPSimpleType::F_signed; } -#line 7285 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7764 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 384: -#line 2864 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 406: +#line 3086 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_float); } -#line 7293 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7772 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 385: -#line 2868 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 407: +#line 3090 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_double); } -#line 7301 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7780 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 386: -#line 2872 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 408: +#line 3094 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_double, CPPSimpleType::F_long); } -#line 7310 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7789 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 387: -#line 2880 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 409: +#line 3102 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.simple_type) = new CPPSimpleType(CPPSimpleType::T_void); } -#line 7318 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7797 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 388: -#line 2889 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 410: +#line 3111 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_lexer->_resolve_identifiers = false; } -#line 7326 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7805 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 389: -#line 2893 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 411: +#line 3115 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { current_lexer->_resolve_identifiers = true; } -#line 7334 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 495: -#line 2936 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { -} -#line 7341 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7813 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 519: -#line 2945 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3159 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (CPPExpression *)NULL; } -#line 7349 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 520: -#line 2949 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 7357 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 521: -#line 2956 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 7365 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 522: -#line 2960 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 7373 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 523: -#line 2967 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 7381 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 524: -#line 2971 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(',', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7389 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 525: -#line 2978 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 7397 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 526: -#line 2982 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-2].u.type), (yyvsp[0].u.expr))); -} -#line 7405 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 527: -#line 2986 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_static_cast)); -} -#line 7413 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 528: -#line 2990 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_dynamic_cast)); -} -#line 7421 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 529: -#line 2994 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_const_cast)); -} -#line 7429 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 530: -#line 2998 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_reinterpret_cast)); -} -#line 7437 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 531: -#line 3002 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func((yyvsp[-1].u.type))); -} -#line 7445 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 532: -#line 3006 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_ellipsis_func((yyvsp[-1].u.identifier))); -} -#line 7453 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 533: -#line 3010 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::alignof_func((yyvsp[-1].u.type))); -} -#line 7461 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 534: -#line 3014 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_NOT, (yyvsp[0].u.expr)); -} -#line 7469 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 535: -#line 3018 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_NEGATE, (yyvsp[0].u.expr)); -} -#line 7477 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 536: -#line 3022 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_MINUS, (yyvsp[0].u.expr)); -} -#line 7485 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 537: -#line 3026 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_PLUS, (yyvsp[0].u.expr)); -} -#line 7493 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 538: -#line 3030 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_STAR, (yyvsp[0].u.expr)); -} -#line 7501 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 539: -#line 3034 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_REF, (yyvsp[0].u.expr)); -} -#line 7509 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 540: -#line 3038 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('*', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7517 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 541: -#line 3042 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('/', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7525 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 542: -#line 3046 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('%', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7533 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7820 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 543: -#line 3050 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3168 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('+', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = (CPPExpression *)NULL; } -#line 7541 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7828 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 544: -#line 3054 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('-', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7549 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 545: -#line 3058 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('|', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7557 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 546: -#line 3062 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('^', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7565 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 547: -#line 3066 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('&', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7573 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 548: -#line 3070 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(OROR, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7581 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 549: -#line 3074 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(ANDAND, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7589 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 550: -#line 3078 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(EQCOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7597 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 551: -#line 3082 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(NECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7605 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 552: -#line 3086 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(LECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7613 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 553: -#line 3090 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(GECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7621 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 554: -#line 3094 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(LSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7629 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 555: -#line 3098 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(RSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7637 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 556: -#line 3102 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('?', (yyvsp[-4].u.expr), (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7645 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 557: -#line 3106 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('[', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); -} -#line 7653 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 558: -#line 3110 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('f', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); -} -#line 7661 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 559: -#line 3114 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('f', (yyvsp[-2].u.expr)); -} -#line 7669 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 560: -#line 3118 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('.', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7677 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 561: -#line 3122 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(POINTSAT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7685 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 562: -#line 3126 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[-1].u.expr); -} -#line 7693 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 563: -#line 3134 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3172 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = (yyvsp[0].u.expr); } -#line 7701 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7836 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 564: -#line 3138 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 545: +#line 3179 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (CPPExpression *)NULL; +} +#line 7844 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 546: +#line 3183 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[0].u.expr); +} +#line 7852 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 547: +#line 3190 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[0].u.expr); +} +#line 7860 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 548: +#line 3194 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(',', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 7868 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 549: +#line 3201 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[0].u.expr); +} +#line 7876 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 550: +#line 3205 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-2].u.type), (yyvsp[0].u.expr))); } -#line 7709 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7884 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 565: -#line 3142 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 551: +#line 3209 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_static_cast)); } -#line 7717 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7892 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 566: -#line 3146 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 552: +#line 3213 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_dynamic_cast)); } -#line 7725 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7900 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 567: -#line 3150 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 553: +#line 3217 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_const_cast)); } -#line 7733 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7908 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 568: -#line 3154 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 554: +#line 3221 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_reinterpret_cast)); } -#line 7741 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7916 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 555: +#line 3225 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func((yyvsp[-1].u.type))); +} +#line 7924 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 556: +#line 3229 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPDeclaration *arg = (yyvsp[-1].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (arg == (CPPDeclaration *)NULL) { + yyerror("undefined sizeof argument: " + (yyvsp[-1].u.identifier)->get_fully_scoped_name(), (yylsp[-1])); + } else if (arg->get_subtype() == CPPDeclaration::ST_instance) { + CPPInstance *inst = arg->as_instance(); + (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func(inst->_type)); + } else { + (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func(arg->as_type())); + } +} +#line 7940 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 557: +#line 3241 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_ellipsis_func((yyvsp[-1].u.identifier))); +} +#line 7948 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 558: +#line 3245 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::alignof_func((yyvsp[-1].u.type))); +} +#line 7956 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 559: +#line 3249 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(UNARY_NOT, (yyvsp[0].u.expr)); +} +#line 7964 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 560: +#line 3253 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(UNARY_NEGATE, (yyvsp[0].u.expr)); +} +#line 7972 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 561: +#line 3257 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(UNARY_MINUS, (yyvsp[0].u.expr)); +} +#line 7980 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 562: +#line 3261 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(UNARY_PLUS, (yyvsp[0].u.expr)); +} +#line 7988 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 563: +#line 3265 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(UNARY_STAR, (yyvsp[0].u.expr)); +} +#line 7996 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 564: +#line 3269 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(UNARY_REF, (yyvsp[0].u.expr)); +} +#line 8004 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 565: +#line 3273 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('*', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8012 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 566: +#line 3277 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('/', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8020 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 567: +#line 3281 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('%', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8028 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 568: +#line 3285 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('+', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8036 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 569: -#line 3158 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3289 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('-', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8044 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 570: +#line 3293 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('|', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8052 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 571: +#line 3297 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('^', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8060 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 572: +#line 3301 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('&', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8068 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 573: +#line 3305 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(OROR, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8076 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 574: +#line 3309 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(ANDAND, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8084 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 575: +#line 3313 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(EQCOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8092 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 576: +#line 3317 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(NECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8100 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 577: +#line 3321 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(LECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8108 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 578: +#line 3325 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(GECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8116 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 579: +#line 3329 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(LSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8124 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 580: +#line 3333 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(RSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8132 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 581: +#line 3337 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('?', (yyvsp[-4].u.expr), (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8140 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 582: +#line 3341 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('[', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); +} +#line 8148 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 583: +#line 3345 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('f', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); +} +#line 8156 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 584: +#line 3349 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('f', (yyvsp[-2].u.expr)); +} +#line 8164 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 585: +#line 3353 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('.', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8172 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 586: +#line 3357 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(POINTSAT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 8180 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 587: +#line 3361 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[-1].u.expr); +} +#line 8188 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 588: +#line 3369 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[0].u.expr); +} +#line 8196 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 589: +#line 3373 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-2].u.type), (yyvsp[0].u.expr))); +} +#line 8204 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 590: +#line 3377 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_static_cast)); +} +#line 8212 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 591: +#line 3381 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_dynamic_cast)); +} +#line 8220 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 592: +#line 3385 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_const_cast)); +} +#line 8228 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 593: +#line 3389 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_reinterpret_cast)); +} +#line 8236 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 594: +#line 3393 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // A constructor call. CPPType *type = (yyvsp[-3].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); @@ -7760,785 +8246,205 @@ yyreduce: assert(type != NULL); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7755 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8250 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 570: -#line 3168 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 595: +#line 3403 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + // Aggregate initialization. + CPPType *type = (yyvsp[-3].u.identifier)->find_type(current_scope, global_scope, false, current_lexer); + if (type == NULL) { + yyerror(string("internal error resolving type ") + (yyvsp[-3].u.identifier)->get_fully_scoped_name(), (yylsp[-3])); + } + assert(type != NULL); + (yyval.u.expr) = new CPPExpression(CPPExpression::aggregate_init_op(type, (yyvsp[-1].u.expr))); +} +#line 8264 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 596: +#line 3413 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int)); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7765 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8274 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 571: -#line 3174 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 597: +#line 3419 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char)); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7775 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8284 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 572: -#line 3180 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 598: +#line 3425 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_wchar_t)); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7785 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8294 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 573: -#line 3186 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 599: +#line 3431 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char16_t)); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7795 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8304 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 574: -#line 3192 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 600: +#line 3437 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char32_t)); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7805 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8314 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 575: -#line 3198 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 601: +#line 3443 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_bool)); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7815 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8324 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 576: -#line 3204 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 602: +#line 3449 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int, CPPSimpleType::F_short)); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7826 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8335 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 577: -#line 3211 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 603: +#line 3456 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int, CPPSimpleType::F_long)); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7837 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8346 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 578: -#line 3218 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 604: +#line 3463 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int, CPPSimpleType::F_unsigned)); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7848 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8357 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 579: -#line 3225 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 605: +#line 3470 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int, CPPSimpleType::F_signed)); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7859 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8368 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 580: -#line 3232 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 606: +#line 3477 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_float)); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7869 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8378 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 581: -#line 3238 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 607: +#line 3483 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_double)); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 7879 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 582: -#line 3244 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func((yyvsp[-1].u.type))); -} -#line 7887 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 583: -#line 3248 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_ellipsis_func((yyvsp[-1].u.identifier))); -} -#line 7895 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 584: -#line 3252 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::alignof_func((yyvsp[-1].u.type))); -} -#line 7903 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 585: -#line 3256 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::new_op((yyvsp[0].u.type))); -} -#line 7911 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 586: -#line 3260 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::new_op((yyvsp[-3].u.type), (yyvsp[-1].u.expr))); -} -#line 7919 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 587: -#line 3264 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - CPPIdentifier ident(""); - ident.add_name("std"); - ident.add_name("type_info"); - CPPType *std_type_info = ident.find_type(current_scope, global_scope, false, current_lexer); - if (!std_type_info) { - yywarning("cannot use typeid before including ", (yylsp[-3])); - } - (yyval.u.expr) = new CPPExpression(CPPExpression::typeid_op((yyvsp[-1].u.type), std_type_info)); -} -#line 7934 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 588: -#line 3275 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - CPPIdentifier ident(""); - ident.add_name("std"); - ident.add_name("type_info"); - CPPType *std_type_info = ident.find_type(current_scope, global_scope, false, current_lexer); - if (!std_type_info) { - yywarning("cannot use typeid before including ", (yylsp[-3])); - } - (yyval.u.expr) = new CPPExpression(CPPExpression::typeid_op((yyvsp[-1].u.expr), std_type_info)); -} -#line 7949 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 589: -#line 3286 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_NOT, (yyvsp[0].u.expr)); -} -#line 7957 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 590: -#line 3290 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_NEGATE, (yyvsp[0].u.expr)); -} -#line 7965 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 591: -#line 3294 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_MINUS, (yyvsp[0].u.expr)); -} -#line 7973 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 592: -#line 3298 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_PLUS, (yyvsp[0].u.expr)); -} -#line 7981 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 593: -#line 3302 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_STAR, (yyvsp[0].u.expr)); -} -#line 7989 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 594: -#line 3306 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_REF, (yyvsp[0].u.expr)); -} -#line 7997 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 595: -#line 3310 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('*', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8005 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 596: -#line 3314 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('/', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8013 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 597: -#line 3318 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('%', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8021 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 598: -#line 3322 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('+', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8029 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 599: -#line 3326 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('-', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8037 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 600: -#line 3330 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('|', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8045 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 601: -#line 3334 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('^', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8053 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 602: -#line 3338 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('&', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8061 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 603: -#line 3342 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(OROR, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8069 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 604: -#line 3346 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(ANDAND, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8077 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 605: -#line 3350 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(EQCOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8085 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 606: -#line 3354 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(NECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8093 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 607: -#line 3358 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(LECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8101 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8388 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 608: -#line 3362 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(GECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8109 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 609: -#line 3366 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('<', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8117 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 610: -#line 3370 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('>', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8125 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 611: -#line 3374 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(LSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8133 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 612: -#line 3378 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(RSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8141 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 613: -#line 3382 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('?', (yyvsp[-4].u.expr), (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8149 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 614: -#line 3386 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('[', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); -} -#line 8157 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 615: -#line 3390 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('f', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); -} -#line 8165 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 616: -#line 3394 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('f', (yyvsp[-2].u.expr)); -} -#line 8173 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 617: -#line 3398 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('.', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8181 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 618: -#line 3402 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(POINTSAT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8189 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 619: -#line 3406 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[-1].u.expr); -} -#line 8197 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 620: -#line 3413 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); -} -#line 8205 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 621: -#line 3417 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(true); -} -#line 8213 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 622: -#line 3421 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(false); -} -#line 8221 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 623: -#line 3425 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); -} -#line 8229 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 624: -#line 3429 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression((yyvsp[0].u.real)); -} -#line 8237 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 625: -#line 3433 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 8245 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 626: -#line 3437 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 8253 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 627: -#line 3441 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression((yyvsp[0].u.identifier), current_scope, global_scope, current_lexer); -} -#line 8261 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 628: -#line 3445 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - // A variable named "final". C++11 explicitly permits this. - CPPIdentifier *ident = new CPPIdentifier("final", (yylsp[0])); - (yyval.u.expr) = new CPPExpression(ident, current_scope, global_scope, current_lexer); -} -#line 8271 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 629: -#line 3451 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - // A variable named "override". C++11 explicitly permits this. - CPPIdentifier *ident = new CPPIdentifier("override", (yylsp[0])); - (yyval.u.expr) = new CPPExpression(ident, current_scope, global_scope, current_lexer); -} -#line 8281 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 630: -#line 3457 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::get_nullptr()); -} -#line 8289 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 631: -#line 3461 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = NULL; -} -#line 8297 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 632: -#line 3465 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = NULL; -} -#line 8305 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 633: -#line 3469 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_HAS_VIRTUAL_DESTRUCTOR, (yyvsp[-1].u.type))); -} -#line 8313 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 634: -#line 3473 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_ABSTRACT, (yyvsp[-1].u.type))); -} -#line 8321 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 635: -#line 3477 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CLASS, (yyvsp[-3].u.type), (yyvsp[-1].u.type))); -} -#line 8329 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 636: -#line 3481 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CLASS, (yyvsp[-1].u.type))); -} -#line 8337 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 637: -#line 3485 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CONSTRUCTIBLE, (yyvsp[-1].u.type))); -} -#line 8345 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 638: #line 3489 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CONSTRUCTIBLE, (yyvsp[-3].u.type), (yyvsp[-1].u.type))); -} -#line 8353 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 639: -#line 3493 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CONVERTIBLE_TO, (yyvsp[-3].u.type), (yyvsp[-1].u.type))); -} -#line 8361 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 640: -#line 3497 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_DESTRUCTIBLE, (yyvsp[-1].u.type))); -} -#line 8369 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 641: -#line 3501 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_EMPTY, (yyvsp[-1].u.type))); -} -#line 8377 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 642: -#line 3505 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_ENUM, (yyvsp[-1].u.type))); -} -#line 8385 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 643: -#line 3509 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_FINAL, (yyvsp[-1].u.type))); -} -#line 8393 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 644: -#line 3513 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_FUNDAMENTAL, (yyvsp[-1].u.type))); -} -#line 8401 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 645: -#line 3517 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_POD, (yyvsp[-1].u.type))); -} -#line 8409 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 646: -#line 3521 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_POLYMORPHIC, (yyvsp[-1].u.type))); -} -#line 8417 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 647: -#line 3525 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_STANDARD_LAYOUT, (yyvsp[-1].u.type))); -} -#line 8425 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 648: -#line 3529 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_TRIVIAL, (yyvsp[-1].u.type))); -} -#line 8433 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 649: -#line 3533 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_UNION, (yyvsp[-1].u.type))); -} -#line 8441 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 650: -#line 3547 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 8449 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 651: -#line 3551 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-2].u.type), (yyvsp[0].u.expr))); -} -#line 8457 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 652: -#line 3555 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_static_cast)); -} -#line 8465 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 653: -#line 3559 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_dynamic_cast)); -} -#line 8473 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 654: -#line 3563 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_const_cast)); -} -#line 8481 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 655: -#line 3567 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_reinterpret_cast)); -} -#line 8489 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 656: -#line 3571 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func((yyvsp[-1].u.type))); } -#line 8497 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8396 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 657: -#line 3575 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 609: +#line 3493 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPDeclaration *arg = (yyvsp[-1].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (arg == (CPPDeclaration *)NULL) { + yyerror("undefined sizeof argument: " + (yyvsp[-1].u.identifier)->get_fully_scoped_name(), (yylsp[-1])); + } else if (arg->get_subtype() == CPPDeclaration::ST_instance) { + CPPInstance *inst = arg->as_instance(); + (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func(inst->_type)); + } else { + (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func(arg->as_type())); + } +} +#line 8412 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 610: +#line 3505 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_ellipsis_func((yyvsp[-1].u.identifier))); } -#line 8505 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8420 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 658: -#line 3579 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 611: +#line 3509 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(CPPExpression::alignof_func((yyvsp[-1].u.type))); } -#line 8513 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8428 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 659: -#line 3583 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 612: +#line 3513 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(CPPExpression::new_op((yyvsp[0].u.type))); } -#line 8521 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8436 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 660: -#line 3587 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 613: +#line 3517 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(CPPExpression::new_op((yyvsp[-3].u.type), (yyvsp[-1].u.expr))); } -#line 8529 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8444 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 661: -#line 3591 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 614: +#line 3521 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPIdentifier ident(""); ident.add_name("std"); @@ -8549,11 +8455,11 @@ yyreduce: } (yyval.u.expr) = new CPPExpression(CPPExpression::typeid_op((yyvsp[-1].u.type), std_type_info)); } -#line 8544 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8459 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 662: -#line 3602 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 615: +#line 3532 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPIdentifier ident(""); ident.add_name("std"); @@ -8564,343 +8470,1067 @@ yyreduce: } (yyval.u.expr) = new CPPExpression(CPPExpression::typeid_op((yyvsp[-1].u.expr), std_type_info)); } -#line 8559 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8474 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 663: -#line 3613 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 616: +#line 3543 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(UNARY_NOT, (yyvsp[0].u.expr)); } -#line 8567 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8482 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 664: -#line 3617 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 617: +#line 3547 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(UNARY_NEGATE, (yyvsp[0].u.expr)); } -#line 8575 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8490 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 665: -#line 3621 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 618: +#line 3551 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(UNARY_MINUS, (yyvsp[0].u.expr)); } -#line 8583 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8498 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 666: -#line 3625 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 619: +#line 3555 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(UNARY_PLUS, (yyvsp[0].u.expr)); } -#line 8591 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8506 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 667: -#line 3629 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 620: +#line 3559 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(UNARY_STAR, (yyvsp[0].u.expr)); +} +#line 8514 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 621: +#line 3563 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(UNARY_REF, (yyvsp[0].u.expr)); } -#line 8599 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8522 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 668: -#line 3633 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 622: +#line 3567 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('*', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8607 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8530 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 669: -#line 3637 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 623: +#line 3571 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('/', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8615 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8538 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 670: -#line 3641 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 624: +#line 3575 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('%', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8623 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8546 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 671: -#line 3645 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 625: +#line 3579 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('+', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8631 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8554 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 672: -#line 3649 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 626: +#line 3583 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('-', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8639 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8562 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 673: -#line 3653 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 627: +#line 3587 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('|', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8647 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8570 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 674: -#line 3657 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 628: +#line 3591 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('^', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8655 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8578 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 675: -#line 3661 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 629: +#line 3595 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('&', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8663 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8586 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 676: -#line 3665 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 630: +#line 3599 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(OROR, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8671 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8594 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 677: -#line 3669 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 631: +#line 3603 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(ANDAND, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8679 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8602 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 678: -#line 3673 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 632: +#line 3607 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(EQCOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8687 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8610 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 679: -#line 3677 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 633: +#line 3611 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(NECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8695 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8618 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 680: -#line 3681 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 634: +#line 3615 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(LECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8703 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8626 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 681: -#line 3685 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 635: +#line 3619 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(GECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8711 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8634 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 682: -#line 3689 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 636: +#line 3623 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('<', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8719 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8642 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 683: -#line 3693 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 637: +#line 3627 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('>', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8727 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8650 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 684: -#line 3697 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 638: +#line 3631 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(LSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8735 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8658 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 685: -#line 3701 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 639: +#line 3635 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(RSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8743 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8666 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 686: -#line 3705 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 640: +#line 3639 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('?', (yyvsp[-4].u.expr), (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8751 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8674 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 687: -#line 3709 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 641: +#line 3643 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('[', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); } -#line 8759 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8682 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 688: -#line 3713 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 642: +#line 3647 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('f', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); } -#line 8767 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8690 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 689: -#line 3717 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 643: +#line 3651 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('f', (yyvsp[-2].u.expr)); } -#line 8775 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8698 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 690: -#line 3721 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 644: +#line 3655 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression('.', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8783 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8706 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 691: -#line 3725 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 645: +#line 3659 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(POINTSAT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8791 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8714 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 692: -#line 3729 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 646: +#line 3663 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = (yyvsp[-1].u.expr); } -#line 8799 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8722 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 693: -#line 3736 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 647: +#line 3670 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); } -#line 8807 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8730 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 694: -#line 3740 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 648: +#line 3674 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(true); } -#line 8815 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8738 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 695: -#line 3744 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 649: +#line 3678 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(false); } -#line 8823 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8746 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 696: -#line 3748 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 650: +#line 3682 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); } -#line 8831 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8754 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 697: -#line 3752 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 651: +#line 3686 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression((yyvsp[0].u.real)); } -#line 8839 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8762 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 698: -#line 3756 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 652: +#line 3690 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = (yyvsp[0].u.expr); } -#line 8847 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8770 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 699: -#line 3760 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 653: +#line 3694 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = (yyvsp[0].u.expr); } -#line 8855 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8778 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 700: -#line 3764 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 654: +#line 3698 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression((yyvsp[0].u.identifier), current_scope, global_scope, current_lexer); } -#line 8863 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8786 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 701: -#line 3768 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 655: +#line 3702 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // A variable named "final". C++11 explicitly permits this. CPPIdentifier *ident = new CPPIdentifier("final", (yylsp[0])); (yyval.u.expr) = new CPPExpression(ident, current_scope, global_scope, current_lexer); } -#line 8873 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8796 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 702: -#line 3774 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 656: +#line 3708 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // A variable named "override". C++11 explicitly permits this. CPPIdentifier *ident = new CPPIdentifier("override", (yylsp[0])); (yyval.u.expr) = new CPPExpression(ident, current_scope, global_scope, current_lexer); } +#line 8806 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 657: +#line 3714 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::get_nullptr()); +} +#line 8814 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 658: +#line 3718 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyvsp[-6].u.closure_type)->_flags = (yyvsp[-4].u.integer); + (yyvsp[-6].u.closure_type)->_return_type = (yyvsp[-3].u.type); + (yyval.u.expr) = new CPPExpression(CPPExpression::lambda((yyvsp[-6].u.closure_type))); +} +#line 8824 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 659: +#line 3724 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyvsp[-9].u.closure_type)->_parameters = (yyvsp[-6].u.param_list); + (yyvsp[-9].u.closure_type)->_flags = (yyvsp[-4].u.integer); + (yyvsp[-9].u.closure_type)->_return_type = (yyvsp[-3].u.type); + (yyval.u.expr) = new CPPExpression(CPPExpression::lambda((yyvsp[-9].u.closure_type))); +} +#line 8835 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 660: +#line 3731 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_HAS_VIRTUAL_DESTRUCTOR, (yyvsp[-1].u.type))); +} +#line 8843 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 661: +#line 3735 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_ABSTRACT, (yyvsp[-1].u.type))); +} +#line 8851 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 662: +#line 3739 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CLASS, (yyvsp[-3].u.type), (yyvsp[-1].u.type))); +} +#line 8859 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 663: +#line 3743 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CLASS, (yyvsp[-1].u.type))); +} +#line 8867 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 664: +#line 3747 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CONSTRUCTIBLE, (yyvsp[-1].u.type))); +} +#line 8875 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 665: +#line 3751 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CONSTRUCTIBLE, (yyvsp[-3].u.type), (yyvsp[-1].u.type))); +} #line 8883 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 703: -#line 3780 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 666: +#line 3755 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::get_nullptr()); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CONVERTIBLE_TO, (yyvsp[-3].u.type), (yyvsp[-1].u.type))); } #line 8891 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; + case 667: +#line 3759 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_DESTRUCTIBLE, (yyvsp[-1].u.type))); +} +#line 8899 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 668: +#line 3763 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_EMPTY, (yyvsp[-1].u.type))); +} +#line 8907 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 669: +#line 3767 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_ENUM, (yyvsp[-1].u.type))); +} +#line 8915 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 670: +#line 3771 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_FINAL, (yyvsp[-1].u.type))); +} +#line 8923 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 671: +#line 3775 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_FUNDAMENTAL, (yyvsp[-1].u.type))); +} +#line 8931 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 672: +#line 3779 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_POD, (yyvsp[-1].u.type))); +} +#line 8939 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 673: +#line 3783 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_POLYMORPHIC, (yyvsp[-1].u.type))); +} +#line 8947 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 674: +#line 3787 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_STANDARD_LAYOUT, (yyvsp[-1].u.type))); +} +#line 8955 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 675: +#line 3791 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_TRIVIAL, (yyvsp[-1].u.type))); +} +#line 8963 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 676: +#line 3795 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_UNION, (yyvsp[-1].u.type))); +} +#line 8971 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 677: +#line 3809 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[0].u.expr); +} +#line 8979 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 678: +#line 3813 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-2].u.type), (yyvsp[0].u.expr))); +} +#line 8987 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 679: +#line 3817 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_static_cast)); +} +#line 8995 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 680: +#line 3821 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_dynamic_cast)); +} +#line 9003 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 681: +#line 3825 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_const_cast)); +} +#line 9011 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 682: +#line 3829 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-4].u.type), (yyvsp[-1].u.expr), CPPExpression::T_reinterpret_cast)); +} +#line 9019 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 683: +#line 3833 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func((yyvsp[-1].u.type))); +} +#line 9027 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 684: +#line 3837 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPDeclaration *arg = (yyvsp[-1].u.identifier)->find_symbol(current_scope, global_scope, current_lexer); + if (arg == (CPPDeclaration *)NULL) { + yyerror("undefined sizeof argument: " + (yyvsp[-1].u.identifier)->get_fully_scoped_name(), (yylsp[-1])); + } else if (arg->get_subtype() == CPPDeclaration::ST_instance) { + CPPInstance *inst = arg->as_instance(); + (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func(inst->_type)); + } else { + (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func(arg->as_type())); + } +} +#line 9043 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 685: +#line 3849 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_ellipsis_func((yyvsp[-1].u.identifier))); +} +#line 9051 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 686: +#line 3853 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::alignof_func((yyvsp[-1].u.type))); +} +#line 9059 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 687: +#line 3857 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::new_op((yyvsp[0].u.type))); +} +#line 9067 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 688: +#line 3861 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::new_op((yyvsp[-3].u.type), (yyvsp[-1].u.expr))); +} +#line 9075 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 689: +#line 3865 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPIdentifier ident(""); + ident.add_name("std"); + ident.add_name("type_info"); + CPPType *std_type_info = ident.find_type(current_scope, global_scope, false, current_lexer); + if (!std_type_info) { + yywarning("cannot use typeid before including ", (yylsp[-3])); + } + (yyval.u.expr) = new CPPExpression(CPPExpression::typeid_op((yyvsp[-1].u.type), std_type_info)); +} +#line 9090 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 690: +#line 3876 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + CPPIdentifier ident(""); + ident.add_name("std"); + ident.add_name("type_info"); + CPPType *std_type_info = ident.find_type(current_scope, global_scope, false, current_lexer); + if (!std_type_info) { + yywarning("cannot use typeid before including ", (yylsp[-3])); + } + (yyval.u.expr) = new CPPExpression(CPPExpression::typeid_op((yyvsp[-1].u.expr), std_type_info)); +} +#line 9105 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 691: +#line 3887 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(UNARY_NOT, (yyvsp[0].u.expr)); +} +#line 9113 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 692: +#line 3891 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(UNARY_NEGATE, (yyvsp[0].u.expr)); +} +#line 9121 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 693: +#line 3895 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(UNARY_MINUS, (yyvsp[0].u.expr)); +} +#line 9129 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 694: +#line 3899 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(UNARY_PLUS, (yyvsp[0].u.expr)); +} +#line 9137 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 695: +#line 3903 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(UNARY_REF, (yyvsp[0].u.expr)); +} +#line 9145 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 696: +#line 3907 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('*', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9153 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 697: +#line 3911 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('/', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9161 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 698: +#line 3915 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('%', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9169 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 699: +#line 3919 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('+', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9177 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 700: +#line 3923 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('-', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9185 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 701: +#line 3927 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('|', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9193 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 702: +#line 3931 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('^', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9201 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 703: +#line 3935 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('&', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9209 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 704: +#line 3939 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(OROR, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9217 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 705: +#line 3943 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(ANDAND, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9225 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 706: +#line 3947 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(EQCOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9233 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 707: +#line 3951 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(NECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9241 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 708: +#line 3955 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(LECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9249 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 709: +#line 3959 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(GECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9257 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 710: +#line 3963 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('<', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9265 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + case 711: -#line 3801 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3967 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('>', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9273 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 712: +#line 3971 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(LSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9281 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 713: +#line 3975 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(RSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9289 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 714: +#line 3979 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('?', (yyvsp[-4].u.expr), (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9297 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 715: +#line 3983 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('[', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); +} +#line 9305 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 716: +#line 3987 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('f', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); +} +#line 9313 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 717: +#line 3991 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('f', (yyvsp[-2].u.expr)); +} +#line 9321 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 718: +#line 3995 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression('.', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9329 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 719: +#line 3999 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(POINTSAT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); +} +#line 9337 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 720: +#line 4003 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[-1].u.expr); +} +#line 9345 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 721: +#line 4010 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); +} +#line 9353 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 722: +#line 4014 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(true); +} +#line 9361 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 723: +#line 4018 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(false); +} +#line 9369 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 724: +#line 4022 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); +} +#line 9377 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 725: +#line 4026 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression((yyvsp[0].u.real)); +} +#line 9385 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 726: +#line 4030 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[0].u.expr); +} +#line 9393 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 727: +#line 4034 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[0].u.expr); +} +#line 9401 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 728: +#line 4038 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression((yyvsp[0].u.identifier), current_scope, global_scope, current_lexer); +} +#line 9409 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 729: +#line 4042 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + // A variable named "final". C++11 explicitly permits this. + CPPIdentifier *ident = new CPPIdentifier("final", (yylsp[0])); + (yyval.u.expr) = new CPPExpression(ident, current_scope, global_scope, current_lexer); +} +#line 9419 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 730: +#line 4048 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + // A variable named "override". C++11 explicitly permits this. + CPPIdentifier *ident = new CPPIdentifier("override", (yylsp[0])); + (yyval.u.expr) = new CPPExpression(ident, current_scope, global_scope, current_lexer); +} +#line 9429 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 731: +#line 4054 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::get_nullptr()); +} +#line 9437 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 732: +#line 4062 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.closure_type) = new CPPClosureType(); +} +#line 9445 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 733: +#line 4066 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.closure_type) = new CPPClosureType(CPPClosureType::CT_by_value); +} +#line 9453 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 734: +#line 4070 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.closure_type) = new CPPClosureType(CPPClosureType::CT_by_reference); +} +#line 9461 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 735: +#line 4074 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.closure_type) = new CPPClosureType(); + (yyvsp[-1].u.capture)->_initializer = (yyvsp[0].u.expr); + (yyval.u.closure_type)->_captures.push_back(*(yyvsp[-1].u.capture)); + delete (yyvsp[-1].u.capture); +} +#line 9472 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 736: +#line 4081 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.closure_type) = (yyvsp[-3].u.closure_type); + (yyvsp[-1].u.capture)->_initializer = (yyvsp[0].u.expr); + (yyval.u.closure_type)->_captures.push_back(*(yyvsp[-1].u.capture)); + delete (yyvsp[-1].u.capture); +} +#line 9483 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 737: +#line 4091 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.capture) = new CPPClosureType::Capture; + (yyval.u.capture)->_name = (yyvsp[0].u.identifier)->get_simple_name(); + (yyval.u.capture)->_type = CPPClosureType::CT_by_reference; +} +#line 9493 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 738: +#line 4097 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.capture) = new CPPClosureType::Capture; + (yyval.u.capture)->_name = (yyvsp[-1].u.identifier)->get_simple_name(); + (yyval.u.capture)->_type = CPPClosureType::CT_by_reference; +} +#line 9503 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 739: +#line 4103 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.capture) = new CPPClosureType::Capture; + (yyval.u.capture)->_name = (yyvsp[0].u.identifier)->get_simple_name(); + if ((yyval.u.capture)->_name == "this") { + (yyval.u.capture)->_type = CPPClosureType::CT_by_reference; + } else { + (yyval.u.capture)->_type = CPPClosureType::CT_by_value; + } +} +#line 9517 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 740: +#line 4113 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.capture) = new CPPClosureType::Capture; + (yyval.u.capture)->_name = (yyvsp[0].u.identifier)->get_simple_name(); + (yyval.u.capture)->_type = CPPClosureType::CT_by_value; + if ((yyval.u.capture)->_name != "this") { + yywarning("only capture name 'this' may be preceded by an asterisk", (yylsp[0])); + } +} +#line 9530 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 741: +#line 4125 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPType *type = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, true); if (type == NULL) { @@ -8908,181 +9538,177 @@ yyreduce: } (yyval.u.type) = type; } -#line 8903 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9542 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 712: -#line 3809 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - CPPType *type = (yyvsp[0].u.identifier)->find_type(current_scope, global_scope, true, current_lexer); - if (type == NULL) { - type = CPPType::new_type(new CPPTBDType((yyvsp[0].u.identifier))); - } - (yyval.u.type) = type; -} -#line 8915 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 713: -#line 3817 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 742: +#line 4133 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = CPPType::new_type(new CPPTBDType((yyvsp[0].u.identifier))); } -#line 8923 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9550 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 714: -#line 3821 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 743: +#line 4137 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPClassTemplateParameter *ctp = new CPPClassTemplateParameter((yyvsp[-1].u.identifier)); ctp->_packed = true; (yyval.u.type) = CPPType::new_type(ctp); } -#line 8933 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9560 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 715: -#line 3851 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 744: +#line 4167 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = (yyvsp[0].u.identifier); } -#line 8941 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9568 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 716: -#line 3855 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 745: +#line 4171 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = (yyvsp[0].u.identifier); } -#line 8949 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9576 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 717: -#line 3859 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 746: +#line 4175 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = (yyvsp[0].u.identifier); } -#line 8957 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9584 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 718: -#line 3863 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 747: +#line 4179 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = new CPPIdentifier("final", (yylsp[0])); } -#line 8965 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9592 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 719: -#line 3867 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 748: +#line 4183 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = new CPPIdentifier("override", (yylsp[0])); } -#line 8973 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9600 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 720: -#line 3871 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 749: +#line 4187 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // This is not a keyword in Python, so it is useful to be able to use this // in MAKE_PROPERTY definitions, etc. (yyval.u.identifier) = new CPPIdentifier("signed", (yylsp[0])); } -#line 8983 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9610 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 721: -#line 3877 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 750: +#line 4193 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = new CPPIdentifier("float", (yylsp[0])); } -#line 8991 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9618 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 722: -#line 3881 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 751: +#line 4197 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = new CPPIdentifier("public", (yylsp[0])); } -#line 8999 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9626 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 723: -#line 3885 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 752: +#line 4201 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = new CPPIdentifier("private", (yylsp[0])); } -#line 9007 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9634 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 724: -#line 3889 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 753: +#line 4205 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = new CPPIdentifier("static", (yylsp[0])); } -#line 9015 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9642 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 725: -#line 3900 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 754: +#line 4209 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.identifier) = new CPPIdentifier("default", (yylsp[0])); +} +#line 9650 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 755: +#line 4220 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = (yyvsp[0].u.identifier); } -#line 9023 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9658 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 726: -#line 3904 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 756: +#line 4224 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = (yyvsp[0].u.identifier); } -#line 9031 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9666 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 727: -#line 3908 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 757: +#line 4228 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = (yyvsp[0].u.identifier); } -#line 9039 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9674 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 728: -#line 3912 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 758: +#line 4232 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.identifier) = new CPPIdentifier("override", (yylsp[0])); } -#line 9047 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9682 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 729: -#line 3920 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 759: +#line 4240 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression((yyvsp[0].str)); } -#line 9055 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9690 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 730: -#line 3924 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 760: +#line 4244 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = (yyvsp[0].u.expr); } -#line 9063 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9698 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 731: -#line 3928 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 761: +#line 4248 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // The right string takes on the literal type of the left. (yyval.u.expr) = (yyvsp[-1].u.expr); (yyval.u.expr)->_str += (yyvsp[0].str); } -#line 9073 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9708 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 732: -#line 3934 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 762: +#line 4254 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { // We have to check that the two literal types match up. (yyval.u.expr) = (yyvsp[-1].u.expr); @@ -9091,11 +9717,11 @@ yyreduce: } (yyval.u.expr)->_str += (yyvsp[0].u.expr)->_str; } -#line 9086 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9721 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; -#line 9090 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9725 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires diff --git a/dtool/src/cppparser/cppBison.h.prebuilt b/dtool/src/cppparser/cppBison.h.prebuilt index 223bfdf123..a2ec1fca8c 100644 --- a/dtool/src/cppparser/cppBison.h.prebuilt +++ b/dtool/src/cppparser/cppBison.h.prebuilt @@ -89,103 +89,106 @@ extern int cppyydebug; XOREQUAL = 299, LSHIFTEQUAL = 300, RSHIFTEQUAL = 301, - KW_ALIGNAS = 302, - KW_ALIGNOF = 303, - KW_AUTO = 304, - KW_BEGIN_PUBLISH = 305, - KW_BLOCKING = 306, - KW_BOOL = 307, - KW_CATCH = 308, - KW_CHAR = 309, - KW_CHAR16_T = 310, - KW_CHAR32_T = 311, - KW_CLASS = 312, - KW_CONST = 313, - KW_CONSTEXPR = 314, - KW_CONST_CAST = 315, - KW_DECLTYPE = 316, - KW_DEFAULT = 317, - KW_DELETE = 318, - KW_DOUBLE = 319, - KW_DYNAMIC_CAST = 320, - KW_ELSE = 321, - KW_END_PUBLISH = 322, - KW_ENUM = 323, - KW_EXTENSION = 324, - KW_EXTERN = 325, - KW_EXPLICIT = 326, - KW_PUBLISHED = 327, - KW_FALSE = 328, - KW_FINAL = 329, - KW_FLOAT = 330, - KW_FRIEND = 331, - KW_FOR = 332, - KW_GOTO = 333, - KW_HAS_VIRTUAL_DESTRUCTOR = 334, - KW_IF = 335, - KW_INLINE = 336, - KW_INT = 337, - KW_IS_ABSTRACT = 338, - KW_IS_BASE_OF = 339, - KW_IS_CLASS = 340, - KW_IS_CONSTRUCTIBLE = 341, - KW_IS_CONVERTIBLE_TO = 342, - KW_IS_DESTRUCTIBLE = 343, - KW_IS_EMPTY = 344, - KW_IS_ENUM = 345, - KW_IS_FINAL = 346, - KW_IS_FUNDAMENTAL = 347, - KW_IS_POD = 348, - KW_IS_POLYMORPHIC = 349, - KW_IS_STANDARD_LAYOUT = 350, - KW_IS_TRIVIAL = 351, - KW_IS_UNION = 352, - KW_LONG = 353, - KW_MAKE_MAP_PROPERTY = 354, - KW_MAKE_PROPERTY = 355, - KW_MAKE_PROPERTY2 = 356, - KW_MAKE_SEQ = 357, - KW_MAKE_SEQ_PROPERTY = 358, - KW_MUTABLE = 359, - KW_NAMESPACE = 360, - KW_NEW = 361, - KW_NOEXCEPT = 362, - KW_NULLPTR = 363, - KW_OPERATOR = 364, - KW_OVERRIDE = 365, - KW_PRIVATE = 366, - KW_PROTECTED = 367, - KW_PUBLIC = 368, - KW_REGISTER = 369, - KW_REINTERPRET_CAST = 370, - KW_RETURN = 371, - KW_SHORT = 372, - KW_SIGNED = 373, - KW_SIZEOF = 374, - KW_STATIC = 375, - KW_STATIC_ASSERT = 376, - KW_STATIC_CAST = 377, - KW_STRUCT = 378, - KW_TEMPLATE = 379, - KW_THREAD_LOCAL = 380, - KW_THROW = 381, - KW_TRUE = 382, - KW_TRY = 383, - KW_TYPEDEF = 384, - KW_TYPEID = 385, - KW_TYPENAME = 386, - KW_UNDERLYING_TYPE = 387, - KW_UNION = 388, - KW_UNSIGNED = 389, - KW_USING = 390, - KW_VIRTUAL = 391, - KW_VOID = 392, - KW_VOLATILE = 393, - KW_WCHAR_T = 394, - KW_WHILE = 395, - START_CPP = 396, - START_CONST_EXPR = 397, - START_TYPE = 398 + 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 }; #endif /* Tokens. */ @@ -233,103 +236,106 @@ extern int cppyydebug; #define XOREQUAL 299 #define LSHIFTEQUAL 300 #define RSHIFTEQUAL 301 -#define KW_ALIGNAS 302 -#define KW_ALIGNOF 303 -#define KW_AUTO 304 -#define KW_BEGIN_PUBLISH 305 -#define KW_BLOCKING 306 -#define KW_BOOL 307 -#define KW_CATCH 308 -#define KW_CHAR 309 -#define KW_CHAR16_T 310 -#define KW_CHAR32_T 311 -#define KW_CLASS 312 -#define KW_CONST 313 -#define KW_CONSTEXPR 314 -#define KW_CONST_CAST 315 -#define KW_DECLTYPE 316 -#define KW_DEFAULT 317 -#define KW_DELETE 318 -#define KW_DOUBLE 319 -#define KW_DYNAMIC_CAST 320 -#define KW_ELSE 321 -#define KW_END_PUBLISH 322 -#define KW_ENUM 323 -#define KW_EXTENSION 324 -#define KW_EXTERN 325 -#define KW_EXPLICIT 326 -#define KW_PUBLISHED 327 -#define KW_FALSE 328 -#define KW_FINAL 329 -#define KW_FLOAT 330 -#define KW_FRIEND 331 -#define KW_FOR 332 -#define KW_GOTO 333 -#define KW_HAS_VIRTUAL_DESTRUCTOR 334 -#define KW_IF 335 -#define KW_INLINE 336 -#define KW_INT 337 -#define KW_IS_ABSTRACT 338 -#define KW_IS_BASE_OF 339 -#define KW_IS_CLASS 340 -#define KW_IS_CONSTRUCTIBLE 341 -#define KW_IS_CONVERTIBLE_TO 342 -#define KW_IS_DESTRUCTIBLE 343 -#define KW_IS_EMPTY 344 -#define KW_IS_ENUM 345 -#define KW_IS_FINAL 346 -#define KW_IS_FUNDAMENTAL 347 -#define KW_IS_POD 348 -#define KW_IS_POLYMORPHIC 349 -#define KW_IS_STANDARD_LAYOUT 350 -#define KW_IS_TRIVIAL 351 -#define KW_IS_UNION 352 -#define KW_LONG 353 -#define KW_MAKE_MAP_PROPERTY 354 -#define KW_MAKE_PROPERTY 355 -#define KW_MAKE_PROPERTY2 356 -#define KW_MAKE_SEQ 357 -#define KW_MAKE_SEQ_PROPERTY 358 -#define KW_MUTABLE 359 -#define KW_NAMESPACE 360 -#define KW_NEW 361 -#define KW_NOEXCEPT 362 -#define KW_NULLPTR 363 -#define KW_OPERATOR 364 -#define KW_OVERRIDE 365 -#define KW_PRIVATE 366 -#define KW_PROTECTED 367 -#define KW_PUBLIC 368 -#define KW_REGISTER 369 -#define KW_REINTERPRET_CAST 370 -#define KW_RETURN 371 -#define KW_SHORT 372 -#define KW_SIGNED 373 -#define KW_SIZEOF 374 -#define KW_STATIC 375 -#define KW_STATIC_ASSERT 376 -#define KW_STATIC_CAST 377 -#define KW_STRUCT 378 -#define KW_TEMPLATE 379 -#define KW_THREAD_LOCAL 380 -#define KW_THROW 381 -#define KW_TRUE 382 -#define KW_TRY 383 -#define KW_TYPEDEF 384 -#define KW_TYPEID 385 -#define KW_TYPENAME 386 -#define KW_UNDERLYING_TYPE 387 -#define KW_UNION 388 -#define KW_UNSIGNED 389 -#define KW_USING 390 -#define KW_VIRTUAL 391 -#define KW_VOID 392 -#define KW_VOLATILE 393 -#define KW_WCHAR_T 394 -#define KW_WHILE 395 -#define START_CPP 396 -#define START_CONST_EXPR 397 -#define START_TYPE 398 +#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 /* Value type. */ diff --git a/dtool/src/cppparser/cppBison.yxx b/dtool/src/cppparser/cppBison.yxx index 04cdb53a4b..68f4f96706 100644 --- a/dtool/src/cppparser/cppBison.yxx +++ b/dtool/src/cppparser/cppBison.yxx @@ -8,6 +8,7 @@ #include "cppBisonDefs.h" #include "cppParser.h" +#include "cppClosureType.h" #include "cppExpression.h" #include "cppSimpleType.h" #include "cppExtensionType.h" @@ -44,6 +45,7 @@ static CPPEnumType *current_enum = NULL; static int current_storage_class = 0; static CPPType *current_type = NULL; static CPPExpression *current_expr = NULL; +static CPPClosureType *current_closure = NULL; static int publish_nest_level = 0; static CPPVisibility publish_previous; static YYLTYPE publish_loc; @@ -247,6 +249,8 @@ pop_struct() { %token XOREQUAL %token LSHIFTEQUAL %token RSHIFTEQUAL +%token ATTR_LEFT +%token ATTR_RIGHT %token KW_ALIGNAS %token KW_ALIGNOF @@ -300,6 +304,7 @@ pop_struct() { %token KW_IS_TRIVIAL %token KW_IS_UNION %token KW_LONG +%token KW_MAKE_MAP_KEYS_SEQ %token KW_MAKE_MAP_PROPERTY %token KW_MAKE_PROPERTY %token KW_MAKE_PROPERTY2 @@ -363,6 +368,8 @@ pop_struct() { %type function_parameters %type formal_parameter_list %type formal_parameters +%type capture_list +%type capture %type template_parameter_maybe_initialize %type maybe_initialize %type maybe_initialize_or_constructor_body @@ -391,6 +398,7 @@ pop_struct() { %type class_derivation_name %type enum_element_type %type maybe_trailing_return_type +%type maybe_comma_identifier /*%type typedefname*/ %type name %type name_no_final @@ -542,202 +550,341 @@ declaration: { current_scope->set_current_vis(V_private); } - | KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ')' ';' -{ - - CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5); - } - - CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), NULL, current_scope, @1.file); - current_scope->add_declaration(make_property, global_scope, current_lexer, @1); -} - | KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';' + | KW_MAKE_PROPERTY '(' name ',' IDENTIFIER maybe_comma_identifier ')' ';' { CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5); - } else { - CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer); - CPPFunctionGroup *setter_func = NULL; + CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal, current_scope, @1.file); + make_property->_get_function = getter->as_function_group(); - if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid setter: " + $7->get_fully_scoped_name(), @7); - } else { - setter_func = setter->as_function_group(); + if ($6 != nullptr) { + CPPDeclaration *setter = $6->find_symbol(current_scope, global_scope, current_lexer); + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid setter: " + $6->get_fully_scoped_name(), @6); + } else { + make_property->_set_function = setter->as_function_group(); + } } - CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), - setter_func, current_scope, @1.file); current_scope->add_declaration(make_property, global_scope, current_lexer, @1); } } | KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';' { CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5); } else { - CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer); - CPPFunctionGroup *setter_func = NULL; + CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal, current_scope, @1.file); + make_property->_get_function = getter->as_function_group(); - if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { + CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer); + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid setter: " + $7->get_fully_scoped_name(), @7); } else { - setter_func = setter->as_function_group(); + make_property->_set_function = setter->as_function_group(); } CPPDeclaration *deleter = $9->find_symbol(current_scope, global_scope, current_lexer); - if (deleter == (CPPDeclaration *)NULL || deleter->get_subtype() != CPPDeclaration::ST_function_group) { + if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("reference to non-existent or invalid delete method: " + $9->get_fully_scoped_name(), @9); - deleter = NULL; - } - - CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), - setter_func, current_scope, @1.file); - if (deleter) { + } else { make_property->_del_function = deleter->as_function_group(); } + current_scope->add_declaration(make_property, global_scope, current_lexer, @1); } } | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';' { CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer); - if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5); - length_getter = NULL; + length_getter = nullptr; } CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); + getter = nullptr; } - CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), NULL, current_scope, @1.file); - make_property->_length_function = length_getter->as_function_group(); - current_scope->add_declaration(make_property, global_scope, current_lexer, @1); + if (getter != nullptr && length_getter != nullptr) { + CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file); + make_property->_get_function = getter->as_function_group(); + make_property->_length_function = length_getter->as_function_group(); + current_scope->add_declaration(make_property, global_scope, current_lexer, @1); + } } | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';' { CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer); - if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5); - length_getter = NULL; + length_getter = nullptr; } CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); + getter = nullptr; + } + + if (getter != nullptr && length_getter != nullptr) { + CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file); + make_property->_get_function = getter->as_function_group(); + make_property->_length_function = length_getter->as_function_group(); - } else { CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer); - CPPFunctionGroup *setter_func = NULL; - - if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9); } else { - setter_func = setter->as_function_group(); + make_property->_set_function = setter->as_function_group(); } - CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), - setter_func, current_scope, @1.file); - make_property->_length_function = length_getter->as_function_group(); current_scope->add_declaration(make_property, global_scope, current_lexer, @1); } } | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';' { CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer); - if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5); length_getter = NULL; } CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); + getter = nullptr; + } + + if (getter != nullptr && length_getter != nullptr) { + CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file); + make_property->_get_function = getter->as_function_group(); + make_property->_length_function = length_getter->as_function_group(); - } else { CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer); - CPPFunctionGroup *setter_func = NULL; - - if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9); } else { - setter_func = setter->as_function_group(); + make_property->_set_function = setter->as_function_group(); } CPPDeclaration *deleter = $11->find_symbol(current_scope, global_scope, current_lexer); - if (deleter == (CPPDeclaration *)NULL || deleter->get_subtype() != CPPDeclaration::ST_function_group) { + if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("reference to non-existent or invalid delete method: " + $11->get_fully_scoped_name(), @11); - deleter = NULL; - } - - CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), - setter_func, current_scope, @1.file); - make_property->_length_function = length_getter->as_function_group(); - if (deleter) { + } else { make_property->_del_function = deleter->as_function_group(); } + current_scope->add_declaration(make_property, global_scope, current_lexer, @1); } +} + | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';' +{ + CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer); + if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5); + length_getter = NULL; + } + + CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); + getter = nullptr; + } + + if (getter != nullptr && length_getter != nullptr) { + CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file); + make_property->_get_function = getter->as_function_group(); + make_property->_length_function = length_getter->as_function_group(); + + CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer); + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9); + } else { + make_property->_set_function = setter->as_function_group(); + } + + CPPDeclaration *deleter = $11->find_symbol(current_scope, global_scope, current_lexer); + if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid delete method: " + $11->get_fully_scoped_name(), @11); + } else { + make_property->_del_function = deleter->as_function_group(); + } + + CPPDeclaration *inserter = $13->find_symbol(current_scope, global_scope, current_lexer); + if (inserter == nullptr || inserter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid append method: " + $13->get_fully_scoped_name(), @13); + } else { + make_property->_insert_function = inserter->as_function_group(); + } + + current_scope->add_declaration(make_property, global_scope, current_lexer, @1); + } +} + | KW_MAKE_MAP_PROPERTY '(' name ',' IDENTIFIER ')' ';' +{ + CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer); + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid item getter method: " + $5->get_fully_scoped_name(), @5); + + } else { + CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_mapping, current_scope, @1.file); + make_property->_get_function = getter->as_function_group(); + current_scope->add_declaration(make_property, global_scope, current_lexer, @1); + } +} + | KW_MAKE_MAP_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';' +{ + CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); + + } else { + CPPMakeProperty *make_property; + make_property = new CPPMakeProperty($3, CPPMakeProperty::T_mapping, current_scope, @1.file); + make_property->_get_function = getter->as_function_group(); + + CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer); + if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5); + } else { + make_property->_has_function = hasser->as_function_group(); + } + + current_scope->add_declaration(make_property, global_scope, current_lexer, @1); + } +} + | KW_MAKE_MAP_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER maybe_comma_identifier ')' ';' +{ + CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); + + } else { + CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_mapping, current_scope, @1.file); + make_property->_get_function = getter->as_function_group(); + + CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer); + if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5); + } else { + make_property->_has_function = hasser->as_function_group(); + } + + CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer); + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9); + } else { + make_property->_set_function = setter->as_function_group(); + } + + if ($10 != nullptr) { + CPPDeclaration *deleter = $10->find_symbol(current_scope, global_scope, current_lexer); + if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid delete method: " + $10->get_fully_scoped_name(), @10); + } else { + make_property->_del_function = deleter->as_function_group(); + } + } + + current_scope->add_declaration(make_property, global_scope, current_lexer, @1); + } +} + | KW_MAKE_MAP_KEYS_SEQ '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';' +{ + CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer); + if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5); + length_getter = nullptr; + } + + CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); + getter = nullptr; + } + + if (getter != nullptr && length_getter != nullptr) { + CPPMakeProperty *make_property = nullptr; + for (size_t i = 0; i < current_scope->_declarations.size(); ++i) { + make_property = current_scope->_declarations[i]->as_make_property(); + if (make_property != nullptr) { + if (make_property->get_fully_scoped_name() == $3->get_fully_scoped_name()) { + break; + } else { + make_property = nullptr; + } + } + } + if (make_property != nullptr) { + make_property->_get_key_function = getter->as_function_group(); + make_property->_length_function = length_getter->as_function_group(); + } else { + yyerror("reference to non-existent MAKE_MAP_PROPERTY: " + $3->get_fully_scoped_name(), @3); + } + } } | KW_MAKE_PROPERTY2 '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';' { - CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer); - if (hasser == (CPPDeclaration *)NULL || hasser->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid has-function: " + $5->get_fully_scoped_name(), @5); - } - CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); - } - if (hasser && getter) { + } else { CPPMakeProperty *make_property; - make_property = new CPPMakeProperty($3, - hasser->as_function_group(), - getter->as_function_group(), - NULL, NULL, + make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal, current_scope, @1.file); + make_property->_get_function = getter->as_function_group(); + + CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer); + if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5); + } else { + make_property->_has_function = hasser->as_function_group(); + } + current_scope->add_declaration(make_property, global_scope, current_lexer, @1); } } | KW_MAKE_PROPERTY2 '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';' { - CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer); - if (hasser == (CPPDeclaration *)NULL || hasser->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid has-function: " + $5->get_fully_scoped_name(), @5); - } - CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); - if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { + if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) { yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); - } - CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer); - if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9); - } - - CPPDeclaration *clearer = $11->find_symbol(current_scope, global_scope, current_lexer); - if (clearer == (CPPDeclaration *)NULL || clearer->get_subtype() != CPPDeclaration::ST_function_group) { - yyerror("Reference to non-existent or invalid clear-function: " + $11->get_fully_scoped_name(), @11); - } - - if (hasser && getter && setter && clearer) { + } else { CPPMakeProperty *make_property; - make_property = new CPPMakeProperty($3, - hasser->as_function_group(), - getter->as_function_group(), - setter->as_function_group(), - clearer->as_function_group(), + make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal, current_scope, @1.file); + make_property->_get_function = getter->as_function_group(); + + CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer); + if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5); + } else { + make_property->_has_function = hasser->as_function_group(); + } + + CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer); + if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9); + } else { + make_property->_set_function = setter->as_function_group(); + } + + CPPDeclaration *clearer = $11->find_symbol(current_scope, global_scope, current_lexer); + if (clearer == nullptr || clearer->get_subtype() != CPPDeclaration::ST_function_group) { + yyerror("reference to non-existent or invalid clear method: " + $11->get_fully_scoped_name(), @11); + } else { + make_property->_clear_function = clearer->as_function_group(); + } + current_scope->add_declaration(make_property, global_scope, current_lexer, @1); } } @@ -870,10 +1017,18 @@ storage_class: { $$ = $2 | (int)CPPInstance::SC_thread_local; } - | '[' '[' attribute_specifiers ']' ']' storage_class + | ATTR_LEFT attribute_specifiers ATTR_RIGHT storage_class { // Ignore attribute specifiers for now. - $$ = $6; + $$ = $4; +} + | KW_ALIGNAS '(' const_expr ')' storage_class +{ + $$ = $5; +} + | KW_ALIGNAS '(' type_decl ')' storage_class +{ + $$ = $5; } ; @@ -885,6 +1040,7 @@ attribute_specifiers: attribute_specifier: name | name '(' formal_parameter_list ')' + | KW_USING name ':' attribute_specifier ; type_like_declaration: @@ -913,12 +1069,21 @@ type_like_declaration: current_scope->add_declaration($2, global_scope, current_lexer, @2); } - | storage_class constructor_prototype maybe_initialize_or_constructor_body + | storage_class constructor_prototype { if ($2 != (CPPInstance *)NULL) { + // Push the scope so that the initializers can make use of things defined + // in the class body. + push_scope($2->get_scope(current_scope, global_scope)); $2->_storage_class |= (current_storage_class | $1); + } +} + maybe_initialize_or_constructor_body +{ + if ($2 != (CPPInstance *)NULL) { + pop_scope(); current_scope->add_declaration($2, global_scope, current_lexer, @2); - $2->set_initializer($3); + $2->set_initializer($4); } } | storage_class function_prototype maybe_initialize_or_function_body @@ -1221,6 +1386,15 @@ function_post: { $$ = $1 | (int)CPPFunctionType::F_noexcept; } +/* | function_post KW_NOEXCEPT '(' const_expr ')' +{ + CPPExpression::Result result = $4->evaluate(); + if (result._type == CPPExpression::RT_error) { + yywarning("noexcept requires a constant expression", @4); + } else if (result.as_boolean()) { + $$ = $1 | (int)CPPFunctionType::F_noexcept; + } +}*/ | function_post KW_FINAL { $$ = $1 | (int)CPPFunctionType::F_final; @@ -1241,6 +1415,11 @@ function_post: { // Used for lambdas, currently ignored. $$ = $1; +} + | function_post KW_CONSTEXPR +{ + // Used for lambdas in C++17, currently ignored. + $$ = $1; } | function_post KW_THROW '(' ')' { @@ -1254,10 +1433,10 @@ function_post: { $$ = $1; } -/* | function_post '[' '[' attribute_specifiers ']' ']' + | function_post ATTR_LEFT attribute_specifiers ATTR_RIGHT { $$ = $1; -}*/ +} ; function_operator: @@ -1422,10 +1601,12 @@ function_operator: more_template_declaration: type_like_declaration | template_declaration + | friend_declaration ; template_declaration: - KW_TEMPLATE + KW_EXTERN template_declaration + | KW_TEMPLATE { push_scope(new CPPTemplateScope(current_scope)); } @@ -1433,7 +1614,8 @@ template_declaration: { pop_scope(); } - | KW_TEMPLATE type_like_declaration + | KW_TEMPLATE type_like_declaration + | KW_TEMPLATE friend_declaration ; template_formal_parameters: @@ -1682,6 +1864,18 @@ maybe_trailing_return_type: ; +maybe_comma_identifier: + empty +{ + $$ = NULL; +} + | ',' IDENTIFIER +{ + $$ = $2; +} + ; + + function_parameter_list: empty { @@ -1885,6 +2079,10 @@ function_parameter: | KW_REGISTER function_parameter { $$ = $2; +} + | ATTR_LEFT attribute_specifiers ATTR_RIGHT function_parameter +{ + $$ = $4; } ; @@ -2228,16 +2426,16 @@ type: { $$ = CPPType::new_type($1); } - | struct_keyword name + | struct_keyword struct_attributes name { - CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer); + CPPType *type = $3->find_type(current_scope, global_scope, false, current_lexer); if (type != NULL) { $$ = type; } else { CPPExtensionType *et = - CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file)) + CPPType::new_type(new CPPExtensionType($1, $3, current_scope, @1.file)) ->as_extension_type(); - CPPScope *scope = $2->get_scope(current_scope, global_scope); + CPPScope *scope = $3->get_scope(current_scope, global_scope); if (scope != NULL) { scope->define_extension_type(et); } @@ -2268,6 +2466,10 @@ type: str << *$3; yyerror("could not determine type of " + str.str(), @3); } +} + | KW_DECLTYPE '(' KW_AUTO ')' +{ + $$ = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_auto)); } | KW_UNDERLYING_TYPE '(' full_type ')' { @@ -2325,16 +2527,16 @@ type_decl: { $$ = new CPPTypeDeclaration(CPPType::new_type($1)); } - | struct_keyword name + | struct_keyword struct_attributes name { - CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer); + CPPType *type = $3->find_type(current_scope, global_scope, false, current_lexer); if (type != NULL) { $$ = type; } else { CPPExtensionType *et = - CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file)) + CPPType::new_type(new CPPExtensionType($1, $3, current_scope, @1.file)) ->as_extension_type(); - CPPScope *scope = $2->get_scope(current_scope, global_scope); + CPPScope *scope = $3->get_scope(current_scope, global_scope); if (scope != NULL) { scope->define_extension_type(et); } @@ -2383,6 +2585,10 @@ type_decl: str << *$3; yyerror("could not determine type of " + str.str(), @3); } +} + | KW_DECLTYPE '(' KW_AUTO ')' +{ + $$ = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_auto)); } | KW_UNDERLYING_TYPE '(' full_type ')' { @@ -2417,16 +2623,16 @@ predefined_type: { $$ = CPPType::new_type(new CPPTBDType($2)); } - | struct_keyword name + | struct_keyword struct_attributes name { - CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer); + CPPType *type = $3->find_type(current_scope, global_scope, false, current_lexer); if (type != NULL) { $$ = type; } else { CPPExtensionType *et = - CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file)) + CPPType::new_type(new CPPExtensionType($1, $3, current_scope, @1.file)) ->as_extension_type(); - CPPScope *scope = $2->get_scope(current_scope, global_scope); + CPPScope *scope = $3->get_scope(current_scope, global_scope); if (scope != NULL) { scope->define_extension_type(et); } @@ -2507,8 +2713,15 @@ full_type: } ; +struct_attributes: + empty + | struct_attributes ATTR_LEFT attribute_specifiers ATTR_RIGHT + | struct_attributes KW_ALIGNAS '(' const_expr ')' + | struct_attributes KW_ALIGNAS '(' type_decl ')' + ; + anonymous_struct: - struct_keyword '{' + struct_keyword struct_attributes '{' { CPPVisibility starting_vis = ($1 == CPPExtensionType::T_class) ? V_private : V_public; @@ -2532,19 +2745,19 @@ anonymous_struct: ; named_struct: - struct_keyword name_no_final + struct_keyword struct_attributes name_no_final { CPPVisibility starting_vis = ($1 == CPPExtensionType::T_class) ? V_private : V_public; - CPPScope *scope = $2->get_scope(current_scope, global_scope, current_lexer); + CPPScope *scope = $3->get_scope(current_scope, global_scope, current_lexer); if (scope == NULL) { scope = current_scope; } - CPPScope *new_scope = new CPPScope(scope, $2->_names.back(), + CPPScope *new_scope = new CPPScope(scope, $3->_names.back(), starting_vis); - CPPStructType *st = new CPPStructType($1, $2, current_scope, + CPPStructType *st = new CPPStructType($1, $3, current_scope, new_scope, @1.file); new_scope->set_struct_type(st); current_scope->define_extension_type(st); @@ -2927,6 +3140,7 @@ element: | SCOPE | PLUSPLUS | MINUSMINUS | TIMESEQUAL | DIVIDEEQUAL | MODEQUAL | PLUSEQUAL | MINUSEQUAL | OREQUAL | ANDEQUAL | XOREQUAL | LSHIFTEQUAL | RSHIFTEQUAL + | ATTR_LEFT | ATTR_RIGHT | 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 @@ -3010,6 +3224,18 @@ no_angle_bracket_const_expr: | KW_SIZEOF '(' full_type ')' %prec UNARY { $$ = new CPPExpression(CPPExpression::sizeof_func($3)); +} + | KW_SIZEOF '(' IDENTIFIER ')' %prec UNARY +{ + CPPDeclaration *arg = $3->find_symbol(current_scope, global_scope, current_lexer); + if (arg == (CPPDeclaration *)NULL) { + yyerror("undefined sizeof argument: " + $3->get_fully_scoped_name(), @3); + } else if (arg->get_subtype() == CPPDeclaration::ST_instance) { + CPPInstance *inst = arg->as_instance(); + $$ = new CPPExpression(CPPExpression::sizeof_func(inst->_type)); + } else { + $$ = new CPPExpression(CPPExpression::sizeof_func(arg->as_type())); + } } | KW_SIZEOF ELLIPSIS '(' name ')' %prec UNARY { @@ -3172,6 +3398,16 @@ const_expr: } assert(type != NULL); $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | TYPENAME_IDENTIFIER '{' optional_const_expr_comma '}' +{ + // Aggregate initialization. + CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer); + if (type == NULL) { + yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1); + } + assert(type != NULL); + $$ = new CPPExpression(CPPExpression::aggregate_init_op(type, $3)); } | KW_INT '(' optional_const_expr_comma ')' { @@ -3252,6 +3488,18 @@ const_expr: | KW_SIZEOF '(' full_type ')' %prec UNARY { $$ = new CPPExpression(CPPExpression::sizeof_func($3)); +} + | KW_SIZEOF '(' IDENTIFIER ')' %prec UNARY +{ + CPPDeclaration *arg = $3->find_symbol(current_scope, global_scope, current_lexer); + if (arg == (CPPDeclaration *)NULL) { + yyerror("undefined sizeof argument: " + $3->get_fully_scoped_name(), @3); + } else if (arg->get_subtype() == CPPDeclaration::ST_instance) { + CPPInstance *inst = arg->as_instance(); + $$ = new CPPExpression(CPPExpression::sizeof_func(inst->_type)); + } else { + $$ = new CPPExpression(CPPExpression::sizeof_func(arg->as_type())); + } } | KW_SIZEOF ELLIPSIS '(' name ')' %prec UNARY { @@ -3468,11 +3716,16 @@ const_operand: } | '[' capture_list ']' function_post maybe_trailing_return_type '{' code '}' { - $$ = NULL; + $2->_flags = $4; + $2->_return_type = $5; + $$ = new CPPExpression(CPPExpression::lambda($2)); } | '[' capture_list ']' '(' function_parameter_list ')' function_post maybe_trailing_return_type '{' code '}' { - $$ = NULL; + $2->_parameters = $5; + $2->_flags = $7; + $2->_return_type = $8; + $$ = new CPPExpression(CPPExpression::lambda($2)); } | KW_HAS_VIRTUAL_DESTRUCTOR '(' full_type ')' { @@ -3579,6 +3832,18 @@ formal_const_expr: | KW_SIZEOF '(' full_type ')' %prec UNARY { $$ = new CPPExpression(CPPExpression::sizeof_func($3)); +} + | KW_SIZEOF '(' IDENTIFIER ')' %prec UNARY +{ + CPPDeclaration *arg = $3->find_symbol(current_scope, global_scope, current_lexer); + if (arg == (CPPDeclaration *)NULL) { + yyerror("undefined sizeof argument: " + $3->get_fully_scoped_name(), @3); + } else if (arg->get_subtype() == CPPDeclaration::ST_instance) { + CPPInstance *inst = arg->as_instance(); + $$ = new CPPExpression(CPPExpression::sizeof_func(inst->_type)); + } else { + $$ = new CPPExpression(CPPExpression::sizeof_func(arg->as_type())); + } } | KW_SIZEOF ELLIPSIS '(' name ')' %prec UNARY { @@ -3794,15 +4059,65 @@ formal_const_operand: /* The contents of the [] list preceding a lambda expression. */ capture_list: empty - | capture - | capture ',' capture_list +{ + $$ = new CPPClosureType(); +} + | '=' +{ + $$ = new CPPClosureType(CPPClosureType::CT_by_value); +} + | '&' +{ + $$ = new CPPClosureType(CPPClosureType::CT_by_reference); +} + | capture maybe_initialize +{ + $$ = new CPPClosureType(); + $1->_initializer = $2; + $$->_captures.push_back(*$1); + delete $1; +} + | capture_list ',' capture maybe_initialize +{ + $$ = $1; + $3->_initializer = $4; + $$->_captures.push_back(*$3); + delete $3; +} ; capture: - '&' - | '=' - | '&' name + '&' name +{ + $$ = new CPPClosureType::Capture; + $$->_name = $2->get_simple_name(); + $$->_type = CPPClosureType::CT_by_reference; +} + | '&' name ELLIPSIS +{ + $$ = new CPPClosureType::Capture; + $$->_name = $2->get_simple_name(); + $$->_type = CPPClosureType::CT_by_reference; +} | name +{ + $$ = new CPPClosureType::Capture; + $$->_name = $1->get_simple_name(); + if ($$->_name == "this") { + $$->_type = CPPClosureType::CT_by_reference; + } else { + $$->_type = CPPClosureType::CT_by_value; + } +} + | '*' name +{ + $$ = new CPPClosureType::Capture; + $$->_name = $2->get_simple_name(); + $$->_type = CPPClosureType::CT_by_value; + if ($$->_name != "this") { + yywarning("only capture name 'this' may be preceded by an asterisk", @2); + } +} ; class_derivation_name: @@ -3813,14 +4128,6 @@ class_derivation_name: type = CPPType::new_type(new CPPTBDType($1)); } $$ = type; -} - | struct_keyword name -{ - CPPType *type = $2->find_type(current_scope, global_scope, true, current_lexer); - if (type == NULL) { - type = CPPType::new_type(new CPPTBDType($2)); - } - $$ = type; } | KW_TYPENAME name { @@ -3897,6 +4204,10 @@ name: | KW_STATIC { $$ = new CPPIdentifier("static", @1); +} + | KW_DEFAULT +{ + $$ = new CPPIdentifier("default", @1); } ; diff --git a/dtool/src/cppparser/cppBisonDefs.h b/dtool/src/cppparser/cppBisonDefs.h index b90c47cc20..1ae1caece8 100644 --- a/dtool/src/cppparser/cppBisonDefs.h +++ b/dtool/src/cppparser/cppBisonDefs.h @@ -23,6 +23,7 @@ #include +#include "cppClosureType.h" #include "cppExtensionType.h" #include "cppFile.h" @@ -42,6 +43,7 @@ class CPPParameterList; class CPPTemplateParameterList; class CPPScope; class CPPIdentifier; +class CPPCaptureType; void parse_cpp(CPPParser *cp); CPPExpression *parse_const_expr(CPPPreprocessor *pp, @@ -81,6 +83,8 @@ public: CPPExtensionType::Type extension_enum; CPPExpression *expr; CPPIdentifier *identifier; + CPPClosureType *closure_type; + CPPClosureType::Capture *capture; } u; }; #define YYSTYPE cppyystype diff --git a/dtool/src/cppparser/cppClosureType.cxx b/dtool/src/cppparser/cppClosureType.cxx new file mode 100755 index 0000000000..30197057a6 --- /dev/null +++ b/dtool/src/cppparser/cppClosureType.cxx @@ -0,0 +1,196 @@ +/** + * 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 cppClosureType.cxx + * @author rdb + * @date 2017-01-14 + */ + +#include "cppClosureType.h" +#include "cppParameterList.h" +#include "cppExpression.h" + +/** + * + */ +CPPClosureType:: +CPPClosureType(CaptureType default_capture) : + CPPFunctionType(NULL, NULL, 0), + _default_capture(default_capture) { +} + +/** + * + */ +CPPClosureType:: +CPPClosureType(const CPPClosureType ©) : + CPPFunctionType(copy), + _captures(copy._captures), + _default_capture(copy._default_capture) +{ +} + +/** + * + */ +void CPPClosureType:: +operator = (const CPPClosureType ©) { + CPPFunctionType::operator = (copy); + _captures = copy._captures; + _default_capture = copy._default_capture; +} + +/** + * Adds a new capture to the beginning of the capture list. + */ +void CPPClosureType:: +add_capture(string name, CaptureType type, CPPExpression *initializer) { + if (type == CT_none) { + if (name == "this") { + type = CT_by_reference; + } else { + type = CT_by_value; + } + } + + Capture capture = {move(name), type, initializer}; + _captures.insert(_captures.begin(), move(capture)); +} + +/** + * Returns true if this declaration is an actual, factual declaration, or + * false if some part of the declaration depends on a template parameter which + * has not yet been instantiated. + */ +bool CPPClosureType:: +is_fully_specified() const { + return CPPFunctionType::is_fully_specified(); +} + +/** + * Returns true if the type is default-constructible. + */ +bool CPPClosureType:: +is_default_constructible() const { + return false; +} + +/** + * Returns true if the type is copy-constructible. + */ +bool CPPClosureType:: +is_copy_constructible() const { + return true; +} + +/** + * Returns true if the type is destructible. + */ +bool CPPClosureType:: +is_destructible() const { + return true; +} + +/** + * + */ +void CPPClosureType:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + out.put('['); + + bool have_capture = false; + switch (_default_capture) { + case CT_none: + break; + case CT_by_reference: + out.put('&'); + have_capture = true; + break; + case CT_by_value: + out.put('='); + have_capture = true; + break; + } + + Captures::const_iterator it; + for (it = _captures.begin(); it != _captures.end(); ++it) { + const Capture &capture = *it; + if (have_capture) { + out << ", "; + } + if (capture._name == "this") { + if (capture._type == CT_by_value) { + out.put('*'); + } + } else { + if (capture._type == CT_by_reference) { + out.put('&'); + } + } + out << capture._name; + + if (capture._initializer != NULL) { + out << " = " << *capture._initializer; + } + + have_capture = true; + } + out.put(']'); + + if (_parameters != NULL) { + out.put('('); + _parameters->output(out, scope, true, -1); + out.put(')'); + } + + if (_flags & F_noexcept) { + out << " noexcept"; + } + + if (_return_type != NULL) { + out << " -> "; + _return_type->output(out, indent_level, scope, false); + } + + out << " {}"; +} + +/** + * + */ +CPPDeclaration::SubType CPPClosureType:: +get_subtype() const { + return ST_closure; +} + +/** + * + */ +CPPClosureType *CPPClosureType:: +as_closure_type() { + return this; +} + +/** + * Called by CPPDeclaration() to determine whether this type is equivalent to + * another type of the same type. + */ +bool CPPClosureType:: +is_equal(const CPPDeclaration *other) const { + return (this == other); +} + + +/** + * Called by CPPDeclaration() to determine whether this type should be ordered + * before another type of the same type, in an arbitrary but fixed ordering. + */ +bool CPPClosureType:: +is_less(const CPPDeclaration *other) const { + return (this < other); +} diff --git a/dtool/src/cppparser/cppClosureType.h b/dtool/src/cppparser/cppClosureType.h new file mode 100755 index 0000000000..9c3533ad4a --- /dev/null +++ b/dtool/src/cppparser/cppClosureType.h @@ -0,0 +1,65 @@ +/** + * 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 cppClosureType.h + * @author rdb + * @date 2017-01-14 + */ + +#ifndef CPPCLOSURETYPE_H +#define CPPCLOSURETYPE_H + +#include "dtoolbase.h" + +#include "cppFunctionType.h" + +/** + * The type of a lambda expression. This is like a function, but with + * additional captures defined. + */ +class CPPClosureType : public CPPFunctionType { +public: + enum CaptureType { + CT_none, + CT_by_reference, + CT_by_value, + }; + + CPPClosureType(CaptureType default_capture = CT_none); + CPPClosureType(const CPPClosureType ©); + void operator = (const CPPClosureType ©); + + struct Capture { + string _name; + CaptureType _type; + CPPExpression *_initializer; + }; + typedef vector Captures; + Captures _captures; + + CaptureType _default_capture; + + void add_capture(string name, CaptureType type, CPPExpression *initializer = NULL); + + virtual bool is_fully_specified() const; + + virtual bool is_default_constructible() const; + virtual bool is_copy_constructible() const; + virtual bool is_destructible() const; + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + virtual CPPClosureType *as_closure_type(); + +protected: + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; +}; + +#endif diff --git a/dtool/src/cppparser/cppDeclaration.cxx b/dtool/src/cppparser/cppDeclaration.cxx index 76f72c43e0..3d2e5e68f5 100644 --- a/dtool/src/cppparser/cppDeclaration.cxx +++ b/dtool/src/cppparser/cppDeclaration.cxx @@ -311,6 +311,14 @@ as_make_seq() { return (CPPMakeSeq *)NULL; } +/** + * + */ +CPPClosureType *CPPDeclaration:: +as_closure_type() { + return (CPPClosureType *)NULL; +} + /** * Called by CPPDeclaration to determine whether this type is equivalent to * another type of the same type. diff --git a/dtool/src/cppparser/cppDeclaration.h b/dtool/src/cppparser/cppDeclaration.h index a06baa3123..8acaf6e50c 100644 --- a/dtool/src/cppparser/cppDeclaration.h +++ b/dtool/src/cppparser/cppDeclaration.h @@ -48,6 +48,7 @@ class CPPEnumType; class CPPTypeProxy; class CPPMakeProperty; class CPPMakeSeq; +class CPPClosureType; class CPPClassTemplateParameter; class CPPTBDType; class CPPScope; @@ -85,6 +86,7 @@ public: ST_tbd, ST_type_proxy, ST_typedef, + ST_closure, }; CPPDeclaration(const CPPFile &file); @@ -140,6 +142,7 @@ public: virtual CPPTypeProxy *as_type_proxy(); virtual CPPMakeProperty *as_make_property(); virtual CPPMakeSeq *as_make_seq(); + virtual CPPClosureType *as_closure_type(); inline const CPPInstance *as_instance() const { return ((CPPDeclaration *)this)->as_instance(); @@ -207,6 +210,9 @@ public: inline const CPPMakeSeq *as_make_seq() const { return ((CPPDeclaration *)this)->as_make_seq(); } + inline const CPPClosureType *as_closure_type() const { + return ((CPPDeclaration *)this)->as_closure_type(); + } CPPVisibility _vis; CPPTemplateScope *_template_scope; diff --git a/dtool/src/cppparser/cppExpression.cxx b/dtool/src/cppparser/cppExpression.cxx index f185514de4..adbbaea14e 100644 --- a/dtool/src/cppparser/cppExpression.cxx +++ b/dtool/src/cppparser/cppExpression.cxx @@ -24,6 +24,7 @@ #include "cppInstance.h" #include "cppFunctionGroup.h" #include "cppFunctionType.h" +#include "cppClosureType.h" #include "cppStructType.h" #include "cppBison.h" #include "pdtoa.h" @@ -256,13 +257,12 @@ CPPExpression(CPPIdentifier *ident, CPPScope *current_scope, _u._variable = inst; return; } - // Actually, we can't scope function groups. - /*CPPFunctionGroup *fgroup = decl->as_function_group(); + CPPFunctionGroup *fgroup = decl->as_function_group(); if (fgroup != NULL) { _type = T_function; _u._fgroup = fgroup; return; - }*/ + } } _type = T_unknown_ident; @@ -346,6 +346,22 @@ construct_op(CPPType *type, CPPExpression *op1) { return expr; } +/** + * Creates an expression that represents an aggregate initialization. + */ +CPPExpression CPPExpression:: +aggregate_init_op(CPPType *type, CPPExpression *op1) { + CPPExpression expr(0); + if (op1 == NULL) { + expr._type = T_empty_aggregate_init; + } else { + expr._type = T_aggregate_init; + } + expr._u._typecast._to = type; + expr._u._typecast._op1 = op1; + return expr; +} + /** * Creates an expression that represents a use of the new operator. */ @@ -438,6 +454,17 @@ alignof_func(CPPType *type) { return expr; } +/** + * + */ +CPPExpression CPPExpression:: +lambda(CPPClosureType *type) { + CPPExpression expr(0); + expr._type = T_lambda; + expr._u._closure_type = type; + return expr; +} + /** * */ @@ -594,6 +621,8 @@ evaluate() const { case T_construct: case T_default_construct: + case T_aggregate_init: + case T_empty_aggregate_init: case T_new: case T_default_new: case T_sizeof: @@ -1017,6 +1046,8 @@ determine_type() const { case T_reinterpret_cast: case T_construct: case T_default_construct: + case T_aggregate_init: + case T_empty_aggregate_init: return _u._typecast._to; case T_new: @@ -1135,10 +1166,28 @@ determine_type() const { case 'f': // Function evaluation if (t1 != NULL) { + // Easy case, function with only a single overload. CPPFunctionType *ftype = t1->as_function_type(); if (ftype != (CPPFunctionType *)NULL) { return ftype->_return_type; } + } else if (_u._op._op1->_type == T_function) { + CPPFunctionGroup *fgroup = _u._op._op1->_u._fgroup; + if (_u._op._op2 == NULL) { + // If we are passing no args, look for an overload that has takes no + // args. + for (auto it = fgroup->_instances.begin(); it != fgroup->_instances.end(); ++it) { + CPPInstance *inst = *it; + if (inst != NULL && inst->_type != NULL) { + CPPFunctionType *type = inst->_type->as_function_type(); + if (type != NULL && type->accepts_num_parameters(0)) { + return type->_return_type; + } + } + } + } else { + //TODO + } } return NULL; @@ -1169,6 +1218,9 @@ determine_type() const { case T_type_trait: return bool_type; + case T_lambda: + return _u._closure_type; + default: cerr << "**invalid operand**\n"; abort(); @@ -1215,11 +1267,13 @@ is_fully_specified() const { case T_const_cast: case T_reinterpret_cast: case T_construct: + case T_aggregate_init: case T_new: return (_u._typecast._to->is_fully_specified() && _u._typecast._op1->is_fully_specified()); case T_default_construct: + case T_empty_aggregate_init: case T_default_new: case T_sizeof: case T_alignof: @@ -1259,6 +1313,9 @@ is_fully_specified() const { case T_type_trait: return _u._type_trait._type->is_fully_specified(); + case T_lambda: + return _u._closure_type->is_fully_specified(); + default: return true; } @@ -1342,6 +1399,7 @@ substitute_decl(CPPDeclaration::SubstDecl &subst, case T_const_cast: case T_reinterpret_cast: case T_construct: + case T_aggregate_init: case T_new: rep->_u._typecast._op1 = _u._typecast._op1->substitute_decl(subst, current_scope, global_scope) @@ -1350,6 +1408,7 @@ substitute_decl(CPPDeclaration::SubstDecl &subst, // fall through case T_default_construct: + case T_empty_aggregate_init: case T_default_new: case T_sizeof: case T_alignof: @@ -1444,6 +1503,8 @@ is_tbd() const { case T_const_cast: case T_reinterpret_cast: case T_construct: + case T_aggregate_init: + case T_empty_aggregate_init: case T_new: case T_default_construct: case T_default_new: @@ -1478,6 +1539,9 @@ is_tbd() const { case T_type_trait: return _u._type_trait._type->is_tbd(); + case T_lambda: + return _u._closure_type->is_tbd(); + default: return false; } @@ -1558,6 +1622,10 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const { out << "\\\""; break; + case '\\': + out << "\\\\"; + break; + default: if (isprint(*si)) { out << *si; @@ -1590,7 +1658,12 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const { break; case T_function: - out << _u._fgroup->_name; + // Pick any instance; they all have the same name anyway. + if (!_u._fgroup->_instances.empty() && _u._fgroup->_instances[0]->_ident != NULL) { + _u._fgroup->_instances[0]->_ident->output(out, scope); + } else { + out << _u._fgroup->_name; + } break; case T_unknown_ident: @@ -1649,6 +1722,18 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const { out << "()"; break; + case T_aggregate_init: + _u._typecast._to->output(out, indent_level, scope, false); + out << "{"; + _u._typecast._op1->output(out, indent_level, scope, false); + out << "}"; + break; + + case T_empty_aggregate_init: + _u._typecast._to->output(out, indent_level, scope, false); + out << "{}"; + break; + case T_new: out << "(new "; _u._typecast._to->output(out, indent_level, scope, false); @@ -1946,6 +2031,10 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const { out << ')'; break; + case T_lambda: + _u._closure_type->output(out, indent_level, scope, false); + break; + default: out << "(** invalid operand type " << (int)_type << " **)"; } @@ -2066,11 +2155,13 @@ is_equal(const CPPDeclaration *other) const { case T_const_cast: case T_reinterpret_cast: case T_construct: + case T_aggregate_init: case T_new: return _u._typecast._to == ot->_u._typecast._to && *_u._typecast._op1 == *ot->_u._typecast._op1; case T_default_construct: + case T_empty_aggregate_init: case T_default_new: case T_sizeof: case T_alignof: @@ -2105,6 +2196,9 @@ is_equal(const CPPDeclaration *other) const { return _u._type_trait._trait == ot->_u._type_trait._trait && _u._type_trait._type == ot->_u._type_trait._type; + case T_lambda: + return _u._closure_type == ot->_u._closure_type; + default: cerr << "(** invalid operand type " << (int)_type << " **)"; } @@ -2161,6 +2255,7 @@ is_less(const CPPDeclaration *other) const { case T_const_cast: case T_reinterpret_cast: case T_construct: + case T_aggregate_init: case T_new: if (_u._typecast._to != ot->_u._typecast._to) { return _u._typecast._to < ot->_u._typecast._to; @@ -2168,6 +2263,7 @@ is_less(const CPPDeclaration *other) const { return *_u._typecast._op1 < *ot->_u._typecast._op1; case T_default_construct: + case T_empty_aggregate_init: case T_default_new: case T_sizeof: case T_alignof: @@ -2212,6 +2308,9 @@ is_less(const CPPDeclaration *other) const { } return *_u._type_trait._type < *ot->_u._type_trait._type; + case T_lambda: + return _u._closure_type < ot->_u._closure_type; + default: cerr << "(** invalid operand type " << (int)_type << " **)"; } diff --git a/dtool/src/cppparser/cppExpression.h b/dtool/src/cppparser/cppExpression.h index c3483ef3e9..3178d4d178 100644 --- a/dtool/src/cppparser/cppExpression.h +++ b/dtool/src/cppparser/cppExpression.h @@ -48,6 +48,8 @@ public: T_reinterpret_cast, T_construct, T_default_construct, + T_aggregate_init, + T_empty_aggregate_init, T_new, T_default_new, T_sizeof, @@ -61,6 +63,7 @@ public: T_typeid_type, T_typeid_expr, T_type_trait, + T_lambda, // These are used when parsing =default and =delete methods. T_default, @@ -80,6 +83,7 @@ public: static CPPExpression typecast_op(CPPType *type, CPPExpression *op1, Type cast_type = T_typecast); static CPPExpression construct_op(CPPType *type, CPPExpression *op1); + static CPPExpression aggregate_init_op(CPPType *type, CPPExpression *op1); static CPPExpression new_op(CPPType *type, CPPExpression *op1 = NULL); static CPPExpression typeid_op(CPPType *type, CPPType *std_type_info); static CPPExpression typeid_op(CPPExpression *op1, CPPType *std_type_info); @@ -87,6 +91,7 @@ public: static CPPExpression sizeof_func(CPPType *type); static CPPExpression sizeof_ellipsis_func(CPPIdentifier *ident); static CPPExpression alignof_func(CPPType *type); + static CPPExpression lambda(CPPClosureType *type); static CPPExpression literal(unsigned long long value, CPPInstance *lit_op); static CPPExpression literal(long double value, CPPInstance *lit_op); @@ -150,6 +155,7 @@ public: CPPInstance *_variable; CPPFunctionGroup *_fgroup; CPPIdentifier *_ident; + CPPClosureType *_closure_type; struct { union { CPPType *_type; diff --git a/dtool/src/cppparser/cppExtensionType.cxx b/dtool/src/cppparser/cppExtensionType.cxx index 8a0676883d..eba135eb64 100644 --- a/dtool/src/cppparser/cppExtensionType.cxx +++ b/dtool/src/cppparser/cppExtensionType.cxx @@ -131,6 +131,14 @@ is_copy_constructible() const { return (_type == T_enum || _type == T_enum_class || _type == T_enum_struct); } +/** + * Returns true if the type is copy-assignable. + */ +bool CPPExtensionType:: +is_copy_assignable() const { + return (_type == T_enum || _type == T_enum_class || _type == T_enum_struct); +} + /** * */ diff --git a/dtool/src/cppparser/cppExtensionType.h b/dtool/src/cppparser/cppExtensionType.h index 7f7bf15abf..dca9cf8cb3 100644 --- a/dtool/src/cppparser/cppExtensionType.h +++ b/dtool/src/cppparser/cppExtensionType.h @@ -52,6 +52,7 @@ public: virtual bool is_constructible(const CPPType *type) const; virtual bool is_default_constructible() const; virtual bool is_copy_constructible() const; + virtual bool is_copy_assignable() const; virtual CPPDeclaration *substitute_decl(SubstDecl &subst, CPPScope *current_scope, diff --git a/dtool/src/cppparser/cppFunctionType.cxx b/dtool/src/cppparser/cppFunctionType.cxx index 1872ea11f5..38eb3d98ba 100644 --- a/dtool/src/cppparser/cppFunctionType.cxx +++ b/dtool/src/cppparser/cppFunctionType.cxx @@ -31,7 +31,8 @@ CPPFunctionType(CPPType *return_type, CPPParameterList *parameters, // If the parameter list contains just the token "void", it means no // parameters. - if (_parameters->_parameters.size() == 1 && + if (_parameters != NULL && + _parameters->_parameters.size() == 1 && _parameters->_parameters.front()->_type->as_simple_type() != NULL && _parameters->_parameters.front()->_type->as_simple_type()->_type == CPPSimpleType::T_void && @@ -65,6 +66,31 @@ operator = (const CPPFunctionType ©) { _class_owner = copy._class_owner; } +/** + * Returns true if the function accepts the given number of parameters. + */ +bool CPPFunctionType:: +accepts_num_parameters(int num_parameters) { + if (_parameters == NULL) { + return (num_parameters == 0); + } + size_t actual_num_parameters = _parameters->_parameters.size(); + // If we passed too many parameters, it must have an ellipsis. + if (num_parameters > actual_num_parameters) { + return _parameters->_includes_ellipsis; + } + + // Make sure all superfluous parameters have a default value. + for (size_t i = num_parameters; i < actual_num_parameters; ++i) { + CPPInstance *param = _parameters->_parameters[i]; + if (param->_initializer == NULL) { + return false; + } + } + + return true; +} + /** * Returns true if this declaration is an actual, factual declaration, or * false if some part of the declaration depends on a template parameter which @@ -95,8 +121,10 @@ substitute_decl(CPPDeclaration::SubstDecl &subst, ->as_type(); } - rep->_parameters = - _parameters->substitute_decl(subst, current_scope, global_scope); + if (_parameters != NULL) { + rep->_parameters = + _parameters->substitute_decl(subst, current_scope, global_scope); + } if (rep->_return_type == _return_type && rep->_parameters == _parameters) { @@ -117,8 +145,12 @@ substitute_decl(CPPDeclaration::SubstDecl &subst, CPPType *CPPFunctionType:: resolve_type(CPPScope *current_scope, CPPScope *global_scope) { CPPType *rtype = _return_type->resolve_type(current_scope, global_scope); - CPPParameterList *params = - _parameters->resolve_type(current_scope, global_scope); + CPPParameterList *params; + if (_parameters == NULL) { + params = NULL; + } else { + params = _parameters->resolve_type(current_scope, global_scope); + } if (rtype != _return_type || params != _parameters) { CPPFunctionType *rep = new CPPFunctionType(*this); @@ -139,7 +171,7 @@ is_tbd() const { if (_return_type->is_tbd()) { return true; } - return _parameters->is_tbd(); + return _parameters == NULL || _parameters->is_tbd(); } /** @@ -294,6 +326,10 @@ get_num_default_parameters() const { // The trick is just to count, beginning from the end and working towards // the front, the number of parameters that have some initializer. + if (_parameters == NULL) { + return 0; + } + const CPPParameterList::Parameters ¶ms = _parameters->_parameters; CPPParameterList::Parameters::const_reverse_iterator pi; int count = 0; @@ -362,7 +398,11 @@ is_equal(const CPPDeclaration *other) const { if (_flags != ot->_flags) { return false; } - if (*_parameters != *ot->_parameters) { + if (_parameters == ot->_parameters) { + return true; + } + if (_parameters == NULL || ot->_parameters == NULL || + *_parameters != *ot->_parameters) { return false; } return true; @@ -384,6 +424,11 @@ is_less(const CPPDeclaration *other) const { if (_flags != ot->_flags) { return _flags < ot->_flags; } - + if (_parameters == ot->_parameters) { + return 0; + } + if (_parameters == NULL || ot->_parameters == NULL) { + return _parameters < ot->_parameters; + } return *_parameters < *ot->_parameters; } diff --git a/dtool/src/cppparser/cppFunctionType.h b/dtool/src/cppparser/cppFunctionType.h index 1e44681f13..b6bdff7ac6 100644 --- a/dtool/src/cppparser/cppFunctionType.h +++ b/dtool/src/cppparser/cppFunctionType.h @@ -43,6 +43,8 @@ public: F_volatile_method = 0x4000, F_lvalue_method = 0x8000, F_rvalue_method = 0x10000, + F_copy_assignment_operator = 0x20000, + F_move_assignment_operator = 0x40000, }; CPPFunctionType(CPPType *return_type, CPPParameterList *parameters, @@ -50,6 +52,8 @@ public: CPPFunctionType(const CPPFunctionType ©); void operator = (const CPPFunctionType ©); + bool accepts_num_parameters(int num_parameters); + CPPType *_return_type; CPPParameterList *_parameters; int _flags; diff --git a/dtool/src/cppparser/cppInstance.cxx b/dtool/src/cppparser/cppInstance.cxx index 86768af511..0bfdcabde1 100644 --- a/dtool/src/cppparser/cppInstance.cxx +++ b/dtool/src/cppparser/cppInstance.cxx @@ -328,8 +328,8 @@ get_fully_scoped_name() const { /** * If this is a function type instance, checks whether the function name - * matches the class name (or ~name), and if so, flags it as a constructor (or - * destructor). + * matches the class name (or ~name), and if so, flags it as a constructor, + * destructor or assignment operator */ void CPPInstance:: check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) { @@ -344,13 +344,16 @@ check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) { string class_name = scope->get_local_name(); if (!method_name.empty() && !class_name.empty()) { - if (method_name == class_name) { + // Check either a constructor or assignment operator. + if (method_name == class_name || method_name == "operator =") { CPPType *void_type = CPPType::new_type (new CPPSimpleType(CPPSimpleType::T_void)); - int flags = func->_flags | CPPFunctionType::F_constructor; + int flags = func->_flags; + if (method_name == class_name) { + flags |= CPPFunctionType::F_constructor; + } - // Check if it might be a copy or move constructor. CPPParameterList *params = func->_parameters; if (params->_parameters.size() == 1 && !params->_includes_ellipsis) { CPPType *param_type = params->_parameters[0]->_type; @@ -360,10 +363,18 @@ check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) { param_type = ref_type->_pointing_at->remove_cv(); if (class_name == param_type->get_simple_name()) { - if (ref_type->_value_category == CPPReferenceType::VC_rvalue) { - flags |= CPPFunctionType::F_move_constructor; + if (flags & CPPFunctionType::F_constructor) { + if (ref_type->_value_category == CPPReferenceType::VC_rvalue) { + flags |= CPPFunctionType::F_move_constructor; + } else { + flags |= CPPFunctionType::F_copy_constructor; + } } else { - flags |= CPPFunctionType::F_copy_constructor; + if (ref_type->_value_category == CPPReferenceType::VC_rvalue) { + flags |= CPPFunctionType::F_move_assignment_operator; + } else { + flags |= CPPFunctionType::F_copy_assignment_operator; + } } } } diff --git a/dtool/src/cppparser/cppMakeProperty.cxx b/dtool/src/cppparser/cppMakeProperty.cxx index c65fcf93b6..02e4f21c60 100644 --- a/dtool/src/cppparser/cppMakeProperty.cxx +++ b/dtool/src/cppparser/cppMakeProperty.cxx @@ -18,37 +18,19 @@ * */ CPPMakeProperty:: -CPPMakeProperty(CPPIdentifier *ident, - CPPFunctionGroup *getter, CPPFunctionGroup *setter, +CPPMakeProperty(CPPIdentifier *ident, Type type, CPPScope *current_scope, const CPPFile &file) : CPPDeclaration(file), _ident(ident), - _length_function(NULL), - _has_function(NULL), - _get_function(getter), - _set_function(setter), - _clear_function(NULL), - _del_function(NULL) -{ - _ident->_native_scope = current_scope; -} - -/** - * - */ -CPPMakeProperty:: -CPPMakeProperty(CPPIdentifier *ident, - CPPFunctionGroup *hasser, CPPFunctionGroup *getter, - CPPFunctionGroup *setter, CPPFunctionGroup *clearer, - CPPScope *current_scope, const CPPFile &file) : - CPPDeclaration(file), - _ident(ident), - _length_function(NULL), - _has_function(hasser), - _get_function(getter), - _set_function(setter), - _clear_function(clearer), - _del_function(NULL) + _type(type), + _length_function(nullptr), + _has_function(nullptr), + _get_function(nullptr), + _set_function(nullptr), + _clear_function(nullptr), + _del_function(nullptr), + _insert_function(nullptr), + _get_key_function(nullptr) { _ident->_native_scope = current_scope; } @@ -116,6 +98,10 @@ output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { out << ", " << _del_function->_name; } + if (_insert_function != NULL) { + out << ", " << _insert_function->_name; + } + out << ");"; } diff --git a/dtool/src/cppparser/cppMakeProperty.h b/dtool/src/cppparser/cppMakeProperty.h index 44a1a5671a..cd364b30ec 100644 --- a/dtool/src/cppparser/cppMakeProperty.h +++ b/dtool/src/cppparser/cppMakeProperty.h @@ -23,16 +23,72 @@ * This is a MAKE_PROPERTY() declaration appearing within a class body. It * means to generate a property within Python, replacing (for instance) * get_something()/set_something() with a synthetic 'something' attribute. + * + * This is an example of a simple property (MAKE_PROPERTY is defined as + * the built-in __make_property): + * @@code + * Thing get_thing() const; + * void set_thing(const Thing &); + * + * MAKE_PROPERTY(thing, get_thing, set_thing); + * @@endcode + * The setter may be omitted to make the property read-only. + * + * There is also a secondary macro that allows the property to be set to a + * cleared state using separate clear functions. In the scripting language, + * this would be represented by a "null" value, or an "optional" construct in + * languages that have no notion of a null value. + * + * @@code + * bool has_thing() const; + * Thing get_thing() const; + * void set_thing(const Thing &); + * void clear_thing(); + * MAKE_PROPERTY2(thing, has_thing, get_thing, set_thing, clear_thing); + * @@endcode + * As with MAKE_PROPERTY, both the setter and clearer can be omitted to create + * a read-only property. + * + * Thirdly, there is a variant called MAKE_SEQ_PROPERTY. It takes a length + * function as argument and the getter and setter take an index as first + * argument: + * @@code + * size_t get_num_things() const; + * Thing &get_thing(size_t i) const; + * void set_thing(size_t i, Thing value) const; + * void remove_thing(size_t i) const; + * + * MAKE_SEQ_PROPERTY(get_num_things, get_thing, set_thing, remove_thing); + * @@endcode + * + * Lastly, there is the possibility to have properties with key/value + * associations, often called a "map" or "dictionary" in scripting languages: + * @@code + * bool has_thing(string key) const; + * Thing &get_thing(string key) const; + * void set_thing(string key, Thing value) const; + * void clear_thing(string key) const; + * + * MAKE_MAP_PROPERTY(things, has_thing, get_thing, set_thing, clear_thing); + * @@endcode + * You may also replace the "has" function with a "find" function that returns + * an index. If the returned index is negative (or in the case of an unsigned + * integer, the maximum value), the item is assumed not to be present in the + * mapping. + * + * It is also possible to use both MAKE_SEQ_PROPERTY and MAKE_MAP_PROPERTY on + * the same property name. This implies that this property has both a + * sequence and mapping interface. */ class CPPMakeProperty : public CPPDeclaration { public: - CPPMakeProperty(CPPIdentifier *ident, - CPPFunctionGroup *getter, CPPFunctionGroup *setter, - CPPScope *current_scope, const CPPFile &file); + enum Type { + T_normal = 0x0, + T_sequence = 0x1, + T_mapping = 0x2, + }; - CPPMakeProperty(CPPIdentifier *ident, - CPPFunctionGroup *hasser, CPPFunctionGroup *getter, - CPPFunctionGroup *setter, CPPFunctionGroup *clearer, + CPPMakeProperty(CPPIdentifier *ident, Type type, CPPScope *current_scope, const CPPFile &file); virtual string get_simple_name() const; @@ -46,14 +102,15 @@ public: virtual CPPMakeProperty *as_make_property(); CPPIdentifier *_ident; - // If length_function is not NULL, this is actually a sequence property, - // and the other functions take an additional index argument. + Type _type; CPPFunctionGroup *_length_function; CPPFunctionGroup *_has_function; CPPFunctionGroup *_get_function; CPPFunctionGroup *_set_function; CPPFunctionGroup *_clear_function; CPPFunctionGroup *_del_function; + CPPFunctionGroup *_insert_function; + CPPFunctionGroup *_get_key_function; }; #endif diff --git a/dtool/src/cppparser/cppPointerType.cxx b/dtool/src/cppparser/cppPointerType.cxx index 6fac65a6b1..4ae396e22d 100644 --- a/dtool/src/cppparser/cppPointerType.cxx +++ b/dtool/src/cppparser/cppPointerType.cxx @@ -177,6 +177,14 @@ is_copy_constructible() const { return true; } +/** + * Returns true if the type is copy-assignable. + */ +bool CPPPointerType:: +is_copy_assignable() const { + return true; +} + /** * This is a little more forgiving than is_equal(): it returns true if the * types appear to be referring to the same thing, even if they may have diff --git a/dtool/src/cppparser/cppPointerType.h b/dtool/src/cppparser/cppPointerType.h index 41aed4876b..3fce00b2f5 100644 --- a/dtool/src/cppparser/cppPointerType.h +++ b/dtool/src/cppparser/cppPointerType.h @@ -41,6 +41,7 @@ public: virtual bool is_constructible(const CPPType *other) const; virtual bool is_default_constructible() const; virtual bool is_copy_constructible() const; + virtual bool is_copy_assignable() const; virtual bool is_equivalent(const CPPType &other) const; virtual void output(ostream &out, int indent_level, CPPScope *scope, diff --git a/dtool/src/cppparser/cppPreprocessor.cxx b/dtool/src/cppparser/cppPreprocessor.cxx index 83050d3c41..ecaecb98b6 100644 --- a/dtool/src/cppparser/cppPreprocessor.cxx +++ b/dtool/src/cppparser/cppPreprocessor.cxx @@ -209,6 +209,7 @@ CPPPreprocessor() { _state = S_eof; _paren_nesting = 0; _parsing_template_params = false; + _parsing_attribute = false; _unget = '\0'; _last_c = '\0'; _start_of_line = true; @@ -809,6 +810,8 @@ expand_manifests(const string &input_expr, bool expand_undefined, // Here's an identifier. Is it "defined"? if (ident == "defined") { expand_defined_function(expr, q, p); + } else if (expand_undefined && ident == "__has_include") { + expand_has_include_function(expr, q, p, loc); } else { // Is it a manifest? Manifests::const_iterator mi = _manifests.find(ident); @@ -817,6 +820,20 @@ expand_manifests(const string &input_expr, bool expand_undefined, expand_manifest_inline(expr, q, p, manifest); manifest_found = true; + } else if (ident == "__FILE__") { + // Special case: this is a dynamic definition. + string file = string("\"") + loc.file._filename_as_referenced.get_fullpath() + "\""; + expr = expr.substr(0, q) + file + expr.substr(p); + p = q + file.size(); + manifest_found = true; + + } else if (ident == "__LINE__") { + // So is this. + string line = format_string(loc.first_line); + expr = expr.substr(0, q) + line + expr.substr(p); + p = q + line.size(); + manifest_found = true; + } else if (expand_undefined && ident != "true" && ident != "false") { // It is not found. Expand it to 0, but only if we are currently // parsing an #if expression. @@ -970,6 +987,13 @@ internal_get_next_token() { return CPPToken(0, loc); } } + } else if (_parsing_attribute) { + // If we're parsing an attribute, also keep track of the paren nesting. + if (c == '[' || c == '(') { + ++_paren_nesting; + } else if (c == ']' || c == ')') { + --_paren_nesting; + } } // Look for an end-of-line comment, and parse it before we finish this @@ -1074,6 +1098,20 @@ check_digraph(int c) { if (next_c == '=') return MODEQUAL; if (next_c == '>') return '}'; break; + + case '[': + if (next_c == '[' && !_parsing_attribute) { + _parsing_attribute = true; + return ATTR_LEFT; + } + break; + + case ']': + if (next_c == ']' && _parsing_attribute && _paren_nesting == 0) { + _parsing_attribute = false; + return ATTR_RIGHT; + } + break; } return 0; @@ -1486,14 +1524,10 @@ handle_undef_directive(const string &args, const YYLTYPE &loc) { */ void CPPPreprocessor:: handle_ifdef_directive(const string &args, const YYLTYPE &loc) { - Manifests::const_iterator mi = _manifests.find(args); - if (mi != _manifests.end()) { - // The macro is defined. We continue. - return; + if (!is_manifest_defined(args)) { + // The macro is undefined. Skip stuff. + skip_false_if_block(true); } - - // The macro is undefined. Skip stuff. - skip_false_if_block(true); } /** @@ -1501,17 +1535,12 @@ handle_ifdef_directive(const string &args, const YYLTYPE &loc) { */ void CPPPreprocessor:: handle_ifndef_directive(const string &args, const YYLTYPE &loc) { - Manifests::const_iterator mi = _manifests.find(args); - if (mi == _manifests.end()) { - // The macro is undefined. We continue. - return; + if (is_manifest_defined(args)) { + // The macro is defined. Skip stuff. + skip_false_if_block(true); } - - // The macro is defined. Skip stuff. - skip_false_if_block(true); } - /** * */ @@ -1593,6 +1622,8 @@ handle_include_directive(const string &args, const YYLTYPE &loc) { _angle_includes.insert(filename); } } + } else { + warning("Ignoring invalid #include directive", loc); } filename.set_text(); @@ -1600,71 +1631,30 @@ handle_include_directive(const string &args, const YYLTYPE &loc) { // Now look for the filename. If we didn't use angle quotes, look first in // the current directory. - bool found_file = false; CPPFile::Source source = CPPFile::S_none; - if (okflag) { - found_file = false; + if (find_include(filename, angle_quotes, source)) { + _last_c = '\0'; - // Search the current directory. - if (!angle_quotes && !found_file && filename.exists()) { - found_file = true; + // If it was explicitly named on the command-line, mark it S_local. + filename.make_canonical(); + if (_explicit_files.count(filename)) { source = CPPFile::S_local; } - // Search the same directory as the includer. - if (!angle_quotes && !found_file) { - Filename match(get_file()._filename.get_dirname(), filename); - if (match.exists()) { - filename = match; - found_file = true; - source = CPPFile::S_alternate; - } + CPPFile file(filename, filename_as_referenced, source); + + // Don't include it if we included it before and it had #pragma once. + ParsedFiles::const_iterator it = _parsed_files.find(file); + if (it != _parsed_files.end() && it->_pragma_once) { + return; } - // Now search the angle-include-path - if (angle_quotes && !found_file && filename.resolve_filename(_angle_include_path)) { - found_file = true; - source = CPPFile::S_system; - } - - // Now search the quote-include-path - if (!angle_quotes && !found_file) { - for (size_t dir=0; dir<_quote_include_path.get_num_directories(); dir++) { - Filename match(_quote_include_path.get_directory(dir), filename); - if (match.exists()) { - filename = match; - found_file = true; - source = _quote_include_kind[dir]; - } - } - } - - if (!found_file) { - warning("Cannot find " + filename.get_fullpath(), loc); - } else { - _last_c = '\0'; - - // If it was explicitly named on the command-line, mark it S_local. - filename.make_canonical(); - if (_explicit_files.count(filename)) { - source = CPPFile::S_local; - } - - CPPFile file(filename, filename_as_referenced, source); - - // Don't include it if we included it before and it had #pragma once. - ParsedFiles::const_iterator it = _parsed_files.find(file); - if (it != _parsed_files.end() && it->_pragma_once) { - return; - } - - if (!push_file(file)) { - warning("Unable to read " + filename.get_fullpath(), loc); - } + if (!push_file(file)) { + warning("Unable to read " + filename.get_fullpath(), loc); } } else { - warning("Ignoring invalid #include directive", loc); + warning("Cannot find " + filename.get_fullpath(), loc); } } @@ -1779,6 +1769,69 @@ skip_false_if_block(bool consider_elifs) { _save_comments = true; } +/** + * Returns true if the given manifest is defined. + */ +bool CPPPreprocessor:: +is_manifest_defined(const string &manifest_name) { + Manifests::const_iterator mi = _manifests.find(manifest_name); + if (mi != _manifests.end()) { + return true; + } + + if (manifest_name == "__has_include" || + manifest_name == "__FILE__" || + manifest_name == "__LINE__") { + // Special built-in directives that are considered "defined". + return true; + } + + return false; +} + +/** + * Locates the given filename. Changes the first argument to the full path. + */ +bool CPPPreprocessor:: +find_include(Filename &filename, bool angle_quotes, CPPFile::Source &source) { + // Now look for the filename. If we didn't use angle quotes, look first in + // the current directory. + if (!angle_quotes && filename.exists()) { + source = CPPFile::S_local; + return true; + } + + // Search the same directory as the includer. + if (!angle_quotes) { + Filename match(get_file()._filename.get_dirname(), filename); + if (match.exists()) { + filename = match; + source = CPPFile::S_alternate; + return true; + } + } + + // Now search the angle-include-path + if (angle_quotes && filename.resolve_filename(_angle_include_path)) { + source = CPPFile::S_system; + return true; + } + + // Now search the quote-include-path + if (!angle_quotes) { + for (size_t dir = 0; dir < _quote_include_path.get_num_directories(); ++dir) { + Filename match(_quote_include_path.get_directory(dir), filename); + if (match.exists()) { + filename = match; + source = _quote_include_kind[dir]; + return true; + } + } + } + + return false; +} + /** * */ @@ -1839,11 +1892,19 @@ get_identifier(int c) { loc.last_column = get_col_number(); if ((c == '\'' || c == '"') && - (name == "L" || name == "u8" || - name == "u" || name == "U")) { + (name == "L" || name == "u8" || name == "u" || name == "U" || + name == "R" || name == "LR" || name == "u8R" || name == "uR" || name == "UR")) { // This is actually a wide-character or wide-string literal or some such. - // Figure out the correct character type to use. + get(); + string str; + if (name[name.size() - 1] == 'R') { + name.resize(name.size() - 1); + str = scan_raw(c); + } else { + str = scan_quoted(c); + } + // Figure out the correct character type to use. CPPExpression::Type type; if (name == "L") { type = CPPExpression::T_wstring; @@ -1856,8 +1917,6 @@ get_identifier(int c) { } else { type = CPPExpression::T_string; } - get(); - string str = scan_quoted(c); loc.last_line = get_line_number(); loc.last_column = get_col_number(); @@ -1885,6 +1944,14 @@ get_identifier(int c) { if (mi != _manifests.end() && !should_ignore_manifest((*mi).second)) { return expand_manifest((*mi).second); } + if (name == "__FILE__") { + return get_literal(SIMPLE_STRING, loc, loc.file._filename_as_referenced); + } + if (name == "__LINE__") { + YYSTYPE result; + result.u.integer = loc.first_line; + return CPPToken(INTEGER, loc, "", result); + } // Check for keywords. int kw = check_keyword(name); @@ -2227,9 +2294,7 @@ expand_defined_function(string &expr, size_t q, size_t &p) { vector_string args; extract_manifest_args_inline("defined", 1, -1, args, expr, p); if (args.size() >= 1) { - const string &manifest_name = args[0]; - Manifests::const_iterator mi = _manifests.find(manifest_name); - if (mi != _manifests.end()) { + if (is_manifest_defined(args[0])) { // The macro is defined; the result is "1". result = "1"; } else { @@ -2242,6 +2307,70 @@ expand_defined_function(string &expr, size_t q, size_t &p) { p = q + result.size(); } +/** + * Expands the __has_include(manifest) function to either 1 or 0, depending on + * whether the include file exists. + */ +void CPPPreprocessor:: +expand_has_include_function(string &expr, size_t q, size_t &p, YYLTYPE loc) { + bool found_file = false; + + // Skip whitespace till paren. + while (p < expr.size() && isspace(expr[p])) { + p++; + } + size_t args_begin = p + 1; + + vector_string args; + extract_manifest_args_inline("__has_include", 1, -1, args, expr, p); + + if (!args.empty() && args[0].size() >= 2) { + Filename filename; + bool angle_quotes = false; + + string inc = args[0]; + + // Just to play things safe, since our manifest-expansion logic might not + // filter out quotes and angle brackets properly, we'll only expand + // manifests if we don't begin with a quote or bracket. + if (!inc.empty() && (inc[0] != '"' && inc[0] != '<')) { + inc = expand_manifests(inc, false, loc); + } + + if (inc[0] == '"' && inc[inc.size() - 1] == '"') { + filename = inc.substr(1, inc.size() - 2); + } else if (inc[0] == '<' && inc[inc.size() - 1] == '>') { + filename = inc.substr(1, inc.size() - 2); + if (!_noangles) { + // If _noangles is true, we don't make a distinction between angle + // brackets and quote marks--all #inc statements are treated the + // same, as if they used quote marks. + angle_quotes = true; + } + } else { + loc.last_column += loc.first_column + p - 2; + loc.first_column += args_begin; + warning("invalid argument for __has_include() directive", loc); + expr = expr.substr(0, q) + "0" + expr.substr(p); + p = q + 1; + return; + } + + filename.set_text(); + + CPPFile::Source source = CPPFile::S_none; + found_file = find_include(filename, angle_quotes, source); + } else { + loc.last_column += loc.first_column + p - 2; + loc.first_column += args_begin; + warning("invalid argument for __has_include() directive", loc); + } + + string result = found_file ? "1" : "0"; + expr = expr.substr(0, q) + result + expr.substr(p); + p = q + result.size(); +} + /** * */ @@ -2512,6 +2641,7 @@ check_keyword(const string &name) { if (name == "__is_trivial") return KW_IS_TRIVIAL; if (name == "__is_union") return KW_IS_UNION; if (name == "long") return KW_LONG; + if (name == "__make_map_keys_seq") return KW_MAKE_MAP_KEYS_SEQ; if (name == "__make_map_property") return KW_MAKE_MAP_PROPERTY; if (name == "__make_property") return KW_MAKE_PROPERTY; if (name == "__make_property2") return KW_MAKE_PROPERTY2; @@ -2671,6 +2801,43 @@ scan_quoted(int c) { return str; } +/** + * Parses a C++11 raw string. + */ +string CPPPreprocessor:: +scan_raw(int c) { + int quote_mark = c; + + string delimiter = ")"; + + string str; + c = get(); + while (c != EOF && c != '(') { + delimiter += c; + c = get(); + } + + // OK, now start parsing the string, until we see the delimiter again. + c = get(); + while (c != EOF) { + if (c == quote_mark) { + // We encountered a quote mark - did the last part of the string end + // with the given delimiter? If so, we've reached the end. + if (str.compare(str.size() - delimiter.size(), delimiter.size(), delimiter) == 0) { + str.resize(str.size() - delimiter.size()); + break; + } + } + str += c; + c = get(); + } + + if (c != quote_mark) { + warning("Unclosed string"); + } + return str; +} + /** * Returns true if the manifest is one that is being ignored right now * (presumably because we are presently expanding it). diff --git a/dtool/src/cppparser/cppPreprocessor.h b/dtool/src/cppparser/cppPreprocessor.h index e375b259dd..02f3715339 100644 --- a/dtool/src/cppparser/cppPreprocessor.h +++ b/dtool/src/cppparser/cppPreprocessor.h @@ -143,6 +143,8 @@ private: void handle_error_directive(const string &args, const YYLTYPE &loc); void skip_false_if_block(bool consider_elifs); + bool is_manifest_defined(const string &manifest_name); + bool find_include(Filename &filename, bool angle_quotes, CPPFile::Source &source); CPPToken get_quoted_char(int c); CPPToken get_quoted_string(int c); @@ -153,6 +155,7 @@ private: void extract_manifest_args(const string &name, int num_args, int va_arg, vector_string &args); void expand_defined_function(string &expr, size_t q, size_t &p); + void expand_has_include_function(string &expr, size_t q, size_t &p, YYLTYPE loc); void expand_manifest_inline(string &expr, size_t q, size_t &p, const CPPManifest *manifest); void extract_manifest_args_inline(const string &name, int num_args, @@ -163,6 +166,7 @@ private: static int check_keyword(const string &name); int scan_escape_sequence(int c); string scan_quoted(int c); + string scan_raw(int c); bool should_ignore_manifest(const CPPManifest *manifest) const; bool should_ignore_preprocessor() const; @@ -209,6 +213,7 @@ private: State _state; int _paren_nesting; bool _parsing_template_params; + bool _parsing_attribute; bool _start_of_line; int _unget; diff --git a/dtool/src/cppparser/cppSimpleType.cxx b/dtool/src/cppparser/cppSimpleType.cxx index 82d7735bdc..8107e7ee24 100644 --- a/dtool/src/cppparser/cppSimpleType.cxx +++ b/dtool/src/cppparser/cppSimpleType.cxx @@ -103,6 +103,14 @@ is_copy_constructible() const { return (_type != T_void); } +/** + * Returns true if the type is copy-assignable. + */ +bool CPPSimpleType:: +is_copy_assignable() const { + return (_type != T_void); +} + /** * Returns true if the type is destructible. */ diff --git a/dtool/src/cppparser/cppSimpleType.h b/dtool/src/cppparser/cppSimpleType.h index 8adb254a1b..850353e9eb 100644 --- a/dtool/src/cppparser/cppSimpleType.h +++ b/dtool/src/cppparser/cppSimpleType.h @@ -75,6 +75,7 @@ public: virtual bool is_constructible(const CPPType *type) const; virtual bool is_default_constructible() const; virtual bool is_copy_constructible() const; + virtual bool is_copy_assignable() const; virtual bool is_destructible() const; virtual bool is_parameter_expr() const; diff --git a/dtool/src/cppparser/cppStructType.cxx b/dtool/src/cppparser/cppStructType.cxx index afb03dd463..fda8feaa2b 100644 --- a/dtool/src/cppparser/cppStructType.cxx +++ b/dtool/src/cppparser/cppStructType.cxx @@ -435,6 +435,17 @@ is_copy_constructible() const { return is_copy_constructible(V_public); } +/** + * Returns true if the type is copy-assignable. + */ +bool CPPStructType:: +is_copy_assignable() const { + if (is_abstract()) { + return false; + } + return is_copy_assignable(V_public); +} + /** * Returns true if the type is destructible. */ @@ -606,6 +617,97 @@ is_move_constructible(CPPVisibility min_vis) const { return is_copy_constructible(min_vis); } +/** + * Returns true if the type is copy-assignable, without checking whether the + * class is abstract. + */ +bool CPPStructType:: +is_copy_assignable(CPPVisibility min_vis) const { + CPPInstance *assignment_operator = get_copy_assignment_operator(); + if (assignment_operator != (CPPInstance *)NULL) { + // It has a copy assignment operator. + if (assignment_operator->_vis > min_vis) { + // Inaccessible copy assignment operator. + return false; + } + + if (assignment_operator->_storage_class & CPPInstance::SC_deleted) { + // Deleted copy assignment operator. + return false; + } + + // NB: if it's defaulted, it may still be deleted. + if ((assignment_operator->_storage_class & CPPInstance::SC_defaulted) == 0) { + return true; + } + } + + // Implicit copy assignment operator. Check if the implicit or defaulted + // copy assignment operator is deleted. + if (!assignment_operator && (get_move_constructor() || get_move_assignment_operator())) { + // It's not explicitly defaulted, and there is a move constructor or move + // assignment operator, so the implicitly-declared one is deleted. + return false; + } + + Derivation::const_iterator di; + for (di = _derivation.begin(); di != _derivation.end(); ++di) { + CPPStructType *base = (*di)._base->as_struct_type(); + if (base != NULL) { + if (!base->is_copy_assignable(V_protected)) { + return false; + } + } + } + + // Make sure all members are assignable. + CPPScope::Variables::const_iterator vi; + for (vi = _scope->_variables.begin(); vi != _scope->_variables.end(); ++vi) { + CPPInstance *instance = (*vi).second; + assert(instance != NULL); + + if (instance->_storage_class & CPPInstance::SC_static) { + // Static members don't count. + continue; + } + + if (!instance->_type->is_copy_assignable()) { + // Const or reference member, can't do it. + return false; + } + } + + return true; +} + +/** + * Returns true if the type is move-assignable. + */ +bool CPPStructType:: +is_move_assignable(CPPVisibility min_vis) const { + CPPInstance *assignment_operator = get_move_assignment_operator(); + if (assignment_operator != (CPPInstance *)NULL) { + // It has a user-declared move assignment_operator. + if (assignment_operator->_vis > min_vis) { + // Inaccessible move assignment_operator. + return false; + } + + if (assignment_operator->_storage_class & CPPInstance::SC_deleted) { + // It is deleted. + return false; + } + + if (is_abstract()) { + return false; + } + + return true; + } + + return is_copy_assignable(min_vis); +} + /** * Returns true if the type is destructible. */ @@ -886,6 +988,80 @@ get_move_constructor() const { return (CPPInstance *)NULL; } +/** + * Returns the assignment operator defined for the struct type, if any, or + * NULL if no assignment operator is found. + */ +CPPFunctionGroup *CPPStructType:: +get_assignment_operator() const { + // Just look for the function with the name "operator =" + CPPScope::Functions::const_iterator fi; + fi = _scope->_functions.find("operator ="); + if (fi != _scope->_functions.end()) { + return fi->second; + } else { + return (CPPFunctionGroup *)NULL; + } +} + +/** + * Returns the copy assignment operator defined for the struct type, or NULL + * if no user-declared copy assignment operator exists. + */ +CPPInstance *CPPStructType:: +get_copy_assignment_operator() const { + CPPFunctionGroup *fgroup = get_assignment_operator(); + if (fgroup == (CPPFunctionGroup *)NULL) { + return (CPPInstance *)NULL; + } + + CPPFunctionGroup::Instances::const_iterator ii; + for (ii = fgroup->_instances.begin(); + ii != fgroup->_instances.end(); + ++ii) { + CPPInstance *inst = (*ii); + assert(inst->_type != (CPPType *)NULL); + + CPPFunctionType *ftype = inst->_type->as_function_type(); + assert(ftype != (CPPFunctionType *)NULL); + + if ((ftype->_flags & CPPFunctionType::F_copy_assignment_operator) != 0) { + return inst; + } + } + + return (CPPInstance *)NULL; +} + +/** + * Returns the move assignment operator defined for the struct type, or NULL + * if no user-declared move assignment operator exists. + */ +CPPInstance *CPPStructType:: +get_move_assignment_operator() const { + CPPFunctionGroup *fgroup = get_assignment_operator(); + if (fgroup == (CPPFunctionGroup *)NULL) { + return (CPPInstance *)NULL; + } + + CPPFunctionGroup::Instances::const_iterator ii; + for (ii = fgroup->_instances.begin(); + ii != fgroup->_instances.end(); + ++ii) { + CPPInstance *inst = (*ii); + assert(inst->_type != (CPPType *)NULL); + + CPPFunctionType *ftype = inst->_type->as_function_type(); + assert(ftype != (CPPFunctionType *)NULL); + + if ((ftype->_flags & CPPFunctionType::F_move_assignment_operator) != 0) { + return inst; + } + } + + return (CPPInstance *)NULL; +} + /** * Returns the destructor defined for the struct type, if any, or NULL if no * user-declared destructor is found. diff --git a/dtool/src/cppparser/cppStructType.h b/dtool/src/cppparser/cppStructType.h index 585c7476b2..9da8777a6c 100644 --- a/dtool/src/cppparser/cppStructType.h +++ b/dtool/src/cppparser/cppStructType.h @@ -56,10 +56,13 @@ public: virtual bool is_constructible(const CPPType *arg_type) const; virtual bool is_default_constructible() const; virtual bool is_copy_constructible() const; + virtual bool is_copy_assignable() const; virtual bool is_destructible() const; bool is_default_constructible(CPPVisibility min_vis) const; bool is_copy_constructible(CPPVisibility min_vis) const; - bool is_move_constructible(CPPVisibility min_vis) const; + bool is_move_constructible(CPPVisibility min_vis = V_public) const; + bool is_copy_assignable(CPPVisibility min_vis) const; + bool is_move_assignable(CPPVisibility min_vis = V_public) const; bool is_destructible(CPPVisibility min_vis) const; virtual bool is_convertible_to(const CPPType *other) const; @@ -69,6 +72,9 @@ public: CPPInstance *get_default_constructor() const; CPPInstance *get_copy_constructor() const; CPPInstance *get_move_constructor() const; + CPPFunctionGroup *get_assignment_operator() const; + CPPInstance *get_copy_assignment_operator() const; + CPPInstance *get_move_assignment_operator() const; CPPInstance *get_destructor() const; virtual CPPDeclaration * diff --git a/dtool/src/cppparser/cppToken.cxx b/dtool/src/cppparser/cppToken.cxx index 025590113a..d2e00982dc 100644 --- a/dtool/src/cppparser/cppToken.cxx +++ b/dtool/src/cppparser/cppToken.cxx @@ -252,6 +252,14 @@ output(ostream &out) const { out << "RSHIFTEQUAL"; break; + case ATTR_LEFT: + out << "ATTR_LEFT"; + break; + + case ATTR_RIGHT: + out << "ATTR_RIGHT"; + break; + case KW_BOOL: out << "KW_BOOL"; break; diff --git a/dtool/src/cppparser/cppType.cxx b/dtool/src/cppparser/cppType.cxx index 801c571add..af3f511da4 100644 --- a/dtool/src/cppparser/cppType.cxx +++ b/dtool/src/cppparser/cppType.cxx @@ -110,6 +110,14 @@ is_copy_constructible() const { return false; } +/** + * Returns true if the type is copy-assignable. + */ +bool CPPType:: +is_copy_assignable() const { + return false; +} + /** * Returns true if the type is destructible. */ diff --git a/dtool/src/cppparser/cppType.h b/dtool/src/cppparser/cppType.h index c709c475d6..a312f45d33 100644 --- a/dtool/src/cppparser/cppType.h +++ b/dtool/src/cppparser/cppType.h @@ -51,6 +51,7 @@ public: virtual bool is_constructible(const CPPType *type) const; virtual bool is_default_constructible() const; virtual bool is_copy_constructible() const; + virtual bool is_copy_assignable() const; virtual bool is_destructible() const; virtual bool is_parameter_expr() const; diff --git a/dtool/src/cppparser/cppTypedefType.cxx b/dtool/src/cppparser/cppTypedefType.cxx index 61c569760e..d965d5db48 100644 --- a/dtool/src/cppparser/cppTypedefType.cxx +++ b/dtool/src/cppparser/cppTypedefType.cxx @@ -205,6 +205,14 @@ is_copy_constructible() const { return _type->is_copy_constructible(); } +/** + * Returns true if the type is copy-assignable. + */ +bool CPPTypedefType:: +is_copy_assignable() const { + return _type->is_copy_assignable(); +} + /** * Returns true if the type is destructible. */ diff --git a/dtool/src/cppparser/cppTypedefType.h b/dtool/src/cppparser/cppTypedefType.h index 280033738d..04a9b05995 100644 --- a/dtool/src/cppparser/cppTypedefType.h +++ b/dtool/src/cppparser/cppTypedefType.h @@ -48,6 +48,7 @@ public: virtual bool is_constructible(const CPPType *type) const; virtual bool is_default_constructible() const; virtual bool is_copy_constructible() const; + virtual bool is_copy_assignable() const; virtual bool is_destructible() const; virtual bool is_fully_specified() const; diff --git a/dtool/src/cppparser/p3cppParser_composite1.cxx b/dtool/src/cppparser/p3cppParser_composite1.cxx index 8b60e60a17..e8020dd64d 100644 --- a/dtool/src/cppparser/p3cppParser_composite1.cxx +++ b/dtool/src/cppparser/p3cppParser_composite1.cxx @@ -2,6 +2,7 @@ #include "cppFunctionType.cxx" #include "cppGlobals.cxx" #include "cppCommentBlock.cxx" +#include "cppClosureType.cxx" #include "cppConstType.cxx" #include "cppDeclaration.cxx" #include "cppMakeProperty.cxx" diff --git a/dtool/src/dtoolbase/deletedBufferChain.cxx b/dtool/src/dtoolbase/deletedBufferChain.cxx index 122edf8837..27d7214d79 100644 --- a/dtool/src/dtoolbase/deletedBufferChain.cxx +++ b/dtool/src/dtoolbase/deletedBufferChain.cxx @@ -39,7 +39,7 @@ allocate(size_t size, TypeHandle type_handle) { assert(size <= _buffer_size); // Determine how much space to allocate. - const size_t alloc_size = _buffer_size + flag_reserved_bytes + MemoryHook::get_memory_alignment() - 1; + const size_t alloc_size = _buffer_size + flag_reserved_bytes + MEMORY_HOOK_ALIGNMENT - 1; ObjectNode *obj; @@ -71,8 +71,8 @@ allocate(size_t size, TypeHandle type_handle) { // Allocate memory, and make sure the object starts at the proper alignment. void *mem = NeverFreeMemory::alloc(alloc_size); - intptr_t pad = ((intptr_t)flag_reserved_bytes - (intptr_t)mem) % MemoryHook::get_memory_alignment(); - obj = (ObjectNode *)((uintptr_t)mem + pad); + uintptr_t aligned = ((uintptr_t)mem + flag_reserved_bytes + MEMORY_HOOK_ALIGNMENT - 1) & ~(MEMORY_HOOK_ALIGNMENT - 1); + obj = (ObjectNode *)(aligned - flag_reserved_bytes); #ifdef USE_DELETEDCHAINFLAG obj->_flag = DCF_alive; @@ -80,8 +80,8 @@ allocate(size_t size, TypeHandle type_handle) { void *ptr = node_to_buffer(obj); -#ifdef _DEBUG - assert(((uintptr_t)ptr % MemoryHook::get_memory_alignment()) == 0); +#ifndef NDEBUG + assert(((uintptr_t)ptr % MEMORY_HOOK_ALIGNMENT) == 0); #endif #ifdef DO_MEMORY_USAGE @@ -106,8 +106,8 @@ deallocate(void *ptr, TypeHandle type_handle) { assert(ptr != (void *)NULL); #ifdef DO_MEMORY_USAGE - type_handle.dec_memory_usage(TypeHandle::MC_deleted_chain_active, - _buffer_size + flag_reserved_bytes); + const size_t alloc_size = _buffer_size + flag_reserved_bytes + MEMORY_HOOK_ALIGNMENT - 1; + type_handle.dec_memory_usage(TypeHandle::MC_deleted_chain_active, alloc_size); // type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_inactive, // _buffer_size + flag_reserved_bytes); diff --git a/dtool/src/dtoolbase/deletedChain.T b/dtool/src/dtoolbase/deletedChain.T index 5f50c9a36e..96e800ba3b 100644 --- a/dtool/src/dtoolbase/deletedChain.T +++ b/dtool/src/dtoolbase/deletedChain.T @@ -1,25 +1,22 @@ -// Filename: deletedChain.T -// Created by: drose (01Apr06) -// -//////////////////////////////////////////////////////////////////// -// -// 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." -// -//////////////////////////////////////////////////////////////////// +/** + * 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 deletedChain.T + * @author drose + * @date 2006-04-01 + */ template DeletedChain StaticDeletedChain::_chain; -//////////////////////////////////////////////////////////////////// -// Function: DeletedChain::allocate -// Access: Public -// Description: Allocates the memory for a new object of Type. -//////////////////////////////////////////////////////////////////// +/** + * Allocates the memory for a new object of Type. + */ template INLINE Type *DeletedChain:: allocate(size_t size, TypeHandle type_handle) { @@ -31,14 +28,12 @@ allocate(size_t size, TypeHandle type_handle) { memory_hook->mark_pointer(ptr, _chain->get_buffer_size(), make_ref_ptr(ptr)); #endif // DO_MEMORY_USAGE - return (Type *)ptr; + return (Type *)ASSUME_ALIGNED(ptr, MEMORY_HOOK_ALIGNMENT); } -//////////////////////////////////////////////////////////////////// -// Function: DeletedChain::deallocate -// Access: Public -// Description: Frees the memory for an object of Type. -//////////////////////////////////////////////////////////////////// +/** + * Frees the memory for an object of Type. + */ template INLINE void DeletedChain:: deallocate(Type *ptr, TypeHandle type_handle) { @@ -57,16 +52,13 @@ deallocate(Type *ptr, TypeHandle type_handle) { _chain->deallocate(ptr, type_handle); } -//////////////////////////////////////////////////////////////////// -// Function: DeletedChain::validate -// Access: Public -// Description: Returns true if the pointer is valid, false if it has -// been deleted or if it was never a valid pointer. -// -// This is only meaningful in debug mode, where -// USE_DELETEDCHAINFLAG is defined. If not, this -// trivially returns true. -//////////////////////////////////////////////////////////////////// +/** + * Returns true if the pointer is valid, false if it has been deleted or if it + * was never a valid pointer. + * + * This is only meaningful in debug mode, where USE_DELETEDCHAINFLAG is + * defined. If not, this trivially returns true. + */ template INLINE bool DeletedChain:: validate(const Type *ptr) { @@ -80,48 +72,37 @@ validate(const Type *ptr) { #endif // USE_DELETEDCHAINFLAG } -//////////////////////////////////////////////////////////////////// -// Function: DeletedChain::make_ref_ptr -// Access: Public, Static -// Description: This method has two overloads: one that accepts a -// void *, and one that accepts a ReferenceCount *. We -// rely on the C++ compiler to select the most -// appropriate one for a given type to return the -// ReferenceCount pointer that corresponds to a -// particular type, or NULL if the type does not inherit -// from ReferenceCount. -//////////////////////////////////////////////////////////////////// +/** + * This method has two overloads: one that accepts a void *, and one that + * accepts a ReferenceCount *. We rely on the C++ compiler to select the most + * appropriate one for a given type to return the ReferenceCount pointer that + * corresponds to a particular type, or NULL if the type does not inherit from + * ReferenceCount. + */ template INLINE ReferenceCount *DeletedChain:: make_ref_ptr(void *) { return NULL; } -//////////////////////////////////////////////////////////////////// -// Function: DeletedChain::make_ref_ptr -// Access: Public, Static -// Description: This method has two overloads: one that accepts a -// void *, and one that accepts a ReferenceCount *. We -// rely on the C++ compiler to select the most -// appropriate one for a given type to return the -// ReferenceCount pointer that corresponds to a -// particular type, or NULL if the type does not inherit -// from ReferenceCount. -//////////////////////////////////////////////////////////////////// +/** + * This method has two overloads: one that accepts a void *, and one that + * accepts a ReferenceCount *. We rely on the C++ compiler to select the most + * appropriate one for a given type to return the ReferenceCount pointer that + * corresponds to a particular type, or NULL if the type does not inherit from + * ReferenceCount. + */ template INLINE ReferenceCount *DeletedChain:: make_ref_ptr(ReferenceCount *ptr) { return ptr; } -//////////////////////////////////////////////////////////////////// -// Function: DeletedChain::init_deleted_chain -// Access: Private -// Description: Assigns the _chain pointer if it is not already -// assigned. This can't be done by a constructor, since -// often the DeletedChain instance is used before its -// static construct has had a chance to be called. -//////////////////////////////////////////////////////////////////// +/** + * Assigns the _chain pointer if it is not already assigned. This can't be + * done by a constructor, since often the DeletedChain instance is used before + * its static construct has had a chance to be called. + */ template void DeletedChain:: init_deleted_chain() { @@ -131,38 +112,32 @@ init_deleted_chain() { } } -//////////////////////////////////////////////////////////////////// -// Function: StaticDeletedChain::allocate -// Access: Public, Static -// Description: Allocates the memory for a new object of Type. -//////////////////////////////////////////////////////////////////// +/** + * Allocates the memory for a new object of Type. + */ template INLINE Type *StaticDeletedChain:: allocate(size_t size, TypeHandle type_handle) { - return _chain.allocate(size, type_handle); + Type *ptr = _chain.allocate(size, type_handle); + return (Type *)ASSUME_ALIGNED(ptr, MEMORY_HOOK_ALIGNMENT); } -//////////////////////////////////////////////////////////////////// -// Function: StaticDeletedChain::deallocate -// Access: Public -// Description: Frees the memory for an object of Type. -//////////////////////////////////////////////////////////////////// +/** + * Frees the memory for an object of Type. + */ template INLINE void StaticDeletedChain:: deallocate(Type *ptr, TypeHandle type_handle) { _chain.deallocate(ptr, type_handle); } -//////////////////////////////////////////////////////////////////// -// Function: StaticDeletedChain::validate -// Access: Public -// Description: Returns true if the pointer is valid, false if it has -// been deleted or if it was never a valid pointer. -// -// This is only meaningful in debug mode, where -// USE_DELETEDCHAINFLAG is defined. If not, this -// trivially returns true. -//////////////////////////////////////////////////////////////////// +/** + * Returns true if the pointer is valid, false if it has been deleted or if it + * was never a valid pointer. + * + * This is only meaningful in debug mode, where USE_DELETEDCHAINFLAG is + * defined. If not, this trivially returns true. + */ template INLINE bool StaticDeletedChain:: validate(const Type *ptr) { diff --git a/dtool/src/dtoolbase/deletedChain.h b/dtool/src/dtoolbase/deletedChain.h index 17a723a89b..a25be5d429 100644 --- a/dtool/src/dtoolbase/deletedChain.h +++ b/dtool/src/dtoolbase/deletedChain.h @@ -77,7 +77,7 @@ public: // Place this macro within a class definition to define appropriate operator // new and delete methods that take advantage of DeletedChain. #define ALLOC_DELETED_CHAIN(Type) \ - inline void *operator new(size_t size) { \ + inline void *operator new(size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT) { \ return (void *)StaticDeletedChain< Type >::allocate(size, get_type_handle(Type)); \ } \ inline void *operator new(size_t size, void *ptr) { \ @@ -96,7 +96,7 @@ public: // Use this variant of the above macro in cases in which the compiler fails to // unify the static template pointers properly, to prevent leaks. #define ALLOC_DELETED_CHAIN_DECL(Type) \ - inline void *operator new(size_t size) { \ + inline void *operator new(size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT) { \ return (void *)_deleted_chain.allocate(size, get_type_handle(Type)); \ } \ inline void *operator new(size_t size, void *ptr) { \ diff --git a/dtool/src/dtoolbase/dlmalloc_src.cxx b/dtool/src/dtoolbase/dlmalloc_src.cxx index 008350de73..a26fcd28cd 100644 --- a/dtool/src/dtoolbase/dlmalloc_src.cxx +++ b/dtool/src/dtoolbase/dlmalloc_src.cxx @@ -587,8 +587,11 @@ MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP #define MAX_SIZE_T (~(size_t)0) #ifndef USE_LOCKS /* ensure true if spin or recursive locks set */ -#define USE_LOCKS ((defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || \ - (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0)) +#if (defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) +#define USE_LOCKS 1 +#else +#define USE_LOCKS 0 +#endif #endif /* USE_LOCKS */ #if USE_LOCKS /* Spin locks for gcc >= 4.1, older gcc on x86, MSC >= 1310 */ @@ -647,7 +650,9 @@ MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP #ifndef HAVE_MREMAP #ifdef linux #define HAVE_MREMAP 1 +#ifndef _GNU_SOURCE #define _GNU_SOURCE /* Turns on mremap() definition */ +#endif #else /* linux */ #define HAVE_MREMAP 0 #endif /* linux */ diff --git a/dtool/src/dtoolbase/dtool_platform.h b/dtool/src/dtoolbase/dtool_platform.h index 7955c7a941..4cb25abc3a 100644 --- a/dtool/src/dtoolbase/dtool_platform.h +++ b/dtool/src/dtoolbase/dtool_platform.h @@ -72,10 +72,8 @@ #define DTOOL_PLATFORM "linux_ppc" #endif -#ifndef DTOOL_PLATFORM +#if !defined(DTOOL_PLATFORM) && !defined(CPPPARSER) #error "Can't determine platform; please define DTOOL_PLATFORM in Config.pp file." #endif - - #endif diff --git a/dtool/src/dtoolbase/dtoolbase.h b/dtool/src/dtoolbase/dtoolbase.h index b192f24055..3f8e07f998 100644 --- a/dtool/src/dtoolbase/dtoolbase.h +++ b/dtool/src/dtoolbase/dtoolbase.h @@ -58,24 +58,16 @@ #pragma warning (disable : 4267) /* C4577: 'noexcept' used with no exception handling mode specified */ #pragma warning (disable : 4577) - -#if _MSC_VER >= 1300 - #if _MSC_VER >= 1310 - #define USING_MSVC7_1 -// #pragma message("VC 7.1") - #else -// #pragma message("VC 7.0") - #endif -#define USING_MSVC7 -#else -// #pragma message("VC 6.0") -#endif #endif /* WIN32_VC */ #ifndef __has_builtin #define __has_builtin(x) 0 #endif +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + // Use NODEFAULT to optimize a switch() stmt to tell MSVC to automatically go // to the final untested case after it has failed all the other cases (i.e. // 'assume at least one of the cases is always true') @@ -89,6 +81,26 @@ #define NODEFAULT #endif +// Use this to hint the compiler that a memory address is aligned. +#if __has_builtin(__builtin_assume_aligned) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) +#define ASSUME_ALIGNED(x, y) (__builtin_assume_aligned(x, y)) +#else +#define ASSUME_ALIGNED(x, y) (x) +#endif + +#if __has_attribute(assume_aligned) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9) +#define RETURNS_ALIGNED(x) __attribute__((assume_aligned(x))) +#else +#define RETURNS_ALIGNED(x) +#endif + +#ifdef __GNUC__ +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif /* include win32 defns for everything up to WinServer2003, and assume @@ -328,7 +340,7 @@ typedef struct _object PyObject; #ifdef __WORDSIZE #define NATIVE_WORDSIZE __WORDSIZE -#elif defined(_LP64) +#elif defined(_LP64) || defined(_WIN64) #define NATIVE_WORDSIZE 64 #else #define NATIVE_WORDSIZE 32 @@ -339,21 +351,26 @@ typedef struct _object PyObject; #define ALIGN_4BYTE #define ALIGN_8BYTE #define ALIGN_16BYTE +#define ALIGN_32BYTE #define ALIGN_64BYTE #elif defined(_MSC_VER) #define ALIGN_4BYTE __declspec(align(4)) #define ALIGN_8BYTE __declspec(align(8)) #define ALIGN_16BYTE __declspec(align(16)) +#define ALIGN_32BYTE __declspec(align(32)) #define ALIGN_64BYTE __declspec(align(64)) #elif defined(__GNUC__) #define ALIGN_4BYTE __attribute__ ((aligned (4))) #define ALIGN_8BYTE __attribute__ ((aligned (8))) #define ALIGN_16BYTE __attribute__ ((aligned (16))) +#define ALIGN_32BYTE __attribute__ ((aligned (32))) #define ALIGN_64BYTE __attribute__ ((aligned (64))) #else #define ALIGN_4BYTE #define ALIGN_8BYTE #define ALIGN_16BYTE +#define ALIGN_32BYTE +#define ALIGN_64BYTE #endif // Do we need to implement memory-alignment enforcement within the MemoryHook @@ -389,6 +406,35 @@ typedef struct _object PyObject; #endif +#ifdef LINMATH_ALIGN +/* We require 16-byte alignment of certain structures, to support SSE2. We + don't strictly have to align everything, but it's just easier to do so. */ +#if defined(HAVE_EIGEN) && defined(__AVX__) && defined(STDFLOAT_DOUBLE) +/* Eigen uses AVX instructions, but let's only enable this when compiling with + double precision, so that we can keep our ABI a bit more stable. */ +#define MEMORY_HOOK_ALIGNMENT 32 +#else +#define MEMORY_HOOK_ALIGNMENT 16 +#endif +/* Otherwise, align to two words. This seems to be pretty standard to the + point where some code may rely on this being the case. */ +#elif defined(IS_OSX) || NATIVE_WORDSIZE >= 64 +#define MEMORY_HOOK_ALIGNMENT 16 +#else +#define MEMORY_HOOK_ALIGNMENT 8 +#endif + +#ifdef HAVE_EIGEN +/* Make sure that Eigen doesn't assume alignment guarantees we don't offer. */ +#define EIGEN_MAX_ALIGN_BYTES MEMORY_HOOK_ALIGNMENT +#ifndef EIGEN_MPL2_ONLY +#define EIGEN_MPL2_ONLY 1 +#endif +#if !defined(_DEBUG) && !defined(EIGEN_NO_DEBUG) +#define EIGEN_NO_DEBUG 1 +#endif +#endif + /* Determine our memory-allocation requirements. */ #if defined(USE_MEMORY_PTMALLOC2) || defined(USE_MEMORY_DLMALLOC) || defined(DO_MEMORY_USAGE) || defined(MEMORY_HOOK_DO_ALIGN) /* In this case we have some custom memory management requirements. */ @@ -418,6 +464,8 @@ typedef struct _object PyObject; #define MAKE_PROPERTY2(property_name, ...) __make_property2(property_name, __VA_ARGS__) #define MAKE_SEQ(seq_name, num_name, element_name) __make_seq(seq_name, num_name, element_name) #define MAKE_SEQ_PROPERTY(property_name, ...) __make_seq_property(property_name, __VA_ARGS__) +#define MAKE_MAP_PROPERTY(property_name, ...) __make_map_property(property_name, __VA_ARGS__) +#define MAKE_MAP_KEYS_SEQ(property_name, ...) __make_map_keys_seq(property_name, __VA_ARGS__) #define EXTENSION(x) __extension x #define EXTEND __extension #else @@ -428,6 +476,8 @@ typedef struct _object PyObject; #define MAKE_PROPERTY2(property_name, ...) #define MAKE_SEQ(seq_name, num_name, element_name) #define MAKE_SEQ_PROPERTY(property_name, ...) +#define MAKE_MAP_PROPERTY(property_name, ...) +#define MAKE_MAP_KEYS_SEQ(property_name, ...) #define EXTENSION(x) #define EXTEND #endif diff --git a/dtool/src/dtoolbase/dtoolbase_cc.h b/dtool/src/dtoolbase/dtoolbase_cc.h index eb99381c01..eb84f1fccd 100644 --- a/dtool/src/dtoolbase/dtoolbase_cc.h +++ b/dtool/src/dtoolbase/dtoolbase_cc.h @@ -34,11 +34,15 @@ using namespace std; #define ALWAYS_INLINE inline #define TYPENAME typename #define CONSTEXPR constexpr +#define ALWAYS_INLINE_CONSTEXPR constexpr #define NOEXCEPT noexcept #define FINAL final -#define OVERRIDE override #define MOVE(x) x #define DEFAULT_CTOR = default +#define DEFAULT_DTOR = default +#define DEFAULT_ASSIGN = default +#define DELETED = delete +#define DELETED_ASSIGN = delete #define EXPORT_TEMPLATE_CLASS(expcl, exptp, classname) @@ -75,6 +79,7 @@ typedef int ios_seekdir; #endif #include +#include #ifdef HAVE_NAMESPACE using namespace std; @@ -114,6 +119,20 @@ typedef ios::iostate ios_iostate; typedef ios::seekdir ios_seekdir; #endif +// Apple has an outdated libstdc++. Not all is lost, though, as we can fill +// in some important missing functions. +#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20070719 +typedef decltype(nullptr) nullptr_t; + +template struct remove_reference {typedef T type;}; +template struct remove_reference {typedef T type;}; +template struct remove_reference{typedef T type;}; + +template typename remove_reference::type &&move(T &&t) { + return static_cast::type&&>(t); +} +#endif + #ifdef _MSC_VER #define ALWAYS_INLINE __forceinline #elif defined(__GNUC__) @@ -134,7 +153,9 @@ typedef ios::seekdir ios_seekdir; // Determine the availability of C++11 features. #if defined(__has_extension) // Clang magic. # if __has_extension(cxx_constexpr) -# define CONSTEXPR constexpr +# if !defined(__apple_build_version__) || __apple_build_version__ >= 5000000 +# define CONSTEXPR constexpr +# endif # endif # if __has_extension(cxx_noexcept) # define NOEXCEPT noexcept @@ -145,35 +166,42 @@ typedef ios::seekdir ios_seekdir; # endif # if __has_extension(cxx_override_control) && (__cplusplus >= 201103L) # define FINAL final -# define OVERRIDE override # endif # if __has_extension(cxx_defaulted_functions) # define DEFAULT_CTOR = default +# define DEFAULT_DTOR = default +# define DEFAULT_ASSIGN = default # endif -#elif defined(__GNUC__) && (__cplusplus >= 201103L) // GCC - -// GCC defines several macros which we can query. List of all supported -// builtin macros: https:gcc.gnu.orgprojectscxx0x.html -# if __cpp_constexpr >= 200704 -# define CONSTEXPR constexpr +# if __has_extension(cxx_deleted_functions) +# define DELETED = delete # endif +#elif defined(__GNUC__) // GCC // Starting at GCC 4.4 # if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) # define DEFAULT_CTOR = default +# define DEFAULT_DTOR = default +# define DEFAULT_ASSIGN = default +# define DELETED = delete # endif // Starting at GCC 4.6 # if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +# define CONSTEXPR constexpr # define NOEXCEPT noexcept # define USE_MOVE_SEMANTICS -# define FINAL final # define MOVE(x) move(x) # endif // Starting at GCC 4.7 # if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) -# define OVERRIDE override +# define FINAL final +# endif + +// GCC defines several macros which we can query. List of all supported +// builtin macros: https://gcc.gnu.org/projects/cxx-status.html +# if !defined(CONSTEXPR) && __cpp_constexpr >= 200704 +# define CONSTEXPR constexpr # endif #elif defined(_MSC_VER) && _MSC_VER >= 1900 // Visual Studio 2015 @@ -181,20 +209,27 @@ typedef ios::seekdir ios_seekdir; # define NOEXCEPT noexcept # define USE_MOVE_SEMANTICS # define FINAL final -# define OVERRIDE override # define MOVE(x) move(x) -# define DEFAULT_CTOR = default #elif defined(_MSC_VER) && _MSC_VER >= 1600 // Visual Studio 2010 # define NOEXCEPT throw() -# define OVERRIDE override # define USE_MOVE_SEMANTICS # define FINAL sealed # define MOVE(x) move(x) #endif +#if defined(_MSC_VER) && _MSC_VER >= 1800 // Visual Studio 2013 +# define DEFAULT_CTOR = default +# define DEFAULT_DTOR = default +# define DEFAULT_ASSIGN = default +# define DELETED = delete +#endif + // Fallbacks if features are not supported #ifndef CONSTEXPR # define CONSTEXPR INLINE +# define ALWAYS_INLINE_CONSTEXPR ALWAYS_INLINE +#else +# define ALWAYS_INLINE_CONSTEXPR ALWAYS_INLINE CONSTEXPR #endif #ifndef NOEXCEPT # define NOEXCEPT @@ -205,12 +240,21 @@ typedef ios::seekdir ios_seekdir; #ifndef FINAL # define FINAL #endif -#ifndef OVERRIDE -# define OVERRIDE -#endif #ifndef DEFAULT_CTOR # define DEFAULT_CTOR {} #endif +#ifndef DEFAULT_DTOR +# define DEFAULT_DTOR {} +#endif +#ifndef DEFAULT_ASSIGN +# define DEFAULT_ASSIGN {return *this;} +#endif +#ifndef DELETED +# define DELETED {assert(false);} +# define DELETED_ASSIGN {assert(false);return *this;} +#else +# define DELETED_ASSIGN DELETED +#endif #if !defined(LINK_ALL_STATIC) && defined(EXPORT_TEMPLATES) @@ -243,10 +287,10 @@ EXPCL_DTOOL void init_memory_hook(); // Now redefine some handy macros to hook into the above MemoryHook object. #ifndef USE_MEMORY_NOWRAPPERS -#define PANDA_MALLOC_SINGLE(size) (memory_hook->heap_alloc_single(size)) +#define PANDA_MALLOC_SINGLE(size) (ASSUME_ALIGNED(memory_hook->heap_alloc_single(size), MEMORY_HOOK_ALIGNMENT)) #define PANDA_FREE_SINGLE(ptr) memory_hook->heap_free_single(ptr) -#define PANDA_MALLOC_ARRAY(size) (memory_hook->heap_alloc_array(size)) -#define PANDA_REALLOC_ARRAY(ptr, size) (memory_hook->heap_realloc_array(ptr, size)) +#define PANDA_MALLOC_ARRAY(size) (ASSUME_ALIGNED(memory_hook->heap_alloc_array(size), MEMORY_HOOK_ALIGNMENT)) +#define PANDA_REALLOC_ARRAY(ptr, size) (ASSUME_ALIGNED(memory_hook->heap_realloc_array(ptr, size), MEMORY_HOOK_ALIGNMENT)) #define PANDA_FREE_ARRAY(ptr) memory_hook->heap_free_array(ptr) #else #define PANDA_MALLOC_SINGLE(size) ::malloc(size) diff --git a/dtool/src/dtoolbase/memoryBase.h b/dtool/src/dtoolbase/memoryBase.h index 2f361f1bb1..6d755e695b 100644 --- a/dtool/src/dtoolbase/memoryBase.h +++ b/dtool/src/dtoolbase/memoryBase.h @@ -26,7 +26,7 @@ #ifndef USE_MEMORY_NOWRAPPERS #define ALLOC_MEMORY_BASE \ - inline void *operator new(size_t size) { \ + inline void *operator new(size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT) { \ return PANDA_MALLOC_SINGLE(size); \ } \ inline void *operator new(size_t size, void *ptr) { \ @@ -38,7 +38,7 @@ } \ inline void operator delete(void *, void *) { \ } \ - inline void *operator new[](size_t size) { \ + inline void *operator new[](size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT) { \ return PANDA_MALLOC_ARRAY(size); \ } \ inline void *operator new[](size_t size, void *ptr) { \ diff --git a/dtool/src/dtoolbase/memoryHook.I b/dtool/src/dtoolbase/memoryHook.I index 3d2be05097..bd5a55af79 100644 --- a/dtool/src/dtoolbase/memoryHook.I +++ b/dtool/src/dtoolbase/memoryHook.I @@ -38,57 +38,9 @@ dec_heap(size_t size) { * Returns the global memory alignment. This is the number of bytes at which * each allocated memory pointer will be aligned. */ -INLINE size_t MemoryHook:: +CONSTEXPR size_t MemoryHook:: get_memory_alignment() { -#ifdef LINMATH_ALIGN - // We require 16-byte alignment of certain structures, to support SSE2. We - // don't strictly have to align *everything*, but it's just easier to do so. -#ifdef __AVX__ - // Eigen requires 32-byte alignment when using AVX instructions. - const size_t alignment_size = 32; -#else - const size_t alignment_size = 16; -#endif -#else - // Otherwise, align to two words. This seems to be pretty standard to the - // point where some code may rely on this being the case. - const size_t alignment_size = sizeof(void *) * 2; -#endif - return alignment_size; -} - -/** - * Returns the number of additional bytes that are reserved at the beginning - * of every allocated block to store a size_t. - */ -INLINE size_t MemoryHook:: -get_header_reserved_bytes() { - // We need to figure out the minimum amount of additional space we need in - // order to place a single word at the start of each allocated block, to - // store the size of the block. - -#ifdef LINMATH_ALIGN - // If we're doing SSE2 alignment, we must reserve a full 16-byte block, - // since anything less than that will spoil the alignment. -#ifdef __AVX__ - // Eigen requires 32-byte alignment when using AVX instructions. - static const size_t header_reserved_bytes = 32; -#else - static const size_t header_reserved_bytes = 16; -#endif - -#elif defined(MEMORY_HOOK_DO_ALIGN) - // If we're just aligning to words, we reserve a block as big as two words, - // to allow us wiggle room to align the word precisely within that block. - static const size_t header_reserved_bytes = sizeof(size_t) + sizeof(size_t); - -#else - // Virtually all allocators align to two words, so we make sure we preserve - // that alignment for the benefit of anyone who relies upon that. - static const size_t header_reserved_bytes = sizeof(void *) * 2; -#endif - - return header_reserved_bytes; + return MEMORY_HOOK_ALIGNMENT; } /** @@ -110,67 +62,29 @@ round_up_to_page_size(size_t size) const { } /** - * Increments the amount of requested size as necessary to accommodate the - * extra data we might piggyback on each allocated block. + * Given a pointer that was returned by a MemoryHook allocation, returns the + * number of bytes that were allocated for it. This may be slightly larger + * than the number of bytes requested. + * The behavior of this function is undefined if the given pointer was not + * returned by the MemoryHook allocator or was already freed. + * May return 0 if not compiling with DO_MEMORY_USAGE. + * + * This is only defined publicly so TypeHandle can get at it; it really + * shouldn't be used outside of dtoolbase. */ INLINE size_t MemoryHook:: -inflate_size(size_t size) { +get_ptr_size(void *ptr) { #if defined(MEMORY_HOOK_DO_ALIGN) - // If we're aligning, we need to request the header size, plus extra bytes - // to give us wiggle room to adjust the pointer. - return size + get_header_reserved_bytes() + get_memory_alignment() - 1; + uintptr_t *root = (uintptr_t *)ptr; + return (size_t)root[-2]; +#elif defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2) + // If we are using dlmalloc, we know how it stores the size. + size_t *root = (size_t *)ptr; + return (root[-1] & ~0x7) - sizeof(size_t); #elif defined(DO_MEMORY_USAGE) - // If we're not aligning, but we're tracking memory allocations, we just - // need the header size extra (this gives us a place to store the size of - // the allocated block). - return size + get_header_reserved_bytes(); + size_t *root = (size_t *)((char *)ptr - MEMORY_HOOK_ALIGNMENT); + return *root; #else - // If we're not doing any of that, we can just allocate the precise - // requested amount. - return size; -#endif // DO_MEMORY_USAGE -} - -/** - * Converts an allocated pointer to a pointer returnable to the application. - * Stuffs size in the first n bytes of the allocated space. - */ -INLINE void *MemoryHook:: -alloc_to_ptr(void *alloc, size_t size) { -#if defined(MEMORY_HOOK_DO_ALIGN) - size_t alignment = get_memory_alignment(); - // Move the allocated pointer up to the next even alignment. - size_t *root = (size_t *)((((size_t)alloc + alignment - 1) / alignment) * alignment); - assert(alloc <= root && (size_t)((char *)root - (char *)alloc) < alignment); - root[0] = size; - root[1] = (size_t)alloc; // Save the pointer we originally allocated. - return (void *)((char *)root + get_header_reserved_bytes()); -#elif defined(DO_MEMORY_USAGE) - size_t *root = (size_t *)alloc; - root[0] = size; - return (void *)((char *)root + get_header_reserved_bytes()); -#else - return alloc; -#endif // DO_MEMORY_USAGE -} - -/** - * Converts an application pointer back to the original allocated pointer. - * Extracts size from the first n bytes of the allocated space. - */ -INLINE void *MemoryHook:: -ptr_to_alloc(void *ptr, size_t &size) { -#if defined(MEMORY_HOOK_DO_ALIGN) - size_t *root = (size_t *)((char *)ptr - get_header_reserved_bytes()); - size = root[0]; - void *alloc = (void *)root[1]; // Get the pointer we originally allocated. - assert(alloc <= root && (size_t)((char *)root - (char *)alloc) < get_memory_alignment()); - return alloc; -#elif defined(DO_MEMORY_USAGE) - size_t *root = (size_t *)((char *)ptr - get_header_reserved_bytes()); - size = root[0]; - return (void *)root; -#else - return ptr; + return 0; #endif // DO_MEMORY_USAGE } diff --git a/dtool/src/dtoolbase/memoryHook.cxx b/dtool/src/dtoolbase/memoryHook.cxx index e6131a5266..510b5b2688 100644 --- a/dtool/src/dtoolbase/memoryHook.cxx +++ b/dtool/src/dtoolbase/memoryHook.cxx @@ -14,6 +14,7 @@ #include "memoryHook.h" #include "deletedBufferChain.h" #include +#include "typeRegistry.h" #ifdef WIN32 @@ -36,8 +37,19 @@ #endif // WIN32 +// Ensure we made the right decisions about the alignment size. +static_assert(MEMORY_HOOK_ALIGNMENT >= sizeof(size_t), + "MEMORY_HOOK_ALIGNMENT should at least be sizeof(size_t)"); +static_assert(MEMORY_HOOK_ALIGNMENT >= sizeof(void *), + "MEMORY_HOOK_ALIGNMENT should at least be sizeof(void *)"); +static_assert(MEMORY_HOOK_ALIGNMENT * 8 >= NATIVE_WORDSIZE, + "MEMORY_HOOK_ALIGNMENT * 8 should at least be NATIVE_WORDSIZE"); +static_assert((MEMORY_HOOK_ALIGNMENT & (MEMORY_HOOK_ALIGNMENT - 1)) == 0, + "MEMORY_HOOK_ALIGNMENT should be a power of two"); -#if defined(USE_MEMORY_DLMALLOC) +#if defined(CPPPARSER) + +#elif defined(USE_MEMORY_DLMALLOC) // Memory manager: DLMALLOC This is Doug Lea's memory manager. It is very // fast, but it is not thread-safe. However, we provide thread locking within @@ -49,17 +61,8 @@ #ifdef _DEBUG #define DEBUG 1 #endif -#ifdef LINMATH_ALIGN -// drose: We require 16-byte alignment of certain structures, to -// support SSE2. We don't strictly have to align *everything*, but -// it's just easier to do so. -#ifdef __AVX__ -// Eigen requires 32-byte alignment when using AVX instructions. -#define MALLOC_ALIGNMENT ((size_t)32U) -#else -#define MALLOC_ALIGNMENT ((size_t)16U) -#endif -#endif +// dlmalloc can do the alignment we ask for. +#define MALLOC_ALIGNMENT MEMORY_HOOK_ALIGNMENT #include "dlmalloc_src.cxx" @@ -104,6 +107,83 @@ #endif // USE_MEMORY_* +/** + * Increments the amount of requested size as necessary to accommodate the + * extra data we might piggyback on each allocated block. + */ +INLINE static size_t +inflate_size(size_t size) { +#if defined(MEMORY_HOOK_DO_ALIGN) + // If we're aligning, we need to request the header size, plus extra bytes + // to give us wiggle room to adjust the pointer. + return size + sizeof(uintptr_t) * 2 + MEMORY_HOOK_ALIGNMENT - 1; +#elif defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2) + // If we are can access the allocator's bookkeeping to figure out how many + // bytes were allocated, we don't need to add our own information. + return size; +#elif defined(DO_MEMORY_USAGE) + // If we're not aligning, but we're tracking memory allocations, we just + // need the header size extra (this gives us a place to store the size of + // the allocated block). However, we do need to make sure that any + // alignment guarantee is kept. + return size + MEMORY_HOOK_ALIGNMENT; +#else + // If we're not doing any of that, we can just allocate the precise + // requested amount. + return size; +#endif // DO_MEMORY_USAGE +} + +/** + * Converts an allocated pointer to a pointer returnable to the application. + * Stuffs size in the first n bytes of the allocated space. + */ +INLINE static void * +alloc_to_ptr(void *alloc, size_t size) { +#if defined(MEMORY_HOOK_DO_ALIGN) + // Add room for two uintptr_t values. + uintptr_t *root = (uintptr_t *)((char *)alloc + sizeof(uintptr_t) * 2); + // Align this to the requested boundary. + root = (uintptr_t *)(((uintptr_t)root + MEMORY_HOOK_ALIGNMENT - 1) & ~(MEMORY_HOOK_ALIGNMENT - 1)); + root[-2] = size; + root[-1] = (uintptr_t)alloc; // Save the pointer we originally allocated. + return (void *)root; +#elif defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2) + return alloc; +#elif defined(DO_MEMORY_USAGE) + size_t *root = (size_t *)alloc; + root[0] = size; + return (void *)((char *)root + MEMORY_HOOK_ALIGNMENT); +#else + return alloc; +#endif // DO_MEMORY_USAGE +} + +/** + * Converts an application pointer back to the original allocated pointer. + * Extracts size from the first n bytes of the allocated space, but only if + * DO_MEMORY_USAGE is defined. + */ +INLINE static void * +ptr_to_alloc(void *ptr, size_t &size) { +#if defined(MEMORY_HOOK_DO_ALIGN) + uintptr_t *root = (uintptr_t *)ptr; + size = root[-2]; + return (void *)root[-1]; // Get the pointer we originally allocated. +#elif defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2) +#ifdef DO_MEMORY_USAGE + size = MemoryHook::get_ptr_size(ptr); +#endif + return ptr; +#elif defined(DO_MEMORY_USAGE) + size_t *root = (size_t *)((char *)ptr - MEMORY_HOOK_ALIGNMENT); + size = root[0]; + return (void *)root; +#else + return ptr; +#endif // DO_MEMORY_USAGE +} + /** * */ @@ -124,13 +204,11 @@ MemoryHook() { #endif // WIN32 -#ifdef DO_MEMORY_USAGE _total_heap_single_size = 0; _total_heap_array_size = 0; _requested_heap_size = 0; _total_mmap_size = 0; _max_heap_size = ~(size_t)0; -#endif } /** @@ -138,19 +216,16 @@ MemoryHook() { */ MemoryHook:: MemoryHook(const MemoryHook ©) : - _page_size(copy._page_size) -{ -#ifdef DO_MEMORY_USAGE - _total_heap_single_size = copy._total_heap_single_size; - _total_heap_array_size = copy._total_heap_array_size; - _requested_heap_size = copy._requested_heap_size; - _total_mmap_size = copy._total_mmap_size; - _max_heap_size = copy._max_heap_size; -#endif + _page_size(copy._page_size), + _total_heap_single_size(copy._total_heap_single_size), + _total_heap_array_size(copy._total_heap_array_size), + _requested_heap_size(copy._requested_heap_size), + _total_mmap_size(copy._total_mmap_size), + _max_heap_size(copy._max_heap_size) { - ((MutexImpl &)copy._lock).acquire(); + copy._lock.acquire(); _deleted_chains = copy._deleted_chains; - ((MutexImpl &)copy._lock).release(); + copy._lock.release(); } /** @@ -195,6 +270,11 @@ heap_alloc_single(size_t size) { #ifdef DO_MEMORY_USAGE // In the DO_MEMORY_USAGE case, we want to track the total size of allocated // bytes on the heap. +#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2) + // dlmalloc may slightly overallocate, however. + size = get_ptr_size(alloc); + inflated_size = size; +#endif AtomicAdjust::add(_total_heap_single_size, (AtomicAdjust::Integer)size); if ((size_t)AtomicAdjust::get(_total_heap_single_size) + (size_t)AtomicAdjust::get(_total_heap_array_size) > @@ -204,7 +284,10 @@ heap_alloc_single(size_t size) { #endif // DO_MEMORY_USAGE void *ptr = alloc_to_ptr(alloc, size); +#ifdef _DEBUG + assert(((uintptr_t)ptr % MEMORY_HOOK_ALIGNMENT) == 0); assert(ptr >= alloc && (char *)ptr + size <= (char *)alloc + inflated_size); +#endif return ptr; } @@ -264,6 +347,11 @@ heap_alloc_array(size_t size) { #ifdef DO_MEMORY_USAGE // In the DO_MEMORY_USAGE case, we want to track the total size of allocated // bytes on the heap. +#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2) + // dlmalloc may slightly overallocate, however. + size = get_ptr_size(alloc); + inflated_size = size; +#endif AtomicAdjust::add(_total_heap_array_size, (AtomicAdjust::Integer)size); if ((size_t)AtomicAdjust::get(_total_heap_single_size) + (size_t)AtomicAdjust::get(_total_heap_array_size) > @@ -273,7 +361,10 @@ heap_alloc_array(size_t size) { #endif // DO_MEMORY_USAGE void *ptr = alloc_to_ptr(alloc, size); +#ifdef _DEBUG + assert(((uintptr_t)ptr % MEMORY_HOOK_ALIGNMENT) == 0); assert(ptr >= alloc && (char *)ptr + size <= (char *)alloc + inflated_size); +#endif return ptr; } @@ -285,11 +376,6 @@ heap_realloc_array(void *ptr, size_t size) { size_t orig_size; void *alloc = ptr_to_alloc(ptr, orig_size); -#ifdef DO_MEMORY_USAGE - assert((AtomicAdjust::Integer)orig_size <= _total_heap_array_size); - AtomicAdjust::add(_total_heap_array_size, (AtomicAdjust::Integer)size-(AtomicAdjust::Integer)orig_size); -#endif // DO_MEMORY_USAGE - size_t inflated_size = inflate_size(size); void *alloc1 = alloc; @@ -316,17 +402,40 @@ heap_realloc_array(void *ptr, size_t size) { #endif } - void *ptr1 = alloc_to_ptr(alloc1, size); - assert(ptr1 >= alloc1 && (char *)ptr1 + size <= (char *)alloc1 + inflated_size); -#if defined(MEMORY_HOOK_DO_ALIGN) - // We might have to shift the memory to account for the new offset due to - // the alignment. +#ifdef DO_MEMORY_USAGE +#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2) + // dlmalloc may slightly overallocate, however. + size = get_ptr_size(alloc1); + inflated_size = size; +#endif + assert((AtomicAdjust::Integer)orig_size <= _total_heap_array_size); + AtomicAdjust::add(_total_heap_array_size, (AtomicAdjust::Integer)size-(AtomicAdjust::Integer)orig_size); +#endif // DO_MEMORY_USAGE + + // Align this to the requested boundary. +#ifdef MEMORY_HOOK_DO_ALIGN + // This copies the code from alloc_to_ptr, since we can't write the size and + // pointer until after we have done the memmove. + uintptr_t *root = (uintptr_t *)((char *)alloc1 + sizeof(uintptr_t) * 2); + root = (uintptr_t *)(((uintptr_t)root + MEMORY_HOOK_ALIGNMENT - 1) & ~(MEMORY_HOOK_ALIGNMENT - 1)); + void *ptr1 = (void *)root; + size_t orig_delta = (char *)ptr - (char *)alloc; size_t new_delta = (char *)ptr1 - (char *)alloc1; if (orig_delta != new_delta) { memmove((char *)alloc1 + new_delta, (char *)alloc1 + orig_delta, min(size, orig_size)); } -#endif // MEMORY_HOOK_DO_ALIGN + + root[-2] = size; + root[-1] = (uintptr_t)alloc1; // Save the pointer we originally allocated. +#else + void *ptr1 = alloc_to_ptr(alloc1, size); +#endif + +#ifdef _DEBUG + assert(ptr1 >= alloc1 && (char *)ptr1 + size <= (char *)alloc1 + inflated_size); + assert(((uintptr_t)ptr1 % MEMORY_HOOK_ALIGNMENT) == 0); +#endif return ptr1; } @@ -519,7 +628,6 @@ alloc_fail(size_t attempted_size) { abort(); } -#ifdef DO_MEMORY_USAGE /** * This callback method is called whenever the total allocated heap size * exceeds _max_heap_size. It's mainly intended for reporting memory leaks, @@ -530,6 +638,7 @@ alloc_fail(size_t attempted_size) { */ void MemoryHook:: overflow_heap_size() { +#ifdef DO_MEMORY_USAGE _max_heap_size = ~(size_t)0; -} #endif // DO_MEMORY_USAGE +} diff --git a/dtool/src/dtoolbase/memoryHook.h b/dtool/src/dtoolbase/memoryHook.h index d53e03ba2e..18ce70c0bb 100644 --- a/dtool/src/dtoolbase/memoryHook.h +++ b/dtool/src/dtoolbase/memoryHook.h @@ -52,8 +52,7 @@ public: bool heap_trim(size_t pad); - INLINE static size_t get_memory_alignment(); - INLINE static size_t get_header_reserved_bytes(); + CONSTEXPR static size_t get_memory_alignment(); virtual void *mmap_alloc(size_t size, bool allow_exec); virtual void mmap_free(void *ptr, size_t size); @@ -66,12 +65,8 @@ public: virtual void alloc_fail(size_t attempted_size); -private: - INLINE static size_t inflate_size(size_t size); - INLINE static void *alloc_to_ptr(void *alloc, size_t size); - INLINE static void *ptr_to_alloc(void *ptr, size_t &size); + INLINE static size_t get_ptr_size(void *ptr); -#ifdef DO_MEMORY_USAGE protected: TVOLATILE AtomicAdjust::Integer _total_heap_single_size; TVOLATILE AtomicAdjust::Integer _total_heap_array_size; @@ -83,7 +78,6 @@ protected: size_t _max_heap_size; virtual void overflow_heap_size(); -#endif // DO_MEMORY_USAGE private: size_t _page_size; @@ -91,7 +85,7 @@ private: typedef map DeletedChains; DeletedChains _deleted_chains; - MutexImpl _lock; + mutable MutexImpl _lock; }; #include "memoryHook.I" diff --git a/dtool/src/dtoolbase/mutexDummyImpl.I b/dtool/src/dtoolbase/mutexDummyImpl.I index 9c0bbc01d2..94afaa2cc5 100644 --- a/dtool/src/dtoolbase/mutexDummyImpl.I +++ b/dtool/src/dtoolbase/mutexDummyImpl.I @@ -14,28 +14,14 @@ /** * */ -INLINE MutexDummyImpl:: -MutexDummyImpl() { -} - -/** - * - */ -INLINE MutexDummyImpl:: -~MutexDummyImpl() { -} - -/** - * - */ -INLINE void MutexDummyImpl:: +ALWAYS_INLINE void MutexDummyImpl:: acquire() { } /** * */ -INLINE bool MutexDummyImpl:: +ALWAYS_INLINE bool MutexDummyImpl:: try_acquire() { return true; } @@ -43,6 +29,6 @@ try_acquire() { /** * */ -INLINE void MutexDummyImpl:: +ALWAYS_INLINE void MutexDummyImpl:: release() { } diff --git a/dtool/src/dtoolbase/mutexDummyImpl.h b/dtool/src/dtoolbase/mutexDummyImpl.h index 703fb81803..d88eaed1aa 100644 --- a/dtool/src/dtoolbase/mutexDummyImpl.h +++ b/dtool/src/dtoolbase/mutexDummyImpl.h @@ -23,12 +23,16 @@ */ class EXPCL_DTOOL MutexDummyImpl { public: - INLINE MutexDummyImpl(); - INLINE ~MutexDummyImpl(); + CONSTEXPR MutexDummyImpl() DEFAULT_CTOR; - INLINE void acquire(); - INLINE bool try_acquire(); - INLINE void release(); +private: + MutexDummyImpl(const MutexDummyImpl ©) DELETED; + MutexDummyImpl &operator = (const MutexDummyImpl ©) DELETED_ASSIGN; + +public: + ALWAYS_INLINE void acquire(); + ALWAYS_INLINE bool try_acquire(); + ALWAYS_INLINE void release(); }; #include "mutexDummyImpl.I" diff --git a/dtool/src/dtoolbase/mutexPosixImpl.I b/dtool/src/dtoolbase/mutexPosixImpl.I index 9b86139b41..05acfae1c2 100644 --- a/dtool/src/dtoolbase/mutexPosixImpl.I +++ b/dtool/src/dtoolbase/mutexPosixImpl.I @@ -14,16 +14,8 @@ /** * */ -INLINE MutexPosixImpl:: -MutexPosixImpl() { - TAU_PROFILE("MutexPosixImpl::MutexPosixImpl", " ", TAU_USER); - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - // The symbol PTHREAD_MUTEX_DEFAULT isn't always available? - // pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT); - int result = pthread_mutex_init(&_lock, &attr); - pthread_mutexattr_destroy(&attr); - assert(result == 0); +CONSTEXPR MutexPosixImpl:: +MutexPosixImpl() NOEXCEPT : _lock(PTHREAD_MUTEX_INITIALIZER) { } /** @@ -78,6 +70,11 @@ get_posix_lock() { /** * */ +#ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP +CONSTEXPR ReMutexPosixImpl:: +ReMutexPosixImpl() NOEXCEPT : _lock(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) { +} +#else INLINE ReMutexPosixImpl:: ReMutexPosixImpl() { TAU_PROFILE("ReMutexPosixImpl::ReMutexPosixImpl", " ", TAU_USER); @@ -88,6 +85,7 @@ ReMutexPosixImpl() { pthread_mutexattr_destroy(&attr); assert(result == 0); } +#endif /** * diff --git a/dtool/src/dtoolbase/mutexPosixImpl.h b/dtool/src/dtoolbase/mutexPosixImpl.h index e4f684de52..4d4fb3c11b 100644 --- a/dtool/src/dtoolbase/mutexPosixImpl.h +++ b/dtool/src/dtoolbase/mutexPosixImpl.h @@ -28,9 +28,14 @@ */ class EXPCL_DTOOL MutexPosixImpl { public: - INLINE MutexPosixImpl(); + CONSTEXPR MutexPosixImpl() NOEXCEPT; INLINE ~MutexPosixImpl(); +private: + MutexPosixImpl(const MutexPosixImpl ©) DELETED; + MutexPosixImpl &operator = (const MutexPosixImpl ©) DELETED_ASSIGN; + +public: INLINE void acquire(); INLINE bool try_acquire(); INLINE void release(); @@ -47,9 +52,18 @@ private: */ class EXPCL_DTOOL ReMutexPosixImpl { public: +#ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP + CONSTEXPR ReMutexPosixImpl() NOEXCEPT; +#else INLINE ReMutexPosixImpl(); +#endif INLINE ~ReMutexPosixImpl(); +private: + ReMutexPosixImpl(const ReMutexPosixImpl ©) DELETED; + ReMutexPosixImpl &operator = (const ReMutexPosixImpl ©) DELETED; + +public: INLINE void acquire(); INLINE bool try_acquire(); INLINE void release(); diff --git a/dtool/src/dtoolbase/mutexSpinlockImpl.I b/dtool/src/dtoolbase/mutexSpinlockImpl.I index 5b62bf4f55..9fca1e9bf6 100644 --- a/dtool/src/dtoolbase/mutexSpinlockImpl.I +++ b/dtool/src/dtoolbase/mutexSpinlockImpl.I @@ -14,16 +14,8 @@ /** * */ -INLINE MutexSpinlockImpl:: -MutexSpinlockImpl() { - _lock = 0; -} - -/** - * - */ -INLINE MutexSpinlockImpl:: -~MutexSpinlockImpl() { +CONSTEXPR MutexSpinlockImpl:: +MutexSpinlockImpl() : _lock(0) { } /** diff --git a/dtool/src/dtoolbase/mutexSpinlockImpl.h b/dtool/src/dtoolbase/mutexSpinlockImpl.h index 19a8d1802c..06853a1af9 100644 --- a/dtool/src/dtoolbase/mutexSpinlockImpl.h +++ b/dtool/src/dtoolbase/mutexSpinlockImpl.h @@ -29,9 +29,13 @@ */ class EXPCL_DTOOL MutexSpinlockImpl { public: - INLINE MutexSpinlockImpl(); - INLINE ~MutexSpinlockImpl(); + CONSTEXPR MutexSpinlockImpl(); +private: + MutexSpinlockImpl(const MutexSpinlockImpl ©) DELETED; + MutexSpinlockImpl &operator = (const MutexSpinlockImpl ©) DELETED_ASSIGN; + +public: INLINE void acquire(); INLINE bool try_acquire(); INLINE void release(); diff --git a/dtool/src/dtoolbase/mutexWin32Impl.h b/dtool/src/dtoolbase/mutexWin32Impl.h index 0407d5a883..31a4539e01 100644 --- a/dtool/src/dtoolbase/mutexWin32Impl.h +++ b/dtool/src/dtoolbase/mutexWin32Impl.h @@ -31,6 +31,11 @@ public: MutexWin32Impl(); INLINE ~MutexWin32Impl(); +private: + MutexWin32Impl(const MutexWin32Impl ©) DELETED; + MutexWin32Impl &operator = (const MutexWin32Impl ©) DELETED_ASSIGN; + +public: INLINE void acquire(); INLINE bool try_acquire(); INLINE void release(); diff --git a/dtool/src/dtoolbase/pallocator.T b/dtool/src/dtoolbase/pallocator.T index b8a2fe73be..e9bfd3f98e 100644 --- a/dtool/src/dtoolbase/pallocator.T +++ b/dtool/src/dtoolbase/pallocator.T @@ -1,31 +1,31 @@ -// Filename: pallocator.T -// Created by: drose (05Jun01) -// -//////////////////////////////////////////////////////////////////// -// -// 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." -// -//////////////////////////////////////////////////////////////////// +/** + * 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 pallocator.T + * @author drose + * @date 2001-06-05 + */ template INLINE pallocator_single:: -pallocator_single(TypeHandle type_handle) throw() : +pallocator_single(TypeHandle type_handle) NOEXCEPT : _type_handle(type_handle) { } template -INLINE TYPENAME pallocator_single::pointer pallocator_single:: +INLINE Type *pallocator_single:: allocate(TYPENAME pallocator_single::size_type n, TYPENAME allocator::const_pointer) { TAU_PROFILE("pallocator_single:allocate()", " ", TAU_USER); // This doesn't support allocating arrays. assert(n == 1); - return StaticDeletedChain::allocate(sizeof(Type), _type_handle); + return (Type *)ASSUME_ALIGNED(StaticDeletedChain::allocate(sizeof(Type), _type_handle), + MEMORY_HOOK_ALIGNMENT); } template @@ -37,41 +37,20 @@ deallocate(TYPENAME pallocator_single::pointer p, TYPENAME pallocator_sing template INLINE pallocator_array:: -pallocator_array(TypeHandle type_handle) throw() : +pallocator_array(TypeHandle type_handle) NOEXCEPT : _type_handle(type_handle) { } template -INLINE TYPENAME pallocator_array::pointer pallocator_array:: +INLINE Type *pallocator_array:: allocate(TYPENAME pallocator_array::size_type n, TYPENAME allocator::const_pointer) { - TAU_PROFILE("pallocator_array:allocate()", " ", TAU_USER); -#ifdef DO_MEMORY_USAGE - const size_t header_reserved_bytes = MemoryHook::get_header_reserved_bytes(); - size_t alloc_size = n * sizeof(Type); - // We also need to store the total number of bytes we allocated. - alloc_size += header_reserved_bytes; - _type_handle.inc_memory_usage(TypeHandle::MC_array, alloc_size); - void *ptr = (TYPENAME pallocator_array::pointer)PANDA_MALLOC_ARRAY(alloc_size); - *((size_t *)ptr) = alloc_size; - return (TYPENAME pallocator_array::pointer)(((char *)ptr) + header_reserved_bytes); -#else - return (TYPENAME pallocator_array::pointer)PANDA_MALLOC_ARRAY(n * sizeof(Type)); -#endif // DO_MEMORY_USAGE + return (TYPENAME pallocator_array::pointer) + ASSUME_ALIGNED(_type_handle.allocate_array(n * sizeof(Type)), MEMORY_HOOK_ALIGNMENT); } template INLINE void pallocator_array:: deallocate(TYPENAME pallocator_array::pointer p, TYPENAME pallocator_array::size_type) { - TAU_PROFILE("pallocator_array:deallocate()", " ", TAU_USER); -#ifdef DO_MEMORY_USAGE - // Now we need to recover the total number of bytes. - const size_t header_reserved_bytes = MemoryHook::get_header_reserved_bytes(); - void *ptr = (void *)((char *)p - header_reserved_bytes); - size_t alloc_size = *((size_t *)ptr); - _type_handle.dec_memory_usage(TypeHandle::MC_array, alloc_size); - PANDA_FREE_ARRAY(ptr); -#else - PANDA_FREE_ARRAY(p); -#endif // DO_MEMORY_USAGE + _type_handle.deallocate_array((void *)p); } diff --git a/dtool/src/dtoolbase/pallocator.h b/dtool/src/dtoolbase/pallocator.h index deddc2abfc..3c91b6cc7e 100644 --- a/dtool/src/dtoolbase/pallocator.h +++ b/dtool/src/dtoolbase/pallocator.h @@ -52,14 +52,15 @@ public: typedef TYPENAME allocator::const_reference const_reference; typedef TYPENAME allocator::size_type size_type; - INLINE pallocator_single(TypeHandle type_handle) throw(); + INLINE pallocator_single(TypeHandle type_handle) NOEXCEPT; // template member functions in VC++ can only be defined in-class. template - INLINE pallocator_single(const pallocator_single ©) throw() : + INLINE pallocator_single(const pallocator_single ©) NOEXCEPT : _type_handle(copy._type_handle) { } - INLINE pointer allocate(size_type n, allocator::const_pointer hint = 0); + INLINE Type *allocate(size_type n, allocator::const_pointer hint = 0) + RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT); INLINE void deallocate(pointer p, size_type n); template struct rebind { @@ -80,14 +81,15 @@ public: typedef TYPENAME allocator::const_reference const_reference; typedef TYPENAME allocator::size_type size_type; - INLINE pallocator_array(TypeHandle type_handle = TypeHandle::none()) throw(); + INLINE pallocator_array(TypeHandle type_handle = TypeHandle::none()) NOEXCEPT; // template member functions in VC++ can only be defined in-class. template - INLINE pallocator_array(const pallocator_array ©) throw() : + INLINE pallocator_array(const pallocator_array ©) NOEXCEPT : _type_handle(copy._type_handle) { } - INLINE pointer allocate(size_type n, allocator::const_pointer hint = 0); + INLINE Type *allocate(size_type n, allocator::const_pointer hint = 0) + RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT); INLINE void deallocate(pointer p, size_type n); template struct rebind { diff --git a/dtool/src/dtoolbase/pdeque.h b/dtool/src/dtoolbase/pdeque.h index 245f7e299a..96cf319e77 100644 --- a/dtool/src/dtoolbase/pdeque.h +++ b/dtool/src/dtoolbase/pdeque.h @@ -37,7 +37,6 @@ public: typedef pallocator_array allocator; typedef TYPENAME deque::size_type size_type; pdeque(TypeHandle type_handle = pdeque_type_handle) : deque >(allocator(type_handle)) { } - pdeque(const pdeque ©) : deque >(copy) { } pdeque(size_type n, TypeHandle type_handle = pdeque_type_handle) : deque >(n, Type(), allocator(type_handle)) { } pdeque(size_type n, const Type &value, TypeHandle type_handle = pdeque_type_handle) : deque >(n, value, allocator(type_handle)) { } }; diff --git a/dtool/src/dtoolbase/plist.h b/dtool/src/dtoolbase/plist.h index 9effd8db6a..bbb9e55089 100644 --- a/dtool/src/dtoolbase/plist.h +++ b/dtool/src/dtoolbase/plist.h @@ -38,7 +38,6 @@ public: typedef list base_class; typedef TYPENAME base_class::size_type size_type; plist(TypeHandle type_handle = plist_type_handle) : base_class(allocator(type_handle)) { } - plist(const plist ©) : base_class(copy) { } plist(size_type n, TypeHandle type_handle = plist_type_handle) : base_class(n, Type(), allocator(type_handle)) { } plist(size_type n, const Type &value, TypeHandle type_handle = plist_type_handle) : base_class(n, value, allocator(type_handle)) { } diff --git a/dtool/src/dtoolbase/pmap.h b/dtool/src/dtoolbase/pmap.h index e9b318c36b..e115191b45 100644 --- a/dtool/src/dtoolbase/pmap.h +++ b/dtool/src/dtoolbase/pmap.h @@ -52,7 +52,6 @@ public: typedef map base_class; pmap(TypeHandle type_handle = pmap_type_handle) : base_class(Compare(), allocator(type_handle)) { } - pmap(const pmap ©) : base_class(copy) { } pmap(const Compare &comp, TypeHandle type_handle = pmap_type_handle) : base_class(comp, allocator(type_handle)) { } #ifdef USE_TAU @@ -118,7 +117,6 @@ class pmultimap : public multimap > allocator; pmultimap(TypeHandle type_handle = pmap_type_handle) : multimap(Compare(), allocator(type_handle)) { } - pmultimap(const pmultimap ©) : multimap(copy) { } pmultimap(const Compare &comp, TypeHandle type_handle = pmap_type_handle) : multimap(comp, allocator(type_handle)) { } }; @@ -132,7 +130,6 @@ template > > class phash_map : public stdext::hash_map > > { public: phash_map() : stdext::hash_map > >() { } - phash_map(const phash_map ©) : stdext::hash_map > >(copy) { } phash_map(const Compare &comp) : stdext::hash_map > >(comp) { } }; @@ -145,7 +142,6 @@ template > > class phash_multimap : public stdext::hash_multimap > > { public: phash_multimap() : stdext::hash_multimap > >() { } - phash_multimap(const phash_multimap ©) : stdext::hash_multimap > >(copy) { } phash_multimap(const Compare &comp) : stdext::hash_multimap > >(comp) { } }; diff --git a/dtool/src/dtoolbase/pset.h b/dtool/src/dtoolbase/pset.h index 4b1ca1617c..b7990ad671 100644 --- a/dtool/src/dtoolbase/pset.h +++ b/dtool/src/dtoolbase/pset.h @@ -51,7 +51,6 @@ public: typedef pallocator_single allocator; typedef set base_class; pset(TypeHandle type_handle = pset_type_handle) : base_class(Compare(), allocator(type_handle)) { } - pset(const pset ©) : base_class(copy) { } pset(const Compare &comp, TypeHandle type_handle = pset_type_handle) : base_class(comp, type_handle) { } #ifdef USE_TAU @@ -110,7 +109,6 @@ class pmultiset : public multiset > { public: typedef pallocator_single allocator; pmultiset(TypeHandle type_handle = pset_type_handle) : multiset(Compare(), allocator(type_handle)) { } - pmultiset(const pmultiset ©) : multiset(copy) { } pmultiset(const Compare &comp, TypeHandle type_handle = pset_type_handle) : multiset(comp, type_handle) { } }; @@ -124,7 +122,6 @@ template > > class phash_set : public stdext::hash_set > { public: phash_set() : stdext::hash_set >() { } - phash_set(const phash_set ©) : stdext::hash_set >(copy) { } phash_set(const Compare &comp) : stdext::hash_set >(comp) { } }; @@ -137,7 +134,6 @@ template > > class phash_multiset : public stdext::hash_multiset > { public: phash_multiset() : stdext::hash_multiset >() { } - phash_multiset(const phash_multiset ©) : stdext::hash_multiset >(copy) { } phash_multiset(const Compare &comp) : stdext::hash_multiset >(comp) { } }; diff --git a/dtool/src/dtoolbase/pvector.h b/dtool/src/dtoolbase/pvector.h index 14bd542f37..b146947543 100644 --- a/dtool/src/dtoolbase/pvector.h +++ b/dtool/src/dtoolbase/pvector.h @@ -46,24 +46,9 @@ public: typedef TYPENAME base_class::size_type size_type; explicit pvector(TypeHandle type_handle = pvector_type_handle) : base_class(allocator(type_handle)) { } - pvector(const pvector ©) : base_class(copy) { } explicit pvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator(type_handle)) { } explicit pvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator(type_handle)) { } pvector(const Type *begin, const Type *end, TypeHandle type_handle = pvector_type_handle) : base_class(begin, end, allocator(type_handle)) { } - -#ifdef USE_MOVE_SEMANTICS - pvector(pvector &&from) NOEXCEPT : base_class(move(from)) {}; - - pvector &operator =(pvector &&from) NOEXCEPT { - base_class::operator =(move(from)); - return *this; - } -#endif - - pvector &operator =(const pvector ©) { - base_class::operator =(copy); - return *this; - } }; #endif // USE_STL_ALLOCATOR diff --git a/dtool/src/dtoolbase/typeHandle.N b/dtool/src/dtoolbase/typeHandle.N new file mode 100644 index 0000000000..9cd1d579b0 --- /dev/null +++ b/dtool/src/dtoolbase/typeHandle.N @@ -0,0 +1 @@ +defconstruct TypeHandle TypeHandle(TypeHandle::none()) diff --git a/dtool/src/dtoolbase/typeHandle.cxx b/dtool/src/dtoolbase/typeHandle.cxx index ea3a51d6a1..ed85da8d03 100644 --- a/dtool/src/dtoolbase/typeHandle.cxx +++ b/dtool/src/dtoolbase/typeHandle.cxx @@ -18,7 +18,6 @@ // This is initialized to zero by static initialization. TypeHandle TypeHandle::_none; -#ifdef DO_MEMORY_USAGE /** * Returns the total allocated memory used by objects of this type, for the * indicated memory class. This is only updated if track-memory-usage is set @@ -26,6 +25,7 @@ TypeHandle TypeHandle::_none; */ size_t TypeHandle:: get_memory_usage(MemoryClass memory_class) const { +#ifdef DO_MEMORY_USAGE assert((int)memory_class >= 0 && (int)memory_class < (int)MC_limit); if ((*this) == TypeHandle::none()) { return 0; @@ -34,17 +34,20 @@ get_memory_usage(MemoryClass memory_class) const { assert(rnode != (TypeRegistryNode *)NULL); return (size_t)AtomicAdjust::get(rnode->_memory_usage[memory_class]); } -} #endif // DO_MEMORY_USAGE + return 0; +} -#ifdef DO_MEMORY_USAGE /** * Adds the indicated amount to the record for the total allocated memory for * objects of this type. */ void TypeHandle:: inc_memory_usage(MemoryClass memory_class, size_t size) { +#ifdef DO_MEMORY_USAGE +#ifdef _DEBUG assert((int)memory_class >= 0 && (int)memory_class < (int)MC_limit); +#endif if ((*this) != TypeHandle::none()) { TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL); assert(rnode != (TypeRegistryNode *)NULL); @@ -52,21 +55,23 @@ inc_memory_usage(MemoryClass memory_class, size_t size) { // cerr << *this << ".inc(" << memory_class << ", " << size << ") -> " << // rnode->_memory_usage[memory_class] << "\n"; if (rnode->_memory_usage[memory_class] < 0) { - cerr << "Memory usage overflow for type " << *this << ".\n"; + cerr << "Memory usage overflow for type " << rnode->_name << ".\n"; abort(); } } -} #endif // DO_MEMORY_USAGE +} -#ifdef DO_MEMORY_USAGE /** * Subtracts the indicated amount from the record for the total allocated * memory for objects of this type. */ void TypeHandle:: dec_memory_usage(MemoryClass memory_class, size_t size) { +#ifdef DO_MEMORY_USAGE +#ifdef _DEBUG assert((int)memory_class >= 0 && (int)memory_class < (int)MC_limit); +#endif if ((*this) != TypeHandle::none()) { TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL); assert(rnode != (TypeRegistryNode *)NULL); @@ -75,8 +80,81 @@ dec_memory_usage(MemoryClass memory_class, size_t size) { // rnode->_memory_usage[memory_class] << "\n"; assert(rnode->_memory_usage[memory_class] >= 0); } -} #endif // DO_MEMORY_USAGE +} + +/** + * Allocates memory, adding it to the total amount of memory allocated for + * this type. + */ +void *TypeHandle:: +allocate_array(size_t size) { + TAU_PROFILE("TypeHandle:allocate_array()", " ", TAU_USER); + + void *ptr = PANDA_MALLOC_ARRAY(size); +#ifdef DO_MEMORY_USAGE + if ((*this) != TypeHandle::none()) { + size_t alloc_size = MemoryHook::get_ptr_size(ptr); +#ifdef _DEBUG + assert(size <= alloc_size); +#endif + TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL); + assert(rnode != (TypeRegistryNode *)NULL); + AtomicAdjust::add(rnode->_memory_usage[MC_array], (AtomicAdjust::Integer)alloc_size); + if (rnode->_memory_usage[MC_array] < 0) { + cerr << "Memory usage overflow for type " << rnode->_name << ".\n"; + abort(); + } + } +#endif // DO_MEMORY_USAGE + return ptr; +} + +/** + * Reallocates memory, adjusting the total amount of memory allocated for this + * type. + */ +void *TypeHandle:: +reallocate_array(void *old_ptr, size_t size) { + TAU_PROFILE("TypeHandle:reallocate_array()", " ", TAU_USER); + +#ifdef DO_MEMORY_USAGE + size_t old_size = MemoryHook::get_ptr_size(old_ptr); + void *new_ptr = PANDA_REALLOC_ARRAY(old_ptr, size); + + if ((*this) != TypeHandle::none()) { + size_t new_size = MemoryHook::get_ptr_size(new_ptr); + + TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL); + assert(rnode != (TypeRegistryNode *)NULL); + AtomicAdjust::add(rnode->_memory_usage[MC_array], (AtomicAdjust::Integer)new_size - (AtomicAdjust::Integer)old_size); + assert(rnode->_memory_usage[MC_array] >= 0); + } +#else + void *new_ptr = PANDA_REALLOC_ARRAY(old_ptr, size); +#endif + return new_ptr; +} + +/** + * Deallocates memory, subtracting it from the total amount of memory + * allocated for this type. + */ +void TypeHandle:: +deallocate_array(void *ptr) { + TAU_PROFILE("TypeHandle:deallocate_array()", " ", TAU_USER); + +#ifdef DO_MEMORY_USAGE + size_t alloc_size = MemoryHook::get_ptr_size(ptr); + if ((*this) != TypeHandle::none()) { + TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL); + assert(rnode != (TypeRegistryNode *)NULL); + AtomicAdjust::add(rnode->_memory_usage[MC_array], -(AtomicAdjust::Integer)alloc_size); + assert(rnode->_memory_usage[MC_array] >= 0); + } +#endif // DO_MEMORY_USAGE + PANDA_FREE_ARRAY(ptr); +} /** * Return the Index of the BEst fit Classs from a set diff --git a/dtool/src/dtoolbase/typeHandle.h b/dtool/src/dtoolbase/typeHandle.h index fe611c2fd1..f770b1810b 100644 --- a/dtool/src/dtoolbase/typeHandle.h +++ b/dtool/src/dtoolbase/typeHandle.h @@ -18,25 +18,49 @@ #include -// The following illustrates the convention for declaring a type that uses -// TypeHandle. In this example, ThisThingie inherits from TypedObject, which -// automatically supplies some type-differentiation functions at the cost of -// one virtual function, get_type(); however, this inheritance is optional, -// and may be omitted to avoid the virtual function pointer overhead. (If you -// do use TypedObject, be sure to consider whether your destructor should also -// be virtual.) - -/* - * class ThatThingie : public SimpleTypedObject { public: static TypeHandle - * get_class_type() { return _type_handle; } static void init_type() { - * register_type(_type_handle, "ThatThingie"); } private: static TypeHandle - * _type_handle; }; class ThisThingie : public ThatThingie, publid TypedObject - * { public: static TypeHandle get_class_type() { return _type_handle; } - * static void init_type() { ThatThingie::init_type(); - * TypedObject::init_type(); register_type(_type_handle, "ThisThingie", - * ThatThingie::get_class_type(), TypedObject::get_class_type()); } virtual - * TypeHandle get_type() const { return get_class_type(); } private: static - * TypeHandle _type_handle; }; +/** + * The following illustrates the convention for declaring a type that uses + * TypeHandle. In this example, ThisThingie inherits from TypedObject, which + * automatically supplies some type-differentiation functions at the cost of + * one virtual function, get_type(); however, this inheritance is optional, + * and may be omitted to avoid the virtual function pointer overhead. (If you + * do use TypedObject, be sure to consider whether your destructor should also + * be virtual.) + * + * @code + * class ThatThingie : public SimpleTypedObject { + * public: + * static TypeHandle get_class_type() { + * return _type_handle; + * } + * static void init_type() { + * register_type(_type_handle, "ThatThingie"); + * } + * + * private: + * static TypeHandle _type_handle; + * }; + * + * class ThisThingie : public ThatThingie, publid TypedObject { + * public: + * static TypeHandle get_class_type() { + * return _type_handle; + * } + * static void init_type() { + * ThatThingie::init_type(); + * TypedObject::init_type(); + * register_type(_type_handle, "ThisThingie", + * ThatThingie::get_class_type(), + * TypedObject::get_class_type()); + * } + * virtual TypeHandle get_type() const { + * return get_class_type(); + * } + * + * private: + * static TypeHandle _type_handle; + * }; + * @endcode */ class TypedObject; @@ -97,15 +121,9 @@ PUBLISHED: int get_best_parent_from_Set(const std::set< int > &legal_vals) const; -#ifdef DO_MEMORY_USAGE size_t get_memory_usage(MemoryClass memory_class) const; void inc_memory_usage(MemoryClass memory_class, size_t size); void dec_memory_usage(MemoryClass memory_class, size_t size); -#else - static CONSTEXPR size_t get_memory_usage(MemoryClass) { return 0; } - INLINE void inc_memory_usage(MemoryClass, size_t) { } - INLINE void dec_memory_usage(MemoryClass, size_t) { } -#endif // DO_MEMORY_USAGE INLINE int get_index() const; INLINE void output(ostream &out) const; @@ -118,6 +136,10 @@ PUBLISHED: MAKE_SEQ_PROPERTY(child_classes, get_num_child_classes, get_child_class); public: + void *allocate_array(size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT); + void *reallocate_array(void *ptr, size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT); + void deallocate_array(void *ptr); + INLINE static TypeHandle from_index(int index); private: diff --git a/panda/src/express/typeHandle_ext.cxx b/dtool/src/dtoolbase/typeHandle_ext.cxx similarity index 100% rename from panda/src/express/typeHandle_ext.cxx rename to dtool/src/dtoolbase/typeHandle_ext.cxx diff --git a/panda/src/express/typeHandle_ext.h b/dtool/src/dtoolbase/typeHandle_ext.h similarity index 100% rename from panda/src/express/typeHandle_ext.h rename to dtool/src/dtoolbase/typeHandle_ext.h diff --git a/dtool/src/dtoolbase/typeRegistry.I b/dtool/src/dtoolbase/typeRegistry.I index f0105c1eda..e35f37c207 100644 --- a/dtool/src/dtoolbase/typeRegistry.I +++ b/dtool/src/dtoolbase/typeRegistry.I @@ -23,6 +23,19 @@ freshen_derivations() { } } +/** + * Returns the pointer to the global TypeRegistry object. + */ +INLINE TypeRegistry *TypeRegistry:: +ptr() { + // It's OK that we don't acquire the lock, because we guarantee that this is + // called at static init time. + if (_global_pointer == NULL) { + init_global_pointer(); + } + return _global_pointer; +} + /** * Ensures the lock pointer has been allocated. */ diff --git a/dtool/src/dtoolbase/typeRegistry.cxx b/dtool/src/dtoolbase/typeRegistry.cxx index 7349d20dd4..01b8ec498a 100644 --- a/dtool/src/dtoolbase/typeRegistry.cxx +++ b/dtool/src/dtoolbase/typeRegistry.cxx @@ -488,20 +488,6 @@ write(ostream &out) const { _lock->release(); } -/** - * Returns the pointer to the global TypeRegistry object. - */ -TypeRegistry *TypeRegistry:: -ptr() { - init_lock(); - _lock->acquire(); - if (_global_pointer == NULL) { - init_global_pointer(); - } - _lock->release(); - return _global_pointer; -} - /** * */ @@ -531,6 +517,7 @@ TypeRegistry() { */ void TypeRegistry:: init_global_pointer() { + init_lock(); init_memory_hook(); _global_pointer = new TypeRegistry; } diff --git a/dtool/src/dtoolbase/typeRegistry.h b/dtool/src/dtoolbase/typeRegistry.h index 828cee6c8c..651eb1904b 100644 --- a/dtool/src/dtoolbase/typeRegistry.h +++ b/dtool/src/dtoolbase/typeRegistry.h @@ -77,7 +77,7 @@ PUBLISHED: void write(ostream &out) const; // ptr() returns the pointer to the global TypeRegistry object. - static TypeRegistry *ptr(); + static INLINE TypeRegistry *ptr(); MAKE_SEQ_PROPERTY(typehandles, get_num_typehandles, get_typehandle); MAKE_SEQ_PROPERTY(root_classes, get_num_root_classes, get_root_class); diff --git a/dtool/src/dtoolbase/typeRegistryNode.cxx b/dtool/src/dtoolbase/typeRegistryNode.cxx index cdc45d7beb..a88533a769 100644 --- a/dtool/src/dtoolbase/typeRegistryNode.cxx +++ b/dtool/src/dtoolbase/typeRegistryNode.cxx @@ -26,9 +26,7 @@ TypeRegistryNode(TypeHandle handle, const string &name, TypeHandle &ref) : _handle(handle), _name(name), _ref(ref) { clear_subtree(); -#ifdef DO_MEMORY_USAGE memset(_memory_usage, 0, sizeof(_memory_usage)); -#endif } /** diff --git a/dtool/src/dtoolbase/typeRegistryNode.h b/dtool/src/dtoolbase/typeRegistryNode.h index 7eb64f8a65..46e4b9f5a1 100644 --- a/dtool/src/dtoolbase/typeRegistryNode.h +++ b/dtool/src/dtoolbase/typeRegistryNode.h @@ -47,9 +47,7 @@ public: Classes _parent_classes; Classes _child_classes; -#ifdef DO_MEMORY_USAGE AtomicAdjust::Integer _memory_usage[TypeHandle::MC_limit]; -#endif static bool _paranoid_inheritance; diff --git a/dtool/src/dtoolbase/typedObject.I b/dtool/src/dtoolbase/typedObject.I index 68776b7c13..37bb183781 100644 --- a/dtool/src/dtoolbase/typedObject.I +++ b/dtool/src/dtoolbase/typedObject.I @@ -11,27 +11,6 @@ * @date 2001-05-11 */ -/** - * - */ -INLINE TypedObject:: -TypedObject() { -} - -/** - * - */ -INLINE TypedObject:: -TypedObject(const TypedObject &) { -} - -/** - * - */ -INLINE void TypedObject:: -operator = (const TypedObject &) { -} - /** * Returns the internal index number associated with this object's TypeHandle, * a unique number for each different type. This is equivalent to diff --git a/dtool/src/dtoolbase/typedObject.h b/dtool/src/dtoolbase/typedObject.h index 37b0bd67f4..cc963b0fac 100644 --- a/dtool/src/dtoolbase/typedObject.h +++ b/dtool/src/dtoolbase/typedObject.h @@ -47,29 +47,49 @@ * What follows are some examples that can be used in new classes that you * create. * - * @par In the class definition (.h file): @code public: static TypeHandle - * get_class_type() { return _type_handle; } static void init_type() { - * <<>>::init_type(); <<>>::init_type(); - * <<>>::init_type(); register_type(_type_handle, - * "<<>>", <<>>::get_class_type(), - * <<>>::get_class_type(), <<>>::get_class_type()); - * } virtual TypeHandle get_type() const { return get_class_type(); } virtual - * TypeHandle force_init_type() {init_type(); return get_class_type();} + * @par In the class definition (.h file): + * @code + * public: + * static TypeHandle get_class_type() { + * return _type_handle; + * } + * static void init_type() { + * <<>>::init_type(); + * <<>>::init_type(); + * <<>>::init_type(); + * register_type(_type_handle, "<<>>", + * <<>>::get_class_type(), + * <<>>::get_class_type(), + * <<>>::get_class_type()); + * } + * virtual TypeHandle get_type() const { + * return get_class_type(); + * } + * virtual TypeHandle force_init_type() {init_type(); return get_class_type();} * - * private: static TypeHandle _type_handle; @endcode + * private: + * static TypeHandle _type_handle; + * @endcode * - * @par In the class .cxx file: @code TypeHandle - * <<>>::_type_handle; @endcode + * @par In the class .cxx file: + * @code + * TypeHandle <<>>::_type_handle; + * @endcode * - * @par In the class config_<<>>.cxx file: @code - * ConfigureFn(config_<<>>) { <<>>::init_type(); - * <<>>::init_type(); <<>>::init_type(); } @endcode + * @par In the class config_<<>>.cxx file: + * @code + * ConfigureFn(config_<<>>) { + * <<>>::init_type(); + * <<>>::init_type(); + * <<>>::init_type(); + * } + * @endcode */ class EXPCL_DTOOL TypedObject : public MemoryBase { public: - INLINE TypedObject(); - INLINE TypedObject(const TypedObject ©); - INLINE void operator = (const TypedObject ©); + INLINE TypedObject() DEFAULT_CTOR; + INLINE TypedObject(const TypedObject ©) DEFAULT_CTOR; + INLINE TypedObject &operator = (const TypedObject ©) DEFAULT_ASSIGN; PUBLISHED: // A virtual destructor is just a good idea. @@ -77,6 +97,8 @@ PUBLISHED: // Derived classes should override this function to return get_class_type(). virtual TypeHandle get_type() const=0; + + // Returns the TypeHandle representing this object's type. MAKE_PROPERTY(type, get_type); INLINE int get_type_index() const; diff --git a/dtool/src/dtoolutil/config_dtoolutil.N b/dtool/src/dtoolutil/config_dtoolutil.N new file mode 100644 index 0000000000..2577aa3b24 --- /dev/null +++ b/dtool/src/dtoolutil/config_dtoolutil.N @@ -0,0 +1,9 @@ +forcetype ofstream +forcetype ifstream +forcetype fstream + +forcetype ios_base +forcetype ios +forcetype istream +forcetype ostream +forcetype iostream diff --git a/dtool/src/dtoolutil/config_dtoolutil.h b/dtool/src/dtoolutil/config_dtoolutil.h index 1563b0250f..20e9784c55 100644 --- a/dtool/src/dtoolutil/config_dtoolutil.h +++ b/dtool/src/dtoolutil/config_dtoolutil.h @@ -16,4 +16,7 @@ #include "dtoolbase.h" +// Include this so interrogate can find it. +#include + #endif diff --git a/dtool/src/dtoolutil/executionEnvironment.cxx b/dtool/src/dtoolutil/executionEnvironment.cxx index bcf4584ab0..ad782b521e 100644 --- a/dtool/src/dtoolutil/executionEnvironment.cxx +++ b/dtool/src/dtoolutil/executionEnvironment.cxx @@ -341,6 +341,22 @@ ns_get_environment_variable(const string &var) const { } } +#elif !defined(__APPLE__) + // Similarly, we define fallbacks on POSIX systems for the variables defined + // in the XDG Base Directory specification, so that they can be safely used + // in Config.prc files. + if (var == "XDG_CONFIG_HOME") { + Filename home_dir = Filename::get_home_directory(); + return home_dir.get_fullpath() + "/.config"; + + } else if (var == "XDG_CACHE_HOME") { + Filename home_dir = Filename::get_home_directory(); + return home_dir.get_fullpath() + "/.cache"; + + } else if (var == "XDG_DATA_HOME") { + Filename home_dir = Filename::get_home_directory(); + return home_dir.get_fullpath() + "/.local/share"; + } #endif // _WIN32 return string(); @@ -591,8 +607,8 @@ read_args() { dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map); while (map != NULL) { - char *tail = strrchr(map->l_name, '/'); - char *head = strchr(map->l_name, '/'); + const char *tail = strrchr(map->l_name, '/'); + const char *head = strchr(map->l_name, '/'); if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0 || strcmp(tail, "/libp3dtool.so") == 0)) { _dtool_name = head; @@ -615,8 +631,8 @@ read_args() { char buffer[PATH_MAX]; buffer[0] = 0; maps.getline(buffer, PATH_MAX); - char *tail = strrchr(buffer, '/'); - char *head = strchr(buffer, '/'); + const char *tail = strrchr(buffer, '/'); + const char *head = strchr(buffer, '/'); if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0 || strcmp(tail, "/libp3dtool.so") == 0)) { _dtool_name = head; diff --git a/dtool/src/dtoolutil/executionEnvironment.h b/dtool/src/dtoolutil/executionEnvironment.h index bd1c6e879a..f954a1e44d 100644 --- a/dtool/src/dtoolutil/executionEnvironment.h +++ b/dtool/src/dtoolutil/executionEnvironment.h @@ -51,6 +51,15 @@ PUBLISHED: static Filename get_cwd(); +PUBLISHED: + MAKE_MAP_PROPERTY(environment_variables, has_environment_variable, + get_environment_variable, set_environment_variable); + + MAKE_SEQ_PROPERTY(args, get_num_args, get_arg); + MAKE_PROPERTY(binary_name, get_binary_name, set_binary_name); + MAKE_PROPERTY(dtool_name, get_dtool_name, set_dtool_name); + MAKE_PROPERTY(cwd, get_cwd); + private: bool ns_has_environment_variable(const string &var) const; string ns_get_environment_variable(const string &var) const; diff --git a/dtool/src/dtoolutil/filename.cxx b/dtool/src/dtoolutil/filename.cxx index 169f4bee05..c862c0aabf 100644 --- a/dtool/src/dtoolutil/filename.cxx +++ b/dtool/src/dtoolutil/filename.cxx @@ -600,8 +600,14 @@ get_user_appdata_directory() { user_appdata_directory.set_basename("files"); #else - // Posix case. - user_appdata_directory = get_home_directory(); + // Posix case. We follow the XDG base directory spec. + struct stat st; + const char *datadir = getenv("XDG_DATA_HOME"); + if (datadir != nullptr && stat(datadir, &st) == 0 && S_ISDIR(st.st_mode)) { + user_appdata_directory = datadir; + } else { + user_appdata_directory = Filename(get_home_directory(), ".local/share"); + } #endif // WIN32 @@ -649,9 +655,10 @@ get_common_appdata_directory() { common_appdata_directory.set_dirname(_internal_data_dir); common_appdata_directory.set_basename("files"); +#elif defined(__FreeBSD__) + common_appdata_directory = "/usr/local/share"; #else - // Posix case. - common_appdata_directory = "/var"; + common_appdata_directory = "/usr/share"; #endif // WIN32 if (common_appdata_directory.empty()) { diff --git a/panda/src/express/filename_ext.cxx b/dtool/src/dtoolutil/filename_ext.cxx similarity index 98% rename from panda/src/express/filename_ext.cxx rename to dtool/src/dtoolutil/filename_ext.cxx index 1c9bf78e7e..3d71317786 100644 --- a/panda/src/express/filename_ext.cxx +++ b/dtool/src/dtoolutil/filename_ext.cxx @@ -55,7 +55,7 @@ __init__(PyObject *path) { if (Py_TYPE(path) == &Dtool_Filename._PyType) { // Copy constructor. - (*_this) = *((Filename *)((Dtool_PyInstDef *)path)->_ptr_to_object); + *_this = *(Filename *)DtoolInstance_VOID_PTR(path); return; } diff --git a/panda/src/express/filename_ext.h b/dtool/src/dtoolutil/filename_ext.h similarity index 100% rename from panda/src/express/filename_ext.h rename to dtool/src/dtoolutil/filename_ext.h diff --git a/dtool/src/dtoolutil/globPattern.cxx b/dtool/src/dtoolutil/globPattern.cxx index 2a065e9a86..57a6afdb57 100644 --- a/dtool/src/dtoolutil/globPattern.cxx +++ b/dtool/src/dtoolutil/globPattern.cxx @@ -99,7 +99,7 @@ match_files(vector_string &results, const Filename &cwd) const { pattern = source; } else { pattern = source.substr(0, slash); - suffix = source.substr(slash + 1); + suffix = source.substr(slash); } GlobPattern glob(pattern); @@ -118,11 +118,21 @@ r_match_files(const Filename &prefix, const string &suffix, size_t slash = suffix.find('/'); if (slash == string::npos) { next_pattern = suffix; + } else if (slash + 1 == suffix.size()) { + // If the slash is at the end, we need to keep it, since it indicates that + // we only want to match directories. + next_pattern = suffix.substr(0, slash); + next_suffix = "/"; } else { next_pattern = suffix.substr(0, slash); next_suffix = suffix.substr(slash + 1); } + if (_pattern == "**" && next_pattern == "**") { + // Collapse consecutive globstar patterns. + return r_match_files(prefix, next_suffix, results, cwd); + } + Filename parent_dir; if (prefix.is_local() && !cwd.empty()) { parent_dir = Filename(cwd, prefix); @@ -136,19 +146,24 @@ r_match_files(const Filename &prefix, const string &suffix, if (!has_glob_characters()) { // If there are no special characters in the pattern, it's a literal // match. + Filename fn(parent_dir, _pattern); if (suffix.empty()) { // Time to stop. - Filename single_filename(parent_dir, _pattern); - if (single_filename.exists()) { + if (fn.exists()) { results.push_back(Filename(prefix, _pattern)); return 1; } return 0; + } else if (fn.is_directory()) { + // If the pattern ends with a slash, match a directory only. + if (suffix == "/") { + results.push_back(Filename(prefix, _pattern + "/")); + return 1; + } else { + return next_glob.r_match_files(Filename(prefix, _pattern), + next_suffix, results, cwd); + } } - - return next_glob.r_match_files(Filename(prefix, _pattern), - next_suffix, results, cwd); - } // If there *are* special glob characters, we must attempt to match the @@ -164,18 +179,44 @@ r_match_files(const Filename &prefix, const string &suffix, // the pattern. int num_matched = 0; + // A globstar pattern matches zero or more directories. + if (_pattern == "**") { + // Try to match this directory (as if the globstar wasn't there) + if (suffix.empty()) { + // This is a directory. Add it. + results.push_back(Filename(prefix)); + num_matched++; + } else if (suffix == "/") { + // Keep the trailing slash, but be sure not to duplicate it. + results.push_back(Filename(prefix, "")); + num_matched++; + } else { + num_matched += next_glob.r_match_files(prefix, next_suffix, results, cwd); + } + next_suffix = suffix; + next_glob = *this; + } + vector_string::const_iterator fi; for (fi = dir_files.begin(); fi != dir_files.end(); ++fi) { const string &local_file = (*fi); if (_pattern[0] == '.' || (local_file.empty() || local_file[0] != '.')) { if (matches(local_file)) { // We have a match; continue. - if (suffix.empty()) { + if (Filename(parent_dir, local_file).is_directory()) { + if (suffix.empty() && _pattern != "**") { + results.push_back(Filename(prefix, local_file)); + num_matched++; + } else if (suffix == "/" && _pattern != "**") { + results.push_back(Filename(prefix, local_file + "/")); + num_matched++; + } else { + num_matched += next_glob.r_match_files(Filename(prefix, local_file), + next_suffix, results, cwd); + } + } else if (suffix.empty()) { results.push_back(Filename(prefix, local_file)); num_matched++; - } else { - num_matched += next_glob.r_match_files(Filename(prefix, local_file), - next_suffix, results, cwd); } } } diff --git a/panda/src/express/globPattern_ext.cxx b/dtool/src/dtoolutil/globPattern_ext.cxx similarity index 100% rename from panda/src/express/globPattern_ext.cxx rename to dtool/src/dtoolutil/globPattern_ext.cxx diff --git a/panda/src/express/globPattern_ext.h b/dtool/src/dtoolutil/globPattern_ext.h similarity index 100% rename from panda/src/express/globPattern_ext.h rename to dtool/src/dtoolutil/globPattern_ext.h diff --git a/dtool/src/dtoolutil/load_dso.cxx b/dtool/src/dtoolutil/load_dso.cxx index ae224d3617..ba63eee044 100644 --- a/dtool/src/dtoolutil/load_dso.cxx +++ b/dtool/src/dtoolutil/load_dso.cxx @@ -47,20 +47,20 @@ load_dso(const DSearchPath &path, const Filename &filename) { if (!abspath.is_regular_file()) { return NULL; } - string os_specific = abspath.to_os_specific(); + wstring os_specific_w = abspath.to_os_specific_w(); // Try using LoadLibraryEx, if possible. - typedef HMODULE (WINAPI *tLoadLibraryEx)(LPCTSTR, HANDLE, DWORD); + typedef HMODULE (WINAPI *tLoadLibraryEx)(LPCWSTR, HANDLE, DWORD); tLoadLibraryEx pLoadLibraryEx; HINSTANCE hLib = LoadLibrary("kernel32.dll"); if (hLib) { - pLoadLibraryEx = (tLoadLibraryEx)GetProcAddress(hLib, "LoadLibraryExA"); + pLoadLibraryEx = (tLoadLibraryEx)GetProcAddress(hLib, "LoadLibraryExW"); if (pLoadLibraryEx) { - return pLoadLibraryEx(os_specific.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + return pLoadLibraryEx(os_specific_w.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH); } } - return LoadLibrary(os_specific.c_str()); + return LoadLibraryW(os_specific_w.c_str()); } bool diff --git a/dtool/src/dtoolutil/p3dtoolutil_ext_composite.cxx b/dtool/src/dtoolutil/p3dtoolutil_ext_composite.cxx new file mode 100644 index 0000000000..89a0ebcc30 --- /dev/null +++ b/dtool/src/dtoolutil/p3dtoolutil_ext_composite.cxx @@ -0,0 +1,2 @@ +#include "filename_ext.cxx" +#include "globPattern_ext.cxx" diff --git a/dtool/src/dtoolutil/pandaFileStream.h b/dtool/src/dtoolutil/pandaFileStream.h index bd943bd11a..f5c7e6f766 100644 --- a/dtool/src/dtoolutil/pandaFileStream.h +++ b/dtool/src/dtoolutil/pandaFileStream.h @@ -29,7 +29,7 @@ class EXPCL_DTOOL IFileStream : public istream { PUBLISHED: INLINE IFileStream(); - INLINE IFileStream(const char *filename, ios::openmode mode = ios::in); + INLINE explicit IFileStream(const char *filename, ios::openmode mode = ios::in); INLINE ~IFileStream(); INLINE void open(const char *filename, ios::openmode mode = ios::in); @@ -57,7 +57,7 @@ private: class EXPCL_DTOOL OFileStream : public ostream { PUBLISHED: INLINE OFileStream(); - INLINE OFileStream(const char *filename, ios::openmode mode = ios::out); + INLINE explicit OFileStream(const char *filename, ios::openmode mode = ios::out); INLINE ~OFileStream(); INLINE void open(const char *filename, ios::openmode mode = ios::out); @@ -86,7 +86,7 @@ private: class EXPCL_DTOOL FileStream : public iostream { PUBLISHED: INLINE FileStream(); - INLINE FileStream(const char *filename, ios::openmode mode = ios::in); + INLINE explicit FileStream(const char *filename, ios::openmode mode = ios::in); INLINE ~FileStream(); INLINE void open(const char *filename, ios::openmode mode = ios::in); diff --git a/dtool/src/dtoolutil/pandaSystem.cxx b/dtool/src/dtoolutil/pandaSystem.cxx index cef04eb37b..79e5aa7762 100644 --- a/dtool/src/dtoolutil/pandaSystem.cxx +++ b/dtool/src/dtoolutil/pandaSystem.cxx @@ -45,6 +45,11 @@ PandaSystem() : #else set_system_tag("eigen", "vectorize", "0"); #endif +#ifdef __AVX__ + set_system_tag("eigen", "avx", "1"); +#else + set_system_tag("eigen", "avx", "0"); +#endif #endif // HAVE_EIGEN #ifdef USE_MEMORY_DLMALLOC @@ -189,6 +194,14 @@ is_official_version() { #endif } +/** + * Returns the memory alignment that Panda's allocators are using. + */ +int PandaSystem:: +get_memory_alignment() { + return MEMORY_HOOK_ALIGNMENT; +} + /** * Returns the string defined by the distributor of this version of Panda, or * "homebuilt" if this version was built directly from the sources by the end- diff --git a/dtool/src/dtoolutil/pandaSystem.h b/dtool/src/dtoolutil/pandaSystem.h index d88e3b92a3..85c1a0ea38 100644 --- a/dtool/src/dtoolutil/pandaSystem.h +++ b/dtool/src/dtoolutil/pandaSystem.h @@ -39,6 +39,8 @@ PUBLISHED: static int get_sequence_version(); static bool is_official_version(); + static int get_memory_alignment(); + static string get_distributor(); static string get_compiler(); static string get_build_date(); @@ -46,6 +48,21 @@ PUBLISHED: static string get_platform(); + MAKE_PROPERTY(version_string, get_version_string); + MAKE_PROPERTY(major_version, get_major_version); + MAKE_PROPERTY(minor_version, get_minor_version); + MAKE_PROPERTY(sequence_version, get_sequence_version); + MAKE_PROPERTY(official_version, is_official_version); + + MAKE_PROPERTY(memory_alignment, get_memory_alignment); + + MAKE_PROPERTY(distributor, get_distributor); + MAKE_PROPERTY(compiler, get_compiler); + MAKE_PROPERTY(build_date, get_build_date); + MAKE_PROPERTY(git_commit, get_git_commit); + + MAKE_PROPERTY(platform, get_platform); + bool has_system(const string &system) const; size_t get_num_systems() const; string get_system(size_t n) const; diff --git a/dtool/src/dtoolutil/pfstream.h b/dtool/src/dtoolutil/pfstream.h index b7565c1c64..c26456bbe6 100644 --- a/dtool/src/dtoolutil/pfstream.h +++ b/dtool/src/dtoolutil/pfstream.h @@ -17,7 +17,7 @@ #include "pfstreamBuf.h" class EXPCL_DTOOL IPipeStream : public istream { -PUBLISHED: +public: INLINE IPipeStream(const std::string); #if _MSC_VER >= 1800 @@ -33,7 +33,7 @@ private: }; class EXPCL_DTOOL OPipeStream : public ostream { -PUBLISHED: +public: INLINE OPipeStream(const std::string); #if _MSC_VER >= 1800 diff --git a/dtool/src/dtoolutil/textEncoder.h b/dtool/src/dtoolutil/textEncoder.h index bc42d4a4c1..ca885b15c3 100644 --- a/dtool/src/dtoolutil/textEncoder.h +++ b/dtool/src/dtoolutil/textEncoder.h @@ -46,6 +46,7 @@ PUBLISHED: INLINE static void set_default_encoding(Encoding encoding); INLINE static Encoding get_default_encoding(); + MAKE_PROPERTY(default_encoding, get_default_encoding, set_default_encoding); INLINE void set_text(const string &text); INLINE void set_text(const string &text, Encoding encoding); diff --git a/dtool/src/interrogate/README.md b/dtool/src/interrogate/README.md new file mode 100644 index 0000000000..4da044c8b5 --- /dev/null +++ b/dtool/src/interrogate/README.md @@ -0,0 +1,28 @@ +A key advantage of Panda3D is that it provides developers with the +ability to use both C++ and Python simultaneously. Essentially, Panda3D +gives programmers the best of both worlds, as they are able to take +advantage of the high performance and low-level programming found in +C++ in addition to the flexibility, interactive scripting, and +rapid-prototyping capabilities of Python. This feature is made possible +due to Python’s ability to call C libraries, and ultimately make use of +Panda3D’s Interrogate System: an automated C++ Extension Module +generation utility similar to SWIG. Although Python is the favored +scripting language of Panda3D, the engine is highly extensible in this +aspect, as any language that has a foreign function interface can make +use of the Interrogate System. + +The Interrogate System works like a compiler by scanning and parsing +C++ code for the Panda3D-specific, “PUBLISHED” keyword. This keyword +marks the particular methods of a class that are to be exposed within a +C++ Extension Module for that class which is eventually generated. One +benefit of using the “PUBLISHED” keyword is that it alleviates the need +for an interface file that provides function prototypes for the class +methods that will be exposed within the extension module, as is the +case with SWIG. Interrogate turns a class into a loose collection of +Python interface wrapper functions that make up the C++ Extension +Module. + +This package depends on the 'cppparser' package, which contains the +code that parses the C++ headers, and the 'interrogatedb' package, +which contains the intermediate representation of the interfaces that +can be saved to a database file for FFI generation tools to consume. diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index 6659a310db..42a420644f 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -772,16 +772,14 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak first_param = 1; } - if (_has_this || _type == T_constructor) { - if (_parameters.size() > (size_t)first_param && _parameters[first_param]._name == "self" && - TypeManager::is_pointer_to_PyObject(_parameters[first_param]._remap->get_orig_type())) { - // Here's a special case. If the first parameter of a nonstatic method - // is a PyObject * called "self", then we will automatically fill it in - // from the this pointer, and remove it from the generated parameter - // list. - _parameters.erase(_parameters.begin() + first_param); - _flags |= F_explicit_self; - } + if (_parameters.size() > (size_t)first_param && _parameters[first_param]._name == "self" && + TypeManager::is_pointer_to_PyObject(_parameters[first_param]._remap->get_orig_type())) { + // Here's a special case. If the first parameter of a nonstatic method + // is a PyObject * called "self", then we will automatically fill it in + // from the this pointer, and remove it from the generated parameter + // list. + _parameters.erase(_parameters.begin() + first_param); + _flags |= F_explicit_self; } if ((int)_parameters.size() == first_param) { @@ -791,6 +789,16 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak _args_type = InterfaceMaker::AT_single_arg; } else { _args_type = InterfaceMaker::AT_varargs; + + // If the arguments are named "args" and "kwargs", we will be directly + // passing the argument tuples to the function. + if (_parameters.size() == first_param + 2 && + _parameters[first_param]._name == "args" && + (_parameters[first_param + 1]._name == "kwargs" || + _parameters[first_param + 1]._name == "kwds")) { + _flags |= F_explicit_args; + _args_type = InterfaceMaker::AT_keyword_args; + } } switch (_type) { @@ -839,7 +847,7 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } } else if (fname == "__iter__") { - if (_has_this && _parameters.size() == 1 && + if ((int)_parameters.size() == first_param && TypeManager::is_pointer(_return_type->get_new_type())) { // It receives no parameters, and returns a pointer. _flags |= F_iter; @@ -860,8 +868,14 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } if (_args_type == InterfaceMaker::AT_varargs) { - // Of course methods named "make" can still take kwargs. - _args_type = InterfaceMaker::AT_keyword_args; + // Of course methods named "make" can still take kwargs, if they are + // named. + for (int i = first_param; i < _parameters.size(); ++i) { + if (_parameters[i]._has_name) { + _args_type = InterfaceMaker::AT_keyword_args; + break; + } + } } } else if (fname == "operator /") { @@ -889,8 +903,20 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } else { if (_args_type == InterfaceMaker::AT_varargs) { // Every other method can take keyword arguments, if they take more - // than one argument. - _args_type = InterfaceMaker::AT_keyword_args; + // than one argument, and the arguments are named. + for (int i = first_param; i < _parameters.size(); ++i) { + if (_parameters[i]._has_name) { + _args_type |= InterfaceMaker::AT_keyword_args; + break; + } + } + } else if (_args_type == InterfaceMaker::AT_single_arg) { + // If it takes an argument named "args", we are directly passing the + // "args" tuple to the function. + if (_parameters[first_param]._name == "args") { + _flags |= F_explicit_args; + _args_type = InterfaceMaker::AT_varargs; + } } } break; @@ -932,8 +958,14 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak _flags |= F_coerce_constructor; } - // Constructors always take varargs and keyword args. - _args_type = InterfaceMaker::AT_keyword_args; + // Constructors always take varargs, and possibly keyword args. + _args_type = InterfaceMaker::AT_varargs; + for (int i = first_param; i < _parameters.size(); ++i) { + if (_parameters[i]._has_name) { + _args_type = InterfaceMaker::AT_keyword_args; + break; + } + } break; default: diff --git a/dtool/src/interrogate/functionRemap.h b/dtool/src/interrogate/functionRemap.h index 72e18cf09e..b4b8aea6dd 100644 --- a/dtool/src/interrogate/functionRemap.h +++ b/dtool/src/interrogate/functionRemap.h @@ -100,6 +100,7 @@ public: F_coerce_constructor = 0x1000, F_divide_float = 0x2000, F_hash = 0x4000, + F_explicit_args = 0x8000, }; typedef vector Parameters; diff --git a/dtool/src/interrogate/interfaceMaker.cxx b/dtool/src/interrogate/interfaceMaker.cxx index c84456983d..218affd565 100644 --- a/dtool/src/interrogate/interfaceMaker.cxx +++ b/dtool/src/interrogate/interfaceMaker.cxx @@ -75,8 +75,8 @@ InterfaceMaker::MakeSeq:: MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) : _name(name), _imake_seq(imake_seq), - _length_getter(NULL), - _element_getter(NULL) + _length_getter(nullptr), + _element_getter(nullptr) { } @@ -86,12 +86,13 @@ MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) : InterfaceMaker::Property:: Property(const InterrogateElement &ielement) : _ielement(ielement), - _length_function(NULL), - _getter(NULL), - _setter(NULL), - _has_function(NULL), - _clear_function(NULL), - _deleter(NULL) + _length_function(nullptr), + _has_function(nullptr), + _clear_function(nullptr), + _deleter(nullptr), + _inserter(nullptr), + _getkey_function(nullptr), + _has_this(false) { } @@ -615,7 +616,7 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) { // If *any* of the variants of this function has a "this" pointer, // the entire set of functions is deemed to have a "this" pointer. - if (remap->_has_this) { + if (remap->_has_this || (remap->_flags & FunctionRemap::F_explicit_self) != 0) { func->_has_this = true; } diff --git a/dtool/src/interrogate/interfaceMaker.h b/dtool/src/interrogate/interfaceMaker.h index c84714eb51..f6b4730609 100644 --- a/dtool/src/interrogate/interfaceMaker.h +++ b/dtool/src/interrogate/interfaceMaker.h @@ -126,12 +126,15 @@ public: Property(const InterrogateElement &ielement); const InterrogateElement &_ielement; + vector _getter_remaps; + vector _setter_remaps; Function *_length_function; - Function *_getter; - Function *_setter; Function *_has_function; Function *_clear_function; Function *_deleter; + Function *_inserter; + Function *_getkey_function; + bool _has_this; }; typedef vector Properties; diff --git a/dtool/src/interrogate/interfaceMakerPython.cxx b/dtool/src/interrogate/interfaceMakerPython.cxx index f799335dfa..37125a7e64 100644 --- a/dtool/src/interrogate/interfaceMakerPython.cxx +++ b/dtool/src/interrogate/interfaceMakerPython.cxx @@ -51,7 +51,7 @@ test_assert(ostream &out, int indent_level) const { indent(out, indent_level) << "Notify *notify = Notify::ptr();\n"; indent(out, indent_level) - << "if (notify->has_assert_failed()) {\n"; + << "if (UNLIKELY(notify->has_assert_failed())) {\n"; indent(out, indent_level + 2) << "PyErr_SetString(PyExc_AssertionError, notify->get_assert_error_message().c_str());\n"; indent(out, indent_level + 2) diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index a844771fa6..3a861a4844 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -478,6 +478,11 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap, def._wrapper_type = WT_mapping_setitem; return true; } + if (remap->_flags & FunctionRemap::F_size) { + def._answer_location = "mp_length"; + def._wrapper_type = WT_sequence_size; + return true; + } } if (obj->_protocol_types & Object::PT_iter) { @@ -494,6 +499,24 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap, } } + if (method_name == "__await__") { + def._answer_location = "am_await"; + def._wrapper_type = WT_no_params; + return true; + } + + if (method_name == "__aiter__") { + def._answer_location = "am_aiter"; + def._wrapper_type = WT_no_params; + return true; + } + + if (method_name == "__anext__") { + def._answer_location = "am_anext"; + def._wrapper_type = WT_no_params; + return true; + } + if (method_name == "operator ()") { def._answer_location = "tp_call"; def._wrapper_type = WT_none; @@ -707,7 +730,10 @@ write_python_instance(ostream &out, int indent_level, const string &return_expr, string class_name = itype.get_scoped_name(); - if (IsPandaTypedObject(itype._cpptype->as_struct_type())) { + // We don't handle final classes via DTool_CreatePyInstanceTyped since we + // know it can't be of a subclass type, so we don't need to do the downcast. + CPPStructType *struct_type = itype._cpptype->as_struct_type(); + if (IsPandaTypedObject(struct_type) && !struct_type->is_final()) { // We can't let DTool_CreatePyInstanceTyped do the NULL check since we // will be grabbing the type index (which would obviously crash when // called on a NULL pointer), so we do it here. @@ -719,11 +745,31 @@ write_python_instance(ostream &out, int indent_level, const string &return_expr, << " return Py_None;\n"; indent(out, indent_level) << "} else {\n"; - indent(out, indent_level) - << " return DTool_CreatePyInstanceTyped((void *)" << return_expr - << ", *Dtool_Ptr_" << make_safe_name(class_name) << ", " - << owns_memory << ", " << is_const << ", " - << return_expr << "->as_typed_object()->get_type_index());\n"; + // Special exception if we are returning TypedWritable, which might + // actually be a derived class that inherits from ReferenceCount. + if (!owns_memory && !is_const && class_name == "TypedWritable") { + indent(out, indent_level) + << " ReferenceCount *rc = " << return_expr << "->as_reference_count();\n"; + indent(out, indent_level) + << " bool is_refcount = (rc != (ReferenceCount *)NULL);\n"; + indent(out, indent_level) + << " if (is_refcount) {\n"; + indent(out, indent_level) + << " rc->ref();\n"; + indent(out, indent_level) + << " }\n"; + indent(out, indent_level) + << " return DTool_CreatePyInstanceTyped((void *)" << return_expr + << ", *Dtool_Ptr_" << make_safe_name(class_name) << ", is_refcount, " + << is_const << ", " << return_expr + << "->get_type_index());\n"; + } else { + indent(out, indent_level) + << " return DTool_CreatePyInstanceTyped((void *)" << return_expr + << ", *Dtool_Ptr_" << make_safe_name(class_name) << ", " + << owns_memory << ", " << is_const << ", " + << return_expr << "->as_typed_object()->get_type_index());\n"; + } indent(out, indent_level) << "}\n"; } else { @@ -829,31 +875,13 @@ write_prototypes(ostream &out_code, ostream *out_h) { << " return ((bool (*)(PyObject *, PT(" << class_name << ") &))Dtool_Ptr_" << safe_name << "->_Dtool_Coerce)(args, coerced);\n" << "}\n"; } - - } else if (TypeManager::is_trivial(type)) { + } else { out_code << "inline static " << class_name << " *Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " &coerced) {\n" << " nassertr(Dtool_Ptr_" << safe_name << " != NULL, NULL);\n" << " nassertr(Dtool_Ptr_" << safe_name << "->_Dtool_Coerce != NULL, NULL);\n" << " return ((" << class_name << " *(*)(PyObject *, " << class_name << " &))Dtool_Ptr_" << safe_name << "->_Dtool_Coerce)(args, coerced);\n" << "}\n"; - - } else { - out_code - << "inline static bool Dtool_ConstCoerce_" << safe_name << "(PyObject *args, " << class_name << " const *&coerced, bool &manage) {\n" - << " nassertr(Dtool_Ptr_" << safe_name << " != NULL, false);\n" - << " nassertr(Dtool_Ptr_" << safe_name << "->_Dtool_ConstCoerce != NULL, false);\n" - << " return ((bool (*)(PyObject *, " << class_name << " const *&, bool&))Dtool_Ptr_" << safe_name << "->_Dtool_ConstCoerce)(args, coerced, manage);\n" - << "}\n"; - - if (has_coerce > 1) { - out_code - << "inline static bool Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " *&coerced, bool &manage) {\n" - << " nassertr(Dtool_Ptr_" << safe_name << " != NULL, false);\n" - << " nassertr(Dtool_Ptr_" << safe_name << "->_Dtool_Coerce != NULL, false);\n" - << " return ((bool (*)(PyObject *, " << class_name << " *&, bool&))Dtool_Ptr_" << safe_name << "->_Dtool_Coerce)(args, coerced, manage);\n" - << "}\n"; - } } } out_code << "#else\n"; @@ -862,20 +890,12 @@ write_prototypes(ostream &out_code, ostream *out_h) { if (has_coerce > 0) { if (TypeManager::is_reference_count(type)) { - assert(!type->is_trivial()); out_code << "extern bool Dtool_ConstCoerce_" << safe_name << "(PyObject *args, CPT(" << class_name << ") &coerced);\n"; if (has_coerce > 1) { out_code << "extern bool Dtool_Coerce_" << safe_name << "(PyObject *args, PT(" << class_name << ") &coerced);\n"; } - - } else if (TypeManager::is_trivial(type)) { - out_code << "extern " << class_name << " *Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " &coerced);\n"; - } else { - out_code << "extern bool Dtool_ConstCoerce_" << safe_name << "(PyObject *args, " << class_name << " const *&coerced, bool &manage);\n"; - if (has_coerce > 1) { - out_code << "extern bool Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " *&coerced, bool &manage);\n"; - } + out_code << "extern " << class_name << " *Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " &coerced);\n"; } } out_code << "#endif\n"; @@ -1036,7 +1056,7 @@ write_class_details(ostream &out, Object *obj) { int has_coerce = has_coerce_constructor(cpptype->as_struct_type()); if (has_coerce > 0) { write_coerce_constructor(out, obj, true); - if (has_coerce > 1 && !TypeManager::is_trivial(obj->_itype._cpptype)) { + if (has_coerce > 1 && TypeManager::is_reference_count(obj->_itype._cpptype)) { write_coerce_constructor(out, obj, false); } } @@ -1074,14 +1094,14 @@ write_class_details(ostream &out, Object *obj) { // Write support methods to cast from and to pointers of this type. { out << "static void *Dtool_UpcastInterface_" << ClassName << "(PyObject *self, Dtool_PyTypedObject *requested_type) {\n"; - out << " Dtool_PyTypedObject *SelfType = ((Dtool_PyInstDef *)self)->_My_Type;\n"; - out << " if (SelfType != Dtool_Ptr_" << ClassName << ") {\n"; + out << " Dtool_PyTypedObject *type = DtoolInstance_TYPE(self);\n"; + out << " if (type != &Dtool_" << ClassName << ") {\n"; out << " printf(\"" << ClassName << " ** Bad Source Type-- Requesting Conversion from %s to %s\\n\", Py_TYPE(self)->tp_name, requested_type->_PyType.tp_name); fflush(NULL);\n";; out << " return NULL;\n"; out << " }\n"; out << "\n"; - out << " " << cClassName << " *local_this = (" << cClassName << " *)((Dtool_PyInstDef *)self)->_ptr_to_object;\n"; - out << " if (requested_type == Dtool_Ptr_" << ClassName << ") {\n"; + out << " " << cClassName << " *local_this = (" << cClassName << " *)DtoolInstance_VOID_PTR(self);\n"; + out << " if (requested_type == &Dtool_" << ClassName << ") {\n"; out << " return local_this;\n"; out << " }\n"; @@ -1133,7 +1153,8 @@ write_class_declarations(ostream &out, ostream *out_h, Object *obj) { // to a macro function. out << "typedef " << c_class_name << " " << class_name << "_localtype;\n"; if (obj->_itype.has_destructor() || - obj->_itype.destructor_is_inherited()) { + obj->_itype.destructor_is_inherited() || + obj->_itype.destructor_is_implicit()) { if (TypeManager::is_reference_count(type)) { out << "Define_Module_ClassRef"; @@ -1155,20 +1176,12 @@ write_class_declarations(ostream &out, ostream *out_h, Object *obj) { int has_coerce = has_coerce_constructor(type->as_struct_type()); if (has_coerce > 0) { if (TypeManager::is_reference_count(type)) { - assert(!type->is_trivial()); out << "bool Dtool_ConstCoerce_" << class_name << "(PyObject *args, CPT(" << c_class_name << ") &coerced);\n"; if (has_coerce > 1) { out << "bool Dtool_Coerce_" << class_name << "(PyObject *args, PT(" << c_class_name << ") &coerced);\n"; } - - } else if (TypeManager::is_trivial(type)) { - out << "" << c_class_name << " *Dtool_Coerce_" << class_name << "(PyObject *args, " << c_class_name << " &coerced);\n"; - } else { - out << "bool Dtool_ConstCoerce_" << class_name << "(PyObject *args, " << c_class_name << " const *&coerced, bool &manage);\n"; - if (has_coerce > 1) { - out << "bool Dtool_Coerce_" << class_name << "(PyObject *args, " << c_class_name << " *&coerced, bool &manage);\n"; - } + out << "" << c_class_name << " *Dtool_Coerce_" << class_name << "(PyObject *args, " << c_class_name << " &coerced);\n"; } } @@ -2099,10 +2112,10 @@ write_module_class(ostream &out, Object *obj) { for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) { FunctionRemap *remap = (*ri); - if (remap->_flags & FunctionRemap::F_setitem_int) { + if (remap->_flags & FunctionRemap::F_setitem) { setitem_remaps.insert(remap); - } else if (remap->_flags & FunctionRemap::F_delitem_int) { + } else if (remap->_flags & FunctionRemap::F_delitem) { delitem_remaps.insert(remap); } } @@ -2136,14 +2149,21 @@ write_module_class(ostream &out, Object *obj) { out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n"; out << "//////////////////\n"; out << "static int " << def._wrapper_name << "(PyObject *self) {\n"; - out << " " << cClassName << " *local_this = NULL;\n"; - out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; - out << " return -1;\n"; - out << " }\n\n"; + // Find the remap. There should be only one. FunctionRemap *remap = *def._remaps.begin(); + const char *container = ""; + + if (remap->_has_this) { + out << " " << cClassName << " *local_this = NULL;\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; + out << " return -1;\n"; + out << " }\n\n"; + container = "local_this"; + } + vector_string params; - out << " return (int) " << remap->call_function(out, 4, false, "local_this", params) << ";\n"; + out << " return (int) " << remap->call_function(out, 4, false, container, params) << ";\n"; out << "}\n\n"; } break; @@ -2200,13 +2220,13 @@ write_module_class(ostream &out, Object *obj) { // provide a writable buffer or a readonly buffer. const string const_this = "(const " + cClassName + " *)local_this"; if (remap_const != NULL && remap_nonconst != NULL) { - out << " if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; + out << " if (!DtoolInstance_IS_CONST(self)) {\n"; out << " return " << remap_nonconst->call_function(out, 4, false, "local_this", params_nonconst) << ";\n"; out << " } else {\n"; out << " return " << remap_const->call_function(out, 4, false, const_this, params_const) << ";\n"; out << " }\n"; } else if (remap_nonconst != NULL) { - out << " if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; + out << " if (!DtoolInstance_IS_CONST(self)) {\n"; out << " return " << remap_nonconst->call_function(out, 4, false, "local_this", params_nonconst) << ";\n"; out << " } else {\n"; out << " Dtool_Raise_TypeError(\"Cannot call " << ClassName << ".__getbuffer__() on a const object.\");\n"; @@ -2265,7 +2285,7 @@ write_module_class(ostream &out, Object *obj) { string return_expr; const string const_this = "(const " + cClassName + " *)local_this"; if (remap_const != NULL && remap_nonconst != NULL) { - out << " if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; + out << " if (!DtoolInstance_IS_CONST(self)) {\n"; return_expr = remap_nonconst->call_function(out, 4, false, "local_this", params_nonconst); if (!return_expr.empty()) { out << " " << return_expr << ";\n"; @@ -2373,23 +2393,25 @@ write_module_class(ostream &out, Object *obj) { out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n"; out << "//////////////////\n"; out << "static int " << def._wrapper_name << "(PyObject *self, visitproc visit, void *arg) {\n"; - out << " " << cClassName << " *local_this = NULL;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **) &local_this);\n"; - out << " if (local_this == NULL) {\n"; - out << " return 0;\n"; - out << " }\n\n"; // Find the remap. There should be only one. FunctionRemap *remap = *def._remaps.begin(); + const char *container = ""; - vector_string params(1); - if (remap->_flags & FunctionRemap::F_explicit_self) { - params.push_back("self"); + if (remap->_has_this) { + out << " " << cClassName << " *local_this = NULL;\n"; + out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **) &local_this);\n"; + out << " if (local_this == NULL) {\n"; + out << " return 0;\n"; + out << " }\n\n"; + container = "local_this"; } + + vector_string params((int)remap->_has_this); params.push_back("visit"); params.push_back("arg"); - out << " return " << remap->call_function(out, 2, false, "local_this", params) << ";\n"; + out << " return " << remap->call_function(out, 2, false, container, params) << ";\n"; out << "}\n\n"; } break; @@ -2622,7 +2644,7 @@ write_module_class(ostream &out, Object *obj) { for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) { Property *property = (*pit); const InterrogateElement &ielem = property->_ielement; - if (property->_getter == NULL || !is_function_legal(property->_getter)) { + if (!property->_has_this || property->_getter_remaps.empty()) { continue; } @@ -2633,8 +2655,7 @@ write_module_class(ostream &out, Object *obj) { string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter"; string setter = "NULL"; - if (property->_length_function == NULL && - property->_setter != NULL && is_function_legal(property->_setter)) { + if (!ielem.is_sequence() && !ielem.is_mapping() && !property->_setter_remaps.empty()) { setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter"; } @@ -2781,6 +2802,20 @@ write_module_class(ostream &out, Object *obj) { out << "};\n\n"; } + bool have_async = false; + if (has_parent_class || slots.count("am_await") != 0 || + slots.count("am_aiter") != 0 || + slots.count("am_anext") != 0) { + out << "#if PY_VERSION_HEX >= 0x03050000\n"; + out << "static PyAsyncMethods Dtool_AsyncMethods_" << ClassName << " = {\n"; + write_function_slot(out, 2, slots, "am_await"); + write_function_slot(out, 2, slots, "am_aiter"); + write_function_slot(out, 2, slots, "am_anext"); + out << "};\n"; + out << "#endif\n\n"; + have_async = true; + } + // Output the actual PyTypeObject definition. out << "struct Dtool_PyTypedObject Dtool_" << ClassName << " = {\n"; out << " {\n"; @@ -2802,7 +2837,13 @@ write_module_class(ostream &out, Object *obj) { write_function_slot(out, 4, slots, "tp_setattr"); // cmpfunc tp_compare; (reserved in Python 3) - out << "#if PY_MAJOR_VERSION >= 3\n"; + out << "#if PY_VERSION_HEX >= 0x03050000\n"; + if (have_async) { + out << " &Dtool_AsyncMethods_" << ClassName << ",\n"; + } else { + out << " 0, // tp_as_async\n"; + } + out << "#elif PY_MAJOR_VERSION >= 3\n"; out << " 0, // tp_reserved\n"; out << "#else\n"; if (has_hash_compare) { @@ -2855,11 +2896,9 @@ write_module_class(ostream &out, Object *obj) { } // getattrofunc tp_getattro; - write_function_slot(out, 4, slots, "tp_getattro", - "PyObject_GenericGetAttr"); + write_function_slot(out, 4, slots, "tp_getattro"); // setattrofunc tp_setattro; - write_function_slot(out, 4, slots, "tp_setattro", - "PyObject_GenericSetAttr"); + write_function_slot(out, 4, slots, "tp_setattro"); // PyBufferProcs *tp_as_buffer; if (has_parent_class || has_local_getbuffer) { @@ -2993,8 +3032,7 @@ write_module_class(ostream &out, Object *obj) { int has_coerce = has_coerce_constructor(obj->_itype._cpptype->as_struct_type()); if (has_coerce > 0) { - if (TypeManager::is_reference_count(obj->_itype._cpptype) || - !TypeManager::is_trivial(obj->_itype._cpptype)) { + if (TypeManager::is_reference_count(obj->_itype._cpptype)) { out << " (CoerceFunction)Dtool_ConstCoerce_" << ClassName << ",\n"; if (has_coerce > 1) { out << " (CoerceFunction)Dtool_Coerce_" << ClassName << ",\n"; @@ -3040,7 +3078,7 @@ write_module_class(ostream &out, Object *obj) { out << " Dtool_" << ClassName << "._PyType.tp_bases = PyTuple_Pack(" << bases.size() << baseargs << ");\n"; } else { - out << " Dtool_" << ClassName << "._PyType.tp_base = (PyTypeObject *)Dtool_Ptr_DTOOL_SUPER_BASE;\n"; + out << " Dtool_" << ClassName << "._PyType.tp_base = (PyTypeObject *)&Dtool_DTOOL_SUPER_BASE;\n"; } int num_nested = obj->_itype.number_of_nested_types(); @@ -3168,6 +3206,43 @@ write_module_class(ostream &out, Object *obj) { } } + // Also add the static properties, which can't be added via getset. + Properties::const_iterator pit; + for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) { + Property *property = (*pit); + const InterrogateElement &ielem = property->_ielement; + if (property->_has_this || property->_getter_remaps.empty()) { + continue; + } + + string name1 = methodNameFromCppName(ielem.get_name(), "", false); + // string name2 = methodNameFromCppName(ielem.get_name(), "", true); + + string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter"; + string setter = "NULL"; + if (!ielem.is_sequence() && !ielem.is_mapping() && !property->_setter_remaps.empty()) { + setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter"; + } + + out << " static const PyGetSetDef def_" << name1 << " = {(char *)\"" << name1 << "\", " << getter << ", " << setter; + + if (ielem.has_comment()) { + out << ", (char *)\n"; + output_quoted(out, 4, ielem.get_comment()); + out << ",\n "; + } else { + out << ", NULL, "; + } + + // Extra void* argument; we don't make use of it. + out << "NULL};\n"; + + out << " PyDict_SetItemString(dict, \"" << name1 << "\", Dtool_NewStaticProperty(&Dtool_" << ClassName << "._PyType, &def_" << name1 << "));\n"; + /* Alternative spelling: + out << " PyDict_SetItemString(\"" << name2 << "\", &def_" << name1 << ");\n"; + */ + } + out << " if (PyType_Ready((PyTypeObject *)&Dtool_" << ClassName << ") < 0) {\n" " Dtool_Raise_TypeError(\"PyType_Ready(" << ClassName << ")\");\n" " return;\n" @@ -3388,6 +3463,7 @@ write_function_for_name(ostream &out, Object *obj, FunctionRemap *remap = NULL; int max_required_args = 0; bool all_nonconst = true; + bool has_keywords = false; out << "/**\n * Python function wrapper for:\n"; for (ri = remaps.begin(); ri != remaps.end(); ++ri) { @@ -3403,6 +3479,10 @@ write_function_for_name(ostream &out, Object *obj, all_nonconst = false; } + if (remap->_args_type == AT_keyword_args) { + has_keywords = true; + } + max_required_args = max(max_num_args, max_required_args); for (int i = min_num_args; i <= max_num_args; ++i) { @@ -3435,8 +3515,9 @@ write_function_for_name(ostream &out, Object *obj, out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", " << "(void **)&local_this, \"" << classNameFromCppName(cClassName, false) << "." << methodNameFromCppName(remap, cClassName, false) << "\")) {\n"; + } else { - out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; + out << " if (!DtoolInstance_GetPointer(self, local_this, Dtool_" << ClassName << ")) {\n"; } error_return(out, 4, return_flags); @@ -3449,11 +3530,31 @@ write_function_for_name(ostream &out, Object *obj, return; } + if (args_type == AT_keyword_args && !has_keywords) { + // We don't actually take keyword arguments. Make sure we didn't get any. + out << " if (kwds != NULL && PyDict_Size(kwds) > 0) {\n"; + out << "#ifdef NDEBUG\n"; + error_raise_return(out, 4, return_flags, "TypeError", "function takes no keyword arguments"); + out << "#else\n"; + error_raise_return(out, 4, return_flags, "TypeError", + methodNameFromCppName(remap, "", false) + "() takes no keyword arguments"); + out << "#endif\n"; + out << " }\n"; + args_type = AT_varargs; + } + if (args_type == AT_keyword_args || args_type == AT_varargs) { max_required_args = collapse_default_remaps(map_sets, max_required_args); } - if (map_sets.size() > 1 && (args_type == AT_varargs || args_type == AT_keyword_args)) { + if (remap->_flags & FunctionRemap::F_explicit_args) { + // We have a remap that wants to handle the wrapper itself. + string expected_params; + write_function_instance(out, remap, 0, 0, expected_params, 2, true, true, + args_type, return_flags); + + } else if (map_sets.size() > 1 && (args_type == AT_varargs || args_type == AT_keyword_args)) { + // We have more than one remap. switch (args_type) { case AT_keyword_args: indent(out, 2) << "int parameter_count = (int)PyTuple_Size(args);\n"; @@ -3491,16 +3592,50 @@ write_function_for_name(ostream &out, Object *obj, indent(out, 2) << "case " << i << ":\n"; num_args.insert(i + add_self); } - indent(out, 4) << "{\n"; num_args.insert(max_args + add_self); - if (min_args == 1 && max_args == 1 && args_type == AT_varargs) { - // Might as well, since we already checked the number of args. - indent(out, 6) << " PyObject *arg = PyTuple_GET_ITEM(args, 0);\n"; + bool strip_keyword_args = false; + + // Check whether any remap actually takes keyword arguments. If not, + // then we don't have to bother checking that for every remap. + if (args_type == AT_keyword_args && max_args > 0) { + strip_keyword_args = true; + + std::set::iterator sii; + for (sii = mii->second.begin(); sii != mii->second.end(); ++sii) { + remap = (*sii); + int first_param = remap->_has_this ? 1 : 0; + for (int i = first_param; i < remap->_parameters.size(); ++i) { + if (remap->_parameters[i]._has_name) { + strip_keyword_args = false; + break; + } + } + } + } + + if (strip_keyword_args) { + // None of the remaps take any keyword arguments, so let's check that + // we take none. This saves some checks later on. + indent(out, 4) << "if (kwds == NULL || PyDict_GET_SIZE(kwds) == 0) {\n"; + if (min_args == 1 && min_args == 1) { + indent(out, 4) << " PyObject *arg = PyTuple_GET_ITEM(args, 0);\n"; + write_function_forset(out, mii->second, min_args, max_args, expected_params, 6, + coercion_allowed, true, AT_single_arg, return_flags, true, !all_nonconst); + } else { + write_function_forset(out, mii->second, min_args, max_args, expected_params, 6, + coercion_allowed, true, AT_varargs, return_flags, true, !all_nonconst); + } + } else if (min_args == 1 && max_args == 1 && args_type == AT_varargs) { + // We already checked that the args tuple has only one argument, so + // we might as well extract that from the tuple now. + indent(out, 4) << "{\n"; + indent(out, 4) << " PyObject *arg = PyTuple_GET_ITEM(args, 0);\n"; write_function_forset(out, mii->second, min_args, max_args, expected_params, 6, coercion_allowed, true, AT_single_arg, return_flags, true, !all_nonconst); } else { + indent(out, 4) << "{\n"; write_function_forset(out, mii->second, min_args, max_args, expected_params, 6, coercion_allowed, true, args_type, return_flags, true, !all_nonconst); } @@ -3567,14 +3702,14 @@ write_function_for_name(ostream &out, Object *obj, if (mii->first == 0 && args_type != AT_no_args) { switch (args_type) { case AT_keyword_args: - out << " if (PyTuple_Size(args) > 0 || (kwds != NULL && PyDict_Size(kwds) > 0)) {\n"; + out << " if (!Dtool_CheckNoArgs(args, kwds)) {\n"; out << " int parameter_count = (int)PyTuple_Size(args);\n"; out << " if (kwds != NULL) {\n"; out << " parameter_count += (int)PyDict_Size(kwds);\n"; out << " }\n"; break; case AT_varargs: - out << " if (PyTuple_Size(args) > 0) {\n"; + out << " if (!Dtool_CheckNoArgs(args)) {\n"; out << " const int parameter_count = (int)PyTuple_GET_SIZE(args);\n"; break; case AT_single_arg: @@ -3600,14 +3735,14 @@ write_function_for_name(ostream &out, Object *obj, } else if (args_type == AT_keyword_args && max_required_args == 1 && mii->first == 1) { // Check this to be sure, as we handle the case of only 1 keyword arg in // write_function_forset (not using ParseTupleAndKeywords). - out << " int parameter_count = (int)PyTuple_Size(args);\n" - " if (kwds != NULL) {\n" - " parameter_count += (int)PyDict_Size(kwds);\n" - " }\n" + out << " int parameter_count = (int)PyTuple_Size(args);\n" + " if (kwds != NULL) {\n" + " parameter_count += (int)PyDict_Size(kwds);\n" + " }\n" " if (parameter_count != 1) {\n" "#ifdef NDEBUG\n"; error_raise_return(out, 4, return_flags, "TypeError", - "function takes exactly 1 argument"); + "function takes exactly 1 argument"); out << "#else\n"; error_raise_return(out, 4, return_flags, "TypeError", methodNameFromCppName(remap, "", false) + "() takes exactly 1 argument (%d given)", @@ -3756,11 +3891,10 @@ write_coerce_constructor(ostream &out, Object *obj, bool is_const) { // Note: this relies on the PT() being initialized to NULL. This is // currently the case in all invocations, but this may not be true in the // future. - out << " DTOOL_Call_ExtractThisPointerForType(args, &Dtool_" << ClassName << ", (void**)&coerced.cheat());\n"; - out << " if (coerced != NULL) {\n"; + out << " if (DtoolInstance_GetPointer(args, coerced.cheat(), Dtool_" << ClassName << ")) {\n"; out << " // The argument is already of matching type, no need to coerce.\n"; if (!is_const) { - out << " if (!((Dtool_PyInstDef *)args)->_is_const) {\n"; + out << " if (!DtoolInstance_IS_CONST(args)) {\n"; out << " // A non-const instance is required, which this is.\n"; out << " coerced->ref();\n"; out << " return true;\n"; @@ -3771,13 +3905,12 @@ write_coerce_constructor(ostream &out, Object *obj, bool is_const) { } return_flags |= RF_err_false; - } else if (TypeManager::is_trivial(obj->_itype._cpptype)) { + } else { out << cClassName << " *Dtool_Coerce_" << ClassName << "(PyObject *args, " << cClassName << " &coerced) {\n"; out << " " << cClassName << " *local_this;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(args, &Dtool_" << ClassName << ", (void**)&local_this);\n"; - out << " if (local_this != NULL) {\n"; - out << " if (((Dtool_PyInstDef *)args)->_is_const) {\n"; + out << " if (DtoolInstance_GetPointer(args, local_this, Dtool_" << ClassName << ")) {\n"; + out << " if (DtoolInstance_IS_CONST(args)) {\n"; out << " // This is a const object. Make a copy.\n"; out << " coerced = *(const " << cClassName << " *)local_this;\n"; out << " return &coerced;\n"; @@ -3785,26 +3918,6 @@ write_coerce_constructor(ostream &out, Object *obj, bool is_const) { out << " return local_this;\n"; return_flags |= RF_err_null; - - } else { - if (is_const) { - out << "bool Dtool_ConstCoerce_" << ClassName << "(PyObject *args, " << cClassName << " const *&coerced, bool &manage) {\n"; - } else { - out << "bool Dtool_Coerce_" << ClassName << "(PyObject *args, " << cClassName << " *&coerced, bool &manage) {\n"; - } - - out << " DTOOL_Call_ExtractThisPointerForType(args, &Dtool_" << ClassName << ", (void**)&coerced);\n"; - out << " if (coerced != NULL) {\n"; - if (!is_const) { - out << " if (!((Dtool_PyInstDef *)args)->_is_const) {\n"; - out << " // A non-const instance is required, which this is.\n"; - out << " return true;\n"; - out << " }\n"; - } else { - out << " return true;\n"; - } - - return_flags |= RF_err_false; } out << " }\n\n"; @@ -4060,7 +4173,9 @@ int get_type_sort(CPPType *type) { // printf(" %s\n",type->get_local_name().c_str()); // The highest numbered one will be checked first. - if (TypeManager::is_pointer_to_Py_buffer(type)) { + if (TypeManager::is_nullptr(type)) { + return 15; + } else if (TypeManager::is_pointer_to_Py_buffer(type)) { return 14; } else if (TypeManager::is_pointer_to_PyTypeObject(type)) { return 13; @@ -4078,7 +4193,7 @@ int get_type_sort(CPPType *type) { return 7; } else if (TypeManager::is_longlong(type)) { return 6; - } else if (TypeManager::is_integer(type)) { + } else if (TypeManager::is_integer(type) && !TypeManager::is_bool(type)) { return 5; } else if (TypeManager::is_double(type)) { return 4; @@ -4224,7 +4339,7 @@ write_function_forset(ostream &out, if (all_nonconst) { // Yes, they do. Check that the parameter has the required constness. indent(out, indent_level) - << "if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; + << "if (!DtoolInstance_IS_CONST(self)) {\n"; indent_level += 2; verify_const = false; } @@ -4240,14 +4355,16 @@ write_function_forset(ostream &out, args_type == AT_keyword_args) { sii = remapsin.begin(); remap = (*sii); - first_param_name = remap->_parameters[(int)remap->_has_this]._name; - same_first_param = true; + if (remap->_parameters[(int)remap->_has_this]._has_name) { + first_param_name = remap->_parameters[(int)remap->_has_this]._name; + same_first_param = true; - for (++sii; sii != remapsin.end(); ++sii) { - remap = (*sii); - if (remap->_parameters[(int)remap->_has_this]._name != first_param_name) { - same_first_param = false; - break; + for (++sii; sii != remapsin.end(); ++sii) { + remap = (*sii); + if (remap->_parameters[(int)remap->_has_this]._name != first_param_name) { + same_first_param = false; + break; + } } } } @@ -4256,21 +4373,9 @@ write_function_forset(ostream &out, // Yes, they all have the same argument name (or there is only one remap). // Extract it from the dict so we don't have to call // ParseTupleAndKeywords. - indent(out, indent_level) << "PyObject *arg = NULL;\n"; - indent(out, indent_level) << "if (PyTuple_GET_SIZE(args) == 1) {\n"; - indent(out, indent_level) << " arg = PyTuple_GET_ITEM(args, 0);\n"; - indent(out, indent_level) << "} else if (kwds != NULL) {\n"; - indent(out, indent_level) << " arg = PyDict_GetItemString(kwds, \"" << first_param_name << "\");\n"; - indent(out, indent_level) << "}\n"; - if (report_errors) { - indent(out, indent_level) << "if (arg == (PyObject *)NULL) {\n"; - error_raise_return(out, indent_level + 2, return_flags, "TypeError", - "Required argument '" + first_param_name + "' (pos 1) not found"); - indent(out, indent_level) << "}\n"; - } else { - indent(out, indent_level) << "if (arg != (PyObject *)NULL) {\n"; - indent_level += 2; - } + indent(out, indent_level) << "PyObject *arg;\n"; + indent(out, indent_level) << "if (Dtool_ExtractArg(&arg, args, kwds, \"" << first_param_name << "\")) {\n"; + indent_level += 2; args_type = AT_single_arg; } @@ -4282,57 +4387,6 @@ write_function_forset(ostream &out, std::sort(remaps.begin(), remaps.end(), RemapCompareLess); std::vector::const_iterator sii; - // Check if all of them have an InternalName pointer as first parameter. - // This is a dirty hack, of course, to work around an awkward overload - // resolution problem in NodePath::set_shader_input() (while perhaps also - // improving its performance). If I had more time I'd create a better - // solution. - bool first_internalname = false; - string first_pexpr2(first_pexpr); - if (first_pexpr.empty() && args_type != AT_no_args) { - first_internalname = true; - - for (sii = remaps.begin(); sii != remaps.end(); ++sii) { - remap = (*sii); - if (remap->_parameters.size() > (size_t)remap->_has_this) { - ParameterRemap *param = remap->_parameters[(size_t)remap->_has_this]._remap; - string param_name = param->get_orig_type()->get_local_name(&parser); - - if (param_name != "CPT_InternalName" && - param_name != "InternalName const *" && - param_name != "InternalName *") { - // Aw. - first_internalname = false; - break; - } - } else { - first_internalname = false; - break; - } - } - if (first_internalname) { - // Yeah, all remaps have a first InternalName parameter, so process - // that and remove it from the args tuple. - if (args_type == AT_single_arg) { - // Bit of a weird case, but whatever. - indent(out, indent_level) << "PyObject *name_obj = arg;\n"; - args_type = AT_no_args; - } else if (min_num_args == 2 && max_num_args == 2) { - indent(out, indent_level) << "PyObject *name_obj = PyTuple_GET_ITEM(args, 0);\n"; - indent(out, indent_level) << "PyObject *arg = PyTuple_GET_ITEM(args, 1);\n"; - args_type = AT_single_arg; - } else { - indent(out, indent_level) << "PyObject *name_obj = PyTuple_GET_ITEM(args, 0);\n"; - indent(out, indent_level) << "args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));\n"; - return_flags |= RF_decref_args; - } - indent(out, indent_level) << "PT(InternalName) name;\n"; - indent(out, indent_level) << "if (Dtool_Coerce_InternalName(name_obj, name)) {\n"; - indent_level += 2; - first_pexpr2 = "name"; - } - } - int num_coercion_possible = 0; sii = remaps.begin(); while (sii != remaps.end()) { @@ -4351,7 +4405,7 @@ write_function_forset(ostream &out, if (verify_const && (remap->_has_this && !remap->_const_method)) { // If it's a non-const method, we only allow a non-const this. indent(out, indent_level) - << "if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; + << "if (!DtoolInstance_IS_CONST(self)) {\n"; } else { indent(out, indent_level) << "{\n"; @@ -4367,7 +4421,7 @@ write_function_forset(ostream &out, write_function_instance(out, remap, min_num_args, max_num_args, expected_params, indent_level + 2, false, false, args_type, return_flags, - check_exceptions, first_pexpr2); + check_exceptions, first_pexpr); indent(out, indent_level) << "}\n\n"; } @@ -4386,7 +4440,7 @@ write_function_forset(ostream &out, if (verify_const && (remap->_has_this && !remap->_const_method)) { indent(out, indent_level) - << "if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; + << "if (!DtoolInstance_IS_CONST(self)) {\n"; } else { indent(out, indent_level) << "{\n"; @@ -4400,28 +4454,11 @@ write_function_forset(ostream &out, write_function_instance(out, remap, min_num_args, max_num_args, ignore_expected_params, indent_level + 2, true, false, args_type, return_flags, - check_exceptions, first_pexpr2); + check_exceptions, first_pexpr); indent(out, indent_level) << "}\n\n"; } } - - if (first_internalname) { - indent_level -= 2; - if (report_errors) { - indent(out, indent_level) << "} else {\n"; - - string class_name = remap->_cpptype->get_simple_name(); - ostringstream msg; - msg << classNameFromCppName(class_name, false) << "." - << methodNameFromCppName(remap, class_name, false) - << "() first argument must be str or InternalName"; - - error_raise_return(out, indent_level + 2, return_flags, - "TypeError", msg.str()); - } - indent(out, indent_level) << "}\n"; - } } else { // There is only one possible overload with this number of parameters. // Just call it. @@ -4441,7 +4478,7 @@ write_function_forset(ostream &out, } // Close the brace we opened earlier. - if (same_first_param && !report_errors) { + if (same_first_param) { indent_level -= 2; indent(out, indent_level) << "}\n"; } @@ -4510,6 +4547,8 @@ write_function_instance(ostream &out, FunctionRemap *remap, string parameter_list; string container; string type_check; + string param_name; + bool has_keywords = false; vector_string pexprs; LineStream extra_convert; ostringstream extra_param_check; @@ -4536,19 +4575,23 @@ write_function_instance(ostream &out, FunctionRemap *remap, expected_params += methodNameFromCppName(remap, "", false); expected_params += "("; - int num_params = max_num_args; - if (remap->_has_this) { - num_params += 1; - } - if (num_params > (int)remap->_parameters.size()) { - // Limit to how many parameters this remap actually has. - num_params = (int)remap->_parameters.size(); - max_num_args = num_params; + int num_params = 0; + + if ((remap->_flags & FunctionRemap::F_explicit_args) == 0) { + num_params = max_num_args; if (remap->_has_this) { - --max_num_args; + num_params += 1; } + if (num_params > (int)remap->_parameters.size()) { + // Limit to how many parameters this remap actually has. + num_params = (int)remap->_parameters.size(); + max_num_args = num_params; + if (remap->_has_this) { + --max_num_args; + } + } + nassertv(num_params <= (int)remap->_parameters.size()); } - nassertv(num_params <= (int)remap->_parameters.size()); bool only_pyobjects = true; @@ -4584,13 +4627,24 @@ write_function_instance(ostream &out, FunctionRemap *remap, } } + if (remap->_flags & FunctionRemap::F_explicit_args) { + // The function handles the arguments by itself. + expected_params += "*args"; + pexprs.push_back("args"); + if (args_type == AT_keyword_args) { + expected_params += ", **kwargs"; + pexprs.push_back("kwds"); + } + num_params = 0; + } + // Now convert (the rest of the) actual arguments, one by one. for (; pn < num_params; ++pn) { ParameterRemap *param = remap->_parameters[pn]._remap; CPPType *orig_type = param->get_orig_type(); CPPType *type = param->get_new_type(); CPPExpression *default_value = param->get_default_value(); - string param_name = remap->get_parameter_name(pn); + param_name = remap->get_parameter_name(pn); if (!is_cpp_type_legal(orig_type)) { // We can't wrap this. We sometimes get here for default arguments. @@ -4645,7 +4699,14 @@ write_function_instance(ostream &out, FunctionRemap *remap, } string reported_name = remap->_parameters[pn]._name; - keyword_list += "\"" + reported_name + "\", "; + if (!keyword_list.empty()) { + keyword_list += ", \"" + reported_name + "\""; + } else { + keyword_list = "\"" + reported_name + "\""; + } + if (remap->_parameters[pn]._has_name) { + has_keywords = true; + } if (param->new_type_is_atomic_string()) { @@ -4760,26 +4821,17 @@ write_function_instance(ostream &out, FunctionRemap *remap, << default_value->_str.size() << ";\n"; } } else { - indent(out, indent_level) << "char *" << param_name << "_str = NULL;\n"; + indent(out, indent_level) << "const char *" << param_name << "_str = NULL;\n"; indent(out, indent_level) << "Py_ssize_t " << param_name << "_len;\n"; } if (args_type == AT_single_arg) { out << "#if PY_MAJOR_VERSION >= 3\n"; - // As a special hack to fix pickling in Python 3, if the method name - // starts with py_decode_, we take a bytes object instead of a str. - if (remap->_cppfunc->get_local_name().substr(0, 10) == "py_decode_") { - indent(out, indent_level) << "if (PyBytes_AsStringAndSize(arg, &" - << param_name << "_str, &" << param_name << "_len) == -1) {\n"; - indent(out, indent_level + 2) << param_name << "_str = NULL;\n"; - indent(out, indent_level) << "}\n"; - } else { - indent(out, indent_level) - << param_name << "_str = PyUnicode_AsUTF8AndSize(arg, &" - << param_name << "_len);\n"; - } + indent(out, indent_level) + << param_name << "_str = PyUnicode_AsUTF8AndSize(arg, &" + << param_name << "_len);\n"; out << "#else\n"; // NB. PyString_AsStringAndSize also accepts a PyUnicode. - indent(out, indent_level) << "if (PyString_AsStringAndSize(arg, &" + indent(out, indent_level) << "if (PyString_AsStringAndSize(arg, (char **)&" << param_name << "_str, &" << param_name << "_len) == -1) {\n"; indent(out, indent_level + 2) << param_name << "_str = NULL;\n"; indent(out, indent_level) << "}\n"; @@ -4847,6 +4899,19 @@ write_function_instance(ostream &out, FunctionRemap *remap, pexpr_string = "(PyObject_IsTrue(" + param_name + ") != 0)"; expected_params += "bool"; + } else if (TypeManager::is_nullptr(type)) { + if (args_type == AT_single_arg) { + type_check = "arg == Py_None"; + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name << default_expr << ";\n"; + extra_param_check << " && " << param_name << " == Py_None"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } + pexpr_string = "nullptr"; + expected_params += "NoneType"; + } else if (TypeManager::is_char(type)) { indent(out, indent_level) << "char " << param_name << default_expr << ";\n"; @@ -4885,27 +4950,43 @@ write_function_instance(ostream &out, FunctionRemap *remap, only_pyobjects = false; } else if (TypeManager::is_size(type)) { - // It certainly isn't the exact same thing as size_t, but Py_ssize_t - // should at least be the same size. The problem with mapping this to - // unsigned int is that that doesn't work well on 64-bit systems, on - // which size_t is a 64-bit integer. - indent(out, indent_level) << "Py_ssize_t " << param_name << default_expr << ";\n"; - format_specifiers += "n"; - parameter_list += ", &" + param_name; + if (args_type == AT_single_arg) { + type_check = "PyLongOrInt_Check(arg)"; + + extra_convert << + "size_t arg_val = PyLongOrInt_AsSize_t(arg);\n" + "#ifndef NDEBUG\n" + "if (arg_val == (size_t)-1 && _PyErr_OCCURRED()) {\n"; + error_return(extra_convert, 2, return_flags); + extra_convert << + "}\n" + "#endif\n"; + + pexpr_string = "arg_val"; + + } else { + // It certainly isn't the exact same thing as size_t, but Py_ssize_t + // should at least be the same size. The problem with mapping this to + // unsigned int is that that doesn't work well on 64-bit systems, on + // which size_t is a 64-bit integer. + indent(out, indent_level) << "Py_ssize_t " << param_name << default_expr << ";\n"; + format_specifiers += "n"; + parameter_list += ", &" + param_name; + + extra_convert + << "#ifndef NDEBUG\n" + << "if (" << param_name << " < 0) {\n"; + + error_raise_return(extra_convert, 2, return_flags, "OverflowError", + "can't convert negative value %zd to size_t", + param_name); + extra_convert + << "}\n" + << "#endif\n"; + } expected_params += "int"; only_pyobjects = false; - extra_convert - << "#ifndef NDEBUG\n" - << "if (" << param_name << " < 0) {\n"; - - error_raise_return(extra_convert, 2, return_flags, "OverflowError", - "can't convert negative value %zd to size_t", - param_name); - extra_convert - << "}\n" - << "#endif\n"; - } else if (TypeManager::is_longlong(type)) { // It's not trivial to do overflow checking for a long long, so we // simply don't do it. @@ -5372,8 +5453,8 @@ write_function_instance(ostream &out, FunctionRemap *remap, // actual PointerTo. This eliminates an unref()ref() pair. pexpr_string = "MOVE(" + param_name + "_this)"; - } else if (TypeManager::is_trivial(obj_type)) { - // This is a trivial type, such as TypeHandle or LVecBase4. + } else { + // This is a move-assignable type, such as TypeHandle or LVecBase4. obj_type->output_instance(extra_convert, param_name + "_local", &parser); extra_convert << ";\n"; @@ -5396,29 +5477,6 @@ write_function_instance(ostream &out, FunctionRemap *remap, coerce_call = "(" + param_name + "_this != NULL)"; pexpr_string = param_name + "_this"; - - } else { - // This is a bit less elegant: we use a bool to store whether we're - // supposed to clean up the reference afterward. - type->output_instance(extra_convert, param_name + "_this", &parser); - extra_convert - << default_expr << ";\n" - << "bool " << param_name << "_manage = false;\n"; - - if (TypeManager::is_const_pointer_or_ref(orig_type)) { - coerce_call = "Dtool_ConstCoerce_" + make_safe_name(class_name) + - "(" + param_name + ", " + param_name + "_this, " + param_name + "_manage)"; - } else { - coerce_call = "Dtool_Coerce_" + make_safe_name(class_name) + - "(" + param_name + ", " + param_name + "_this, " + param_name + "_manage)"; - } - - extra_cleanup - << "if (" << param_name << "_manage) {\n" - << " delete " << param_name << "_this;\n" - << "}\n"; - - pexpr_string = param_name + "_this"; } if (report_errors) { @@ -5466,14 +5524,13 @@ write_function_instance(ostream &out, FunctionRemap *remap, // This function does the same thing in this case and is slightly // simpler. But maybe we should just reorganize these functions // entirely? - extra_convert << ";\n"; - if (is_optional) { - extra_convert << " "; - } - extra_convert - << "DTOOL_Call_ExtractThisPointerForType(" << param_name - << ", Dtool_Ptr_" << make_safe_name(class_name) - << ", (void **)&" << param_name << "_this);\n"; + extra_convert << " = NULL;\n"; + int indent_level = is_optional ? 2 : 0; + indent(extra_convert, indent_level) + << "DtoolInstance_GetPointer(" << param_name + << ", " << param_name << "_this" + << ", *Dtool_Ptr_" << make_safe_name(class_name) + << ");\n"; } else { extra_convert << boolalpha << " = (" << class_name << " *)" @@ -5533,15 +5590,60 @@ write_function_instance(ostream &out, FunctionRemap *remap, switch (args_type) { case AT_keyword_args: // Wrapper takes a varargs tuple and a keyword args dict. - indent(out, indent_level) - << "static const char *keyword_list[] = {" << keyword_list << "NULL};\n"; - indent(out, indent_level) - << "if (PyArg_ParseTupleAndKeywords(args, kwds, \"" - << format_specifiers << ":" << method_name - << "\", (char **)keyword_list" << parameter_list << ")) {\n"; + if (has_keywords) { + if (only_pyobjects && max_num_args == 1) { + // But we are only expecting one object arg, which is an easy common + // case we have implemented ourselves. + if (min_num_args == 1) { + indent(out, indent_level) + << "if (Dtool_ExtractArg(&" << param_name << ", args, kwds, " << keyword_list << ")) {\n"; + } else { + indent(out, indent_level) + << "if (Dtool_ExtractOptionalArg(&" << param_name << ", args, kwds, " << keyword_list << ")) {\n"; + } + } else { + // We have to use the more expensive PyArg_ParseTupleAndKeywords. + clear_error = true; + indent(out, indent_level) + << "static const char *keyword_list[] = {" << keyword_list << ", NULL};\n"; + indent(out, indent_level) + << "if (PyArg_ParseTupleAndKeywords(args, kwds, \"" + << format_specifiers << ":" << method_name + << "\", (char **)keyword_list" << parameter_list << ")) {\n"; + } + + } else if (only_pyobjects) { + // This function actually has no named parameters, so let's not take + // any keyword arguments. + if (max_num_args == 1) { + if (min_num_args == 1) { + indent(out, indent_level) + << "if (Dtool_ExtractArg(&" << param_name << ", args, kwds)) {\n"; + } else { + indent(out, indent_level) + << "if (Dtool_ExtractOptionalArg(&" << param_name << ", args, kwds)) {\n"; + } + } else if (max_num_args == 0) { + indent(out, indent_level) + << "if (Dtool_CheckNoArgs(args, kwds)) {\n"; + } else { + clear_error = true; + indent(out, indent_level) + << "if ((kwds == NULL || PyDict_Size(kwds) == 0) && PyArg_UnpackTuple(args, \"" + << methodNameFromCppName(remap, "", false) + << "\", " << min_num_args << ", " << max_num_args + << parameter_list << ")) {\n"; + } + + } else { + clear_error = true; + indent(out, indent_level) + << "if ((kwds == NULL || PyDict_Size(kwds) == 0) && PyArg_ParseTuple(args, \"" + << format_specifiers << ":" << method_name + << "\"" << parameter_list << ")) {\n"; + } ++open_scopes; - clear_error = true; indent_level += 2; break; @@ -5550,20 +5652,28 @@ write_function_instance(ostream &out, FunctionRemap *remap, if (only_pyobjects) { // All parameters are PyObject*, so we can use the slightly more // efficient PyArg_UnpackTuple function instead. - indent(out, indent_level) - << "if (PyArg_UnpackTuple(args, \"" - << methodNameFromCppName(remap, "", false) - << "\", " << min_num_args << ", " << max_num_args - << parameter_list << ")) {\n"; + if (min_num_args == 1 && max_num_args == 1) { + indent(out, indent_level) + << "if (PyTuple_GET_SIZE(args) == 1) {\n"; + indent(out, indent_level + 2) + << param_name << " = PyTuple_GET_ITEM(args, 0);\n"; + } else { + clear_error = true; + indent(out, indent_level) + << "if (PyArg_UnpackTuple(args, \"" + << methodNameFromCppName(remap, "", false) + << "\", " << min_num_args << ", " << max_num_args + << parameter_list << ")) {\n"; + } } else { + clear_error = true; indent(out, indent_level) << "if (PyArg_ParseTuple(args, \"" << format_specifiers << ":" << method_name << "\"" << parameter_list << ")) {\n"; } ++open_scopes; - clear_error = true; indent_level += 2; break; @@ -5602,7 +5712,8 @@ write_function_instance(ostream &out, FunctionRemap *remap, indent_level += 2; } - if (!remap->_has_this && (remap->_flags & FunctionRemap::F_explicit_self) != 0) { + if (is_constructor && !remap->_has_this && + (remap->_flags & FunctionRemap::F_explicit_self) != 0) { // If we'll be passing "self" to the constructor, we need to pre- // initialize it here. Unfortunately, we can't pre-load the "this" // pointer, but the constructor itself can do this. @@ -5668,7 +5779,7 @@ write_function_instance(ostream &out, FunctionRemap *remap, manage_return = remap->_return_value_needs_management; return_expr = "return_value"; - } else if ((return_flags & RF_coerced) != 0 && TypeManager::is_trivial(remap->_cpptype)) { + } else if ((return_flags & RF_coerced) != 0 && !TypeManager::is_reference_count(remap->_cpptype)) { // Another special case is the coerce constructor for a trivial type. We // don't want to invoke "operator new" unnecessarily. if (is_constructor && remap->_extension) { @@ -5851,14 +5962,14 @@ write_function_instance(ostream &out, FunctionRemap *remap, // this for coercion constructors since they are called by other wrapper // functions which already check this on their own. Generated getters // obviously can't raise asserts. - if (watch_asserts && (return_flags & RF_coerced) == 0 && + if (watch_asserts && (return_flags & (RF_coerced | RF_raise_keyerror)) == 0 && remap->_type != FunctionRemap::T_getter && remap->_type != FunctionRemap::T_setter) { out << "#ifndef NDEBUG\n"; indent(out, indent_level) << "Notify *notify = Notify::ptr();\n"; indent(out, indent_level) - << "if (notify->has_assert_failed()) {\n"; + << "if (UNLIKELY(notify->has_assert_failed())) {\n"; if (manage_return) { // Output code to delete any temporary object we may have allocated. @@ -5883,7 +5994,7 @@ write_function_instance(ostream &out, FunctionRemap *remap, // Okay, we're past all the error conditions and special cases. Now return // the return type in the way that was requested. - if (return_flags & RF_int) { + if ((return_flags & RF_int) != 0 && (return_flags & RF_raise_keyerror) == 0) { CPPType *orig_type = remap->_return_type->get_orig_type(); if (is_constructor) { // Special case for constructor. @@ -5972,14 +6083,30 @@ write_function_instance(ostream &out, FunctionRemap *remap, indent(out, indent_level) << "coerced = MOVE(" << return_expr << ");\n"; indent(out, indent_level) << "return true;\n"; - } else if (TypeManager::is_trivial(remap->_cpptype)) { - indent(out, indent_level) << "return &coerced;\n"; - } else { - indent(out, indent_level) << "coerced = " << return_expr << ";\n"; - indent(out, indent_level) << "manage = true;\n"; - indent(out, indent_level) << "return true;\n"; + indent(out, indent_level) << "return &coerced;\n"; } + + } else if (return_flags & RF_raise_keyerror) { + CPPType *orig_type = remap->_return_type->get_orig_type(); + + if (TypeManager::is_bool(orig_type) || TypeManager::is_pointer(orig_type)) { + indent(out, indent_level) << "if (!" << return_expr << ") {\n"; + } else if (TypeManager::is_unsigned_integer(orig_type)) { + indent(out, indent_level) << "if ((int)" << return_expr << " == -1) {\n"; + } else if (TypeManager::is_integer(orig_type)) { + indent(out, indent_level) << "if (" << return_expr << " < 0) {\n"; + } else { + indent(out, indent_level) << "if (false) {\n"; + } + + if (args_type == AT_single_arg) { + indent(out, indent_level) << " PyErr_SetObject(PyExc_KeyError, arg);\n"; + } else { + indent(out, indent_level) << " PyErr_SetObject(PyExc_KeyError, key);\n"; + } + error_return(out, indent_level + 2, return_flags); + indent(out, indent_level) << "}\n"; } // Close the extra braces opened earlier. @@ -6198,7 +6325,7 @@ write_make_seq(ostream &out, Object *obj, const std::string &ClassName, // the assumption that the called method doesn't do anything with this // tuple other than unpack it (which is a fairly safe assumption to make). out << " PyTupleObject args;\n"; - out << " PyObject_INIT_VAR(&args, &PyTuple_Type, 1);\n"; + out << " (void)PyObject_INIT_VAR((PyVarObject *)&args, &PyTuple_Type, 1);\n"; } out << @@ -6252,19 +6379,23 @@ write_make_seq(ostream &out, Object *obj, const std::string &ClassName, */ void InterfaceMakerPythonNative:: write_getset(ostream &out, Object *obj, Property *property) { + // We keep around this empty vector for passing to get_call_str. + const vector_string pexprs; string ClassName = make_safe_name(obj->_itype.get_scoped_name()); std::string cClassName = obj->_itype.get_true_name(); const InterrogateElement &ielem = property->_ielement; - if (property->_length_function != NULL) { + FunctionRemap *len_remap = nullptr; + if (property->_length_function != nullptr) { + assert(!property->_length_function->_remaps.empty()); + // This is actually a sequence. Wrap this with a special class. - FunctionRemap *len_remap = property->_length_function->_remaps.front(); - vector_string pexprs; + len_remap = property->_length_function->_remaps.front(); out << "/**\n" - " * sequence length function for property " << cClassName << "::" << ielem.get_name() << "\n" + " * sequence length function for property " << ielem.get_scoped_name() << "\n" " */\n" "static Py_ssize_t Dtool_" + ClassName + "_" + ielem.get_name() + "_Len(PyObject *self) {\n"; if (property->_length_function->_has_this) { @@ -6278,74 +6409,95 @@ write_getset(ostream &out, Object *obj, Property *property) { out << " return (Py_ssize_t)" << len_remap->get_call_str("", pexprs) << ";\n"; } out << "}\n\n"; + } - // Now write out the getitem helper function. - if (property->_getter != NULL) { + if (property->_getter_remaps.empty()) { + return; + } + + if (ielem.is_sequence()) { + assert(len_remap != nullptr); + out << + "/**\n" + " * sequence getter for property " << ielem.get_scoped_name() << "\n" + " */\n" + "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Sequence_Getitem(PyObject *self, Py_ssize_t index) {\n"; + + if (property->_has_this) { out << - "/**\n" - " * sequence getter for property " << cClassName << "::" << ielem.get_name() << "\n" - " */\n" - "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getitem(PyObject *self, Py_ssize_t index) {\n" " " << cClassName << " *local_this = NULL;\n" " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n" " return NULL;\n" " }\n"; + } + + // This is a getitem of a sequence type. This means we *need* to raise + // IndexError if we're out of bounds. + out << " if (index < 0 || index >= (Py_ssize_t)" + << len_remap->get_call_str("local_this", pexprs) << ") {\n"; + out << " PyErr_SetString(PyExc_IndexError, \"" << ClassName << "." << ielem.get_name() << "[] index out of range\");\n"; + out << " return NULL;\n"; + out << " }\n"; + + /*if (property->_has_function != NULL) { + out << " if (!local_this->" << property->_has_function->_ifunc.get_name() << "(index)) {\n" + << " Py_INCREF(Py_None);\n" + << " return Py_None;\n" + << " }\n"; + }*/ + + std::set remaps; + + // Extract only the getters that take one integral argument. + Function::Remaps::iterator it; + for (it = property->_getter_remaps.begin(); + it != property->_getter_remaps.end(); + ++it) { + FunctionRemap *remap = *it; + int min_num_args = remap->get_min_num_args(); + int max_num_args = remap->get_max_num_args(); + if (min_num_args <= 1 && max_num_args >= 1 && + TypeManager::is_integer(remap->_parameters[(size_t)remap->_has_this]._remap->get_new_type())) { + remaps.insert(remap); + } + } + + string expected_params; + write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true, + AT_no_args, RF_pyobject | RF_err_null, false, true, "index"); + + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " return Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 6, expected_params); + out << ");\n" + " }\n" + "}\n\n"; + + // Write out a setitem if this is not a read-only property. + if (!property->_setter_remaps.empty()) { + out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Sequence_Setitem(PyObject *self, Py_ssize_t index, PyObject *arg) {\n"; + if (property->_has_this) { + out << " " << cClassName << " *local_this = NULL;\n"; + out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \"" + << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n"; + out << " return -1;\n"; + out << " }\n\n"; + } - // This is a getitem of a sequence type. This means we *need* to raise - // IndexError if we're out of bounds. out << " if (index < 0 || index >= (Py_ssize_t)" << len_remap->get_call_str("local_this", pexprs) << ") {\n"; out << " PyErr_SetString(PyExc_IndexError, \"" << ClassName << "." << ielem.get_name() << "[] index out of range\");\n"; - out << " return NULL;\n"; - out << " }\n"; - - if (property->_has_function != NULL) { - out << " if (!local_this->" << property->_has_function->_ifunc.get_name() << "(index)) {\n" - << " Py_INCREF(Py_None);\n" - << " return Py_None;\n" - << " }\n"; - } - - std::set remaps; - - // Extract only the getters that take one argument. - Function::Remaps::iterator it; - for (it = property->_getter->_remaps.begin(); - it != property->_getter->_remaps.end(); - ++it) { - FunctionRemap *remap = *it; - int min_num_args = remap->get_min_num_args(); - int max_num_args = remap->get_max_num_args(); - if (min_num_args <= 1 && max_num_args >= 1) { - remaps.insert(remap); - } - } - - string expected_params; - write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true, - AT_no_args, RF_pyobject | RF_err_null, false, true, "index"); - - out << " if (!_PyErr_OCCURRED()) {\n"; - out << " return Dtool_Raise_BadArgumentsError(\n"; - output_quoted(out, 6, expected_params); - out << ");\n" - " }\n" - "}\n\n"; - } - - // Write out a setitem if this is not a read-only property. - if (property->_setter != NULL) { - out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setitem(PyObject *self, Py_ssize_t index, PyObject *arg) {\n"; - out << " " << cClassName << " *local_this = NULL;\n"; - out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \"" - << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n"; out << " return -1;\n"; - out << " }\n\n"; + out << " }\n"; out << " if (arg == (PyObject *)NULL) {\n"; if (property->_deleter != NULL) { - out << " local_this->" << property->_deleter->_ifunc.get_name() << "(index);\n" - << " return 0;\n"; + if (property->_deleter->_has_this) { + out << " local_this->" << property->_deleter->_ifunc.get_name() << "(index);\n"; + } else { + out << " " << cClassName << "::" << property->_deleter->_ifunc.get_name() << "(index);\n"; + } + out << " return 0;\n"; } else { out << " Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << "[] attribute\");\n" " return -1;\n"; @@ -6353,9 +6505,13 @@ write_getset(ostream &out, Object *obj, Property *property) { out << " }\n"; if (property->_clear_function != NULL) { - out << " if (arg == Py_None) {\n" - << " local_this->" << property->_clear_function->_ifunc.get_name() << "(index);\n" - << " return 0;\n" + out << " if (arg == Py_None) {\n"; + if (property->_clear_function->_has_this) { + out << " local_this->" << property->_clear_function->_ifunc.get_name() << "(index);\n"; + } else { + out << " " << cClassName << "::" << property->_clear_function->_ifunc.get_name() << "(index);\n"; + } + out << " return 0;\n" << " }\n"; } @@ -6363,13 +6519,14 @@ write_getset(ostream &out, Object *obj, Property *property) { // Extract only the setters that take two arguments. Function::Remaps::iterator it; - for (it = property->_setter->_remaps.begin(); - it != property->_setter->_remaps.end(); + for (it = property->_setter_remaps.begin(); + it != property->_setter_remaps.end(); ++it) { FunctionRemap *remap = *it; int min_num_args = remap->get_min_num_args(); int max_num_args = remap->get_max_num_args(); - if (min_num_args <= 2 && max_num_args >= 2) { + if (min_num_args <= 2 && max_num_args >= 2 && + TypeManager::is_integer(remap->_parameters[1]._remap->get_new_type())) { remaps.insert(remap); } } @@ -6388,40 +6545,324 @@ write_getset(ostream &out, Object *obj, Property *property) { out << "}\n\n"; } - // Now write the getter, which returns a special wrapper object. - out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n" - " Py_INCREF(self);\n" - " Dtool_SequenceWrapper *wrap = PyObject_New(Dtool_SequenceWrapper, &Dtool_SequenceWrapper_Type);\n" - " wrap->_base = self;\n" - " wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n" - " wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Getitem;\n"; - if (property->_setter != NULL) { - out << " wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Setitem;\n"; - } else { - out << " wrap->_setitem_func = NULL;\n"; - } - out << " return (PyObject *)wrap;\n" - "}\n\n"; + // Finally, add the inserter, if one exists. + if (property->_inserter != nullptr) { + out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Sequence_insert(PyObject *self, size_t index, PyObject *arg) {\n"; + if (property->_has_this) { + out << " " << cClassName << " *local_this = NULL;\n"; + out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \"" + << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n"; + out << " return NULL;\n"; + out << " }\n\n"; + } - } else if (property->_getter != NULL) { - // Write out a regular, unwrapped getter. - out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n"; - FunctionRemap *remap = property->_getter->_remaps.front(); + std::set remaps; + remaps.insert(property->_inserter->_remaps.begin(), + property->_inserter->_remaps.end()); - if (remap->_const_method) { - out << " const " << cClassName << " *local_this = NULL;\n"; - out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; - } else { - out << " " << cClassName << " *local_this = NULL;\n"; - out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \"" - << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n"; + string expected_params; + write_function_forset(out, remaps, 2, 2, + expected_params, 2, true, true, AT_single_arg, + RF_pyobject | RF_err_null, false, false, "index"); + + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 6, expected_params); + out << ");\n"; + out << " }\n"; + out << " return NULL;\n"; + out << "}\n\n"; + } + } + + + // Write the getitem functions. + if (ielem.is_mapping()) { + out << + "/**\n" + " * mapping getitem for property " << ielem.get_scoped_name() << "\n" + " */\n" + "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Mapping_Getitem(PyObject *self, PyObject *arg) {\n"; + + // Before we do the has_function: if this is also a sequence, then we have + // to also handle the case here that we were passed an index. + if (ielem.is_sequence()) { + out << + "#if PY_MAJOR_VERSION >= 3\n" + " if (PyLong_CheckExact(arg)) {\n" + "#else\n" + " if (PyLong_CheckExact(arg) || PyInt_CheckExact(arg)) {\n" + "#endif\n" + " return Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem(self, PyLongOrInt_AsSize_t(arg));\n" + " }\n\n"; + } + + if (property->_has_this) { + out << + " " << cClassName << " *local_this = NULL;\n" + " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n" + " return NULL;\n" + " }\n"; } - out << " return NULL;\n"; - out << " }\n\n"; if (property->_has_function != NULL) { - out << " if (!local_this->" << property->_has_function->_ifunc.get_name() << "()) {\n" - << " Py_INCREF(Py_None);\n" + std::set remaps; + remaps.insert(property->_has_function->_remaps.begin(), + property->_has_function->_remaps.end()); + + out << " {\n"; + string expected_params; + write_function_forset(out, remaps, 1, 1, expected_params, 4, true, true, + AT_single_arg, RF_raise_keyerror | RF_err_null, false, true); + out << " }\n"; + } + + std::set remaps; + // Extract only the getters that take one argument. Fish out the ones + // already taken by the sequence getter. + Function::Remaps::iterator it; + for (it = property->_getter_remaps.begin(); + it != property->_getter_remaps.end(); + ++it) { + FunctionRemap *remap = *it; + int min_num_args = remap->get_min_num_args(); + int max_num_args = remap->get_max_num_args(); + if (min_num_args <= 1 && max_num_args >= 1 && + (!ielem.is_sequence() || !TypeManager::is_integer(remap->_parameters[(size_t)remap->_has_this]._remap->get_new_type()))) { + remaps.insert(remap); + } + } + + string expected_params; + write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true, + AT_single_arg, RF_pyobject | RF_err_null, false, true); + + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " return Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 6, expected_params); + out << ");\n" + " }\n" + " return NULL;\n" + "}\n\n"; + + // Write out a setitem if this is not a read-only property. + if (!property->_setter_remaps.empty()) { + out << + "/**\n" + " * mapping setitem for property " << ielem.get_scoped_name() << "\n" + " */\n" + "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Mapping_Setitem(PyObject *self, PyObject *key, PyObject *value) {\n"; + + if (property->_has_this) { + out << + " " << cClassName << " *local_this = NULL;\n" + " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \"" + << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n" + " return -1;\n" + " }\n\n"; + } + + out << " if (value == (PyObject *)NULL) {\n"; + if (property->_deleter != NULL) { + out << " PyObject *arg = key;\n"; + + if (property->_has_function != NULL) { + std::set remaps; + remaps.insert(property->_has_function->_remaps.begin(), + property->_has_function->_remaps.end()); + + out << " {\n"; + string expected_params; + write_function_forset(out, remaps, 1, 1, expected_params, 6, true, true, + AT_single_arg, RF_raise_keyerror | RF_int, false, true); + out << " }\n"; + } + + std::set remaps; + remaps.insert(property->_deleter->_remaps.begin(), + property->_deleter->_remaps.end()); + + string expected_params; + write_function_forset(out, remaps, 1, 1, + expected_params, 4, true, true, AT_single_arg, + RF_int, false, false); + out << " return -1;\n"; + } else { + out << " Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << "[] attribute\");\n" + " return -1;\n"; + } + out << " }\n"; + + if (property->_clear_function != NULL) { + out << " if (value == Py_None) {\n" + << " local_this->" << property->_clear_function->_ifunc.get_name() << "(key);\n" + << " return 0;\n" + << " }\n"; + } + + std::set remaps; + remaps.insert(property->_setter_remaps.begin(), + property->_setter_remaps.end()); + + // We have to create an args tuple only to unpack it later, ugh. + out << " PyObject *args = PyTuple_New(2);\n" + << " PyTuple_SET_ITEM(args, 0, key);\n" + << " PyTuple_SET_ITEM(args, 1, value);\n" + << " Py_INCREF(key);\n" + << " Py_INCREF(value);\n"; + + string expected_params; + write_function_forset(out, remaps, 2, 2, + expected_params, 2, true, true, AT_varargs, + RF_int | RF_decref_args, false, false); + + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 6, expected_params); + out << ");\n"; + out << " }\n"; + out << " Py_DECREF(args);\n"; + out << " return -1;\n"; + out << "}\n\n"; + } + + if (property->_getkey_function != nullptr) { + out << + "/**\n" + " * mapping key-getter for property " << ielem.get_scoped_name() << "\n" + " */\n" + "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Mapping_Getkey(PyObject *self, Py_ssize_t index) {\n"; + + if (property->_has_this) { + out << + " " << cClassName << " *local_this = NULL;\n" + " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n" + " return NULL;\n" + " }\n"; + } + + // We need to raise IndexError if we're out of bounds. + if (len_remap != nullptr) { + out << " if (index < 0 || index >= (Py_ssize_t)" + << len_remap->get_call_str("local_this", pexprs) << ") {\n"; + out << " PyErr_SetString(PyExc_IndexError, \"" << ClassName << "." << ielem.get_name() << "[] index out of range\");\n"; + out << " return NULL;\n"; + out << " }\n"; + } + + std::set remaps; + + // Extract only the getters that take one integral argument. + Function::Remaps::iterator it; + for (it = property->_getkey_function->_remaps.begin(); + it != property->_getkey_function->_remaps.end(); + ++it) { + FunctionRemap *remap = *it; + int min_num_args = remap->get_min_num_args(); + int max_num_args = remap->get_max_num_args(); + if (min_num_args <= 1 && max_num_args >= 1 && + TypeManager::is_integer(remap->_parameters[(size_t)remap->_has_this]._remap->get_new_type())) { + remaps.insert(remap); + } + } + + string expected_params; + write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true, + AT_no_args, RF_pyobject | RF_err_null, false, true, "index"); + + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " return Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 6, expected_params); + out << ");\n" + " }\n" + "}\n\n"; + } + } + + // Now write the actual getter wrapper. It will be a different wrapper + // depending on whether it's a mapping or a sequence. + if (ielem.is_mapping()) { + out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n"; + if (property->_has_this) { + out << " nassertr(self != NULL, NULL);\n"; + } + if (property->_setter_remaps.empty()) { + out << " Dtool_MappingWrapper *wrap = Dtool_NewMappingWrapper(self, \"" << ClassName << "." << ielem.get_name() << "\");\n"; + } else { + out << " Dtool_MappingWrapper *wrap = Dtool_NewMutableMappingWrapper(self, \"" << ClassName << "." << ielem.get_name() << "\");\n"; + } + out << " if (wrap != NULL) {\n" + " wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getitem;\n"; + if (!property->_setter_remaps.empty()) { + out << " if (!DtoolInstance_IS_CONST(self)) {\n"; + out << " wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Setitem;\n"; + out << " }\n"; + } + if (property->_length_function != nullptr) { + out << " wrap->_keys._len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"; + if (property->_getkey_function != nullptr) { + out << " wrap->_keys._getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getkey;\n"; + } + } + out << " }\n" + " return (PyObject *)wrap;\n" + "}\n\n"; + + } else if (ielem.is_sequence()) { + out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n"; + if (property->_has_this) { + out << " nassertr(self != NULL, NULL);\n"; + } + if (property->_setter_remaps.empty()) { + out << + " Dtool_SequenceWrapper *wrap = Dtool_NewSequenceWrapper(self, \"" << ClassName << "." << ielem.get_name() << "\");\n" + " if (wrap != NULL) {\n" + " wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n" + " wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n"; + } else { + out << + " Dtool_MutableSequenceWrapper *wrap = Dtool_NewMutableSequenceWrapper(self, \"" << ClassName << "." << ielem.get_name() << "\");\n" + " if (wrap != NULL) {\n" + " wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n" + " wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n"; + if (!property->_setter_remaps.empty()) { + out << " if (!DtoolInstance_IS_CONST(self)) {\n"; + out << " wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Setitem;\n"; + if (property->_inserter != nullptr) { + out << " wrap->_insert_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_insert;\n"; + } + out << " }\n"; + } + } + out << " }\n" + " return (PyObject *)wrap;\n" + "}\n\n"; + + } else { + // Write out a regular, unwrapped getter. + out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n"; + FunctionRemap *remap = property->_getter_remaps.front(); + + if (remap->_has_this) { + if (remap->_const_method) { + out << " const " << cClassName << " *local_this = NULL;\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; + } else { + out << " " << cClassName << " *local_this = NULL;\n"; + out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \"" + << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n"; + } + out << " return NULL;\n"; + out << " }\n\n"; + } + + if (property->_has_function != NULL) { + if (remap->_has_this) { + out << " if (!local_this->" << property->_has_function->_ifunc.get_name() << "()) {\n"; + } else { + out << " if (!" << cClassName << "::" << property->_has_function->_ifunc.get_name() << "()) {\n"; + } + out << " Py_INCREF(Py_None);\n" << " return Py_None;\n" << " }\n"; } @@ -6436,28 +6877,37 @@ write_getset(ostream &out, Object *obj, Property *property) { out << "}\n\n"; // Write out a setter if this is not a read-only property. - if (property->_setter != NULL) { + if (!property->_setter_remaps.empty()) { out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter(PyObject *self, PyObject *arg, void *) {\n"; - out << " " << cClassName << " *local_this = NULL;\n"; - out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \"" - << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n"; - out << " return -1;\n"; - out << " }\n\n"; + if (remap->_has_this) { + out << " " << cClassName << " *local_this = NULL;\n"; + out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \"" + << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n"; + out << " return -1;\n"; + out << " }\n\n"; + } out << " if (arg == (PyObject *)NULL) {\n"; - if (property->_deleter != NULL) { + if (property->_deleter != NULL && remap->_has_this) { out << " local_this->" << property->_deleter->_ifunc.get_name() << "();\n" << " return 0;\n"; + } else if (property->_deleter != NULL) { + out << " " << cClassName << "::" << property->_deleter->_ifunc.get_name() << "();\n" + << " return 0;\n"; } else { out << " Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << " attribute\");\n" - " return -1;\n"; + " return -1;\n"; } out << " }\n"; if (property->_clear_function != NULL) { - out << " if (arg == Py_None) {\n" - << " local_this->" << property->_clear_function->_ifunc.get_name() << "();\n" - << " return 0;\n" + out << " if (arg == Py_None) {\n"; + if (remap->_has_this) { + out << " local_this->" << property->_clear_function->_ifunc.get_name() << "();\n"; + } else { + out << " " << cClassName << "::" << property->_clear_function->_ifunc.get_name() << "();\n"; + } + out << " return 0;\n" << " }\n"; } @@ -6465,9 +6915,9 @@ write_getset(ostream &out, Object *obj, Property *property) { // Extract only the setters that take one argument. Function::Remaps::iterator it; - for (it = property->_setter->_remaps.begin(); - it != property->_setter->_remaps.end(); - ++it) { + for (it = property->_setter_remaps.begin(); + it != property->_setter_remaps.end(); + ++it) { FunctionRemap *remap = *it; int min_num_args = remap->get_min_num_args(); int max_num_args = remap->get_max_num_args(); @@ -6579,54 +7029,8 @@ record_object(TypeIndex type_index) { ElementIndex element_index = itype.get_element(ei); const InterrogateElement &ielement = idb->get_element(element_index); - Property *property = new Property(ielement); - - if (ielement.has_setter()) { - FunctionIndex func_index = ielement.get_setter(); - Function *setter = record_function(itype, func_index); - if (is_function_legal(setter)) { - property->_setter = setter; - } - } - - if (ielement.has_getter()) { - FunctionIndex func_index = ielement.get_getter(); - Function *getter = record_function(itype, func_index); - if (is_function_legal(getter)) { - property->_getter = getter; - } - } - - if (ielement.has_has_function()) { - FunctionIndex func_index = ielement.get_has_function(); - Function *has_function = record_function(itype, func_index); - if (is_function_legal(has_function)) { - property->_has_function = has_function; - } - } - - if (ielement.has_clear_function()) { - FunctionIndex func_index = ielement.get_clear_function(); - Function *clear_function = record_function(itype, func_index); - if (is_function_legal(clear_function)) { - property->_clear_function = clear_function; - } - } - - if (ielement.has_del_function()) { - FunctionIndex func_index = ielement.get_del_function(); - Function *del_function = record_function(itype, func_index); - if (is_function_legal(del_function)) { - property->_deleter = del_function; - } - } - - if (ielement.is_sequence()) { - FunctionIndex func_index = ielement.get_length_function(); - property->_length_function = record_function(itype, func_index); - } - - if (property->_getter != NULL) { + Property *property = record_property(itype, itype.get_element(ei)); + if (property != nullptr) { object->_properties.push_back(property); } else { // No use exporting a property without a getter. @@ -6658,6 +7062,117 @@ record_object(TypeIndex type_index) { } return object; } + +/** + * + */ +InterfaceMaker::Property *InterfaceMakerPythonNative:: +record_property(const InterrogateType &itype, ElementIndex element_index) { + InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); + const InterrogateElement &ielement = idb->get_element(element_index); + if (!ielement.has_getter()) { + // A property needs at the very least a getter. + return nullptr; + } + + Property *property; + { + FunctionIndex func_index = ielement.get_getter(); + if (func_index != 0) { + const InterrogateFunction &ifunc = idb->get_function(func_index); + property = new Property(ielement); + + InterrogateFunction::Instances::const_iterator ii; + for (ii = ifunc._instances->begin(); ii != ifunc._instances->end(); ++ii) { + CPPInstance *cppfunc = (*ii).second; + FunctionRemap *remap = + make_function_remap(itype, ifunc, cppfunc, 0); + + if (remap != nullptr && is_remap_legal(remap)) { + property->_getter_remaps.push_back(remap); + property->_has_this |= remap->_has_this; + } + } + } else { + return nullptr; + } + } + + if (ielement.has_setter()) { + FunctionIndex func_index = ielement.get_setter(); + if (func_index != 0) { + const InterrogateFunction &ifunc = idb->get_function(func_index); + + InterrogateFunction::Instances::const_iterator ii; + for (ii = ifunc._instances->begin(); ii != ifunc._instances->end(); ++ii) { + CPPInstance *cppfunc = (*ii).second; + FunctionRemap *remap = + make_function_remap(itype, ifunc, cppfunc, 0); + + if (remap != nullptr && is_remap_legal(remap)) { + property->_setter_remaps.push_back(remap); + property->_has_this |= remap->_has_this; + } + } + } + } + + if (ielement.has_has_function()) { + FunctionIndex func_index = ielement.get_has_function(); + Function *has_function = record_function(itype, func_index); + if (is_function_legal(has_function)) { + property->_has_function = has_function; + property->_has_this |= has_function->_has_this; + } + } + + if (ielement.has_clear_function()) { + FunctionIndex func_index = ielement.get_clear_function(); + Function *clear_function = record_function(itype, func_index); + if (is_function_legal(clear_function)) { + property->_clear_function = clear_function; + property->_has_this |= clear_function->_has_this; + } + } + + if (ielement.has_del_function()) { + FunctionIndex func_index = ielement.get_del_function(); + Function *del_function = record_function(itype, func_index); + if (is_function_legal(del_function)) { + property->_deleter = del_function; + property->_has_this |= del_function->_has_this; + } + } + + if (ielement.is_sequence() || ielement.is_mapping()) { + FunctionIndex func_index = ielement.get_length_function(); + if (func_index != 0) { + property->_length_function = record_function(itype, func_index); + } + } + + if (ielement.is_sequence() && ielement.has_insert_function()) { + FunctionIndex func_index = ielement.get_insert_function(); + Function *insert_function = record_function(itype, func_index); + if (is_function_legal(insert_function)) { + property->_inserter = insert_function; + property->_has_this |= insert_function->_has_this; + } + } + + if (ielement.is_mapping() && ielement.has_getkey_function()) { + FunctionIndex func_index = ielement.get_getkey_function(); + assert(func_index != 0); + Function *getkey_function = record_function(itype, func_index); + if (is_function_legal(getkey_function)) { + property->_getkey_function = getkey_function; + property->_has_this |= getkey_function->_has_this; + } + } + + return property; +} + /** * Walks through the set of functions in the database and generates wrappers * for each function, storing these in the database. No actual code should be @@ -6743,7 +7258,7 @@ is_cpp_type_legal(CPPType *in_ctype) { return true; } else if (TypeManager::is_basic_string_wchar(type)) { return true; - } else if (TypeManager::is_vector_unsigned_char(type)) { + } else if (TypeManager::is_vector_unsigned_char(in_ctype)) { return true; } else if (TypeManager::is_simple(type)) { return true; @@ -6852,6 +7367,14 @@ has_coerce_constructor(CPPStructType *type) { return 0; } + // It is convenient to set default-constructability and move-assignability + // as requirement for non-reference-counted objects, since it simplifies the + // implementation and it holds for all classes we need it for. + if (!TypeManager::is_reference_count(type) && + (!type->is_default_constructible() || !type->is_move_assignable())) { + return 0; + } + CPPScope *scope = type->get_scope(); if (scope == NULL) { return 0; diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.h b/dtool/src/interrogate/interfaceMakerPythonNative.h index 8f66fcf3b0..2c0195fdbf 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.h +++ b/dtool/src/interrogate/interfaceMakerPythonNative.h @@ -47,6 +47,7 @@ public: virtual bool separate_overloading(); virtual Object *record_object(TypeIndex type_index); + Property *record_property(const InterrogateType &itype, ElementIndex element_index); protected: virtual string get_wrapper_prefix(); @@ -111,6 +112,9 @@ private: // Decref temporary args object before returning. RF_decref_args = 0x1000, + + // This raises a KeyError on falsey (or -1) return value. + RF_raise_keyerror = 0x4000, }; class SlottedFunctionDef { diff --git a/dtool/src/interrogate/interrogate.cxx b/dtool/src/interrogate/interrogate.cxx index 206132bb31..94175b9d2b 100644 --- a/dtool/src/interrogate/interrogate.cxx +++ b/dtool/src/interrogate/interrogate.cxx @@ -19,6 +19,7 @@ #include "pnotify.h" #include "panda_getopt_long.h" #include "preprocess_argv.h" +#include "pystub.h" #include CPPParser parser; @@ -305,6 +306,8 @@ predefine_macro(CPPParser& parser, const string& inoption) { int main(int argc, char **argv) { + pystub(); + preprocess_argv(argc, argv); string command_line; int i; diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index 83f36c533b..6cbf8f02c0 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -1798,30 +1798,39 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP } string property_name = make_property->get_local_name(&parser); + InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); // First, check to see if it's already there. + ElementIndex index = 0; PropertiesByName::const_iterator tni = _properties_by_name.find(property_name); if (tni != _properties_by_name.end()) { - ElementIndex index = (*tni).second; - return index; + index = (*tni).second; + const InterrogateElement &ielem = idb->get_element(index); + if (ielem._make_property == make_property) { + // This is the same property. + return index; + } + + // It is possible to have property definitions with the same name, but + // they cannot define conflicting interfaces. + if ((ielem.is_sequence() || ielem.is_mapping()) != + (make_property->_type != CPPMakeProperty::T_normal)) { + cerr << "Conflicting property definitions for " << property_name << "!\n"; + return index; + } } // If we have a length function (ie. this is a sequence property), we should // find the function that will give us the length. FunctionIndex length_function = 0; - bool is_seq = false; - - CPPFunctionGroup::Instances::const_iterator fi; CPPFunctionGroup *fgroup = make_property->_length_function; - if (fgroup != NULL) { - is_seq = true; - + if (fgroup != nullptr) { + CPPFunctionGroup::Instances::const_iterator fi; for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) { CPPInstance *function = (*fi); - CPPFunctionType *ftype = - function->_type->as_function_type(); - if (ftype != NULL) { + CPPFunctionType *ftype = function->_type->as_function_type(); + if (ftype != nullptr) { length_function = get_function(function, "", struct_type, struct_type->get_scope(), 0); if (length_function != 0) { @@ -1840,6 +1849,9 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP CPPInstance *getter = NULL; CPPType *return_type = NULL; + // How many arguments we expect the getter to have. + size_t num_args = (size_t)(make_property->_type != CPPMakeProperty::T_normal); + fgroup = make_property->_get_function; if (fgroup != NULL) { CPPFunctionGroup::Instances::const_iterator fi; @@ -1850,12 +1862,29 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP continue; } + const CPPParameterList::Parameters ¶ms = ftype->_parameters->_parameters; + + size_t expected_num_args = 0; + size_t index_arg = 0; + + if (make_property->_type != CPPMakeProperty::T_normal) { + ++expected_num_args; + } + + if (!params.empty() && params[0]->get_simple_name() == "self" && + TypeManager::is_pointer_to_PyObject(params[0]->_type)) { + // Taking a PyObject *self argument. + expected_num_args += 1; + index_arg += 1; + } + // The getter must either take no arguments, or all defaults. - if (ftype->_parameters->_parameters.size() == (size_t)is_seq || - (ftype->_parameters->_parameters.size() > (size_t)is_seq && - ftype->_parameters->_parameters[(size_t)is_seq]->_initializer != NULL)) { + if (params.size() == expected_num_args || + (params.size() > expected_num_args && + params[expected_num_args]->_initializer != NULL)) { // If this is a sequence getter, it must take an index argument. - if (is_seq && !TypeManager::is_integer(ftype->_parameters->_parameters[0]->_type)) { + if (make_property->_type == CPPMakeProperty::T_sequence && + !TypeManager::is_integer(params[index_arg]->_type)) { continue; } @@ -1887,13 +1916,14 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP CPPInstance *function = (*fi); CPPFunctionType *ftype = function->_type->as_function_type(); - if (ftype != NULL && TypeManager::is_bool(ftype->_return_type)) { + if (ftype != nullptr && (TypeManager::is_integer(ftype->_return_type) || + TypeManager::is_pointer(ftype->_return_type))) { hasser = function; break; } } - if (hasser == NULL || return_type == NULL) { + if (hasser == nullptr) { cerr << "No instance of has-function '" << fgroup->_name << "' is suitable!\n"; return 0; @@ -1909,44 +1939,127 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) { CPPInstance *function = (*fi); CPPFunctionType *ftype = function->_type->as_function_type(); - if (ftype != NULL && ftype->_parameters->_parameters.size() == (size_t)is_seq) { - deleter = function; - break; + if (ftype != nullptr) { + const CPPParameterList::Parameters ¶ms = ftype->_parameters->_parameters; + if (params.size() == num_args || + (params.size() > num_args && params[num_args]->_initializer != nullptr)) { + deleter = function; + break; + } } } - if (deleter == NULL || return_type == NULL) { + if (deleter == nullptr) { cerr << "No instance of delete-function '" << fgroup->_name << "' is suitable!\n"; return 0; } } - InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - // It isn't here, so we'll have to define it. - ElementIndex index = idb->get_next_index(); - _properties_by_name[property_name] = index; + // And the "inserter". + CPPInstance *inserter = nullptr; - InterrogateElement iproperty; - iproperty._name = make_property->get_simple_name(); - iproperty._scoped_name = descope(make_property->get_local_name(&parser)); + fgroup = make_property->_insert_function; + if (fgroup != nullptr) { + CPPFunctionGroup::Instances::const_iterator fi; + for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) { + CPPInstance *function = (*fi); + CPPFunctionType *ftype = function->_type->as_function_type(); + if (ftype != nullptr && ftype->_parameters->_parameters.size() == 2) { + inserter = function; + break; + } + } + + if (inserter == nullptr) { + cerr << "No instance of insert-function '" + << fgroup->_name << "' is suitable!\n"; + return 0; + } + } + + // And the function that returns a key by index. + CPPInstance *getkey_function = nullptr; + + fgroup = make_property->_get_key_function; + if (fgroup != nullptr) { + CPPFunctionGroup::Instances::const_iterator fi; + for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) { + CPPInstance *function = (*fi); + CPPFunctionType *ftype = function->_type->as_function_type(); + if (ftype != nullptr) { + getkey_function = function; + break; + } + } + + if (getkey_function == nullptr) { + cerr << "No instance of get-key-function '" + << fgroup->_name << "' is suitable!\n"; + return 0; + } + } + + if (index == 0) { + // It isn't here, so we'll have to define it. + index = idb->get_next_index(); + _properties_by_name[property_name] = index; + + InterrogateElement iproperty; + iproperty._name = make_property->get_simple_name(); + iproperty._scoped_name = descope(make_property->get_local_name(&parser)); + idb->add_element(index, iproperty); + } + + InterrogateElement &iproperty = idb->update_element(index); if (return_type != NULL) { - iproperty._type = get_type(TypeManager::unwrap_reference(return_type), false); + TypeIndex return_index = get_type(TypeManager::unwrap_reference(return_type), false); + if (iproperty._type != 0 && iproperty._type != return_index) { + cerr << "Property " << property_name << " has inconsistent element type!\n"; + } } else { iproperty._type = 0; } - if (length_function != 0) { + if (make_property->_type & CPPMakeProperty::T_sequence) { iproperty._flags |= InterrogateElement::F_sequence; iproperty._length_function = length_function; + assert(length_function != 0); + } + + if (make_property->_type & CPPMakeProperty::T_mapping) { + iproperty._flags |= InterrogateElement::F_mapping; + iproperty._length_function = length_function; } - if (getter != NULL) { - iproperty._flags |= InterrogateElement::F_has_getter; - iproperty._getter = get_function(getter, "", struct_type, - struct_type->get_scope(), 0); - nassertr(iproperty._getter, 0); + if (make_property->_type == CPPMakeProperty::T_normal) { + if (getter != NULL) { + iproperty._flags |= InterrogateElement::F_has_getter; + iproperty._getter = get_function(getter, "", struct_type, + struct_type->get_scope(), 0); + nassertr(iproperty._getter, 0); + } + } else { + // We could have a mixed sequence/mapping property, so synthesize a + // getitem function. We don't really care what's in here; we just use + // this to store the remaps. + if (!iproperty.has_getter()) { + iproperty._flags |= InterrogateElement::F_has_getter; + iproperty._getter = InterrogateDatabase::get_ptr()->get_next_index(); + InterrogateFunction *ifunction = new InterrogateFunction; + ifunction->_instances = new InterrogateFunction::Instances; + InterrogateDatabase::get_ptr()->add_function(iproperty._getter, ifunction); + } + + // Add our getter to the generated getitem function. + string signature = TypeManager::get_function_signature(getter); + InterrogateFunction &ifunction = + InterrogateDatabase::get_ptr()->update_function(iproperty._getter); + if (ifunction._instances == nullptr) { + ifunction._instances = new InterrogateFunction::Instances; + } + ifunction._instances->insert(InterrogateFunction::Instances::value_type(signature, getter)); } if (hasser != NULL) { @@ -1963,9 +2076,27 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP nassertr(iproperty._del_function, 0); } + if (inserter != NULL) { + iproperty._flags |= InterrogateElement::F_has_insert_function; + iproperty._insert_function = get_function(inserter, "", struct_type, + struct_type->get_scope(), 0); + nassertr(iproperty._insert_function, 0); + } + + if (getkey_function != NULL) { + iproperty._flags |= InterrogateElement::F_has_getkey_function; + iproperty._getkey_function = get_function(getkey_function, "", struct_type, + struct_type->get_scope(), 0); + nassertr(iproperty._getkey_function, 0); + } + // See if there happens to be a comment before the MAKE_PROPERTY macro. if (make_property->_leading_comment != (CPPCommentBlock *)NULL) { iproperty._comment = trim_blanks(make_property->_leading_comment->_comment); + + } else if (getter->_leading_comment != (CPPCommentBlock *)NULL) { + // Take the comment from the getter. + iproperty._comment = trim_blanks(getter->_leading_comment->_comment); } // Now look for setters. @@ -1995,7 +2126,6 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP } } - idb->add_element(index, iproperty); return index; } @@ -2346,6 +2476,10 @@ define_atomic_type(InterrogateType &itype, CPPSimpleType *cpptype) { itype._atomic_token = AT_void; break; + case CPPSimpleType::T_nullptr: + itype._atomic_token = AT_null; + break; + default: nout << "Type \"" << *cpptype << "\" has invalid CPPSimpleType: " << (int)cpptype->_type << "\n"; @@ -2420,6 +2554,10 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, break; } + if (cpptype->is_final()) { + itype._flags |= InterrogateType::F_final; + } + if (cpptype->_file.is_c_file()) { // This type declaration appears in a .C file. We can only export types // defined in a .h file. @@ -2597,7 +2735,9 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, } else if ((*di)->get_subtype() == CPPDeclaration::ST_make_property) { ElementIndex element_index = get_make_property((*di)->as_make_property(), cpptype, scope); - itype._elements.push_back(element_index); + if (find(itype._elements.begin(), itype._elements.end(), element_index) == itype._elements.end()) { + itype._elements.push_back(element_index); + } } else if ((*di)->get_subtype() == CPPDeclaration::ST_make_seq) { MakeSeqIndex make_seq_index = get_make_seq((*di)->as_make_seq(), cpptype); diff --git a/dtool/src/interrogate/interrogate_module.cxx b/dtool/src/interrogate/interrogate_module.cxx index 8e397d8b9e..5feae341b7 100644 --- a/dtool/src/interrogate/interrogate_module.cxx +++ b/dtool/src/interrogate/interrogate_module.cxx @@ -19,6 +19,7 @@ #include "interrogate_interface.h" #include "interrogate_request.h" #include "load_dso.h" +#include "pystub.h" #include "pnotify.h" #include "panda_getopt_long.h" #include "preprocess_argv.h" @@ -379,6 +380,8 @@ int main(int argc, char *argv[]) { extern int optind; int flag; + pystub(); + preprocess_argv(argc, argv); flag = getopt_long_only(argc, argv, short_options, long_options, NULL); while (flag != EOF) { diff --git a/dtool/src/interrogate/parse_file.cxx b/dtool/src/interrogate/parse_file.cxx index 2b32f39561..6670bcbe44 100644 --- a/dtool/src/interrogate/parse_file.cxx +++ b/dtool/src/interrogate/parse_file.cxx @@ -22,6 +22,7 @@ #include "cppGlobals.h" #include "panda_getopt_long.h" #include "preprocess_argv.h" +#include "pystub.h" #include CPPParser parser; @@ -193,6 +194,8 @@ show_nested_types(const string &str) { int main(int argc, char **argv) { + pystub(); + extern char *optarg; extern int optind; const char *optstr = "I:S:D:o:l:vp"; diff --git a/dtool/src/interrogate/typeManager.cxx b/dtool/src/interrogate/typeManager.cxx index db5b07f58a..1485c785ca 100644 --- a/dtool/src/interrogate/typeManager.cxx +++ b/dtool/src/interrogate/typeManager.cxx @@ -364,6 +364,27 @@ is_const_ref_to_enum(CPPType *type) { } } +/** + * Returns true if the indicated type is nullptr_t, possibly const or a + * typedef to it. + */ +bool TypeManager:: +is_nullptr(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_simple: + return type->as_simple_type()->_type == CPPSimpleType::T_nullptr; + + case CPPDeclaration::ST_const: + return is_nullptr(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_typedef: + return is_nullptr(type->as_typedef_type()->_type); + + default: + return false; + } +} + /** * Returns true if the indicated type is something that a scripting language * can handle directly as a concrete, like an int or float, either const or @@ -2496,55 +2517,3 @@ is_local(CPPType *source_type) { return false; */ } - -/** - * Returns true if the type is trivial (or trivial enough for our purposes). - */ -bool TypeManager:: -is_trivial(CPPType *source_type) { - switch (source_type->get_subtype()) { - case CPPDeclaration::ST_const: - return is_trivial(source_type->as_const_type()->_wrapped_around); - - case CPPDeclaration::ST_reference: - return false; - - case CPPDeclaration::ST_pointer: - return true; - - case CPPDeclaration::ST_simple: - return true; - - case CPPDeclaration::ST_typedef: - return is_trivial(source_type->as_typedef_type()->_type); - - default: - if (source_type->is_trivial() || is_handle(source_type)) { - return true; - } else { - // This is a bit of a hack. is_trivial() returns false for types that - // have an empty constructor (since we can't use =default yet). For the - // other classes, it's just convenient to consider them trivial even if - // they aren't, since they are simple enough. - string name = source_type->get_simple_name(); - return (name == "ButtonHandle" || name == "DatagramIterator" || - name == "BitMask" || name == "Filename" || name == "pixel" || - name == "NodePath" || name == "LoaderOptions" || - name == "PointerToArray" || name == "ConstPointerToArray" || - name == "PStatThread" || - (name.size() >= 6 && name.substr(0, 6) == "LPlane") || - (name.size() > 6 && name.substr(0, 6) == "LPoint") || - (name.size() > 7 && name.substr(0, 7) == "LVector") || - (name.size() > 7 && name.substr(0, 7) == "LMatrix") || - (name.size() > 8 && name.substr(0, 8) == "LVecBase") || - (name.size() >= 9 && name.substr(0, 9) == "LParabola") || - (name.size() >= 9 && name.substr(0, 9) == "LRotation") || - (name.size() >= 11 && name.substr(0, 11) == "LQuaternion") || - (name.size() >= 12 && name.substr(0, 12) == "LOrientation") || - (name.size() > 16 && name.substr(0, 16) == "UnalignedLMatrix") || - (name.size() > 17 && name.substr(0, 17) == "UnalignedLVecBase")); - } - } - - return false; -} diff --git a/dtool/src/interrogate/typeManager.h b/dtool/src/interrogate/typeManager.h index 4e5356a24e..65f192edf7 100644 --- a/dtool/src/interrogate/typeManager.h +++ b/dtool/src/interrogate/typeManager.h @@ -55,6 +55,7 @@ public: static bool is_enum(CPPType *type); static bool is_const_enum(CPPType *type); static bool is_const_ref_to_enum(CPPType *type); + static bool is_nullptr(CPPType *type); static bool is_simple(CPPType *type); static bool is_const_simple(CPPType *type); static bool is_const_ref_to_simple(CPPType *type); @@ -148,7 +149,6 @@ public: static bool is_exported(CPPType *type); static bool is_local(CPPType *type); - static bool is_trivial(CPPType *type); }; #endif diff --git a/dtool/src/interrogatedb/interrogateDatabase.cxx b/dtool/src/interrogatedb/interrogateDatabase.cxx index 559f707ca5..c11675d352 100644 --- a/dtool/src/interrogatedb/interrogateDatabase.cxx +++ b/dtool/src/interrogatedb/interrogateDatabase.cxx @@ -20,7 +20,7 @@ InterrogateDatabase *InterrogateDatabase::_global_ptr = NULL; int InterrogateDatabase::_file_major_version = 0; int InterrogateDatabase::_file_minor_version = 0; int InterrogateDatabase::_current_major_version = 3; -int InterrogateDatabase::_current_minor_version = 2; +int InterrogateDatabase::_current_minor_version = 3; /** * diff --git a/dtool/src/interrogatedb/interrogateElement.I b/dtool/src/interrogatedb/interrogateElement.I index 7e3ba61f77..914aba2327 100644 --- a/dtool/src/interrogatedb/interrogateElement.I +++ b/dtool/src/interrogatedb/interrogateElement.I @@ -25,7 +25,10 @@ InterrogateElement(InterrogateModuleDef *def) : _has_function = 0; _clear_function = 0; _del_function = 0; + _insert_function = 0; + _getkey_function = 0; _length_function = 0; + _make_property = nullptr; } /** @@ -51,7 +54,10 @@ operator = (const InterrogateElement ©) { _has_function = copy._has_function; _clear_function = copy._clear_function; _del_function = copy._del_function; + _insert_function = copy._insert_function; + _getkey_function = copy._getkey_function; _length_function = copy._length_function; + _make_property = copy._make_property; } /** @@ -183,6 +189,38 @@ get_del_function() const { return _del_function; } +/** + * + */ +INLINE bool InterrogateElement:: +has_insert_function() const { + return (_flags & F_has_insert_function) != 0; +} + +/** + * + */ +INLINE FunctionIndex InterrogateElement:: +get_insert_function() const { + return _insert_function; +} + +/** + * + */ +INLINE bool InterrogateElement:: +has_getkey_function() const { + return (_flags & F_has_getkey_function) != 0; +} + +/** + * + */ +INLINE FunctionIndex InterrogateElement:: +get_getkey_function() const { + return _getkey_function; +} + /** * */ @@ -199,6 +237,14 @@ get_length_function() const { return _length_function; } +/** + * + */ +INLINE bool InterrogateElement:: +is_mapping() const { + return (_flags & F_mapping) != 0; +} + INLINE ostream & operator << (ostream &out, const InterrogateElement &element) { diff --git a/dtool/src/interrogatedb/interrogateElement.cxx b/dtool/src/interrogatedb/interrogateElement.cxx index a7469b9941..38c7c8f4ec 100644 --- a/dtool/src/interrogatedb/interrogateElement.cxx +++ b/dtool/src/interrogatedb/interrogateElement.cxx @@ -29,7 +29,9 @@ output(ostream &out) const { << _has_function << " " << _clear_function << " " << _del_function << " " - << _length_function << " "; + << _length_function << " " + << _insert_function << " " + << _getkey_function << " "; idf_output_string(out, _scoped_name); idf_output_string(out, _comment, '\n'); } @@ -45,6 +47,9 @@ input(istream &in) { in >> _has_function >> _clear_function; if (InterrogateDatabase::get_file_minor_version() >= 2) { in >> _del_function >> _length_function; + if (InterrogateDatabase::get_file_minor_version() >= 3) { + in >> _insert_function >> _getkey_function; + } } } idf_input_string(in, _scoped_name); @@ -63,5 +68,7 @@ remap_indices(const IndexRemapper &remap) { _has_function = remap.map_from(_has_function); _clear_function = remap.map_from(_clear_function); _del_function = remap.map_from(_del_function); + _insert_function = remap.map_from(_insert_function); + _getkey_function = remap.map_from(_getkey_function); _length_function = remap.map_from(_length_function); } diff --git a/dtool/src/interrogatedb/interrogateElement.h b/dtool/src/interrogatedb/interrogateElement.h index 3506d7b9a8..f824b760cf 100644 --- a/dtool/src/interrogatedb/interrogateElement.h +++ b/dtool/src/interrogatedb/interrogateElement.h @@ -19,6 +19,7 @@ #include "interrogateComponent.h" class IndexRemapper; +class CPPMakeProperty; /** * An internal representation of a data element, like a data member or a @@ -49,8 +50,13 @@ public: INLINE FunctionIndex get_clear_function() const; INLINE bool has_del_function() const; INLINE FunctionIndex get_del_function() const; + INLINE bool has_insert_function() const; + INLINE FunctionIndex get_insert_function() const; + INLINE bool has_getkey_function() const; + INLINE FunctionIndex get_getkey_function() const; INLINE bool is_sequence() const; INLINE FunctionIndex get_length_function() const; + INLINE bool is_mapping() const; void output(ostream &out) const; void input(istream &in); @@ -67,6 +73,8 @@ private: F_has_del_function= 0x0020, F_sequence = 0x0040, F_mapping = 0x0080, + F_has_insert_function= 0x0100, + F_has_getkey_function= 0x0200, }; int _flags; @@ -79,6 +87,10 @@ private: FunctionIndex _has_function; FunctionIndex _clear_function; FunctionIndex _del_function; + FunctionIndex _insert_function; + FunctionIndex _getkey_function; + + CPPMakeProperty *_make_property; friend class InterrogateBuilder; }; diff --git a/dtool/src/interrogatedb/interrogateType.I b/dtool/src/interrogatedb/interrogateType.I index f6037fc571..840fdb18f3 100644 --- a/dtool/src/interrogatedb/interrogateType.I +++ b/dtool/src/interrogatedb/interrogateType.I @@ -289,6 +289,14 @@ is_union() const { return (_flags & F_union) != 0; } +/** + * + */ +INLINE bool InterrogateType:: +is_final() const { + return (_flags & F_final) != 0; +} + /** * */ @@ -343,6 +351,14 @@ destructor_is_inherited() const { return (_flags & F_inherited_destructor) != 0; } +/** + * + */ +INLINE bool InterrogateType:: +destructor_is_implicit() const { + return (_flags & F_implicit_destructor) != 0; +} + /** * */ diff --git a/dtool/src/interrogatedb/interrogateType.h b/dtool/src/interrogatedb/interrogateType.h index a1cedd1d82..176efb4fb9 100644 --- a/dtool/src/interrogatedb/interrogateType.h +++ b/dtool/src/interrogatedb/interrogateType.h @@ -75,6 +75,7 @@ public: INLINE bool is_struct() const; INLINE bool is_class() const; INLINE bool is_union() const; + INLINE bool is_final() const; INLINE bool is_fully_defined() const; INLINE bool is_unpublished() const; @@ -82,6 +83,7 @@ public: INLINE FunctionIndex get_constructor(int n) const; INLINE bool has_destructor() const; INLINE bool destructor_is_inherited() const; + INLINE bool destructor_is_implicit() const; INLINE FunctionIndex get_destructor() const; INLINE int number_of_elements() const; INLINE ElementIndex get_element(int n) const; @@ -138,6 +140,7 @@ private: F_typedef = 0x200000, F_array = 0x400000, F_scoped_enum = 0x800000, + F_final =0x1000000, }; public: diff --git a/dtool/src/interrogatedb/interrogate_interface.h b/dtool/src/interrogatedb/interrogate_interface.h index b3003be2fe..63e385d527 100644 --- a/dtool/src/interrogatedb/interrogate_interface.h +++ b/dtool/src/interrogatedb/interrogate_interface.h @@ -82,7 +82,11 @@ enum AtomicToken { // string means whatever the native string representation is. AT_string = 7, - AT_longlong = 8 + AT_longlong = 8, + + // This is not a type that C has, but C++ and many scripting languages do; + // it indicates a null value, or the absence of any value. + AT_null = 9, }; EXPCL_INTERROGATEDB void interrogate_add_search_directory(const char *dirname); diff --git a/dtool/src/interrogatedb/p3interrogatedb_composite2.cxx b/dtool/src/interrogatedb/p3interrogatedb_composite2.cxx index c6b6c61362..fda72f2ce8 100644 --- a/dtool/src/interrogatedb/p3interrogatedb_composite2.cxx +++ b/dtool/src/interrogatedb/p3interrogatedb_composite2.cxx @@ -5,7 +5,5 @@ #include "interrogate_interface.cxx" #include "interrogate_request.cxx" #include "py_panda.cxx" - - - - +#include "py_compat.cxx" +#include "py_wrappers.cxx" diff --git a/dtool/src/interrogatedb/py_compat.cxx b/dtool/src/interrogatedb/py_compat.cxx new file mode 100755 index 0000000000..f7f02607c0 --- /dev/null +++ b/dtool/src/interrogatedb/py_compat.cxx @@ -0,0 +1,54 @@ +/** + * 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 py_compat.cxx + * @author rdb + * @date 2017-12-03 + */ + +#include "py_compat.h" + +#ifdef HAVE_PYTHON + +PyTupleObject Dtool_EmptyTuple = {PyVarObject_HEAD_INIT(nullptr, 0)}; + +#if PY_MAJOR_VERSION < 3 +/** + * Given a long or int, returns a size_t, or raises an OverflowError if it is + * out of range. + */ +size_t PyLongOrInt_AsSize_t(PyObject *vv) { + if (PyInt_Check(vv)) { + long value = PyInt_AS_LONG(vv); + if (value < 0) { + PyErr_SetString(PyExc_OverflowError, + "can't convert negative value to size_t"); + return (size_t)-1; + } + return (size_t)value; + } + + if (!PyLong_Check(vv)) { + Dtool_Raise_TypeError("a long or int was expected"); + return (size_t)-1; + } + + size_t bytes; + int one = 1; + int res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes, + SIZEOF_SIZE_T, (int)*(unsigned char*)&one, 0); + + if (res < 0) { + return (size_t)res; + } else { + return bytes; + } +} +#endif + +#endif // HAVE_PYTHON diff --git a/dtool/src/interrogatedb/py_compat.h b/dtool/src/interrogatedb/py_compat.h new file mode 100755 index 0000000000..46537114aa --- /dev/null +++ b/dtool/src/interrogatedb/py_compat.h @@ -0,0 +1,175 @@ +/** + * 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 py_compat.h + * @author rdb + * @date 2017-12-02 + */ + +#ifndef PY_COMPAT_H +#define PY_COMPAT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +// The contents of this file were originally part of py_panda.h. It +// specifically contains polyfills that are required to maintain compatibility +// with Python 2 and older versions of Python 3. + +// These compatibility hacks are sorted by Python version that removes the +// need for the respective hack. + +#ifdef _POSIX_C_SOURCE +# undef _POSIX_C_SOURCE +#endif + +#ifdef _XOPEN_SOURCE +# undef _XOPEN_SOURCE +#endif + +// See PEP 353 +#define PY_SSIZE_T_CLEAN 1 + +#include "Python.h" + +/* Python 2.4 */ + +// 2.4 macros which aren't available in 2.3 +#ifndef Py_RETURN_NONE +# define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None +#endif + +#ifndef Py_RETURN_TRUE +# define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True +#endif + +#ifndef Py_RETURN_FALSE +# define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False +#endif + +/* Python 2.5 */ + +// Prior to Python 2.5, we didn't have Py_ssize_t. +#if PY_VERSION_HEX < 0x02050000 +typedef int Py_ssize_t; +# define PyInt_FromSsize_t PyInt_FromLong +# define PyInt_AsSsize_t PyInt_AsLong +#endif + +/* Python 2.6 */ + +#ifndef Py_TYPE +# define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) +#endif + +/* Python 2.7, 3.1 */ + +#ifndef PyVarObject_HEAD_INIT + #define PyVarObject_HEAD_INIT(type, size) \ + PyObject_HEAD_INIT(type) size, +#endif + +/* Python 2.7, 3.2 */ + +#if PY_VERSION_HEX < 0x03020000 +# define PyErr_NewExceptionWithDoc(name, doc, base, dict) \ + PyErr_NewException(name, base, dict) +#endif + +/* Python 3.0 */ + +// Always on in Python 3 +#ifndef Py_TPFLAGS_CHECKTYPES +# define Py_TPFLAGS_CHECKTYPES 0 +#endif + +// Macros for writing code that will compile in both versions. +#if PY_MAJOR_VERSION >= 3 +# define nb_nonzero nb_bool +# define nb_divide nb_true_divide +# define nb_inplace_divide nb_inplace_true_divide + +# define PyLongOrInt_Check(x) PyLong_Check(x) +# define PyLongOrInt_AS_LONG PyLong_AS_LONG +# define PyInt_Check PyLong_Check +# define PyInt_AsLong PyLong_AsLong +# define PyInt_AS_LONG PyLong_AS_LONG +# define PyLongOrInt_AsSize_t PyLong_AsSize_t +#else +# define PyLongOrInt_Check(x) (PyInt_Check(x) || PyLong_Check(x)) +// PyInt_FromSize_t automatically picks the right type. +# define PyLongOrInt_AS_LONG PyInt_AsLong + +EXPCL_INTERROGATEDB size_t PyLongOrInt_AsSize_t(PyObject *); +#endif + +// Which character to use in PyArg_ParseTuple et al for a byte string. +#if PY_MAJOR_VERSION >= 3 +# define FMTCHAR_BYTES "y" +#else +# define FMTCHAR_BYTES "s" +#endif + +/* Python 3.2 */ + +#if PY_VERSION_HEX < 0x03020000 +typedef long Py_hash_t; +#endif + +/* Python 3.3 */ + +#if PY_MAJOR_VERSION >= 3 +// Python 3 versions before 3.3.3 defined this incorrectly. +# undef _PyErr_OCCURRED +# define _PyErr_OCCURRED() (PyThreadState_GET()->curexc_type) + +// Python versions before 3.3 did not define this. +# if PY_VERSION_HEX < 0x03030000 +# define PyUnicode_AsUTF8 _PyUnicode_AsString +# define PyUnicode_AsUTF8AndSize _PyUnicode_AsStringAndSize +# endif +#endif + +/* Python 3.6 */ + +// Used to implement _PyObject_CallNoArg +extern EXPCL_INTERROGATEDB PyTupleObject Dtool_EmptyTuple; + +#ifndef _PyObject_CallNoArg +# define _PyObject_CallNoArg(func) PyObject_Call((func), (PyObject *)&Dtool_EmptyTuple, nullptr) +#endif + +// Python versions before 3.6 didn't require longlong support to be enabled. +#ifndef HAVE_LONG_LONG +# define PyLong_FromLongLong(x) PyLong_FromLong((long) (x)) +# define PyLong_FromUnsignedLongLong(x) PyLong_FromUnsignedLong((unsigned long) (x)) +# define PyLong_AsLongLong(x) PyLong_AsLong(x) +# define PyLong_AsUnsignedLongLong(x) PyLong_AsUnsignedLong(x) +# define PyLong_AsUnsignedLongLongMask(x) PyLong_AsUnsignedLongMask(x) +# define PyLong_AsLongLongAndOverflow(x) PyLong_AsLongAndOverflow(x) +#endif + +/* Python 3.7 */ + +#ifndef PyDict_GET_SIZE +# define PyDict_GET_SIZE(mp) (((PyDictObject *)mp)->ma_used) +#endif + +/* Other Python implementations */ + +// _PyErr_OCCURRED is an undocumented macro version of PyErr_Occurred. +// Some implementations of the CPython API (e.g. PyPy's cpyext) do not define +// it, so in these cases we just silently fall back to PyErr_Occurred. +#ifndef _PyErr_OCCURRED +# define _PyErr_OCCURRED() PyErr_Occurred() +#endif + +#endif // HAVE_PYTHON + +#endif // PY_COMPAT_H diff --git a/dtool/src/interrogatedb/py_panda.I b/dtool/src/interrogatedb/py_panda.I index 0a06358beb..1986a128c5 100644 --- a/dtool/src/interrogatedb/py_panda.I +++ b/dtool/src/interrogatedb/py_panda.I @@ -11,20 +11,54 @@ * @date 2016-06-06 */ +#ifdef _MSC_VER +#define _IS_FINAL(T) (__is_sealed(T)) +#elif defined(__GNUC__) +#define _IS_FINAL(T) (__is_final(T)) +#else +#define _IS_FINAL(T) (0) +#endif + /** * Template function that can be used to extract any TypedObject pointer from * a wrapped Python object. */ template INLINE bool -DTOOL_Call_ExtractThisPointer(PyObject *self, T *&into) { - if (DtoolCanThisBeAPandaInstance(self)) { +DtoolInstance_GetPointer(PyObject *self, T *&into) { + if (DtoolInstance_Check(self)) { Dtool_PyTypedObject *target_class = Dtool_RuntimeTypeDtoolType(get_type_handle(T).get_index()); - if (target_class != NULL) { - into = (T*) ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, target_class); - return (into != NULL); + if (target_class != nullptr) { + if (_IS_FINAL(T)) { + if (DtoolInstance_TYPE(self) == target_class) { + into = (T *)DtoolInstance_VOID_PTR(self); + } + } else { + into = (T *)DtoolInstance_UPCAST(self, *target_class); + } + return (into != nullptr); } } - into = NULL; + into = nullptr; + return false; +} + +/** + * Template function that can be used to extract any TypedObject pointer from + * a wrapped Python object. In this case, the Dtool_PyTypedObject is known. + */ +template INLINE bool +DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &target_class) { + if (DtoolInstance_Check(self)) { + if (_IS_FINAL(T)) { + if (DtoolInstance_TYPE(self) == &target_class) { + into = (T *)DtoolInstance_VOID_PTR(self); + } + } else { + into = (T *)DtoolInstance_UPCAST(self, target_class); + } + return (into != nullptr); + } + into = nullptr; return false; } @@ -59,6 +93,23 @@ DTool_CreatePyInstanceTyped(T *obj, bool memory_rules) { return DTool_CreatePyInstanceTyped((void*) obj, *known_class, memory_rules, false, obj->get_type().get_index()); } +/** + * Checks that the tuple is empty. + */ +ALWAYS_INLINE bool +Dtool_CheckNoArgs(PyObject *args) { + return PyTuple_GET_SIZE(args) == 0; +} + +/** + * Checks that the tuple is empty, and that the dict is empty or NULL. + */ +ALWAYS_INLINE bool +Dtool_CheckNoArgs(PyObject *args, PyObject *kwds) { + return PyTuple_GET_SIZE(args) == 0 && + (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0); +} + /** * The following functions wrap an arbitrary C++ value into a PyObject. */ @@ -196,11 +247,16 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(wchar_t value) { return PyUnicode_FromWideChar(&value, 1); } +ALWAYS_INLINE PyObject *Dtool_WrapValue(nullptr_t) { + Py_INCREF(Py_None); + return Py_None; +} + ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value) { return value; } -ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::vector &value) { +ALWAYS_INLINE PyObject *Dtool_WrapValue(const vector_uchar &value) { #if PY_MAJOR_VERSION >= 3 return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size()); #else diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index 9fe9916573..76733237a8 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -31,33 +31,14 @@ static RuntimeTypeMap runtime_type_map; static RuntimeTypeSet runtime_type_set; static NamedTypeMap named_type_map; -/** - * Given a valid (non-NULL) PyObject, does a simple check to see if it might - * be an instance of a Panda type. It does this using a signature that is - * encoded on each instance. - */ -bool DtoolCanThisBeAPandaInstance(PyObject *self) { - // simple sanity check for the class type..size.. will stop basic foobars.. - // It is arguably better to use something like this: - // PyType_IsSubtype(Py_TYPE(self), &Dtool_DTOOL_SUPER_BASE._PyType) ...but - // probably not as fast. - if (Py_TYPE(self)->tp_basicsize >= (int)sizeof(Dtool_PyInstDef)) { - Dtool_PyInstDef *pyself = (Dtool_PyInstDef *) self; - if (pyself->_signature == PY_PANDA_SIGNATURE) { - return true; - } - } - return false; -} - /** */ void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *classdef, void **answer) { - if (DtoolCanThisBeAPandaInstance(self)) { - *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, classdef); + if (DtoolInstance_Check(self)) { + *answer = DtoolInstance_UPCAST(self, *classdef); } else { - *answer = NULL; + *answer = nullptr; } } @@ -67,12 +48,12 @@ void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *c * was of the wrong type, raises an AttributeError. */ bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer) { - if (self == NULL || !DtoolCanThisBeAPandaInstance(self) || ((Dtool_PyInstDef *)self)->_ptr_to_object == NULL) { + if (self == nullptr || !DtoolInstance_Check(self) || DtoolInstance_VOID_PTR(self) == nullptr) { Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed."); return false; } - *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, &classdef); + *answer = DtoolInstance_UPCAST(self, classdef); return true; } @@ -87,12 +68,12 @@ bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef, void **answer, const char *method_name) { - if (self == NULL || !DtoolCanThisBeAPandaInstance(self) || ((Dtool_PyInstDef *)self)->_ptr_to_object == NULL) { + if (self == nullptr || !DtoolInstance_Check(self) || DtoolInstance_VOID_PTR(self) == nullptr) { Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed."); return false; } - if (((Dtool_PyInstDef *)self)->_is_const) { + if (DtoolInstance_IS_CONST(self)) { // All overloads of this function are non-const. PyErr_Format(PyExc_TypeError, "Cannot call %s() on a const object.", @@ -100,7 +81,7 @@ bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject return false; } - *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, &classdef); + *answer = DtoolInstance_UPCAST(self, classdef); return true; } @@ -130,19 +111,19 @@ void * DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, int param, const string &function_name, bool const_ok, bool report_errors) { - // if (PyErr_Occurred()) { return NULL; } - if (self == NULL) { + // if (PyErr_Occurred()) { return nullptr; } + if (self == nullptr) { if (report_errors) { - return Dtool_Raise_TypeError("self is NULL"); + return Dtool_Raise_TypeError("self is nullptr"); } - return NULL; + return nullptr; } - if (DtoolCanThisBeAPandaInstance(self)) { - void *result = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, classdef); + if (DtoolInstance_Check(self)) { + void *result = DtoolInstance_UPCAST(self, *classdef); - if (result != NULL) { - if (const_ok || !((Dtool_PyInstDef *)self)->_is_const) { + if (result != nullptr) { + if (const_ok || !DtoolInstance_IS_CONST(self)) { return result; } @@ -151,7 +132,7 @@ DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, "%s() argument %d may not be const", function_name.c_str(), param); } - return NULL; + return nullptr; } } @@ -159,17 +140,14 @@ DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, return Dtool_Raise_ArgTypeError(self, param, function_name.c_str(), classdef->_PyType.tp_name); } - return NULL; + return nullptr; } void *DTOOL_Call_GetPointerThis(PyObject *self) { - if (self != NULL) { - if (DtoolCanThisBeAPandaInstance(self)) { - Dtool_PyInstDef * pyself = (Dtool_PyInstDef *) self; - return pyself->_ptr_to_object; - } + if (self != nullptr && DtoolInstance_Check(self)) { + return DtoolInstance_VOID_PTR(self); } - return NULL; + return nullptr; } /** @@ -286,11 +264,11 @@ PyObject *_Dtool_Raise_BadArgumentsError() { * NULL, otherwise Py_None. */ PyObject *_Dtool_Return_None() { - if (_PyErr_OCCURRED()) { + if (UNLIKELY(_PyErr_OCCURRED())) { return NULL; } #ifndef NDEBUG - if (Notify::ptr()->has_assert_failed()) { + if (UNLIKELY(Notify::ptr()->has_assert_failed())) { return Dtool_Raise_AssertionError(); } #endif @@ -303,11 +281,11 @@ PyObject *_Dtool_Return_None() { * NULL, otherwise the given boolean value as a PyObject *. */ PyObject *Dtool_Return_Bool(bool value) { - if (_PyErr_OCCURRED()) { + if (UNLIKELY(_PyErr_OCCURRED())) { return NULL; } #ifndef NDEBUG - if (Notify::ptr()->has_assert_failed()) { + if (UNLIKELY(Notify::ptr()->has_assert_failed())) { return Dtool_Raise_AssertionError(); } #endif @@ -322,11 +300,11 @@ PyObject *Dtool_Return_Bool(bool value) { * increased. */ PyObject *_Dtool_Return(PyObject *value) { - if (_PyErr_OCCURRED()) { + if (UNLIKELY(_PyErr_OCCURRED())) { return NULL; } #ifndef NDEBUG - if (Notify::ptr()->has_assert_failed()) { + if (UNLIKELY(Notify::ptr()->has_assert_failed())) { return Dtool_Raise_AssertionError(); } #endif @@ -575,10 +553,45 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) { dtool_inited = true; if (PyType_Ready(&Dtool_SequenceWrapper_Type) < 0) { - PyErr_SetString(PyExc_TypeError, "PyType_Ready(Dtool_SequenceWrapper)"); - return NULL; + return Dtool_Raise_TypeError("PyType_Ready(Dtool_SequenceWrapper)"); } + if (PyType_Ready(&Dtool_MutableSequenceWrapper_Type) < 0) { + return Dtool_Raise_TypeError("PyType_Ready(Dtool_MutableSequenceWrapper)"); + } + + if (PyType_Ready(&Dtool_MappingWrapper_Type) < 0) { + return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper)"); + } + + if (PyType_Ready(&Dtool_MutableMappingWrapper_Type) < 0) { + return Dtool_Raise_TypeError("PyType_Ready(Dtool_MutableMappingWrapper)"); + } + + if (PyType_Ready(&Dtool_MappingWrapper_Keys_Type) < 0) { + return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper_Keys)"); + } + + if (PyType_Ready(&Dtool_MappingWrapper_Values_Type) < 0) { + return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper_Values)"); + } + + if (PyType_Ready(&Dtool_MappingWrapper_Items_Type) < 0) { + return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper_Items)"); + } + + if (PyType_Ready(&Dtool_GeneratorWrapper_Type) < 0) { + return Dtool_Raise_TypeError("PyType_Ready(Dtool_GeneratorWrapper)"); + } + + if (PyType_Ready(&Dtool_StaticProperty_Type) < 0) { + return Dtool_Raise_TypeError("PyType_Ready(Dtool_StaticProperty_Type)"); + } + +#ifdef Py_TRACE_REFS + _Py_AddToAllObjects((PyObject *)&Dtool_EmptyTuple, 0); +#endif + // Initialize the base class of everything. Dtool_PyModuleClassInit_DTOOL_SUPER_BASE(NULL); } @@ -677,7 +690,7 @@ PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args) { PyObject *to_in = NULL; if (PyArg_UnpackTuple(args, "Dtool_BorrowThisReference", 2, 2, &to_in, &from_in)) { - if (DtoolCanThisBeAPandaInstance(from_in) && DtoolCanThisBeAPandaInstance(to_in)) { + if (DtoolInstance_Check(from_in) && DtoolInstance_Check(to_in)) { Dtool_PyInstDef *from = (Dtool_PyInstDef *) from_in; Dtool_PyInstDef *to = (Dtool_PyInstDef *) to_in; @@ -722,9 +735,8 @@ PyObject *Dtool_AddToDictionary(PyObject *self1, PyObject *args) { } Py_hash_t DTOOL_PyObject_HashPointer(PyObject *self) { - if (self != NULL && DtoolCanThisBeAPandaInstance(self)) { - Dtool_PyInstDef * pyself = (Dtool_PyInstDef *) self; - return (Py_hash_t) pyself->_ptr_to_object; + if (self != nullptr && DtoolInstance_Check(self)) { + return (Py_hash_t)DtoolInstance_VOID_PTR(self); } return -1; } @@ -767,12 +779,16 @@ int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2) { if (func == NULL) { PyErr_Clear(); } else { +#if PY_VERSION_HEX >= 0x03060000 + PyObject *res = _PyObject_FastCall(func, &v2, 1); +#else PyObject *res = NULL; PyObject *args = PyTuple_Pack(1, v2); if (args != NULL) { res = PyObject_Call(func, args, NULL); Py_DECREF(args); } +#endif Py_DECREF(func); PyErr_Clear(); // just in case the function threw an error // only use if the function returns an INT... hmm @@ -844,7 +860,13 @@ PyObject *DTOOL_PyObject_RichCompare(PyObject *v1, PyObject *v2, int op) { * make_copy() method. */ PyObject *copy_from_make_copy(PyObject *self, PyObject *noargs) { - return PyObject_CallMethod(self, (char *)"make_copy", (char *)"()"); + PyObject *callable = PyObject_GetAttrString(self, "make_copy"); + if (callable == NULL) { + return NULL; + } + PyObject *result = _PyObject_CallNoArg(callable); + Py_DECREF(callable); + return result; } /** @@ -852,13 +874,15 @@ PyObject *copy_from_make_copy(PyObject *self, PyObject *noargs) { * copy constructor. */ PyObject *copy_from_copy_constructor(PyObject *self, PyObject *noargs) { - PyObject *this_class = PyObject_Type(self); - if (this_class == NULL) { - return NULL; - } + PyObject *callable = (PyObject *)Py_TYPE(self); - PyObject *result = PyObject_CallFunction(this_class, (char *)"(O)", self); - Py_DECREF(this_class); +#if PY_VERSION_HEX >= 0x03060000 + PyObject *result = _PyObject_FastCall(callable, &self, 1); +#else + PyObject *args = PyTuple_Pack(1, self); + PyObject *result = PyObject_Call(callable, args, NULL); + Py_DECREF(args); +#endif return result; } @@ -868,110 +892,109 @@ PyObject *copy_from_copy_constructor(PyObject *self, PyObject *noargs) { * __copy__(). */ PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) { - return PyObject_CallMethod(self, (char *)"__copy__", (char *)"()"); + PyObject *callable = PyObject_GetAttrString(self, "__copy__"); + if (callable == NULL) { + return NULL; + } + PyObject *result = _PyObject_CallNoArg(callable); + Py_DECREF(callable); + return result; } /** - * This class is returned from properties that require a settable interface, - * ie. something.children[i] = 3. + * A more efficient version of PyArg_ParseTupleAndKeywords for the special + * case where there is only a single PyObject argument. */ -static void Dtool_SequenceWrapper_dealloc(PyObject *self) { - Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; - nassertv(wrap); - Py_DECREF(wrap->_base); -} +bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds, + const char *keyword) { -static Py_ssize_t Dtool_SequenceWrapper_length(PyObject *self) { - Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; - nassertr(wrap, -1); - nassertr(wrap->_len_func, -1); - return wrap->_len_func(wrap->_base); -} - -static PyObject *Dtool_SequenceWrapper_getitem(PyObject *self, Py_ssize_t index) { - Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; - nassertr(wrap, NULL); - nassertr(wrap->_getitem_func, NULL); - return wrap->_getitem_func(wrap->_base, index); -} - -static int Dtool_SequenceWrapper_setitem(PyObject *self, Py_ssize_t index, PyObject *value) { - Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; - nassertr(wrap, -1); - nassertr(wrap->_setitem_func, -1); - return wrap->_setitem_func(wrap->_base, index, value); -} - -static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = { - Dtool_SequenceWrapper_length, - 0, // sq_concat - 0, // sq_repeat - Dtool_SequenceWrapper_getitem, - 0, // sq_slice - Dtool_SequenceWrapper_setitem, - 0, // sq_ass_slice - 0, // sq_contains - 0, // sq_inplace_concat - 0, // sq_inplace_repeat -}; - -PyTypeObject Dtool_SequenceWrapper_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "sequence wrapper", - sizeof(Dtool_SequenceWrapper), - 0, // tp_itemsize - Dtool_SequenceWrapper_dealloc, - 0, // tp_print - 0, // tp_getattr - 0, // tp_setattr -#if PY_MAJOR_VERSION >= 3 - 0, // tp_reserved + if (PyTuple_GET_SIZE(args) == 1) { + if (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0) { + *result = PyTuple_GET_ITEM(args, 0); + return true; + } + } else if (PyTuple_GET_SIZE(args) == 0) { + PyObject *key; + Py_ssize_t ppos = 0; + if (kwds != nullptr && PyDict_GET_SIZE(kwds) == 1 && + PyDict_Next(kwds, &ppos, &key, result)) { + // We got the item, we just need to make sure that it had the right key. +#if PY_VERSION_HEX >= 0x03060000 + return PyUnicode_CheckExact(key) && _PyUnicode_EqualToASCIIString(key, keyword); +#elif PY_MAJOR_VERSION >= 3 + return PyUnicode_CheckExact(key) && PyUnicode_CompareWithASCIIString(key, keyword) == 0; #else - 0, // tp_compare + return PyString_CheckExact(key) && strcmp(PyString_AS_STRING(key), keyword) == 0; #endif - 0, // tp_repr - 0, // tp_as_number - &Dtool_SequenceWrapper_SequenceMethods, - 0, // tp_as_mapping - 0, // tp_hash - 0, // tp_call - 0, // tp_str - PyObject_GenericGetAttr, - PyObject_GenericSetAttr, - 0, // tp_as_buffer - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, - 0, // tp_doc - 0, // tp_traverse - 0, // tp_clear - 0, // tp_richcompare - 0, // tp_weaklistoffset - 0, // tp_iter - 0, // tp_iternext - 0, // tp_methods - 0, // tp_members - 0, // tp_getset - 0, // tp_base - 0, // tp_dict - 0, // tp_descr_get - 0, // tp_descr_set - 0, // tp_dictoffset - 0, // tp_init - PyType_GenericAlloc, - 0, // tp_new - PyObject_Del, - 0, // tp_is_gc - 0, // tp_bases - 0, // tp_mro - 0, // tp_cache - 0, // tp_subclasses - 0, // tp_weaklist - 0, // tp_del -#if PY_VERSION_HEX >= 0x02060000 - 0, // tp_version_tag + } + } + + return false; +} + +/** + * Variant of Dtool_ExtractArg that does not accept a keyword argument. + */ +bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds) { + if (PyTuple_GET_SIZE(args) == 1 && + (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0)) { + *result = PyTuple_GET_ITEM(args, 0); + return true; + } + return false; +} + +/** + * A more efficient version of PyArg_ParseTupleAndKeywords for the special + * case where there is only a single optional PyObject argument. + * + * Returns true if valid (including if there were 0 items), false if there was + * an error, such as an invalid number of parameters. + */ +bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds, + const char *keyword) { + + if (PyTuple_GET_SIZE(args) == 1) { + if (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0) { + *result = PyTuple_GET_ITEM(args, 0); + return true; + } + } else if (PyTuple_GET_SIZE(args) == 0) { + if (kwds != nullptr && PyDict_GET_SIZE(kwds) == 1) { + PyObject *key; + Py_ssize_t ppos = 0; + if (!PyDict_Next(kwds, &ppos, &key, result)) { + return true; + } + + // We got the item, we just need to make sure that it had the right key. +#if PY_VERSION_HEX >= 0x03060000 + return PyUnicode_CheckExact(key) && _PyUnicode_EqualToASCIIString(key, keyword); +#elif PY_MAJOR_VERSION >= 3 + return PyUnicode_CheckExact(key) && PyUnicode_CompareWithASCIIString(key, keyword) == 0; +#else + return PyString_CheckExact(key) && strcmp(PyString_AS_STRING(key), keyword) == 0; #endif -#if PY_VERSION_HEX >= 0x03040000 - 0, // tp_finalize -#endif -}; + } else { + return true; + } + } + + return false; +} + +/** + * Variant of Dtool_ExtractOptionalArg that does not accept a keyword argument. + */ +bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds) { + if (kwds != nullptr && PyDict_GET_SIZE(kwds) != 0) { + return false; + } + if (PyTuple_GET_SIZE(args) == 1) { + *result = PyTuple_GET_ITEM(args, 0); + return true; + } + return (PyTuple_GET_SIZE(args) == 0); +} #endif // HAVE_PYTHON diff --git a/dtool/src/interrogatedb/py_panda.h b/dtool/src/interrogatedb/py_panda.h index f5e77e0dbb..74f13680f8 100644 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -20,121 +20,15 @@ #define Py_DEBUG #endif -#ifndef NO_RUNTIME_TYPES - -#include "dtoolbase.h" -#include "typedObject.h" -#include "typeRegistry.h" - -#endif - #include "pnotify.h" +#include "vector_uchar.h" #if defined(HAVE_PYTHON) && !defined(CPPPARSER) -#ifdef _POSIX_C_SOURCE -#undef _POSIX_C_SOURCE -#endif - -#ifdef _XOPEN_SOURCE -#undef _XOPEN_SOURCE -#endif - -#define PY_SSIZE_T_CLEAN 1 - -#include "Python.h" +// py_compat.h includes Python.h. +#include "py_compat.h" #include "structmember.h" -#ifndef HAVE_LONG_LONG -#define PyLong_FromLongLong(x) PyLong_FromLong((long) (x)) -#define PyLong_FromUnsignedLongLong(x) PyLong_FromUnsignedLong((unsigned long) (x)) -#define PyLong_AsLongLong(x) PyLong_AsLong(x) -#define PyLong_AsUnsignedLongLong(x) PyLong_AsUnsignedLong(x) -#define PyLong_AsUnsignedLongLongMask(x) PyLong_AsUnsignedLongMask(x) -#define PyLong_AsLongLongAndOverflow(x) PyLong_AsLongAndOverflow(x) -#endif - -#if PY_VERSION_HEX < 0x02050000 - -// Prior to Python 2.5, we didn't have Py_ssize_t. -typedef int Py_ssize_t; -#define PyInt_FromSsize_t PyInt_FromLong -#define PyInt_AsSsize_t PyInt_AsLong - -#endif // PY_VERSION_HEX - -// 2.4 macros which aren't available in 2.3 -#ifndef Py_RETURN_NONE -inline PyObject* doPy_RETURN_NONE() -{ Py_INCREF(Py_None); return Py_None; } -#define Py_RETURN_NONE return doPy_RETURN_NONE() -#endif - -#ifndef Py_RETURN_TRUE -inline PyObject* doPy_RETURN_TRUE() -{Py_INCREF(Py_True); return Py_True;} -#define Py_RETURN_TRUE return doPy_RETURN_TRUE() -#endif - -#ifndef Py_RETURN_FALSE -inline PyObject* doPy_RETURN_FALSE() -{Py_INCREF(Py_False); return Py_False;} -#define Py_RETURN_FALSE return doPy_RETURN_FALSE() -#endif - -#ifndef PyVarObject_HEAD_INIT -#define PyVarObject_HEAD_INIT(type, size) \ - PyObject_HEAD_INIT(type) size, -#endif - -#ifndef Py_TYPE -#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) -#endif - -#ifndef Py_TPFLAGS_CHECKTYPES -// Always on in Python 3 -#define Py_TPFLAGS_CHECKTYPES 0 -#endif - -#if PY_MAJOR_VERSION >= 3 -// For writing code that will compile in both versions. -#define nb_nonzero nb_bool -#define nb_divide nb_true_divide -#define nb_inplace_divide nb_inplace_true_divide - -#define PyLongOrInt_Check(x) PyLong_Check(x) -#define PyLongOrInt_AS_LONG PyLong_AS_LONG -#define PyInt_Check PyLong_Check -#define PyInt_AsLong PyLong_AsLong -#define PyInt_AS_LONG PyLong_AS_LONG -#else -#define PyLongOrInt_Check(x) (PyInt_Check(x) || PyLong_Check(x)) -// PyInt_FromSize_t automatically picks the right type. -#define PyLongOrInt_AS_LONG PyInt_AsLong - -// For more portably defining hash functions. -typedef long Py_hash_t; -#endif - -#if PY_MAJOR_VERSION >= 3 -// Python 3 versions before 3.3.3 defined this incorrectly. -#undef _PyErr_OCCURRED -#define _PyErr_OCCURRED() (PyThreadState_GET()->curexc_type) - -// Python versions before 3.3 did not define this. -#if PY_VERSION_HEX < 0x03030000 -#define PyUnicode_AsUTF8 _PyUnicode_AsString -#define PyUnicode_AsUTF8AndSize _PyUnicode_AsStringAndSize -#endif -#endif - -// Which character to use in PyArg_ParseTuple et al for a byte string. -#if PY_MAJOR_VERSION >= 3 -#define FMTCHAR_BYTES "y" -#else -#define FMTCHAR_BYTES "s" -#endif - using namespace std; // this is tempory .. untill this is glued better into the panda build system @@ -233,7 +127,7 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ #else // NDEBUG #define Define_Dtool_FreeInstance_Private(CLASS_NAME,CNAME)\ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ - if (((Dtool_PyInstDef *)self)->_ptr_to_object != NULL) {\ + if (DtoolInstance_VOID_PTR(self) != nullptr) {\ if (((Dtool_PyInstDef *)self)->_memory_rules) {\ cerr << "Detected leak for " << #CLASS_NAME \ << " which interrogate cannot delete.\n"; \ @@ -245,9 +139,9 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ #define Define_Dtool_FreeInstance(CLASS_NAME,CNAME)\ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ - if (((Dtool_PyInstDef *)self)->_ptr_to_object != NULL) {\ + if (DtoolInstance_VOID_PTR(self) != nullptr) {\ if (((Dtool_PyInstDef *)self)->_memory_rules) {\ - delete ((CNAME *)((Dtool_PyInstDef *)self)->_ptr_to_object);\ + delete (CNAME *)DtoolInstance_VOID_PTR(self);\ }\ }\ Py_TYPE(self)->tp_free(self);\ @@ -255,9 +149,9 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ #define Define_Dtool_FreeInstanceRef(CLASS_NAME,CNAME)\ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ - if (((Dtool_PyInstDef *)self)->_ptr_to_object != NULL) {\ + if (DtoolInstance_VOID_PTR(self) != nullptr) {\ if (((Dtool_PyInstDef *)self)->_memory_rules) {\ - unref_delete((CNAME *)((Dtool_PyInstDef *)self)->_ptr_to_object);\ + unref_delete((CNAME *)DtoolInstance_VOID_PTR(self));\ }\ }\ Py_TYPE(self)->tp_free(self);\ @@ -269,8 +163,17 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ Py_TYPE(self)->tp_free(self);\ } -// Simple Recognition Functions.. -EXPCL_INTERROGATEDB bool DtoolCanThisBeAPandaInstance(PyObject *self); +// Use DtoolInstance_Check to check whether a PyObject* is a DtoolInstance. +#define DtoolInstance_Check(obj) \ + (Py_TYPE(obj)->tp_basicsize >= (int)sizeof(Dtool_PyInstDef) && \ + ((Dtool_PyInstDef *)obj)->_signature == PY_PANDA_SIGNATURE) + +// These macros access the DtoolInstance without error checking. +#define DtoolInstance_TYPE(obj) (((Dtool_PyInstDef *)obj)->_My_Type) +#define DtoolInstance_IS_CONST(obj) (((Dtool_PyInstDef *)obj)->_is_const) +#define DtoolInstance_VOID_PTR(obj) (((Dtool_PyInstDef *)obj)->_ptr_to_object) +#define DtoolInstance_INIT_PTR(obj, ptr) { ((Dtool_PyInstDef *)obj)->_ptr_to_object = (void*)(ptr); } +#define DtoolInstance_UPCAST(obj, type) (((Dtool_PyInstDef *)(obj))->_My_Type->_Dtool_UpcastInterface((obj), &(type))) // ** HACK ** allert.. Need to keep a runtime type dictionary ... that is // forward declared of typed object. We rely on the fact that typed objects @@ -298,16 +201,16 @@ EXPCL_INTERROGATEDB bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyT EXPCL_INTERROGATEDB bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef, void **answer, const char *method_name); -template INLINE bool DTOOL_Call_ExtractThisPointer(PyObject *self, T *&into); +template INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into); +template INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &classdef); // Functions related to error reporting. EXPCL_INTERROGATEDB bool _Dtool_CheckErrorOccurred(); #ifdef NDEBUG -// _PyErr_OCCURRED is an undocumented inline version of PyErr_Occurred. -#define Dtool_CheckErrorOccurred() (_PyErr_OCCURRED() != NULL) +#define Dtool_CheckErrorOccurred() (UNLIKELY(_PyErr_OCCURRED() != nullptr)) #else -#define Dtool_CheckErrorOccurred() _Dtool_CheckErrorOccurred() +#define Dtool_CheckErrorOccurred() (UNLIKELY(_Dtool_CheckErrorOccurred())) #endif EXPCL_INTERROGATEDB PyObject *Dtool_Raise_AssertionError(); @@ -324,13 +227,16 @@ EXPCL_INTERROGATEDB PyObject *_Dtool_Raise_BadArgumentsError(); #define Dtool_Raise_BadArgumentsError(x) Dtool_Raise_TypeError("Arguments must match:\n" x) #endif +// These functions are similar to Dtool_WrapValue, except that they also +// contain code for checking assertions and exceptions when compiling with +// NDEBUG mode on. EXPCL_INTERROGATEDB PyObject *_Dtool_Return_None(); EXPCL_INTERROGATEDB PyObject *Dtool_Return_Bool(bool value); EXPCL_INTERROGATEDB PyObject *_Dtool_Return(PyObject *value); #ifdef NDEBUG -#define Dtool_Return_None() (_PyErr_OCCURRED() != NULL ? NULL : (Py_INCREF(Py_None), Py_None)) -#define Dtool_Return(value) (_PyErr_OCCURRED() != NULL ? NULL : value) +#define Dtool_Return_None() (LIKELY(_PyErr_OCCURRED() == nullptr) ? (Py_INCREF(Py_None), Py_None) : nullptr) +#define Dtool_Return(value) (LIKELY(_PyErr_OCCURRED() == nullptr) ? value : nullptr) #else #define Dtool_Return_None() _Dtool_Return_None() #define Dtool_Return(value) _Dtool_Return(value) @@ -450,18 +356,19 @@ EXPCL_INTERROGATEDB PyObject * map_deepcopy_to_copy(PyObject *self, PyObject *args); /** - * This class is returned from properties that require a settable interface, - * ie. something.children[i] = 3. + * These functions check whether the arguments passed to a function conform to + * certain expectations. */ -struct Dtool_SequenceWrapper { - PyObject_HEAD - PyObject *_base; - lenfunc _len_func; - ssizeargfunc _getitem_func; - ssizeobjargproc _setitem_func; -}; - -EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SequenceWrapper_Type; +ALWAYS_INLINE bool Dtool_CheckNoArgs(PyObject *args); +ALWAYS_INLINE bool Dtool_CheckNoArgs(PyObject *args, PyObject *kwds); +EXPCL_INTERROGATEDB bool Dtool_ExtractArg(PyObject **result, PyObject *args, + PyObject *kwds, const char *keyword); +EXPCL_INTERROGATEDB bool Dtool_ExtractArg(PyObject **result, PyObject *args, + PyObject *kwds); +EXPCL_INTERROGATEDB bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, + PyObject *kwds, const char *keyword); +EXPCL_INTERROGATEDB bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, + PyObject *kwds); /** * These functions convert a C++ value into the corresponding Python object. @@ -485,8 +392,9 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::string *value); ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::wstring *value); ALWAYS_INLINE PyObject *Dtool_WrapValue(char value); ALWAYS_INLINE PyObject *Dtool_WrapValue(wchar_t value); +ALWAYS_INLINE PyObject *Dtool_WrapValue(nullptr_t); ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value); -ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::vector &value); +ALWAYS_INLINE PyObject *Dtool_WrapValue(const vector_uchar &value); #if PY_MAJOR_VERSION >= 0x02060000 ALWAYS_INLINE PyObject *Dtool_WrapValue(Py_buffer *value); @@ -502,6 +410,8 @@ EXPCL_INTERROGATEDB extern void Dtool_PyModuleClassInit_DTOOL_SUPER_BASE(PyObjec #include "py_panda.I" +#include "py_wrappers.h" + #endif // HAVE_PYTHON && !CPPPARSER #endif // PY_PANDA_H_ diff --git a/dtool/src/interrogatedb/py_wrappers.cxx b/dtool/src/interrogatedb/py_wrappers.cxx new file mode 100755 index 0000000000..4d56eee41a --- /dev/null +++ b/dtool/src/interrogatedb/py_wrappers.cxx @@ -0,0 +1,1675 @@ +/** + * 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 py_wrappers.cxx + * @author rdb + * @date 2017-11-26 + */ + +#include "py_wrappers.h" + +#ifdef HAVE_PYTHON + +#if PY_VERSION_HEX >= 0x03040000 +#define _COLLECTIONS_ABC "_collections_abc" +#elif PY_VERSION_HEX >= 0x03030000 +#define _COLLECTIONS_ABC "collections.abc" +#else +#define _COLLECTIONS_ABC "_abcoll" +#endif + +static void _register_collection(PyTypeObject *type, const char *abc) { + PyObject *sys_modules = PyImport_GetModuleDict(); + if (sys_modules != nullptr) { + PyObject *module = PyDict_GetItemString(sys_modules, _COLLECTIONS_ABC); + if (module != nullptr) { + PyObject *dict = PyModule_GetDict(module); + if (module != nullptr) { +#if PY_MAJOR_VERSION >= 3 + static PyObject *register_str = PyUnicode_InternFromString("register"); +#else + static PyObject *register_str = PyString_InternFromString("register"); +#endif + PyObject *sequence = PyDict_GetItemString(dict, abc); + if (sequence != nullptr) { + if (PyObject_CallMethodObjArgs(sequence, register_str, (PyObject *)type, nullptr) == nullptr) { + PyErr_Print(); + } + } + } + } + } +} + +/** + * These classes are returned from properties that require a subscript + * interface, ie. something.children[i] = 3. + */ +static void Dtool_WrapperBase_dealloc(PyObject *self) { + Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self; + nassertv(wrap); + Py_XDECREF(wrap->_self); +} + +static PyObject *Dtool_WrapperBase_repr(PyObject *self) { + Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self; + nassertr(wrap, nullptr); + + PyObject *repr = PyObject_Repr(wrap->_self); + PyObject *result; +#if PY_MAJOR_VERSION >= 3 + result = PyUnicode_FromFormat("<%s[] of %s>", wrap->_name, PyUnicode_AsUTF8(repr)); +#else + result = PyString_FromFormat("<%s[] of %s>", wrap->_name, PyString_AS_STRING(repr)); +#endif + Py_DECREF(repr); + return result; +} + +static PyObject *Dtool_SequenceWrapper_repr(PyObject *self) { + Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; + nassertr(wrap, nullptr); + + Py_ssize_t len = -1; + if (wrap->_len_func != nullptr) { + len = wrap->_len_func(wrap->_base._self); + } + + if (len < 0) { + PyErr_Restore(nullptr, nullptr, nullptr); + return Dtool_WrapperBase_repr(self); + } + + PyObject *repr = PyObject_Repr(wrap->_base._self); + PyObject *result; +#if PY_MAJOR_VERSION >= 3 + result = PyUnicode_FromFormat("<%s[%zd] of %s>", wrap->_base._name, len, PyUnicode_AsUTF8(repr)); +#else + result = PyString_FromFormat("<%s[%zd] of %s>", wrap->_base._name, len, PyString_AS_STRING(repr)); +#endif + Py_DECREF(repr); + return result; +} + +static Py_ssize_t Dtool_SequenceWrapper_length(PyObject *self) { + Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; + nassertr(wrap, -1); + if (wrap->_len_func != nullptr) { + return wrap->_len_func(wrap->_base._self); + } else { + Dtool_Raise_TypeError("property does not support len()"); + return -1; + } +} + +static PyObject *Dtool_SequenceWrapper_getitem(PyObject *self, Py_ssize_t index) { + Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; + nassertr(wrap, nullptr); + nassertr(wrap->_getitem_func, nullptr); + return wrap->_getitem_func(wrap->_base._self, index); +} + +/** + * Implementation of (x in property) + */ +static int Dtool_SequenceWrapper_contains(PyObject *self, PyObject *value) { + Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; + nassertr(wrap, -1); + nassertr(wrap->_len_func, -1); + nassertr(wrap->_getitem_func, -1); + + Py_ssize_t length = wrap->_len_func(wrap->_base._self); + + // Iterate through the items, invoking the equality function for each, until + // we have found the matching one. + for (Py_ssize_t index = 0; index < length; ++index) { + PyObject *item = wrap->_getitem_func(wrap->_base._self, index); + if (item != nullptr) { + int cmp = PyObject_RichCompareBool(item, value, Py_EQ); + if (cmp > 0) { + return 1; + } + if (cmp < 0) { + return -1; + } + } else { + return -1; + } + } + return 0; +} + +/** + * Implementation of property.index(x) which returns the index of the first + * occurrence of x in the sequence, or raises a ValueError if it isn't found. + */ +static PyObject *Dtool_SequenceWrapper_index(PyObject *self, PyObject *value) { + Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; + nassertr(wrap, nullptr); + nassertr(wrap->_len_func, nullptr); + nassertr(wrap->_getitem_func, nullptr); + + Py_ssize_t length = wrap->_len_func(wrap->_base._self); + + // Iterate through the items, invoking the equality function for each, until + // we have found the right one. + for (Py_ssize_t index = 0; index < length; ++index) { + PyObject *item = wrap->_getitem_func(wrap->_base._self, index); + if (item != nullptr) { + int cmp = PyObject_RichCompareBool(item, value, Py_EQ); + if (cmp > 0) { + return Dtool_WrapValue(index); + } + if (cmp < 0) { + return nullptr; + } + } else { + return nullptr; + } + } + // Not found, raise ValueError. + return PyErr_Format(PyExc_ValueError, "%s.index() did not find value", wrap->_base._name); +} + +/** + * Implementation of property.count(x) which returns the number of occurrences + * of x in the sequence. + */ +static PyObject *Dtool_SequenceWrapper_count(PyObject *self, PyObject *value) { + Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; + nassertr(wrap, nullptr); + Py_ssize_t index = 0; + if (wrap->_len_func != nullptr) { + index = wrap->_len_func(wrap->_base._self); + } else { + return Dtool_Raise_TypeError("property does not support count()"); + } + // Iterate through the items, invoking the == operator for each. + long count = 0; + nassertr(wrap->_getitem_func, nullptr); + while (index > 0) { + --index; + PyObject *item = wrap->_getitem_func(wrap->_base._self, index); + if (item == nullptr) { + return nullptr; + } + int cmp = PyObject_RichCompareBool(item, value, Py_EQ); + if (cmp > 0) { + ++count; + } + if (cmp < 0) { + return nullptr; + } + } +#if PY_MAJOR_VERSION >= 3 + return PyLong_FromLong(count); +#else + return PyInt_FromLong(count); +#endif +} + +/** + * Implementation of `property[i] = x` + */ +static int Dtool_MutableSequenceWrapper_setitem(PyObject *self, Py_ssize_t index, PyObject *value) { + Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self; + nassertr(wrap, -1); + if (wrap->_setitem_func != nullptr) { + return wrap->_setitem_func(wrap->_base._self, index, value); + } else { + Dtool_Raise_TypeError("property does not support item assignment"); + return -1; + } +} + +/** + * Implementation of property.clear() which removes all elements in the + * sequence, starting with the last. + */ +static PyObject *Dtool_MutableSequenceWrapper_clear(PyObject *self, PyObject *) { + Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self; + nassertr(wrap, nullptr); + Py_ssize_t index = 0; + if (wrap->_len_func != nullptr && wrap->_setitem_func != nullptr) { + index = wrap->_len_func(wrap->_base._self); + } else { + return Dtool_Raise_TypeError("property does not support clear()"); + } + + // Iterate through the items, invoking the delete function for each. We do + // this in reverse order, which may be more efficient. + while (index > 0) { + --index; + if (wrap->_setitem_func(wrap->_base._self, index, nullptr) != 0) { + return nullptr; + } + } + Py_INCREF(Py_None); + return Py_None; +} + +/** + * Implementation of property.remove(x) which removes the first occurrence of + * x in the sequence, or raises a ValueError if it isn't found. + */ +static PyObject *Dtool_MutableSequenceWrapper_remove(PyObject *self, PyObject *value) { + Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self; + nassertr(wrap, nullptr); + Py_ssize_t length = 0; + if (wrap->_len_func != nullptr && wrap->_setitem_func != nullptr) { + length = wrap->_len_func(wrap->_base._self); + } else { + return Dtool_Raise_TypeError("property does not support remove()"); + } + + // Iterate through the items, invoking the equality function for each, until + // we have found the right one. + nassertr(wrap->_getitem_func, nullptr); + for (Py_ssize_t index = 0; index < length; ++index) { + PyObject *item = wrap->_getitem_func(wrap->_base._self, index); + if (item != nullptr) { + int cmp = PyObject_RichCompareBool(item, value, Py_EQ); + if (cmp > 0) { + if (wrap->_setitem_func(wrap->_base._self, index, nullptr) == 0) { + Py_INCREF(Py_None); + return Py_None; + } else { + return nullptr; + } + } + if (cmp < 0) { + return nullptr; + } + } else { + return nullptr; + } + } + // Not found, raise ValueError. + return PyErr_Format(PyExc_ValueError, "%s.remove() did not find value", wrap->_base._name); +} + +/** + * Implementation of property.pop([i=-1]) which returns and removes the + * element at the indicated index in the sequence. If no index is provided, + * it removes from the end of the list. + */ +static PyObject *Dtool_MutableSequenceWrapper_pop(PyObject *self, PyObject *args) { + Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self; + nassertr(wrap, nullptr); + if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr || + wrap->_len_func == nullptr) { + return Dtool_Raise_TypeError("property does not support pop()"); + } + + Py_ssize_t length = wrap->_len_func(wrap->_base._self); + Py_ssize_t index; + switch (PyTuple_GET_SIZE(args)) { + case 0: + index = length - 1; + break; + case 1: + index = PyNumber_AsSsize_t(PyTuple_GET_ITEM(args, 0), PyExc_IndexError); + if (index == -1 && _PyErr_OCCURRED()) { + return nullptr; + } + if (index < 0) { + index += length; + } + break; + default: + return Dtool_Raise_TypeError("pop([i=-1]) takes 0 or 1 arguments"); + } + + if (length <= 0) { + return PyErr_Format(PyExc_IndexError, "%s.pop() from empty sequence", wrap->_base._name); + } + + // Index error will be caught by getitem_func. + PyObject *value = wrap->_getitem_func(wrap->_base._self, index); + if (value != nullptr) { + if (wrap->_setitem_func(wrap->_base._self, index, nullptr) != 0) { + return nullptr; + } + return value; + } + return nullptr; +} + +/** + * Implementation of property.append(x) which is an alias for + * property.insert(len(property), x). + */ +static PyObject *Dtool_MutableSequenceWrapper_append(PyObject *self, PyObject *arg) { + Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self; + nassertr(wrap, nullptr); + if (wrap->_insert_func == nullptr) { + return Dtool_Raise_TypeError("property does not support append()"); + } + return wrap->_insert_func(wrap->_base._self, (size_t)-1, arg); +} + +/** + * Implementation of property.insert(i, x) which inserts the given item at the + * given position. + */ +static PyObject *Dtool_MutableSequenceWrapper_insert(PyObject *self, PyObject *args) { + Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self; + nassertr(wrap, nullptr); + if (wrap->_insert_func == nullptr) { + return Dtool_Raise_TypeError("property does not support insert()"); + } + if (PyTuple_GET_SIZE(args) != 2) { + return Dtool_Raise_TypeError("insert() takes exactly 2 arguments"); + } + Py_ssize_t index = PyNumber_AsSsize_t(PyTuple_GET_ITEM(args, 0), PyExc_IndexError); + if (index == -1 && _PyErr_OCCURRED()) { + return nullptr; + } + if (index < 0) { + if (wrap->_len_func != nullptr) { + index += wrap->_len_func(wrap->_base._self); + } else { + return PyErr_Format(PyExc_TypeError, "%s.insert() does not support negative indices", wrap->_base._name); + } + } + return wrap->_insert_func(wrap->_base._self, (ssize_t)max(index, (Py_ssize_t)0), PyTuple_GET_ITEM(args, 1)); +} + +/** + * Implementation of property.extend(seq) which is equivalent to: + * @code + * for x in seq: + * property.append(seq) + * @endcode + */ +static PyObject *Dtool_MutableSequenceWrapper_extend(PyObject *self, PyObject *arg) { + Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self; + nassertr(wrap, nullptr); + if (wrap->_insert_func == nullptr) { + return Dtool_Raise_TypeError("property does not support extend()"); + } + PyObject *iter = PyObject_GetIter(arg); + if (iter == nullptr) { + return nullptr; + } + PyObject *next = PyIter_Next(iter); + PyObject *retval = nullptr; + while (next != nullptr) { + retval = wrap->_insert_func(wrap->_base._self, (size_t)-1, next); + Py_DECREF(next); + if (retval == nullptr) { + Py_DECREF(iter); + return nullptr; + } + Py_DECREF(retval); + next = PyIter_Next(iter); + } + + Py_DECREF(iter); + Py_INCREF(Py_None); + return Py_None; +} + +/** + * Implementation of `x in mapping`. + */ +static int Dtool_MappingWrapper_contains(PyObject *self, PyObject *key) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, -1); + nassertr(wrap->_getitem_func, -1); + PyObject *value = wrap->_getitem_func(wrap->_base._self, key); + if (value != nullptr) { + Py_DECREF(value); + return 1; + } else if (_PyErr_OCCURRED() == PyExc_KeyError || + _PyErr_OCCURRED() == PyExc_TypeError) { + PyErr_Restore(nullptr, nullptr, nullptr); + return 0; + } else { + return -1; + } +} + +static PyObject *Dtool_MappingWrapper_getitem(PyObject *self, PyObject *key) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + nassertr(wrap->_getitem_func, nullptr); + return wrap->_getitem_func(wrap->_base._self, key); +} + +/** + * Implementation of iter(property) that returns an iterable over all the + * keys. + */ +static PyObject *Dtool_MappingWrapper_iter(PyObject *self) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + + if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) { + return PyErr_Format(PyExc_TypeError, "%s is not iterable", wrap->_base._name); + } + + Dtool_SequenceWrapper *keys = Dtool_NewSequenceWrapper(wrap->_base._self, wrap->_base._name); + if (keys != nullptr) { + keys->_len_func = wrap->_keys._len_func; + keys->_getitem_func = wrap->_keys._getitem_func; + return PySeqIter_New((PyObject *)keys); + } else { + return nullptr; + } +} + +/** + * Implementation of property.get(key[,def=None]) which returns the value with + * the given key in the mapping, or the given default value (which defaults to + * None) if the key isn't found in the mapping. + */ +static PyObject *Dtool_MappingWrapper_get(PyObject *self, PyObject *args) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + nassertr(wrap->_getitem_func, nullptr); + Py_ssize_t size = PyTuple_GET_SIZE(args); + if (size != 1 && size != 2) { + return PyErr_Format(PyExc_TypeError, "%s.get() takes 1 or 2 arguments", wrap->_base._name); + } + PyObject *defvalue = Py_None; + if (size >= 2) { + defvalue = PyTuple_GET_ITEM(args, 1); + } + PyObject *key = PyTuple_GET_ITEM(args, 0); + PyObject *value = wrap->_getitem_func(wrap->_base._self, key); + if (value != nullptr) { + return value; + } else if (_PyErr_OCCURRED() == PyExc_KeyError) { + PyErr_Restore(nullptr, nullptr, nullptr); + Py_INCREF(defvalue); + return defvalue; + } else { + return nullptr; + } +} + +/** + * Implementation of property.keys(...) that returns a view of all the keys. + */ +static PyObject *Dtool_MappingWrapper_keys(PyObject *self, PyObject *) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + + if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) { + return Dtool_Raise_TypeError("property does not support keys()"); + } + + Dtool_MappingWrapper *keys = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper)); + if (keys == nullptr) { + return PyErr_NoMemory(); + } + + // If the collections.abc module is loaded, register this as a subclass. + static bool registered = false; + if (!registered) { + registered = true; + _register_collection((PyTypeObject *)&Dtool_MappingWrapper_Keys_Type, "MappingView"); + } + + PyObject_INIT(keys, &Dtool_MappingWrapper_Keys_Type); + Py_XINCREF(wrap->_base._self); + keys->_base._self = wrap->_base._self; + keys->_base._name = wrap->_base._name; + keys->_keys._len_func = wrap->_keys._len_func; + keys->_keys._getitem_func = wrap->_keys._getitem_func; + keys->_getitem_func = wrap->_getitem_func; + keys->_setitem_func = nullptr; + return (PyObject *)keys; +} + +/** + * Implementation of property.values(...) that returns a view of the values. + */ +static PyObject *Dtool_MappingWrapper_values(PyObject *self, PyObject *) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + nassertr(wrap->_getitem_func, nullptr); + + if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) { + return Dtool_Raise_TypeError("property does not support values()"); + } + + Dtool_MappingWrapper *values = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper)); + if (values == nullptr) { + return PyErr_NoMemory(); + } + + // If the collections.abc module is loaded, register this as a subclass. + static bool registered = false; + if (!registered) { + registered = true; + _register_collection((PyTypeObject *)&Dtool_MappingWrapper_Values_Type, "ValuesView"); + } + + PyObject_INIT(values, &Dtool_MappingWrapper_Values_Type); + Py_XINCREF(wrap->_base._self); + values->_base._self = wrap->_base._self; + values->_base._name = wrap->_base._name; + values->_keys._len_func = wrap->_keys._len_func; + values->_keys._getitem_func = wrap->_keys._getitem_func; + values->_getitem_func = wrap->_getitem_func; + values->_setitem_func = nullptr; + return (PyObject *)values; +} + +/** + * Implementation of property.items(...) that returns an iterable yielding a + * `(key, value)` tuple for every item. + */ +static PyObject *Dtool_MappingWrapper_items(PyObject *self, PyObject *) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + nassertr(wrap->_getitem_func, nullptr); + + if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) { + return Dtool_Raise_TypeError("property does not support items()"); + } + + Dtool_MappingWrapper *items = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper)); + if (items == nullptr) { + return PyErr_NoMemory(); + } + + // If the collections.abc module is loaded, register this as a subclass. + static bool registered = false; + if (!registered) { + registered = true; + _register_collection((PyTypeObject *)&Dtool_MappingWrapper_Items_Type, "MappingView"); + } + + PyObject_INIT(items, &Dtool_MappingWrapper_Items_Type); + Py_XINCREF(wrap->_base._self); + items->_base._self = wrap->_base._self; + items->_base._name = wrap->_base._name; + items->_keys._len_func = wrap->_keys._len_func; + items->_keys._getitem_func = wrap->_keys._getitem_func; + items->_getitem_func = wrap->_getitem_func; + items->_setitem_func = nullptr; + return (PyObject *)items; +} + +/** + * Implementation of `property[key] = value` + */ +static int Dtool_MutableMappingWrapper_setitem(PyObject *self, PyObject *key, PyObject *value) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap->_setitem_func != nullptr, -1); + return wrap->_setitem_func(wrap->_base._self, key, value); +} + +/** + * Implementation of property.pop(key[,def=None]) which is the same as get() + * except that it also removes the element from the mapping. + */ +static PyObject *Dtool_MutableMappingWrapper_pop(PyObject *self, PyObject *args) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) { + return Dtool_Raise_TypeError("property does not support pop()"); + } + + Py_ssize_t size = PyTuple_GET_SIZE(args); + if (size != 1 && size != 2) { + return PyErr_Format(PyExc_TypeError, "%s.pop() takes 1 or 2 arguments", wrap->_base._name); + } + PyObject *defvalue = Py_None; + if (size >= 2) { + defvalue = PyTuple_GET_ITEM(args, 1); + } + + PyObject *key = PyTuple_GET_ITEM(args, 0); + PyObject *value = wrap->_getitem_func(wrap->_base._self, key); + if (value != nullptr) { + // OK, now set unset this value. + if (wrap->_setitem_func(wrap->_base._self, key, nullptr) == 0) { + return value; + } else { + Py_DECREF(value); + return nullptr; + } + } else if (_PyErr_OCCURRED() == PyExc_KeyError) { + PyErr_Restore(nullptr, nullptr, nullptr); + Py_INCREF(defvalue); + return defvalue; + } else { + return nullptr; + } +} + +/** + * Implementation of property.popitem() which returns and removes an arbitrary + * (key, value) pair from the mapping. Useful for destructive iteration. + */ +static PyObject *Dtool_MutableMappingWrapper_popitem(PyObject *self, PyObject *) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr || + wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) { + return Dtool_Raise_TypeError("property does not support popitem()"); + } + + Py_ssize_t length = wrap->_keys._len_func(wrap->_base._self); + if (length < 1) { + return PyErr_Format(PyExc_KeyError, "%s is empty", wrap->_base._name); + } + + PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, length - 1); + if (key != nullptr) { + PyObject *value = wrap->_getitem_func(wrap->_base._self, key); + if (value != nullptr) { + // OK, now set unset this value. + if (wrap->_setitem_func(wrap->_base._self, key, nullptr) == 0) { + PyObject *item = PyTuple_New(2); + PyTuple_SET_ITEM(item, 0, key); + PyTuple_SET_ITEM(item, 1, value); + return item; + } + Py_DECREF(value); + } + } + return nullptr; +} + +/* + * Implementation of property.clear() which removes all elements in the + * mapping. + */ +static PyObject *Dtool_MutableMappingWrapper_clear(PyObject *self, PyObject *) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + Py_ssize_t index = 0; + if (wrap->_keys._len_func != nullptr && wrap->_keys._getitem_func != nullptr && + wrap->_setitem_func != nullptr) { + index = wrap->_keys._len_func(wrap->_base._self); + } else { + return Dtool_Raise_TypeError("property does not support clear()"); + } + + // Iterate through the items, invoking the delete function for each. We do + // this in reverse order, which may be more efficient. + while (index > 0) { + --index; + PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, index); + if (key != nullptr) { + int result = wrap->_setitem_func(wrap->_base._self, key, nullptr); + Py_DECREF(key); + if (result != 0) { + return nullptr; + } + } + } + Py_INCREF(Py_None); + return Py_None; +} + +/** + * Implementation of property.setdefault(key[,def=None]) which is the same as + * get() except that it also writes the default value back to the mapping if + * the key was not found is missing. + */ +static PyObject *Dtool_MutableMappingWrapper_setdefault(PyObject *self, PyObject *args) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + + if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) { + return Dtool_Raise_TypeError("property does not support setdefault()"); + } + + Py_ssize_t size = PyTuple_GET_SIZE(args); + if (size != 1 && size != 2) { + return PyErr_Format(PyExc_TypeError, "%s.setdefault() takes 1 or 2 arguments", wrap->_base._name); + } + PyObject *defvalue = Py_None; + if (size >= 2) { + defvalue = PyTuple_GET_ITEM(args, 1); + } + PyObject *key = PyTuple_GET_ITEM(args, 0); + PyObject *value = wrap->_getitem_func(wrap->_base._self, key); + if (value != nullptr) { + return value; + } else if (_PyErr_OCCURRED() == PyExc_KeyError) { + PyErr_Restore(nullptr, nullptr, nullptr); + if (wrap->_setitem_func(wrap->_base._self, key, defvalue) == 0) { + Py_INCREF(defvalue); + return defvalue; + } + } + return nullptr; +} + +/** + * Implementation of property.update(...) which sets multiple values in one + * go. It accepts either a single dictionary or keyword arguments, not both. + */ +static PyObject *Dtool_MutableMappingWrapper_update(PyObject *self, PyObject *args, PyObject *kwargs) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + + if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) { + return Dtool_Raise_TypeError("property does not support update()"); + } + + // We accept either a dict argument or keyword arguments, but not both. + PyObject *dict; + switch (PyTuple_GET_SIZE(args)) { + case 0: + if (kwargs == nullptr) { + // This is legal. + Py_INCREF(Py_None); + return Py_None; + } + dict = kwargs; + break; + case 1: + if (PyDict_Check(PyTuple_GET_ITEM(args, 0)) && (kwargs == nullptr || Py_SIZE(kwargs) == 0)) { + dict = PyTuple_GET_ITEM(args, 0); + break; + } + // Fall through + default: + return PyErr_Format(PyExc_TypeError, "%s.update() takes either a dict argument or keyword arguments", wrap->_base._name); + } + + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(dict, &pos, &key, &value)) { + if (wrap->_setitem_func(wrap->_base._self, key, value) != 0) { + return nullptr; + } + } + Py_INCREF(Py_None); + return Py_None; +} + +/** + * This variant defines only a sequence interface. + */ +static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = { + Dtool_SequenceWrapper_length, + 0, // sq_concat + 0, // sq_repeat + Dtool_SequenceWrapper_getitem, + 0, // sq_slice + 0, // sq_ass_item + 0, // sq_ass_slice + Dtool_SequenceWrapper_contains, + 0, // sq_inplace_concat + 0, // sq_inplace_repeat +}; + +static PyMethodDef Dtool_SequenceWrapper_Methods[] = { + {"index", &Dtool_SequenceWrapper_index, METH_O, nullptr}, + {"count", &Dtool_SequenceWrapper_count, METH_O, nullptr}, + {nullptr, nullptr, 0, nullptr} +}; + +PyTypeObject Dtool_SequenceWrapper_Type = { + PyVarObject_HEAD_INIT(nullptr, 0) + "sequence wrapper", + sizeof(Dtool_SequenceWrapper), + 0, // tp_itemsize + Dtool_WrapperBase_dealloc, + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + Dtool_SequenceWrapper_repr, + 0, // tp_as_number + &Dtool_SequenceWrapper_SequenceMethods, + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + PyObject_GenericGetAttr, + PyObject_GenericSetAttr, + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, + 0, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + PySeqIter_New, + 0, // tp_iternext + Dtool_SequenceWrapper_Methods, + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + PyType_GenericAlloc, + 0, // tp_new + PyObject_Del, + 0, // tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, // tp_weaklist + 0, // tp_del +}; + +/** + * This is a variant on SequenceWrapper that also has an insert() method. + */ +static PySequenceMethods Dtool_MutableSequenceWrapper_SequenceMethods = { + Dtool_SequenceWrapper_length, + 0, // sq_concat + 0, // sq_repeat + Dtool_SequenceWrapper_getitem, + 0, // sq_slice + Dtool_MutableSequenceWrapper_setitem, + 0, // sq_ass_slice + Dtool_SequenceWrapper_contains, + Dtool_MutableSequenceWrapper_extend, + 0, // sq_inplace_repeat +}; + +static PyMethodDef Dtool_MutableSequenceWrapper_Methods[] = { + {"index", &Dtool_SequenceWrapper_index, METH_O, nullptr}, + {"count", &Dtool_SequenceWrapper_count, METH_O, nullptr}, + {"clear", &Dtool_MutableSequenceWrapper_clear, METH_NOARGS, nullptr}, + {"pop", &Dtool_MutableSequenceWrapper_pop, METH_VARARGS, nullptr}, + {"remove", &Dtool_MutableSequenceWrapper_remove, METH_O, nullptr}, + {"append", &Dtool_MutableSequenceWrapper_append, METH_O, nullptr}, + {"insert", &Dtool_MutableSequenceWrapper_insert, METH_VARARGS, nullptr}, + {"extend", &Dtool_MutableSequenceWrapper_extend, METH_O, nullptr}, + {nullptr, nullptr, 0, nullptr} +}; + +PyTypeObject Dtool_MutableSequenceWrapper_Type = { + PyVarObject_HEAD_INIT(nullptr, 0) + "sequence wrapper", + sizeof(Dtool_MutableSequenceWrapper), + 0, // tp_itemsize + Dtool_WrapperBase_dealloc, + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + Dtool_SequenceWrapper_repr, + 0, // tp_as_number + &Dtool_MutableSequenceWrapper_SequenceMethods, + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + PyObject_GenericGetAttr, + PyObject_GenericSetAttr, + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, + 0, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + PySeqIter_New, + 0, // tp_iternext + Dtool_MutableSequenceWrapper_Methods, + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + PyType_GenericAlloc, + 0, // tp_new + PyObject_Del, + 0, // tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, // tp_weaklist + 0, // tp_del +}; + +/** + * This variant defines only a mapping interface. + */ +static PySequenceMethods Dtool_MappingWrapper_SequenceMethods = { + Dtool_SequenceWrapper_length, + 0, // sq_concat + 0, // sq_repeat + 0, // sq_item + 0, // sq_slice + 0, // sq_ass_item + 0, // sq_ass_slice + Dtool_MappingWrapper_contains, + 0, // sq_inplace_concat + 0, // sq_inplace_repeat +}; + +static PyMappingMethods Dtool_MappingWrapper_MappingMethods = { + Dtool_SequenceWrapper_length, + Dtool_MappingWrapper_getitem, + 0, // mp_ass_subscript +}; + +static PyMethodDef Dtool_MappingWrapper_Methods[] = { + {"get", &Dtool_MappingWrapper_get, METH_VARARGS, nullptr}, + {"keys", &Dtool_MappingWrapper_keys, METH_NOARGS, nullptr}, + {"values", &Dtool_MappingWrapper_values, METH_NOARGS, nullptr}, + {"items", &Dtool_MappingWrapper_items, METH_NOARGS, nullptr}, + {nullptr, nullptr, 0, nullptr} +}; + +PyTypeObject Dtool_MappingWrapper_Type = { + PyVarObject_HEAD_INIT(nullptr, 0) + "mapping wrapper", + sizeof(Dtool_MappingWrapper), + 0, // tp_itemsize + Dtool_WrapperBase_dealloc, + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + Dtool_WrapperBase_repr, + 0, // tp_as_number + &Dtool_MappingWrapper_SequenceMethods, + &Dtool_MappingWrapper_MappingMethods, + 0, // tp_hash + 0, // tp_call + 0, // tp_str + PyObject_GenericGetAttr, + PyObject_GenericSetAttr, + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, + 0, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + Dtool_MappingWrapper_iter, + 0, // tp_iternext + Dtool_MappingWrapper_Methods, + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + PyType_GenericAlloc, + 0, // tp_new + PyObject_Del, + 0, // tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, // tp_weaklist + 0, // tp_del +}; + +/** + * This variant defines only a mutable mapping interface. + */ +static PyMappingMethods Dtool_MutableMappingWrapper_MappingMethods = { + Dtool_SequenceWrapper_length, + Dtool_MappingWrapper_getitem, + Dtool_MutableMappingWrapper_setitem, +}; + +static PyMethodDef Dtool_MutableMappingWrapper_Methods[] = { + {"get", &Dtool_MappingWrapper_get, METH_VARARGS, nullptr}, + {"pop", &Dtool_MutableMappingWrapper_pop, METH_VARARGS, nullptr}, + {"popitem", &Dtool_MutableMappingWrapper_popitem, METH_NOARGS, nullptr}, + {"clear", &Dtool_MutableMappingWrapper_clear, METH_VARARGS, nullptr}, + {"setdefault", &Dtool_MutableMappingWrapper_setdefault, METH_VARARGS, nullptr}, + {"update", (PyCFunction) &Dtool_MutableMappingWrapper_update, METH_VARARGS | METH_KEYWORDS, nullptr}, + {"keys", &Dtool_MappingWrapper_keys, METH_NOARGS, nullptr}, + {"values", &Dtool_MappingWrapper_values, METH_NOARGS, nullptr}, + {"items", &Dtool_MappingWrapper_items, METH_NOARGS, nullptr}, + {nullptr, nullptr, 0, nullptr} +}; + +PyTypeObject Dtool_MutableMappingWrapper_Type = { + PyVarObject_HEAD_INIT(nullptr, 0) + "mapping wrapper", + sizeof(Dtool_MappingWrapper), + 0, // tp_itemsize + Dtool_WrapperBase_dealloc, + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + Dtool_WrapperBase_repr, + 0, // tp_as_number + &Dtool_MappingWrapper_SequenceMethods, + &Dtool_MutableMappingWrapper_MappingMethods, + 0, // tp_hash + 0, // tp_call + 0, // tp_str + PyObject_GenericGetAttr, + PyObject_GenericSetAttr, + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, + 0, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + Dtool_MappingWrapper_iter, + 0, // tp_iternext + Dtool_MutableMappingWrapper_Methods, + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + PyType_GenericAlloc, + 0, // tp_new + PyObject_Del, + 0, // tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, // tp_weaklist + 0, // tp_del +}; + +/** + * This is returned by mapping.items(). + */ +static PyObject *Dtool_MappingWrapper_Items_repr(PyObject *self) { + Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self; + nassertr(wrap, nullptr); + + PyObject *repr = PyObject_Repr(wrap->_self); + PyObject *result; +#if PY_MAJOR_VERSION >= 3 + result = PyUnicode_FromFormat("<%s.items() of %s>", wrap->_name, PyUnicode_AsUTF8(repr)); +#else + result = PyString_FromFormat("<%s.items() of %s>", wrap->_name, PyString_AS_STRING(repr)); +#endif + Py_DECREF(repr); + return result; +} + +static PyObject *Dtool_MappingWrapper_Items_getitem(PyObject *self, Py_ssize_t index) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + nassertr(wrap->_keys._getitem_func, nullptr); + + PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, index); + if (key != nullptr) { + PyObject *value = wrap->_getitem_func(wrap->_base._self, key); + if (value != nullptr) { + // PyTuple_SET_ITEM steals the reference. + PyObject *item = PyTuple_New(2); + PyTuple_SET_ITEM(item, 0, key); + PyTuple_SET_ITEM(item, 1, value); + return item; + } else { + Py_DECREF(key); + } + } + return nullptr; +} + +static PySequenceMethods Dtool_MappingWrapper_Items_SequenceMethods = { + Dtool_SequenceWrapper_length, + 0, // sq_concat + 0, // sq_repeat + Dtool_MappingWrapper_Items_getitem, + 0, // sq_slice + 0, // sq_ass_item + 0, // sq_ass_slice + Dtool_MappingWrapper_contains, + 0, // sq_inplace_concat + 0, // sq_inplace_repeat +}; + +PyTypeObject Dtool_MappingWrapper_Items_Type = { + PyVarObject_HEAD_INIT(nullptr, 0) + "sequence wrapper", + sizeof(Dtool_MappingWrapper), + 0, // tp_itemsize + Dtool_WrapperBase_dealloc, + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + Dtool_MappingWrapper_Items_repr, + 0, // tp_as_number + &Dtool_MappingWrapper_Items_SequenceMethods, + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + PyObject_GenericGetAttr, + PyObject_GenericSetAttr, + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, + 0, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + PySeqIter_New, + 0, // tp_iternext + 0, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + PyType_GenericAlloc, + 0, // tp_new + PyObject_Del, + 0, // tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, // tp_weaklist + 0, // tp_del +}; + +/** + * This is returned by mapping.keys(). + */ +static PyObject *Dtool_MappingWrapper_Keys_repr(PyObject *self) { + Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self; + nassertr(wrap, nullptr); + + PyObject *repr = PyObject_Repr(wrap->_self); + PyObject *result; +#if PY_MAJOR_VERSION >= 3 + result = PyUnicode_FromFormat("<%s.keys() of %s>", wrap->_name, PyUnicode_AsUTF8(repr)); +#else + result = PyString_FromFormat("<%s.keys() of %s>", wrap->_name, PyString_AS_STRING(repr)); +#endif + Py_DECREF(repr); + return result; +} + +static PySequenceMethods Dtool_MappingWrapper_Keys_SequenceMethods = { + Dtool_SequenceWrapper_length, + 0, // sq_concat + 0, // sq_repeat + Dtool_MappingWrapper_Items_getitem, + 0, // sq_slice + 0, // sq_ass_item + 0, // sq_ass_slice + Dtool_MappingWrapper_contains, + 0, // sq_inplace_concat + 0, // sq_inplace_repeat +}; + +PyTypeObject Dtool_MappingWrapper_Keys_Type = { + PyVarObject_HEAD_INIT(nullptr, 0) + "sequence wrapper", + sizeof(Dtool_SequenceWrapper), + 0, // tp_itemsize + Dtool_WrapperBase_dealloc, + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + Dtool_MappingWrapper_Keys_repr, + 0, // tp_as_number + &Dtool_SequenceWrapper_SequenceMethods, + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + PyObject_GenericGetAttr, + PyObject_GenericSetAttr, + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, + 0, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + PySeqIter_New, + 0, // tp_iternext + 0, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + PyType_GenericAlloc, + 0, // tp_new + PyObject_Del, + 0, // tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, // tp_weaklist + 0, // tp_del +}; + +/** + * This is returned by mapping.values(). + */ +static PyObject *Dtool_MappingWrapper_Values_repr(PyObject *self) { + Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self; + nassertr(wrap, nullptr); + + PyObject *repr = PyObject_Repr(wrap->_self); + PyObject *result; +#if PY_MAJOR_VERSION >= 3 + result = PyUnicode_FromFormat("<%s.values() of %s>", wrap->_name, PyUnicode_AsUTF8(repr)); +#else + result = PyString_FromFormat("<%s.values() of %s>", wrap->_name, PyString_AS_STRING(repr)); +#endif + Py_DECREF(repr); + return result; +} + +static PyObject *Dtool_MappingWrapper_Values_getitem(PyObject *self, Py_ssize_t index) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self; + nassertr(wrap, nullptr); + nassertr(wrap->_keys._getitem_func, nullptr); + + PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, index); + if (key != nullptr) { + PyObject *value = wrap->_getitem_func(wrap->_base._self, key); + Py_DECREF(key); + return value; + } + return nullptr; +} + +static PySequenceMethods Dtool_MappingWrapper_Values_SequenceMethods = { + Dtool_SequenceWrapper_length, + 0, // sq_concat + 0, // sq_repeat + Dtool_MappingWrapper_Values_getitem, + 0, // sq_slice + 0, // sq_ass_item + 0, // sq_ass_slice + Dtool_MappingWrapper_contains, + 0, // sq_inplace_concat + 0, // sq_inplace_repeat +}; + +PyTypeObject Dtool_MappingWrapper_Values_Type = { + PyVarObject_HEAD_INIT(nullptr, 0) + "sequence wrapper", + sizeof(Dtool_MappingWrapper), + 0, // tp_itemsize + Dtool_WrapperBase_dealloc, + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + Dtool_MappingWrapper_Values_repr, + 0, // tp_as_number + &Dtool_MappingWrapper_Values_SequenceMethods, + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + PyObject_GenericGetAttr, + PyObject_GenericSetAttr, + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, + 0, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + PySeqIter_New, + 0, // tp_iternext + 0, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + PyType_GenericAlloc, + 0, // tp_new + PyObject_Del, + 0, // tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, // tp_weaklist + 0, // tp_del +}; + +/** + * This variant defines only a generator interface. + */ +static PyObject *Dtool_GeneratorWrapper_iternext(PyObject *self) { + Dtool_GeneratorWrapper *wrap = (Dtool_GeneratorWrapper *)self; + nassertr(wrap, nullptr); + nassertr(wrap->_iternext_func, nullptr); + return wrap->_iternext_func(wrap->_base._self); +} + +PyTypeObject Dtool_GeneratorWrapper_Type = { + PyVarObject_HEAD_INIT(nullptr, 0) + "generator wrapper", + sizeof(Dtool_GeneratorWrapper), + 0, // tp_itemsize + Dtool_WrapperBase_dealloc, + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + PyObject_GenericGetAttr, + PyObject_GenericSetAttr, + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, + 0, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + PyObject_SelfIter, + Dtool_GeneratorWrapper_iternext, + 0, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + PyType_GenericAlloc, + 0, // tp_new + PyObject_Del, + 0, // tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, // tp_weaklist + 0, // tp_del +}; + +/** + * This is a variant of the Python getset mechanism that permits static + * properties. + */ +static void +Dtool_StaticProperty_dealloc(PyDescrObject *descr) { + _PyObject_GC_UNTRACK(descr); + Py_XDECREF(descr->d_type); + Py_XDECREF(descr->d_name); +//#if PY_MAJOR_VERSION >= 3 +// Py_XDECREF(descr->d_qualname); +//#endif + PyObject_GC_Del(descr); +} + +static PyObject * +Dtool_StaticProperty_repr(PyDescrObject *descr, const char *format) { +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromFormat("", + PyUnicode_AsUTF8(descr->d_name), + descr->d_type->tp_name); +#else + return PyString_FromFormat("", + PyString_AS_STRING(descr->d_name), + descr->d_type->tp_name); +#endif +} + +static int +Dtool_StaticProperty_traverse(PyObject *self, visitproc visit, void *arg) { + PyDescrObject *descr = (PyDescrObject *)self; + Py_VISIT(descr->d_type); + return 0; +} + +static PyObject * +Dtool_StaticProperty_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *type) { + if (descr->d_getset->get != nullptr) { + return descr->d_getset->get(obj, descr->d_getset->closure); + } else { + return PyErr_Format(PyExc_AttributeError, + "attribute '%s' of type '%.100s' is not readable", +#if PY_MAJOR_VERSION >= 3 + PyUnicode_AsUTF8(((PyDescrObject *)descr)->d_name), +#else + PyString_AS_STRING(((PyDescrObject *)descr)->d_name), +#endif + ((PyDescrObject *)descr)->d_type->tp_name); + } +} + +static int +Dtool_StaticProperty_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) { + if (descr->d_getset->set != nullptr) { + return descr->d_getset->set(obj, value, descr->d_getset->closure); + } else { + PyErr_Format(PyExc_AttributeError, + "attribute '%s' of type '%.100s' is not writable", +#if PY_MAJOR_VERSION >= 3 + PyUnicode_AsUTF8(((PyDescrObject *)descr)->d_name), +#else + PyString_AS_STRING(((PyDescrObject *)descr)->d_name), +#endif + ((PyDescrObject *)descr)->d_type->tp_name); + return -1; + } +} + +PyTypeObject Dtool_StaticProperty_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "getset_descriptor", + sizeof(PyGetSetDescrObject), + 0, // tp_itemsize + (destructor)Dtool_StaticProperty_dealloc, + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_reserved + (reprfunc)Dtool_StaticProperty_repr, + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + PyObject_GenericGetAttr, + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, + 0, // tp_doc + Dtool_StaticProperty_traverse, + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + 0, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + (descrgetfunc)Dtool_StaticProperty_get, + (descrsetfunc)Dtool_StaticProperty_set, + 0, // tp_dictoffset + 0, // tp_init + 0, // tp_alloc + 0, // tp_new + 0, // tp_del + 0, // tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, // tp_weaklist + 0, // tp_del +}; + +/** + * This wraps around a property that exposes a sequence interface. + */ +Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name) { + Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)PyObject_MALLOC(sizeof(Dtool_SequenceWrapper)); + if (wrap == nullptr) { + return (Dtool_SequenceWrapper *)PyErr_NoMemory(); + } + + // If the collections.abc module is loaded, register this as a subclass. + static bool registered = false; + if (!registered) { + registered = true; + _register_collection((PyTypeObject *)&Dtool_MutableSequenceWrapper_Type, "Sequence"); + } + + PyObject_INIT(wrap, &Dtool_SequenceWrapper_Type); + Py_XINCREF(self); + wrap->_base._self = self; + wrap->_base._name = name; + wrap->_len_func = nullptr; + wrap->_getitem_func = nullptr; + return wrap; +} + +/** + * This wraps around a property that exposes a mutable sequence interface. + */ +Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, const char *name) { + Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)PyObject_MALLOC(sizeof(Dtool_MutableSequenceWrapper)); + if (wrap == nullptr) { + return (Dtool_MutableSequenceWrapper *)PyErr_NoMemory(); + } + + // If the collections.abc module is loaded, register this as a subclass. + static bool registered = false; + if (!registered) { + registered = true; + _register_collection((PyTypeObject *)&Dtool_MutableSequenceWrapper_Type, "MutableSequence"); + } + + PyObject_INIT(wrap, &Dtool_MutableSequenceWrapper_Type); + Py_XINCREF(self); + wrap->_base._self = self; + wrap->_base._name = name; + wrap->_len_func = nullptr; + wrap->_getitem_func = nullptr; + wrap->_setitem_func = nullptr; + wrap->_insert_func = nullptr; + return wrap; +} + +/** + * This wraps around a mapping interface, with getitem function. + */ +Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper)); + if (wrap == nullptr) { + return (Dtool_MappingWrapper *)PyErr_NoMemory(); + } + + // If the collections.abc module is loaded, register this as a subclass. + static bool registered = false; + if (!registered) { + registered = true; + _register_collection((PyTypeObject *)&Dtool_MappingWrapper_Type, "Mapping"); + } + + PyObject_INIT(wrap, &Dtool_MappingWrapper_Type); + Py_XINCREF(self); + wrap->_base._self = self; + wrap->_base._name = name; + wrap->_keys._len_func = nullptr; + wrap->_keys._getitem_func = nullptr; + wrap->_getitem_func = nullptr; + wrap->_setitem_func = nullptr; + return wrap; +} + +/** + * This wraps around a mapping interface, with getitem/setitem functions. + */ +Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char *name) { + Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper)); + if (wrap == nullptr) { + return (Dtool_MappingWrapper *)PyErr_NoMemory(); + } + + // If the collections.abc module is loaded, register this as a subclass. + static bool registered = false; + if (!registered) { + registered = true; + _register_collection((PyTypeObject *)&Dtool_MutableMappingWrapper_Type, "MutableMapping"); + } + + PyObject_INIT(wrap, &Dtool_MutableMappingWrapper_Type); + Py_XINCREF(self); + wrap->_base._self = self; + wrap->_base._name = name; + wrap->_keys._len_func = nullptr; + wrap->_keys._getitem_func = nullptr; + wrap->_getitem_func = nullptr; + wrap->_setitem_func = nullptr; + return wrap; +} + +/** + * This is a variant of the Python getset mechanism that permits static + * properties. + */ +PyObject * +Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) { + PyGetSetDescrObject *descr; + descr = (PyGetSetDescrObject *)PyType_GenericAlloc(&Dtool_StaticProperty_Type, 0); + if (descr != nullptr) { + Py_XINCREF(type); + descr->d_getset = (PyGetSetDef *)getset; +#if PY_MAJOR_VERSION >= 3 + descr->d_common.d_type = type; + descr->d_common.d_name = PyUnicode_InternFromString(getset->name); +#if PY_VERSION_HEX >= 0x03030000 + descr->d_common.d_qualname = nullptr; +#endif +#else + descr->d_type = type; + descr->d_name = PyString_InternFromString(getset->name); +#endif + } + return (PyObject *)descr; +} + +#endif // HAVE_PYTHON diff --git a/dtool/src/interrogatedb/py_wrappers.h b/dtool/src/interrogatedb/py_wrappers.h new file mode 100755 index 0000000000..7bf2c2e19f --- /dev/null +++ b/dtool/src/interrogatedb/py_wrappers.h @@ -0,0 +1,78 @@ +/** + * 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 py_wrappers.h + * @author rdb + * @date 2017-11-26 + */ + +#ifndef PY_WRAPPERS_H +#define PY_WRAPPERS_H + +#include "py_panda.h" + +#ifdef HAVE_PYTHON + +/** + * These classes are returned from properties that require a subscript + * interface, ie. something.children[i] = 3. + */ +struct Dtool_WrapperBase { + PyObject_HEAD; + PyObject *_self; + const char *_name; +}; + +struct Dtool_SequenceWrapper { + Dtool_WrapperBase _base; + lenfunc _len_func; + ssizeargfunc _getitem_func; +}; + +struct Dtool_MutableSequenceWrapper { + Dtool_WrapperBase _base; + lenfunc _len_func; + ssizeargfunc _getitem_func; + ssizeobjargproc _setitem_func; + PyObject *(*_insert_func)(PyObject *, size_t, PyObject *); +}; + +struct Dtool_MappingWrapper { + union { + Dtool_WrapperBase _base; + Dtool_SequenceWrapper _keys; + }; + binaryfunc _getitem_func; + objobjargproc _setitem_func; +}; + +struct Dtool_GeneratorWrapper { + Dtool_WrapperBase _base; + iternextfunc _iternext_func; +}; + +EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SequenceWrapper_Type; +EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MutableSequenceWrapper_Type; +EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MappingWrapper_Type; +EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MutableMappingWrapper_Type; +EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MappingWrapper_Items_Type; +EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MappingWrapper_Keys_Type; +EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MappingWrapper_Values_Type; +EXPCL_INTERROGATEDB extern PyTypeObject Dtool_GeneratorWrapper_Type; +EXPCL_INTERROGATEDB extern PyTypeObject Dtool_StaticProperty_Type; + +EXPCL_INTERROGATEDB Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name); +EXPCL_INTERROGATEDB Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, const char *name); +EXPCL_INTERROGATEDB Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name); +EXPCL_INTERROGATEDB Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char *name); +EXPCL_INTERROGATEDB PyObject *Dtool_NewGenerator(PyObject *self, const char *name, iternextfunc func); +EXPCL_INTERROGATEDB PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset); + +#endif // HAVE_PYTHON + +#endif // PY_WRAPPERS_H diff --git a/dtool/src/parser-inc/arpa/inet.h b/dtool/src/parser-inc/arpa/inet.h new file mode 100644 index 0000000000..a3ed68c461 --- /dev/null +++ b/dtool/src/parser-inc/arpa/inet.h @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/dtool/src/parser-inc/btBulletDynamicsCommon.h b/dtool/src/parser-inc/btBulletDynamicsCommon.h index f62fc5bee6..5185d952b2 100644 --- a/dtool/src/parser-inc/btBulletDynamicsCommon.h +++ b/dtool/src/parser-inc/btBulletDynamicsCommon.h @@ -65,7 +65,6 @@ class btPoint2PointConstraint; class btPolyhedralConvexShape; class btQuaternion; class btSequentialImpulseConstraintSolver; -class btScalar; class btSliderConstraint; class btSoftBodyHelpers; class btSoftBodyRigidBodyCollisionConfiguration; @@ -77,14 +76,17 @@ class btStaticPlaneShape; class btStridingMeshInterface; class btTransform; class btTranslationalLimitMotor; +class btTriangleIndexVertexArray; class btTriangleMesh; class btTypedConstraint; class btTypedObject; -class btVector3; class btVehicleRaycaster; template class btAlignedObjectArray; +struct btVector3 {}; +typedef double btScalar; + class btWheelInfo { public: class RaycastInfo; diff --git a/dtool/src/parser-inc/ft2build.h b/dtool/src/parser-inc/ft2build.h index 6b4184a467..80966f984d 100644 --- a/dtool/src/parser-inc/ft2build.h +++ b/dtool/src/parser-inc/ft2build.h @@ -29,6 +29,7 @@ class FT_Library; class FT_Bitmap; class FT_Vector; class FT_Span; +class FT_Outline; #endif diff --git a/dtool/src/parser-inc/iostream b/dtool/src/parser-inc/iostream index 6ba0763c44..170d1d03f9 100644 --- a/dtool/src/parser-inc/iostream +++ b/dtool/src/parser-inc/iostream @@ -88,6 +88,9 @@ __published: streampos tellp(); void seekp(streampos pos); void seekp(streamoff off, ios_base::seekdir dir); + +protected: + ostream(ostream &&); }; class istream : virtual public ios { __published: @@ -97,12 +100,18 @@ __published: streampos tellg(); void seekg(streampos pos); void seekg(streamoff off, ios_base::seekdir dir); + +protected: + istream(istream &&); }; class iostream : public istream, public ostream { __published: iostream(const iostream&) = delete; void flush(); + +protected: + iostream(iostream &&); }; class ofstream : public ostream { diff --git a/dtool/src/parser-inc/iterator b/dtool/src/parser-inc/iterator index 40b8e2f479..c6cdf654fa 100644 --- a/dtool/src/parser-inc/iterator +++ b/dtool/src/parser-inc/iterator @@ -1,6 +1,74 @@ #pragma once +#include +#include + namespace std { - template class iterator; - template class reverse_iterator; + // Iterator tags + struct input_iterator_tag {}; + struct output_iterator_tag {}; + struct forward_iterator_tag : public input_iterator_tag {}; + struct bidirectional_iterator_tag : public forward_iterator_tag {}; + struct random_access_iterator_tag : public bidirectional_iterator_tag {}; + + // Iterator traits + template struct iterator_traits { + typedef typename Iterator::difference_type difference_type; + typedef typename Iterator::value_type value_type; + typedef typename Iterator::pointer pointer; + typedef typename Iterator::reference reference; + typedef typename Iterator::iterator_category iterator_category; + }; + + template struct iterator_traits { + typedef ptrdiff_t difference_type; + typedef T value_type; + typedef T *pointer; + typedef T &reference; + typedef random_access_iterator_tag iterator_category; + }; + + template struct iterator_traits { + typedef ptrdiff_t difference_type; + typedef T value_type; + typedef const T *pointer; + typedef const T &reference; + typedef random_access_iterator_tag iterator_category; + }; + + // Iterators + template struct iterator { + typedef T value_type; + typedef Distance difference_type; + typedef Pointer pointer; + typedef Reference reference; + typedef Category iterator_category; + }; + + template + class reverse_iterator : public + iterator::iterator_category, + typename iterator_traits::value_type, + typename iterator_traits::difference_type, + typename iterator_traits::pointer, + typename iterator_traits::reference> { + public: + typedef Iterator iterator_type; + typedef typename iterator_traits::difference_type difference_type; + typedef typename iterator_traits::reference reference; + typedef typename iterator_traits::pointer pointer; + }; + + template, + class Distance = ptrdiff_t> + class istream_iterator; + + template> + class ostream_iterator; + + template> + class istreambuf_iterator; + template > + class ostreambuf_iterator; }; diff --git a/dtool/src/parser-inc/memory b/dtool/src/parser-inc/memory index 30a54d006a..8c3a7b1b1d 100644 --- a/dtool/src/parser-inc/memory +++ b/dtool/src/parser-inc/memory @@ -122,7 +122,7 @@ namespace std { struct is_always_equal; template using rebind_alloc = size_t; - template using rebind_traits = allocator_traits >; + //template using rebind_traits = allocator_traits >; }; template class allocator; diff --git a/dtool/src/parser-inc/netdb.h b/dtool/src/parser-inc/netdb.h new file mode 100644 index 0000000000..367025cd3a --- /dev/null +++ b/dtool/src/parser-inc/netdb.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +struct hostent; +struct netent; +struct protoent; +struct servent; diff --git a/dtool/src/parser-inc/openssl/evp.h b/dtool/src/parser-inc/openssl/evp.h index a9ef1fd091..9cc780cb19 100644 --- a/dtool/src/parser-inc/openssl/evp.h +++ b/dtool/src/parser-inc/openssl/evp.h @@ -2,7 +2,6 @@ #ifndef EVP_H #define EVP_H -struct EVP_CIPHER_CTX; -struct EVP_PKEY; +#include #endif diff --git a/dtool/src/parser-inc/openssl/ssl.h b/dtool/src/parser-inc/openssl/ssl.h index d43aca6090..50fe63c4bc 100644 --- a/dtool/src/parser-inc/openssl/ssl.h +++ b/dtool/src/parser-inc/openssl/ssl.h @@ -2,13 +2,14 @@ #ifndef SSL_H #define SSL_H -struct BIO; -struct SSL_CTX; -struct EVP_CIPHER_CTX; -struct EVP_PKEY; -struct X509; -struct X509_STORE; -struct X509_NAME; +typedef struct bio_st BIO; +typedef struct ssl_ctx_st SSL_CTX; +typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX; +typedef struct evp_pkey_st EVP_PKEY; +typedef struct x509_st X509; +typedef struct x509_store_st X509_STORE; +typedef struct X509_name_st X509_NAME; +typedef struct ssl_cipher_st SSL_CIPHER; struct SSL; #define STACK_OF(type) struct stack_st_##type diff --git a/dtool/src/parser-inc/openssl/x509.h b/dtool/src/parser-inc/openssl/x509.h index bbd2598dfc..d2bfcbaa5c 100644 --- a/dtool/src/parser-inc/openssl/x509.h +++ b/dtool/src/parser-inc/openssl/x509.h @@ -2,9 +2,7 @@ #ifndef X509_H #define X509_H -struct X509; -struct X509_STORE; -struct X509_NAME; +#include #endif diff --git a/dtool/src/parser-inc/opus/opus.h b/dtool/src/parser-inc/opus/opus.h new file mode 100644 index 0000000000..8b5341cae7 --- /dev/null +++ b/dtool/src/parser-inc/opus/opus.h @@ -0,0 +1,6 @@ +#ifndef OPUS_H +#define OPUS_H + +#include "opus_types.h" + +#endif diff --git a/dtool/src/parser-inc/opus/opus_types.h b/dtool/src/parser-inc/opus/opus_types.h new file mode 100644 index 0000000000..ca01109eb7 --- /dev/null +++ b/dtool/src/parser-inc/opus/opus_types.h @@ -0,0 +1,19 @@ +#ifndef OPUS_TYPES_H +#define OPUS_TYPES_H + +#include + +typedef int16_t opus_int16; +typedef uint16_t opus_uint16; +typedef int32_t opus_int32; +typedef uint32_t opus_uint32; + +#define opus_int int +#define opus_int64 long long +#define opus_int8 signed char + +#define opus_uint unsigned int +#define opus_uint64 unsigned long long +#define opus_uint8 unsigned char + +#endif diff --git a/dtool/src/parser-inc/opus/opusfile.h b/dtool/src/parser-inc/opus/opusfile.h new file mode 100644 index 0000000000..1d9b656a05 --- /dev/null +++ b/dtool/src/parser-inc/opus/opusfile.h @@ -0,0 +1,8 @@ +#include "opus.h" + +typedef struct OpusHead OpusHead; +typedef struct OpusTags OpusTags; +typedef struct OpusPictureTag OpusPictureTag; +typedef struct OpusServerInfo OpusServerInfo; +typedef struct OpusFileCallbacks OpusFileCallbacks; +typedef struct OggOpusFile OggOpusFile; diff --git a/dtool/src/parser-inc/stddef.h b/dtool/src/parser-inc/stddef.h index 6867ac2156..c0ff839b74 100644 --- a/dtool/src/parser-inc/stddef.h +++ b/dtool/src/parser-inc/stddef.h @@ -23,7 +23,5 @@ #define offsetof(type,member) ((size_t) &(((type*)0)->member)) -typedef decltype(nullptr) nullptr_t; - #endif diff --git a/dtool/src/parser-inc/stdtypedefs.h b/dtool/src/parser-inc/stdtypedefs.h index a14a140d2f..c1febd3955 100644 --- a/dtool/src/parser-inc/stdtypedefs.h +++ b/dtool/src/parser-inc/stdtypedefs.h @@ -41,11 +41,7 @@ inline namespace std { struct timeval; -#ifdef __cplusplus -#define NULL 0L -#else -#define NULL ((void *)0) -#endif +typedef decltype(nullptr) nullptr_t; // One day, we might extend interrogate to be able to parse this, // but we currently don't need it. diff --git a/dtool/src/prc/configVariable.h b/dtool/src/prc/configVariable.h index c8adb39e65..3e42efe4d0 100644 --- a/dtool/src/prc/configVariable.h +++ b/dtool/src/prc/configVariable.h @@ -35,7 +35,7 @@ protected: const string &description, int flags); PUBLISHED: - INLINE ConfigVariable(const string &name); + INLINE explicit ConfigVariable(const string &name); INLINE ~ConfigVariable(); INLINE const string &get_string_value() const; diff --git a/dtool/src/prc/configVariableBool.I b/dtool/src/prc/configVariableBool.I index 4e6b0ab35f..7eba0820d0 100644 --- a/dtool/src/prc/configVariableBool.I +++ b/dtool/src/prc/configVariableBool.I @@ -67,7 +67,7 @@ operator = (bool value) { /** * Returns the variable's value. */ -INLINE ConfigVariableBool:: +ALWAYS_INLINE ConfigVariableBool:: operator bool () const { return get_value(); } @@ -100,12 +100,11 @@ set_value(bool value) { /** * Returns the variable's value. */ -INLINE bool ConfigVariableBool:: +ALWAYS_INLINE bool ConfigVariableBool:: get_value() const { TAU_PROFILE("bool ConfigVariableBool::get_value() const", " ", TAU_USER); if (!is_cache_valid(_local_modified)) { - mark_cache_valid(((ConfigVariableBool *)this)->_local_modified); - ((ConfigVariableBool *)this)->_cache = get_bool_word(0); + reload_value(); } return _cache; } diff --git a/dtool/src/prc/configVariableBool.cxx b/dtool/src/prc/configVariableBool.cxx index d063b06fd5..7fb604642d 100644 --- a/dtool/src/prc/configVariableBool.cxx +++ b/dtool/src/prc/configVariableBool.cxx @@ -12,3 +12,12 @@ */ #include "configVariableBool.h" + +/** + * Refreshes the cached value. + */ +void ConfigVariableBool:: +reload_value() const { + mark_cache_valid(_local_modified); + _cache = get_bool_word(0); +} diff --git a/dtool/src/prc/configVariableBool.h b/dtool/src/prc/configVariableBool.h index a0d8b0b1dd..9f486c4266 100644 --- a/dtool/src/prc/configVariableBool.h +++ b/dtool/src/prc/configVariableBool.h @@ -29,13 +29,13 @@ PUBLISHED: const string &description = string(), int flags = 0); INLINE void operator = (bool value); - INLINE operator bool () const; + ALWAYS_INLINE operator bool () const; INLINE size_t size() const; INLINE bool operator [] (size_t n) const; INLINE void set_value(bool value); - INLINE bool get_value() const; + ALWAYS_INLINE bool get_value() const; INLINE bool get_default_value() const; MAKE_PROPERTY(value, get_value, set_value); MAKE_PROPERTY(default_value, get_default_value); @@ -44,8 +44,10 @@ PUBLISHED: INLINE void set_word(size_t n, bool value); private: - AtomicAdjust::Integer _local_modified; - bool _cache; + void reload_value() const; + + mutable AtomicAdjust::Integer _local_modified; + mutable bool _cache; }; #include "configVariableBool.I" diff --git a/dtool/src/prc/encryptStream.h b/dtool/src/prc/encryptStream.h index 3093e5d12f..a5d4972ccd 100644 --- a/dtool/src/prc/encryptStream.h +++ b/dtool/src/prc/encryptStream.h @@ -34,8 +34,8 @@ class EXPCL_DTOOLCONFIG IDecryptStream : public istream { PUBLISHED: INLINE IDecryptStream(); - INLINE IDecryptStream(istream *source, bool owns_source, - const string &password); + INLINE explicit IDecryptStream(istream *source, bool owns_source, + const string &password); #if _MSC_VER >= 1800 INLINE IDecryptStream(const IDecryptStream ©) = delete; @@ -69,8 +69,8 @@ private: class EXPCL_DTOOLCONFIG OEncryptStream : public ostream { PUBLISHED: INLINE OEncryptStream(); - INLINE OEncryptStream(ostream *dest, bool owns_dest, - const string &password); + INLINE explicit OEncryptStream(ostream *dest, bool owns_dest, + const string &password); #if _MSC_VER >= 1800 INLINE OEncryptStream(const OEncryptStream ©) = delete; diff --git a/dtool/src/prc/encryptStreamBuf.cxx b/dtool/src/prc/encryptStreamBuf.cxx index 6cb0ee6b39..21a33f93c0 100644 --- a/dtool/src/prc/encryptStreamBuf.cxx +++ b/dtool/src/prc/encryptStreamBuf.cxx @@ -21,6 +21,7 @@ #ifdef HAVE_OPENSSL #include "openssl/rand.h" +#include "openssl/evp.h" #ifndef HAVE_STREAMSIZE // Some compilers (notably SGI) don't define this for us @@ -235,7 +236,7 @@ open_write(ostream *dest, bool owns_dest, const string &password) { // Generate a random IV. It doesn't need to be cryptographically secure, // just unique. unsigned char *iv = (unsigned char *)alloca(iv_length); - RAND_pseudo_bytes(iv, iv_length); + RAND_bytes(iv, iv_length); _write_ctx = EVP_CIPHER_CTX_new(); nassertv(_write_ctx != NULL); diff --git a/dtool/src/prc/encryptStreamBuf.h b/dtool/src/prc/encryptStreamBuf.h index 4861a89240..b7d95424c2 100644 --- a/dtool/src/prc/encryptStreamBuf.h +++ b/dtool/src/prc/encryptStreamBuf.h @@ -19,7 +19,7 @@ // This module is not compiled if OpenSSL is not available. #ifdef HAVE_OPENSSL -#include "openssl/evp.h" +typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX; /** * The streambuf object that implements IDecompressStream and OCompressStream. diff --git a/dtool/src/prc/notifyCategory.I b/dtool/src/prc/notifyCategory.I index a485447dcc..46aca56bb2 100644 --- a/dtool/src/prc/notifyCategory.I +++ b/dtool/src/prc/notifyCategory.I @@ -70,7 +70,8 @@ is_on(NotifySeverity severity) const { */ INLINE bool NotifyCategory:: is_spam() const { - return is_on(NS_spam); + // Instruct the compiler to optimize for the usual case. + return UNLIKELY(is_on(NS_spam)); } /** @@ -78,7 +79,8 @@ is_spam() const { */ INLINE bool NotifyCategory:: is_debug() const { - return is_on(NS_debug); + // Instruct the compiler to optimize for the usual case. + return UNLIKELY(is_on(NS_debug)); } #else /** diff --git a/dtool/src/prc/notifyCategoryProxy.I b/dtool/src/prc/notifyCategoryProxy.I index 08c253f081..9a05f35625 100644 --- a/dtool/src/prc/notifyCategoryProxy.I +++ b/dtool/src/prc/notifyCategoryProxy.I @@ -69,7 +69,8 @@ is_on(NotifySeverity severity) { template INLINE bool NotifyCategoryProxy:: is_spam() { - return get_unsafe_ptr()->is_spam(); + // Instruct the compiler to optimize for the usual case. + return UNLIKELY(get_unsafe_ptr()->is_spam()); } #else template @@ -86,7 +87,8 @@ is_spam() { template INLINE bool NotifyCategoryProxy:: is_debug() { - return get_unsafe_ptr()->is_debug(); + // Instruct the compiler to optimize for the usual case. + return UNLIKELY(get_unsafe_ptr()->is_debug()); } #else template diff --git a/dtool/src/prc/p3prc_ext_composite.cxx b/dtool/src/prc/p3prc_ext_composite.cxx new file mode 100644 index 0000000000..223ef504cd --- /dev/null +++ b/dtool/src/prc/p3prc_ext_composite.cxx @@ -0,0 +1,2 @@ +#include "streamReader_ext.cxx" +#include "streamWriter_ext.cxx" diff --git a/dtool/src/prc/pnotify.h b/dtool/src/prc/pnotify.h index a8254cc910..e51022c28a 100644 --- a/dtool/src/prc/pnotify.h +++ b/dtool/src/prc/pnotify.h @@ -122,6 +122,13 @@ private: // constant expressions and compilation will fail if the assertion is not // true. +#ifdef __GNUC__ +// Tell the optimizer to optimize for the case where the condition is true. +#define _nassert_check(condition) (__builtin_expect(!(condition), 0)) +#else +#define _nassert_check(condition) (!(condition)) +#endif + #ifdef NDEBUG #define nassertr(condition, return_value) @@ -131,27 +138,25 @@ private: #define nassertr_always(condition, return_value) \ { \ - if (!(condition)) { \ + if (_nassert_check(condition)) { \ return return_value; \ } \ } #define nassertv_always(condition) \ { \ - if (!(condition)) { \ + if (_nassert_check(condition)) { \ return; \ } \ } #define nassert_raise(message) Notify::write_string(message) -#define enter_debugger_if(condition) ((void)0) - #else // NDEBUG #define nassertr(condition, return_value) \ { \ - if (!(condition)) { \ + if (_nassert_check(condition)) { \ if (Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) { \ return return_value; \ } \ @@ -160,7 +165,7 @@ private: #define nassertv(condition) \ { \ - if (!(condition)) { \ + if (_nassert_check(condition)) { \ if (Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) { \ return; \ } \ @@ -168,7 +173,7 @@ private: } #define nassertd(condition) \ - if (!(condition) && \ + if (_nassert_check(condition) && \ Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) #define nassertr_always(condition, return_value) nassertr(condition, return_value) @@ -176,13 +181,6 @@ private: #define nassert_raise(message) Notify::ptr()->assert_failure(message, __LINE__, __FILE__) -#define enter_debugger_if(condition) \ - if (condition) { \ - Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__); \ - __asm { int 3 } \ - } - - #endif // NDEBUG #if __cplusplus >= 201103 diff --git a/dtool/src/prc/prcKeyRegistry.cxx b/dtool/src/prc/prcKeyRegistry.cxx index 9b760ac53d..6329a8abbd 100644 --- a/dtool/src/prc/prcKeyRegistry.cxx +++ b/dtool/src/prc/prcKeyRegistry.cxx @@ -19,8 +19,12 @@ #ifdef HAVE_OPENSSL +#include "openssl/evp.h" #include "openssl/pem.h" +// Some versions of OpenSSL appear to define this as a macro. Yucky. +#undef set_key + PrcKeyRegistry *PrcKeyRegistry::_global_ptr = NULL; /** diff --git a/dtool/src/prc/prcKeyRegistry.h b/dtool/src/prc/prcKeyRegistry.h index 1b53287215..69e25d949e 100644 --- a/dtool/src/prc/prcKeyRegistry.h +++ b/dtool/src/prc/prcKeyRegistry.h @@ -22,10 +22,8 @@ #ifdef HAVE_OPENSSL #include -#include "openssl/evp.h" -// Some versions of OpenSSL appear to define this as a macro. Yucky. -#undef set_key +typedef struct evp_pkey_st EVP_PKEY; /** * This class records the set of public keys used to verify the signature on a diff --git a/dtool/src/prc/streamReader.h b/dtool/src/prc/streamReader.h index 1c820253c1..3d8865e21d 100644 --- a/dtool/src/prc/streamReader.h +++ b/dtool/src/prc/streamReader.h @@ -28,7 +28,7 @@ class EXPCL_DTOOLCONFIG StreamReader { public: INLINE StreamReader(istream &in); PUBLISHED: - INLINE StreamReader(istream *in, bool owns_stream); + INLINE explicit StreamReader(istream *in, bool owns_stream); INLINE StreamReader(const StreamReader ©); INLINE void operator = (const StreamReader ©); INLINE ~StreamReader(); diff --git a/panda/src/express/streamReader_ext.cxx b/dtool/src/prc/streamReader_ext.cxx similarity index 100% rename from panda/src/express/streamReader_ext.cxx rename to dtool/src/prc/streamReader_ext.cxx diff --git a/panda/src/express/streamReader_ext.h b/dtool/src/prc/streamReader_ext.h similarity index 100% rename from panda/src/express/streamReader_ext.h rename to dtool/src/prc/streamReader_ext.h diff --git a/dtool/src/prc/streamWrapper.h b/dtool/src/prc/streamWrapper.h index 97d391a894..856cf6f855 100644 --- a/dtool/src/prc/streamWrapper.h +++ b/dtool/src/prc/streamWrapper.h @@ -25,6 +25,9 @@ class EXPCL_DTOOLCONFIG StreamWrapperBase { protected: INLINE StreamWrapperBase(); +private: + INLINE StreamWrapperBase(const StreamWrapperBase ©) DELETED; + PUBLISHED: INLINE void acquire(); INLINE void release(); @@ -49,7 +52,7 @@ class EXPCL_DTOOLCONFIG IStreamWrapper : virtual public StreamWrapperBase { public: INLINE IStreamWrapper(istream *stream, bool owns_pointer); PUBLISHED: - INLINE IStreamWrapper(istream &stream); + INLINE explicit IStreamWrapper(istream &stream); ~IStreamWrapper(); INLINE istream *get_istream() const; @@ -76,7 +79,7 @@ class EXPCL_DTOOLCONFIG OStreamWrapper : virtual public StreamWrapperBase { public: INLINE OStreamWrapper(ostream *stream, bool owns_pointer, bool stringstream_hack = false); PUBLISHED: - INLINE OStreamWrapper(ostream &stream); + INLINE explicit OStreamWrapper(ostream &stream); ~OStreamWrapper(); INLINE ostream *get_ostream() const; @@ -112,7 +115,7 @@ class EXPCL_DTOOLCONFIG StreamWrapper : public IStreamWrapper, public OStreamWra public: INLINE StreamWrapper(iostream *stream, bool owns_pointer, bool stringstream_hack = false); PUBLISHED: - INLINE StreamWrapper(iostream &stream); + INLINE explicit StreamWrapper(iostream &stream); ~StreamWrapper(); INLINE iostream *get_iostream() const; diff --git a/dtool/src/prc/streamWriter.h b/dtool/src/prc/streamWriter.h index 2f04a9ab8d..8e2cf4dc1b 100644 --- a/dtool/src/prc/streamWriter.h +++ b/dtool/src/prc/streamWriter.h @@ -30,7 +30,7 @@ class EXPCL_DTOOLCONFIG StreamWriter { public: INLINE StreamWriter(ostream &out); PUBLISHED: - INLINE StreamWriter(ostream *out, bool owns_stream); + INLINE explicit StreamWriter(ostream *out, bool owns_stream); INLINE StreamWriter(const StreamWriter ©); INLINE void operator = (const StreamWriter ©); INLINE ~StreamWriter(); diff --git a/panda/src/express/streamWriter_ext.cxx b/dtool/src/prc/streamWriter_ext.cxx similarity index 100% rename from panda/src/express/streamWriter_ext.cxx rename to dtool/src/prc/streamWriter_ext.cxx diff --git a/panda/src/express/streamWriter_ext.h b/dtool/src/prc/streamWriter_ext.h similarity index 100% rename from panda/src/express/streamWriter_ext.h rename to dtool/src/prc/streamWriter_ext.h diff --git a/dtool/src/prckeys/makePrcKey.cxx b/dtool/src/prckeys/makePrcKey.cxx index 57672e5c8a..6832860540 100644 --- a/dtool/src/prckeys/makePrcKey.cxx +++ b/dtool/src/prckeys/makePrcKey.cxx @@ -108,16 +108,25 @@ output_c_string(ostream &out, const string &string_name, */ EVP_PKEY * generate_key() { - RSA *rsa = RSA_generate_key(1024, 7, NULL, NULL); - - if (rsa == (RSA *)NULL) { + RSA *rsa = RSA_new(); + BIGNUM *e = BN_new(); + if (rsa == nullptr || e == nullptr) { output_ssl_errors(); exit(1); } + BN_set_word(e, 7); + + if (!RSA_generate_key_ex(rsa, 1024, e, nullptr)) { + BN_free(e); + RSA_free(rsa); + output_ssl_errors(); + exit(1); + } + BN_free(e); + EVP_PKEY *pkey = EVP_PKEY_new(); EVP_PKEY_assign_RSA(pkey, rsa); - return pkey; } @@ -161,7 +170,7 @@ write_public_keys(Filename outfile) { } output_c_string(out, "prc_pubkey", i, mbio); - BIO_reset(mbio); + (void)BIO_reset(mbio); out << "\n"; } } diff --git a/dtool/src/pystub/pystub.cxx b/dtool/src/pystub/pystub.cxx index d0d6a4ca1a..f9a30e74bf 100644 --- a/dtool/src/pystub/pystub.cxx +++ b/dtool/src/pystub/pystub.cxx @@ -32,6 +32,7 @@ extern "C" { EXPCL_PYSTUB int PyDict_GetItem(...); EXPCL_PYSTUB int PyDict_GetItemString(...); EXPCL_PYSTUB int PyDict_New(...); + EXPCL_PYSTUB int PyDict_Next(...); EXPCL_PYSTUB int PyDict_SetItem(...); EXPCL_PYSTUB int PyDict_SetItemString(...); EXPCL_PYSTUB int PyDict_Size(...); @@ -49,7 +50,6 @@ extern "C" { EXPCL_PYSTUB int PyErr_WarnEx(...); EXPCL_PYSTUB int PyEval_CallFunction(...); EXPCL_PYSTUB int PyEval_CallObjectWithKeywords(...); - EXPCL_PYSTUB int PyEval_InitThreads(...); EXPCL_PYSTUB int PyEval_RestoreThread(...); EXPCL_PYSTUB int PyEval_SaveThread(...); EXPCL_PYSTUB int PyFloat_AsDouble(...); @@ -66,6 +66,7 @@ extern "C" { EXPCL_PYSTUB int PyInt_FromLong(...); EXPCL_PYSTUB int PyInt_FromSize_t(...); EXPCL_PYSTUB int PyInt_Type(...); + EXPCL_PYSTUB int PyIter_Next(...); EXPCL_PYSTUB int PyList_Append(...); EXPCL_PYSTUB int PyList_AsTuple(...); EXPCL_PYSTUB int PyList_GetItem(...); @@ -90,6 +91,9 @@ extern "C" { EXPCL_PYSTUB int PyModule_AddObject(...); EXPCL_PYSTUB int PyModule_AddStringConstant(...); EXPCL_PYSTUB int PyModule_Create2(...); + EXPCL_PYSTUB int PyModule_Create2TraceRefs(...); + EXPCL_PYSTUB int PyModule_GetDict(...); + EXPCL_PYSTUB int PyNumber_AsSsize_t(...); EXPCL_PYSTUB int PyNumber_Check(...); EXPCL_PYSTUB int PyNumber_Float(...); EXPCL_PYSTUB int PyNumber_Int(...); @@ -104,18 +108,23 @@ extern "C" { EXPCL_PYSTUB int PyObject_Cmp(...); EXPCL_PYSTUB int PyObject_Compare(...); EXPCL_PYSTUB int PyObject_Free(...); + EXPCL_PYSTUB int PyObject_GC_Del(...); EXPCL_PYSTUB int PyObject_GenericGetAttr(...); EXPCL_PYSTUB int PyObject_GenericSetAttr(...); EXPCL_PYSTUB int PyObject_GetAttrString(...); EXPCL_PYSTUB int PyObject_GetBuffer(...); + EXPCL_PYSTUB int PyObject_GetIter(...); EXPCL_PYSTUB int PyObject_HasAttrString(...); EXPCL_PYSTUB int PyObject_IsInstance(...); EXPCL_PYSTUB int PyObject_IsTrue(...); + EXPCL_PYSTUB int PyObject_Malloc(...); EXPCL_PYSTUB int PyObject_Repr(...); EXPCL_PYSTUB int PyObject_RichCompareBool(...); + EXPCL_PYSTUB int PyObject_SelfIter(...); EXPCL_PYSTUB int PyObject_SetAttrString(...); EXPCL_PYSTUB int PyObject_Str(...); EXPCL_PYSTUB int PyObject_Type(...); + EXPCL_PYSTUB int PySeqIter_New(...); EXPCL_PYSTUB int PySequence_Check(...); EXPCL_PYSTUB int PySequence_Fast(...); EXPCL_PYSTUB int PySequence_GetItem(...); @@ -140,28 +149,30 @@ extern "C" { EXPCL_PYSTUB int PyTuple_New(...); EXPCL_PYSTUB int PyTuple_Pack(...); EXPCL_PYSTUB int PyTuple_Size(...); - EXPCL_PYSTUB int PyTuple_Type(...); EXPCL_PYSTUB int PyType_GenericAlloc(...); EXPCL_PYSTUB int PyType_IsSubtype(...); EXPCL_PYSTUB int PyType_Ready(...); + EXPCL_PYSTUB int PyUnicodeUCS2_AsWideChar(...); + EXPCL_PYSTUB int PyUnicodeUCS2_AsWideCharString(...); + EXPCL_PYSTUB int PyUnicodeUCS2_CompareWithASCIIString(...); EXPCL_PYSTUB int PyUnicodeUCS2_FromFormat(...); EXPCL_PYSTUB int PyUnicodeUCS2_FromString(...); EXPCL_PYSTUB int PyUnicodeUCS2_FromStringAndSize(...); EXPCL_PYSTUB int PyUnicodeUCS2_FromWideChar(...); - EXPCL_PYSTUB int PyUnicodeUCS2_AsWideChar(...); - EXPCL_PYSTUB int PyUnicodeUCS2_AsWideCharString(...); EXPCL_PYSTUB int PyUnicodeUCS2_GetSize(...); + EXPCL_PYSTUB int PyUnicodeUCS4_AsWideChar(...); + EXPCL_PYSTUB int PyUnicodeUCS4_AsWideCharString(...); + EXPCL_PYSTUB int PyUnicodeUCS4_CompareWithASCIIString(...); EXPCL_PYSTUB int PyUnicodeUCS4_FromFormat(...); EXPCL_PYSTUB int PyUnicodeUCS4_FromString(...); EXPCL_PYSTUB int PyUnicodeUCS4_FromStringAndSize(...); EXPCL_PYSTUB int PyUnicodeUCS4_FromWideChar(...); - EXPCL_PYSTUB int PyUnicodeUCS4_AsWideChar(...); - EXPCL_PYSTUB int PyUnicodeUCS4_AsWideCharString(...); EXPCL_PYSTUB int PyUnicodeUCS4_GetSize(...); EXPCL_PYSTUB int PyUnicode_AsUTF8(...); EXPCL_PYSTUB int PyUnicode_AsUTF8AndSize(...); EXPCL_PYSTUB int PyUnicode_AsWideChar(...); EXPCL_PYSTUB int PyUnicode_AsWideCharString(...); + EXPCL_PYSTUB int PyUnicode_CompareWithASCIIString(...); EXPCL_PYSTUB int PyUnicode_FromFormat(...); EXPCL_PYSTUB int PyUnicode_FromString(...); EXPCL_PYSTUB int PyUnicode_FromStringAndSize(...); @@ -175,22 +186,33 @@ extern "C" { EXPCL_PYSTUB int Py_InitModule4(...); EXPCL_PYSTUB int Py_InitModule4_64(...); EXPCL_PYSTUB int Py_InitModule4TraceRefs(...); + EXPCL_PYSTUB int Py_InitModule4TraceRefs_64(...); EXPCL_PYSTUB int _PyArg_ParseTuple_SizeT(...); EXPCL_PYSTUB int _PyArg_ParseTupleAndKeywords_SizeT(...); EXPCL_PYSTUB int _PyArg_Parse_SizeT(...); + EXPCL_PYSTUB int _PyErr_BadInternalCall(...); + EXPCL_PYSTUB int _PyLong_AsByteArray(...); EXPCL_PYSTUB int _PyObject_CallFunction_SizeT(...); EXPCL_PYSTUB int _PyObject_CallMethod_SizeT(...); EXPCL_PYSTUB int _PyObject_DebugFree(...); EXPCL_PYSTUB int _PyObject_Del(...); + EXPCL_PYSTUB int _PyObject_FastCallDict(...); EXPCL_PYSTUB int _PyUnicode_AsString(...); EXPCL_PYSTUB int _PyUnicode_AsStringAndSize(...); + EXPCL_PYSTUB int _PyUnicode_EqualToASCIIString(...); + EXPCL_PYSTUB int _Py_AddToAllObjects(...); EXPCL_PYSTUB int _Py_BuildValue_SizeT(...); EXPCL_PYSTUB int _Py_Dealloc(...); + EXPCL_PYSTUB int _Py_ForgetReference(...); EXPCL_PYSTUB int _Py_NegativeRefcount(...); + EXPCL_PYSTUB int _Py_NewReference(...); + EXPCL_PYSTUB int _Py_PrintReferenceAddresses(...); + EXPCL_PYSTUB int _Py_PrintReferences(...); EXPCL_PYSTUB int _Py_RefTotal(...); EXPCL_PYSTUB void Py_Initialize(); EXPCL_PYSTUB int Py_IsInitialized(); + EXPCL_PYSTUB void PyEval_InitThreads(); EXPCL_PYSTUB extern void *PyExc_AssertionError; EXPCL_PYSTUB extern void *PyExc_AttributeError; @@ -200,13 +222,17 @@ extern "C" { EXPCL_PYSTUB extern void *PyExc_FutureWarning; EXPCL_PYSTUB extern void *PyExc_ImportError; EXPCL_PYSTUB extern void *PyExc_IndexError; + EXPCL_PYSTUB extern void *PyExc_KeyError; EXPCL_PYSTUB extern void *PyExc_OSError; + EXPCL_PYSTUB extern void *PyExc_OverflowError; EXPCL_PYSTUB extern void *PyExc_RuntimeError; EXPCL_PYSTUB extern void *PyExc_StandardError; EXPCL_PYSTUB extern void *PyExc_StopIteration; EXPCL_PYSTUB extern void *PyExc_SystemExit; EXPCL_PYSTUB extern void *PyExc_TypeError; EXPCL_PYSTUB extern void *PyExc_ValueError; + EXPCL_PYSTUB extern void *PyTuple_Type; + EXPCL_PYSTUB extern void *PyType_Type; EXPCL_PYSTUB extern void *_PyThreadState_Current; EXPCL_PYSTUB extern void *_Py_FalseStruct; EXPCL_PYSTUB extern void *_Py_NoneStruct; @@ -234,6 +260,7 @@ int PyDict_DelItemString(...) { return 0; } int PyDict_GetItem(...) { return 0; } int PyDict_GetItemString(...) { return 0; } int PyDict_New(...) { return 0; }; +int PyDict_Next(...) { return 0; }; int PyDict_SetItem(...) { return 0; }; int PyDict_SetItemString(...) { return 0; }; int PyDict_Size(...){ return 0; } @@ -268,6 +295,7 @@ int PyInt_AsSsize_t(...) { return 0; } int PyInt_FromLong(...) { return 0; } int PyInt_FromSize_t(...) { return 0; } int PyInt_Type(...) { return 0; } +int PyIter_Next(...) { return 0; } int PyList_Append(...) { return 0; } int PyList_AsTuple(...) { return 0; } int PyList_GetItem(...) { return 0; } @@ -292,6 +320,9 @@ int PyModule_AddIntConstant(...) { return 0; }; int PyModule_AddObject(...) { return 0; }; int PyModule_AddStringConstant(...) { return 0; }; int PyModule_Create2(...) { return 0; }; +int PyModule_Create2TraceRefs(...) { return 0; }; +int PyModule_GetDict(...) { return 0; }; +int PyNumber_AsSsize_t(...) { return 0; } int PyNumber_Check(...) { return 0; } int PyNumber_Float(...) { return 0; } int PyNumber_Int(...) { return 0; } @@ -306,18 +337,23 @@ int PyObject_CallObject(...) { return 0; } int PyObject_Cmp(...) { return 0; } int PyObject_Compare(...) { return 0; } int PyObject_Free(...) { return 0; } +int PyObject_GC_Del(...) { return 0; } int PyObject_GenericGetAttr(...) { return 0; }; int PyObject_GenericSetAttr(...) { return 0; }; int PyObject_GetAttrString(...) { return 0; } int PyObject_GetBuffer(...) { return 0; } +int PyObject_GetIter(...) { return 0; } int PyObject_HasAttrString(...) { return 0; } int PyObject_IsInstance(...) { return 0; } int PyObject_IsTrue(...) { return 0; } +int PyObject_Malloc(...) { return 0; } int PyObject_Repr(...) { return 0; } int PyObject_RichCompareBool(...) { return 0; } +int PyObject_SelfIter(...) { return 0; } int PyObject_SetAttrString(...) { return 0; } int PyObject_Str(...) { return 0; } int PyObject_Type(...) { return 0; } +int PySeqIter_New(...) { return 0; } int PySequence_Check(...) { return 0; } int PySequence_Fast(...) { return 0; } int PySequence_GetItem(...) { return 0; } @@ -342,28 +378,30 @@ int PyTuple_GetItem(...) { return 0; } int PyTuple_New(...) { return 0; } int PyTuple_Pack(...) { return 0; } int PyTuple_Size(...) { return 0; }; -int PyTuple_Type(...) { return 0; }; int PyType_GenericAlloc(...) { return 0; }; int PyType_IsSubtype(...) { return 0; } int PyType_Ready(...) { return 0; }; +int PyUnicodeUCS2_AsWideChar(...) { return 0; } +int PyUnicodeUCS2_AsWideCharString(...) { return 0; } +int PyUnicodeUCS2_CompareWithASCIIString(...) { return 0; } int PyUnicodeUCS2_FromFormat(...) { return 0; } int PyUnicodeUCS2_FromString(...) { return 0; } int PyUnicodeUCS2_FromStringAndSize(...) { return 0; } int PyUnicodeUCS2_FromWideChar(...) { return 0; } -int PyUnicodeUCS2_AsWideChar(...) { return 0; } -int PyUnicodeUCS2_AsWideCharString(...) { return 0; } int PyUnicodeUCS2_GetSize(...) { return 0; } +int PyUnicodeUCS4_AsWideChar(...) { return 0; } +int PyUnicodeUCS4_AsWideCharString(...) { return 0; } +int PyUnicodeUCS4_CompareWithASCIIString(...) { return 0; } int PyUnicodeUCS4_FromFormat(...) { return 0; } int PyUnicodeUCS4_FromString(...) { return 0; } int PyUnicodeUCS4_FromStringAndSize(...) { return 0; } int PyUnicodeUCS4_FromWideChar(...) { return 0; } -int PyUnicodeUCS4_AsWideChar(...) { return 0; } -int PyUnicodeUCS4_AsWideCharString(...) { return 0; } int PyUnicodeUCS4_GetSize(...) { return 0; } int PyUnicode_AsUTF8(...) { return 0; } int PyUnicode_AsUTF8AndSize(...) { return 0; } int PyUnicode_AsWideChar(...) { return 0; } int PyUnicode_AsWideCharString(...) { return 0; } +int PyUnicode_CompareWithASCIIString(...) { return 0; } int PyUnicode_FromFormat(...) { return 0; } int PyUnicode_FromString(...) { return 0; } int PyUnicode_FromStringAndSize(...) { return 0; } @@ -377,18 +415,28 @@ int Py_BuildValue(...) { return 0; } int Py_InitModule4(...) { return 0; } int Py_InitModule4_64(...) { return 0; } int Py_InitModule4TraceRefs(...) { return 0; }; +int Py_InitModule4TraceRefs_64(...) { return 0; }; int _PyArg_ParseTuple_SizeT(...) { return 0; }; int _PyArg_ParseTupleAndKeywords_SizeT(...) { return 0; }; int _PyArg_Parse_SizeT(...) { return 0; }; +int _PyErr_BadInternalCall(...) { return 0; }; +int _PyLong_AsByteArray(...) { return 0; }; int _PyObject_CallFunction_SizeT(...) { return 0; }; int _PyObject_CallMethod_SizeT(...) { return 0; }; int _PyObject_DebugFree(...) { return 0; }; int _PyObject_Del(...) { return 0; }; +int _PyObject_FastCallDict(...) { return 0; }; int _PyUnicode_AsString(...) { return 0; }; int _PyUnicode_AsStringAndSize(...) { return 0; }; +int _PyUnicode_EqualToASCIIString(...) { return 0; }; +int _Py_AddToAllObjects(...) { return 0; }; int _Py_BuildValue_SizeT(...) { return 0; }; int _Py_Dealloc(...) { return 0; }; +int _Py_ForgetReference(...) { return 0; }; int _Py_NegativeRefcount(...) { return 0; }; +int _Py_NewReference(...) { return 0; }; +int _Py_PrintReferenceAddresses(...) { return 0; }; +int _Py_PrintReferences(...) { return 0; }; int _Py_RefTotal(...) { return 0; }; // We actually might call this one. @@ -397,6 +445,8 @@ void Py_Initialize() { int Py_IsInitialized() { return 0; } +void PyEval_InitThreads() { +} void *PyExc_AssertionError = (void *)NULL; @@ -407,13 +457,17 @@ void *PyExc_Exception = (void *)NULL; void *PyExc_FutureWarning = (void *)NULL; void *PyExc_ImportError = (void *)NULL; void *PyExc_IndexError = (void *)NULL; +void *PyExc_KeyError = (void *)NULL; void *PyExc_OSError = (void *)NULL; +void *PyExc_OverflowError = (void *)NULL; void *PyExc_RuntimeError = (void *)NULL; void *PyExc_StandardError = (void *)NULL; void *PyExc_StopIteration = (void *)NULL; void *PyExc_SystemExit = (void *)NULL; void *PyExc_TypeError = (void *)NULL; void *PyExc_ValueError = (void *)NULL; +void *PyTuple_Type = (void *)NULL; +void *PyType_Type = (void *)NULL; void *_PyThreadState_Current = (void *)NULL; void *_Py_FalseStruct = (void *)NULL; void *_Py_NoneStruct = (void *)NULL; diff --git a/dtool/src/test_interrogate/test_interrogate.cxx b/dtool/src/test_interrogate/test_interrogate.cxx index f9aa7c1ab4..6c2b961ff3 100644 --- a/dtool/src/test_interrogate/test_interrogate.cxx +++ b/dtool/src/test_interrogate/test_interrogate.cxx @@ -17,6 +17,7 @@ #include "interrogate_request.h" #include "load_dso.h" #include "filename.h" +#include "pystub.h" #include "panda_getopt.h" #include "preprocess_argv.h" @@ -520,6 +521,8 @@ main(int argc, char **argv) { extern int optind; const char *optstr = "p:ftqh"; + pystub(); + bool all_functions = false; bool all_types = false; bool quick_load = false; diff --git a/makepanda/config.in b/makepanda/config.in index e61f73f04b..1317517339 100755 --- a/makepanda/config.in +++ b/makepanda/config.in @@ -89,7 +89,7 @@ hardware-animated-vertices #f # Enable the model-cache, but only for models, not textures. -model-cache-dir $HOME/.panda3d/cache +model-cache-dir $XDG_CACHE_HOME/panda3d model-cache-textures #f # This option specifies the default profiles for Cg shaders. diff --git a/makepanda/installer.nsi b/makepanda/installer.nsi index d934cc4ee0..bd139f7083 100755 --- a/makepanda/installer.nsi +++ b/makepanda/installer.nsi @@ -141,8 +141,9 @@ SectionGroup "Panda3D Libraries" SetDetailsPrint listonly SetOutPath "$INSTDIR" - File "${BUILT}\LICENSE" - File /r /x CVS "${BUILT}\ReleaseNotes" + File /nonfatal "${BUILT}\LICENSE" + File /nonfatal "${BUILT}\ReleaseNotes" + File /nonfatal "${BUILT}\pandaIcon.ico" SetOutPath $INSTDIR\etc File /r "${BUILT}\etc\*" @@ -532,15 +533,17 @@ Section "Sample programs" SecSamples WriteINIStr $INSTDIR\Manual.url "InternetShortcut" "URL" "https://www.panda3d.org/manual/index.php" WriteINIStr $INSTDIR\Samples.url "InternetShortcut" "URL" "https://www.panda3d.org/manual/index.php/Sample_Programs_in_the_Distribution" SetOutPath $INSTDIR - CreateShortCut "$SMPROGRAMS\${TITLE}\Panda3D Manual.lnk" "$INSTDIR\Manual.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Panda3D Manual" - CreateShortCut "$SMPROGRAMS\${TITLE}\Panda3D Website.lnk" "$INSTDIR\Website.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Panda3D Website" - CreateShortCut "$SMPROGRAMS\${TITLE}\Sample Program Manual.lnk" "$INSTDIR\Samples.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Sample Program Manual" + CreateShortCut "$SMPROGRAMS\${TITLE}\Panda3D Manual.lnk" "$INSTDIR\Manual.url" "" "$INSTDIR\pandaIcon.ico" 0 "" "" "Panda3D Manual" + CreateShortCut "$SMPROGRAMS\${TITLE}\Panda3D Website.lnk" "$INSTDIR\Website.url" "" "$INSTDIR\pandaIcon.ico" 0 "" "" "Panda3D Website" + CreateShortCut "$SMPROGRAMS\${TITLE}\Sample Program Manual.lnk" "$INSTDIR\Samples.url" "" "$INSTDIR\pandaIcon.ico" 0 "" "" "Sample Program Manual" FindFirst $0 $1 $INSTDIR\samples\* loop: StrCmp $1 "" done StrCmp $1 "." next StrCmp $1 ".." next + FindFirst $2 $3 $INSTDIR\samples\$1\*.py + StrCmp $3 "" next Push $1 Push "-" Push " " @@ -557,14 +560,13 @@ Section "Sample programs" SecSamples DetailPrint "Creating shortcuts for sample program $READABLE" CreateDirectory "$SMPROGRAMS\${TITLE}\Sample Programs\$READABLE" SetOutPath $INSTDIR\samples\$1 - WriteINIStr $INSTDIR\samples\$1\ManualPage.url "InternetShortcut" "URL" "http://panda3d.org/wiki/index.php/Sample_Programs:_$MANPAGE" - CreateShortCut "$SMPROGRAMS\${TITLE}\Sample Programs\$READABLE\Manual Page.lnk" "$INSTDIR\samples\$1\ManualPage.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Manual Entry on this Sample Program" + WriteINIStr $INSTDIR\samples\$1\ManualPage.url "InternetShortcut" "URL" "https://www.panda3d.org/wiki/index.php/Sample_Programs:_$MANPAGE" + CreateShortCut "$SMPROGRAMS\${TITLE}\Sample Programs\$READABLE\Manual Page.lnk" "$INSTDIR\samples\$1\ManualPage.url" "" "$INSTDIR\pandaIcon.ico" 0 "" "" "Manual Entry on this Sample Program" CreateShortCut "$SMPROGRAMS\${TITLE}\Sample Programs\$READABLE\View Source Code.lnk" "$INSTDIR\samples\$1" - FindFirst $2 $3 $INSTDIR\samples\$1\*.py iloop: StrCmp $3 "" idone - CreateShortCut "$SMPROGRAMS\${TITLE}\Sample Programs\$READABLE\Run $3.lnk" "$INSTDIR\python\python.exe" "-E $3" "$INSTDIR\bin\eggcacher.exe" 0 SW_SHOWMINIMIZED "" "Run $3" - CreateShortCut "$INSTDIR\samples\$1\Run $3.lnk" "$INSTDIR\python\python.exe" "-E $3" "$INSTDIR\bin\eggcacher.exe" 0 SW_SHOWMINIMIZED "" "Run $3" + CreateShortCut "$SMPROGRAMS\${TITLE}\Sample Programs\$READABLE\Run $3.lnk" "$INSTDIR\python\python.exe" "-E $3" "$INSTDIR\pandaIcon.ico" 0 SW_SHOWMINIMIZED "" "Run $3" + CreateShortCut "$INSTDIR\samples\$1\Run $3.lnk" "$INSTDIR\python\python.exe" "-E $3" "$INSTDIR\pandaIcon.ico" 0 SW_SHOWMINIMIZED "" "Run $3" FindNext $2 $3 goto iloop idone: @@ -586,6 +588,7 @@ Section "3ds Max plug-ins" SecMaxPlugins SetOutPath $INSTDIR\plugins File /nonfatal /r "${BUILT}\plugins\*.dle" File /nonfatal /r "${BUILT}\plugins\*.dlo" + File /nonfatal /r "${BUILT}\plugins\*.ms" File "${SOURCE}\doc\INSTALLING-PLUGINS.TXT" SectionEnd !endif @@ -601,7 +604,6 @@ Section "Maya plug-ins" SecMayaPlugins SetOutPath $INSTDIR\plugins File /nonfatal /r "${BUILT}\plugins\*.mll" File /nonfatal /r "${BUILT}\plugins\*.mel" - File /nonfatal /r "${BUILT}\plugins\*.ms" File "${SOURCE}\doc\INSTALLING-PLUGINS.TXT" SectionEnd !endif @@ -617,13 +619,10 @@ Section -post DetailPrint "Preloading .egg files into the model cache..." SetDetailsPrint listonly - ; We need to set the $PATH for eggcacher. SetOutPath $INSTDIR - ReadEnvStr $R0 "PATH" - StrCpy $R0 "$INSTDIR\python;$INSTDIR\bin;$R0" - System::Call 'Kernel32::SetEnvironmentVariableA(t, t) i("PATH", R0).r2' - - nsExec::ExecToLog '"$INSTDIR\bin\eggcacher.exe" --concise models samples' + nsExec::ExecToLog '"$INSTDIR\python\python.exe" -m direct.directscripts.eggcacher --concise models samples' + Pop $0 + DetailPrint "Command returned exit status $0" SetDetailsPrint both DetailPrint "Writing the uninstaller ..." @@ -684,6 +683,10 @@ Section -post DetailPrint "Broadcasting WM_WININICHANGE message..." SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=500 + # Now dump the log to disk. + StrCpy $0 "$INSTDIR\install.log" + Push $0 + Call DumpLog SectionEnd Section Uninstall @@ -1235,3 +1238,47 @@ done: Exch $R3 FunctionEnd + +!define LVM_GETITEMCOUNT 0x1004 +!define LVM_GETITEMTEXT 0x102D + +Function DumpLog + Exch $5 + Push $0 + Push $1 + Push $2 + Push $3 + Push $4 + Push $6 + + FindWindow $0 "#32770" "" $HWNDPARENT + GetDlgItem $0 $0 1016 + StrCmp $0 0 exit + FileOpen $5 $5 "w" + StrCmp $5 "" exit + SendMessage $0 ${LVM_GETITEMCOUNT} 0 0 $6 + System::Alloc ${NSIS_MAX_STRLEN} + Pop $3 + StrCpy $2 0 + System::Call "*(i, i, i, i, i, i, i, i, i) i \ + (0, 0, 0, 0, 0, r3, ${NSIS_MAX_STRLEN}) .r1" + loop: StrCmp $2 $6 done + System::Call "User32::SendMessageA(i, i, i, i) i \ + ($0, ${LVM_GETITEMTEXT}, $2, r1)" + System::Call "*$3(&t${NSIS_MAX_STRLEN} .r4)" + FileWrite $5 "$4$\r$\n" + IntOp $2 $2 + 1 + Goto loop + done: + FileClose $5 + System::Free $1 + System::Free $3 + exit: + Pop $6 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 + Exch $5 +FunctionEnd diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index f4cb93abc1..75261047f9 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -40,6 +40,7 @@ import sys COMPILER=0 INSTALLER=0 WHEEL=0 +RUNTESTS=0 GENMAN=0 COMPRESSOR="zlib" THREADCOUNT=0 @@ -52,6 +53,7 @@ RUNTIME=0 DISTRIBUTOR="" VERSION=None DEBVERSION=None +WHLVERSION=None RPMRELEASE="1" GIT_COMMIT=None P3DSUFFIX=None @@ -76,11 +78,12 @@ PkgListSet(["PYTHON", "DIRECT", # Python support "EGL", # OpenGL (ES) integration "EIGEN", # Linear algebra acceleration "OPENAL", "FMODEX", # Audio playback - "VORBIS", "FFMPEG", "SWSCALE", "SWRESAMPLE", # Audio decoding + "VORBIS", "OPUS", "FFMPEG", "SWSCALE", "SWRESAMPLE", # Audio decoding "ODE", "PHYSX", "BULLET", "PANDAPHYSICS", # Physics "SPEEDTREE", # SpeedTree - "ZLIB", "PNG", "JPEG", "TIFF", "OPENEXR", "SQUISH", "FREETYPE", # 2D Formats support - ] + MAYAVERSIONS + MAXVERSIONS + [ "FCOLLADA", "ASSIMP", # 3D Formats support + "ZLIB", "PNG", "JPEG", "TIFF", "OPENEXR", "SQUISH", # 2D Formats support + ] + MAYAVERSIONS + MAXVERSIONS + [ "FCOLLADA", "ASSIMP", "EGG", # 3D Formats support + "FREETYPE", "HARFBUZZ", # Text rendering "VRPN", "OPENSSL", # Transport "FFTW", # Algorithm helpers "ARTOOLKIT", "OPENCV", "DIRECTCAM", "VISION", # Augmented Reality @@ -89,7 +92,7 @@ PkgListSet(["PYTHON", "DIRECT", # Python support "ROCKET", "AWESOMIUM", # GUI libraries "CARBON", "COCOA", # Mac OS X toolkits "X11", # Unix platform support - "PANDATOOL", "PVIEW", "DEPLOYTOOLS", "DIRECTSCRIPTS",# Toolchain + "PANDATOOL", "PVIEW", "DEPLOYTOOLS", # Toolchain "SKEL", # Example SKEL project "PANDAFX", # Some distortion special lenses "PANDAPARTICLESYSTEM", # Built in particle system @@ -124,6 +127,7 @@ def usage(problem): print(" --help (print the help message you're reading now)") print(" --verbose (print out more information)") print(" --runtime (build a runtime build instead of an SDK build)") + print(" --tests (run the test suite)") print(" --installer (build an installer)") print(" --wheel (build a pip-installable .whl)") print(" --optimize X (optimization level can be 1,2,3,4)") @@ -161,17 +165,17 @@ def usage(problem): os._exit(1) def parseopts(args): - global INSTALLER,WHEEL,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION + global INSTALLER,WHEEL,RUNTESTS,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION global COMPRESSOR,THREADCOUNT,OSXTARGET,OSX_ARCHS,HOST_URL - global DEBVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION + global DEBVERSION,WHLVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION global STRDXSDKVERSION, WINDOWS_SDK, MSVC_VERSION, BOOUSEINTELCOMPILER longopts = [ - "help","distributor=","verbose","runtime","osxtarget=", + "help","distributor=","verbose","runtime","osxtarget=","tests", "optimize=","everything","nothing","installer","wheel","rtdist","nocolor", "version=","lzma","no-python","threads=","outputdir=","override=", "static","host=","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=", "directx-sdk=", "windows-sdk=", "msvc-version=", "clean", "use-icl", - "universal", "target=", "arch=", "git-commit=", + "universal", "target=", "arch=", "git-commit=", "no-directscripts", "use-touchinput", "no-touchinput"] anything = 0 optimize = "" @@ -190,6 +194,7 @@ def parseopts(args): if (option=="--help"): raise Exception elif (option=="--optimize"): optimize=value elif (option=="--installer"): INSTALLER=1 + elif (option=="--tests"): RUNTESTS=1 elif (option=="--wheel"): WHEEL=1 elif (option=="--verbose"): SetVerbose(True) elif (option=="--distributor"): DISTRIBUTOR=value @@ -206,8 +211,11 @@ def parseopts(args): elif (option=="--arch"): target_arch = value.strip() elif (option=="--nocolor"): DisableColors() elif (option=="--version"): - VERSION=value - if (len(VERSION.split(".")) != 3): raise Exception + match = re.match(r'^\d+\.\d+\.\d+', value) + if not match: + usage("version requires three digits") + WHLVERSION = value + VERSION = match.group() elif (option=="--lzma"): COMPRESSOR="lzma" elif (option=="--override"): AddOverride(value.strip()) elif (option=="--static"): SetLinkAllStatic(True) @@ -220,6 +228,7 @@ def parseopts(args): # Backward compatibility, OPENGL was renamed to GL elif (option=="--use-opengl"): PkgEnable("GL") elif (option=="--no-opengl"): PkgDisable("GL") + elif (option=="--no-directscripts"): pass elif (option=="--directx-sdk"): STRDXSDKVERSION = value.strip().lower() if STRDXSDKVERSION == '': @@ -308,12 +317,14 @@ def parseopts(args): if GetTarget() == 'windows': if not MSVC_VERSION: print("No MSVC version specified. Defaulting to 10 (Visual Studio 2010).") - MSVC_VERSION = 10 - - try: - MSVC_VERSION = int(MSVC_VERSION) - except: - usage("Invalid setting for --msvc-version") + MSVC_VERSION = (10, 0) + else: + try: + MSVC_VERSION = tuple(int(d) for d in MSVC_VERSION.split('.'))[:2] + if (len(MSVC_VERSION) == 1): + MSVC_VERSION += (0,) + except: + usage("Invalid setting for --msvc-version") if not WINDOWS_SDK: print("No Windows SDK version specified. Defaulting to '7.1'.") @@ -361,7 +372,11 @@ if VERSION is None: if RUNTIME: VERSION = PLUGIN_VERSION else: - VERSION = ParsePandaVersion("dtool/PandaVersion.pp") + # Take the value from the setup.cfg file. + VERSION = GetMetadataValue('version') + +if WHLVERSION is None: + WHLVERSION = VERSION print("Version: %s" % VERSION) if RUNTIME or RTDIST: @@ -563,6 +578,10 @@ if (COMPILER == "MSVC"): #LibName(pkg, 'ddraw.lib') LibName(pkg, 'dxguid.lib') + if SDK.get("VISUALSTUDIO_VERSION") >= (14,0): + # dxerr needs this for __vsnwprintf definition. + LibName(pkg, 'legacy_stdio_definitions.lib') + if not PkgSkip("FREETYPE") and os.path.isdir(GetThirdpartyDir() + "freetype/include/freetype2"): IncDirectory("FREETYPE", GetThirdpartyDir() + "freetype/include/freetype2") @@ -627,10 +646,12 @@ if (COMPILER == "MSVC"): if (PkgSkip("NVIDIACG")==0): LibName("CGDX9", GetThirdpartyDir() + "nvidiacg/lib/cgD3D9.lib") if (PkgSkip("NVIDIACG")==0): LibName("NVIDIACG", GetThirdpartyDir() + "nvidiacg/lib/cg.lib") if (PkgSkip("FREETYPE")==0): LibName("FREETYPE", GetThirdpartyDir() + "freetype/lib/freetype.lib") + if (PkgSkip("HARFBUZZ")==0): + LibName("HARFBUZZ", GetThirdpartyDir() + "harfbuzz/lib/harfbuzz.lib") + IncDirectory("HARFBUZZ", GetThirdpartyDir() + "harfbuzz/include/harfbuzz") if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/rfftw.lib") if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/fftw.lib") if (PkgSkip("ARTOOLKIT")==0):LibName("ARTOOLKIT",GetThirdpartyDir() + "artoolkit/lib/libAR.lib") - if (PkgSkip("ASSIMP")==0): PkgDisable("ASSIMP") # Not yet supported if (PkgSkip("OPENCV")==0): LibName("OPENCV", GetThirdpartyDir() + "opencv/lib/cv.lib") if (PkgSkip("OPENCV")==0): LibName("OPENCV", GetThirdpartyDir() + "opencv/lib/highgui.lib") if (PkgSkip("OPENCV")==0): LibName("OPENCV", GetThirdpartyDir() + "opencv/lib/cvaux.lib") @@ -645,6 +666,9 @@ if (COMPILER == "MSVC"): if (PkgSkip("FCOLLADA")==0): LibName("FCOLLADA", GetThirdpartyDir() + "fcollada/lib/FCollada.lib") IncDirectory("FCOLLADA", GetThirdpartyDir() + "fcollada/include/FCollada") + if (PkgSkip("ASSIMP")==0): + LibName("ASSIMP", GetThirdpartyDir() + "assimp/lib/assimp.lib") + IncDirectory("ASSIMP", GetThirdpartyDir() + "assimp/include/assimp") if (PkgSkip("SQUISH")==0): if GetOptimize() <= 2: LibName("SQUISH", GetThirdpartyDir() + "squish/lib/squishd.lib") @@ -682,9 +706,15 @@ if (COMPILER == "MSVC"): DefSymbol("WX", "_UNICODE", "") DefSymbol("WX", "UNICODE", "") if (PkgSkip("VORBIS")==0): - LibName("VORBIS", GetThirdpartyDir() + "vorbis/lib/libogg_static.lib") - LibName("VORBIS", GetThirdpartyDir() + "vorbis/lib/libvorbis_static.lib") - LibName("VORBIS", GetThirdpartyDir() + "vorbis/lib/libvorbisfile_static.lib") + for lib in ('ogg', 'vorbis', 'vorbisfile'): + path = GetThirdpartyDir() + "vorbis/lib/lib{0}_static.lib".format(lib) + if not os.path.isfile(path): + path = GetThirdpartyDir() + "vorbis/lib/{0}.lib".format(lib) + LibName("VORBIS", path) + if (PkgSkip("OPUS")==0): + LibName("OPUS", GetThirdpartyDir() + "opus/lib/libogg_static.lib") + LibName("OPUS", GetThirdpartyDir() + "opus/lib/libopus_static.lib") + LibName("OPUS", GetThirdpartyDir() + "opus/lib/libopusfile_static.lib") for pkg in MAYAVERSIONS: if (PkgSkip(pkg)==0): LibName(pkg, '"' + SDK[pkg] + '/lib/Foundation.lib"') @@ -790,6 +820,7 @@ if (COMPILER=="GCC"): SmartPkgEnable("FFTW", "", ("rfftw", "fftw"), ("fftw.h", "rfftw.h")) SmartPkgEnable("FMODEX", "", ("fmodex"), ("fmodex", "fmodex/fmod.h")) SmartPkgEnable("FREETYPE", "freetype2", ("freetype"), ("freetype2", "freetype2/freetype/freetype.h")) + SmartPkgEnable("HARFBUZZ", "harfbuzz", ("harfbuzz"), ("harfbuzz", "harfbuzz/hb-ft.h")) SmartPkgEnable("GL", "gl", ("GL"), ("GL/gl.h"), framework = "OpenGL") SmartPkgEnable("GLES", "glesv1_cm", ("GLESv1_CM"), ("GLES/gl.h"), framework = "OpenGLES") SmartPkgEnable("GLES2", "glesv2", ("GLESv2"), ("GLES2/gl2.h")) #framework = "OpenGLES"? @@ -803,6 +834,7 @@ if (COMPILER=="GCC"): SmartPkgEnable("VRPN", "", ("vrpn", "quat"), ("vrpn", "quat.h", "vrpn/vrpn_Types.h")) SmartPkgEnable("BULLET", "bullet", ("BulletSoftBody", "BulletDynamics", "BulletCollision", "LinearMath"), ("bullet", "bullet/btBulletDynamicsCommon.h")) SmartPkgEnable("VORBIS", "vorbisfile",("vorbisfile", "vorbis", "ogg"), ("ogg/ogg.h", "vorbis/vorbisfile.h")) + SmartPkgEnable("OPUS", "opusfile", ("opusfile", "opus", "ogg"), ("ogg/ogg.h", "opus/opusfile.h")) SmartPkgEnable("JPEG", "", ("jpeg"), "jpeglib.h") SmartPkgEnable("PNG", "libpng", ("png"), "png.h", tool = "libpng-config") @@ -814,13 +846,16 @@ if (COMPILER=="GCC"): # Needed when linking ffmpeg statically on Linux. LibName("FFMPEG", "-Wl,-Bsymbolic") - cv_lib = ChooseLib(("opencv_core", "cv"), "OPENCV") - if cv_lib == "opencv_core": - OPENCV_VER_23 = True - SmartPkgEnable("OPENCV", "opencv", ("opencv_core", "opencv_highgui"), ("opencv2/core/core.hpp")) + if PkgSkip("FFMPEG") or GetTarget() == "darwin": + cv_lib = ChooseLib(("opencv_core", "cv"), "OPENCV") + if cv_lib == "opencv_core": + OPENCV_VER_23 = True + SmartPkgEnable("OPENCV", "opencv", ("opencv_core", "opencv_highgui"), ("opencv2/core/core.hpp")) + else: + SmartPkgEnable("OPENCV", "opencv", ("cv", "highgui", "cvaux", "ml", "cxcore"), + ("opencv", "opencv/cv.h", "opencv/cxcore.h", "opencv/highgui.h")) else: - SmartPkgEnable("OPENCV", "opencv", ("cv", "highgui", "cvaux", "ml", "cxcore"), - ("opencv", "opencv/cv.h", "opencv/cxcore.h", "opencv/highgui.h")) + PkgDisable("OPENCV") rocket_libs = ("RocketCore", "RocketControls") if (GetOptimize() <= 3): @@ -829,7 +864,11 @@ if (COMPILER=="GCC"): SmartPkgEnable("ROCKET", "", rocket_libs, "Rocket/Core.h") if not PkgSkip("PYTHON"): - SmartPkgEnable("PYTHON", "", SDK["PYTHONVERSION"], (SDK["PYTHONVERSION"], SDK["PYTHONVERSION"] + "/Python.h"), tool = SDK["PYTHONVERSION"] + "-config") + python_lib = SDK["PYTHONVERSION"] + if not RTDIST: + # We don't link anything in the SDK with libpython. + python_lib = "" + SmartPkgEnable("PYTHON", "", python_lib, (SDK["PYTHONVERSION"], SDK["PYTHONVERSION"] + "/Python.h")) SmartPkgEnable("OPENSSL", "openssl", ("ssl", "crypto"), ("openssl/ssl.h", "openssl/crypto.h")) SmartPkgEnable("ZLIB", "zlib", ("z"), "zlib.h") @@ -844,7 +883,7 @@ if (COMPILER=="GCC"): if not PkgSkip("NVIDIACG") and not RUNTIME: SmartPkgEnable("CGGL", "", ("CgGL"), "Cg/cgGL.h", thirdparty_dir = "nvidiacg") if not RUNTIME: - SmartPkgEnable("X11", "x11", "X11", ("X11", "X11/Xlib.h")) + SmartPkgEnable("X11", "x11", "X11", ("X11", "X11/Xlib.h", "X11/XKBlib.h")) if GetHost() != "darwin": # Workaround for an issue where pkg-config does not include this path @@ -962,9 +1001,7 @@ if GetTarget() == 'android': DefSymbol("ALWAYS", "ANDROID") if not PkgSkip("EIGEN"): - DefSymbol("ALWAYS", "EIGEN_MPL2_ONLY") if GetOptimize() >= 3: - DefSymbol("ALWAYS", "EIGEN_NO_DEBUG") if COMPILER == "MSVC": # Squeeze out a bit more performance on MSVC builds... # Only do this if EIGEN_NO_DEBUG is also set, otherwise it @@ -1057,7 +1094,7 @@ def CompileCxx(obj,src,opts): # We still target Windows XP. cmd += "/DWINVER=0x501 " # Work around a WinXP/2003 bug when using VS 2015+. - if SDK.get("VISUALSTUDIO_VERSION") == '14.0': + if SDK.get("VISUALSTUDIO_VERSION") >= (14,0): cmd += "/Zc:threadSafeInit- " cmd += "/Fo" + obj + " /nologo /c" @@ -1098,7 +1135,7 @@ def CompileCxx(obj,src,opts): if GetTargetArch() == 'x64': cmd += " /DWIN64_VC /DWIN64" - if WINDOWS_SDK.startswith('7.') and MSVC_VERSION > 10: + if WINDOWS_SDK.startswith('7.') and MSVC_VERSION > (10,): # To preserve Windows XP compatibility. cmd += " /D_USING_V110_SDK71_" @@ -1191,7 +1228,7 @@ def CompileCxx(obj,src,opts): if (COMPILER=="GCC"): if (src.endswith(".c")): cmd = GetCC() +' -fPIC -c -o ' + obj - else: cmd = GetCXX()+' -ftemplate-depth-70 -fPIC -c -o ' + obj + else: cmd = GetCXX()+' -std=gnu++11 -ftemplate-depth-70 -fPIC -c -o ' + obj for (opt, dir) in INCDIRECTORIES: if (opt=="ALWAYS") or (opt in opts): cmd += ' -I' + BracketNameWithQuotes(dir) for (opt, dir) in FRAMEWORKDIRECTORIES: @@ -1281,7 +1318,7 @@ def CompileCxx(obj,src,opts): cmd += " -fno-strict-aliasing" if optlevel >= 3: - cmd += " -ffast-math" + cmd += " -ffast-math -fno-stack-protector" if optlevel == 3: # Fast math is nice, but we'd like to see NaN in dev builds. cmd += " -fno-finite-math-only" @@ -1489,7 +1526,9 @@ def CompileLib(lib, obj, opts): if HasTargetArch(): cmd += " /MACHINE:" + GetTargetArch().upper() cmd += ' /OUT:' + BracketNameWithQuotes(lib) - for x in obj: cmd += ' ' + BracketNameWithQuotes(x) + for x in obj: + if not x.endswith('.lib'): + cmd += ' ' + BracketNameWithQuotes(x) oscmd(cmd) else: # Choose Intel linker; from Jean-Claude @@ -1542,6 +1581,21 @@ def CompileLink(dll, obj, opts): cmd += " /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO " cmd += ' /OUT:' + BracketNameWithQuotes(dll) + if not PkgSkip("PYTHON"): + # If we're building without Python, don't pick it up implicitly. + if "PYTHON" not in opts: + pythonv = SDK["PYTHONVERSION"].replace('.', '') + if optlevel <= 2: + cmd += ' /NOD:{}d.lib'.format(pythonv) + else: + cmd += ' /NOD:{}.lib'.format(pythonv) + + # Yes, we know we are importing "locally defined symbols". + for x in obj: + if x.endswith('libp3pystub.lib'): + cmd += ' /ignore:4049,4217' + break + # Set the subsystem. Specify that we want to target Windows XP. subsystem = GetValueOption(opts, "SUBSYSTEM:") or "CONSOLE" cmd += " /SUBSYSTEM:" + subsystem @@ -2218,13 +2272,13 @@ DTOOL_CONFIG=[ ("HAVE_PNM", '1', '1'), ("HAVE_STB_IMAGE", '1', '1'), ("HAVE_VORBIS", 'UNDEF', 'UNDEF'), + ("HAVE_OPUS", 'UNDEF', 'UNDEF'), ("HAVE_FREETYPE", 'UNDEF', 'UNDEF'), ("HAVE_FFTW", 'UNDEF', 'UNDEF'), ("HAVE_OPENSSL", 'UNDEF', 'UNDEF'), ("HAVE_NET", 'UNDEF', 'UNDEF'), ("WANT_NATIVE_NET", '1', '1'), ("SIMULATE_NETWORK_DELAY", 'UNDEF', 'UNDEF'), - ("HAVE_EGG", '1', '1'), ("HAVE_CG", 'UNDEF', 'UNDEF'), ("HAVE_CGGL", 'UNDEF', 'UNDEF'), ("HAVE_CGDX9", 'UNDEF', 'UNDEF'), @@ -2658,14 +2712,16 @@ for basename in del_files: # Write an appropriate panda3d/__init__.py p3d_init = """"Python bindings for the Panda3D libraries" -""" + +__version__ = '%s' +""" % (WHLVERSION) if GetTarget() == 'windows': p3d_init += """ import os bindir = os.path.join(os.path.dirname(__file__), '..', 'bin') -if os.path.isfile(os.path.join(bindir, 'libpanda.dll')): +if os.path.isdir(bindir): if not os.environ.get('PATH'): os.environ['PATH'] = bindir else: @@ -2700,7 +2756,8 @@ if not PkgSkip("VISION"): panda_modules.append('vision') if not PkgSkip("SKEL"): panda_modules.append('skel') -panda_modules.append('egg') +if not PkgSkip("EGG"): + panda_modules.append('egg') if not PkgSkip("AWESOMIUM"): panda_modules.append('awesomium') if not PkgSkip("ODE"): @@ -2753,12 +2810,12 @@ else: configprc = ReadFile("makepanda/config.in") if (GetTarget() == 'windows'): - configprc = configprc.replace("$HOME/.panda3d", "$USER_APPDATA/Panda3D-%s" % MAJOR_VERSION) + configprc = configprc.replace("$XDG_CACHE_HOME/panda3d", "$USER_APPDATA/Panda3D-%s" % MAJOR_VERSION) else: configprc = configprc.replace("aux-display pandadx9", "") if (GetTarget() == 'darwin'): - configprc = configprc.replace(".panda3d/cache", "Library/Caches/Panda3D-%s" % MAJOR_VERSION) + configprc = configprc.replace("$XDG_CACHE_HOME/panda3d", "Library/Caches/Panda3D-%s" % MAJOR_VERSION) # OpenAL is not yet working well on OSX for us, so let's do this for now. configprc = configprc.replace("p3openal_audio", "p3fmod_audio") @@ -2779,17 +2836,17 @@ else: tp_dir = GetThirdpartyDir() if tp_dir is not None: - dylibs = set() + dylibs = {} if GetTarget() == 'darwin': # Make a list of all the dylibs we ship, to figure out whether we should use # install_name_tool to correct the library reference to point to our copy. for lib in glob.glob(tp_dir + "/*/lib/*.dylib"): - dylibs.add(os.path.basename(lib)) + dylibs[os.path.basename(lib)] = os.path.basename(os.path.realpath(lib)) if not PkgSkip("PYTHON"): for lib in glob.glob(tp_dir + "/*/lib/" + SDK["PYTHONVERSION"] + "/*.dylib"): - dylibs.add(os.path.basename(lib)) + dylibs[os.path.basename(lib)] = os.path.basename(os.path.realpath(lib)) for pkg in PkgListGet(): if PkgSkip(pkg): @@ -2844,7 +2901,8 @@ if tp_dir is not None: libdep = line.split(" ", 1)[0] dep_basename = os.path.basename(libdep) if dep_basename in dylibs: - oscmd("install_name_tool -change %s %s%s %s" % (libdep, dep_prefix, dep_basename, target), True) + dep_target = dylibs[dep_basename] + oscmd("install_name_tool -change %s %s%s %s" % (libdep, dep_prefix, dep_target, target), True) JustBuilt([target], [tp_lib]) @@ -2865,17 +2923,20 @@ if tp_dir is not None: CopyFile(GetOutputDir() + "/" + base, tp_lib) if GetTarget() == 'windows': - CopyAllFiles(GetOutputDir() + "/bin/", tp_dir + "extras/bin/") + if os.path.isdir(os.path.join(tp_dir, "extras", "bin")): + CopyAllFiles(GetOutputDir() + "/bin/", tp_dir + "extras/bin/") if not PkgSkip("PYTHON") and not RTDIST: - #XXX rdb I don't think we need to copy over the Python DLL, do we? - #pydll = "/" + SDK["PYTHONVERSION"].replace(".", "") - #if (GetOptimize() <= 2): pydll += "_d.dll" - #else: pydll += ".dll" - #CopyFile(GetOutputDir() + "/bin" + pydll, SDK["PYTHON"] + pydll) + # We need to copy the Python DLL to the bin directory for now. + pydll = "/" + SDK["PYTHONVERSION"].replace(".", "") + if GetOptimize() <= 2: + pydll += "_d.dll" + else: + pydll += ".dll" + CopyFile(GetOutputDir() + "/bin" + pydll, SDK["PYTHON"] + pydll) - #for fn in glob.glob(SDK["PYTHON"] + "/vcruntime*.dll"): - # CopyFile(GetOutputDir() + "/bin/", fn) + for fn in glob.glob(SDK["PYTHON"] + "/vcruntime*.dll"): + CopyFile(GetOutputDir() + "/bin/", fn) # Copy the whole Python directory. CopyTree(GetOutputDir() + "/python", SDK["PYTHON"]) @@ -2894,7 +2955,7 @@ if tp_dir is not None: else: idents = () - for ident in tree.findall('./{urn:schemas-microsoft-com:asm.v1}dependency/{urn:schemas-microsoft-com:asm.v1}dependentAssembly/{urn:schemas-microsoft-com:asm.v1}assemblyIdentity'): + for ident in idents: sxs_name = '_'.join([ ident.get('processorArchitecture'), ident.get('name').lower(), @@ -2925,9 +2986,13 @@ if tp_dir is not None: # Copy over the MSVC runtime. if GetTarget() == 'windows' and "VISUALSTUDIO" in SDK: - vcver = SDK["VISUALSTUDIO_VERSION"].replace('.', '') - crtname = "Microsoft.VC%s.CRT" % (vcver) - dir = os.path.join(SDK["VISUALSTUDIO"], "VC", "redist", GetTargetArch(), crtname) + vsver = "%s%s" % SDK["VISUALSTUDIO_VERSION"] + vcver = "%s%s" % (SDK["MSVC_VERSION"][0], 0) # ignore minor version. + crtname = "Microsoft.VC%s.CRT" % (vsver) + if ("VCTOOLSVERSION" in SDK): + dir = os.path.join(SDK["VISUALSTUDIO"], "VC", "Redist", "MSVC", SDK["VCTOOLSVERSION"], "onecore", GetTargetArch(), crtname) + else: + dir = os.path.join(SDK["VISUALSTUDIO"], "VC", "redist", GetTargetArch(), crtname) if os.path.isfile(os.path.join(dir, "msvcr" + vcver + ".dll")): CopyFile(GetOutputDir() + "/bin/", os.path.join(dir, "msvcr" + vcver + ".dll")) @@ -2946,6 +3011,7 @@ if GetTarget() == 'windows': # Convert to Windows newlines so they can be opened by notepad. WriteFile(GetOutputDir() + "/LICENSE", ReadFile("doc/LICENSE"), newline='\r\n') WriteFile(GetOutputDir() + "/ReleaseNotes", ReadFile("doc/ReleaseNotes"), newline='\r\n') + CopyFile(GetOutputDir() + "/pandaIcon.ico", "panda/src/configfiles/pandaIcon.ico") else: CopyFile(GetOutputDir()+"/", "doc/LICENSE") CopyFile(GetOutputDir()+"/", "doc/ReleaseNotes") @@ -3051,15 +3117,17 @@ CopyAllHeaders('panda/src/downloadertools') CopyAllHeaders('panda/src/windisplay') CopyAllHeaders('panda/src/dxgsg9') CopyAllHeaders('panda/metalibs/pandadx9') -CopyAllHeaders('panda/src/egg') -CopyAllHeaders('panda/src/egg2pg') +if not PkgSkip("EGG"): + CopyAllHeaders('panda/src/egg') + CopyAllHeaders('panda/src/egg2pg') CopyAllHeaders('panda/src/framework') CopyAllHeaders('panda/metalibs/pandafx') CopyAllHeaders('panda/src/glstuff') CopyAllHeaders('panda/src/glgsg') CopyAllHeaders('panda/src/glesgsg') CopyAllHeaders('panda/src/gles2gsg') -CopyAllHeaders('panda/metalibs/pandaegg') +if not PkgSkip("EGG"): + CopyAllHeaders('panda/metalibs/pandaegg') if GetTarget() == 'windows': CopyAllHeaders('panda/src/wgldisplay') elif GetTarget() == 'darwin': @@ -3097,7 +3165,6 @@ if (PkgSkip("DIRECT")==0): CopyAllHeaders('direct/src/distributed') CopyAllHeaders('direct/src/interval') CopyAllHeaders('direct/src/showbase') - CopyAllHeaders('direct/metalibs/direct') CopyAllHeaders('direct/src/dcparse') if (RUNTIME or RTDIST): @@ -3382,6 +3449,59 @@ if (not RTDIST and not RUNTIME): TargetAdd('test_interrogate.exe', input='libp3pystub.lib') TargetAdd('test_interrogate.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) +# +# DIRECTORY: dtool/src/dtoolbase/ +# + +OPTS=['DIR:dtool/src/dtoolbase', 'PYTHON'] +IGATEFILES=GetDirectoryContents('dtool/src/dtoolbase', ["*_composite*.cxx"]) +IGATEFILES += [ + "typeHandle.h", + "typeHandle_ext.h", + "typeRegistry.h", + "typedObject.h", + "neverFreeMemory.h", +] +TargetAdd('libp3dtoolbase.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3dtoolbase.in', opts=['IMOD:panda3d.core', 'ILIB:libp3dtoolbase', 'SRCDIR:dtool/src/dtoolbase']) +TargetAdd('libp3dtoolbase_igate.obj', input='libp3dtoolbase.in', opts=["DEPENDENCYONLY"]) +TargetAdd('p3dtoolbase_typeHandle_ext.obj', opts=OPTS, input='typeHandle_ext.cxx') + +# +# DIRECTORY: dtool/src/dtoolutil/ +# + +OPTS=['DIR:dtool/src/dtoolutil', 'PYTHON'] +IGATEFILES=GetDirectoryContents('dtool/src/dtoolutil', ["*_composite*.cxx"]) +IGATEFILES += [ + "config_dtoolutil.h", + "pandaSystem.h", + "dSearchPath.h", + "executionEnvironment.h", + "textEncoder.h", + "filename.h", + "filename_ext.h", + "globPattern.h", + "globPattern_ext.h", + "pandaFileStream.h", + "lineStream.h", +] +TargetAdd('libp3dtoolutil.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3dtoolutil.in', opts=['IMOD:panda3d.core', 'ILIB:libp3dtoolutil', 'SRCDIR:dtool/src/dtoolutil']) +TargetAdd('libp3dtoolutil_igate.obj', input='libp3dtoolutil.in', opts=["DEPENDENCYONLY"]) +TargetAdd('p3dtoolutil_ext_composite.obj', opts=OPTS, input='p3dtoolutil_ext_composite.cxx') + +# +# DIRECTORY: dtool/src/prc/ +# + +OPTS=['DIR:dtool/src/prc', 'PYTHON'] +IGATEFILES=GetDirectoryContents('dtool/src/prc', ["*.h", "*_composite*.cxx"]) +TargetAdd('libp3prc.in', opts=OPTS, input=IGATEFILES) +TargetAdd('libp3prc.in', opts=['IMOD:panda3d.core', 'ILIB:libp3prc', 'SRCDIR:dtool/src/prc']) +TargetAdd('libp3prc_igate.obj', input='libp3prc.in', opts=["DEPENDENCYONLY"]) +TargetAdd('p3prc_ext_composite.obj', opts=OPTS, input='p3prc_ext_composite.cxx') + # # DIRECTORY: panda/src/pandabase/ # @@ -3513,6 +3633,7 @@ if (not RUNTIME): TargetAdd('p3event_composite2.obj', opts=OPTS, input='p3event_composite2.cxx') OPTS=['DIR:panda/src/event', 'PYTHON'] + TargetAdd('p3event_asyncFuture_ext.obj', opts=OPTS, input='asyncFuture_ext.cxx') TargetAdd('p3event_pythonTask.obj', opts=OPTS, input='pythonTask.cxx') IGATEFILES=GetDirectoryContents('panda/src/event', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3event.in', opts=OPTS, input=IGATEFILES) @@ -3784,7 +3905,10 @@ if (PkgSkip("FREETYPE")==0 and not RUNTIME): # if (not RUNTIME): - OPTS=['DIR:panda/src/text', 'BUILDING:PANDA', 'ZLIB', 'FREETYPE'] + if not PkgSkip("HARFBUZZ"): + DefSymbol("HARFBUZZ", "HAVE_HARFBUZZ") + + OPTS=['DIR:panda/src/text', 'BUILDING:PANDA', 'ZLIB', 'FREETYPE', 'HARFBUZZ'] TargetAdd('p3text_composite1.obj', opts=OPTS, input='p3text_composite1.cxx') TargetAdd('p3text_composite2.obj', opts=OPTS, input='p3text_composite2.cxx') @@ -3799,10 +3923,10 @@ if (not RUNTIME): # if (not RUNTIME): - OPTS=['DIR:panda/src/movies', 'BUILDING:PANDA', 'VORBIS'] + OPTS=['DIR:panda/src/movies', 'BUILDING:PANDA', 'VORBIS', 'OPUS'] TargetAdd('p3movies_composite1.obj', opts=OPTS, input='p3movies_composite1.cxx') - OPTS=['DIR:panda/src/movies', 'VORBIS', 'PYTHON'] + OPTS=['DIR:panda/src/movies', 'VORBIS', 'OPUS', 'PYTHON'] IGATEFILES=GetDirectoryContents('panda/src/movies', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3movies.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3movies.in', opts=['IMOD:panda3d.core', 'ILIB:libp3movies', 'SRCDIR:panda/src/movies']) @@ -3934,9 +4058,10 @@ if (not RUNTIME): # if (not RUNTIME): - OPTS=['DIR:panda/metalibs/panda', 'BUILDING:PANDA', 'JPEG', 'PNG', + OPTS=['DIR:panda/metalibs/panda', 'BUILDING:PANDA', 'JPEG', 'PNG', 'HARFBUZZ', 'TIFF', 'OPENEXR', 'ZLIB', 'OPENSSL', 'FREETYPE', 'FFTW', 'ADVAPI', 'WINSOCK2', - 'SQUISH', 'NVIDIACG', 'VORBIS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI', 'SETUPAPI'] + 'SQUISH', 'NVIDIACG', 'VORBIS', 'OPUS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI', + 'SETUPAPI'] TargetAdd('panda_panda.obj', opts=OPTS, input='panda.cxx') @@ -4013,6 +4138,10 @@ if (not RUNTIME): TargetAdd('libpanda.dll', dep='dtool_have_freetype.dat') TargetAdd('libpanda.dll', opts=OPTS) + TargetAdd('core_module.obj', input='libp3dtoolbase.in') + TargetAdd('core_module.obj', input='libp3dtoolutil.in') + TargetAdd('core_module.obj', input='libp3prc.in') + TargetAdd('core_module.obj', input='libp3downloader.in') TargetAdd('core_module.obj', input='libp3express.in') @@ -4052,6 +4181,13 @@ if (not RUNTIME): TargetAdd('core_module.obj', opts=['PYTHON']) TargetAdd('core_module.obj', opts=['IMOD:panda3d.core', 'ILIB:core']) + TargetAdd('core.pyd', input='libp3dtoolbase_igate.obj') + TargetAdd('core.pyd', input='p3dtoolbase_typeHandle_ext.obj') + TargetAdd('core.pyd', input='libp3dtoolutil_igate.obj') + TargetAdd('core.pyd', input='p3dtoolutil_ext_composite.obj') + TargetAdd('core.pyd', input='libp3prc_igate.obj') + TargetAdd('core.pyd', input='p3prc_ext_composite.obj') + TargetAdd('core.pyd', input='libp3downloader_igate.obj') TargetAdd('core.pyd', input='p3downloader_stringStream_ext.obj') TargetAdd('core.pyd', input='p3express_ext_composite.obj') @@ -4092,6 +4228,7 @@ if (not RUNTIME): TargetAdd('core.pyd', input='p3pipeline_pythonThread.obj') TargetAdd('core.pyd', input='p3putil_ext_composite.obj') TargetAdd('core.pyd', input='p3pnmimage_pfmFile_ext.obj') + TargetAdd('core.pyd', input='p3event_asyncFuture_ext.obj') TargetAdd('core.pyd', input='p3event_pythonTask.obj') TargetAdd('core.pyd', input='p3gobj_ext_composite.obj') TargetAdd('core.pyd', input='p3pgraph_ext_composite.obj') @@ -4448,7 +4585,7 @@ if GetTarget() == 'windows' and PkgSkip("DX9")==0 and not RUNTIME: # DIRECTORY: panda/src/egg/ # -if (not RUNTIME): +if not RUNTIME and not PkgSkip("EGG"): OPTS=['DIR:panda/src/egg', 'BUILDING:PANDAEGG', 'ZLIB', 'BISONPREFIX_eggyy', 'FLEXDASHI'] CreateFile(GetOutputDir()+"/include/parser.h") TargetAdd('p3egg_parser.obj', opts=OPTS, input='parser.yxx') @@ -4469,7 +4606,7 @@ if (not RUNTIME): # DIRECTORY: panda/src/egg2pg/ # -if (not RUNTIME): +if not RUNTIME and not PkgSkip("EGG"): OPTS=['DIR:panda/src/egg2pg', 'BUILDING:PANDAEGG'] TargetAdd('p3egg2pg_composite1.obj', opts=OPTS, input='p3egg2pg_composite1.cxx') TargetAdd('p3egg2pg_composite2.obj', opts=OPTS, input='p3egg2pg_composite2.cxx') @@ -4488,11 +4625,13 @@ if (not RUNTIME): deps = [] # Framework wants to link in a renderer when building statically, so tell it what is available. if GetLinkAllStatic(): - deps = ['dtool_have_gl.dat', 'dtool_have_tinydisplay.dat'] + deps = ['dtool_have_gl.dat', 'dtool_have_tinydisplay.dat', 'dtool_have_egg.dat'] if not PkgSkip("GL"): DefSymbol("FRAMEWORK", "HAVE_GL") if not PkgSkip("TINYDISPLAY"): DefSymbol("FRAMEWORK", "HAVE_TINYDISPLAY") + if not PkgSkip("EGG"): + DefSymbol("FRAMEWORK", "HAVE_EGG") OPTS=['DIR:panda/src/framework', 'BUILDING:FRAMEWORK', 'FRAMEWORK'] TargetAdd('p3framework_composite1.obj', opts=OPTS, input='p3framework_composite1.cxx', dep=deps) @@ -4531,7 +4670,7 @@ if (not RUNTIME and PkgSkip("GLES2")==0): # DIRECTORY: panda/metalibs/pandaegg/ # -if (not RUNTIME): +if not RUNTIME and not PkgSkip("EGG"): OPTS=['DIR:panda/metalibs/pandaegg', 'DIR:panda/src/egg', 'BUILDING:PANDAEGG'] TargetAdd('pandaegg_pandaegg.obj', opts=OPTS, input='pandaegg.cxx') @@ -4766,7 +4905,7 @@ if (PkgSkip("BULLET")==0 and not RUNTIME): # if (PkgSkip("PHYSX")==0): - OPTS=['DIR:panda/src/physx', 'BUILDING:PANDAPHYSX', 'PHYSX', 'NOARCH:PPC'] + OPTS=['DIR:panda/src/physx', 'BUILDING:PANDAPHYSX', 'PHYSX', 'NOARCH:PPC', 'PYTHON'] TargetAdd('p3physx_composite.obj', opts=OPTS, input='p3physx_composite.cxx') OPTS=['DIR:panda/src/physx', 'PHYSX', 'NOARCH:PPC', 'PYTHON'] @@ -4786,7 +4925,7 @@ if (PkgSkip("PHYSX")==0): TargetAdd('libpandaphysx.dll', input='pandaphysx_pandaphysx.obj') TargetAdd('libpandaphysx.dll', input='p3physx_composite.obj') TargetAdd('libpandaphysx.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libpandaphysx.dll', opts=['WINUSER', 'PHYSX', 'NOARCH:PPC']) + TargetAdd('libpandaphysx.dll', opts=['WINUSER', 'PHYSX', 'NOARCH:PPC', 'PYTHON']) OPTS=['DIR:panda/metalibs/pandaphysx', 'PHYSX', 'NOARCH:PPC', 'PYTHON'] TargetAdd('physx_module.obj', input='libpandaphysx.in') @@ -4901,7 +5040,8 @@ if (not RTDIST and not RUNTIME and PkgSkip("PVIEW")==0 and GetTarget() != 'andro TargetAdd('pview_pview.obj', opts=OPTS, input='pview.cxx') TargetAdd('pview.exe', input='pview_pview.obj') TargetAdd('pview.exe', input='libp3framework.dll') - TargetAdd('pview.exe', input='libpandaegg.dll') + if not PkgSkip("EGG"): + TargetAdd('pview.exe', input='libpandaegg.dll') TargetAdd('pview.exe', input=COMMON_PANDA_LIBS) TargetAdd('pview.exe', opts=['ADVAPI', 'WINSOCK2', 'WINSHELL']) @@ -4927,7 +5067,8 @@ if (not RUNTIME and GetTarget() == 'android'): TargetAdd('pview.exe', input='android_main.obj') TargetAdd('pview.exe', input='pview_pview.obj') TargetAdd('pview.exe', input='libp3framework.dll') - TargetAdd('pview.exe', input='libpandaegg.dll') + if not PkgSkip("EGG"): + TargetAdd('pview.exe', input='libpandaegg.dll') TargetAdd('pview.exe', input='libp3android.dll') TargetAdd('pview.exe', input=COMMON_PANDA_LIBS) TargetAdd('AndroidManifest.xml', opts=OPTS, input='pview_manifest.xml') @@ -4990,17 +5131,6 @@ if (PkgSkip("DIRECT")==0): OPTS=['DIR:direct/src/directbase', 'PYTHON'] TargetAdd('p3directbase_directbase.obj', opts=OPTS+['BUILDING:DIRECT'], input='directbase.cxx') - if not PkgSkip("PYTHON") and not RTDIST and not RUNTIME and not PkgSkip("DIRECTSCRIPTS"): - DefSymbol("BUILDING:PACKPANDA", "IMPORT_MODULE", "direct.directscripts.packpanda") - TargetAdd('packpanda.obj', opts=OPTS+['BUILDING:PACKPANDA'], input='ppython.cxx') - TargetAdd('packpanda.exe', input='packpanda.obj') - TargetAdd('packpanda.exe', opts=['PYTHON']) - - DefSymbol("BUILDING:EGGCACHER", "IMPORT_MODULE", "direct.directscripts.eggcacher") - TargetAdd('eggcacher.obj', opts=OPTS+['BUILDING:EGGCACHER'], input='ppython.cxx') - TargetAdd('eggcacher.exe', input='eggcacher.obj') - TargetAdd('eggcacher.exe', opts=['PYTHON']) - # # DIRECTORY: direct/src/dcparser/ # @@ -5102,10 +5232,6 @@ if (PkgSkip("DIRECT")==0): # if (PkgSkip("DIRECT")==0): - OPTS=['DIR:direct/metalibs/direct', 'BUILDING:DIRECT'] - TargetAdd('p3direct_direct.obj', opts=OPTS, input='direct.cxx') - - TargetAdd('libp3direct.dll', input='p3direct_direct.obj') TargetAdd('libp3direct.dll', input='p3directbase_directbase.obj') TargetAdd('libp3direct.dll', input='p3showbase_showBase.obj') if GetTarget() == 'darwin': @@ -5117,7 +5243,7 @@ if (PkgSkip("DIRECT")==0): TargetAdd('libp3direct.dll', input=COMMON_PANDA_LIBS) TargetAdd('libp3direct.dll', opts=['ADVAPI', 'OPENSSL', 'WINUSER', 'WINGDI']) - OPTS=['DIR:direct/metalibs/direct', 'PYTHON'] + OPTS=['PYTHON'] TargetAdd('direct_module.obj', input='libp3dcparser.in') TargetAdd('direct_module.obj', input='libp3showbase.in') TargetAdd('direct_module.obj', input='libp3deadrec.in') @@ -5492,7 +5618,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/converter/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/converter'] TargetAdd('p3converter_somethingToEggConverter.obj', opts=OPTS, input='somethingToEggConverter.cxx') TargetAdd('p3converter_eggToSomethingConverter.obj', opts=OPTS, input='eggToSomethingConverter.cxx') @@ -5503,7 +5629,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/progbase/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/progbase', 'ZLIB'] TargetAdd('p3progbase_composite1.obj', opts=OPTS, input='p3progbase_composite1.cxx') TargetAdd('libp3progbase.lib', input='p3progbase_composite1.obj') @@ -5512,7 +5638,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/eggbase/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/eggbase'] TargetAdd('p3eggbase_composite1.obj', opts=OPTS, input='p3eggbase_composite1.cxx') TargetAdd('libp3eggbase.lib', input='p3eggbase_composite1.obj') @@ -5521,31 +5647,31 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/bam/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/bam'] TargetAdd('bam-info_bamInfo.obj', opts=OPTS, input='bamInfo.cxx') TargetAdd('bam-info.exe', input='bam-info_bamInfo.obj') TargetAdd('bam-info.exe', input='libp3progbase.lib') TargetAdd('bam-info.exe', input='libp3pandatoolbase.lib') - TargetAdd('bam-info.exe', input='libpandaegg.dll') TargetAdd('bam-info.exe', input=COMMON_PANDA_LIBS) TargetAdd('bam-info.exe', opts=['ADVAPI', 'FFTW']) - TargetAdd('bam2egg_bamToEgg.obj', opts=OPTS, input='bamToEgg.cxx') - TargetAdd('bam2egg.exe', input='bam2egg_bamToEgg.obj') - TargetAdd('bam2egg.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('bam2egg.exe', opts=['ADVAPI', 'FFTW']) + if not PkgSkip("EGG"): + TargetAdd('bam2egg_bamToEgg.obj', opts=OPTS, input='bamToEgg.cxx') + TargetAdd('bam2egg.exe', input='bam2egg_bamToEgg.obj') + TargetAdd('bam2egg.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('bam2egg.exe', opts=['ADVAPI', 'FFTW']) - TargetAdd('egg2bam_eggToBam.obj', opts=OPTS, input='eggToBam.cxx') - TargetAdd('egg2bam.exe', input='egg2bam_eggToBam.obj') - TargetAdd('egg2bam.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('egg2bam.exe', opts=['ADVAPI', 'FFTW']) + TargetAdd('egg2bam_eggToBam.obj', opts=OPTS, input='eggToBam.cxx') + TargetAdd('egg2bam.exe', input='egg2bam_eggToBam.obj') + TargetAdd('egg2bam.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('egg2bam.exe', opts=['ADVAPI', 'FFTW']) # # DIRECTORY: pandatool/src/cvscopy/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/cvscopy'] TargetAdd('p3cvscopy_composite1.obj', opts=OPTS, input='p3cvscopy_composite1.cxx') TargetAdd('libp3cvscopy.lib', input='p3cvscopy_composite1.obj') @@ -5553,7 +5679,7 @@ if (PkgSkip("PANDATOOL")==0): # # DIRECTORY: pandatool/src/daeegg/ # -if (PkgSkip("PANDATOOL")==0 and PkgSkip("FCOLLADA")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("FCOLLADA") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/daeegg', 'FCOLLADA'] TargetAdd('p3daeegg_composite1.obj', opts=OPTS, input='p3daeegg_composite1.cxx') TargetAdd('libp3daeegg.lib', input='p3daeegg_composite1.obj') @@ -5562,17 +5688,17 @@ if (PkgSkip("PANDATOOL")==0 and PkgSkip("FCOLLADA")==0): # # DIRECTORY: pandatool/src/assimp # -if (PkgSkip("PANDATOOL") == 0 and PkgSkip("ASSIMP")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("ASSIMP"): OPTS=['DIR:pandatool/src/assimp', 'BUILDING:ASSIMP', 'ASSIMP', 'MODULE'] TargetAdd('p3assimp_composite1.obj', opts=OPTS, input='p3assimp_composite1.cxx') TargetAdd('libp3assimp.dll', input='p3assimp_composite1.obj') TargetAdd('libp3assimp.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libp3assimp.dll', opts=OPTS) + TargetAdd('libp3assimp.dll', opts=OPTS+['ZLIB']) # # DIRECTORY: pandatool/src/daeprogs/ # -if (PkgSkip("PANDATOOL")==0 and PkgSkip("FCOLLADA")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("FCOLLADA") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/daeprogs', 'FCOLLADA'] TargetAdd('dae2egg_daeToEgg.obj', opts=OPTS, input='daeToEgg.cxx') TargetAdd('dae2egg.exe', input='dae2egg_daeToEgg.obj') @@ -5584,7 +5710,7 @@ if (PkgSkip("PANDATOOL")==0 and PkgSkip("FCOLLADA")==0): # DIRECTORY: pandatool/src/dxf/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/dxf'] TargetAdd('p3dxf_composite1.obj', opts=OPTS, input='p3dxf_composite1.cxx') TargetAdd('libp3dxf.lib', input='p3dxf_composite1.obj') @@ -5593,7 +5719,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/dxfegg/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/dxfegg'] TargetAdd('p3dxfegg_dxfToEggConverter.obj', opts=OPTS, input='dxfToEggConverter.cxx') TargetAdd('p3dxfegg_dxfToEggLayer.obj', opts=OPTS, input='dxfToEggLayer.cxx') @@ -5604,7 +5730,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/dxfprogs/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/dxfprogs'] TargetAdd('dxf-points_dxfPoints.obj', opts=OPTS, input='dxfPoints.cxx') TargetAdd('dxf-points.exe', input='dxf-points_dxfPoints.obj') @@ -5614,26 +5740,27 @@ if (PkgSkip("PANDATOOL")==0): TargetAdd('dxf-points.exe', input=COMMON_PANDA_LIBS) TargetAdd('dxf-points.exe', opts=['ADVAPI', 'FFTW']) - TargetAdd('dxf2egg_dxfToEgg.obj', opts=OPTS, input='dxfToEgg.cxx') - TargetAdd('dxf2egg.exe', input='dxf2egg_dxfToEgg.obj') - TargetAdd('dxf2egg.exe', input='libp3dxfegg.lib') - TargetAdd('dxf2egg.exe', input='libp3dxf.lib') - TargetAdd('dxf2egg.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('dxf2egg.exe', opts=['ADVAPI', 'FFTW']) + if not PkgSkip("EGG"): + TargetAdd('dxf2egg_dxfToEgg.obj', opts=OPTS, input='dxfToEgg.cxx') + TargetAdd('dxf2egg.exe', input='dxf2egg_dxfToEgg.obj') + TargetAdd('dxf2egg.exe', input='libp3dxfegg.lib') + TargetAdd('dxf2egg.exe', input='libp3dxf.lib') + TargetAdd('dxf2egg.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('dxf2egg.exe', opts=['ADVAPI', 'FFTW']) - TargetAdd('egg2dxf_eggToDXF.obj', opts=OPTS, input='eggToDXF.cxx') - TargetAdd('egg2dxf_eggToDXFLayer.obj', opts=OPTS, input='eggToDXFLayer.cxx') - TargetAdd('egg2dxf.exe', input='egg2dxf_eggToDXF.obj') - TargetAdd('egg2dxf.exe', input='egg2dxf_eggToDXFLayer.obj') - TargetAdd('egg2dxf.exe', input='libp3dxf.lib') - TargetAdd('egg2dxf.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('egg2dxf.exe', opts=['ADVAPI', 'FFTW']) + TargetAdd('egg2dxf_eggToDXF.obj', opts=OPTS, input='eggToDXF.cxx') + TargetAdd('egg2dxf_eggToDXFLayer.obj', opts=OPTS, input='eggToDXFLayer.cxx') + TargetAdd('egg2dxf.exe', input='egg2dxf_eggToDXF.obj') + TargetAdd('egg2dxf.exe', input='egg2dxf_eggToDXFLayer.obj') + TargetAdd('egg2dxf.exe', input='libp3dxf.lib') + TargetAdd('egg2dxf.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('egg2dxf.exe', opts=['ADVAPI', 'FFTW']) # # DIRECTORY: pandatool/src/objegg/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/objegg'] TargetAdd('p3objegg_objToEggConverter.obj', opts=OPTS, input='objToEggConverter.cxx') TargetAdd('p3objegg_eggToObjConverter.obj', opts=OPTS, input='eggToObjConverter.cxx') @@ -5646,7 +5773,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/objprogs/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/objprogs'] TargetAdd('obj2egg_objToEgg.obj', opts=OPTS, input='objToEgg.cxx') TargetAdd('obj2egg.exe', input='obj2egg_objToEgg.obj') @@ -5662,7 +5789,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/palettizer/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/palettizer'] TargetAdd('p3palettizer_composite1.obj', opts=OPTS, input='p3palettizer_composite1.cxx') TargetAdd('libp3palettizer.lib', input='p3palettizer_composite1.obj') @@ -5671,7 +5798,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/egg-mkfont/ # -if (PkgSkip("FREETYPE")==0) and (PkgSkip("PANDATOOL")==0): +if not PkgSkip("FREETYPE") and not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/egg-mkfont', 'DIR:pandatool/src/palettizer', 'FREETYPE'] TargetAdd('egg-mkfont_eggMakeFont.obj', opts=OPTS, input='eggMakeFont.cxx') TargetAdd('egg-mkfont_rangeDescription.obj', opts=OPTS, input='rangeDescription.cxx') @@ -5687,7 +5814,7 @@ if (PkgSkip("FREETYPE")==0) and (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/eggcharbase/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/eggcharbase', 'ZLIB'] TargetAdd('p3eggcharbase_composite1.obj', opts=OPTS, input='p3eggcharbase_composite1.cxx') TargetAdd('libp3eggcharbase.lib', input='p3eggcharbase_composite1.obj') @@ -5696,7 +5823,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/egg-optchar/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/egg-optchar'] TargetAdd('egg-optchar_config_egg_optchar.obj', opts=OPTS, input='config_egg_optchar.cxx') TargetAdd('egg-optchar_eggOptchar.obj', opts=OPTS, input='eggOptchar.cxx') @@ -5714,7 +5841,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/egg-palettize/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/egg-palettize', 'DIR:pandatool/src/palettizer'] TargetAdd('egg-palettize_eggPalettize.obj', opts=OPTS, input='eggPalettize.cxx') TargetAdd('egg-palettize.exe', input='egg-palettize_eggPalettize.obj') @@ -5726,7 +5853,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/egg-qtess/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/egg-qtess'] TargetAdd('egg-qtess_composite1.obj', opts=OPTS, input='egg-qtess_composite1.cxx') TargetAdd('egg-qtess.exe', input='egg-qtess_composite1.obj') @@ -5740,7 +5867,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/eggprogs/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/eggprogs'] TargetAdd('egg-crop_eggCrop.obj', opts=OPTS, input='eggCrop.cxx') TargetAdd('egg-crop.exe', input='egg-crop_eggCrop.obj') @@ -5793,7 +5920,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/flt/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/flt', 'ZLIB'] TargetAdd('p3flt_composite1.obj', opts=OPTS, input='p3flt_composite1.cxx') TargetAdd('libp3flt.lib', input=['p3flt_composite1.obj']) @@ -5802,7 +5929,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/fltegg/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/fltegg'] TargetAdd('p3fltegg_fltToEggConverter.obj', opts=OPTS, input='fltToEggConverter.cxx') TargetAdd('p3fltegg_fltToEggLevelState.obj', opts=OPTS, input='fltToEggLevelState.cxx') @@ -5812,46 +5939,52 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/fltprogs/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/fltprogs', 'DIR:pandatool/src/flt', 'DIR:pandatool/src/cvscopy'] - TargetAdd('egg2flt_eggToFlt.obj', opts=OPTS, input='eggToFlt.cxx') - TargetAdd('egg2flt.exe', input='egg2flt_eggToFlt.obj') - TargetAdd('egg2flt.exe', input='libp3flt.lib') - TargetAdd('egg2flt.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('egg2flt.exe', opts=['ADVAPI']) - TargetAdd('flt-info_fltInfo.obj', opts=OPTS, input='fltInfo.cxx') TargetAdd('flt-info.exe', input='flt-info_fltInfo.obj') TargetAdd('flt-info.exe', input='libp3flt.lib') - TargetAdd('flt-info.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('flt-info.exe', input='libp3progbase.lib') + TargetAdd('flt-info.exe', input='libp3pandatoolbase.lib') + TargetAdd('flt-info.exe', input=COMMON_PANDA_LIBS) TargetAdd('flt-info.exe', opts=['ADVAPI']) TargetAdd('flt-trans_fltTrans.obj', opts=OPTS, input='fltTrans.cxx') TargetAdd('flt-trans.exe', input='flt-trans_fltTrans.obj') TargetAdd('flt-trans.exe', input='libp3flt.lib') - TargetAdd('flt-trans.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('flt-trans.exe', input='libp3progbase.lib') + TargetAdd('flt-trans.exe', input='libp3pandatoolbase.lib') + TargetAdd('flt-trans.exe', input=COMMON_PANDA_LIBS) TargetAdd('flt-trans.exe', opts=['ADVAPI']) - TargetAdd('flt2egg_fltToEgg.obj', opts=OPTS, input='fltToEgg.cxx') - TargetAdd('flt2egg.exe', input='flt2egg_fltToEgg.obj') - TargetAdd('flt2egg.exe', input='libp3flt.lib') - TargetAdd('flt2egg.exe', input='libp3fltegg.lib') - TargetAdd('flt2egg.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('flt2egg.exe', opts=['ADVAPI']) - TargetAdd('fltcopy_fltCopy.obj', opts=OPTS, input='fltCopy.cxx') TargetAdd('fltcopy.exe', input='fltcopy_fltCopy.obj') TargetAdd('fltcopy.exe', input='libp3cvscopy.lib') TargetAdd('fltcopy.exe', input='libp3flt.lib') - TargetAdd('fltcopy.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('fltcopy.exe', input='libp3progbase.lib') + TargetAdd('fltcopy.exe', input='libp3pandatoolbase.lib') + TargetAdd('fltcopy.exe', input=COMMON_PANDA_LIBS) TargetAdd('fltcopy.exe', opts=['ADVAPI']) + if not PkgSkip("EGG"): + TargetAdd('egg2flt_eggToFlt.obj', opts=OPTS, input='eggToFlt.cxx') + TargetAdd('egg2flt.exe', input='egg2flt_eggToFlt.obj') + TargetAdd('egg2flt.exe', input='libp3flt.lib') + TargetAdd('egg2flt.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('egg2flt.exe', opts=['ADVAPI']) + + TargetAdd('flt2egg_fltToEgg.obj', opts=OPTS, input='fltToEgg.cxx') + TargetAdd('flt2egg.exe', input='flt2egg_fltToEgg.obj') + TargetAdd('flt2egg.exe', input='libp3flt.lib') + TargetAdd('flt2egg.exe', input='libp3fltegg.lib') + TargetAdd('flt2egg.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('flt2egg.exe', opts=['ADVAPI']) # # DIRECTORY: pandatool/src/imagebase/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/imagebase'] TargetAdd('p3imagebase_composite1.obj', opts=OPTS, input='p3imagebase_composite1.cxx') TargetAdd('libp3imagebase.lib', input='p3imagebase_composite1.obj') @@ -5860,14 +5993,13 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/imageprogs/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/imageprogs'] TargetAdd('image-info_imageInfo.obj', opts=OPTS, input='imageInfo.cxx') TargetAdd('image-info.exe', input='image-info_imageInfo.obj') TargetAdd('image-info.exe', input='libp3imagebase.lib') TargetAdd('image-info.exe', input='libp3progbase.lib') TargetAdd('image-info.exe', input='libp3pandatoolbase.lib') - TargetAdd('image-info.exe', input='libpandaegg.dll') TargetAdd('image-info.exe', input=COMMON_PANDA_LIBS) TargetAdd('image-info.exe', opts=['ADVAPI']) @@ -5876,7 +6008,6 @@ if (PkgSkip("PANDATOOL")==0): TargetAdd('image-resize.exe', input='libp3imagebase.lib') TargetAdd('image-resize.exe', input='libp3progbase.lib') TargetAdd('image-resize.exe', input='libp3pandatoolbase.lib') - TargetAdd('image-resize.exe', input='libpandaegg.dll') TargetAdd('image-resize.exe', input=COMMON_PANDA_LIBS) TargetAdd('image-resize.exe', opts=['ADVAPI']) @@ -5885,7 +6016,6 @@ if (PkgSkip("PANDATOOL")==0): TargetAdd('image-trans.exe', input='libp3imagebase.lib') TargetAdd('image-trans.exe', input='libp3progbase.lib') TargetAdd('image-trans.exe', input='libp3pandatoolbase.lib') - TargetAdd('image-trans.exe', input='libpandaegg.dll') TargetAdd('image-trans.exe', input=COMMON_PANDA_LIBS) TargetAdd('image-trans.exe', opts=['ADVAPI']) @@ -5893,7 +6023,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/pfmprogs/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/pfmprogs'] TargetAdd('pfm-trans_pfmTrans.obj', opts=OPTS, input='pfmTrans.cxx') TargetAdd('pfm-trans.exe', input='pfm-trans_pfmTrans.obj') @@ -5915,7 +6045,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/lwo/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/lwo'] TargetAdd('p3lwo_composite1.obj', opts=OPTS, input='p3lwo_composite1.cxx') TargetAdd('libp3lwo.lib', input='p3lwo_composite1.obj') @@ -5924,7 +6054,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/lwoegg/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/lwoegg'] TargetAdd('p3lwoegg_composite1.obj', opts=OPTS, input='p3lwoegg_composite1.cxx') TargetAdd('libp3lwoegg.lib', input='p3lwoegg_composite1.obj') @@ -5933,23 +6063,23 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/lwoprogs/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/lwoprogs', 'DIR:pandatool/src/lwo'] TargetAdd('lwo-scan_lwoScan.obj', opts=OPTS, input='lwoScan.cxx') TargetAdd('lwo-scan.exe', input='lwo-scan_lwoScan.obj') TargetAdd('lwo-scan.exe', input='libp3lwo.lib') TargetAdd('lwo-scan.exe', input='libp3progbase.lib') TargetAdd('lwo-scan.exe', input='libp3pandatoolbase.lib') - TargetAdd('lwo-scan.exe', input='libpandaegg.dll') TargetAdd('lwo-scan.exe', input=COMMON_PANDA_LIBS) TargetAdd('lwo-scan.exe', opts=['ADVAPI']) - TargetAdd('lwo2egg_lwoToEgg.obj', opts=OPTS, input='lwoToEgg.cxx') - TargetAdd('lwo2egg.exe', input='lwo2egg_lwoToEgg.obj') - TargetAdd('lwo2egg.exe', input='libp3lwo.lib') - TargetAdd('lwo2egg.exe', input='libp3lwoegg.lib') - TargetAdd('lwo2egg.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('lwo2egg.exe', opts=['ADVAPI']) + if not PkgSkip("EGG"): + TargetAdd('lwo2egg_lwoToEgg.obj', opts=OPTS, input='lwoToEgg.cxx') + TargetAdd('lwo2egg.exe', input='lwo2egg_lwoToEgg.obj') + TargetAdd('lwo2egg.exe', input='libp3lwo.lib') + TargetAdd('lwo2egg.exe', input='libp3lwoegg.lib') + TargetAdd('lwo2egg.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('lwo2egg.exe', opts=['ADVAPI']) # # DIRECTORY: pandatool/src/maya/ @@ -5957,7 +6087,7 @@ if (PkgSkip("PANDATOOL")==0): for VER in MAYAVERSIONS: VNUM=VER[4:] - if (PkgSkip(VER)==0) and (PkgSkip("PANDATOOL")==0): + if not PkgSkip(VER) and not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/maya', VER] TargetAdd('maya'+VNUM+'_composite1.obj', opts=OPTS, input='p3maya_composite1.cxx') TargetAdd('libmaya'+VNUM+'.lib', input='maya'+VNUM+'_composite1.obj') @@ -5968,7 +6098,7 @@ for VER in MAYAVERSIONS: for VER in MAYAVERSIONS: VNUM=VER[4:] - if (PkgSkip(VER)==0) and (PkgSkip("PANDATOOL")==0): + if not PkgSkip(VER) and not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/mayaegg', 'DIR:pandatool/src/maya', VER] TargetAdd('mayaegg'+VNUM+'_loader.obj', opts=OPTS, input='mayaEggLoader.cxx') TargetAdd('mayaegg'+VNUM+'_composite1.obj', opts=OPTS, input='p3mayaegg_composite1.cxx') @@ -5981,7 +6111,7 @@ for VER in MAYAVERSIONS: for VER in MAXVERSIONS: VNUM=VER[3:] - if (PkgSkip(VER)==0) and (PkgSkip("PANDATOOL")==0): + if not PkgSkip(VER) and not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/maxegg', VER, "WINCOMCTL", "WINCOMDLG", "WINUSER", "MSFORSCOPE", "RTTI"] TargetAdd('maxEgg'+VNUM+'.res', opts=OPTS, input='maxEgg.rc') TargetAdd('maxegg'+VNUM+'_loader.obj', opts=OPTS, input='maxEggLoader.cxx') @@ -5998,7 +6128,7 @@ for VER in MAXVERSIONS: for VER in MAXVERSIONS: VNUM=VER[3:] - if (PkgSkip(VER)==0) and (PkgSkip("PANDATOOL")==0): + if not PkgSkip(VER) and not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/maxprogs', VER, "WINCOMCTL", "WINCOMDLG", "WINUSER", "MSFORSCOPE", "RTTI"] TargetAdd('maxImportRes.res', opts=OPTS, input='maxImportRes.rc') TargetAdd('maxprogs'+VNUM+'_maxeggimport.obj', opts=OPTS, input='maxEggImport.cxx') @@ -6016,7 +6146,7 @@ for VER in MAXVERSIONS: # DIRECTORY: pandatool/src/vrml/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/vrml', 'ZLIB', 'BISONPREFIX_vrmlyy'] CreateFile(GetOutputDir()+"/include/vrmlParser.h") TargetAdd('p3vrml_vrmlParser.obj', opts=OPTS, input='vrmlParser.yxx') @@ -6037,7 +6167,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/vrmlegg/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/vrmlegg', 'DIR:pandatool/src/vrml'] TargetAdd('p3vrmlegg_indexedFaceSet.obj', opts=OPTS, input='indexedFaceSet.cxx') TargetAdd('p3vrmlegg_vrmlAppearance.obj', opts=OPTS, input='vrmlAppearance.cxx') @@ -6050,7 +6180,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/xfile/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/xfile', 'ZLIB', 'BISONPREFIX_xyy', 'FLEXDASHI'] CreateFile(GetOutputDir()+"/include/xParser.h") TargetAdd('p3xfile_xParser.obj', opts=OPTS, input='xParser.yxx') @@ -6065,7 +6195,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/xfileegg/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): OPTS=['DIR:pandatool/src/xfileegg', 'DIR:pandatool/src/xfile'] TargetAdd('p3xfileegg_composite1.obj', opts=OPTS, input='p3xfileegg_composite1.cxx') TargetAdd('libp3xfileegg.lib', input='p3xfileegg_composite1.obj') @@ -6074,7 +6204,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/ptloader/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): if not PkgSkip("FCOLLADA"): DefSymbol("FCOLLADA", "HAVE_FCOLLADA") @@ -6140,7 +6270,6 @@ if (PkgSkip("PANDATOOL")==0): TargetAdd('softcvs.exe', input='softcvs_softFilename.obj') TargetAdd('softcvs.exe', input='libp3progbase.lib') TargetAdd('softcvs.exe', input='libp3pandatoolbase.lib') - TargetAdd('softcvs.exe', input='libpandaegg.dll') TargetAdd('softcvs.exe', input=COMMON_PANDA_LIBS) TargetAdd('softcvs.exe', opts=['ADVAPI']) @@ -6157,7 +6286,6 @@ if (PkgSkip("PANDATOOL")==0): TargetAdd('text-stats.exe', input='libp3progbase.lib') TargetAdd('text-stats.exe', input='libp3pstatserver.lib') TargetAdd('text-stats.exe', input='libp3pandatoolbase.lib') - TargetAdd('text-stats.exe', input='libpandaegg.dll') TargetAdd('text-stats.exe', input=COMMON_PANDA_LIBS) TargetAdd('text-stats.exe', opts=['ADVAPI']) @@ -6165,7 +6293,7 @@ if (PkgSkip("PANDATOOL")==0): # DIRECTORY: pandatool/src/vrmlprogs/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/vrmlprogs', 'DIR:pandatool/src/vrml', 'DIR:pandatool/src/vrmlegg'] TargetAdd('vrml-trans_vrmlTrans.obj', opts=OPTS, input='vrmlTrans.cxx') TargetAdd('vrml-trans.exe', input='vrml-trans_vrmlTrans.obj') @@ -6175,12 +6303,13 @@ if (PkgSkip("PANDATOOL")==0): TargetAdd('vrml-trans.exe', input=COMMON_PANDA_LIBS) TargetAdd('vrml-trans.exe', opts=['ADVAPI']) - TargetAdd('vrml2egg_vrmlToEgg.obj', opts=OPTS, input='vrmlToEgg.cxx') - TargetAdd('vrml2egg.exe', input='vrml2egg_vrmlToEgg.obj') - TargetAdd('vrml2egg.exe', input='libp3vrmlegg.lib') - TargetAdd('vrml2egg.exe', input='libp3vrml.lib') - TargetAdd('vrml2egg.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('vrml2egg.exe', opts=['ADVAPI']) + if not PkgSkip("EGG"): + TargetAdd('vrml2egg_vrmlToEgg.obj', opts=OPTS, input='vrmlToEgg.cxx') + TargetAdd('vrml2egg.exe', input='vrml2egg_vrmlToEgg.obj') + TargetAdd('vrml2egg.exe', input='libp3vrmlegg.lib') + TargetAdd('vrml2egg.exe', input='libp3vrml.lib') + TargetAdd('vrml2egg.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('vrml2egg.exe', opts=['ADVAPI']) # # DIRECTORY: pandatool/src/win-stats/ @@ -6205,15 +6334,8 @@ if (PkgSkip("PANDATOOL")==0 and (GetTarget() == 'windows' or PkgSkip("GTK2")==0) # DIRECTORY: pandatool/src/xfileprogs/ # -if (PkgSkip("PANDATOOL")==0): +if not PkgSkip("PANDATOOL"): OPTS=['DIR:pandatool/src/xfileprogs', 'DIR:pandatool/src/xfile', 'DIR:pandatool/src/xfileegg'] - TargetAdd('egg2x_eggToX.obj', opts=OPTS, input='eggToX.cxx') - TargetAdd('egg2x.exe', input='egg2x_eggToX.obj') - TargetAdd('egg2x.exe', input='libp3xfileegg.lib') - TargetAdd('egg2x.exe', input='libp3xfile.lib') - TargetAdd('egg2x.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('egg2x.exe', opts=['ADVAPI']) - TargetAdd('x-trans_xFileTrans.obj', opts=OPTS, input='xFileTrans.cxx') TargetAdd('x-trans.exe', input='x-trans_xFileTrans.obj') TargetAdd('x-trans.exe', input='libp3progbase.lib') @@ -6222,12 +6344,20 @@ if (PkgSkip("PANDATOOL")==0): TargetAdd('x-trans.exe', input=COMMON_PANDA_LIBS) TargetAdd('x-trans.exe', opts=['ADVAPI']) - TargetAdd('x2egg_xFileToEgg.obj', opts=OPTS, input='xFileToEgg.cxx') - TargetAdd('x2egg.exe', input='x2egg_xFileToEgg.obj') - TargetAdd('x2egg.exe', input='libp3xfileegg.lib') - TargetAdd('x2egg.exe', input='libp3xfile.lib') - TargetAdd('x2egg.exe', input=COMMON_EGG2X_LIBS) - TargetAdd('x2egg.exe', opts=['ADVAPI']) + if not PkgSkip("EGG"): + TargetAdd('egg2x_eggToX.obj', opts=OPTS, input='eggToX.cxx') + TargetAdd('egg2x.exe', input='egg2x_eggToX.obj') + TargetAdd('egg2x.exe', input='libp3xfileegg.lib') + TargetAdd('egg2x.exe', input='libp3xfile.lib') + TargetAdd('egg2x.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('egg2x.exe', opts=['ADVAPI']) + + TargetAdd('x2egg_xFileToEgg.obj', opts=OPTS, input='xFileToEgg.cxx') + TargetAdd('x2egg.exe', input='x2egg_xFileToEgg.obj') + TargetAdd('x2egg.exe', input='libp3xfileegg.lib') + TargetAdd('x2egg.exe', input='libp3xfile.lib') + TargetAdd('x2egg.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('x2egg.exe', opts=['ADVAPI']) # # DIRECTORY: pandatool/src/mayaprogs/ @@ -6235,7 +6365,7 @@ if (PkgSkip("PANDATOOL")==0): for VER in MAYAVERSIONS: VNUM = VER[4:] - if not PkgSkip(VER) and not PkgSkip("PANDATOOL"): + if not PkgSkip(VER) and not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): if GetTarget() == 'darwin' and int(VNUM) >= 2012: ARCH_OPTS = ['NOARCH:PPC', 'NOARCH:I386'] if len(OSX_ARCHS) != 0 and 'x86_64' not in OSX_ARCHS: @@ -6371,8 +6501,9 @@ if (PkgSkip("CONTRIB")==0 and not RUNTIME): # Generate the models directory and samples directory # -if (PkgSkip("DIRECT")==0 and not RUNTIME): +if not PkgSkip("DIRECT") and not RUNTIME and not PkgSkip("EGG"): model_extensions = ["*.egg"] + # Check if we have access to an flt2egg utility, either self-compiled or on the system. if ((PkgSkip("PANDATOOL")==0 and GetHost()==GetTarget()) or LocateBinary('flt2egg')): model_extensions.append("*.flt") @@ -6398,6 +6529,7 @@ if (PkgSkip("DIRECT")==0 and not RUNTIME): newname = model[:-4] + ".egg" TargetAdd(GetOutputDir()+"/models/"+newname, input="models/"+model) +if not PkgSkip("DIRECT") and not RUNTIME: CopyAllFiles(GetOutputDir()+"/models/audio/sfx/", "dmodels/src/audio/sfx/", ".wav") CopyAllFiles(GetOutputDir()+"/models/icons/", "dmodels/src/icons/", ".gif") @@ -6567,6 +6699,16 @@ except: SaveDependencyCache() raise +# Run the test suite. +if RUNTESTS: + cmdstr = BracketNameWithQuotes(SDK["PYTHONEXEC"].replace('\\', '/')) + if sys.version_info >= (2, 6): + cmdstr += " -B" + cmdstr += " -m pytest tests" + if GetVerbose(): + cmdstr += " --verbose" + oscmd(cmdstr) + ########################################################################################## # # The Installers @@ -6593,7 +6735,7 @@ def MakeInstallerNSIS(file, title, installdir): AddToPathEnv("PATH", GetOutputDir() + "\\bin") AddToPathEnv("PATH", GetOutputDir() + "\\plugins") - cmd = sys.executable + " -B -u direct\\src\\plugin_installer\\make_installer.py" + cmd = sys.executable + " -B -u " + os.path.join("direct", "src", "plugin_installer", "make_installer.py") cmd += " --version %s --regview %s" % (VERSION, regview) if GetTargetArch() == 'x64': @@ -6602,7 +6744,7 @@ def MakeInstallerNSIS(file, title, installdir): cmd += " --install \"$PROGRAMFILES32\\Panda3D\" " oscmd(cmd) - shutil.move("direct\\src\\plugin_installer\\p3d-setup.exe", file) + shutil.move(os.path.join("direct", "src", "plugin_installer", "p3d-setup.exe"), file) return print("Building "+title+" installer at %s" % (file)) @@ -6782,7 +6924,7 @@ Panda3D's intended game-development language is Python. The engine itself is wri This package contains the SDK for development with Panda3D, install panda3d-runtime for the runtime files. -WWW: http://www.panda3d.org/ +WWW: https://www.panda3d.org/ """ # FreeBSD pkg-descr @@ -6791,7 +6933,7 @@ Runtime binary and browser plugin for the Panda3D Game Engine This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file. -WWW: http://www.panda3d.org/ +WWW: https://www.panda3d.org/ """ # FreeBSD PKG Manifest template file @@ -6800,8 +6942,8 @@ name: NAME version: VERSION arch: ARCH origin: ORIGIN -comment: "Panda 3D Engine" -www: http://www.panda3d.org +comment: "Panda3D free 3D engine SDK" +www: https://www.panda3d.org maintainer: rdb prefix: /usr/local flatsize: INSTSIZEMB @@ -6810,7 +6952,7 @@ deps: {DEPENDS} def MakeInstallerLinux(): if not RUNTIME and not PkgSkip("PYTHON"): - PYTHONV = SDK["PYTHONVERSION"] + PYTHONV = SDK["PYTHONVERSION"].rstrip('dmu') else: PYTHONV = "python" PV = PYTHONV.replace("python", "") @@ -6965,8 +7107,13 @@ def MakeInstallerOSX(): oscmd(cmdstr) return + dmg_name = "Panda3D-" + VERSION + if not SDK["PYTHONVERSION"].startswith("python2."): + dmg_name += '-py' + SDK["PYTHONVERSION"][6:9] + dmg_name += ".dmg" + import compileall - if (os.path.isfile("Panda3D-%s.dmg" % VERSION)): oscmd("rm -f Panda3D-%s.dmg" % VERSION) + if (os.path.isfile(dmg_name)): oscmd("rm -f %s" % dmg_name) if (os.path.exists("dstroot")): oscmd("rm -rf dstroot") if (os.path.exists("Panda3D-rw.dmg")): oscmd('rm -f Panda3D-rw.dmg') @@ -7007,7 +7154,7 @@ def MakeInstallerOSX(): oscmd("cp -R " + GetOutputDir() + "/bin/" + base + " " + binname) if PkgSkip("PYTHON")==0: - PV = SDK["PYTHONVERSION"].replace("python", "") + PV = SDK["PYTHONVERSION"][6:9] oscmd("mkdir -p dstroot/pythoncode/usr/local/bin") oscmd("mkdir -p dstroot/pythoncode/Developer/Panda3D/panda3d") oscmd("mkdir -p dstroot/pythoncode/Library/Python/%s/site-packages" % PV) @@ -7130,10 +7277,14 @@ def MakeInstallerOSX(): if not PkgSkip("FFMPEG"): dist.write(' \n') - else: + elif PkgSkip("VORBIS"): + dist.write(' It is not required for loading .wav or .opus files, which Panda3D can read out of the box.">\n') + elif PkgSkip("OPUS"): dist.write(' It is not required for loading .wav or .ogg files, which Panda3D can read out of the box.">\n') + else: + dist.write(' It is not required for loading .wav, .ogg or .opus files, which Panda3D can read out of the box.">\n') dist.write(' \n') dist.write(' \n') @@ -7162,7 +7313,7 @@ def MakeInstallerOSX(): dist.close() oscmd('hdiutil create Panda3D-rw.dmg -volname "Panda3D SDK %s" -srcfolder dstroot/Panda3D' % (VERSION)) - oscmd('hdiutil convert Panda3D-rw.dmg -format UDBZ -o Panda3D-%s.dmg' % (VERSION)) + oscmd('hdiutil convert Panda3D-rw.dmg -format UDBZ -o %s' % (dmg_name)) oscmd('rm -f Panda3D-rw.dmg') def MakeInstallerFreeBSD(): @@ -7184,8 +7335,8 @@ def MakeInstallerFreeBSD(): plist_txt += os.path.join(root, f)[21:] + "\n" if not RUNTIME: - plist_txt += "@exec /sbin/ldconfig -m /usr/local/lib\n" - plist_txt += "@unexec /sbin/ldconfig -R\n" + plist_txt += "@postexec /sbin/ldconfig -m /usr/local/lib/panda3d\n" + plist_txt += "@postunexec /sbin/ldconfig -R\n" for remdir in ("lib/panda3d", "share/panda3d", "include/panda3d"): for root, dirs, files in os.walk("targetroot/usr/local/" + remdir, False): @@ -7206,7 +7357,7 @@ def MakeInstallerFreeBSD(): if python_pkg: dependencies += python_pkg - manifest_txt = INSTALLER_PKG_MANIFEST_FILE[1:].replace("NAME", 'Panda3D' if not RUNTIME else 'Panda3D-Runtime') + manifest_txt = INSTALLER_PKG_MANIFEST_FILE[1:].replace("NAME", 'panda3d' if not RUNTIME else 'panda3d-runtime') manifest_txt = manifest_txt.replace("VERSION", VERSION) manifest_txt = manifest_txt.replace("ARCH", pkg_arch) manifest_txt = manifest_txt.replace("ORIGIN", 'devel/panda3d' if not RUNTIME else 'graphics/panda3d-runtime') @@ -7258,7 +7409,7 @@ try: if WHEEL: ProgressOutput(100.0, "Building wheel") from makewheel import makewheel - makewheel(VERSION, GetOutputDir()) + makewheel(WHLVERSION, GetOutputDir()) finally: SaveDependencyCache() diff --git a/makepanda/makepanda.vcproj b/makepanda/makepanda.vcproj index f533c17448..a787653e1e 100755 --- a/makepanda/makepanda.vcproj +++ b/makepanda/makepanda.vcproj @@ -2371,12 +2371,9 @@ - - - diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 2eb4342b06..b1666aee39 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -12,9 +12,11 @@ from distutils import sysconfig if sys.version_info >= (3, 0): import pickle import _thread as thread + import configparser else: import cPickle as pickle import thread + import ConfigParser as configparser SUFFIX_INC = [".cxx",".cpp",".c",".h",".I",".yxx",".lxx",".mm",".rc",".r"] SUFFIX_DLL = [".dll",".dlo",".dle",".dli",".dlm",".mll",".exe",".pyd",".ocx"] @@ -55,6 +57,20 @@ else: # case. host_64 = (platform.architecture()[0] == '64bit') +######################################################################## +## +## Visual C++ Version (MSVC) and Visual Studio Information Map +## +######################################################################## + +MSVCVERSIONINFO = { + (10,0): {"vsversion":(10,0), "vsname":"Visual Studio 2010"}, + (11,0): {"vsversion":(11,0), "vsname":"Visual Studio 2012"}, + (12,0): {"vsversion":(12,0), "vsname":"Visual Studio 2013"}, + (14,0): {"vsversion":(14,0), "vsname":"Visual Studio 2015"}, + (14,1): {"vsversion":(15,0), "vsname":"Visual Studio 2017"}, +} + ######################################################################## ## ## Maya and Max Version List (with registry keys) @@ -76,7 +92,8 @@ MAYAVERSIONINFO = [("MAYA6", "6.0"), ("MAYA2014","2014"), ("MAYA2015","2015"), ("MAYA2016","2016"), - ("MAYA20165","2016.5") + ("MAYA20165","2016.5"), + ("MAYA2017","2017") ] MAXVERSIONINFO = [("MAX6", "SOFTWARE\\Autodesk\\3DSMAX\\6.0", "installdir", "maxsdk\\cssdk\\include"), @@ -396,13 +413,13 @@ def CrossCompiling(): return GetTarget() != GetHost() def GetCC(): - if TARGET == 'darwin': + if TARGET == 'darwin' or TARGET == 'freebsd': return os.environ.get('CC', TOOLCHAIN_PREFIX + 'clang') else: return os.environ.get('CC', TOOLCHAIN_PREFIX + 'gcc') def GetCXX(): - if TARGET == 'darwin': + if TARGET == 'darwin' or TARGET == 'freebsd': return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'clang++') else: return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'g++') @@ -918,6 +935,17 @@ def GetProgramFiles(): return "E:\\Program Files" return 0 +def GetProgramFiles_x86(): + if ("ProgramFiles(x86)" in os.environ): + return os.environ["ProgramFiles(x86)"] + elif (os.path.isdir("C:\\Program Files (x86)")): + return "C:\\Program Files (x86)" + elif (os.path.isdir("D:\\Program Files (x86)")): + return "D:\\Program Files (x86)" + elif (os.path.isdir("E:\\Program Files (x86)")): + return "E:\\Program Files (x86)" + return GetProgramFiles() + ######################################################################## ## ## Parsing Compiler Option Lists @@ -980,7 +1008,10 @@ def WriteFile(wfile, data, newline=None): data = data.replace('\n', newline) try: - dsthandle = open(wfile, "w") + if sys.version_info >= (3, 0): + dsthandle = open(wfile, "w", newline='') + else: + dsthandle = open(wfile, "w") dsthandle.write(data) dsthandle.close() except: @@ -1141,7 +1172,7 @@ def GetThirdpartyDir(): target_arch = GetTargetArch() if (target == 'windows'): - vc = SDK["VISUALSTUDIO_VERSION"].split('.')[0] + vc = str(SDK["MSVC_VERSION"][0]) if target_arch == 'x64': THIRDPARTYDIR = base + "/win-libs-vc" + vc + "-x64/" @@ -1574,7 +1605,7 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None, location = os.path.join(GetOutputDir(), "lib", os.path.basename(location)) LibName(target_pkg, location) else: - print(GetColor("cyan") + "Couldn't find library lib" + libname + " in thirdparty directory " + pkg.lower() + GetColor()) + print(GetColor("cyan") + "Couldn't find library lib" + libname + " in thirdparty directory " + thirdparty_dir + GetColor()) for d, v in defs.values(): DefSymbol(target_pkg, d, v) @@ -1925,6 +1956,8 @@ def SdkLocatePython(prefer_thirdparty_python=False): SDK["PYTHONEXEC"] = os.path.realpath(sys.executable) return + abiflags = getattr(sys, 'abiflags', '') + if GetTarget() == 'windows': sdkdir = GetThirdpartyBase() + "/win-python" @@ -1989,26 +2022,28 @@ def SdkLocatePython(prefer_thirdparty_python=False): SDK["PYTHON"] = tp_python + "/include/" + SDK["PYTHONVERSION"] elif GetTarget() == 'darwin': - # On Mac OS X, use the system Python framework. - py_fwx = SDK.get("MACOSX", "") + "/System/Library/Frameworks/Python.framework/Versions/Current" + # On macOS, search for the Python framework directory matching the + # version number of our current Python version. + sysroot = SDK.get("MACOSX", "") + version = sysconfig.get_python_version() - if not os.path.islink(py_fwx): - exit("Could not locate Python installation at %s" % (py_fwx)) + py_fwx = "{0}/System/Library/Frameworks/Python.framework/Versions/{1}".format(sysroot, version) - ver = os.path.basename(os.readlink(py_fwx)) - py_fwx = SDK.get("MACOSX", "") + "/System/Library/Frameworks/Python.framework/Versions/%s" % ver + if not os.path.exists(py_fwx): + # Fall back to looking on the system. + py_fwx = "/Library/Frameworks/Python.framework/Versions/" + version - SDK["PYTHON"] = py_fwx + "/Headers" - SDK["PYTHONVERSION"] = "python" + ver - SDK["PYTHONEXEC"] = "/System/Library/Frameworks/Python.framework/Versions/" + ver + "/bin/python" + ver + if not os.path.exists(py_fwx): + exit("Could not locate Python installation at %s" % (py_fwx)) - # Avoid choosing the one in the thirdparty package dir. - PkgSetCustomLocation("PYTHON") - IncDirectory("PYTHON", py_fwx + "/include") - LibDirectory("PYTHON", "%s/usr/lib" % (SDK.get("MACOSX", ""))) + SDK["PYTHON"] = py_fwx + "/Headers" + SDK["PYTHONVERSION"] = "python" + version + abiflags + SDK["PYTHONEXEC"] = py_fwx + "/bin/python" + version - if sys.version[:3] != ver: - print("Warning: building with Python %s instead of %s since you targeted a specific Mac OS X version." % (ver, sys.version[:3])) + # Avoid choosing the one in the thirdparty package dir. + PkgSetCustomLocation("PYTHON") + IncDirectory("PYTHON", py_fwx + "/include") + LibDirectory("PYTHON", py_fwx + "/lib") #elif GetTarget() == 'windows': # SDK["PYTHON"] = os.path.dirname(sysconfig.get_python_inc()) @@ -2017,13 +2052,13 @@ def SdkLocatePython(prefer_thirdparty_python=False): else: SDK["PYTHON"] = sysconfig.get_python_inc() - SDK["PYTHONVERSION"] = "python" + sysconfig.get_python_version() + SDK["PYTHONVERSION"] = "python" + sysconfig.get_python_version() + abiflags SDK["PYTHONEXEC"] = os.path.realpath(sys.executable) if CrossCompiling(): # We need a version of Python we can run. SDK["PYTHONEXEC"] = sys.executable - host_version = "python" + sysconfig.get_python_version() + host_version = "python" + sysconfig.get_python_version() + abiflags if SDK["PYTHONVERSION"] != host_version: exit("Host Python version (%s) must be the same as target Python version (%s)!" % (host_version, SDK["PYTHONVERSION"])) @@ -2032,21 +2067,64 @@ def SdkLocatePython(prefer_thirdparty_python=False): else: print("Using Python %s" % (SDK["PYTHONVERSION"][6:9])) -def SdkLocateVisualStudio(version=10): +def SdkLocateVisualStudio(version=(10,0)): if (GetHost() != "windows"): return - version = str(version) + '.0' + try: + msvcinfo = MSVCVERSIONINFO[version] + except: + exit("Couldn't get Visual Studio infomation with MSVC %s.%s version." % version) - vcdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7", version) - if (vcdir != 0) and (vcdir[-4:] == "\\VC\\"): + vsversion = msvcinfo["vsversion"] + vsversion_str = "%s.%s" % vsversion + version_str = "%s.%s" % version + + # try to use vswhere.exe + vswhere_path = LocateBinary("vswhere.exe") + if not vswhere_path: + if sys.platform == 'cygwin': + vswhere_path = "/cygdrive/c/Program Files/Microsoft Visual Studio/Installer/vswhere.exe" + else: + vswhere_path = "%s\\Microsoft Visual Studio\\Installer\\vswhere.exe" % GetProgramFiles() + if not os.path.isfile(vswhere_path): + vswhere_path = None + + if not vswhere_path: + if sys.platform == 'cygwin': + vswhere_path = "/cygdrive/c/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe" + else: + vswhere_path = "%s\\Microsoft Visual Studio\\Installer\\vswhere.exe" % GetProgramFiles_x86() + if not os.path.isfile(vswhere_path): + vswhere_path = None + + vsdir = 0 + if vswhere_path: + min_vsversion = vsversion_str + max_vsversion = "%s.%s" % (vsversion[0]+1, 0) + vswhere_cmd = ["vswhere.exe", "-legacy", "-property", "installationPath", + "-version", "[{},{})".format(min_vsversion, max_vsversion)] + handle = subprocess.Popen(vswhere_cmd, executable=vswhere_path, stdout=subprocess.PIPE) + found_paths = handle.communicate()[0].splitlines() + if found_paths: + vsdir = found_paths[0].decode("utf-8") + "\\" + + # try to use registry + if (vsdir == 0): + vsdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", vsversion_str) + vcdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7", version_str) + + if (vsdir != 0): + SDK["VISUALSTUDIO"] = vsdir + + elif (vcdir != 0) and (vcdir[-4:] == "\\VC\\"): vcdir = vcdir[:-3] SDK["VISUALSTUDIO"] = vcdir - elif (os.path.isfile("C:\\Program Files\\Microsoft Visual Studio %s\\VC\\bin\\cl.exe" % (version))): - SDK["VISUALSTUDIO"] = "C:\\Program Files\\Microsoft Visual Studio %s\\" % (version) + elif (os.path.isfile("C:\\Program Files\\Microsoft Visual Studio %s\\VC\\bin\\cl.exe" % (vsversion_str))): + SDK["VISUALSTUDIO"] = "C:\\Program Files\\Microsoft Visual Studio %s\\" % (vsversion_str) - elif (os.path.isfile("C:\\Program Files (x86)\\Microsoft Visual Studio %s\\VC\\bin\\cl.exe" % (version))): - SDK["VISUALSTUDIO"] = "C:\\Program Files (x86)\\Microsoft Visual Studio %s\\" % (version) + elif (os.path.isfile("C:\\Program Files (x86)\\Microsoft Visual Studio %s\\VC\\bin\\cl.exe" % (vsversion_str))): + SDK["VISUALSTUDIO"] = "C:\\Program Files (x86)\\Microsoft Visual Studio %s\\" % (vsversion_str) elif "VCINSTALLDIR" in os.environ: vcdir = os.environ["VCINSTALLDIR"] @@ -2058,14 +2136,17 @@ def SdkLocateVisualStudio(version=10): SDK["VISUALSTUDIO"] = vcdir else: - exit("Couldn't find Visual Studio %s. To use a different version, use the --msvc-version option." % version) + exit("Couldn't find %s. To use a different version, use the --msvc-version option." % msvcinfo["vsname"]) - SDK["VISUALSTUDIO_VERSION"] = version + SDK["MSVC_VERSION"] = version + SDK["VISUALSTUDIO_VERSION"] = vsversion if GetVerbose(): - print("Using Visual Studio %s located at %s" % (version, SDK["VISUALSTUDIO"])) + print("Using %s located at %s" % (msvcinfo["vsname"], SDK["VISUALSTUDIO"])) else: - print("Using Visual Studio %s" % (version)) + print("Using %s" % (msvcinfo["vsname"])) + + print("Using MSVC %s" % version_str) def SdkLocateWindows(version = '7.1'): if GetTarget() != "windows" or GetHost() != "windows": @@ -2373,38 +2454,64 @@ def SetupVisualStudioEnviron(): exit("Could not find Visual Studio install directory") if ("MSPLATFORM" not in SDK): exit("Could not find the Microsoft Platform SDK") - os.environ["VCINSTALLDIR"] = SDK["VISUALSTUDIO"] + "VC" + + if (SDK["VISUALSTUDIO_VERSION"] >= (15,0)): + try: + vsver_file = open(os.path.join(SDK["VISUALSTUDIO"], + "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"), "r") + SDK["VCTOOLSVERSION"] = vsver_file.readline().strip() + vcdir_suffix = "VC\\Tools\\MSVC\\%s\\" % SDK["VCTOOLSVERSION"] + except: + exit("Couldn't find tool version of %s." % MSVCVERSIONINFO[SDK["MSVC_VERSION"]]["vsname"]) + else: + vcdir_suffix = "VC\\" + + os.environ["VCINSTALLDIR"] = SDK["VISUALSTUDIO"] + vcdir_suffix os.environ["WindowsSdkDir"] = SDK["MSPLATFORM"] + winsdk_ver = SDK["MSPLATFORM_VERSION"] + # Determine the directories to look in based on the architecture. arch = GetTargetArch() bindir = "" libdir = "" - if (arch == 'x64'): - bindir = 'amd64' - libdir = 'amd64' - elif (arch != 'x86'): - bindir = arch + if ("VCTOOLSVERSION" in SDK): + bindir = "Host" + GetHostArch().upper() + "\\" + arch libdir = arch + else: + if (arch == 'x64'): + bindir = 'amd64' + libdir = 'amd64' + elif (arch != 'x86'): + bindir = arch + libdir = arch - if (arch != 'x86' and GetHostArch() == 'x86'): - # Special version of the tools that run on x86. - bindir = 'x86_' + bindir + if (arch != 'x86' and GetHostArch() == 'x86'): + # Special version of the tools that run on x86. + bindir = 'x86_' + bindir - binpath = SDK["VISUALSTUDIO"] + "VC\\bin\\" + bindir - if not os.path.isdir(binpath): - exit("Couldn't find compilers in %s. You may need to install the Windows SDK 7.1 and the Visual C++ 2010 SP1 Compiler Update for Windows SDK 7.1." % binpath) + vc_binpath = SDK["VISUALSTUDIO"] + vcdir_suffix + "bin" + binpath = os.path.join(vc_binpath, bindir) + if not os.path.isfile(binpath + "\\cl.exe"): + # Try the x86 tools, those should work just as well. + if arch == 'x64' and os.path.isfile(vc_binpath + "\\x86_amd64\\cl.exe"): + binpath = "{0}\\x86_amd64;{0}".format(vc_binpath) + elif winsdk_ver.startswith('10.'): + exit("Couldn't find compilers in %s. You may need to install the Windows SDK 7.1 and the Visual C++ 2010 SP1 Compiler Update for Windows SDK 7.1." % binpath) + else: + exit("Couldn't find compilers in %s." % binpath) AddToPathEnv("PATH", binpath) AddToPathEnv("PATH", SDK["VISUALSTUDIO"] + "Common7\\IDE") - AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\include") - AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\atlmfc\\include") - AddToPathEnv("LIB", SDK["VISUALSTUDIO"] + "VC\\lib\\" + libdir) - AddToPathEnv("LIB", SDK["VISUALSTUDIO"] + "VC\\atlmfc\\lib\\" + libdir) + AddToPathEnv("INCLUDE", os.environ["VCINSTALLDIR"] + "include") + AddToPathEnv("INCLUDE", os.environ["VCINSTALLDIR"] + "atlmfc\\include") + AddToPathEnv("LIB", os.environ["VCINSTALLDIR"] + "lib\\" + libdir) + AddToPathEnv("LIB", os.environ["VCINSTALLDIR"] + "atlmfc\\lib\\" + libdir) winsdk_ver = SDK["MSPLATFORM_VERSION"] if winsdk_ver.startswith('10.'): AddToPathEnv("PATH", SDK["MSPLATFORM"] + "bin\\" + arch) + AddToPathEnv("PATH", SDK["MSPLATFORM"] + "bin\\" + winsdk_ver + "\\" + arch) # Windows Kit 10 introduces the "universal CRT". inc_dir = SDK["MSPLATFORM"] + "Include\\" + winsdk_ver + "\\" @@ -2414,6 +2521,16 @@ def SetupVisualStudioEnviron(): AddToPathEnv("INCLUDE", inc_dir + "um") AddToPathEnv("LIB", lib_dir + "ucrt\\" + arch) AddToPathEnv("LIB", lib_dir + "um\\" + arch) + elif winsdk_ver == '8.1': + AddToPathEnv("PATH", SDK["MSPLATFORM"] + "bin\\" + arch) + + inc_dir = SDK["MSPLATFORM"] + "Include\\" + lib_dir = SDK["MSPLATFORM"] + "Lib\\winv6.3\\" + AddToPathEnv("INCLUDE", inc_dir + "shared") + AddToPathEnv("INCLUDE", inc_dir + "ucrt") + AddToPathEnv("INCLUDE", inc_dir + "um") + AddToPathEnv("LIB", lib_dir + "ucrt\\" + arch) + AddToPathEnv("LIB", lib_dir + "um\\" + arch) else: AddToPathEnv("PATH", SDK["MSPLATFORM"] + "bin") AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include") @@ -2436,7 +2553,7 @@ def SetupVisualStudioEnviron(): # Targeting the 7.1 SDK (which is the only way to have Windows XP support) # with Visual Studio 2015 requires use of the Universal CRT. - if winsdk_ver == '7.1' and SDK["VISUALSTUDIO_VERSION"] == '14.0': + if winsdk_ver == '7.1' and SDK["VISUALSTUDIO_VERSION"] >= (14,0): win_kit = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10") # Fallback in case we can't read the registry. @@ -2516,6 +2633,7 @@ def SetupBuildEnvironment(compiler): # Setting it to UTF-8 is necessary for Python 3 modules to import # correctly. os.environ["LC_ALL"] = "en_US.UTF-8" + os.environ["LANGUAGE"] = "en" if compiler == "MSVC": # Add the visual studio tools to PATH et al. @@ -2601,6 +2719,8 @@ def SetupBuildEnvironment(compiler): if os.path.isdir(pcbsd_inc): SYS_INC_DIRS.append(pcbsd_inc) + null.close() + # Print out the search paths if GetVerbose(): print("System library search path:") @@ -2828,6 +2948,22 @@ def CopyPythonTree(dstdir, srcdir, lib2to3_fixers=[], threads=0): ## ######################################################################## +cfg_parser = None + +def GetMetadataValue(key): + global cfg_parser + if not cfg_parser: + # Parse the metadata from the setup.cfg file. + cfg_parser = configparser.ConfigParser() + path = os.path.join(os.path.dirname(__file__), '..', 'setup.cfg') + assert cfg_parser.read(path), "Could not read setup.cfg file." + + value = cfg_parser.get('metadata', key) + if key == 'classifiers': + value = value.strip().split('\n') + return value + +# This function is being phased out. def ParsePandaVersion(fn): try: f = open(fn, "r") @@ -3139,12 +3275,30 @@ def TargetAdd(target, dummy=0, opts=[], input=[], dep=[], ipath=None, winrc=None for d in CxxCalcDependencies(fullinput, ipath, []): t.deps[d] = 1 + # If we are linking statically, add the source DLL's dynamic dependencies. + if GetLinkAllStatic() and ORIG_EXT[fullinput] == '.lib' and fullinput in TARGET_TABLE: + tdep = TARGET_TABLE[fullinput] + for y in tdep.inputs: + if ORIG_EXT[y] == '.lib': + t.inputs.append(y) + + for opt, _ in LIBNAMES + LIBDIRECTORIES + FRAMEWORKDIRECTORIES: + if opt in tdep.opts and opt not in t.opts: + t.opts.append(opt) + if x.endswith(".in"): # Mark the _igate.cxx file as a dependency also. outbase = os.path.basename(x)[:-3] woutc = GetOutputDir()+"/tmp/"+outbase+"_igate.cxx" t.deps[woutc] = 1 + if target.endswith(".in"): + # Add any .N files. + base, ext = os.path.splitext(fullinput) + fulln = base + ".N" + if os.path.isfile(fulln): + t.deps[fulln] = 1 + for x in dep: fulldep = FindLocation(x, ipath) t.deps[fulldep] = 1 diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index 6d3b6fdf72..92037098dd 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -20,7 +20,7 @@ import tempfile import subprocess from distutils.sysconfig import get_config_var from optparse import OptionParser -from makepandacore import ColorText, LocateBinary, ParsePandaVersion, GetExtensionSuffix, SetVerbose, GetVerbose +from makepandacore import ColorText, LocateBinary, ParsePandaVersion, GetExtensionSuffix, SetVerbose, GetVerbose, GetMetadataValue from base64 import urlsafe_b64encode @@ -103,16 +103,15 @@ Tag: {0}-{1}-{2} """ METADATA = { - "license": "BSD", - "name": "Panda3D", + "license": GetMetadataValue('license'), + "name": GetMetadataValue('name'), "metadata_version": "2.0", "generator": "makepanda", - "summary": "Panda3D is a game engine, a framework for 3D rendering and " - "game development for Python and C++ programs.", + "summary": GetMetadataValue('description'), "extensions": { "python.details": { "project_urls": { - "Home": "https://www.panda3d.org/" + "Home": GetMetadataValue('url'), }, "document_names": { "license": "LICENSE.txt" @@ -120,25 +119,13 @@ METADATA = { "contacts": [ { "role": "author", - "email": "etc-panda3d@lists.andrew.cmu.edu", - "name": "Panda3D Team" + "name": GetMetadataValue('author'), + "email": GetMetadataValue('author_email'), } ] } }, - "classifiers": [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "Intended Audience :: End Users/Desktop", - "License :: OSI Approved :: BSD License", - "Operating System :: OS Independent", - "Programming Language :: C++", - "Programming Language :: Python", - "Topic :: Games/Entertainment", - "Topic :: Multimedia", - "Topic :: Multimedia :: Graphics", - "Topic :: Multimedia :: Graphics :: 3D Rendering" - ] + "classifiers": GetMetadataValue('classifiers'), } PANDA3D_TOOLS_INIT = """import os, sys @@ -401,7 +388,7 @@ class WheelFile(object): fp.close() # Save it in PEP-0376 format for writing out later. - digest = str(urlsafe_b64encode(sha.digest())) + digest = urlsafe_b64encode(sha.digest()).decode('ascii') digest = digest.rstrip('=') self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, size)) @@ -417,7 +404,7 @@ class WheelFile(object): sha = hashlib.sha256() sha.update(source_data.encode()) - digest = str(urlsafe_b64encode(sha.digest())) + digest = urlsafe_b64encode(sha.digest()).decode('ascii') digest = digest.rstrip('=') self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, len(source_data))) @@ -551,7 +538,7 @@ def makewheel(version, output_dir, platform=default_platform): # Add a panda3d-tools directory containing the executables. entry_points = '[console_scripts]\n' entry_points += 'eggcacher = direct.directscripts.eggcacher:main\n' - entry_points += 'packpanda = direct.directscripts.packpanda:main\n' + entry_points += 'pfreeze = direct.showutil.pfreeze:main\n' tools_init = '' for file in os.listdir(bin_dir): basename = os.path.splitext(file)[0] diff --git a/makepanda/test_imports.py b/makepanda/test_imports.py index b5ab5c1977..290ec03e13 100644 --- a/makepanda/test_imports.py +++ b/makepanda/test_imports.py @@ -7,13 +7,19 @@ import os, importlib import direct.showbase.VerboseImport +import imp import panda3d dir = os.path.dirname(panda3d.__file__) -for basename in os.listdir(dir): - module, ext = os.path.splitext(basename) +extensions = set() +for suffix in imp.get_suffixes(): + extensions.add(suffix[0]) - if ext in ('.pyd', '.so'): +for basename in os.listdir(dir): + module = basename.split('.', 1)[0] + ext = basename[len(module):] + + if ext in extensions: importlib.import_module('panda3d.%s' % (module)) diff --git a/models/panda-model.egg b/models/panda-model.egg index df9ca085a0..5a3b6ec7d1 100755 --- a/models/panda-model.egg +++ b/models/panda-model.egg @@ -12378,11 +12378,6 @@ { Tex1 } { 670 158 673 { panda_mesh.verts } } } - { - { 1 1 1 1 } - { Tex1 } - { 602 674 627 { panda_mesh.verts } } - } { { 1 1 1 1 } { Tex1 } @@ -12398,16 +12393,6 @@ { Tex1 } { 628 630 676 { panda_mesh.verts } } } - { - { 1 1 1 1 } - { Tex1 } - { 674 676 630 { panda_mesh.verts } } - } - { - { 1 1 1 1 } - { Tex1 } - { 630 627 674 { panda_mesh.verts } } - } { { 1 1 1 1 } { Tex1 } @@ -16888,11 +16873,6 @@ { Tex1 } { 826 1321 1320 { panda_mesh.verts } } } - { - { 1 1 1 1 } - { Tex1 } - { 674 602 627 { panda_mesh.verts } } - } { { 1 1 1 1 } { Tex1 } @@ -16908,16 +16888,6 @@ { Tex1 } { 630 1283 676 { panda_mesh.verts } } } - { - { 1 1 1 1 } - { Tex1 } - { 676 674 630 { panda_mesh.verts } } - } - { - { 1 1 1 1 } - { Tex1 } - { 627 630 674 { panda_mesh.verts } } - } { { 1 1 1 1 } { Tex1 } @@ -24400,7 +24370,7 @@ } { 294 295 593 595 597 600 602 624 625 626 627 628 629 630 631 632 - 633 634 635 636 637 638 649 650 651 653 656 657 658 659 674 675 + 633 634 635 636 637 638 649 650 651 653 656 657 658 659 675 676 956 957 1251 1254 1256 1258 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1301 1302 1303 1305 1308 1309 1310 1311 1325 diff --git a/panda/src/audio/audioLoadRequest.I b/panda/src/audio/audioLoadRequest.I index 21eea901c5..647692315d 100644 --- a/panda/src/audio/audioLoadRequest.I +++ b/panda/src/audio/audioLoadRequest.I @@ -20,8 +20,7 @@ AudioLoadRequest(AudioManager *audio_manager, const string &filename, bool positional) : _audio_manager(audio_manager), _filename(filename), - _positional(positional), - _is_ready(false) + _positional(positional) { } @@ -55,18 +54,22 @@ get_positional() const { * Returns true if this request has completed, false if it is still pending. * When this returns true, you may retrieve the sound loaded by calling * get_sound(). + * Equivalent to `req.done() and not req.cancelled()`. + * @see done() */ INLINE bool AudioLoadRequest:: is_ready() const { - return _is_ready; + return (FutureState)AtomicAdjust::get(_future_state) == FS_finished; } /** - * Returns the sound that was loaded asynchronously, if any, or NULL if there - * was an error. It is an error to call this unless is_ready() returns true. + * Returns the sound that was loaded asynchronously, if any, or nullptr if + * there was an error. It is an error to call this unless done() returns + * true. + * @deprecated Use result() instead. */ INLINE AudioSound *AudioLoadRequest:: get_sound() const { - nassertr(_is_ready, NULL); - return _sound; + nassertr_always(done(), nullptr); + return (AudioSound *)_result; } diff --git a/panda/src/audio/audioLoadRequest.cxx b/panda/src/audio/audioLoadRequest.cxx index c44780b354..e3a04d38b8 100644 --- a/panda/src/audio/audioLoadRequest.cxx +++ b/panda/src/audio/audioLoadRequest.cxx @@ -21,8 +21,7 @@ TypeHandle AudioLoadRequest::_type_handle; */ AsyncTask::DoneStatus AudioLoadRequest:: do_task() { - _sound = _audio_manager->get_sound(_filename, _positional); - _is_ready = true; + set_result(_audio_manager->get_sound(_filename, _positional)); // Don't continue the task; we're done. return DS_done; diff --git a/panda/src/audio/audioLoadRequest.h b/panda/src/audio/audioLoadRequest.h index 7e5fb3d23b..6d0e59bf4c 100644 --- a/panda/src/audio/audioLoadRequest.h +++ b/panda/src/audio/audioLoadRequest.h @@ -32,8 +32,9 @@ public: ALLOC_DELETED_CHAIN(AudioLoadRequest); PUBLISHED: - INLINE AudioLoadRequest(AudioManager *audio_manager, const string &filename, - bool positional); + INLINE explicit AudioLoadRequest(AudioManager *audio_manager, + const string &filename, + bool positional); INLINE AudioManager *get_audio_manager() const; INLINE const string &get_filename() const; @@ -50,9 +51,6 @@ private: string _filename; bool _positional; - bool _is_ready; - PT(AudioSound) _sound; - public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/audio/audioManager.cxx b/panda/src/audio/audioManager.cxx index 8dd52a8786..c09bbffc05 100644 --- a/panda/src/audio/audioManager.cxx +++ b/panda/src/audio/audioManager.cxx @@ -170,7 +170,7 @@ get_null_sound() { * */ int AudioManager:: -getSpeakerSetup() { +get_speaker_setup() { // intentionally blank return 0; } @@ -179,7 +179,7 @@ getSpeakerSetup() { * */ void AudioManager:: -setSpeakerSetup(SpeakerModeCategory cat) { +set_speaker_setup(SpeakerModeCategory cat) { // intentionally blank } diff --git a/panda/src/audio/audioManager.h b/panda/src/audio/audioManager.h index 262593a1d5..c957581534 100644 --- a/panda/src/audio/audioManager.h +++ b/panda/src/audio/audioManager.h @@ -62,8 +62,8 @@ PUBLISHED: SM_stream, }; - virtual int getSpeakerSetup(); - virtual void setSpeakerSetup(SpeakerModeCategory cat); + virtual int get_speaker_setup(); + virtual void set_speaker_setup(SpeakerModeCategory cat); virtual bool configure_filters(FilterProperties *config); // Create an AudioManager for each category of sounds you have. E.g. @@ -154,9 +154,10 @@ PUBLISHED: PN_stdfloat *ux, PN_stdfloat *uy, PN_stdfloat *uz); // Control the "relative scale that sets the distance factor" units for 3D - // spacialized audio. Default is 1.0 Fmod uses meters internally, so give a - // float in Units-per meter Don't know what Miles uses. Default is 1.0 - // which is adjust in panda to be feet. + // spacialized audio. This is a float in units-per-meter. Default value is + // 1.0, which means that Panda units are understood as meters; for e.g. + // feet, set 3.28. This factor is applied only to Fmod and OpenAL at the + // moment. virtual void audio_3d_set_distance_factor(PN_stdfloat factor); virtual PN_stdfloat audio_3d_get_distance_factor() const; @@ -172,6 +173,7 @@ PUBLISHED: virtual PN_stdfloat audio_3d_get_drop_off_factor() const; static Filename get_dls_pathname(); + MAKE_PROPERTY(dls_pathname, get_dls_pathname); virtual void output(ostream &out) const; virtual void write(ostream &out) const; diff --git a/panda/src/audiotraits/fmodAudioManager.cxx b/panda/src/audiotraits/fmodAudioManager.cxx index 7d7ae246e3..cc1b8ecd0b 100644 --- a/panda/src/audiotraits/fmodAudioManager.cxx +++ b/panda/src/audiotraits/fmodAudioManager.cxx @@ -42,14 +42,8 @@ pset FmodAudioManager::_all_managers; bool FmodAudioManager::_system_is_valid = false; - -// This sets the distance factor for 3D audio to use feet. FMOD uses meters -// by default. Since Panda use feet we need to compensate for that with a -// factor of 3.28 This can be overwritten. You just need to call -// audio_3d_set_distance_factor(PN_stdfloat factor) and set your new factor. - PN_stdfloat FmodAudioManager::_doppler_factor = 1; -PN_stdfloat FmodAudioManager::_distance_factor = 3.28; +PN_stdfloat FmodAudioManager::_distance_factor = 1; PN_stdfloat FmodAudioManager::_drop_off_factor = 1; @@ -100,6 +94,8 @@ FmodAudioManager() { _up.y = 0; _up.z = 0; + _active = true; + _saved_outputtype = FMOD_OUTPUTTYPE_AUTODETECT; if (_system == (FMOD::System *)NULL) { @@ -424,8 +420,8 @@ get_sound(const string &file_name, bool positional, int) { vfs->resolve_filename(path, get_model_path()); // Build a new AudioSound from the audio data. - PT(AudioSound) audioSound = 0; - PT(FmodAudioSound) fmodAudioSound = new FmodAudioSound(this, path, positional ); + PT(AudioSound) audioSound; + PT(FmodAudioSound) fmodAudioSound = new FmodAudioSound(this, path, positional); _all_sounds.insert(fmodAudioSound); @@ -447,7 +443,7 @@ get_sound(MovieAudio *source, bool positional, int) { * This is to query if you are using a MultiChannel Setup. */ int FmodAudioManager:: -getSpeakerSetup() { +get_speaker_setup() { ReMutexHolder holder(_lock); FMOD_RESULT result; FMOD_SPEAKERMODE speakerMode; @@ -502,7 +498,7 @@ getSpeakerSetup() { * init or re-init the AudioManagers after Panda is running. */ void FmodAudioManager:: -setSpeakerSetup(AudioManager::SpeakerModeCategory cat) { +set_speaker_setup(AudioManager::SpeakerModeCategory cat) { ReMutexHolder holder(_lock); FMOD_RESULT result; FMOD_SPEAKERMODE speakerModeType = (FMOD_SPEAKERMODE)cat; diff --git a/panda/src/audiotraits/fmodAudioManager.h b/panda/src/audiotraits/fmodAudioManager.h index 9a5a9f3759..751385e5a7 100644 --- a/panda/src/audiotraits/fmodAudioManager.h +++ b/panda/src/audiotraits/fmodAudioManager.h @@ -91,8 +91,8 @@ public: virtual PT(AudioSound) get_sound(const string&, bool positional = false, int mode=SM_heuristic); virtual PT(AudioSound) get_sound(MovieAudio *, bool positional = false, int mode=SM_heuristic); - virtual int getSpeakerSetup(); - virtual void setSpeakerSetup(SpeakerModeCategory cat); + virtual int get_speaker_setup(); + virtual void set_speaker_setup(SpeakerModeCategory cat); virtual void set_volume(PN_stdfloat); virtual PN_stdfloat get_volume() const; @@ -123,9 +123,11 @@ public: PN_stdfloat *fx, PN_stdfloat *fy, PN_stdfloat *fz, PN_stdfloat *ux, PN_stdfloat *uy, PN_stdfloat *uz); - // Control the "relative distance factor" for 3D spacialized audio. Default - // is 1.0 Fmod uses meters internally, so give a float in Units-per meter - // Don't know what Miles uses. + // Control the "relative scale that sets the distance factor" units for 3D + // spacialized audio. This is a float in units-per-meter. Default value is + // 1.0, which means that Panda units are understood as meters; for e.g. + // feet, set 3.28. This factor is applied only to Fmod and OpenAL at the + // moment. virtual void audio_3d_set_distance_factor(PN_stdfloat factor); virtual PN_stdfloat audio_3d_get_distance_factor() const; diff --git a/panda/src/audiotraits/fmodAudioSound.cxx b/panda/src/audiotraits/fmodAudioSound.cxx index a793b9bfa5..f10750ade3 100644 --- a/panda/src/audiotraits/fmodAudioSound.cxx +++ b/panda/src/audiotraits/fmodAudioSound.cxx @@ -55,6 +55,9 @@ FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) { _velocity.y = 0; _velocity.z = 0; + _min_dist = 1.0; + _max_dist = 1000000000.0; + // Play Rate Variable _playrate = 1; diff --git a/panda/src/audiotraits/openalAudioManager.cxx b/panda/src/audiotraits/openalAudioManager.cxx index ba1b4684a3..2e82a9c718 100644 --- a/panda/src/audiotraits/openalAudioManager.cxx +++ b/panda/src/audiotraits/openalAudioManager.cxx @@ -97,7 +97,7 @@ OpenALAudioManager() { _is_valid = true; // Init 3D attributes - _distance_factor = 3.28; + _distance_factor = 1; _drop_off_factor = 1; _position[0] = 0; @@ -715,12 +715,11 @@ audio_3d_get_listener_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat * *uz = _forward_up[4]; } - /** - * Set units per foot WARNING: OpenAL has no distance factor but we use this - * as a scale on the min/max distances of sounds to preserve FMOD - * compatibility. Also, adjusts the speed of sound to compensate for unit - * difference. OpenAL's default speed of sound is 343.3 m/s == 1126.3 ft/s + * Set value in units per meter + * WARNING: OpenAL has no distance factor but we use this as a scale + * on the min/max distances of sounds to preserve FMOD compatibility. + * Also adjusts the speed of sound to compensate for unit difference. */ void OpenALAudioManager:: audio_3d_set_distance_factor(PN_stdfloat factor) { @@ -732,7 +731,7 @@ audio_3d_set_distance_factor(PN_stdfloat factor) { alGetError(); // clear errors if (_distance_factor>0) { - alSpeedOfSound(1126.3*_distance_factor); + alSpeedOfSound(343.3*_distance_factor); al_audio_errcheck("alSpeedOfSound()"); // resets the doppler factor to the correct setting in case it was set to // 0.0 by a distance_factor<=0.0 @@ -752,7 +751,7 @@ audio_3d_set_distance_factor(PN_stdfloat factor) { } /** - * Sets units per foot + * Get value in units per meter */ PN_stdfloat OpenALAudioManager:: audio_3d_get_distance_factor() const { @@ -1022,7 +1021,6 @@ cleanup() { OpenALAudioManager::SoundData:: SoundData() : _manager(0), - _movie(0), _sample(0), _stream(NULL), _length(0.0), diff --git a/panda/src/audiotraits/openalAudioManager.h b/panda/src/audiotraits/openalAudioManager.h index 70089c138d..5917900869 100644 --- a/panda/src/audiotraits/openalAudioManager.h +++ b/panda/src/audiotraits/openalAudioManager.h @@ -84,11 +84,14 @@ class EXPCL_OPENAL_AUDIO OpenALAudioManager : public AudioManager { PN_stdfloat *fx, PN_stdfloat *fy, PN_stdfloat *fz, PN_stdfloat *ux, PN_stdfloat *uy, PN_stdfloat *uz); - // Control the "relative distance factor" for 3D spacialized audio in units- - // per-foot. Default is 1.0 OpenAL has no distance factor but we use this - // as a scale on the minmax distances of sounds to preserve FMOD - // compatibility. Also, adjusts the speed of sound to compensate for unit - // difference. + + // Control the "relative scale that sets the distance factor" units for 3D + // spacialized audio. This is a float in units-per-meter. Default value is + // 1.0, which means that Panda units are understood as meters; for e.g. + // feet, set 3.28. This factor is applied only to Fmod and OpenAL at the + // moment. + // OpenAL in fact has no distance factor like Fmod, but works with the speed + // of sound instead, so we use this factor to scale the speed of sound. virtual void audio_3d_set_distance_factor(PN_stdfloat factor); virtual PN_stdfloat audio_3d_get_distance_factor() const; diff --git a/panda/src/audiotraits/openalAudioSound.cxx b/panda/src/audiotraits/openalAudioSound.cxx index 43ebd149cb..da379bfd5c 100644 --- a/panda/src/audiotraits/openalAudioSound.cxx +++ b/panda/src/audiotraits/openalAudioSound.cxx @@ -48,7 +48,7 @@ OpenALAudioSound(OpenALAudioManager* manager, _balance(0), _play_rate(1.0), _positional(positional), - _min_dist(3.28f), + _min_dist(1.0f), _max_dist(1000000000.0f), _drop_off_factor(1.0f), _length(0.0), @@ -673,7 +673,7 @@ set_3d_min_distance(PN_stdfloat dist) { _manager->make_current(); alGetError(); // clear errors - alSourcef(_source,AL_REFERENCE_DISTANCE,_min_dist*_manager->audio_3d_get_distance_factor()); + alSourcef(_source,AL_REFERENCE_DISTANCE,_min_dist); al_audio_errcheck("alSourcefv(_source,AL_REFERENCE_DISTANCE)"); } } @@ -698,7 +698,7 @@ set_3d_max_distance(PN_stdfloat dist) { _manager->make_current(); alGetError(); // clear errors - alSourcef(_source,AL_MAX_DISTANCE,_max_dist*_manager->audio_3d_get_distance_factor()); + alSourcef(_source,AL_MAX_DISTANCE,_max_dist); al_audio_errcheck("alSourcefv(_source,AL_MAX_DISTANCE)"); } } diff --git a/panda/src/bullet/bulletAllHitsRayResult.h b/panda/src/bullet/bulletAllHitsRayResult.h index c5ff637ac9..b0b31e04d3 100644 --- a/panda/src/bullet/bulletAllHitsRayResult.h +++ b/panda/src/bullet/bulletAllHitsRayResult.h @@ -39,6 +39,13 @@ PUBLISHED: int get_shape_part() const; int get_triangle_index() const; + MAKE_PROPERTY(node, get_node); + MAKE_PROPERTY(hit_pos, get_hit_pos); + MAKE_PROPERTY(hit_normal, get_hit_normal); + MAKE_PROPERTY(hit_fraction, get_hit_fraction); + MAKE_PROPERTY(shape_part, get_shape_part); + MAKE_PROPERTY(triangle_index, get_triangle_index); + private: const btCollisionObject *_object; btVector3 _normal; @@ -69,6 +76,11 @@ PUBLISHED: const BulletRayHit get_hit(int idx) const; MAKE_SEQ(get_hits, get_num_hits, get_hit); + MAKE_PROPERTY(from_pos, get_from_pos); + MAKE_PROPERTY(to_pos, get_to_pos); + MAKE_PROPERTY(closest_hit_fraction, get_closest_hit_fraction); + MAKE_SEQ_PROPERTY(hits, get_num_hits, get_hit); + public: virtual bool needsCollision(btBroadphaseProxy* proxy0) const; virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace); diff --git a/panda/src/bullet/bulletBaseCharacterControllerNode.h b/panda/src/bullet/bulletBaseCharacterControllerNode.h index b09b26db74..e7abe41886 100644 --- a/panda/src/bullet/bulletBaseCharacterControllerNode.h +++ b/panda/src/bullet/bulletBaseCharacterControllerNode.h @@ -27,9 +27,8 @@ * */ class EXPCL_PANDABULLET BulletBaseCharacterControllerNode : public PandaNode { - PUBLISHED: - BulletBaseCharacterControllerNode(const char *name="character"); + explicit BulletBaseCharacterControllerNode(const char *name="character"); public: virtual CollideMask get_legal_collide_mask() const; diff --git a/panda/src/bullet/bulletBodyNode.cxx b/panda/src/bullet/bulletBodyNode.cxx index cb4d39c444..b078864ccc 100644 --- a/panda/src/bullet/bulletBodyNode.cxx +++ b/panda/src/bullet/bulletBodyNode.cxx @@ -442,6 +442,15 @@ set_active(bool active, bool force) { } } +/** + * + */ +void BulletBodyNode:: +force_active(bool active) { + + set_active(active, true); +} + /** * If true, this object will be deactivated after a certain amount of time has * passed without movement. If false, the object will always remain active. diff --git a/panda/src/bullet/bulletBodyNode.h b/panda/src/bullet/bulletBodyNode.h index 53fe6c7136..562e88c7fb 100644 --- a/panda/src/bullet/bulletBodyNode.h +++ b/panda/src/bullet/bulletBodyNode.h @@ -56,7 +56,7 @@ PUBLISHED: // Static and kinematic INLINE bool is_static() const; INLINE bool is_kinematic() const; - + INLINE void set_static(bool value); INLINE void set_kinematic(bool value); @@ -79,6 +79,7 @@ PUBLISHED: // Deactivation bool is_active() const; void set_active(bool active, bool force=false); + void force_active(bool active); void set_deactivation_time(PN_stdfloat dt); PN_stdfloat get_deactivation_time() const; @@ -86,7 +87,7 @@ PUBLISHED: void set_deactivation_enabled(bool enabled); bool is_deactivation_enabled() const; - // Debug Visualistion + // Debug Visualisation INLINE void set_debug_enabled(const bool enabled); INLINE bool is_debug_enabled() const; @@ -100,6 +101,7 @@ PUBLISHED: #if BT_BULLET_VERSION >= 281 INLINE PN_stdfloat get_rolling_friction() const; INLINE void set_rolling_friction(PN_stdfloat friction); + MAKE_PROPERTY(rolling_friction, get_rolling_friction, set_rolling_friction); #endif INLINE bool has_anisotropic_friction() const; @@ -115,6 +117,27 @@ PUBLISHED: // Special void set_transform_dirty(); + MAKE_SEQ_PROPERTY(shapes, get_num_shapes, get_shape); + MAKE_SEQ_PROPERTY(shape_pos, get_num_shapes, get_shape_pos); + MAKE_SEQ_PROPERTY(shape_mat, get_num_shapes, get_shape_mat); + MAKE_SEQ_PROPERTY(shape_transform, get_num_shapes, get_shape_transform); + MAKE_PROPERTY(shape_bounds, get_shape_bounds); + MAKE_PROPERTY(static, is_static, set_static); + MAKE_PROPERTY(kinematic, is_kinematic, set_kinematic); + MAKE_PROPERTY(collision_notification, notifies_collisions, notify_collisions); + MAKE_PROPERTY(collision_response, get_collision_response, set_collision_response); + MAKE_PROPERTY(contact_response, has_contact_response); + MAKE_PROPERTY(contact_processing_threshold, get_contact_processing_threshold, set_contact_processing_threshold); + MAKE_PROPERTY(active, is_active, force_active); + MAKE_PROPERTY(deactivation_time, get_deactivation_time, set_deactivation_time); + MAKE_PROPERTY(deactivation_enabled, is_deactivation_enabled, set_deactivation_enabled); + MAKE_PROPERTY(debug_enabled, is_debug_enabled, set_debug_enabled); + MAKE_PROPERTY(restitution, get_restitution, set_restitution); + MAKE_PROPERTY(friction, get_friction, set_friction); + MAKE_PROPERTY(anisotropic_friction, get_anisotropic_friction, set_anisotropic_friction); + MAKE_PROPERTY(ccd_swept_sphere_radius, get_ccd_swept_sphere_radius, set_ccd_swept_sphere_radius); + MAKE_PROPERTY(ccd_motion_threshold, get_ccd_motion_threshold, set_ccd_motion_threshold); + public: virtual btCollisionObject *get_object() const = 0; diff --git a/panda/src/bullet/bulletBoxShape.I b/panda/src/bullet/bulletBoxShape.I index 1051a4018f..384aace8b6 100644 --- a/panda/src/bullet/bulletBoxShape.I +++ b/panda/src/bullet/bulletBoxShape.I @@ -11,6 +11,15 @@ * @date 2010-01-24 */ +/** + * Only used by make_from_bam. + */ +INLINE BulletBoxShape:: +BulletBoxShape() : + _shape(nullptr), + _half_extents(LVecBase3::zero()) { +} + /** * */ @@ -25,7 +34,7 @@ INLINE BulletBoxShape:: */ INLINE BulletBoxShape:: BulletBoxShape(const BulletBoxShape ©) : - _shape(copy._shape) { + _shape(copy._shape), _half_extents(copy._half_extents) { } /** @@ -34,4 +43,5 @@ BulletBoxShape(const BulletBoxShape ©) : INLINE void BulletBoxShape:: operator = (const BulletBoxShape ©) { _shape = copy._shape; + _half_extents = copy._half_extents; } diff --git a/panda/src/bullet/bulletBoxShape.cxx b/panda/src/bullet/bulletBoxShape.cxx index 7d52cf2513..02b5af45b3 100644 --- a/panda/src/bullet/bulletBoxShape.cxx +++ b/panda/src/bullet/bulletBoxShape.cxx @@ -20,7 +20,7 @@ TypeHandle BulletBoxShape::_type_handle; * */ BulletBoxShape:: -BulletBoxShape(const LVecBase3 &halfExtents) { +BulletBoxShape(const LVecBase3 &halfExtents) : _half_extents(halfExtents) { btVector3 btHalfExtents = LVecBase3_to_btVector3(halfExtents); @@ -85,8 +85,9 @@ register_with_read_factory() { */ void BulletBoxShape:: write_datagram(BamWriter *manager, Datagram &dg) { + BulletShape::write_datagram(manager, dg); dg.add_stdfloat(get_margin()); - get_half_extents_with_margin().write_datagram(dg); + _half_extents.write_datagram(dg); } /** @@ -112,14 +113,14 @@ make_from_bam(const FactoryParams ¶ms) { */ void BulletBoxShape:: fillin(DatagramIterator &scan, BamReader *manager) { - nassertv(_shape == NULL); + BulletShape::fillin(scan, manager); + nassertv(_shape == nullptr); PN_stdfloat margin = scan.get_stdfloat(); - LVector3 half_extents; - half_extents.read_datagram(scan); + _half_extents.read_datagram(scan); - _shape = new btBoxShape(LVecBase3_to_btVector3(half_extents)); + _shape = new btBoxShape(LVecBase3_to_btVector3(_half_extents)); _shape->setUserPointer(this); _shape->setMargin(margin); } diff --git a/panda/src/bullet/bulletBoxShape.h b/panda/src/bullet/bulletBoxShape.h index 82b1f10412..d7fa1c5169 100644 --- a/panda/src/bullet/bulletBoxShape.h +++ b/panda/src/bullet/bulletBoxShape.h @@ -29,10 +29,10 @@ class EXPCL_PANDABULLET BulletBoxShape : public BulletShape { private: // Only used by make_from_bam - INLINE BulletBoxShape() : _shape(NULL) {}; + INLINE BulletBoxShape(); PUBLISHED: - BulletBoxShape(const LVecBase3 &halfExtents); + explicit BulletBoxShape(const LVecBase3 &halfExtents); INLINE BulletBoxShape(const BulletBoxShape ©); INLINE void operator = (const BulletBoxShape ©); INLINE ~BulletBoxShape(); @@ -42,11 +42,15 @@ PUBLISHED: static BulletBoxShape *make_from_solid(const CollisionBox *solid); + MAKE_PROPERTY(half_extents_with_margin, get_half_extents_with_margin); + MAKE_PROPERTY(half_extents_without_margin, get_half_extents_without_margin); + public: virtual btCollisionShape *ptr() const; private: btBoxShape *_shape; + LVecBase3 _half_extents; public: static void register_with_read_factory(); diff --git a/panda/src/bullet/bulletCapsuleShape.I b/panda/src/bullet/bulletCapsuleShape.I index 24a82dbdbf..feb6cdff57 100644 --- a/panda/src/bullet/bulletCapsuleShape.I +++ b/panda/src/bullet/bulletCapsuleShape.I @@ -11,6 +11,16 @@ * @date 2010-01-27 */ +/** + * Only used by make_from_bam. + */ +INLINE BulletCapsuleShape:: +BulletCapsuleShape() : + _shape(nullptr), + _radius(0), + _height(0) { +} + /** * */ @@ -25,7 +35,9 @@ INLINE BulletCapsuleShape:: */ INLINE BulletCapsuleShape:: BulletCapsuleShape(const BulletCapsuleShape ©) : - _shape(copy._shape) { + _shape(copy._shape), + _radius(copy._radius), + _height(copy._height) { } /** @@ -34,22 +46,31 @@ BulletCapsuleShape(const BulletCapsuleShape ©) : INLINE void BulletCapsuleShape:: operator = (const BulletCapsuleShape ©) { _shape = copy._shape; + _radius = copy._radius; + _height = copy._height; } /** - * + * Returns the radius that was used to construct this capsule. */ INLINE PN_stdfloat BulletCapsuleShape:: get_radius() const { - - return (PN_stdfloat)_shape->getRadius(); + return _radius; } /** - * + * Returns half of get_height(). + * @deprecated see get_height() instead. */ INLINE PN_stdfloat BulletCapsuleShape:: get_half_height() const { - - return (PN_stdfloat)_shape->getHalfHeight(); + return _height * 0.5; +} + +/** + * Returns the height that was used to construct this capsule. + */ +INLINE PN_stdfloat BulletCapsuleShape:: +get_height() const { + return _height; } diff --git a/panda/src/bullet/bulletCapsuleShape.cxx b/panda/src/bullet/bulletCapsuleShape.cxx index c1986f1a3a..9f389985e3 100644 --- a/panda/src/bullet/bulletCapsuleShape.cxx +++ b/panda/src/bullet/bulletCapsuleShape.cxx @@ -19,7 +19,9 @@ TypeHandle BulletCapsuleShape::_type_handle; * */ BulletCapsuleShape:: -BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) { +BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) : + _radius(radius), + _height(height) { switch (up) { case X_up: @@ -47,3 +49,79 @@ ptr() const { return _shape; } + +/** + * Tells the BamReader how to create objects of type BulletShape. + */ +void BulletCapsuleShape:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +/** + * Writes the contents of this object to the datagram for shipping out to a + * Bam file. + */ +void BulletCapsuleShape:: +write_datagram(BamWriter *manager, Datagram &dg) { + BulletShape::write_datagram(manager, dg); + dg.add_stdfloat(get_margin()); + + // parameters to serialize: radius, height, up + dg.add_stdfloat(_radius); + dg.add_stdfloat(_height); + dg.add_int8((int8_t)_shape->getUpAxis()); +} + +/** + * This function is called by the BamReader's factory when a new object of + * type BulletShape is encountered in the Bam file. It should create the + * BulletShape and extract its information from the file. + */ +TypedWritable *BulletCapsuleShape:: +make_from_bam(const FactoryParams ¶ms) { + // create a default BulletCapsuleShape + BulletCapsuleShape *param = new BulletCapsuleShape; + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + param->fillin(scan, manager); + + return param; +} + +/** + * This internal function is called by make_from_bam to read in all of the + * relevant data from the BamFile for the new BulletShape. + */ +void BulletCapsuleShape:: +fillin(DatagramIterator &scan, BamReader *manager) { + BulletShape::fillin(scan, manager); + nassertv(_shape == nullptr); + + PN_stdfloat margin = scan.get_stdfloat(); + + // parameters to serialize: radius, height, up + _radius = scan.get_stdfloat(); + _height = scan.get_stdfloat(); + int up = (int) scan.get_int8(); + + switch (up) { + case X_up: + _shape = new btCapsuleShapeX(_radius, _height); + break; + case Y_up: + _shape = new btCapsuleShape(_radius, _height); + break; + case Z_up: + _shape = new btCapsuleShapeZ(_radius, _height); + break; + default: + bullet_cat.error() << "invalid up-axis:" << up << endl; + break; + } + + _shape->setUserPointer(this); + _shape->setMargin(margin); +} diff --git a/panda/src/bullet/bulletCapsuleShape.h b/panda/src/bullet/bulletCapsuleShape.h index 5e2150c27c..5b031cc548 100644 --- a/panda/src/bullet/bulletCapsuleShape.h +++ b/panda/src/bullet/bulletCapsuleShape.h @@ -24,9 +24,12 @@ * */ class EXPCL_PANDABULLET BulletCapsuleShape : public BulletShape { +private: + // Only used by make_from_bam + INLINE BulletCapsuleShape(); PUBLISHED: - BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); + explicit BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); INLINE BulletCapsuleShape(const BulletCapsuleShape ©); INLINE void operator = (const BulletCapsuleShape ©); INLINE ~BulletCapsuleShape(); @@ -35,10 +38,26 @@ PUBLISHED: INLINE PN_stdfloat get_half_height() const; public: + INLINE PN_stdfloat get_height() const; + virtual btCollisionShape *ptr() const; +PUBLISHED: + MAKE_PROPERTY(radius, get_radius); + MAKE_PROPERTY(height, get_height); + private: btCapsuleShape *_shape; + PN_stdfloat _radius; + PN_stdfloat _height; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); public: static TypeHandle get_class_type() { diff --git a/panda/src/bullet/bulletCharacterControllerNode.cxx b/panda/src/bullet/bulletCharacterControllerNode.cxx index 8d9dfdf434..c6af3842e9 100644 --- a/panda/src/bullet/bulletCharacterControllerNode.cxx +++ b/panda/src/bullet/bulletCharacterControllerNode.cxx @@ -296,7 +296,6 @@ set_gravity(PN_stdfloat gravity) { #endif } - /** * */ @@ -304,4 +303,4 @@ void BulletCharacterControllerNode:: set_use_ghost_sweep_test(bool value) { return _character->setUseGhostSweepTest(value); -} +} \ No newline at end of file diff --git a/panda/src/bullet/bulletCharacterControllerNode.h b/panda/src/bullet/bulletCharacterControllerNode.h index 80f6aab834..0b8574c709 100644 --- a/panda/src/bullet/bulletCharacterControllerNode.h +++ b/panda/src/bullet/bulletCharacterControllerNode.h @@ -29,9 +29,9 @@ * */ class EXPCL_PANDABULLET BulletCharacterControllerNode : public BulletBaseCharacterControllerNode { - PUBLISHED: - BulletCharacterControllerNode(BulletShape *shape, PN_stdfloat step_height, const char *name="character"); + explicit BulletCharacterControllerNode(BulletShape *shape, PN_stdfloat step_height, + const char *name="character"); INLINE ~BulletCharacterControllerNode(); void set_linear_movement(const LVector3 &velocity, bool is_local); @@ -39,20 +39,27 @@ PUBLISHED: BulletShape *get_shape() const; + void set_gravity(PN_stdfloat gravity); PN_stdfloat get_gravity() const; - PN_stdfloat get_max_slope() const; void set_fall_speed(PN_stdfloat fall_speed); void set_jump_speed(PN_stdfloat jump_speed); void set_max_jump_height(PN_stdfloat max_jump_height); + void set_max_slope(PN_stdfloat max_slope); - void set_gravity(PN_stdfloat gravity); + PN_stdfloat get_max_slope() const; + void set_use_ghost_sweep_test(bool value); bool is_on_ground() const; bool can_jump() const; void do_jump(); + MAKE_PROPERTY(shape, get_shape); + MAKE_PROPERTY(gravity, get_gravity, set_gravity); + MAKE_PROPERTY(max_slope, get_max_slope, set_max_slope); + MAKE_PROPERTY(on_ground, is_on_ground); + public: INLINE virtual btPairCachingGhostObject *get_ghost() const; INLINE virtual btCharacterControllerInterface *get_character() const; diff --git a/panda/src/bullet/bulletClosestHitRayResult.h b/panda/src/bullet/bulletClosestHitRayResult.h index 0f2bd67128..c6c31f19d0 100644 --- a/panda/src/bullet/bulletClosestHitRayResult.h +++ b/panda/src/bullet/bulletClosestHitRayResult.h @@ -44,6 +44,15 @@ PUBLISHED: int get_shape_part() const; int get_triangle_index() const; + MAKE_PROPERTY(from_pos, get_from_pos); + MAKE_PROPERTY(to_pos, get_to_pos); + MAKE_PROPERTY(node, get_node); + MAKE_PROPERTY(hit_pos, get_hit_pos); + MAKE_PROPERTY(hit_normal, get_hit_normal); + MAKE_PROPERTY(hit_fraction, get_hit_fraction); + MAKE_PROPERTY(shape_part, get_shape_part); + MAKE_PROPERTY(triangle_index, get_triangle_index); + public: virtual bool needsCollision(btBroadphaseProxy* proxy0) const; virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace); diff --git a/panda/src/bullet/bulletClosestHitSweepResult.h b/panda/src/bullet/bulletClosestHitSweepResult.h index 44a82e5fb8..aba5ff57d8 100644 --- a/panda/src/bullet/bulletClosestHitSweepResult.h +++ b/panda/src/bullet/bulletClosestHitSweepResult.h @@ -41,6 +41,13 @@ PUBLISHED: LVector3 get_hit_normal() const; PN_stdfloat get_hit_fraction() const; + MAKE_PROPERTY(from_pos, get_from_pos); + MAKE_PROPERTY(to_pos, get_to_pos); + MAKE_PROPERTY(node, get_node); + MAKE_PROPERTY(hit_pos, get_hit_pos); + MAKE_PROPERTY(hit_normal, get_hit_normal); + MAKE_PROPERTY(hit_fraction, get_hit_fraction); + public: virtual bool needsCollision(btBroadphaseProxy* proxy0) const; diff --git a/panda/src/bullet/bulletConeShape.I b/panda/src/bullet/bulletConeShape.I index 89198b8cf3..bf618a862b 100644 --- a/panda/src/bullet/bulletConeShape.I +++ b/panda/src/bullet/bulletConeShape.I @@ -11,6 +11,16 @@ * @date 2010-01-24 */ +/** + * Only used by make_from_bam. + */ +INLINE BulletConeShape:: +BulletConeShape() : + _shape(nullptr), + _radius(0), + _height(0) { +} + /** * */ @@ -25,7 +35,9 @@ INLINE BulletConeShape:: */ INLINE BulletConeShape:: BulletConeShape(const BulletConeShape ©) : - _shape(copy._shape) { + _shape(copy._shape), + _radius(copy._radius), + _height(copy._height) { } /** @@ -34,22 +46,22 @@ BulletConeShape(const BulletConeShape ©) : INLINE void BulletConeShape:: operator = (const BulletConeShape ©) { _shape = copy._shape; + _radius = copy._radius; + _height = copy._height; } /** - * + * Returns the radius that was passed into the constructor. */ INLINE PN_stdfloat BulletConeShape:: get_radius() const { - - return (PN_stdfloat)_shape->getRadius(); + return _radius; } /** - * + * Returns the height that was passed into the constructor. */ INLINE PN_stdfloat BulletConeShape:: get_height() const { - - return (PN_stdfloat)_shape->getHeight(); + return _height; } diff --git a/panda/src/bullet/bulletConeShape.cxx b/panda/src/bullet/bulletConeShape.cxx index e06bf6d842..80e91a038f 100644 --- a/panda/src/bullet/bulletConeShape.cxx +++ b/panda/src/bullet/bulletConeShape.cxx @@ -19,7 +19,9 @@ TypeHandle BulletConeShape::_type_handle; * */ BulletConeShape:: -BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) { +BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) : + _radius(radius), + _height(height) { switch (up) { case X_up: @@ -47,3 +49,79 @@ ptr() const { return _shape; } + +/** + * Tells the BamReader how to create objects of type BulletShape. + */ +void BulletConeShape:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +/** + * Writes the contents of this object to the datagram for shipping out to a + * Bam file. + */ +void BulletConeShape:: +write_datagram(BamWriter *manager, Datagram &dg) { + BulletShape::write_datagram(manager, dg); + dg.add_stdfloat(get_margin()); + + // parameters to serialize: radius, height, upIndex + dg.add_stdfloat(_radius); + dg.add_stdfloat(_height); + dg.add_int8((int8_t)_shape->getConeUpIndex()); +} + +/** + * This function is called by the BamReader's factory when a new object of + * type BulletShape is encountered in the Bam file. It should create the + * BulletShape and extract its information from the file. + */ +TypedWritable *BulletConeShape:: +make_from_bam(const FactoryParams ¶ms) { + // create a default BulletConeShape + BulletConeShape *param = new BulletConeShape; + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + param->fillin(scan, manager); + + return param; +} + +/** + * This internal function is called by make_from_bam to read in all of the + * relevant data from the BamFile for the new BulletShape. + */ +void BulletConeShape:: +fillin(DatagramIterator &scan, BamReader *manager) { + BulletShape::fillin(scan, manager); + nassertv(_shape == nullptr); + + PN_stdfloat margin = scan.get_stdfloat(); + + // parameters to serialize: radius, height, up + _radius = scan.get_stdfloat(); + _height = scan.get_stdfloat(); + + int up_index = (int) scan.get_int8(); + switch (up_index) { + case 0: + _shape = new btConeShapeX((btScalar)_radius, (btScalar)_height); + break; + case 1: + _shape = new btConeShape((btScalar)_radius, (btScalar)_height); + break; + case 2: + _shape = new btConeShapeZ((btScalar)_radius, (btScalar)_height); + break; + default: + bullet_cat.error() << "invalid up-axis:" << up_index << endl; + break; + } + + _shape->setUserPointer(this); + _shape->setMargin(margin); +} diff --git a/panda/src/bullet/bulletConeShape.h b/panda/src/bullet/bulletConeShape.h index 21349bac14..081367449b 100644 --- a/panda/src/bullet/bulletConeShape.h +++ b/panda/src/bullet/bulletConeShape.h @@ -24,9 +24,12 @@ * */ class EXPCL_PANDABULLET BulletConeShape : public BulletShape { +private: + // Only used by make_from_bam + INLINE BulletConeShape(); PUBLISHED: - BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); + explicit BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); INLINE BulletConeShape(const BulletConeShape ©); INLINE void operator = (const BulletConeShape ©); INLINE ~BulletConeShape(); @@ -34,11 +37,24 @@ PUBLISHED: INLINE PN_stdfloat get_radius() const; INLINE PN_stdfloat get_height() const; + MAKE_PROPERTY(radius, get_radius); + MAKE_PROPERTY(height, get_height); + public: virtual btCollisionShape *ptr() const; private: btConeShape *_shape; + PN_stdfloat _radius; + PN_stdfloat _height; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); public: static TypeHandle get_class_type() { diff --git a/panda/src/bullet/bulletConeTwistConstraint.h b/panda/src/bullet/bulletConeTwistConstraint.h index 1cc4cc61e6..bc4f548dfb 100644 --- a/panda/src/bullet/bulletConeTwistConstraint.h +++ b/panda/src/bullet/bulletConeTwistConstraint.h @@ -30,12 +30,12 @@ class BulletRigidBodyNode; class EXPCL_PANDABULLET BulletConeTwistConstraint : public BulletConstraint { PUBLISHED: - BulletConeTwistConstraint(const BulletRigidBodyNode *node_a, - const TransformState *frame_a); - BulletConeTwistConstraint(const BulletRigidBodyNode *node_a, - const BulletRigidBodyNode *node_b, - const TransformState *frame_a, - const TransformState *frame_b); + explicit BulletConeTwistConstraint(const BulletRigidBodyNode *node_a, + const TransformState *frame_a); + explicit BulletConeTwistConstraint(const BulletRigidBodyNode *node_a, + const BulletRigidBodyNode *node_b, + const TransformState *frame_a, + const TransformState *frame_b); INLINE ~BulletConeTwistConstraint(); void set_limit(int index, PN_stdfloat value); @@ -56,6 +56,10 @@ PUBLISHED: INLINE CPT(TransformState) get_frame_a() const; INLINE CPT(TransformState) get_frame_b() const; + MAKE_PROPERTY(fix_threshold, get_fix_threshold, set_fix_threshold); + MAKE_PROPERTY(frame_a, get_frame_a); + MAKE_PROPERTY(frame_b, get_frame_b); + public: virtual btTypedConstraint *ptr() const; diff --git a/panda/src/bullet/bulletConstraint.I b/panda/src/bullet/bulletConstraint.I index b2f4666608..d66f5b6099 100644 --- a/panda/src/bullet/bulletConstraint.I +++ b/panda/src/bullet/bulletConstraint.I @@ -34,7 +34,7 @@ set_breaking_threshold(PN_stdfloat threshold) { * Returns the applied impluse limit for breaking the constraint. */ INLINE PN_stdfloat BulletConstraint:: -set_breaking_threshold() const { +get_breaking_threshold() const { return (PN_stdfloat)ptr()->getBreakingImpulseThreshold(); } diff --git a/panda/src/bullet/bulletConstraint.h b/panda/src/bullet/bulletConstraint.h index 9f97be7af8..85b0f0d63d 100644 --- a/panda/src/bullet/bulletConstraint.h +++ b/panda/src/bullet/bulletConstraint.h @@ -34,13 +34,13 @@ PUBLISHED: BulletRigidBodyNode *get_rigid_body_b(); void enable_feedback(bool value); - void set_debug_draw_size(PN_stdfloat size); - PN_stdfloat get_applied_impulse() const; + void set_debug_draw_size(PN_stdfloat size); PN_stdfloat get_debug_draw_size(); + PN_stdfloat get_applied_impulse() const; INLINE void set_breaking_threshold(PN_stdfloat threshold); - INLINE PN_stdfloat set_breaking_threshold() const; + INLINE PN_stdfloat get_breaking_threshold() const; INLINE void set_enabled(bool enabled); INLINE bool is_enabled() const; @@ -54,6 +54,13 @@ PUBLISHED: void set_param(ConstraintParam num, PN_stdfloat value, int axis=-1); PN_stdfloat get_param(ConstraintParam num, int axis=-1); + MAKE_PROPERTY(rigid_body_a, get_rigid_body_a); + MAKE_PROPERTY(rigid_body_b, get_rigid_body_b); + MAKE_PROPERTY(debug_draw_size, get_debug_draw_size, set_debug_draw_size); + MAKE_PROPERTY(applied_impulse, get_applied_impulse); + MAKE_PROPERTY(breaking_threshold, get_breaking_threshold, set_breaking_threshold); + MAKE_PROPERTY(enabled, is_enabled, set_enabled); + public: virtual btTypedConstraint *ptr() const = 0; diff --git a/panda/src/bullet/bulletContactCallbackData.h b/panda/src/bullet/bulletContactCallbackData.h index a83f66ff9c..c6c45c1e07 100644 --- a/panda/src/bullet/bulletContactCallbackData.h +++ b/panda/src/bullet/bulletContactCallbackData.h @@ -41,6 +41,14 @@ PUBLISHED: INLINE int get_index0() const; INLINE int get_index1() const; + MAKE_PROPERTY(manifold, get_manifold); + MAKE_PROPERTY(node0, get_node0); + MAKE_PROPERTY(node1, get_node1); + MAKE_PROPERTY(part_id0, get_part_id0); + MAKE_PROPERTY(part_id1, get_part_id1); + MAKE_PROPERTY(index0, get_index0); + MAKE_PROPERTY(index1, get_index1); + private: BulletManifoldPoint &_mp; PandaNode *_node0; diff --git a/panda/src/bullet/bulletContactCallbacks.h b/panda/src/bullet/bulletContactCallbacks.h index fa338154f8..27e8093e55 100644 --- a/panda/src/bullet/bulletContactCallbacks.h +++ b/panda/src/bullet/bulletContactCallbacks.h @@ -26,7 +26,7 @@ #include "eventParameter.h" #include "pandaNode.h" -struct UserPersitentData { +struct UserPersistentData { PT(PandaNode) node0; PT(PandaNode) node1; }; @@ -64,7 +64,7 @@ contact_added_callback(btManifoldPoint &cp, bullet_cat.debug() << "contact added: " << cp.m_userPersistentData << endl; // Gather persistent data - UserPersitentData *data = new UserPersitentData(); + UserPersistentData *data = new UserPersistentData(); data->node0 = node0; data->node1 = node1; @@ -126,7 +126,7 @@ contact_destroyed_callback(void *userPersistentData) { bullet_cat.debug() << "contact removed: " << userPersistentData << endl; - UserPersitentData *data = (UserPersitentData *)userPersistentData; + UserPersistentData *data = (UserPersistentData *)userPersistentData; // Send event if (bullet_enable_contact_events) { diff --git a/panda/src/bullet/bulletContactResult.h b/panda/src/bullet/bulletContactResult.h index 3e0e216135..afcf1ea51a 100644 --- a/panda/src/bullet/bulletContactResult.h +++ b/panda/src/bullet/bulletContactResult.h @@ -39,6 +39,14 @@ PUBLISHED: INLINE int get_part_id0() const; INLINE int get_part_id1() const; + MAKE_PROPERTY(manifold_point, get_manifold_point); + MAKE_PROPERTY(node0, get_node0); + MAKE_PROPERTY(node1, get_node1); + MAKE_PROPERTY(idx0, get_idx0); + MAKE_PROPERTY(idx1, get_idx1); + MAKE_PROPERTY(part_id0, get_part_id0); + MAKE_PROPERTY(part_id1, get_part_id1); + private: static btManifoldPoint _empty; @@ -64,6 +72,7 @@ PUBLISHED: INLINE int get_num_contacts() const; INLINE BulletContact get_contact(int idx); MAKE_SEQ(get_contacts, get_num_contacts, get_contact); + MAKE_SEQ_PROPERTY(contacts, get_num_contacts, get_contact); public: #if BT_BULLET_VERSION >= 281 diff --git a/panda/src/bullet/bulletConvexHullShape.cxx b/panda/src/bullet/bulletConvexHullShape.cxx index 5b4494d330..9b3df8dfe2 100644 --- a/panda/src/bullet/bulletConvexHullShape.cxx +++ b/panda/src/bullet/bulletConvexHullShape.cxx @@ -125,6 +125,7 @@ register_with_read_factory() { */ void BulletConvexHullShape:: write_datagram(BamWriter *manager, Datagram &dg) { + BulletShape::write_datagram(manager, dg); dg.add_stdfloat(get_margin()); unsigned int num_points = _shape->getNumPoints(); @@ -161,7 +162,10 @@ make_from_bam(const FactoryParams ¶ms) { */ void BulletConvexHullShape:: fillin(DatagramIterator &scan, BamReader *manager) { - PN_stdfloat margin = scan.get_stdfloat(); + BulletShape::fillin(scan, manager); + nassertv(_shape == nullptr); + + _shape->setMargin(scan.get_stdfloat()); unsigned int num_points = scan.get_uint32(); #if BT_BULLET_VERSION >= 282 diff --git a/panda/src/bullet/bulletConvexPointCloudShape.I b/panda/src/bullet/bulletConvexPointCloudShape.I index aff49d4698..909dfd2d7e 100644 --- a/panda/src/bullet/bulletConvexPointCloudShape.I +++ b/panda/src/bullet/bulletConvexPointCloudShape.I @@ -11,6 +11,15 @@ * @date 2010-01-30 */ +/** + * Only used by make_from_bam. + */ +INLINE BulletConvexPointCloudShape:: +BulletConvexPointCloudShape() : + _scale(1), + _shape(nullptr) { +} + /** * */ @@ -25,6 +34,7 @@ INLINE BulletConvexPointCloudShape:: */ INLINE BulletConvexPointCloudShape:: BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©) : + _scale(copy._scale), _shape(copy._shape) { } @@ -33,6 +43,7 @@ BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©) : */ INLINE void BulletConvexPointCloudShape:: operator = (const BulletConvexPointCloudShape ©) { + _scale = copy._scale; _shape = copy._shape; } diff --git a/panda/src/bullet/bulletConvexPointCloudShape.cxx b/panda/src/bullet/bulletConvexPointCloudShape.cxx index 256357df8c..1527a712b3 100644 --- a/panda/src/bullet/bulletConvexPointCloudShape.cxx +++ b/panda/src/bullet/bulletConvexPointCloudShape.cxx @@ -21,7 +21,8 @@ TypeHandle BulletConvexPointCloudShape::_type_handle; * */ BulletConvexPointCloudShape:: -BulletConvexPointCloudShape(const PTA_LVecBase3 &points, LVecBase3 scale) { +BulletConvexPointCloudShape(const PTA_LVecBase3 &points, LVecBase3 scale) : + _scale(scale) { btVector3 btScale = LVecBase3_to_btVector3(scale); @@ -56,6 +57,7 @@ BulletConvexPointCloudShape:: BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale) { btVector3 btScale = LVecBase3_to_btVector3(scale); + _scale = scale; // Collect points pvector points; @@ -81,3 +83,71 @@ BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale) { _shape = new btConvexPointCloudShape(btPoints, points.size(), btScale); _shape->setUserPointer(this); } + +/** + * Tells the BamReader how to create objects of type BulletShape. + */ +void BulletConvexPointCloudShape:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +/** + * Writes the contents of this object to the datagram for shipping out to a + * Bam file. + */ +void BulletConvexPointCloudShape:: +write_datagram(BamWriter *manager, Datagram &dg) { + BulletShape::write_datagram(manager, dg); + + // parameters to serialize: num points, points, scale + _scale.write_datagram(dg); + + dg.add_int32(get_num_points()); + for (int i = 0; i < get_num_points(); ++i){ + btVector3_to_LVector3(_shape->getUnscaledPoints()[i]).write_datagram(dg); + } +} + +/** + * This function is called by the BamReader's factory when a new object of + * type BulletShape is encountered in the Bam file. It should create the + * BulletShape and extract its information from the file. + */ +TypedWritable *BulletConvexPointCloudShape:: +make_from_bam(const FactoryParams ¶ms) { + // create a default BulletConvexPointCloudShape + BulletConvexPointCloudShape *param = new BulletConvexPointCloudShape; + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + param->fillin(scan, manager); + + return param; +} + +/** + * This internal function is called by make_from_bam to read in all of the + * relevant data from the BamFile for the new BulletShape. + */ +void BulletConvexPointCloudShape:: +fillin(DatagramIterator &scan, BamReader *manager) { + BulletShape::fillin(scan, manager); + + // parameters to serialize: num points, points, scale + _scale.read_datagram(scan); + + unsigned int num_points = scan.get_uint32(); + + btVector3 *btPoints = new btVector3[num_points]; + for (unsigned int i = 0; i < num_points; ++i) { + LPoint3 point; + point.read_datagram(scan); + btPoints[i] = LVecBase3_to_btVector3(point); + } + + // Create shape + _shape = new btConvexPointCloudShape(btPoints, num_points, LVecBase3_to_btVector3(_scale)); + _shape->setUserPointer(this); +} diff --git a/panda/src/bullet/bulletConvexPointCloudShape.h b/panda/src/bullet/bulletConvexPointCloudShape.h index e1199d7a5b..293ece996b 100644 --- a/panda/src/bullet/bulletConvexPointCloudShape.h +++ b/panda/src/bullet/bulletConvexPointCloudShape.h @@ -26,21 +26,35 @@ * */ class EXPCL_PANDABULLET BulletConvexPointCloudShape : public BulletShape { +private: + // Only used by make_from_bam + INLINE BulletConvexPointCloudShape(); PUBLISHED: - BulletConvexPointCloudShape(const PTA_LVecBase3 &points, LVecBase3 scale=LVecBase3(1.)); - BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale=LVecBase3(1.)); + explicit BulletConvexPointCloudShape(const PTA_LVecBase3 &points, LVecBase3 scale=LVecBase3(1.)); + explicit BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale=LVecBase3(1.)); INLINE BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©); INLINE void operator = (const BulletConvexPointCloudShape ©); INLINE ~BulletConvexPointCloudShape(); INLINE int get_num_points() const; + MAKE_PROPERTY(num_points, get_num_points); + public: virtual btCollisionShape *ptr() const; private: btConvexPointCloudShape *_shape; + LVecBase3 _scale; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); public: static TypeHandle get_class_type() { diff --git a/panda/src/bullet/bulletCylinderShape.I b/panda/src/bullet/bulletCylinderShape.I index 990d0f0f68..c389813444 100644 --- a/panda/src/bullet/bulletCylinderShape.I +++ b/panda/src/bullet/bulletCylinderShape.I @@ -11,6 +11,15 @@ * @date 2010-02-17 */ +/** + * Only used by make_from_bam. + */ +INLINE BulletCylinderShape:: +BulletCylinderShape() : + _half_extents(LVector3::zero()), + _shape(nullptr) { +} + /** * */ @@ -25,7 +34,7 @@ INLINE BulletCylinderShape:: */ INLINE BulletCylinderShape:: BulletCylinderShape(const BulletCylinderShape ©) : - _shape(copy._shape) { + _shape(copy._shape), _half_extents(copy._half_extents) { } /** @@ -34,6 +43,7 @@ BulletCylinderShape(const BulletCylinderShape ©) : INLINE void BulletCylinderShape:: operator = (const BulletCylinderShape ©) { _shape = copy._shape; + _half_extents = copy._half_extents; } /** diff --git a/panda/src/bullet/bulletCylinderShape.cxx b/panda/src/bullet/bulletCylinderShape.cxx index 1de1e5d5f0..fec07ce484 100644 --- a/panda/src/bullet/bulletCylinderShape.cxx +++ b/panda/src/bullet/bulletCylinderShape.cxx @@ -19,7 +19,8 @@ TypeHandle BulletCylinderShape::_type_handle; * */ BulletCylinderShape:: -BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up) { +BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up) : + _half_extents(half_extents){ btVector3 btHalfExtents = LVecBase3_to_btVector3(half_extents); @@ -50,12 +51,15 @@ BulletCylinderShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) { switch (up) { case X_up: _shape = new btCylinderShapeX(btVector3(0.5 * height, radius, radius)); + _half_extents = btVector3_to_LVector3(btVector3(0.5 * height, radius, radius)); break; case Y_up: _shape = new btCylinderShape(btVector3(radius, 0.5 * height, radius)); + _half_extents = btVector3_to_LVector3(btVector3(radius, 0.5 * height, radius)); break; case Z_up: _shape = new btCylinderShapeZ(btVector3(radius, radius, 0.5 * height)); + _half_extents = btVector3_to_LVector3(btVector3(radius, radius, 0.5 * height)); break; default: bullet_cat.error() << "invalid up-axis:" << up << endl; @@ -73,3 +77,79 @@ ptr() const { return _shape; } + +/** + * Tells the BamReader how to create objects of type BulletShape. + */ +void BulletCylinderShape:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +/** + * Writes the contents of this object to the datagram for shipping out to a + * Bam file. + */ +void BulletCylinderShape:: +write_datagram(BamWriter *manager, Datagram &dg) { + BulletShape::write_datagram(manager, dg); + dg.add_stdfloat(get_margin()); + + // parameters to serialize: radius, height, up + _half_extents.write_datagram(dg); + dg.add_int8((int8_t)_shape->getUpAxis()); +} + +/** + * This function is called by the BamReader's factory when a new object of + * type BulletShape is encountered in the Bam file. It should create the + * BulletShape and extract its information from the file. + */ +TypedWritable *BulletCylinderShape:: +make_from_bam(const FactoryParams ¶ms) { + // create a default BulletCylinderShape + BulletCylinderShape *param = new BulletCylinderShape; + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + param->fillin(scan, manager); + + return param; +} + +/** + * This internal function is called by make_from_bam to read in all of the + * relevant data from the BamFile for the new BulletShape. + */ +void BulletCylinderShape:: +fillin(DatagramIterator &scan, BamReader *manager) { + BulletShape::fillin(scan, manager); + nassertv(_shape == nullptr); + + PN_stdfloat margin = scan.get_stdfloat(); + + // parameters to serialize: radius, height, up + _half_extents.read_datagram(scan); + int up = (int) scan.get_int8(); + + btVector3 btHalfExtents = LVecBase3_to_btVector3(_half_extents); + + switch (up) { + case X_up: + _shape = new btCylinderShapeX(btHalfExtents); + break; + case Y_up: + _shape = new btCylinderShape(btHalfExtents); + break; + case Z_up: + _shape = new btCylinderShapeZ(btHalfExtents); + break; + default: + bullet_cat.error() << "invalid up-axis:" << up << endl; + break; + } + + _shape->setUserPointer(this); + _shape->setMargin(margin); +} diff --git a/panda/src/bullet/bulletCylinderShape.h b/panda/src/bullet/bulletCylinderShape.h index d4e9beb31f..2cdd854ac4 100644 --- a/panda/src/bullet/bulletCylinderShape.h +++ b/panda/src/bullet/bulletCylinderShape.h @@ -24,10 +24,13 @@ * */ class EXPCL_PANDABULLET BulletCylinderShape : public BulletShape { +private: + // Only used by make_from_bam + INLINE BulletCylinderShape(); PUBLISHED: - BulletCylinderShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); - BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up=Z_up); + explicit BulletCylinderShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); + explicit BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up=Z_up); INLINE BulletCylinderShape(const BulletCylinderShape ©); INLINE void operator = (const BulletCylinderShape ©); INLINE ~BulletCylinderShape(); @@ -36,12 +39,25 @@ PUBLISHED: INLINE LVecBase3 get_half_extents_without_margin() const; INLINE LVecBase3 get_half_extents_with_margin() const; + MAKE_PROPERTY(radius, get_radius); + MAKE_PROPERTY(half_extents_without_margin, get_half_extents_without_margin); + MAKE_PROPERTY(half_extents_with_margin, get_half_extents_with_margin); + public: virtual btCollisionShape *ptr() const; private: + LVector3 _half_extents; btCylinderShape *_shape; +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/bullet/bulletDebugNode.I b/panda/src/bullet/bulletDebugNode.I index 4d4d8aa069..eae681aba5 100644 --- a/panda/src/bullet/bulletDebugNode.I +++ b/panda/src/bullet/bulletDebugNode.I @@ -29,6 +29,15 @@ show_wireframe(bool show) { draw_mask_changed(); } +/** + * + */ +INLINE bool BulletDebugNode:: +get_show_wireframe() const { + + return _wireframe; +} + /** * */ @@ -39,6 +48,15 @@ show_constraints(bool show) { draw_mask_changed(); } +/** + * + */ +INLINE bool BulletDebugNode:: +get_show_constraints() const { + + return _constraints; +} + /** * */ @@ -49,6 +67,15 @@ show_bounding_boxes(bool show) { draw_mask_changed(); } +/** + * + */ +INLINE bool BulletDebugNode:: +get_show_bounding_boxes() const { + + return _bounds; +} + /** * */ @@ -57,3 +84,12 @@ show_normals(bool show) { _drawer._normals = show; } + +/** + * + */ +INLINE bool BulletDebugNode:: +get_show_normals() const { + + return _drawer._normals; +} \ No newline at end of file diff --git a/panda/src/bullet/bulletDebugNode.cxx b/panda/src/bullet/bulletDebugNode.cxx index 196a9af09f..b4575dd3cf 100644 --- a/panda/src/bullet/bulletDebugNode.cxx +++ b/panda/src/bullet/bulletDebugNode.cxx @@ -13,20 +13,25 @@ #include "bulletDebugNode.h" +#include "cullHandler.h" +#include "cullTraverser.h" +#include "cullableObject.h" #include "geomLines.h" #include "geomVertexData.h" #include "geomTriangles.h" #include "geomVertexFormat.h" #include "geomVertexWriter.h" #include "omniBoundingVolume.h" +#include "pStatTimer.h" TypeHandle BulletDebugNode::_type_handle; +PStatCollector BulletDebugNode::_pstat_debug("App:Bullet:DoPhysics:Debug"); /** * */ BulletDebugNode:: -BulletDebugNode(const char *name) : GeomNode(name) { +BulletDebugNode(const char *name) : PandaNode(name), _debug_stale(true) { _wireframe = true; _constraints = true; @@ -37,40 +42,6 @@ BulletDebugNode(const char *name) : GeomNode(name) { set_bounds(bounds); set_final(true); set_overall_hidden(true); - - // Lines - { - PT(GeomVertexData) vdata; - PT(Geom) geom; - PT(GeomLines) prim; - - vdata = new GeomVertexData("", GeomVertexFormat::get_v3c4(), Geom::UH_stream); - - prim = new GeomLines(Geom::UH_stream); - prim->set_shade_model(Geom::SM_uniform); - - geom = new Geom(vdata); - geom->add_primitive(prim); - - add_geom(geom); - } - - // Triangles - { - PT(GeomVertexData) vdata; - PT(Geom) geom; - PT(GeomTriangles) prim; - - vdata = new GeomVertexData("", GeomVertexFormat::get_v3c4(), Geom::UH_stream); - - prim = new GeomTriangles(Geom::UH_stream); - prim->set_shade_model(Geom::SM_uniform); - - geom = new Geom(vdata); - geom->add_primitive(prim); - - add_geom(geom); - } } /** @@ -174,101 +145,134 @@ draw_mask_changed() { } } +/** + * Returns true if there is some value to visiting this particular node during + * the cull traversal for any camera, false otherwise. This will be used to + * optimize the result of get_net_draw_show_mask(), so that any subtrees that + * contain only nodes for which is_renderable() is false need not be visited. + */ +bool BulletDebugNode:: +is_renderable() const { + return true; +} + +/** + * Adds the node's contents to the CullResult we are building up during the + * cull traversal, so that it will be drawn at render time. For most nodes + * other than GeomNodes, this is a do-nothing operation. + */ +void BulletDebugNode:: +add_for_draw(CullTraverser *trav, CullTraverserData &data) { + PT(Geom) debug_lines; + PT(Geom) debug_triangles; + + { + LightMutexHolder holder(_lock); + if (_debug_world == nullptr) { + return; + } + if (_debug_stale) { + nassertv(_debug_world != nullptr); + PStatTimer timer(_pstat_debug); + + // Collect debug geometry data + _drawer._lines.clear(); + _drawer._triangles.clear(); + + _debug_world->debugDrawWorld(); + + // Render lines + { + PT(GeomVertexData) vdata = + new GeomVertexData("", GeomVertexFormat::get_v3c4(), Geom::UH_stream); + vdata->unclean_set_num_rows(_drawer._lines.size() * 2); + + GeomVertexWriter vwriter(vdata, InternalName::get_vertex()); + GeomVertexWriter cwriter(vdata, InternalName::get_color()); + + pvector::const_iterator lit; + for (lit = _drawer._lines.begin(); lit != _drawer._lines.end(); lit++) { + const Line &line = *lit; + + vwriter.set_data3(line._p0); + vwriter.set_data3(line._p1); + cwriter.set_data4(LVecBase4(line._color)); + cwriter.set_data4(LVecBase4(line._color)); + } + + PT(GeomPrimitive) prim = new GeomLines(Geom::UH_stream); + prim->set_shade_model(Geom::SM_uniform); + prim->add_next_vertices(_drawer._lines.size() * 2); + + debug_lines = new Geom(vdata); + debug_lines->add_primitive(prim); + _debug_lines = debug_lines; + } + + // Render triangles + { + PT(GeomVertexData) vdata = + new GeomVertexData("", GeomVertexFormat::get_v3c4(), Geom::UH_stream); + vdata->unclean_set_num_rows(_drawer._triangles.size() * 3); + + GeomVertexWriter vwriter(vdata, InternalName::get_vertex()); + GeomVertexWriter cwriter(vdata, InternalName::get_color()); + + pvector::const_iterator tit; + for (tit = _drawer._triangles.begin(); tit != _drawer._triangles.end(); tit++) { + const Triangle &tri = *tit; + + vwriter.set_data3(tri._p0); + vwriter.set_data3(tri._p1); + vwriter.set_data3(tri._p2); + cwriter.set_data4(LVecBase4(tri._color)); + cwriter.set_data4(LVecBase4(tri._color)); + cwriter.set_data4(LVecBase4(tri._color)); + } + + PT(GeomPrimitive) prim = new GeomTriangles(Geom::UH_stream); + prim->set_shade_model(Geom::SM_uniform); + prim->add_next_vertices(_drawer._triangles.size() * 3); + + debug_triangles = new Geom(vdata); + debug_triangles->add_primitive(prim); + _debug_triangles = debug_triangles; + } + + // Clear collected data. + _drawer._lines.clear(); + _drawer._triangles.clear(); + + _debug_stale = false; + } else { + debug_lines = _debug_lines; + debug_triangles = _debug_triangles; + } + } + + // Record them without any state or transform. + trav->_geoms_pcollector.add_level(2); + { + CullableObject *object = + new CullableObject(move(debug_lines), RenderState::make_empty(), trav->get_scene()->get_cs_world_transform()); + trav->get_cull_handler()->record_object(object, trav); + } + { + CullableObject *object = + new CullableObject(move(debug_triangles), RenderState::make_empty(), trav->get_scene()->get_cs_world_transform()); + trav->get_cull_handler()->record_object(object, trav); + } +} + /** * */ void BulletDebugNode:: sync_b2p(btDynamicsWorld *world) { + LightMutexHolder holder(_lock); - if (is_overall_hidden()) return; - - nassertv(get_num_geoms() == 2); - - // Collect debug geometry data - _drawer._lines.clear(); - _drawer._triangles.clear(); - - world->debugDrawWorld(); - - // Get inverse of this node's net transform - NodePath np = NodePath::any_path((PandaNode *)this); - LMatrix4 m = np.get_net_transform()->get_mat(); - m.invert_in_place(); - - // Render lines - { - PT(GeomVertexData) vdata; - PT(Geom) geom; - PT(GeomLines) prim; - - vdata = new GeomVertexData("", GeomVertexFormat::get_v3c4(), Geom::UH_stream); - - prim = new GeomLines(Geom::UH_stream); - prim->set_shade_model(Geom::SM_uniform); - - GeomVertexWriter vwriter = GeomVertexWriter(vdata, InternalName::get_vertex()); - GeomVertexWriter cwriter = GeomVertexWriter(vdata, InternalName::get_color()); - - int v = 0; - - pvector::const_iterator lit; - for (lit = _drawer._lines.begin(); lit != _drawer._lines.end(); lit++) { - Line line = *lit; - - vwriter.add_data3(m.xform_point(line._p0)); - vwriter.add_data3(m.xform_point(line._p1)); - cwriter.add_data4(LVecBase4(line._color)); - cwriter.add_data4(LVecBase4(line._color)); - - prim->add_vertex(v++); - prim->add_vertex(v++); - prim->close_primitive(); - } - - geom = new Geom(vdata); - geom->add_primitive(prim); - - set_geom(0, geom); - } - - // Render triangles - { - PT(GeomVertexData) vdata; - PT(Geom) geom; - PT(GeomTriangles) prim; - - vdata = new GeomVertexData("", GeomVertexFormat::get_v3c4(), Geom::UH_stream); - - prim = new GeomTriangles(Geom::UH_stream); - prim->set_shade_model(Geom::SM_uniform); - - GeomVertexWriter vwriter = GeomVertexWriter(vdata, InternalName::get_vertex()); - GeomVertexWriter cwriter = GeomVertexWriter(vdata, InternalName::get_color()); - - int v = 0; - - pvector::const_iterator tit; - for (tit = _drawer._triangles.begin(); tit != _drawer._triangles.end(); tit++) { - Triangle tri = *tit; - - vwriter.add_data3(m.xform_point(tri._p0)); - vwriter.add_data3(m.xform_point(tri._p1)); - vwriter.add_data3(m.xform_point(tri._p2)); - cwriter.add_data4(LVecBase4(tri._color)); - cwriter.add_data4(LVecBase4(tri._color)); - cwriter.add_data4(LVecBase4(tri._color)); - - prim->add_vertex(v++); - prim->add_vertex(v++); - prim->add_vertex(v++); - prim->close_primitive(); - } - - geom = new Geom(vdata); - geom->add_primitive(prim); - - set_geom(1, geom); - } + _debug_world = world; + _debug_stale = true; } /** @@ -431,8 +435,6 @@ register_with_read_factory() { */ void BulletDebugNode:: write_datagram(BamWriter *manager, Datagram &dg) { - // Don't upcall to GeomNode since we're not interested in storing the actual - // debug Geoms in the .bam file. PandaNode::write_datagram(manager, dg); dg.add_bool(_wireframe); @@ -464,8 +466,6 @@ make_from_bam(const FactoryParams ¶ms) { */ void BulletDebugNode:: fillin(DatagramIterator &scan, BamReader *manager) { - // Don't upcall to GeomNode since we're not interested in storing the actual - // debug Geoms in the .bam file. PandaNode::fillin(scan, manager); _wireframe = scan.get_bool(); diff --git a/panda/src/bullet/bulletDebugNode.h b/panda/src/bullet/bulletDebugNode.h index 5104438381..dabe9d13fa 100644 --- a/panda/src/bullet/bulletDebugNode.h +++ b/panda/src/bullet/bulletDebugNode.h @@ -17,16 +17,14 @@ #include "pandabase.h" #include "bullet_includes.h" - -#include "geomNode.h" +#include "lightMutex.h" /** * */ -class EXPCL_PANDABULLET BulletDebugNode : public GeomNode { - +class EXPCL_PANDABULLET BulletDebugNode : public PandaNode { PUBLISHED: - BulletDebugNode(const char *name="debug"); + explicit BulletDebugNode(const char *name="debug"); INLINE ~BulletDebugNode(); virtual void draw_mask_changed(); @@ -35,6 +33,15 @@ PUBLISHED: INLINE void show_constraints(bool show); INLINE void show_bounding_boxes(bool show); INLINE void show_normals(bool show); + INLINE bool get_show_wireframe() const; + INLINE bool get_show_constraints() const; + INLINE bool get_show_bounding_boxes() const; + INLINE bool get_show_normals() const; + + MAKE_PROPERTY(wireframe, get_show_wireframe, show_wireframe); + MAKE_PROPERTY(constraints, get_show_constraints, show_constraints); + MAKE_PROPERTY(bounding_boxes, get_show_bounding_boxes, show_bounding_boxes); + MAKE_PROPERTY(normals, get_show_normals, show_normals); public: virtual bool safe_to_flatten() const; @@ -44,6 +51,9 @@ public: virtual bool safe_to_combine_children() const; virtual bool safe_to_flatten_below() const; + virtual bool is_renderable() const; + virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); + private: void sync_b2p(btDynamicsWorld *world); @@ -91,14 +101,22 @@ private: int _mode; }; + LightMutex _lock; DebugDraw _drawer; + bool _debug_stale; + btDynamicsWorld *_debug_world; + PT(Geom) _debug_lines; + PT(Geom) _debug_triangles; + bool _wireframe; bool _constraints; bool _bounds; friend class BulletWorld; + static PStatCollector _pstat_debug; + public: static void register_with_read_factory(); virtual void write_datagram(BamWriter *manager, Datagram &dg); @@ -112,9 +130,9 @@ public: return _type_handle; } static void init_type() { - GeomNode::init_type(); + PandaNode::init_type(); register_type(_type_handle, "BulletDebugNode", - GeomNode::get_class_type()); + PandaNode::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); diff --git a/panda/src/bullet/bulletFilterCallbackData.h b/panda/src/bullet/bulletFilterCallbackData.h index 00a244cff8..36e3117716 100644 --- a/panda/src/bullet/bulletFilterCallbackData.h +++ b/panda/src/bullet/bulletFilterCallbackData.h @@ -36,6 +36,10 @@ PUBLISHED: INLINE void set_collide(bool collide); INLINE bool get_collide() const; + MAKE_PROPERTY(node_0, get_node_0); + MAKE_PROPERTY(node_1, get_node_1); + MAKE_PROPERTY(collide, get_collide, set_collide); + private: PandaNode *_node0; PandaNode *_node1; diff --git a/panda/src/bullet/bulletGenericConstraint.h b/panda/src/bullet/bulletGenericConstraint.h index c43c2217be..a2446ffcd4 100644 --- a/panda/src/bullet/bulletGenericConstraint.h +++ b/panda/src/bullet/bulletGenericConstraint.h @@ -31,16 +31,15 @@ class BulletRigidBodyNode; * */ class EXPCL_PANDABULLET BulletGenericConstraint : public BulletConstraint { - PUBLISHED: - BulletGenericConstraint(const BulletRigidBodyNode *node_a, - const TransformState *frame_a, - bool use_frame_a); - BulletGenericConstraint(const BulletRigidBodyNode *node_a, - const BulletRigidBodyNode *node_b, - const TransformState *frame_a, - const TransformState *frame_b, - bool use_frame_a); + explicit BulletGenericConstraint(const BulletRigidBodyNode *node_a, + const TransformState *frame_a, + bool use_frame_a); + explicit BulletGenericConstraint(const BulletRigidBodyNode *node_a, + const BulletRigidBodyNode *node_b, + const TransformState *frame_a, + const TransformState *frame_b, + bool use_frame_a); INLINE ~BulletGenericConstraint(); // Geometry @@ -61,6 +60,10 @@ PUBLISHED: INLINE CPT(TransformState) get_frame_a() const; INLINE CPT(TransformState) get_frame_b() const; + MAKE_PROPERTY(translational_limit_motor, get_translational_limit_motor); + MAKE_PROPERTY(frame_a, get_frame_a); + MAKE_PROPERTY(frame_b, get_frame_b); + public: virtual btTypedConstraint *ptr() const; diff --git a/panda/src/bullet/bulletGhostNode.h b/panda/src/bullet/bulletGhostNode.h index 3e8f2b6465..87a1624f3d 100644 --- a/panda/src/bullet/bulletGhostNode.h +++ b/panda/src/bullet/bulletGhostNode.h @@ -29,15 +29,16 @@ class BulletShape; * */ class EXPCL_PANDABULLET BulletGhostNode : public BulletBodyNode { - PUBLISHED: - BulletGhostNode(const char *name="ghost"); + explicit BulletGhostNode(const char *name="ghost"); INLINE ~BulletGhostNode(); // Overlapping INLINE int get_num_overlapping_nodes() const; INLINE PandaNode *get_overlapping_node(int idx) const; MAKE_SEQ(get_overlapping_nodes, get_num_overlapping_nodes, get_overlapping_node); + + MAKE_SEQ_PROPERTY(overlapping_nodes, get_num_overlapping_nodes, get_overlapping_node); public: virtual btCollisionObject *get_object() const; diff --git a/panda/src/bullet/bulletHeightfieldShape.I b/panda/src/bullet/bulletHeightfieldShape.I index 6607443110..5bfceb1e5b 100644 --- a/panda/src/bullet/bulletHeightfieldShape.I +++ b/panda/src/bullet/bulletHeightfieldShape.I @@ -11,12 +11,24 @@ * @date 2010-02-05 */ +/** + * Only used by make_from_bam + */ +INLINE BulletHeightfieldShape:: +BulletHeightfieldShape() : + _num_rows(0), + _num_cols(0), + _data(nullptr), + _shape(nullptr), + _max_height(0.0), + _up(Z_up) { +} + /** * */ INLINE BulletHeightfieldShape:: ~BulletHeightfieldShape() { - delete _shape; delete [] _data; } @@ -28,10 +40,13 @@ INLINE BulletHeightfieldShape:: BulletHeightfieldShape(const BulletHeightfieldShape ©) : _shape(copy._shape), _num_rows(copy._num_rows), - _num_cols(copy._num_cols) { + _num_cols(copy._num_cols), + _max_height(copy._max_height), + _up(copy._up) { - _data = new float[_num_rows * _num_cols]; - memcpy(_data, copy._data, _num_rows * _num_cols * sizeof(float)); + size_t size = (size_t)_num_rows * (size_t)_num_cols; + _data = new btScalar[size]; + memcpy(_data, copy._data, size * sizeof(btScalar)); } /** @@ -39,11 +54,11 @@ BulletHeightfieldShape(const BulletHeightfieldShape ©) : */ INLINE void BulletHeightfieldShape:: operator = (const BulletHeightfieldShape ©) { - _shape = copy._shape; _num_rows = copy._num_rows; _num_cols = copy._num_cols; - _data = new float[_num_rows * _num_cols]; - memcpy(_data, copy._data, _num_rows * _num_cols * sizeof(float)); + size_t size = (size_t)_num_rows * (size_t)_num_cols; + _data = new btScalar[size]; + memcpy(_data, copy._data, size * sizeof(btScalar)); } diff --git a/panda/src/bullet/bulletHeightfieldShape.cxx b/panda/src/bullet/bulletHeightfieldShape.cxx index f6843d199b..6ff476c0a6 100644 --- a/panda/src/bullet/bulletHeightfieldShape.cxx +++ b/panda/src/bullet/bulletHeightfieldShape.cxx @@ -21,12 +21,13 @@ TypeHandle BulletHeightfieldShape::_type_handle; * while rotating it 90 degrees to the right. */ BulletHeightfieldShape:: -BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up) { +BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up) : + _max_height(max_height), _up(up) { _num_rows = image.get_x_size(); _num_cols = image.get_y_size(); - _data = new float[_num_rows * _num_cols]; + _data = new btScalar[_num_rows * _num_cols]; for (int row=0; row < _num_rows; row++) { for (int column=0; column < _num_cols; column++) { @@ -71,14 +72,15 @@ set_use_diamond_subdivision(bool flag) { * that are non-power-of-two and/or rectangular. */ BulletHeightfieldShape:: -BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up) { +BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up) : + _max_height(max_height), _up(up) { _num_rows = tex->get_x_size() + 1; _num_cols = tex->get_y_size() + 1; - _data = new float[_num_rows * _num_cols]; + _data = new btScalar[_num_rows * _num_cols]; - PN_stdfloat step_x = 1.0 / (PN_stdfloat)tex->get_x_size(); - PN_stdfloat step_y = 1.0 / (PN_stdfloat)tex->get_y_size(); + btScalar step_x = 1.0 / (btScalar)tex->get_x_size(); + btScalar step_y = 1.0 / (btScalar)tex->get_y_size(); PT(TexturePeeker) peeker = tex->peek(); LColor sample; @@ -100,4 +102,85 @@ BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up) { up, true, false); _shape->setUserPointer(this); -} \ No newline at end of file +} + +/** + * Tells the BamReader how to create objects of type BulletShape. + */ +void BulletHeightfieldShape:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +/** + * Writes the contents of this object to the datagram for shipping out to a + * Bam file. + */ +void BulletHeightfieldShape:: +write_datagram(BamWriter *manager, Datagram &dg) { + BulletShape::write_datagram(manager, dg); + dg.add_stdfloat(get_margin()); + + // parameters to serialize:_num_rows,_num_cols,_data,max_height,up, + dg.add_int8((int8_t)_up); + dg.add_stdfloat(_max_height); + dg.add_int32(_num_rows); + dg.add_int32(_num_cols); + + size_t size = (size_t)_num_rows * (size_t)_num_cols; + for (size_t i = 0; i < size; ++i) { + dg.add_stdfloat(_data[i]); + } +} + +/** + * This function is called by the BamReader's factory when a new object of + * type BulletShape is encountered in the Bam file. It should create the + * BulletShape and extract its information from the file. + */ +TypedWritable *BulletHeightfieldShape:: +make_from_bam(const FactoryParams ¶ms) { + // create a default BulletHeightfieldShape + BulletHeightfieldShape *param = new BulletHeightfieldShape; + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + param->fillin(scan, manager); + + return param; +} + +/** + * This internal function is called by make_from_bam to read in all of the + * relevant data from the BamFile for the new BulletShape. + */ +void BulletHeightfieldShape:: +fillin(DatagramIterator &scan, BamReader *manager) { + BulletShape::fillin(scan, manager); + nassertv(_shape == nullptr); + + PN_stdfloat margin = scan.get_stdfloat(); + + // parameters to serialize: radius, height, up + _up = (BulletUpAxis) scan.get_int8(); + _max_height = scan.get_stdfloat(); + _num_rows = scan.get_int32(); + _num_cols = scan.get_int32(); + + size_t size = (size_t)_num_rows * (size_t)_num_cols; + delete[] _data; + _data = new float[size]; + for (size_t i = 0; i < size; ++i) { + _data[i] = scan.get_stdfloat(); + } + + _shape = new btHeightfieldTerrainShape(_num_rows, + _num_cols, + _data, + _max_height, + _up, + true, false); + _shape->setUserPointer(this); + _shape->setMargin(margin); +} diff --git a/panda/src/bullet/bulletHeightfieldShape.h b/panda/src/bullet/bulletHeightfieldShape.h index c8bf6f7730..2f70fddac8 100644 --- a/panda/src/bullet/bulletHeightfieldShape.h +++ b/panda/src/bullet/bulletHeightfieldShape.h @@ -28,10 +28,12 @@ * */ class EXPCL_PANDABULLET BulletHeightfieldShape : public BulletShape { +private: + INLINE BulletHeightfieldShape(); PUBLISHED: - BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up=Z_up); - BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up=Z_up); + explicit BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up=Z_up); + explicit BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up=Z_up); INLINE BulletHeightfieldShape(const BulletHeightfieldShape ©); INLINE void operator = (const BulletHeightfieldShape ©); INLINE ~BulletHeightfieldShape(); @@ -44,8 +46,18 @@ public: private: int _num_rows; int _num_cols; - float *_data; + btScalar *_data; btHeightfieldTerrainShape *_shape; + PN_stdfloat _max_height; + BulletUpAxis _up; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); public: static TypeHandle get_class_type() { diff --git a/panda/src/bullet/bulletHelper.h b/panda/src/bullet/bulletHelper.h index 5b3aaf6152..c63f70f111 100644 --- a/panda/src/bullet/bulletHelper.h +++ b/panda/src/bullet/bulletHelper.h @@ -51,6 +51,9 @@ PUBLISHED: static void make_texcoords_for_patch(Geom *geom, int resx, int resy); + MAKE_PROPERTY(sb_index, get_sb_index); + MAKE_PROPERTY(sb_flip, get_sb_flip); + private: static PT(InternalName) _sb_index; static PT(InternalName) _sb_flip; diff --git a/panda/src/bullet/bulletHingeConstraint.h b/panda/src/bullet/bulletHingeConstraint.h index e5d5228454..12a1b1b2c2 100644 --- a/panda/src/bullet/bulletHingeConstraint.h +++ b/panda/src/bullet/bulletHingeConstraint.h @@ -29,28 +29,27 @@ class BulletRigidBodyNode; * adhering to specified limits. It's motor can apply angular force to them. */ class EXPCL_PANDABULLET BulletHingeConstraint : public BulletConstraint { - PUBLISHED: - BulletHingeConstraint(const BulletRigidBodyNode *node_a, - const LPoint3 &pivot_a, - const LVector3 &axis_a, - bool use_frame_a=false); - BulletHingeConstraint(const BulletRigidBodyNode *node_a, - const BulletRigidBodyNode *node_b, - const LPoint3 &pivot_a, - const LPoint3 &pivot_b, - const LVector3 &axis_a, - const LVector3 &axis_b, - bool use_frame_a=false); + explicit BulletHingeConstraint(const BulletRigidBodyNode *node_a, + const LPoint3 &pivot_a, + const LVector3 &axis_a, + bool use_frame_a=false); + explicit BulletHingeConstraint(const BulletRigidBodyNode *node_a, + const BulletRigidBodyNode *node_b, + const LPoint3 &pivot_a, + const LPoint3 &pivot_b, + const LVector3 &axis_a, + const LVector3 &axis_b, + bool use_frame_a=false); - BulletHingeConstraint(const BulletRigidBodyNode *node_a, - const TransformState *ts_a, - bool use_frame_a=false); - BulletHingeConstraint(const BulletRigidBodyNode *node_a, - const BulletRigidBodyNode *node_b, - const TransformState *ts_a, - const TransformState *ts_b, - bool use_frame_a=false); + explicit BulletHingeConstraint(const BulletRigidBodyNode *node_a, + const TransformState *ts_a, + bool use_frame_a=false); + explicit BulletHingeConstraint(const BulletRigidBodyNode *node_a, + const BulletRigidBodyNode *node_b, + const TransformState *ts_a, + const TransformState *ts_b, + bool use_frame_a=false); INLINE ~BulletHingeConstraint(); @@ -73,6 +72,13 @@ PUBLISHED: INLINE CPT(TransformState) get_frame_a() const; INLINE CPT(TransformState) get_frame_b() const; + MAKE_PROPERTY(hinge_angle, get_hinge_angle); + MAKE_PROPERTY(lower_limit, get_lower_limit); + MAKE_PROPERTY(upper_limit, get_upper_limit); + MAKE_PROPERTY(angular_only, get_angular_only, set_angular_only); + MAKE_PROPERTY(frame_a, get_frame_a); + MAKE_PROPERTY(frame_b, get_frame_b); + public: virtual btTypedConstraint *ptr() const; diff --git a/panda/src/bullet/bulletManifoldPoint.h b/panda/src/bullet/bulletManifoldPoint.h index 4184ed9d97..a7bc271599 100644 --- a/panda/src/bullet/bulletManifoldPoint.h +++ b/panda/src/bullet/bulletManifoldPoint.h @@ -68,6 +68,30 @@ PUBLISHED: INLINE PN_stdfloat get_contact_cfm1() const; INLINE PN_stdfloat get_contact_cfm2() const; + MAKE_PROPERTY(life_time, get_life_time); + MAKE_PROPERTY(distance, get_distance); + MAKE_PROPERTY(applied_impulse, get_applied_impulse, set_applied_impulse); + MAKE_PROPERTY(position_world_on_a, get_position_world_on_a); + MAKE_PROPERTY(position_world_on_b, get_position_world_on_b); + MAKE_PROPERTY(normal_world_on_b, get_normal_world_on_b); + MAKE_PROPERTY(local_point_a, get_local_point_a); + MAKE_PROPERTY(local_point_b, get_local_point_b); + MAKE_PROPERTY(part_id0, get_part_id0); + MAKE_PROPERTY(part_id1, get_part_id1); + MAKE_PROPERTY(index0, get_index0); + MAKE_PROPERTY(index1, get_index1); + MAKE_PROPERTY(lateral_friction_initialized, get_lateral_friction_initialized, set_lateral_friction_initialized); + MAKE_PROPERTY(lateral_friction_dir1, get_lateral_friction_dir1, set_lateral_friction_dir1); + MAKE_PROPERTY(lateral_friction_dir2, get_lateral_friction_dir2, set_lateral_friction_dir2); + MAKE_PROPERTY(contact_motion1, get_contact_motion1, set_contact_motion1); + MAKE_PROPERTY(contact_motion2, get_contact_motion2, set_contact_motion2); + MAKE_PROPERTY(combined_friction, get_combined_friction, set_combined_friction); + MAKE_PROPERTY(combined_restitution, get_combined_restitution, set_combined_restitution); + MAKE_PROPERTY(applied_impulse_lateral1, get_applied_impulse_lateral1, set_applied_impulse_lateral1); + MAKE_PROPERTY(applied_impulse_lateral2, get_applied_impulse_lateral2, set_applied_impulse_lateral2); + MAKE_PROPERTY(contact_cfm1, get_contact_cfm1, set_contact_cfm1); + MAKE_PROPERTY(contact_cfm2, get_contact_cfm2, set_contact_cfm2); + public: BulletManifoldPoint(btManifoldPoint &pt); diff --git a/panda/src/bullet/bulletMinkowskiSumShape.I b/panda/src/bullet/bulletMinkowskiSumShape.I index 07f96a74fb..ed4a356f9a 100644 --- a/panda/src/bullet/bulletMinkowskiSumShape.I +++ b/panda/src/bullet/bulletMinkowskiSumShape.I @@ -11,6 +11,16 @@ * @date 2010-01-23 */ +/** + * Only used by make_from_bam. + */ +INLINE BulletMinkowskiSumShape:: +BulletMinkowskiSumShape() : + _shape(nullptr), + _shape_a(nullptr), + _shape_b(nullptr) { +} + /** * */ @@ -95,12 +105,3 @@ get_shape_b() const { return _shape_b; } - -/** - * - */ -INLINE PN_stdfloat BulletMinkowskiSumShape:: -get_margin() const { - - return (PN_stdfloat)_shape->getMargin(); -} diff --git a/panda/src/bullet/bulletMinkowskiSumShape.cxx b/panda/src/bullet/bulletMinkowskiSumShape.cxx index a4382eee52..3dc618adac 100644 --- a/panda/src/bullet/bulletMinkowskiSumShape.cxx +++ b/panda/src/bullet/bulletMinkowskiSumShape.cxx @@ -19,7 +19,9 @@ TypeHandle BulletMinkowskiSumShape::_type_handle; * */ BulletMinkowskiSumShape:: -BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b) { +BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b) : + _shape_a(shape_a), + _shape_b(shape_b) { nassertv(shape_a->is_convex()); nassertv(shape_b->is_convex()); @@ -29,9 +31,6 @@ BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b) _shape = new btMinkowskiSumShape(ptr_a, ptr_b); _shape->setUserPointer(this); - - _shape_a = shape_a; - _shape_b = shape_b; } /** @@ -42,3 +41,102 @@ ptr() const { return _shape; } + +/** + * Tells the BamReader how to create objects of type BulletShape. + */ +void BulletMinkowskiSumShape:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +/** + * Writes the contents of this object to the datagram for shipping out to a + * Bam file. + */ +void BulletMinkowskiSumShape:: +write_datagram(BamWriter *manager, Datagram &dg) { + BulletShape::write_datagram(manager, dg); + dg.add_stdfloat(get_margin()); + + // parameters to serialize: _shape_a, _shape_b, _transform_a, _transform_b + manager->write_pointer(dg, _shape_a); + manager->write_pointer(dg, _shape_b); + manager->write_pointer(dg, get_transform_a()); + manager->write_pointer(dg, get_transform_b()); +} + +/** + * Receives an array of pointers, one for each time manager->read_pointer() + * was called in fillin(). Returns the number of pointers processed. + */ +int BulletMinkowskiSumShape:: +complete_pointers(TypedWritable **p_list, BamReader *manager) { + int pi = BulletShape::complete_pointers(p_list, manager); + + _shape_a = DCAST(BulletShape, p_list[pi++]); + _shape_b = DCAST(BulletShape, p_list[pi++]); + + const TransformState *transform_a = DCAST(TransformState, p_list[pi++]); + const TransformState *transform_b = DCAST(TransformState, p_list[pi++]); + + const btConvexShape *ptr_a = (const btConvexShape *)_shape_a->ptr(); + const btConvexShape *ptr_b = (const btConvexShape *)_shape_b->ptr(); + + _shape = new btMinkowskiSumShape(ptr_a, ptr_b); + _shape->setUserPointer(this); + _shape->setMargin(_margin); + + set_transform_a(transform_a); + set_transform_b(transform_b); + + return pi; +} + +/** + * Some objects require all of their nested pointers to have been completed + * before the objects themselves can be completed. If this is the case, + * override this method to return true, and be careful with circular + * references (which would make the object unreadable from a bam file). + */ +bool BulletMinkowskiSumShape:: +require_fully_complete() const { + // We require the shape pointers to be complete before we add them. + return true; +} + +/** + * This function is called by the BamReader's factory when a new object of + * type BulletShape is encountered in the Bam file. It should create the + * BulletShape and extract its information from the file. + */ +TypedWritable *BulletMinkowskiSumShape:: +make_from_bam(const FactoryParams ¶ms) { + // create a default BulletMinkowskiSumShape + BulletMinkowskiSumShape *param = new BulletMinkowskiSumShape; + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + param->fillin(scan, manager); + + return param; +} + +/** + * This internal function is called by make_from_bam to read in all of the + * relevant data from the BamFile for the new BulletShape. + */ +void BulletMinkowskiSumShape:: +fillin(DatagramIterator &scan, BamReader *manager) { + BulletShape::fillin(scan, manager); + nassertv(_shape == nullptr); + + _margin = scan.get_stdfloat(); + + // parameters to serialize: _shape_a, _shape_b, _transform_a, _transform_b + manager->read_pointer(scan); + manager->read_pointer(scan); + manager->read_pointer(scan); + manager->read_pointer(scan); +} diff --git a/panda/src/bullet/bulletMinkowskiSumShape.h b/panda/src/bullet/bulletMinkowskiSumShape.h index 3b6aa6bc1f..2e66ab9cae 100644 --- a/panda/src/bullet/bulletMinkowskiSumShape.h +++ b/panda/src/bullet/bulletMinkowskiSumShape.h @@ -26,9 +26,12 @@ * */ class EXPCL_PANDABULLET BulletMinkowskiSumShape : public BulletShape { +private: + // Only used by make_from_bam + INLINE BulletMinkowskiSumShape(); PUBLISHED: - BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b); + explicit BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b); INLINE BulletMinkowskiSumShape(const BulletMinkowskiSumShape ©); INLINE void operator = (const BulletMinkowskiSumShape ©); INLINE ~BulletMinkowskiSumShape(); @@ -41,7 +44,10 @@ PUBLISHED: INLINE const BulletShape *get_shape_a() const; INLINE const BulletShape *get_shape_b() const; - INLINE PN_stdfloat get_margin() const; + MAKE_PROPERTY(transform_a, get_transform_a, set_transform_a); + MAKE_PROPERTY(transform_b, get_transform_b, set_transform_b); + MAKE_PROPERTY(shape_a, get_shape_a); + MAKE_PROPERTY(shape_b, get_shape_b); public: virtual btCollisionShape *ptr() const; @@ -52,6 +58,20 @@ private: CPT(BulletShape) _shape_a; CPT(BulletShape) _shape_b; + // This is stored temporarily during read. + PN_stdfloat _margin; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + virtual int complete_pointers(TypedWritable **plist, + BamReader *manager); + virtual bool require_fully_complete() const; + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/bullet/bulletMultiSphereShape.cxx b/panda/src/bullet/bulletMultiSphereShape.cxx index 52e2f16c7d..9defaedc51 100644 --- a/panda/src/bullet/bulletMultiSphereShape.cxx +++ b/panda/src/bullet/bulletMultiSphereShape.cxx @@ -50,3 +50,79 @@ ptr() const { return _shape; } + +/** + * Tells the BamReader how to create objects of type BulletShape. + */ +void BulletMultiSphereShape:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +/** + * Writes the contents of this object to the datagram for shipping out to a + * Bam file. + */ +void BulletMultiSphereShape:: +write_datagram(BamWriter *manager, Datagram &dg) { + BulletShape::write_datagram(manager, dg); + dg.add_stdfloat(get_margin()); + + // parameters to serialize: sphere count, points, radii + dg.add_int32(get_sphere_count()); + for (int i = 0; i < get_sphere_count(); ++i){ + get_sphere_pos(i).write_datagram(dg); + } + + for (int i = 0; i < get_sphere_count(); ++i){ + dg.add_stdfloat(get_sphere_radius(i)); + } +} + +/** + * This function is called by the BamReader's factory when a new object of + * type BulletShape is encountered in the Bam file. It should create the + * BulletShape and extract its information from the file. + */ +TypedWritable *BulletMultiSphereShape:: +make_from_bam(const FactoryParams ¶ms) { + // create a default BulletMultiSphereShape + BulletMultiSphereShape *param = new BulletMultiSphereShape; + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + param->fillin(scan, manager); + + return param; +} + +/** + * This internal function is called by make_from_bam to read in all of the + * relevant data from the BamFile for the new BulletShape. + */ +void BulletMultiSphereShape:: +fillin(DatagramIterator &scan, BamReader *manager) { + BulletShape::fillin(scan, manager); + nassertv(_shape == nullptr); + + PN_stdfloat margin = scan.get_stdfloat(); + + // parameters to serialize: sphere count, points, radii + int sphereCount = scan.get_int32(); + btVector3 *positions = new btVector3[sphereCount]; + for (int i = 0; i < sphereCount; ++i){ + LVector3 pos; + pos.read_datagram(scan); + positions[i] = LVecBase3_to_btVector3(pos); + } + + btScalar *radii = new btScalar[sphereCount]; + for (int i = 0; i < sphereCount; ++i){ + radii[i] = scan.get_stdfloat(); + } + + _shape = new btMultiSphereShape(positions, radii, sphereCount); + _shape->setUserPointer(this); + _shape->setMargin(margin); +} diff --git a/panda/src/bullet/bulletMultiSphereShape.h b/panda/src/bullet/bulletMultiSphereShape.h index b3a55bd5e3..11d3ee936f 100644 --- a/panda/src/bullet/bulletMultiSphereShape.h +++ b/panda/src/bullet/bulletMultiSphereShape.h @@ -26,9 +26,11 @@ * */ class EXPCL_PANDABULLET BulletMultiSphereShape : public BulletShape { +private: + BulletMultiSphereShape() : _shape(nullptr) {} PUBLISHED: - BulletMultiSphereShape(const PTA_LVecBase3 &points, const PTA_stdfloat &radii); + explicit BulletMultiSphereShape(const PTA_LVecBase3 &points, const PTA_stdfloat &radii); INLINE BulletMultiSphereShape(const BulletMultiSphereShape ©); INLINE void operator = (const BulletMultiSphereShape ©); INLINE ~BulletMultiSphereShape(); @@ -37,12 +39,24 @@ PUBLISHED: INLINE LPoint3 get_sphere_pos(int index) const; INLINE PN_stdfloat get_sphere_radius(int index) const; + MAKE_PROPERTY(sphere_count, get_sphere_count); + MAKE_SEQ_PROPERTY(sphere_pos, get_sphere_count, get_sphere_pos); + MAKE_SEQ_PROPERTY(sphere_radius, get_sphere_count, get_sphere_radius); + public: virtual btCollisionShape *ptr() const; private: btMultiSphereShape *_shape; +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/bullet/bulletPersistentManifold.h b/panda/src/bullet/bulletPersistentManifold.h index 2878d0b7ed..25feb57493 100644 --- a/panda/src/bullet/bulletPersistentManifold.h +++ b/panda/src/bullet/bulletPersistentManifold.h @@ -42,6 +42,12 @@ PUBLISHED: void clear_manifold(); + MAKE_PROPERTY(node0, get_node0); + MAKE_PROPERTY(node1, get_node1); + MAKE_SEQ_PROPERTY(manifold_points, get_num_manifold_points, get_manifold_point); + MAKE_PROPERTY(contact_breaking_threshold, get_contact_breaking_threshold); + MAKE_PROPERTY(contact_processing_threshold, get_contact_processing_threshold); + public: BulletPersistentManifold(btPersistentManifold *manifold); diff --git a/panda/src/bullet/bulletPlaneShape.cxx b/panda/src/bullet/bulletPlaneShape.cxx index 5b74f0caae..2b410fb0e9 100644 --- a/panda/src/bullet/bulletPlaneShape.cxx +++ b/panda/src/bullet/bulletPlaneShape.cxx @@ -62,6 +62,8 @@ register_with_read_factory() { */ void BulletPlaneShape:: write_datagram(BamWriter *manager, Datagram &dg) { + BulletShape::write_datagram(manager, dg); + dg.add_stdfloat(get_margin()); get_plane_normal().write_datagram(dg); dg.add_stdfloat(get_plane_constant()); @@ -90,7 +92,8 @@ make_from_bam(const FactoryParams ¶ms) { */ void BulletPlaneShape:: fillin(DatagramIterator &scan, BamReader *manager) { - nassertv(_shape == NULL); + BulletShape::fillin(scan, manager); + nassertv(_shape == nullptr); PN_stdfloat margin = scan.get_stdfloat(); diff --git a/panda/src/bullet/bulletPlaneShape.h b/panda/src/bullet/bulletPlaneShape.h index 44a8e9ede6..4455328f10 100644 --- a/panda/src/bullet/bulletPlaneShape.h +++ b/panda/src/bullet/bulletPlaneShape.h @@ -32,7 +32,7 @@ private: INLINE BulletPlaneShape() : _shape(NULL) {}; PUBLISHED: - BulletPlaneShape(const LVector3 &normal, PN_stdfloat constant); + explicit BulletPlaneShape(const LVector3 &normal, PN_stdfloat constant); INLINE BulletPlaneShape(const BulletPlaneShape ©); INLINE void operator = (const BulletPlaneShape ©); INLINE ~BulletPlaneShape(); @@ -42,6 +42,9 @@ PUBLISHED: static BulletPlaneShape *make_from_solid(const CollisionPlane *solid); + MAKE_PROPERTY(plane_normal, get_plane_normal); + MAKE_PROPERTY(plane_constant, get_plane_constant); + public: virtual btCollisionShape *ptr() const; diff --git a/panda/src/bullet/bulletRigidBodyNode.cxx b/panda/src/bullet/bulletRigidBodyNode.cxx index 5c5e722785..b8c921e99d 100644 --- a/panda/src/bullet/bulletRigidBodyNode.cxx +++ b/panda/src/bullet/bulletRigidBodyNode.cxx @@ -612,6 +612,9 @@ write_datagram(BamWriter *manager, Datagram &dg) { get_gravity().write_datagram(dg); get_linear_factor().write_datagram(dg); get_angular_factor().write_datagram(dg); + // dynamic state (?) + get_linear_velocity().write_datagram(dg); + get_angular_velocity().write_datagram(dg); } /** diff --git a/panda/src/bullet/bulletRigidBodyNode.h b/panda/src/bullet/bulletRigidBodyNode.h index ea59c9202c..c8afaeb8bb 100644 --- a/panda/src/bullet/bulletRigidBodyNode.h +++ b/panda/src/bullet/bulletRigidBodyNode.h @@ -86,6 +86,23 @@ PUBLISHED: // Special bool pick_dirty_flag(); + MAKE_PROPERTY(mass, get_mass, set_mass); + MAKE_PROPERTY(inv_mass, get_inv_mass); + MAKE_PROPERTY(inertia, get_inertia, set_inertia); + MAKE_PROPERTY(inv_inertia_diag_local, get_inv_inertia_diag_local); + MAKE_PROPERTY(inv_inertia_tensor_world, get_inv_inertia_tensor_world); + MAKE_PROPERTY(linear_velocity, get_linear_velocity, set_linear_velocity); + MAKE_PROPERTY(angular_velocity, get_angular_velocity, set_angular_velocity); + MAKE_PROPERTY(linear_damping, get_linear_damping, set_linear_damping); + MAKE_PROPERTY(angular_damping, get_angular_damping, set_angular_damping); + MAKE_PROPERTY(total_force, get_total_force); + MAKE_PROPERTY(total_torque, get_total_torque); + MAKE_PROPERTY(linear_sleep_threshold, get_linear_sleep_threshold, set_linear_sleep_threshold); + MAKE_PROPERTY(angular_sleep_threshold, get_angular_sleep_threshold, set_angular_sleep_threshold); + MAKE_PROPERTY(gravity, get_gravity, set_gravity); + MAKE_PROPERTY(linear_factor, get_linear_factor, set_linear_factor); + MAKE_PROPERTY(angular_factor, get_angular_factor, set_angular_factor); + public: virtual btCollisionObject *get_object() const; diff --git a/panda/src/bullet/bulletRotationalLimitMotor.h b/panda/src/bullet/bulletRotationalLimitMotor.h index b82db7ad57..a1cce18ff4 100644 --- a/panda/src/bullet/bulletRotationalLimitMotor.h +++ b/panda/src/bullet/bulletRotationalLimitMotor.h @@ -50,6 +50,13 @@ PUBLISHED: INLINE PN_stdfloat get_current_position() const; INLINE PN_stdfloat get_accumulated_impulse() const; + MAKE_PROPERTY(limited, is_limited); + MAKE_PROPERTY(motor_enabled, get_motor_enabled, set_motor_enabled); + MAKE_PROPERTY(current_limit, get_current_limit); + MAKE_PROPERTY(current_error, get_current_error); + MAKE_PROPERTY(current_position, get_current_position); + MAKE_PROPERTY(accumulated_impulse, get_accumulated_impulse); + public: BulletRotationalLimitMotor(btRotationalLimitMotor &motor); diff --git a/panda/src/bullet/bulletShape.h b/panda/src/bullet/bulletShape.h index 8094cd1efb..8ebeaf9290 100644 --- a/panda/src/bullet/bulletShape.h +++ b/panda/src/bullet/bulletShape.h @@ -45,10 +45,20 @@ PUBLISHED: PN_stdfloat get_margin() const; BoundingSphere get_shape_bounds() const; + + MAKE_PROPERTY(polyhedral, is_polyhedral); + MAKE_PROPERTY(convex, is_convex); + MAKE_PROPERTY(convex_2d, is_convex_2d); + MAKE_PROPERTY(concave, is_concave); + MAKE_PROPERTY(infinite, is_infinite); + MAKE_PROPERTY(non_moving, is_non_moving); + MAKE_PROPERTY(soft_body, is_soft_body); + MAKE_PROPERTY(margin, get_margin, set_margin); + MAKE_PROPERTY(name, get_name); + MAKE_PROPERTY(shape_bounds, get_shape_bounds); public: virtual btCollisionShape *ptr() const = 0; - LVecBase3 get_local_scale() const; void set_local_scale(const LVecBase3 &scale); diff --git a/panda/src/bullet/bulletSliderConstraint.h b/panda/src/bullet/bulletSliderConstraint.h index b2ea28046e..931e51ec80 100644 --- a/panda/src/bullet/bulletSliderConstraint.h +++ b/panda/src/bullet/bulletSliderConstraint.h @@ -28,16 +28,15 @@ class BulletRigidBodyNode; * */ class EXPCL_PANDABULLET BulletSliderConstraint : public BulletConstraint { - PUBLISHED: - BulletSliderConstraint(const BulletRigidBodyNode *node_a, - const TransformState *frame_a, - bool useFrame_a); - BulletSliderConstraint(const BulletRigidBodyNode *node_a, - const BulletRigidBodyNode *node_b, - const TransformState *frame_a, - const TransformState *frame_b, - bool use_frame_a); + explicit BulletSliderConstraint(const BulletRigidBodyNode *node_a, + const TransformState *frame_a, + bool useFrame_a); + explicit BulletSliderConstraint(const BulletRigidBodyNode *node_a, + const BulletRigidBodyNode *node_b, + const TransformState *frame_a, + const TransformState *frame_b, + bool use_frame_a); INLINE ~BulletSliderConstraint(); PN_stdfloat get_linear_pos() const; @@ -74,6 +73,21 @@ PUBLISHED: INLINE CPT(TransformState) get_frame_a() const; INLINE CPT(TransformState) get_frame_b() const; + MAKE_PROPERTY(linear_pos, get_linear_pos); + MAKE_PROPERTY(angular_pos, get_angular_pos); + MAKE_PROPERTY(lower_linear_limit, get_lower_linear_limit, set_lower_linear_limit); + MAKE_PROPERTY(upper_linear_limit, get_upper_linear_limit, set_upper_linear_limit); + MAKE_PROPERTY(lower_angular_limit, get_lower_angular_limit, set_lower_angular_limit); + MAKE_PROPERTY(upper_angular_limit, get_upper_angular_limit, set_upper_angular_limit); + MAKE_PROPERTY(powered_linear_motor, get_powered_linear_motor, set_powered_linear_motor); + MAKE_PROPERTY(target_linear_motor_velocity, get_target_linear_motor_velocity, set_target_linear_motor_velocity); + MAKE_PROPERTY(max_linear_motor_force, get_max_linear_motor_force, set_max_linear_motor_force); + MAKE_PROPERTY(powered_angular_motor, get_powered_angular_motor, set_powered_angular_motor); + MAKE_PROPERTY(target_angular_motor_velocity, get_target_angular_motor_velocity, set_target_angular_motor_velocity); + MAKE_PROPERTY(max_angular_motor_force, get_max_angular_motor_force, set_max_angular_motor_force); + MAKE_PROPERTY(frame_a, get_frame_a); + MAKE_PROPERTY(frame_b, get_frame_b); + public: virtual btTypedConstraint *ptr() const; diff --git a/panda/src/bullet/bulletSoftBodyConfig.I b/panda/src/bullet/bulletSoftBodyConfig.I index 811463d492..eed9499bbb 100644 --- a/panda/src/bullet/bulletSoftBodyConfig.I +++ b/panda/src/bullet/bulletSoftBodyConfig.I @@ -113,7 +113,7 @@ set_pressure_coefficient(PN_stdfloat value) { * Getter for property kVC. */ INLINE PN_stdfloat BulletSoftBodyConfig:: -get_volume_conversation_coefficient() const { +get_volume_conservation_coefficient() const { return (PN_stdfloat)_cfg.kVC; } @@ -122,7 +122,7 @@ get_volume_conversation_coefficient() const { * Setter for property kVC. */ INLINE void BulletSoftBodyConfig:: -set_volume_conversation_coefficient(PN_stdfloat value) { +set_volume_conservation_coefficient(PN_stdfloat value) { _cfg.kVC = (btScalar)value; } diff --git a/panda/src/bullet/bulletSoftBodyConfig.h b/panda/src/bullet/bulletSoftBodyConfig.h index e3bf316881..2411797146 100644 --- a/panda/src/bullet/bulletSoftBodyConfig.h +++ b/panda/src/bullet/bulletSoftBodyConfig.h @@ -56,7 +56,7 @@ PUBLISHED: INLINE void set_drag_coefficient(PN_stdfloat value); INLINE void set_lift_coefficient(PN_stdfloat value); INLINE void set_pressure_coefficient(PN_stdfloat value); - INLINE void set_volume_conversation_coefficient(PN_stdfloat value); + INLINE void set_volume_conservation_coefficient(PN_stdfloat value); INLINE void set_dynamic_friction_coefficient(PN_stdfloat value); INLINE void set_pose_matching_coefficient(PN_stdfloat value); INLINE void set_rigid_contacts_hardness(PN_stdfloat value); @@ -81,7 +81,7 @@ PUBLISHED: INLINE PN_stdfloat get_drag_coefficient() const; INLINE PN_stdfloat get_lift_coefficient() const; INLINE PN_stdfloat get_pressure_coefficient() const; - INLINE PN_stdfloat get_volume_conversation_coefficient() const; + INLINE PN_stdfloat get_volume_conservation_coefficient() const; INLINE PN_stdfloat get_dynamic_friction_coefficient() const; INLINE PN_stdfloat get_pose_matching_coefficient() const; INLINE PN_stdfloat get_rigid_contacts_hardness() const; @@ -101,6 +101,32 @@ PUBLISHED: INLINE int get_drift_solver_iterations() const; INLINE int get_cluster_solver_iterations() const; + MAKE_PROPERTY(aero_model, get_aero_model, set_aero_model); + MAKE_PROPERTY(velocities_correction_factor, get_velocities_correction_factor, set_velocities_correction_factor); + MAKE_PROPERTY(damping_coefficient, get_damping_coefficient, set_damping_coefficient); + MAKE_PROPERTY(drag_coefficient, get_drag_coefficient, set_drag_coefficient); + MAKE_PROPERTY(lift_coefficient, get_lift_coefficient, set_lift_coefficient); + MAKE_PROPERTY(pressure_coefficient, get_pressure_coefficient, set_pressure_coefficient); + MAKE_PROPERTY(volume_conservation_coefficient, get_volume_conservation_coefficient, set_volume_conservation_coefficient); + MAKE_PROPERTY(dynamic_friction_coefficient, get_dynamic_friction_coefficient, set_dynamic_friction_coefficient); + MAKE_PROPERTY(pose_matching_coefficient, get_pose_matching_coefficient, set_pose_matching_coefficient); + MAKE_PROPERTY(rigid_contacts_hardness, get_rigid_contacts_hardness, set_rigid_contacts_hardness); + MAKE_PROPERTY(kinetic_contacts_hardness, get_kinetic_contacts_hardness, set_kinetic_contacts_hardness); + MAKE_PROPERTY(soft_contacts_hardness, get_soft_contacts_hardness, set_soft_contacts_hardness); + MAKE_PROPERTY(anchors_hardness, get_anchors_hardness, set_anchors_hardness); + MAKE_PROPERTY(soft_vs_rigid_hardness, get_soft_vs_rigid_hardness, set_soft_vs_rigid_hardness); + MAKE_PROPERTY(soft_vs_kinetic_hardness, get_soft_vs_kinetic_hardness, set_soft_vs_kinetic_hardness); + MAKE_PROPERTY(soft_vs_soft_hardness, get_soft_vs_soft_hardness, set_soft_vs_soft_hardness); + MAKE_PROPERTY(soft_vs_rigid_impulse_split, get_soft_vs_rigid_impulse_split, set_soft_vs_rigid_impulse_split); + MAKE_PROPERTY(soft_vs_kinetic_impulse_split, get_soft_vs_kinetic_impulse_split, set_soft_vs_kinetic_impulse_split); + MAKE_PROPERTY(soft_vs_soft_impulse_split, get_soft_vs_soft_impulse_split, set_soft_vs_soft_impulse_split); + MAKE_PROPERTY(maxvolume, get_maxvolume, set_maxvolume); + MAKE_PROPERTY(timescale, get_timescale, set_timescale); + MAKE_PROPERTY(positions_solver_iterations, get_positions_solver_iterations, set_positions_solver_iterations); + MAKE_PROPERTY(velocities_solver_iterations, get_velocities_solver_iterations, set_velocities_solver_iterations); + MAKE_PROPERTY(drift_solver_iterations, get_drift_solver_iterations, set_drift_solver_iterations); + MAKE_PROPERTY(cluster_solver_iterations, get_cluster_solver_iterations, set_cluster_solver_iterations); + public: BulletSoftBodyConfig(btSoftBody::Config &cfg); diff --git a/panda/src/bullet/bulletSoftBodyMaterial.h b/panda/src/bullet/bulletSoftBodyMaterial.h index b2a70c1d9d..3f1c07a7aa 100644 --- a/panda/src/bullet/bulletSoftBodyMaterial.h +++ b/panda/src/bullet/bulletSoftBodyMaterial.h @@ -27,16 +27,17 @@ PUBLISHED: INLINE ~BulletSoftBodyMaterial(); INLINE static BulletSoftBodyMaterial empty(); - INLINE void set_linear_stiffness(PN_stdfloat value); INLINE PN_stdfloat get_linear_stiffness() const; - MAKE_PROPERTY(linear_stiffness, get_linear_stiffness, set_linear_stiffness); + INLINE void set_linear_stiffness(PN_stdfloat value); - INLINE void set_angular_stiffness(PN_stdfloat value); INLINE PN_stdfloat get_angular_stiffness() const; - MAKE_PROPERTY(angular_stiffness, get_angular_stiffness, set_angular_stiffness); + INLINE void set_angular_stiffness(PN_stdfloat value); - INLINE void set_volume_preservation(PN_stdfloat value); INLINE PN_stdfloat get_volume_preservation() const; + INLINE void set_volume_preservation(PN_stdfloat value); + + MAKE_PROPERTY(linear_stiffness, get_linear_stiffness, set_linear_stiffness); + MAKE_PROPERTY(angular_stiffness, get_angular_stiffness, set_angular_stiffness); MAKE_PROPERTY(volume_preservation, get_volume_preservation, set_volume_preservation); public: diff --git a/panda/src/bullet/bulletSoftBodyNode.cxx b/panda/src/bullet/bulletSoftBodyNode.cxx index 349dbe4389..1d5a70d93f 100644 --- a/panda/src/bullet/bulletSoftBodyNode.cxx +++ b/panda/src/bullet/bulletSoftBodyNode.cxx @@ -197,7 +197,7 @@ transform_changed() { _soft->scale(new_scale); } - _sync = ts; + _sync = move(ts); } } diff --git a/panda/src/bullet/bulletSoftBodyNode.h b/panda/src/bullet/bulletSoftBodyNode.h index 38d95c1fd9..0f37217dfe 100644 --- a/panda/src/bullet/bulletSoftBodyNode.h +++ b/panda/src/bullet/bulletSoftBodyNode.h @@ -50,6 +50,13 @@ PUBLISHED: INLINE PN_stdfloat get_area() const; INLINE int is_attached() const; + MAKE_PROPERTY(pos, get_pos); + MAKE_PROPERTY(velocity, get_velocity); + MAKE_PROPERTY(normal, get_normal); + MAKE_PROPERTY(inv_mass, get_inv_mass); + MAKE_PROPERTY(area, get_area); + MAKE_PROPERTY(attached, is_attached); + public: BulletSoftBodyNodeElement(btSoftBody::Node &node); @@ -203,6 +210,14 @@ PUBLISHED: const char *face, const char *node); + MAKE_PROPERTY(cfg, get_cfg); + MAKE_PROPERTY(world_info, get_world_info); + MAKE_PROPERTY(wind_velocity, get_wind_velocity, set_wind_velocity); + MAKE_PROPERTY(aabb, get_aabb); + MAKE_PROPERTY(num_clusters, get_num_clusters); + MAKE_SEQ_PROPERTY(materials, get_num_materials, get_material); + MAKE_SEQ_PROPERTY(nodes, get_num_nodes, get_node); + public: virtual btCollisionObject *get_object() const; diff --git a/panda/src/bullet/bulletSoftBodyShape.h b/panda/src/bullet/bulletSoftBodyShape.h index 28a5aab9a6..85f62df88f 100644 --- a/panda/src/bullet/bulletSoftBodyShape.h +++ b/panda/src/bullet/bulletSoftBodyShape.h @@ -31,6 +31,8 @@ PUBLISHED: BulletSoftBodyNode *get_body() const; + MAKE_PROPERTY(body, get_body); + public: BulletSoftBodyShape(btSoftBodyCollisionShape *shapePtr); diff --git a/panda/src/bullet/bulletSoftBodyWorldInfo.h b/panda/src/bullet/bulletSoftBodyWorldInfo.h index c0aa23454b..5cae2fa0d5 100644 --- a/panda/src/bullet/bulletSoftBodyWorldInfo.h +++ b/panda/src/bullet/bulletSoftBodyWorldInfo.h @@ -43,6 +43,12 @@ PUBLISHED: void garbage_collect(int lifetime=256); + MAKE_PROPERTY(air_density, get_air_density, set_air_density); + MAKE_PROPERTY(water_density, get_water_density, set_water_density); + MAKE_PROPERTY(water_offset, get_water_offset, set_water_offset); + MAKE_PROPERTY(water_normal, get_water_normal, set_water_normal); + MAKE_PROPERTY(gravity, get_gravity, set_gravity); + public: BulletSoftBodyWorldInfo(btSoftBodyWorldInfo &_info); diff --git a/panda/src/bullet/bulletSphereShape.I b/panda/src/bullet/bulletSphereShape.I index 340d9b7f2e..a5177c572a 100644 --- a/panda/src/bullet/bulletSphereShape.I +++ b/panda/src/bullet/bulletSphereShape.I @@ -25,7 +25,8 @@ INLINE BulletSphereShape:: */ INLINE BulletSphereShape:: BulletSphereShape(const BulletSphereShape ©) : - _shape(copy._shape) { + _shape(copy._shape), + _radius(copy._radius) { } /** @@ -34,13 +35,13 @@ BulletSphereShape(const BulletSphereShape ©) : INLINE void BulletSphereShape:: operator = (const BulletSphereShape ©) { _shape = copy._shape; + _radius = copy._radius; } /** - * + * Returns the radius that was used to construct this sphere. */ INLINE PN_stdfloat BulletSphereShape:: get_radius() const { - - return _shape->getRadius(); + return _radius; } diff --git a/panda/src/bullet/bulletSphereShape.cxx b/panda/src/bullet/bulletSphereShape.cxx index 1e3842bbb8..4cda14882b 100644 --- a/panda/src/bullet/bulletSphereShape.cxx +++ b/panda/src/bullet/bulletSphereShape.cxx @@ -19,7 +19,7 @@ TypeHandle BulletSphereShape::_type_handle; * */ BulletSphereShape:: -BulletSphereShape(PN_stdfloat radius) { +BulletSphereShape(PN_stdfloat radius) : _radius(radius) { _shape = new btSphereShape(radius); _shape->setUserPointer(this); @@ -57,8 +57,10 @@ register_with_read_factory() { */ void BulletSphereShape:: write_datagram(BamWriter *manager, Datagram &dg) { + BulletShape::write_datagram(manager, dg); + dg.add_stdfloat(get_margin()); - dg.add_stdfloat(get_radius()); + dg.add_stdfloat(_radius); } /** @@ -84,11 +86,13 @@ make_from_bam(const FactoryParams ¶ms) { */ void BulletSphereShape:: fillin(DatagramIterator &scan, BamReader *manager) { - nassertv(_shape == NULL); + BulletShape::fillin(scan, manager); + nassertv(_shape == nullptr); PN_stdfloat margin = scan.get_stdfloat(); + _radius = scan.get_stdfloat(); - _shape = new btSphereShape(scan.get_stdfloat()); + _shape = new btSphereShape(_radius); _shape->setUserPointer(this); _shape->setMargin(margin); } diff --git a/panda/src/bullet/bulletSphereShape.h b/panda/src/bullet/bulletSphereShape.h index 877f24f5df..c16a1279a6 100644 --- a/panda/src/bullet/bulletSphereShape.h +++ b/panda/src/bullet/bulletSphereShape.h @@ -31,7 +31,7 @@ private: INLINE BulletSphereShape() : _shape(NULL) {}; PUBLISHED: - BulletSphereShape(PN_stdfloat radius); + explicit BulletSphereShape(PN_stdfloat radius); INLINE BulletSphereShape(const BulletSphereShape ©); INLINE void operator = (const BulletSphereShape ©); INLINE ~BulletSphereShape(); @@ -40,11 +40,14 @@ PUBLISHED: static BulletSphereShape *make_from_solid(const CollisionSphere *solid); + MAKE_PROPERTY(radius, get_radius); + public: virtual btCollisionShape *ptr() const; private: btSphereShape *_shape; + PN_stdfloat _radius; public: static void register_with_read_factory(); diff --git a/panda/src/bullet/bulletSphericalConstraint.h b/panda/src/bullet/bulletSphericalConstraint.h index 26f04825ea..c3eb0a548d 100644 --- a/panda/src/bullet/bulletSphericalConstraint.h +++ b/panda/src/bullet/bulletSphericalConstraint.h @@ -32,14 +32,13 @@ class BulletRigidBodyNode; * socket" joint. */ class EXPCL_PANDABULLET BulletSphericalConstraint : public BulletConstraint { - PUBLISHED: - BulletSphericalConstraint(const BulletRigidBodyNode *node_a, - const LPoint3 &pivot_a); - BulletSphericalConstraint(const BulletRigidBodyNode *node_a, - const BulletRigidBodyNode *node_b, - const LPoint3 &pivot_a, - const LPoint3 &pivot_b); + explicit BulletSphericalConstraint(const BulletRigidBodyNode *node_a, + const LPoint3 &pivot_a); + explicit BulletSphericalConstraint(const BulletRigidBodyNode *node_a, + const BulletRigidBodyNode *node_b, + const LPoint3 &pivot_a, + const LPoint3 &pivot_b); INLINE ~BulletSphericalConstraint(); // Pivots @@ -48,6 +47,9 @@ PUBLISHED: LPoint3 get_pivot_in_a() const; LPoint3 get_pivot_in_b() const; + MAKE_PROPERTY(pivot_a, get_pivot_in_a, set_pivot_a); + MAKE_PROPERTY(pivot_b, get_pivot_in_b, set_pivot_b); + public: virtual btTypedConstraint *ptr() const; diff --git a/panda/src/bullet/bulletTickCallbackData.h b/panda/src/bullet/bulletTickCallbackData.h index 80c8570fe1..6218ac237c 100644 --- a/panda/src/bullet/bulletTickCallbackData.h +++ b/panda/src/bullet/bulletTickCallbackData.h @@ -30,6 +30,8 @@ PUBLISHED: INLINE PN_stdfloat get_timestep() const; + MAKE_PROPERTY(timestep, get_timestep); + private: btScalar _timestep; diff --git a/panda/src/bullet/bulletTranslationalLimitMotor.h b/panda/src/bullet/bulletTranslationalLimitMotor.h index bfbdd5e870..f339daefef 100644 --- a/panda/src/bullet/bulletTranslationalLimitMotor.h +++ b/panda/src/bullet/bulletTranslationalLimitMotor.h @@ -32,8 +32,8 @@ PUBLISHED: INLINE void set_motor_enabled(int axis, bool enable); INLINE void set_low_limit(const LVecBase3 &limit); - INLINE void set_high_limit(const LVecBase3 & limit); - INLINE void set_target_velocity(const LVecBase3&velocity); + INLINE void set_high_limit(const LVecBase3 &limit); + INLINE void set_target_velocity(const LVecBase3 &velocity); INLINE void set_max_motor_force(const LVecBase3 &force); INLINE void set_damping(PN_stdfloat damping); INLINE void set_softness(PN_stdfloat softness); @@ -49,6 +49,10 @@ PUBLISHED: INLINE LPoint3 get_current_diff() const; INLINE LVector3 get_accumulated_impulse() const; + MAKE_PROPERTY(current_error, get_current_error); + MAKE_PROPERTY(current_diff, get_current_diff); + MAKE_PROPERTY(accumulated_impulse, get_accumulated_impulse); + public: BulletTranslationalLimitMotor(btTranslationalLimitMotor &motor); diff --git a/panda/src/bullet/bulletTriangleMesh.I b/panda/src/bullet/bulletTriangleMesh.I index 4c3b8f83b8..b6965303aa 100644 --- a/panda/src/bullet/bulletTriangleMesh.I +++ b/panda/src/bullet/bulletTriangleMesh.I @@ -14,19 +14,37 @@ /** * */ -INLINE BulletTriangleMesh:: -~BulletTriangleMesh() { - - delete _mesh; +INLINE btStridingMeshInterface *BulletTriangleMesh:: +ptr() const { + return (btStridingMeshInterface *)&_mesh; } /** - * + * Returns the number of vertices in this triangle mesh. */ -btTriangleMesh *BulletTriangleMesh:: -ptr() const { +INLINE size_t BulletTriangleMesh:: +get_num_vertices() const { + return _vertices.size(); +} - return _mesh; +/** + * Returns the vertex at the given vertex index. + */ +INLINE LPoint3 BulletTriangleMesh:: +get_vertex(size_t index) const { + nassertr(index < _vertices.size(), LPoint3::zero()); + const btVector3 &vertex = _vertices[index]; + return LPoint3(vertex[0], vertex[1], vertex[2]); +} + +/** + * Returns the vertex indices making up the given triangle index. + */ +INLINE LVecBase3i BulletTriangleMesh:: +get_triangle(size_t index) const { + index *= 3; + nassertr(index + 2 < _indices.size(), LVecBase3i::zero()); + return LVecBase3i(_indices[index], _indices[index + 1], _indices[index + 2]); } /** @@ -34,7 +52,6 @@ ptr() const { */ INLINE ostream & operator << (ostream &out, const BulletTriangleMesh &obj) { - obj.output(out); return out; } diff --git a/panda/src/bullet/bulletTriangleMesh.cxx b/panda/src/bullet/bulletTriangleMesh.cxx index d776d572d7..3138e28efd 100644 --- a/panda/src/bullet/bulletTriangleMesh.cxx +++ b/panda/src/bullet/bulletTriangleMesh.cxx @@ -23,150 +23,255 @@ TypeHandle BulletTriangleMesh::_type_handle; * */ BulletTriangleMesh:: -BulletTriangleMesh() { - - _mesh = new btTriangleMesh(); +BulletTriangleMesh() + : _welding_distance(0) { + btIndexedMesh mesh; + mesh.m_numTriangles = 0; + mesh.m_numVertices = 0; + mesh.m_indexType = PHY_INTEGER; + mesh.m_triangleIndexBase = nullptr; + mesh.m_triangleIndexStride = 3 * sizeof(int); + mesh.m_vertexBase = nullptr; + mesh.m_vertexStride = sizeof(btVector3); + _mesh.addIndexedMesh(mesh); } /** - * + * Returns the number of triangles in this triangle mesh. */ -int BulletTriangleMesh:: +size_t BulletTriangleMesh:: get_num_triangles() const { - - return _mesh->getNumTriangles(); + return _indices.size() / 3; } /** - * + * Used to reserve memory in anticipation of the given amount of vertices and + * indices being added to the triangle mesh. This is useful if you are about + * to call add_triangle() many times, to prevent unnecessary reallocations. */ void BulletTriangleMesh:: preallocate(int num_verts, int num_indices) { + _vertices.reserve(num_verts); + _indices.reserve(num_indices); - _mesh->preallocateVertices(num_verts); - _mesh->preallocateIndices(num_indices); + btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0]; + mesh.m_vertexBase = (unsigned char*)&_vertices[0]; + mesh.m_triangleIndexBase = (unsigned char *)&_indices[0]; } /** + * Adds a triangle with the indicated coordinates. * + * If remove_duplicate_vertices is true, it will make sure that it does not + * add duplicate vertices if they already exist in the triangle mesh, within + * the tolerance specified by set_welding_distance(). This comes at a + * significant performance cost, especially for large meshes. */ void BulletTriangleMesh:: add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remove_duplicate_vertices) { - nassertv(!p0.is_nan()); nassertv(!p1.is_nan()); nassertv(!p2.is_nan()); - _mesh->addTriangle( - LVecBase3_to_btVector3(p0), - LVecBase3_to_btVector3(p1), - LVecBase3_to_btVector3(p2), - remove_duplicate_vertices); + btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0]; + mesh.m_numTriangles++; + + if (!remove_duplicate_vertices) { + unsigned int index = _vertices.size(); + _indices.push_back(index++); + _indices.push_back(index++); + _indices.push_back(index++); + + _vertices.push_back(LVecBase3_to_btVector3(p0)); + _vertices.push_back(LVecBase3_to_btVector3(p1)); + _vertices.push_back(LVecBase3_to_btVector3(p2)); + mesh.m_numVertices += 3; + mesh.m_vertexBase = (unsigned char*)&_vertices[0]; + } else { + _indices.push_back(find_or_add_vertex(p0)); + _indices.push_back(find_or_add_vertex(p1)); + _indices.push_back(find_or_add_vertex(p2)); + } + + mesh.m_triangleIndexBase = (unsigned char *)&_indices[0]; } /** + * Sets the square of the distance at which vertices will be merged + * together when adding geometry with remove_duplicate_vertices set to true. * + * The default is 0, meaning vertices will only be merged if they have the + * exact same position. */ void BulletTriangleMesh:: set_welding_distance(PN_stdfloat distance) { - - _mesh->m_weldingThreshold = distance; + _welding_distance = distance; } /** - * + * Returns the value previously set with set_welding_distance(), or the + * value of 0 if none was set. */ PN_stdfloat BulletTriangleMesh:: get_welding_distance() const { - - return _mesh->m_weldingThreshold; + return _welding_distance; } /** + * Adds the geometry from the indicated Geom from the triangle mesh. This is + * a one-time copy operation, and future updates to the Geom will not be + * reflected. * + * If remove_duplicate_vertices is true, it will make sure that it does not + * add duplicate vertices if they already exist in the triangle mesh, within + * the tolerance specified by set_welding_distance(). This comes at a + * significant performance cost, especially for large meshes. */ void BulletTriangleMesh:: add_geom(const Geom *geom, bool remove_duplicate_vertices, const TransformState *ts) { - nassertv(geom); nassertv(ts); - LMatrix4 m = ts->get_mat(); - - // Collect points - pvector points; - CPT(GeomVertexData) vdata = geom->get_vertex_data(); + size_t num_vertices = vdata->get_num_rows(); GeomVertexReader reader = GeomVertexReader(vdata, InternalName::get_vertex()); - while (!reader.is_at_end()) { - points.push_back(m.xform_point(reader.get_data3())); - } + btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0]; - // Convert points - btVector3 *vertices = new btVector3[points.size()]; + if (!remove_duplicate_vertices) { + // Fast path: directly copy the vertices and indices. + mesh.m_numVertices += num_vertices; + unsigned int index_offset = _vertices.size(); + _vertices.reserve(_vertices.size() + num_vertices); - int i = 0; - pvector::const_iterator it; - for (it=points.begin(); it!=points.end(); it++) { - LPoint3 v = *it; - vertices[i] = LVecBase3_to_btVector3(v); - i++; - } - - // Add triangles - for (int k=0; kget_num_primitives(); k++) { - - CPT(GeomPrimitive) prim = geom->get_primitive(k); - prim = prim->decompose(); - - for (int l=0; lget_num_primitives(); l++) { - - int s = prim->get_primitive_start(l); - int e = prim->get_primitive_end(l); - - nassertv(e - s == 3); - - btVector3 v0 = vertices[prim->get_vertex(s)]; - btVector3 v1 = vertices[prim->get_vertex(s+1)]; - btVector3 v2 = vertices[prim->get_vertex(s+2)]; - - _mesh->addTriangle(v0, v1, v2, remove_duplicate_vertices); + if (ts->is_identity()) { + while (!reader.is_at_end()) { + _vertices.push_back(LVecBase3_to_btVector3(reader.get_data3())); + } + } else { + LMatrix4 m = ts->get_mat(); + while (!reader.is_at_end()) { + _vertices.push_back(LVecBase3_to_btVector3(m.xform_point(reader.get_data3()))); + } } + + for (int k = 0; k < geom->get_num_primitives(); ++k) { + CPT(GeomPrimitive) prim = geom->get_primitive(k); + prim = prim->decompose(); + + if (prim->is_of_type(GeomTriangles::get_class_type())) { + int num_vertices = prim->get_num_vertices(); + _indices.reserve(_indices.size() + num_vertices); + mesh.m_numTriangles += num_vertices / 3; + + CPT(GeomVertexArrayData) vertices = prim->get_vertices(); + if (vertices != nullptr) { + GeomVertexReader index(move(vertices), 0); + while (!index.is_at_end()) { + _indices.push_back(index_offset + index.get_data1i()); + } + } else { + int index = index_offset + prim->get_first_vertex(); + int end_index = index + num_vertices; + while (index < end_index) { + _indices.push_back(index++); + } + } + } + } + nassertv(mesh.m_numTriangles * 3 == _indices.size()); + + } else { + // Collect points + pvector points; + points.reserve(_vertices.size() + num_vertices); + + if (ts->is_identity()) { + while (!reader.is_at_end()) { + points.push_back(reader.get_data3()); + } + } else { + LMatrix4 m = ts->get_mat(); + while (!reader.is_at_end()) { + points.push_back(m.xform_point(reader.get_data3())); + } + } + + // Add triangles + for (int k = 0; k < geom->get_num_primitives(); ++k) { + CPT(GeomPrimitive) prim = geom->get_primitive(k); + prim = prim->decompose(); + + if (prim->is_of_type(GeomTriangles::get_class_type())) { + int num_vertices = prim->get_num_vertices(); + _indices.reserve(_indices.size() + num_vertices); + mesh.m_numTriangles += num_vertices / 3; + + CPT(GeomVertexArrayData) vertices = prim->get_vertices(); + if (vertices != nullptr) { + GeomVertexReader index(move(vertices), 0); + while (!index.is_at_end()) { + _indices.push_back(find_or_add_vertex(points[index.get_data1i()])); + } + } else { + int index = prim->get_first_vertex(); + int end_index = index + num_vertices; + while (index < end_index) { + _indices.push_back(find_or_add_vertex(points[index])); + } + } + } + } + nassertv(mesh.m_numTriangles * 3 == _indices.size()); } - delete [] vertices; + // Reset the pointers, since the vectors may have been reallocated. + mesh.m_vertexBase = (unsigned char*)&_vertices[0]; + mesh.m_triangleIndexBase = (unsigned char *)&_indices[0]; } /** + * Adds triangle information from an array of points and indices referring to + * these points. This is more efficient than adding triangles one at a time. * + * If remove_duplicate_vertices is true, it will make sure that it does not + * add duplicate vertices if they already exist in the triangle mesh, within + * the tolerance specified by set_welding_distance(). This comes at a + * significant performance cost, especially for large meshes. */ void BulletTriangleMesh:: add_array(const PTA_LVecBase3 &points, const PTA_int &indices, bool remove_duplicate_vertices) { + btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0]; - // Convert vertices - btVector3 *vertices = new btVector3[points.size()]; + _indices.reserve(_indices.size() + indices.size()); - int i = 0; - PTA_LVecBase3::const_iterator it; - for (it=points.begin(); it!=points.end(); it++) { - LVecBase3 v = *it; - vertices[i] = LVecBase3_to_btVector3(v); - i++; + if (!remove_duplicate_vertices) { + unsigned int index_offset = _vertices.size(); + for (size_t i = 0; i < indices.size(); ++i) { + _indices.push_back(index_offset + indices[i]); + } + + _vertices.reserve(_vertices.size() + points.size()); + for (size_t i = 0; i < points.size(); ++i) { + _vertices.push_back(LVecBase3_to_btVector3(points[i])); + } + + mesh.m_numVertices += points.size(); + + } else { + // Add the points one by one. + _indices.reserve(_indices.size() + indices.size()); + for (size_t i = 0; i < indices.size(); ++i) { + LVecBase3 p = points[indices[i]]; + _indices.push_back(find_or_add_vertex(p)); + } } - // Add triangles - int j = 0; - while (j+2 < (int)indices.size()) { + mesh.m_numTriangles += indices.size() / 3; - btVector3 v0 = vertices[indices[j++]]; - btVector3 v1 = vertices[indices[j++]]; - btVector3 v2 = vertices[indices[j++]]; - - _mesh->addTriangle(v0, v1, v2, remove_duplicate_vertices); - } - - delete [] vertices; + // Reset the pointers, since the vectors may have been reallocated. + mesh.m_vertexBase = (unsigned char*)&_vertices[0]; + mesh.m_triangleIndexBase = (unsigned char *)&_indices[0]; } /** @@ -174,8 +279,7 @@ add_array(const PTA_LVecBase3 &points, const PTA_int &indices, bool remove_dupli */ void BulletTriangleMesh:: output(ostream &out) const { - - out << get_type() << ", " << _mesh->getNumTriangles(); + out << get_type() << ", " << get_num_triangles() << " triangles"; } /** @@ -183,19 +287,39 @@ output(ostream &out) const { */ void BulletTriangleMesh:: write(ostream &out, int indent_level) const { - indent(out, indent_level) << get_type() << ":" << endl; - IndexedMeshArray& array = _mesh->getIndexedMeshArray(); - for (int i=0; i < array.size(); i++) { + const IndexedMeshArray &array = _mesh.getIndexedMeshArray(); + for (size_t i = 0; i < array.size(); ++i) { indent(out, indent_level + 2) << "IndexedMesh " << i << ":" << endl; - btIndexedMesh meshPart = array.at(i); - - indent(out, indent_level + 4) << "num triangles:" << meshPart.m_numTriangles << endl; - indent(out, indent_level + 4) << "num vertices:" << meshPart.m_numVertices << endl; + const btIndexedMesh &mesh = array[0]; + indent(out, indent_level + 4) << "num triangles:" << mesh.m_numTriangles << endl; + indent(out, indent_level + 4) << "num vertices:" << mesh.m_numVertices << endl; } } +/** + * Finds the indicated vertex and returns its index. If it was not found, + * adds it as a new vertex and returns its index. + */ +unsigned int BulletTriangleMesh:: +find_or_add_vertex(const LVecBase3 &p) { + btVector3 vertex = LVecBase3_to_btVector3(p); + + for (int i = 0; i < _vertices.size(); ++i) { + if ((_vertices[i] - vertex).length2() <= _welding_distance) { + return i; + } + } + + _vertices.push_back(vertex); + + btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0]; + mesh.m_numVertices++; + mesh.m_vertexBase = (unsigned char*)&_vertices[0]; + return _vertices.size() - 1; +} + /** * Tells the BamReader how to create objects of type BulletTriangleMesh. */ @@ -215,7 +339,7 @@ write_datagram(BamWriter *manager, Datagram &dg) { // In case we ever want to represent more than 1 indexed mesh. dg.add_int32(1); - btIndexedMesh &mesh = _mesh->getIndexedMeshArray()[0]; + btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0]; dg.add_int32(mesh.m_numVertices); dg.add_int32(mesh.m_numTriangles); @@ -238,22 +362,12 @@ write_datagram(BamWriter *manager, Datagram &dg) { const unsigned char *iptr = mesh.m_triangleIndexBase; nassertv(iptr != NULL || mesh.m_numTriangles == 0); - if (_mesh->getUse32bitIndices()) { - for (int i = 0; i < mesh.m_numTriangles; ++i) { - int *triangle = (int *)iptr; - dg.add_int32(triangle[0]); - dg.add_int32(triangle[1]); - dg.add_int32(triangle[2]); - iptr += mesh.m_triangleIndexStride; - } - } else { - for (int i = 0; i < mesh.m_numTriangles; ++i) { - short int *triangle = (short int *)iptr; - dg.add_int32(triangle[0]); - dg.add_int32(triangle[1]); - dg.add_int32(triangle[2]); - iptr += mesh.m_triangleIndexStride; - } + for (int i = 0; i < mesh.m_numTriangles; ++i) { + int *triangle = (int *)iptr; + dg.add_int32(triangle[0]); + dg.add_int32(triangle[1]); + dg.add_int32(triangle[2]); + iptr += mesh.m_triangleIndexStride; } } @@ -287,23 +401,26 @@ fillin(DatagramIterator &scan, BamReader *manager) { int num_triangles = scan.get_int32(); nassertv(scan.get_bool() == true); + btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0]; + mesh.m_numVertices = num_vertices; + mesh.m_numTriangles = num_triangles; + // Read and add the vertices. - _mesh->preallocateVertices(num_vertices); + _vertices.clear(); + _vertices.reserve(num_vertices); for (int i = 0; i < num_vertices; ++i) { PN_stdfloat x = scan.get_stdfloat(); PN_stdfloat y = scan.get_stdfloat(); PN_stdfloat z = scan.get_stdfloat(); - _mesh->findOrAddVertex(btVector3(x, y, z), false); + _vertices.push_back(btVector3(x, y, z)); } // Now read and add the indices. - int num_indices = num_triangles * 3; - _mesh->preallocateIndices(num_indices); - for (int i = 0; i < num_indices; ++i) { - _mesh->addIndex(scan.get_int32()); - } + size_t num_indices = (size_t)num_triangles * 3; + _indices.resize(num_indices); + scan.extract_bytes((unsigned char *)&_indices[0], num_indices * sizeof(int)); - // Since we manually added the vertices individually, we have to update the - // triangle count appropriately. - _mesh->getIndexedMeshArray()[0].m_numTriangles = num_triangles; + // Reset the pointers, since the vectors may have been reallocated. + mesh.m_vertexBase = (unsigned char*)&_vertices[0]; + mesh.m_triangleIndexBase = (unsigned char *)&_indices[0]; } diff --git a/panda/src/bullet/bulletTriangleMesh.h b/panda/src/bullet/bulletTriangleMesh.h index ade3ccfd7f..64973d83e8 100644 --- a/panda/src/bullet/bulletTriangleMesh.h +++ b/panda/src/bullet/bulletTriangleMesh.h @@ -30,10 +30,9 @@ * */ class EXPCL_PANDABULLET BulletTriangleMesh : public TypedWritableReferenceCount { - PUBLISHED: BulletTriangleMesh(); - INLINE ~BulletTriangleMesh(); + ~BulletTriangleMesh() DEFAULT_DTOR; void add_triangle(const LPoint3 &p0, const LPoint3 &p1, @@ -49,17 +48,34 @@ PUBLISHED: void set_welding_distance(PN_stdfloat distance); void preallocate(int num_verts, int num_indices); - int get_num_triangles() const; + size_t get_num_triangles() const; PN_stdfloat get_welding_distance() const; virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level) const; public: - INLINE btTriangleMesh *ptr() const; + INLINE size_t get_num_vertices() const; + INLINE LPoint3 get_vertex(size_t index) const; + + INLINE LVecBase3i get_triangle(size_t index) const; + +PUBLISHED: + MAKE_PROPERTY(welding_distance, get_welding_distance, set_welding_distance); + + MAKE_SEQ_PROPERTY(vertices, get_num_vertices, get_vertex); + MAKE_SEQ_PROPERTY(triangles, get_num_triangles, get_triangle); + +public: + INLINE btStridingMeshInterface *ptr() const; private: - btTriangleMesh *_mesh; + unsigned int find_or_add_vertex(const LVecBase3 &p); + + btTriangleIndexVertexArray _mesh; + btAlignedObjectArray _vertices; + btAlignedObjectArray _indices; + PN_stdfloat _welding_distance; public: static void register_with_read_factory(); diff --git a/panda/src/bullet/bulletTriangleMeshShape.cxx b/panda/src/bullet/bulletTriangleMeshShape.cxx index 62252db221..22775a6e0a 100644 --- a/panda/src/bullet/bulletTriangleMeshShape.cxx +++ b/panda/src/bullet/bulletTriangleMeshShape.cxx @@ -124,6 +124,8 @@ register_with_read_factory() { */ void BulletTriangleMeshShape:: write_datagram(BamWriter *manager, Datagram &dg) { + BulletShape::write_datagram(manager, dg); + dg.add_stdfloat(get_margin()); manager->write_pointer(dg, _mesh); @@ -145,16 +147,18 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { _mesh = DCAST(BulletTriangleMesh, p_list[pi++]); - btTriangleMesh *mesh_ptr = _mesh->ptr(); + btStridingMeshInterface *mesh_ptr = _mesh->ptr(); nassertr(mesh_ptr != NULL, pi); if (_dynamic) { _gimpact_shape = new btGImpactMeshShape(mesh_ptr); _gimpact_shape->updateBound(); _gimpact_shape->setUserPointer(this); + _gimpact_shape->setMargin(_margin); } else { _bvh_shape = new btBvhTriangleMeshShape(mesh_ptr, _compress, _bvh); _bvh_shape->setUserPointer(this); + _bvh_shape->setMargin(_margin); } return pi; @@ -183,7 +187,9 @@ make_from_bam(const FactoryParams ¶ms) { */ void BulletTriangleMeshShape:: fillin(DatagramIterator &scan, BamReader *manager) { - PN_stdfloat margin = scan.get_stdfloat(); + BulletShape::fillin(scan, manager); + + _margin = scan.get_stdfloat(); manager->read_pointer(scan); diff --git a/panda/src/bullet/bulletTriangleMeshShape.h b/panda/src/bullet/bulletTriangleMeshShape.h index 7a40dd5782..b76227b46e 100644 --- a/panda/src/bullet/bulletTriangleMeshShape.h +++ b/panda/src/bullet/bulletTriangleMeshShape.h @@ -31,7 +31,7 @@ private: INLINE BulletTriangleMeshShape(); PUBLISHED: - BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress=true, bool bvh=true); + explicit BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress=true, bool bvh=true); INLINE BulletTriangleMeshShape(const BulletTriangleMeshShape ©); INLINE void operator = (const BulletTriangleMeshShape ©); INLINE ~BulletTriangleMeshShape(); @@ -41,6 +41,9 @@ PUBLISHED: INLINE bool is_static() const; INLINE bool is_dynamic() const; + MAKE_PROPERTY(static, is_static); + MAKE_PROPERTY(dynamic, is_dynamic); + public: virtual btCollisionShape *ptr() const; @@ -50,6 +53,9 @@ private: PT(BulletTriangleMesh) _mesh; + // Stored temporarily during bam read. + PN_stdfloat _margin; + bool _dynamic : 1; bool _compress : 1; bool _bvh : 1; diff --git a/panda/src/bullet/bulletVehicle.h b/panda/src/bullet/bulletVehicle.h index 7ced2acb46..8b7f7862c6 100644 --- a/panda/src/bullet/bulletVehicle.h +++ b/panda/src/bullet/bulletVehicle.h @@ -46,6 +46,13 @@ PUBLISHED: INLINE PN_stdfloat get_friction_slip() const; INLINE PN_stdfloat get_max_suspension_force() const; + MAKE_PROPERTY(suspension_stiffness, get_suspension_stiffness, set_suspension_stiffness); + MAKE_PROPERTY(suspension_compression, get_suspension_compression, set_suspension_compression); + MAKE_PROPERTY(suspension_damping, get_suspension_damping, set_suspension_damping); + MAKE_PROPERTY(max_suspension_travel_cm, get_max_suspension_travel_cm, set_max_suspension_travel_cm); + MAKE_PROPERTY(friction_slip, get_friction_slip, set_friction_slip); + MAKE_PROPERTY(max_suspension_force, get_max_suspension_force, set_max_suspension_force); + private: btRaycastVehicle::btVehicleTuning _; @@ -87,6 +94,12 @@ PUBLISHED: // Tuning INLINE BulletVehicleTuning &get_tuning(); + MAKE_PROPERTY(chassis, get_chassis); + MAKE_PROPERTY(current_speed_km_hour, get_current_speed_km_hour); + MAKE_PROPERTY(forward_vector, get_forward_vector); + MAKE_SEQ_PROPERTY(wheels, get_num_wheels, get_wheel); + MAKE_PROPERTY(tuning, get_tuning); + public: INLINE btRaycastVehicle *get_vehicle() const; diff --git a/panda/src/bullet/bulletWheel.h b/panda/src/bullet/bulletWheel.h index 2f3fcc2e71..2b6ecd8a80 100644 --- a/panda/src/bullet/bulletWheel.h +++ b/panda/src/bullet/bulletWheel.h @@ -39,6 +39,15 @@ PUBLISHED: INLINE LPoint3 get_hard_point_ws() const; INLINE PandaNode *get_ground_object() const; + MAKE_PROPERTY(in_contact, is_in_contact); + MAKE_PROPERTY(suspension_length, get_suspension_length); + MAKE_PROPERTY(contact_normal_ws, get_contact_normal_ws); + MAKE_PROPERTY(wheel_direction_ws, get_wheel_direction_ws); + MAKE_PROPERTY(wheel_axle_ws, get_wheel_axle_ws); + MAKE_PROPERTY(contact_point_ws, get_contact_point_ws); + MAKE_PROPERTY(hard_point_ws, get_hard_point_ws); + MAKE_PROPERTY(ground_object, get_ground_object); + public: BulletWheelRaycastInfo(btWheelInfo::RaycastInfo &info); @@ -105,6 +114,32 @@ PUBLISHED: PandaNode *get_node() const; BulletWheelRaycastInfo get_raycast_info() const; + MAKE_PROPERTY(raycast_info, get_raycast_info); + MAKE_PROPERTY(suspension_rest_length, get_suspension_rest_length); + MAKE_PROPERTY(suspension_stiffness, get_suspension_stiffness, set_suspension_stiffness); + MAKE_PROPERTY(max_suspension_travel_cm, get_max_suspension_travel_cm, set_max_suspension_travel_cm); + MAKE_PROPERTY(friction_slip, get_friction_slip, set_friction_slip); + MAKE_PROPERTY(max_suspension_force, get_max_suspension_force, set_max_suspension_force); + MAKE_PROPERTY(wheels_damping_compression, get_wheels_damping_compression, set_wheels_damping_compression); + MAKE_PROPERTY(wheels_damping_relaxation, get_wheels_damping_relaxation, set_wheels_damping_relaxation); + MAKE_PROPERTY(roll_influence, get_roll_influence, set_roll_influence); + MAKE_PROPERTY(wheel_radius, get_wheel_radius, set_wheel_radius); + MAKE_PROPERTY(steering, get_steering, set_steering); + MAKE_PROPERTY(rotation, get_rotation, set_rotation); + MAKE_PROPERTY(delta_rotation, get_delta_rotation, set_delta_rotation); + MAKE_PROPERTY(engine_force, get_engine_force, set_engine_force); + MAKE_PROPERTY(brake, get_brake, set_brake); + MAKE_PROPERTY(skid_info, get_skid_info, set_skid_info); + MAKE_PROPERTY(wheels_suspension_force, get_wheels_suspension_force, set_wheels_suspension_force); + MAKE_PROPERTY(suspension_relative_velocity, get_suspension_relative_velocity, set_suspension_relative_velocity); + MAKE_PROPERTY(clipped_inv_connection_point_cs, get_clipped_inv_connection_point_cs, set_clipped_inv_connection_point_cs); + MAKE_PROPERTY(chassis_connection_point_cs, get_chassis_connection_point_cs, set_chassis_connection_point_cs); + MAKE_PROPERTY(wheel_direction_cs, get_wheel_direction_cs, set_wheel_direction_cs); + MAKE_PROPERTY(wheel_axle_cs, get_wheel_axle_cs, set_wheel_axle_cs); + MAKE_PROPERTY(world_transform, get_world_transform, set_world_transform); + MAKE_PROPERTY(front_wheel, is_front_wheel, set_front_wheel); + MAKE_PROPERTY(node, get_node, set_node); + public: BulletWheel(btWheelInfo &info); diff --git a/panda/src/bullet/bulletWorld.I b/panda/src/bullet/bulletWorld.I index e93dc0d176..c2ecd2cec3 100644 --- a/panda/src/bullet/bulletWorld.I +++ b/panda/src/bullet/bulletWorld.I @@ -55,21 +55,12 @@ INLINE BulletWorld:: */ INLINE void BulletWorld:: set_debug_node(BulletDebugNode *node) { - nassertv(node); - - _debug = node; - _world->setDebugDrawer(&(_debug->_drawer)); -} - -/** - * - */ -INLINE void BulletWorld:: -clear_debug_node() { - - _debug = NULL; - _world->setDebugDrawer(NULL); + if (node != _debug) { + clear_debug_node(); + _debug = node; + _world->setDebugDrawer(&(_debug->_drawer)); + } } /** @@ -81,6 +72,15 @@ get_debug_node() const { return _debug; } +/** + * + */ +INLINE bool BulletWorld:: +has_debug_node() const { + + return _debug != NULL; +} + /** * */ diff --git a/panda/src/bullet/bulletWorld.cxx b/panda/src/bullet/bulletWorld.cxx index e7f6f3e267..a7942aee02 100644 --- a/panda/src/bullet/bulletWorld.cxx +++ b/panda/src/bullet/bulletWorld.cxx @@ -17,6 +17,7 @@ #include "bulletSoftBodyWorldInfo.h" #include "collideMask.h" +#include "lightMutexHolder.h" #define clamp(x, x_min, x_max) max(min(x, x_max), x_min) @@ -24,7 +25,6 @@ TypeHandle BulletWorld::_type_handle; PStatCollector BulletWorld::_pstat_physics("App:Bullet:DoPhysics"); PStatCollector BulletWorld::_pstat_simulation("App:Bullet:DoPhysics:Simulation"); -PStatCollector BulletWorld::_pstat_debug("App:Bullet:DoPhysics:Debug"); PStatCollector BulletWorld::_pstat_p2b("App:Bullet:DoPhysics:SyncP2B"); PStatCollector BulletWorld::_pstat_b2p("App:Bullet:DoPhysics:SyncB2P"); @@ -127,6 +127,19 @@ get_world_info() { return BulletSoftBodyWorldInfo(_info); } +/** + * Removes a debug node that has been assigned to this BulletWorld. + */ +void BulletWorld:: +clear_debug_node() { + if (_debug != nullptr) { + LightMutexHolder holder(_debug->_lock); + _debug->_debug_world = nullptr; + _world->setDebugDrawer(nullptr); + _debug = nullptr; + } +} + /** * */ @@ -184,9 +197,7 @@ do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) { // Render debug if (_debug) { - _pstat_debug.start(); _debug->sync_b2p(_world); - _pstat_debug.stop(); } _pstat_physics.stop(); diff --git a/panda/src/bullet/bulletWorld.h b/panda/src/bullet/bulletWorld.h index 8d39186bb0..558d086b37 100644 --- a/panda/src/bullet/bulletWorld.h +++ b/panda/src/bullet/bulletWorld.h @@ -63,8 +63,9 @@ PUBLISHED: // Debug INLINE void set_debug_node(BulletDebugNode *node); - INLINE void clear_debug_node(); + void clear_debug_node(); INLINE BulletDebugNode *get_debug_node() const; + INLINE bool has_debug_node() const; // AttachRemove void attach(TypedObject *object); @@ -159,6 +160,17 @@ PUBLISHED: FA_callback, }; + MAKE_PROPERTY(gravity, get_gravity, set_gravity); + MAKE_PROPERTY(world_info, get_world_info); + MAKE_PROPERTY2(debug_node, has_debug_node, get_debug_node, set_debug_node, clear_debug_node); + MAKE_SEQ_PROPERTY(ghosts, get_num_ghosts, get_ghost); + MAKE_SEQ_PROPERTY(rigid_bodies, get_num_rigid_bodies, get_rigid_body); + MAKE_SEQ_PROPERTY(soft_bodies, get_num_soft_bodies, get_soft_body); + MAKE_SEQ_PROPERTY(characters, get_num_characters, get_character); + MAKE_SEQ_PROPERTY(vehicles, get_num_vehicles, get_vehicle); + MAKE_SEQ_PROPERTY(constraints, get_num_constraints, get_constraint); + MAKE_SEQ_PROPERTY(manifolds, get_num_manifolds, get_manifold); + PUBLISHED: // Deprecated methods, will become private soon void attach_ghost(BulletGhostNode *node); void remove_ghost(BulletGhostNode *node); @@ -196,7 +208,6 @@ private: static PStatCollector _pstat_physics; static PStatCollector _pstat_simulation; - static PStatCollector _pstat_debug; static PStatCollector _pstat_p2b; static PStatCollector _pstat_b2p; diff --git a/panda/src/bullet/config_bullet.cxx b/panda/src/bullet/config_bullet.cxx index 06cebf3928..9878a9f038 100644 --- a/panda/src/bullet/config_bullet.cxx +++ b/panda/src/bullet/config_bullet.cxx @@ -188,6 +188,13 @@ init_libbullet() { BulletSphereShape::register_with_read_factory(); BulletTriangleMesh::register_with_read_factory(); BulletTriangleMeshShape::register_with_read_factory(); + BulletCylinderShape::register_with_read_factory(); + BulletCapsuleShape::register_with_read_factory(); + BulletConeShape::register_with_read_factory(); + BulletHeightfieldShape::register_with_read_factory(); + BulletConvexPointCloudShape::register_with_read_factory(); + BulletMinkowskiSumShape::register_with_read_factory(); + BulletMultiSphereShape::register_with_read_factory(); // Custom contact callbacks gContactAddedCallback = contact_added_callback; diff --git a/panda/src/chan/README.md b/panda/src/chan/README.md new file mode 100644 index 0000000000..298bdf4345 --- /dev/null +++ b/panda/src/chan/README.md @@ -0,0 +1,5 @@ +This package contains the animation channels. This defines the various +kinds of AnimChannels that may be defined, as well as the MovingPart +class which binds to the channels and plays the animation. This is a +support library for char, as well as any other libraries that want to +define objects whose values change over time. diff --git a/panda/src/chan/animBundle.h b/panda/src/chan/animBundle.h index 2bcaec1896..799d262ab8 100644 --- a/panda/src/chan/animBundle.h +++ b/panda/src/chan/animBundle.h @@ -31,7 +31,7 @@ protected: AnimBundle(AnimGroup *parent, const AnimBundle ©); PUBLISHED: - INLINE AnimBundle(const string &name, PN_stdfloat fps, int num_frames); + INLINE explicit AnimBundle(const string &name, PN_stdfloat fps, int num_frames); PT(AnimBundle) copy_bundle() const; diff --git a/panda/src/chan/animBundleNode.h b/panda/src/chan/animBundleNode.h index cb6f577a4c..38df110191 100644 --- a/panda/src/chan/animBundleNode.h +++ b/panda/src/chan/animBundleNode.h @@ -28,7 +28,7 @@ */ class EXPCL_PANDA_CHAN AnimBundleNode : public PandaNode { PUBLISHED: - INLINE AnimBundleNode(const string &name, AnimBundle *bundle); + INLINE explicit AnimBundleNode(const string &name, AnimBundle *bundle); protected: INLINE AnimBundleNode(); diff --git a/panda/src/chan/animChannelMatrixXfmTable.h b/panda/src/chan/animChannelMatrixXfmTable.h index 431d3ef18f..e3a81a7b2a 100644 --- a/panda/src/chan/animChannelMatrixXfmTable.h +++ b/panda/src/chan/animChannelMatrixXfmTable.h @@ -34,7 +34,7 @@ protected: AnimChannelMatrixXfmTable(AnimGroup *parent, const AnimChannelMatrixXfmTable ©); PUBLISHED: - AnimChannelMatrixXfmTable(AnimGroup *parent, const string &name); + explicit AnimChannelMatrixXfmTable(AnimGroup *parent, const string &name); virtual ~AnimChannelMatrixXfmTable(); public: diff --git a/panda/src/chan/animGroup.h b/panda/src/chan/animGroup.h index af81aba3b1..50067213d8 100644 --- a/panda/src/chan/animGroup.h +++ b/panda/src/chan/animGroup.h @@ -37,19 +37,19 @@ protected: PUBLISHED: // This is the normal AnimGroup constructor. - AnimGroup(AnimGroup *parent, const string &name); + explicit AnimGroup(AnimGroup *parent, const string &name); virtual ~AnimGroup(); int get_num_children() const; AnimGroup *get_child(int n) const; MAKE_SEQ(get_children, get_num_children, get_child); - MAKE_SEQ_PROPERTY(children, get_num_children, get_child); - AnimGroup *get_child_named(const string &name) const; AnimGroup *find_child(const string &name) const; void sort_descendants(); + MAKE_SEQ_PROPERTY(children, get_num_children, get_child); + public: virtual TypeHandle get_value_type() const; diff --git a/panda/src/chan/bindAnimRequest.h b/panda/src/chan/bindAnimRequest.h index afb57d46cd..fed5e5d596 100644 --- a/panda/src/chan/bindAnimRequest.h +++ b/panda/src/chan/bindAnimRequest.h @@ -30,13 +30,13 @@ public: ALLOC_DELETED_CHAIN(BindAnimRequest); PUBLISHED: - BindAnimRequest(const string &name, - const Filename &filename, - const LoaderOptions &options, - Loader *loader, - AnimControl *control, - int hierarchy_match_flags, - const PartSubset &subset); + explicit BindAnimRequest(const string &name, + const Filename &filename, + const LoaderOptions &options, + Loader *loader, + AnimControl *control, + int hierarchy_match_flags, + const PartSubset &subset); protected: virtual DoneStatus do_task(); diff --git a/panda/src/chan/partBundle.h b/panda/src/chan/partBundle.h index 0d22fe5e73..60d2b66b8b 100644 --- a/panda/src/chan/partBundle.h +++ b/panda/src/chan/partBundle.h @@ -55,7 +55,7 @@ protected: PartBundle(const PartBundle ©); PUBLISHED: - PartBundle(const string &name = ""); + explicit PartBundle(const string &name = ""); virtual PartGroup *make_copy() const; INLINE CPT(AnimPreloadTable) get_anim_preload() const; diff --git a/panda/src/chan/partBundleNode.h b/panda/src/chan/partBundleNode.h index f4cafaf056..231c1175ee 100644 --- a/panda/src/chan/partBundleNode.h +++ b/panda/src/chan/partBundleNode.h @@ -34,7 +34,7 @@ */ class EXPCL_PANDA_CHAN PartBundleNode : public PandaNode { PUBLISHED: - INLINE PartBundleNode(const string &name, PartBundle *bundle); + INLINE explicit PartBundleNode(const string &name, PartBundle *bundle); protected: INLINE PartBundleNode(); diff --git a/panda/src/chan/partGroup.h b/panda/src/chan/partGroup.h index 0d5967a39a..ae5f919b7c 100644 --- a/panda/src/chan/partGroup.h +++ b/panda/src/chan/partGroup.h @@ -60,7 +60,7 @@ protected: PUBLISHED: // This is the normal PartGroup constructor. - PartGroup(PartGroup *parent, const string &name); + explicit PartGroup(PartGroup *parent, const string &name); virtual ~PartGroup(); virtual bool is_character_joint() const; @@ -70,12 +70,13 @@ PUBLISHED: int get_num_children() const; PartGroup *get_child(int n) const; MAKE_SEQ(get_children, get_num_children, get_child); - MAKE_SEQ_PROPERTY(children, get_num_children, get_child); PartGroup *get_child_named(const string &name) const; PartGroup *find_child(const string &name) const; void sort_descendants(); + MAKE_SEQ_PROPERTY(children, get_num_children, get_child); + bool apply_freeze(const TransformState *transform); virtual bool apply_freeze_matrix(const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale); virtual bool apply_freeze_scalar(PN_stdfloat value); diff --git a/panda/src/char/character.h b/panda/src/char/character.h index ca040d586b..1903d39ab8 100644 --- a/panda/src/char/character.h +++ b/panda/src/char/character.h @@ -41,7 +41,7 @@ protected: Character(const Character ©, bool copy_bundles); PUBLISHED: - Character(const string &name); + explicit Character(const string &name); virtual ~Character(); public: diff --git a/panda/src/char/characterJoint.h b/panda/src/char/characterJoint.h index 10f73a22a3..759d175c7e 100644 --- a/panda/src/char/characterJoint.h +++ b/panda/src/char/characterJoint.h @@ -35,9 +35,9 @@ protected: CharacterJoint(const CharacterJoint ©); PUBLISHED: - CharacterJoint(Character *character, - PartBundle *root, PartGroup *parent, const string &name, - const LMatrix4 &default_value); + explicit CharacterJoint(Character *character, PartBundle *root, + PartGroup *parent, const string &name, + const LMatrix4 &default_value); virtual ~CharacterJoint(); public: diff --git a/panda/src/char/characterJointBundle.h b/panda/src/char/characterJointBundle.h index fead649eb0..4c68fb9b3d 100644 --- a/panda/src/char/characterJointBundle.h +++ b/panda/src/char/characterJointBundle.h @@ -30,7 +30,7 @@ protected: INLINE CharacterJointBundle(const CharacterJointBundle ©); PUBLISHED: - CharacterJointBundle(const string &name = ""); + explicit CharacterJointBundle(const string &name = ""); virtual ~CharacterJointBundle(); PUBLISHED: diff --git a/panda/src/char/characterJointEffect.cxx b/panda/src/char/characterJointEffect.cxx index ec0dec9de3..33441e35c2 100644 --- a/panda/src/char/characterJointEffect.cxx +++ b/panda/src/char/characterJointEffect.cxx @@ -122,8 +122,10 @@ void CharacterJointEffect:: cull_callback(CullTraverser *trav, CullTraverserData &data, CPT(TransformState) &node_transform, CPT(RenderState) &) const { - CPT(TransformState) dummy_transform = TransformState::make_identity(); - adjust_transform(dummy_transform, node_transform, data.node()); + if (_character.is_valid_pointer()) { + _character->update(); + } + node_transform = data.node()->get_transform(); } /** @@ -147,7 +149,7 @@ has_adjust_transform() const { void CharacterJointEffect:: adjust_transform(CPT(TransformState) &net_transform, CPT(TransformState) &node_transform, - PandaNode *node) const { + const PandaNode *node) const { if (_character.is_valid_pointer()) { _character->update(); } diff --git a/panda/src/char/characterJointEffect.h b/panda/src/char/characterJointEffect.h index 031b75237c..0df4cea6fd 100644 --- a/panda/src/char/characterJointEffect.h +++ b/panda/src/char/characterJointEffect.h @@ -53,7 +53,7 @@ public: virtual bool has_adjust_transform() const; virtual void adjust_transform(CPT(TransformState) &net_transform, CPT(TransformState) &node_transform, - PandaNode *node) const; + const PandaNode *node) const; protected: virtual int compare_to_impl(const RenderEffect *other) const; diff --git a/panda/src/char/characterSlider.h b/panda/src/char/characterSlider.h index fe5129bca4..fe7e38945e 100644 --- a/panda/src/char/characterSlider.h +++ b/panda/src/char/characterSlider.h @@ -31,7 +31,7 @@ protected: CharacterSlider(const CharacterSlider ©); PUBLISHED: - CharacterSlider(PartGroup *parent, const string &name); + explicit CharacterSlider(PartGroup *parent, const string &name); virtual ~CharacterSlider(); virtual PartGroup *make_copy() const; diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index 6de003d2aa..ebc9255d63 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -277,7 +277,21 @@ process_events() { break; } - [NSApp sendEvent: event]; + // If we're in fullscreen mode, send mouse events directly to the window. + NSEventType type = [event type]; + if (_properties.get_fullscreen() && ( + type == NSLeftMouseDown || type == NSLeftMouseUp || + type == NSRightMouseDown || type == NSRightMouseUp || + type == NSOtherMouseDown || type == NSOtherMouseUp || + type == NSLeftMouseDragged || + type == NSRightMouseDragged || + type == NSOtherMouseDragged || + type == NSMouseMoved || + type == NSScrollWheel)) { + [_window sendEvent: event]; + } else { + [NSApp sendEvent: event]; + } } if (_window != nil) { diff --git a/panda/src/collide/collisionBox.I b/panda/src/collide/collisionBox.I index d35bfc906d..62c58cbceb 100644 --- a/panda/src/collide/collisionBox.I +++ b/panda/src/collide/collisionBox.I @@ -12,7 +12,7 @@ */ /** - * Create the Box by giving a Center and distances of of each of the sides of + * Create the Box by giving a Center and distances of each of the sides of * box from the Center. */ INLINE CollisionBox:: diff --git a/panda/src/collide/collisionBox.cxx b/panda/src/collide/collisionBox.cxx index b358d8b359..b55988c352 100644 --- a/panda/src/collide/collisionBox.cxx +++ b/panda/src/collide/collisionBox.cxx @@ -199,7 +199,7 @@ PT(CollisionEntry) CollisionBox:: test_intersection_from_sphere(const CollisionEntry &entry) const { const CollisionSphere *sphere; - DCAST_INTO_R(sphere, entry.get_from(), 0); + DCAST_INTO_R(sphere, entry.get_from(), NULL); CPT(TransformState) wrt_space = entry.get_wrt_space(); CPT(TransformState) wrt_prev_space = entry.get_wrt_prev_space(); @@ -402,7 +402,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionBox:: test_intersection_from_ray(const CollisionEntry &entry) const { const CollisionRay *ray; - DCAST_INTO_R(ray, entry.get_from(), 0); + DCAST_INTO_R(ray, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); LPoint3 from_origin = ray->get_origin() * wrt_mat; @@ -483,7 +483,7 @@ test_intersection_from_ray(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionBox:: test_intersection_from_segment(const CollisionEntry &entry) const { const CollisionSegment *seg; - DCAST_INTO_R(seg, entry.get_from(), 0); + DCAST_INTO_R(seg, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); LPoint3 from_origin = seg->get_point_a() * wrt_mat; @@ -564,7 +564,7 @@ test_intersection_from_segment(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionBox:: test_intersection_from_box(const CollisionEntry &entry) const { const CollisionBox *box; - DCAST_INTO_R(box, entry.get_from(), 0); + DCAST_INTO_R(box, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); diff --git a/panda/src/collide/collisionBox.h b/panda/src/collide/collisionBox.h index 8a8cd3d0be..c5bb22be88 100644 --- a/panda/src/collide/collisionBox.h +++ b/panda/src/collide/collisionBox.h @@ -26,9 +26,9 @@ */ class EXPCL_PANDA_COLLIDE CollisionBox : public CollisionSolid { PUBLISHED: - INLINE CollisionBox(const LPoint3 ¢er, - PN_stdfloat x, PN_stdfloat y, PN_stdfloat z); - INLINE CollisionBox(const LPoint3 &min, const LPoint3 &max); + INLINE explicit CollisionBox(const LPoint3 ¢er, + PN_stdfloat x, PN_stdfloat y, PN_stdfloat z); + INLINE explicit CollisionBox(const LPoint3 &min, const LPoint3 &max); virtual LPoint3 get_collision_origin() const; diff --git a/panda/src/collide/collisionFloorMesh.cxx b/panda/src/collide/collisionFloorMesh.cxx index fcec727778..d523875727 100644 --- a/panda/src/collide/collisionFloorMesh.cxx +++ b/panda/src/collide/collisionFloorMesh.cxx @@ -125,7 +125,7 @@ compute_internal_bounds() const { PT(CollisionEntry) CollisionFloorMesh:: test_intersection_from_ray(const CollisionEntry &entry) const { const CollisionRay *ray; - DCAST_INTO_R(ray, entry.get_from(), 0); + DCAST_INTO_R(ray, entry.get_from(), NULL); LPoint3 from_origin = ray->get_origin() * entry.get_wrt_mat(); double fx = from_origin[0]; @@ -190,7 +190,7 @@ test_intersection_from_ray(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionFloorMesh:: test_intersection_from_sphere(const CollisionEntry &entry) const { const CollisionSphere *sphere; - DCAST_INTO_R(sphere, entry.get_from(), 0); + DCAST_INTO_R(sphere, entry.get_from(), NULL); LPoint3 from_origin = sphere->get_center() * entry.get_wrt_mat(); double fx = from_origin[0]; diff --git a/panda/src/collide/collisionHandlerFluidPusher.cxx b/panda/src/collide/collisionHandlerFluidPusher.cxx index eea0cc1f22..0a48381d01 100644 --- a/panda/src/collide/collisionHandlerFluidPusher.cxx +++ b/panda/src/collide/collisionHandlerFluidPusher.cxx @@ -136,7 +136,7 @@ handle_entries() { // currently we only support spheres as the collider const CollisionSphere *sphere; - DCAST_INTO_R(sphere, entries.front()->get_from(), 0); + DCAST_INTO_R(sphere, entries.front()->get_from(), false); from_node_path.set_pos(wrt_node, 0,0,0); LPoint3 sphere_offset = (sphere->get_center() * diff --git a/panda/src/collide/collisionInvSphere.cxx b/panda/src/collide/collisionInvSphere.cxx index 913ef32589..35a73225dd 100644 --- a/panda/src/collide/collisionInvSphere.cxx +++ b/panda/src/collide/collisionInvSphere.cxx @@ -92,7 +92,7 @@ compute_internal_bounds() const { PT(CollisionEntry) CollisionInvSphere:: test_intersection_from_sphere(const CollisionEntry &entry) const { const CollisionSphere *sphere; - DCAST_INTO_R(sphere, entry.get_from(), 0); + DCAST_INTO_R(sphere, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -144,7 +144,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionInvSphere:: test_intersection_from_line(const CollisionEntry &entry) const { const CollisionLine *line; - DCAST_INTO_R(line, entry.get_from(), 0); + DCAST_INTO_R(line, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -185,7 +185,7 @@ test_intersection_from_line(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionInvSphere:: test_intersection_from_ray(const CollisionEntry &entry) const { const CollisionRay *ray; - DCAST_INTO_R(ray, entry.get_from(), 0); + DCAST_INTO_R(ray, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -228,7 +228,7 @@ test_intersection_from_ray(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionInvSphere:: test_intersection_from_segment(const CollisionEntry &entry) const { const CollisionSegment *segment; - DCAST_INTO_R(segment, entry.get_from(), 0); + DCAST_INTO_R(segment, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); diff --git a/panda/src/collide/collisionInvSphere.h b/panda/src/collide/collisionInvSphere.h index bdf08f9009..ffc9628a5b 100644 --- a/panda/src/collide/collisionInvSphere.h +++ b/panda/src/collide/collisionInvSphere.h @@ -26,8 +26,8 @@ */ class EXPCL_PANDA_COLLIDE CollisionInvSphere : public CollisionSphere { PUBLISHED: - INLINE CollisionInvSphere(const LPoint3 ¢er, PN_stdfloat radius); - INLINE CollisionInvSphere(PN_stdfloat cx, PN_stdfloat cy, PN_stdfloat cz, PN_stdfloat radius); + INLINE explicit CollisionInvSphere(const LPoint3 ¢er, PN_stdfloat radius); + INLINE explicit CollisionInvSphere(PN_stdfloat cx, PN_stdfloat cy, PN_stdfloat cz, PN_stdfloat radius); protected: INLINE CollisionInvSphere(); diff --git a/panda/src/collide/collisionLevelState.I b/panda/src/collide/collisionLevelState.I index 5ac7cb9063..5dc74aa691 100644 --- a/panda/src/collide/collisionLevelState.I +++ b/panda/src/collide/collisionLevelState.I @@ -111,10 +111,12 @@ any_in_bounds() { } #endif // NDEBUG - CPT(BoundingVolume) node_bv = node()->get_bounds(); + PandaNode *pnode = node(); + + CPT(BoundingVolume) node_bv = pnode->get_bounds(); if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - const GeometricBoundingVolume *node_gbv; - DCAST_INTO_R(node_gbv, node_bv, false); + const GeometricBoundingVolume *node_gbv = (const GeometricBoundingVolume *)node_bv.p(); + CollideMask this_mask = pnode->get_net_collide_mask(); int num_colliders = get_num_colliders(); for (int c = 0; c < num_colliders; c++) { @@ -125,10 +127,10 @@ any_in_bounds() { // Don't even bother testing the bounding volume if there are no // collide bits in common between our collider and this node. CollideMask from_mask = cnode->get_from_collide_mask() & _include_mask; - if (!(from_mask & node()->get_net_collide_mask()).is_zero()) { + if (!(from_mask & this_mask).is_zero()) { // Also don't test a node with itself, or with any of its // descendants. - if (node() == cnode) { + if (pnode == cnode) { #ifndef NDEBUG if (collide_cat.is_spam()) { indent(collide_cat.spam(false), indent_level) diff --git a/panda/src/collide/collisionLevelStateBase.h b/panda/src/collide/collisionLevelStateBase.h index bde70562c2..4c2abccb58 100644 --- a/panda/src/collide/collisionLevelStateBase.h +++ b/panda/src/collide/collisionLevelStateBase.h @@ -51,7 +51,7 @@ public: INLINE CollisionLevelStateBase(const NodePath &node_path); INLINE CollisionLevelStateBase(const CollisionLevelStateBase &parent, - PandaNode *child); + PandaNode *child); INLINE CollisionLevelStateBase(const CollisionLevelStateBase ©); INLINE void operator = (const CollisionLevelStateBase ©); diff --git a/panda/src/collide/collisionLine.h b/panda/src/collide/collisionLine.h index 7d08521ef4..71904192de 100644 --- a/panda/src/collide/collisionLine.h +++ b/panda/src/collide/collisionLine.h @@ -25,9 +25,9 @@ class EXPCL_PANDA_COLLIDE CollisionLine : public CollisionRay { PUBLISHED: INLINE CollisionLine(); - INLINE CollisionLine(const LPoint3 &origin, const LVector3 &direction); - INLINE CollisionLine(PN_stdfloat ox, PN_stdfloat oy, PN_stdfloat oz, - PN_stdfloat dx, PN_stdfloat dy, PN_stdfloat dz); + INLINE explicit CollisionLine(const LPoint3 &origin, const LVector3 &direction); + INLINE explicit CollisionLine(PN_stdfloat ox, PN_stdfloat oy, PN_stdfloat oz, + PN_stdfloat dx, PN_stdfloat dy, PN_stdfloat dz); public: INLINE CollisionLine(const CollisionLine ©); diff --git a/panda/src/collide/collisionNode.I b/panda/src/collide/collisionNode.I index 01f6e95311..51fe8002fb 100644 --- a/panda/src/collide/collisionNode.I +++ b/panda/src/collide/collisionNode.I @@ -65,7 +65,7 @@ clear_solids() { /** * */ -INLINE int CollisionNode:: +INLINE size_t CollisionNode:: get_num_solids() const { return _solids.size(); } @@ -74,8 +74,8 @@ get_num_solids() const { * */ INLINE CPT(CollisionSolid) CollisionNode:: -get_solid(int n) const { - nassertr(n >= 0 && n < get_num_solids(), NULL); +get_solid(size_t n) const { + nassertr(n < get_num_solids(), nullptr); return _solids[n].get_read_pointer(); } @@ -83,8 +83,8 @@ get_solid(int n) const { * */ INLINE PT(CollisionSolid) CollisionNode:: -modify_solid(int n) { - nassertr(n >= 0 && n < get_num_solids(), NULL); +modify_solid(size_t n) { + nassertr(n < get_num_solids(), nullptr); mark_internal_bounds_stale(); return _solids[n].get_write_pointer(); } @@ -93,19 +93,31 @@ modify_solid(int n) { * Replaces the solid with the indicated index. */ INLINE void CollisionNode:: -set_solid(int n, CollisionSolid *solid) { - nassertv(n >= 0 && n < get_num_solids()); +set_solid(size_t n, CollisionSolid *solid) { + nassertv(n < get_num_solids()); _solids[n] = solid; mark_internal_bounds_stale(); } +/** + * Inserts the indicated solid to the node at the indicated position. + */ +INLINE void CollisionNode:: +insert_solid(size_t n, const CollisionSolid *solid) { + if (n > _solids.size()) { + n = _solids.size(); + } + _solids.insert(_solids.begin() + n, (CollisionSolid *)solid); + mark_internal_bounds_stale(); +} + /** * Removes the solid with the indicated index. This will shift all subsequent * indices down by one. */ INLINE void CollisionNode:: -remove_solid(int n) { - nassertv(n >= 0 && n < get_num_solids()); +remove_solid(size_t n) { + nassertv(n < get_num_solids()); _solids.erase(_solids.begin() + n); mark_internal_bounds_stale(); } @@ -114,7 +126,7 @@ remove_solid(int n) { * Adds the indicated solid to the node. Returns the index of the new solid * within the node's list of solids. */ -INLINE int CollisionNode:: +INLINE size_t CollisionNode:: add_solid(const CollisionSolid *solid) { _solids.push_back((CollisionSolid *)solid); mark_internal_bounds_stale(); diff --git a/panda/src/collide/collisionNode.cxx b/panda/src/collide/collisionNode.cxx index da5aa38cc6..9de94617b3 100644 --- a/panda/src/collide/collisionNode.cxx +++ b/panda/src/collide/collisionNode.cxx @@ -196,7 +196,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { if (respect_prev_transform) { // Determine the previous frame's position, relative to the current // position. - NodePath node_path = data._node_path.get_node_path(); + NodePath node_path = data.get_node_path(); CPT(TransformState) transform = node_path.get_net_transform()->invert_compose(node_path.get_net_prev_transform()); if (!transform->is_identity()) { diff --git a/panda/src/collide/collisionNode.h b/panda/src/collide/collisionNode.h index 6c95e6d435..e9a61b404d 100644 --- a/panda/src/collide/collisionNode.h +++ b/panda/src/collide/collisionNode.h @@ -29,7 +29,7 @@ */ class EXPCL_PANDA_COLLIDE CollisionNode : public PandaNode { PUBLISHED: - CollisionNode(const string &name); + explicit CollisionNode(const string &name); protected: CollisionNode(const CollisionNode ©); @@ -61,20 +61,22 @@ PUBLISHED: set_into_collide_mask); INLINE void clear_solids(); - INLINE int get_num_solids() const; - INLINE CPT(CollisionSolid) get_solid(int n) const; + INLINE size_t get_num_solids() const; + INLINE CPT(CollisionSolid) get_solid(size_t n) const; MAKE_SEQ(get_solids, get_num_solids, get_solid); - INLINE PT(CollisionSolid) modify_solid(int n); - INLINE void set_solid(int n, CollisionSolid *solid); - INLINE void remove_solid(int n); - INLINE int add_solid(const CollisionSolid *solid); - MAKE_SEQ_PROPERTY(solids, get_num_solids, get_solid, set_solid, remove_solid); + INLINE PT(CollisionSolid) modify_solid(size_t n); + INLINE void set_solid(size_t n, CollisionSolid *solid); + INLINE void insert_solid(size_t n, const CollisionSolid *solid); + INLINE void remove_solid(size_t n); + INLINE size_t add_solid(const CollisionSolid *solid); + MAKE_SEQ_PROPERTY(solids, get_num_solids, get_solid, set_solid, remove_solid, insert_solid); INLINE int get_collider_sort() const; INLINE void set_collider_sort(int sort); MAKE_PROPERTY(collider_sort, get_collider_sort, set_collider_sort); INLINE static CollideMask get_default_collide_mask(); + MAKE_PROPERTY(default_collide_mask, get_default_collide_mask); protected: virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, @@ -93,6 +95,8 @@ private: typedef pvector< COWPT(CollisionSolid) > Solids; Solids _solids; + friend class CollisionTraverser; + public: static void register_with_read_factory(); virtual void write_datagram(BamWriter *manager, Datagram &dg); diff --git a/panda/src/collide/collisionParabola.h b/panda/src/collide/collisionParabola.h index 5d6cdedb89..2f72518ac4 100644 --- a/panda/src/collide/collisionParabola.h +++ b/panda/src/collide/collisionParabola.h @@ -32,7 +32,7 @@ class LensNode; class EXPCL_PANDA_COLLIDE CollisionParabola : public CollisionSolid { PUBLISHED: INLINE CollisionParabola(); - INLINE CollisionParabola(const LParabola ¶bola, PN_stdfloat t1, PN_stdfloat t2); + INLINE explicit CollisionParabola(const LParabola ¶bola, PN_stdfloat t1, PN_stdfloat t2); virtual LPoint3 get_collision_origin() const; diff --git a/panda/src/collide/collisionPlane.cxx b/panda/src/collide/collisionPlane.cxx index c551464960..d722adde7b 100644 --- a/panda/src/collide/collisionPlane.cxx +++ b/panda/src/collide/collisionPlane.cxx @@ -107,7 +107,7 @@ compute_internal_bounds() const { PT(CollisionEntry) CollisionPlane:: test_intersection_from_sphere(const CollisionEntry &entry) const { const CollisionSphere *sphere; - DCAST_INTO_R(sphere, entry.get_from(), 0); + DCAST_INTO_R(sphere, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -146,7 +146,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionPlane:: test_intersection_from_line(const CollisionEntry &entry) const { const CollisionLine *line; - DCAST_INTO_R(line, entry.get_from(), 0); + DCAST_INTO_R(line, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -191,7 +191,7 @@ test_intersection_from_line(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionPlane:: test_intersection_from_ray(const CollisionEntry &entry) const { const CollisionRay *ray; - DCAST_INTO_R(ray, entry.get_from(), 0); + DCAST_INTO_R(ray, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -243,7 +243,7 @@ test_intersection_from_ray(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionPlane:: test_intersection_from_segment(const CollisionEntry &entry) const { const CollisionSegment *segment; - DCAST_INTO_R(segment, entry.get_from(), 0); + DCAST_INTO_R(segment, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -300,7 +300,7 @@ test_intersection_from_segment(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionPlane:: test_intersection_from_tube(const CollisionEntry &entry) const { const CollisionTube *tube; - DCAST_INTO_R(tube, entry.get_from(), 0); + DCAST_INTO_R(tube, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -371,7 +371,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionPlane:: test_intersection_from_parabola(const CollisionEntry &entry) const { const CollisionParabola *parabola; - DCAST_INTO_R(parabola, entry.get_from(), 0); + DCAST_INTO_R(parabola, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -437,7 +437,7 @@ test_intersection_from_parabola(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionPlane:: test_intersection_from_box(const CollisionEntry &entry) const { const CollisionBox *box; - DCAST_INTO_R(box, entry.get_from(), 0); + DCAST_INTO_R(box, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); diff --git a/panda/src/collide/collisionPolygon.cxx b/panda/src/collide/collisionPolygon.cxx index ff64dcc298..646a7b9439 100644 --- a/panda/src/collide/collisionPolygon.cxx +++ b/panda/src/collide/collisionPolygon.cxx @@ -364,7 +364,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { } const CollisionSphere *sphere; - DCAST_INTO_R(sphere, entry.get_from(), 0); + DCAST_INTO_R(sphere, entry.get_from(), NULL); CPT(TransformState) wrt_space = entry.get_wrt_space(); CPT(TransformState) wrt_prev_space = entry.get_wrt_prev_space(); @@ -545,7 +545,7 @@ test_intersection_from_line(const CollisionEntry &entry) const { } const CollisionLine *line; - DCAST_INTO_R(line, entry.get_from(), 0); + DCAST_INTO_R(line, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -613,7 +613,7 @@ test_intersection_from_ray(const CollisionEntry &entry) const { } const CollisionRay *ray; - DCAST_INTO_R(ray, entry.get_from(), 0); + DCAST_INTO_R(ray, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -686,7 +686,7 @@ test_intersection_from_segment(const CollisionEntry &entry) const { } const CollisionSegment *segment; - DCAST_INTO_R(segment, entry.get_from(), 0); + DCAST_INTO_R(segment, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -761,7 +761,7 @@ test_intersection_from_parabola(const CollisionEntry &entry) const { } const CollisionParabola *parabola; - DCAST_INTO_R(parabola, entry.get_from(), 0); + DCAST_INTO_R(parabola, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -846,7 +846,7 @@ test_intersection_from_parabola(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionPolygon:: test_intersection_from_box(const CollisionEntry &entry) const { const CollisionBox *box; - DCAST_INTO_R(box, entry.get_from(), 0); + DCAST_INTO_R(box, entry.get_from(), NULL); // To make things easier, transform the box into the coordinate space of the // plane. diff --git a/panda/src/collide/collisionRay.h b/panda/src/collide/collisionRay.h index 515ed7347d..d317761906 100644 --- a/panda/src/collide/collisionRay.h +++ b/panda/src/collide/collisionRay.h @@ -27,9 +27,9 @@ class EXPCL_PANDA_COLLIDE CollisionRay : public CollisionSolid { PUBLISHED: INLINE CollisionRay(); - INLINE CollisionRay(const LPoint3 &origin, const LVector3 &direction); - INLINE CollisionRay(PN_stdfloat ox, PN_stdfloat oy, PN_stdfloat oz, - PN_stdfloat dx, PN_stdfloat dy, PN_stdfloat dz); + INLINE explicit CollisionRay(const LPoint3 &origin, const LVector3 &direction); + INLINE explicit CollisionRay(PN_stdfloat ox, PN_stdfloat oy, PN_stdfloat oz, + PN_stdfloat dx, PN_stdfloat dy, PN_stdfloat dz); virtual LPoint3 get_collision_origin() const; diff --git a/panda/src/collide/collisionSegment.h b/panda/src/collide/collisionSegment.h index e2e00b14e5..429eff426d 100644 --- a/panda/src/collide/collisionSegment.h +++ b/panda/src/collide/collisionSegment.h @@ -31,9 +31,9 @@ class LensNode; class EXPCL_PANDA_COLLIDE CollisionSegment : public CollisionSolid { PUBLISHED: INLINE CollisionSegment(); - INLINE CollisionSegment(const LPoint3 &a, const LPoint3 &db); - INLINE CollisionSegment(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az, - PN_stdfloat bx, PN_stdfloat by, PN_stdfloat bz); + INLINE explicit CollisionSegment(const LPoint3 &a, const LPoint3 &db); + INLINE explicit CollisionSegment(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az, + PN_stdfloat bx, PN_stdfloat by, PN_stdfloat bz); virtual LPoint3 get_collision_origin() const; diff --git a/panda/src/collide/collisionSphere.cxx b/panda/src/collide/collisionSphere.cxx index 2e0802b350..139a39a4b1 100644 --- a/panda/src/collide/collisionSphere.cxx +++ b/panda/src/collide/collisionSphere.cxx @@ -120,7 +120,7 @@ compute_internal_bounds() const { PT(CollisionEntry) CollisionSphere:: test_intersection_from_sphere(const CollisionEntry &entry) const { const CollisionSphere *sphere; - DCAST_INTO_R(sphere, entry.get_from(), 0); + DCAST_INTO_R(sphere, entry.get_from(), NULL); CPT(TransformState) wrt_space = entry.get_wrt_space(); @@ -231,7 +231,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionSphere:: test_intersection_from_line(const CollisionEntry &entry) const { const CollisionLine *line; - DCAST_INTO_R(line, entry.get_from(), 0); + DCAST_INTO_R(line, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -271,7 +271,7 @@ test_intersection_from_line(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionSphere:: test_intersection_from_box(const CollisionEntry &entry) const { const CollisionBox *box; - DCAST_INTO_R(box, entry.get_from(), 0); + DCAST_INTO_R(box, entry.get_from(), NULL); // Instead of transforming the box into the sphere's coordinate space, we do // it the other way around. It's easier that way. @@ -347,7 +347,7 @@ test_intersection_from_box(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionSphere:: test_intersection_from_ray(const CollisionEntry &entry) const { const CollisionRay *ray; - DCAST_INTO_R(ray, entry.get_from(), 0); + DCAST_INTO_R(ray, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -394,7 +394,7 @@ test_intersection_from_ray(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionSphere:: test_intersection_from_segment(const CollisionEntry &entry) const { const CollisionSegment *segment; - DCAST_INTO_R(segment, entry.get_from(), 0); + DCAST_INTO_R(segment, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -443,7 +443,7 @@ test_intersection_from_segment(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionSphere:: test_intersection_from_tube(const CollisionEntry &entry) const { const CollisionTube *tube; - DCAST_INTO_R(tube, entry.get_from(), 0); + DCAST_INTO_R(tube, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -499,7 +499,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionSphere:: test_intersection_from_parabola(const CollisionEntry &entry) const { const CollisionParabola *parabola; - DCAST_INTO_R(parabola, entry.get_from(), 0); + DCAST_INTO_R(parabola, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); diff --git a/panda/src/collide/collisionSphere.h b/panda/src/collide/collisionSphere.h index f6cb45d804..04fa9c4e83 100644 --- a/panda/src/collide/collisionSphere.h +++ b/panda/src/collide/collisionSphere.h @@ -24,8 +24,8 @@ */ class EXPCL_PANDA_COLLIDE CollisionSphere : public CollisionSolid { PUBLISHED: - INLINE CollisionSphere(const LPoint3 ¢er, PN_stdfloat radius); - INLINE CollisionSphere(PN_stdfloat cx, PN_stdfloat cy, PN_stdfloat cz, PN_stdfloat radius); + INLINE explicit CollisionSphere(const LPoint3 ¢er, PN_stdfloat radius); + INLINE explicit CollisionSphere(PN_stdfloat cx, PN_stdfloat cy, PN_stdfloat cz, PN_stdfloat radius); virtual LPoint3 get_collision_origin() const; diff --git a/panda/src/collide/collisionTraverser.cxx b/panda/src/collide/collisionTraverser.cxx index 5934a5e541..74973d6764 100644 --- a/panda/src/collide/collisionTraverser.cxx +++ b/panda/src/collide/collisionTraverser.cxx @@ -57,7 +57,7 @@ public: inline bool operator () (int a, int b) const { const CollisionTraverser::OrderedColliderDef &ocd_a = _trav._ordered_colliders[a]; const CollisionTraverser::OrderedColliderDef &ocd_b = _trav._ordered_colliders[b]; - return DCAST(CollisionNode, ocd_a._node_path.node())->get_collider_sort() < DCAST(CollisionNode, ocd_b._node_path.node())->get_collider_sort(); + return ((const CollisionNode *)ocd_a._node_path.node())->get_collider_sort() < ((const CollisionNode *)ocd_b._node_path.node())->get_collider_sort(); } const CollisionTraverser &_trav; @@ -1117,31 +1117,45 @@ compare_collider_to_node(CollisionEntry &entry, } if (within_node_bounds) { + Thread *current_thread = Thread::get_current_thread(); + CollisionNode *cnode; DCAST_INTO_V(cnode, entry._into_node); + int num_solids = cnode->get_num_solids(); - collide_cat.spam() - << "Colliding against CollisionNode " << entry._into_node - << " which has " << num_solids << " collision solids.\n"; - for (int s = 0; s < num_solids; ++s) { - entry._into = cnode->get_solid(s); + if (collide_cat.is_spam()) { + collide_cat.spam() + << "Colliding against CollisionNode " << entry._into_node + << " which has " << num_solids << " collision solids.\n"; + } - // We should allow a collision test for solid into itself, because the - // solid might be simply instanced into multiple different - // CollisionNodes. We are already filtering out tests for a - // CollisionNode into itself. - CPT(BoundingVolume) solid_bv = entry._into->get_bounds(); - const GeometricBoundingVolume *solid_gbv = NULL; - if (num_solids > 1 && - solid_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - // Only bother to test against each solid's bounding volume if we have - // more than one solid in the node, as a slight optimization. (If the - // node contains just one solid, then the node's bounding volume, - // which we just tested, is the same as the solid's bounding volume.) - DCAST_INTO_V(solid_gbv, solid_bv); + // Only bother to test against each solid's bounding volume if we have + // more than one solid in the node, as a slight optimization. (If the + // node contains just one solid, then the node's bounding volume, which + // we just tested, is the same as the solid's bounding volume.) + if (num_solids == 1) { + entry._into = cnode->_solids[0].get_read_pointer(current_thread); + Colliders::const_iterator ci; + ci = _colliders.find(entry.get_from_node_path()); + nassertv(ci != _colliders.end()); + entry.test_intersection((*ci).second, this); + } else { + CollisionNode::Solids::const_iterator si; + for (si = cnode->_solids.begin(); si != cnode->_solids.end(); ++si) { + entry._into = (*si).get_read_pointer(current_thread); + + // We should allow a collision test for solid into itself, because the + // solid might be simply instanced into multiple different + // CollisionNodes. We are already filtering out tests for a + // CollisionNode into itself. + CPT(BoundingVolume) solid_bv = entry._into->get_bounds(); + const GeometricBoundingVolume *solid_gbv = nullptr; + if (solid_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { + solid_gbv = (const GeometricBoundingVolume *)solid_bv.p(); + } + + compare_collider_to_solid(entry, from_node_gbv, solid_gbv); } - - compare_collider_to_solid(entry, from_node_gbv, solid_gbv); } } } diff --git a/panda/src/collide/collisionTraverser.h b/panda/src/collide/collisionTraverser.h index d4f2b2a24b..8aaa0fd475 100644 --- a/panda/src/collide/collisionTraverser.h +++ b/panda/src/collide/collisionTraverser.h @@ -44,7 +44,7 @@ class CollisionEntry; */ class EXPCL_PANDA_COLLIDE CollisionTraverser : public Namable { PUBLISHED: - CollisionTraverser(const string &name = "ctrav"); + explicit CollisionTraverser(const string &name = "ctrav"); ~CollisionTraverser(); INLINE void set_respect_prev_transform(bool flag); diff --git a/panda/src/collide/collisionTube.cxx b/panda/src/collide/collisionTube.cxx index cb3f24f737..8e3b188aab 100644 --- a/panda/src/collide/collisionTube.cxx +++ b/panda/src/collide/collisionTube.cxx @@ -146,7 +146,7 @@ compute_internal_bounds() const { PT(CollisionEntry) CollisionTube:: test_intersection_from_sphere(const CollisionEntry &entry) const { const CollisionSphere *sphere; - DCAST_INTO_R(sphere, entry.get_from(), 0); + DCAST_INTO_R(sphere, entry.get_from(), NULL); CPT(TransformState) wrt_space = entry.get_wrt_space(); CPT(TransformState) wrt_prev_space = entry.get_wrt_prev_space(); @@ -224,7 +224,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionTube:: test_intersection_from_line(const CollisionEntry &entry) const { const CollisionLine *line; - DCAST_INTO_R(line, entry.get_from(), 0); + DCAST_INTO_R(line, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -272,7 +272,7 @@ test_intersection_from_line(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionTube:: test_intersection_from_ray(const CollisionEntry &entry) const { const CollisionRay *ray; - DCAST_INTO_R(ray, entry.get_from(), 0); + DCAST_INTO_R(ray, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -333,7 +333,7 @@ test_intersection_from_ray(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionTube:: test_intersection_from_segment(const CollisionEntry &entry) const { const CollisionSegment *segment; - DCAST_INTO_R(segment, entry.get_from(), 0); + DCAST_INTO_R(segment, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); @@ -397,7 +397,7 @@ test_intersection_from_segment(const CollisionEntry &entry) const { PT(CollisionEntry) CollisionTube:: test_intersection_from_parabola(const CollisionEntry &entry) const { const CollisionParabola *parabola; - DCAST_INTO_R(parabola, entry.get_from(), 0); + DCAST_INTO_R(parabola, entry.get_from(), NULL); const LMatrix4 &wrt_mat = entry.get_wrt_mat(); diff --git a/panda/src/collide/collisionTube.h b/panda/src/collide/collisionTube.h index fe6a57c981..52701193eb 100644 --- a/panda/src/collide/collisionTube.h +++ b/panda/src/collide/collisionTube.h @@ -25,11 +25,11 @@ */ class EXPCL_PANDA_COLLIDE CollisionTube : public CollisionSolid { PUBLISHED: - INLINE CollisionTube(const LPoint3 &a, const LPoint3 &db, - PN_stdfloat radius); - INLINE CollisionTube(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az, - PN_stdfloat bx, PN_stdfloat by, PN_stdfloat bz, - PN_stdfloat radius); + INLINE explicit CollisionTube(const LPoint3 &a, const LPoint3 &db, + PN_stdfloat radius); + INLINE explicit CollisionTube(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az, + PN_stdfloat bx, PN_stdfloat by, PN_stdfloat bz, + PN_stdfloat radius); virtual LPoint3 get_collision_origin() const; diff --git a/panda/src/collide/collisionVisualizer.cxx b/panda/src/collide/collisionVisualizer.cxx index 19699b9d6f..d58137ce68 100644 --- a/panda/src/collide/collisionVisualizer.cxx +++ b/panda/src/collide/collisionVisualizer.cxx @@ -111,10 +111,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { // its objects according to their appropriate net transform. xform_data._net_transform = TransformState::make_identity(); xform_data._view_frustum = trav->get_view_frustum(); - xform_data.apply_transform_and_state(trav, net_transform, - RenderState::make_empty(), - RenderEffects::make_empty(), - ClipPlaneAttrib::make()); + xform_data.apply_transform(net_transform); // Draw all the collision solids. Solids::const_iterator si; diff --git a/panda/src/collide/collisionVisualizer.h b/panda/src/collide/collisionVisualizer.h index 27c2d257a4..29d920f92d 100644 --- a/panda/src/collide/collisionVisualizer.h +++ b/panda/src/collide/collisionVisualizer.h @@ -33,7 +33,7 @@ */ class EXPCL_PANDA_COLLIDE CollisionVisualizer : public PandaNode, public CollisionRecorder { PUBLISHED: - CollisionVisualizer(const string &name); + explicit CollisionVisualizer(const string &name); virtual ~CollisionVisualizer(); INLINE void set_point_scale(PN_stdfloat point_scale); diff --git a/panda/src/cull/README.md b/panda/src/cull/README.md new file mode 100644 index 0000000000..f5ae26c40d --- /dev/null +++ b/panda/src/cull/README.md @@ -0,0 +1,3 @@ +This package contains the Cull Traverser. The cull traversal collects +all state changes specified, and removes unnecessary state change +requests. Also does all the depth sorting for proper alphaing. diff --git a/panda/src/cull/cullBinBackToFront.cxx b/panda/src/cull/cullBinBackToFront.cxx index e472d0811d..a1a7264af0 100644 --- a/panda/src/cull/cullBinBackToFront.cxx +++ b/panda/src/cull/cullBinBackToFront.cxx @@ -84,10 +84,27 @@ finish_cull(SceneSetup *, Thread *current_thread) { void CullBinBackToFront:: draw(bool force, Thread *current_thread) { PStatTimer timer(_draw_this_pcollector, current_thread); + + GeomPipelineReader geom_reader(current_thread); + GeomVertexDataPipelineReader data_reader(current_thread); + Objects::const_iterator oi; for (oi = _objects.begin(); oi != _objects.end(); ++oi) { CullableObject *object = (*oi)._object; - CullHandler::draw(object, _gsg, force, current_thread); + + if (object->_draw_callback == nullptr) { + nassertd(object->_geom != nullptr) continue; + + _gsg->set_state_and_transform(object->_state, object->_internal_transform); + data_reader.set_object(object->_munged_data); + data_reader.check_array_readers(); + geom_reader.set_object(object->_geom); + geom_reader.draw(_gsg, &data_reader, force); + } else { + // It has a callback associated. + object->draw_callback(_gsg, force, current_thread); + // Now the callback has taken care of drawing. + } } } diff --git a/panda/src/cull/cullBinFixed.cxx b/panda/src/cull/cullBinFixed.cxx index 3db9b9f7fb..212bc4f320 100644 --- a/panda/src/cull/cullBinFixed.cxx +++ b/panda/src/cull/cullBinFixed.cxx @@ -70,10 +70,27 @@ finish_cull(SceneSetup *, Thread *current_thread) { void CullBinFixed:: draw(bool force, Thread *current_thread) { PStatTimer timer(_draw_this_pcollector, current_thread); + + GeomPipelineReader geom_reader(current_thread); + GeomVertexDataPipelineReader data_reader(current_thread); + Objects::const_iterator oi; for (oi = _objects.begin(); oi != _objects.end(); ++oi) { CullableObject *object = (*oi)._object; - CullHandler::draw(object, _gsg, force, current_thread); + + if (object->_draw_callback == nullptr) { + nassertd(object->_geom != nullptr) continue; + + _gsg->set_state_and_transform(object->_state, object->_internal_transform); + data_reader.set_object(object->_munged_data); + data_reader.check_array_readers(); + geom_reader.set_object(object->_geom); + geom_reader.draw(_gsg, &data_reader, force); + } else { + // It has a callback associated. + object->draw_callback(_gsg, force, current_thread); + // Now the callback has taken care of drawing. + } } } diff --git a/panda/src/cull/cullBinFrontToBack.cxx b/panda/src/cull/cullBinFrontToBack.cxx index 1800b5636a..0fb05a68b5 100644 --- a/panda/src/cull/cullBinFrontToBack.cxx +++ b/panda/src/cull/cullBinFrontToBack.cxx @@ -84,10 +84,27 @@ finish_cull(SceneSetup *, Thread *current_thread) { void CullBinFrontToBack:: draw(bool force, Thread *current_thread) { PStatTimer timer(_draw_this_pcollector, current_thread); + + GeomPipelineReader geom_reader(current_thread); + GeomVertexDataPipelineReader data_reader(current_thread); + Objects::const_iterator oi; for (oi = _objects.begin(); oi != _objects.end(); ++oi) { CullableObject *object = (*oi)._object; - CullHandler::draw(object, _gsg, force, current_thread); + + if (object->_draw_callback == nullptr) { + nassertd(object->_geom != nullptr) continue; + + _gsg->set_state_and_transform(object->_state, object->_internal_transform); + data_reader.set_object(object->_munged_data); + data_reader.check_array_readers(); + geom_reader.set_object(object->_geom); + geom_reader.draw(_gsg, &data_reader, force); + } else { + // It has a callback associated. + object->draw_callback(_gsg, force, current_thread); + // Now the callback has taken care of drawing. + } } } diff --git a/panda/src/cull/cullBinStateSorted.cxx b/panda/src/cull/cullBinStateSorted.cxx index c7ba58c295..04890af48c 100644 --- a/panda/src/cull/cullBinStateSorted.cxx +++ b/panda/src/cull/cullBinStateSorted.cxx @@ -69,10 +69,27 @@ finish_cull(SceneSetup *, Thread *current_thread) { void CullBinStateSorted:: draw(bool force, Thread *current_thread) { PStatTimer timer(_draw_this_pcollector, current_thread); + + GeomPipelineReader geom_reader(current_thread); + GeomVertexDataPipelineReader data_reader(current_thread); + Objects::const_iterator oi; for (oi = _objects.begin(); oi != _objects.end(); ++oi) { CullableObject *object = (*oi)._object; - CullHandler::draw(object, _gsg, force, current_thread); + + if (object->_draw_callback == nullptr) { + nassertd(object->_geom != nullptr) continue; + + _gsg->set_state_and_transform(object->_state, object->_internal_transform); + data_reader.set_object(object->_munged_data); + data_reader.check_array_readers(); + geom_reader.set_object(object->_geom); + geom_reader.draw(_gsg, &data_reader, force); + } else { + // It has a callback associated. + object->draw_callback(_gsg, force, current_thread); + // Now the callback has taken care of drawing. + } } } diff --git a/panda/src/cull/cullBinUnsorted.cxx b/panda/src/cull/cullBinUnsorted.cxx index e9de061fb1..4ef8c35ae6 100644 --- a/panda/src/cull/cullBinUnsorted.cxx +++ b/panda/src/cull/cullBinUnsorted.cxx @@ -54,10 +54,27 @@ add_object(CullableObject *object, Thread *current_thread) { void CullBinUnsorted:: draw(bool force, Thread *current_thread) { PStatTimer timer(_draw_this_pcollector, current_thread); + + GeomPipelineReader geom_reader(current_thread); + GeomVertexDataPipelineReader data_reader(current_thread); + Objects::iterator oi; for (oi = _objects.begin(); oi != _objects.end(); ++oi) { CullableObject *object = (*oi); - CullHandler::draw(object, _gsg, force, current_thread); + + if (object->_draw_callback == nullptr) { + nassertd(object->_geom != nullptr) continue; + + _gsg->set_state_and_transform(object->_state, object->_internal_transform); + data_reader.set_object(object->_munged_data); + data_reader.check_array_readers(); + geom_reader.set_object(object->_geom); + geom_reader.draw(_gsg, &data_reader, force); + } else { + // It has a callback associated. + object->draw_callback(_gsg, force, current_thread); + // Now the callback has taken care of drawing. + } } } diff --git a/panda/src/device/analogNode.h b/panda/src/device/analogNode.h index 2a17a6b655..e4c0385ae3 100644 --- a/panda/src/device/analogNode.h +++ b/panda/src/device/analogNode.h @@ -38,8 +38,8 @@ */ class EXPCL_PANDA_DEVICE AnalogNode : public DataNode { PUBLISHED: - AnalogNode(ClientBase *client, const string &device_name); - AnalogNode(InputDevice *device); + explicit AnalogNode(ClientBase *client, const string &device_name); + explicit AnalogNode(InputDevice *device); virtual ~AnalogNode(); INLINE bool is_valid() const; diff --git a/panda/src/device/buttonNode.h b/panda/src/device/buttonNode.h index 6182108933..87672cef8a 100644 --- a/panda/src/device/buttonNode.h +++ b/panda/src/device/buttonNode.h @@ -32,8 +32,8 @@ */ class EXPCL_PANDA_DEVICE ButtonNode : public DataNode { PUBLISHED: - ButtonNode(ClientBase *client, const string &device_name); - ButtonNode(InputDevice *device); + explicit ButtonNode(ClientBase *client, const string &device_name); + explicit ButtonNode(InputDevice *device); virtual ~ButtonNode(); INLINE bool is_valid() const; diff --git a/panda/src/device/dialNode.h b/panda/src/device/dialNode.h index 04c65b1050..13e2083efc 100644 --- a/panda/src/device/dialNode.h +++ b/panda/src/device/dialNode.h @@ -33,7 +33,7 @@ */ class EXPCL_PANDA_DEVICE DialNode : public DataNode { PUBLISHED: - DialNode(ClientBase *client, const string &device_name); + explicit DialNode(ClientBase *client, const string &device_name); virtual ~DialNode(); INLINE bool is_valid() const; diff --git a/panda/src/device/trackerNode.h b/panda/src/device/trackerNode.h index 44bf932469..4e9ae4a63a 100644 --- a/panda/src/device/trackerNode.h +++ b/panda/src/device/trackerNode.h @@ -32,8 +32,8 @@ */ class EXPCL_PANDA_DEVICE TrackerNode : public DataNode { PUBLISHED: - TrackerNode(ClientBase *client, const string &device_name); - TrackerNode(InputDevice *device); + explicit TrackerNode(ClientBase *client, const string &device_name); + explicit TrackerNode(InputDevice *device); virtual ~TrackerNode(); INLINE bool is_valid() const; diff --git a/panda/src/device/virtualMouse.h b/panda/src/device/virtualMouse.h index 885d3a883c..037180305d 100644 --- a/panda/src/device/virtualMouse.h +++ b/panda/src/device/virtualMouse.h @@ -31,7 +31,7 @@ */ class EXPCL_PANDA_DEVICE VirtualMouse : public DataNode { PUBLISHED: - VirtualMouse(const string &name); + explicit VirtualMouse(const string &name); void set_mouse_pos(int x, int y); void set_window_size(int width, int height); diff --git a/panda/src/dgraph/README.md b/panda/src/dgraph/README.md new file mode 100644 index 0000000000..330c952a60 --- /dev/null +++ b/panda/src/dgraph/README.md @@ -0,0 +1,3 @@ +This package defines and manages the data graph, which is the hierarchy +of devices, tforms, and any other things which might have an input or +an output and need to execute every frame. diff --git a/panda/src/dgraph/dataGraphTraverser.h b/panda/src/dgraph/dataGraphTraverser.h index 291958cbca..aa7037894a 100644 --- a/panda/src/dgraph/dataGraphTraverser.h +++ b/panda/src/dgraph/dataGraphTraverser.h @@ -31,7 +31,7 @@ class PandaNode; */ class EXPCL_PANDA_DGRAPH DataGraphTraverser { PUBLISHED: - DataGraphTraverser(Thread *current_thread = Thread::get_current_thread()); + explicit DataGraphTraverser(Thread *current_thread = Thread::get_current_thread()); ~DataGraphTraverser(); INLINE Thread *get_current_thread() const; diff --git a/panda/src/dgraph/dataNode.h b/panda/src/dgraph/dataNode.h index 0519cc5fdd..0f5e52b0fc 100644 --- a/panda/src/dgraph/dataNode.h +++ b/panda/src/dgraph/dataNode.h @@ -51,7 +51,7 @@ class DataNodeTransmit; */ class EXPCL_PANDA_DGRAPH DataNode : public PandaNode { PUBLISHED: - INLINE DataNode(const string &name); + INLINE explicit DataNode(const string &name); protected: INLINE DataNode(const DataNode ©); diff --git a/panda/src/display/README.md b/panda/src/display/README.md new file mode 100644 index 0000000000..f9029b8a2b --- /dev/null +++ b/panda/src/display/README.md @@ -0,0 +1,2 @@ +This package contains the abstract display classes, including pipes, +windows, channels, and display regions. diff --git a/panda/src/display/displayInformation.cxx b/panda/src/display/displayInformation.cxx index 60e3a69e8b..d690544889 100644 --- a/panda/src/display/displayInformation.cxx +++ b/panda/src/display/displayInformation.cxx @@ -14,6 +14,15 @@ #include "graphicsStateGuardian.h" #include "displayInformation.h" +// For __rdtsc +#if defined(__i386) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64) +#ifdef _MSC_VER +#include +#elif defined(__GNUC__) && !defined(__clang__) +#include +#endif +#endif + /** * Returns true if these two DisplayModes are identical. */ @@ -58,12 +67,6 @@ DisplayInformation:: if (_display_mode_array != NULL) { delete[] _display_mode_array; } - if (_cpu_id_data != NULL) { - delete _cpu_id_data; - } - if (_cpu_brand_string != NULL) { - delete _cpu_brand_string; - } } /** @@ -134,12 +137,6 @@ DisplayInformation() { _driver_date_day = 0; _driver_date_year = 0; - _cpu_id_version = 1; - _cpu_id_size = 0; - _cpu_id_data = 0; - - _cpu_vendor_string = 0; - _cpu_brand_string = 0; _cpu_version_information = 0; _cpu_brand_index = 0; @@ -152,7 +149,6 @@ DisplayInformation() { _num_logical_cpus = 0; _get_memory_information_function = 0; - _cpu_time_function = 0; _update_cpu_frequency_function = 0; _os_version_major = -1; @@ -493,62 +489,17 @@ get_driver_date_year() { /** * */ -int DisplayInformation:: -get_cpu_id_version() { - return _cpu_id_version; -} - -/** - * Returns the number of 32-bit values for cpu id binary data. - */ -int DisplayInformation:: -get_cpu_id_size() { - return _cpu_id_size; -} - -/** - * Returns part of cpu id binary data based on the index. - */ -unsigned int DisplayInformation:: -get_cpu_id_data(int index) { - unsigned int data; - - data = 0; - if (index >= 0 && index < _cpu_id_size) { - data = _cpu_id_data [index]; - } - - return data; +const string &DisplayInformation:: +get_cpu_vendor_string() const { + return _cpu_vendor_string; } /** * */ -const char *DisplayInformation:: -get_cpu_vendor_string() { - const char *string; - - string = _cpu_vendor_string; - if (string == 0) { - string = ""; - } - - return string; -} - -/** - * - */ -const char *DisplayInformation:: -get_cpu_brand_string() { - const char *string; - - string = _cpu_brand_string; - if (string == 0) { - string = ""; - } - - return string; +const string &DisplayInformation:: +get_cpu_brand_string() const { + return _cpu_brand_string; } /** @@ -576,18 +527,21 @@ get_cpu_frequency() { } /** - * + * Equivalent to the rdtsc processor instruction. */ uint64_t DisplayInformation:: get_cpu_time() { - uint64_t cpu_time; - - cpu_time = 0; - if (_cpu_time_function) { - cpu_time = _cpu_time_function(); - } - - return cpu_time; +#if defined(__i386) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64) +#if defined(_MSC_VER) || (defined(__GNUC__) && !defined(__clang__)) + return __rdtsc(); +#else + unsigned int lo, hi = 0; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return ((uint64_t)hi << 32) | lo; +#endif +#else + return 0; +#endif } /** diff --git a/panda/src/display/displayInformation.h b/panda/src/display/displayInformation.h index 440e3e8ee8..919e400741 100644 --- a/panda/src/display/displayInformation.h +++ b/panda/src/display/displayInformation.h @@ -94,17 +94,13 @@ PUBLISHED: int get_driver_date_day(); int get_driver_date_year(); - int get_cpu_id_version(); - int get_cpu_id_size(); - unsigned int get_cpu_id_data(int index); - - const char *get_cpu_vendor_string(); - const char *get_cpu_brand_string(); + const string &get_cpu_vendor_string() const; + const string &get_cpu_brand_string() const; unsigned int get_cpu_version_information(); unsigned int get_cpu_brand_index(); uint64_t get_cpu_frequency(); - uint64_t get_cpu_time(); + static uint64_t get_cpu_time(); uint64_t get_maximum_cpu_frequency(); uint64_t get_current_cpu_frequency(); @@ -158,12 +154,9 @@ public: int _driver_date_day; int _driver_date_year; - int _cpu_id_version; - int _cpu_id_size; - unsigned int *_cpu_id_data; - char *_cpu_vendor_string; - char *_cpu_brand_string; + string _cpu_vendor_string; + string _cpu_brand_string; unsigned int _cpu_version_information; unsigned int _cpu_brand_index; @@ -176,7 +169,6 @@ public: int _num_logical_cpus; void (*_get_memory_information_function) (DisplayInformation *display_information); - uint64_t (*_cpu_time_function) (void); int (*_update_cpu_frequency_function) (int processor_number, DisplayInformation *display_information); int _os_version_major; diff --git a/panda/src/display/displayRegion.I b/panda/src/display/displayRegion.I index 11492a3a5e..8982461879 100644 --- a/panda/src/display/displayRegion.I +++ b/panda/src/display/displayRegion.I @@ -480,8 +480,8 @@ set_cull_result(PT(CullResult) cull_result, PT(SceneSetup) scene_setup, Thread *current_thread) { CDCullWriter cdata(_cycler_cull, true, current_thread); #ifdef USE_MOVE_SEMANTICS - cdata->_cull_result = std::move(cull_result); - cdata->_scene_setup = std::move(scene_setup); + cdata->_cull_result = move(cull_result); + cdata->_scene_setup = move(scene_setup); #else swap(cdata->_cull_result, cull_result); swap(cdata->_scene_setup, scene_setup); @@ -883,3 +883,9 @@ INLINE int DisplayRegionPipelineReader:: get_pixel_height(int i) const { return _cdata->_regions[i]._pixels[3] - _cdata->_regions[i]._pixels[2]; } + +INLINE ostream & +operator << (ostream &out, const DisplayRegion &dr) { + dr.output(out); + return out; +} diff --git a/panda/src/display/displayRegion.cxx b/panda/src/display/displayRegion.cxx index c8f7626468..4ffa29285c 100644 --- a/panda/src/display/displayRegion.cxx +++ b/panda/src/display/displayRegion.cxx @@ -491,6 +491,12 @@ get_screenshot() { return NULL; } + { + // Make sure that the correct viewport is active. + DisplayRegionPipelineReader dr_reader(this, current_thread); + gsg->prepare_display_region(&dr_reader); + } + PT(Texture) tex = new Texture; RenderBuffer buffer = gsg->get_render_buffer(get_screenshot_buffer_type(), diff --git a/panda/src/display/displayRegion.h b/panda/src/display/displayRegion.h index 742cf22bd4..091f17e13f 100644 --- a/panda/src/display/displayRegion.h +++ b/panda/src/display/displayRegion.h @@ -16,7 +16,7 @@ #include "pandabase.h" -#include "displayRegionBase.h" +#include "typedReferenceCount.h" #include "drawableRegion.h" #include "referenceCount.h" #include "nodePath.h" @@ -54,7 +54,7 @@ class CullTraverser; * DisplayRegions like panes of glass, usually for layering 2-d interfaces on * top of a 3-d scene. */ -class EXPCL_PANDA_DISPLAY DisplayRegion : public DisplayRegionBase, public DrawableRegion { +class EXPCL_PANDA_DISPLAY DisplayRegion : public TypedReferenceCount, public DrawableRegion { protected: DisplayRegion(GraphicsOutput *window, const LVecBase4 &dimensions); @@ -285,9 +285,9 @@ public: return _type_handle; } static void init_type() { - DisplayRegionBase::init_type(); + TypedReferenceCount::init_type(); register_type(_type_handle, "DisplayRegion", - DisplayRegionBase::get_class_type()); + TypedReferenceCount::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); @@ -374,6 +374,8 @@ private: static TypeHandle _type_handle; }; +INLINE ostream &operator << (ostream &out, const DisplayRegion &dr); + #include "displayRegion.I" #endif /* DISPLAYREGION_H */ diff --git a/panda/src/display/frameBufferProperties.cxx b/panda/src/display/frameBufferProperties.cxx index 98b8341322..ea37cc4381 100644 --- a/panda/src/display/frameBufferProperties.cxx +++ b/panda/src/display/frameBufferProperties.cxx @@ -581,9 +581,20 @@ get_quality(const FrameBufferProperties &reqs) const { } } - // Bonus for each depth bit. Extra: 2 per bit. + // However, deduct for color bits above 24, if we are requesting only 1. + // This is to prevent choosing a 64-bit color mode in NVIDIA cards that + // is linear and therefore causes the gamma to be off in non-sRGB pipelines. + if (reqs._property[FBP_color_bits] <= 3 && _property[FBP_color_bits] > 24) { + quality -= 100; + } + + // Bonus for each depth bit. Extra: 8 per bit. + // Please note that the Intel Windows driver only gives extra depth in + // combination with a stencil buffer, so we need 8 extra depth bits to + // outweigh the penalty of 50 for the unwanted stencil buffer, otherwise we + // will end up only getting 16-bit depth. if (reqs._property[FBP_depth_bits] != 0) { - quality += 2 * _property[FBP_depth_bits]; + quality += 8 * _property[FBP_depth_bits]; } // Bonus for each multisample. Extra: 2 per sample. diff --git a/panda/src/display/get_x11.h b/panda/src/display/get_x11.h index f9112766d7..57ca5cf44e 100644 --- a/panda/src/display/get_x11.h +++ b/panda/src/display/get_x11.h @@ -45,6 +45,7 @@ struct XVisualInfo; #else #include "pre_x11_include.h" +#include #include #include #include diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index ad3aa3caa6..9c11fc9bdc 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -1271,7 +1271,7 @@ set_window_sort(GraphicsOutput *window, int sort) { * model begins with the "-" character. */ void GraphicsEngine:: -cull_and_draw_together(const GraphicsEngine::Windows &wlist, +cull_and_draw_together(GraphicsEngine::Windows wlist, Thread *current_thread) { PStatTimer timer(_cull_pcollector, current_thread); @@ -1328,21 +1328,24 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr, GraphicsStateGuardian *gsg = win->get_gsg(); nassertv(gsg != (GraphicsStateGuardian *)NULL); - DisplayRegionPipelineReader *dr_reader = - new DisplayRegionPipelineReader(dr, current_thread); + PT(SceneSetup) scene_setup; - win->change_scenes(dr_reader); - gsg->prepare_display_region(dr_reader); + { + DisplayRegionPipelineReader dr_reader(dr, current_thread); + win->change_scenes(&dr_reader); + gsg->prepare_display_region(&dr_reader); - if (dr_reader->is_any_clear_active()) { - gsg->clear(dr); + if (dr_reader.is_any_clear_active()) { + gsg->clear(dr); + } + + scene_setup = setup_scene(gsg, &dr_reader); } - PT(SceneSetup) scene_setup = setup_scene(gsg, dr_reader); if (scene_setup == (SceneSetup *)NULL) { // Never mind. - } else if (dr_reader->get_object()->is_stereo()) { + } else if (dr->is_stereo()) { // Don't draw stereo DisplayRegions directly. } else if (!gsg->set_scene(scene_setup)) { @@ -1353,9 +1356,6 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr, } else { DrawCullHandler cull_handler(gsg); if (gsg->begin_scene()) { - delete dr_reader; - dr_reader = NULL; - CallbackObject *cbobj = dr->get_cull_callback(); if (cbobj != (CallbackObject *)NULL) { // Issue the cull callback on this DisplayRegion. @@ -1372,10 +1372,6 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr, gsg->end_scene(); } } - - if (dr_reader != (DisplayRegionPipelineReader *)NULL) { - delete dr_reader; - } } /** @@ -1384,7 +1380,7 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr, * drawing. */ void GraphicsEngine:: -cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { +cull_to_bins(GraphicsEngine::Windows wlist, Thread *current_thread) { PStatTimer timer(_cull_pcollector, current_thread); _singular_warning_last_frame = _singular_warning_this_frame; @@ -1399,27 +1395,41 @@ cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { for (size_t wi = 0; wi < wlist_size; ++wi) { GraphicsOutput *win = wlist[wi]; if (win->is_active() && win->get_gsg()->is_active()) { + GraphicsStateGuardian *gsg = win->get_gsg(); PStatTimer timer(win->get_cull_window_pcollector(), current_thread); int num_display_regions = win->get_num_active_display_regions(); for (int i = 0; i < num_display_regions; ++i) { DisplayRegion *dr = win->get_active_display_region(i); - if (dr != (DisplayRegion *)NULL) { - DisplayRegionPipelineReader *dr_reader = - new DisplayRegionPipelineReader(dr, current_thread); - + if (dr != nullptr) { + PT(SceneSetup) scene_setup; + PT(CullResult) cull_result; CullKey key; - key._gsg = win->get_gsg(); - key._camera = dr_reader->get_camera(); - key._lens_index = dr_reader->get_lens_index(); + { + PStatTimer timer(_cull_setup_pcollector, current_thread); + DisplayRegionPipelineReader dr_reader(dr, current_thread); + scene_setup = setup_scene(gsg, &dr_reader); + if (scene_setup == nullptr) { + continue; + } - AlreadyCulled::iterator aci = already_culled.insert(AlreadyCulled::value_type(key, (DisplayRegion *)NULL)).first; - if ((*aci).second == NULL) { + key._gsg = gsg; + key._camera = dr_reader.get_camera(); + key._lens_index = dr_reader.get_lens_index(); + } + + AlreadyCulled::iterator aci = already_culled.insert(AlreadyCulled::value_type(move(key), nullptr)).first; + if ((*aci).second == nullptr) { // We have not used this camera already in this thread. Perform // the cull operation. - delete dr_reader; - dr_reader = NULL; + cull_result = dr->get_cull_result(current_thread); + if (cull_result != nullptr) { + cull_result = cull_result->make_next(); + } else { + // This DisplayRegion has no cull results; draw it. + cull_result = new CullResult(gsg, dr->get_draw_region_pcollector()); + } (*aci).second = dr; - cull_to_bins(win, dr, current_thread); + cull_to_bins(win, gsg, dr, scene_setup, cull_result, current_thread); } else { // We have already culled a scene using this camera in this @@ -1429,14 +1439,11 @@ cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { // image.) Of course, the cull result will be the same, so just // use the result from the other DisplayRegion. DisplayRegion *other_dr = (*aci).second; - dr->set_cull_result(other_dr->get_cull_result(current_thread), - setup_scene(win->get_gsg(), dr_reader), - current_thread); + cull_result = other_dr->get_cull_result(current_thread); } - if (dr_reader != (DisplayRegionPipelineReader *)NULL) { - delete dr_reader; - } + // Save the results for next frame. + dr->set_cull_result(MOVE(cull_result), MOVE(scene_setup), current_thread); } } } @@ -1447,50 +1454,26 @@ cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { * Called only within the inner loop of cull_to_bins(), above. */ void GraphicsEngine:: -cull_to_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) { - GraphicsStateGuardian *gsg = win->get_gsg(); - if (gsg == (GraphicsStateGuardian *)NULL) { - return; +cull_to_bins(GraphicsOutput *win, GraphicsStateGuardian *gsg, + DisplayRegion *dr, SceneSetup *scene_setup, + CullResult *cull_result, Thread *current_thread) { + + BinCullHandler cull_handler(cull_result); + CallbackObject *cbobj = dr->get_cull_callback(); + if (cbobj != (CallbackObject *)NULL) { + // Issue the cull callback on this DisplayRegion. + DisplayRegionCullCallbackData cbdata(&cull_handler, scene_setup); + cbobj->do_callback(&cbdata); + + // The callback has taken care of the culling. + + } else { + // Perform the cull normally. + dr->do_cull(&cull_handler, scene_setup, gsg, current_thread); } - PT(CullResult) cull_result; - PT(SceneSetup) scene_setup; - { - PStatTimer timer(_cull_setup_pcollector, current_thread); - DisplayRegionPipelineReader dr_reader(dr, current_thread); - scene_setup = setup_scene(gsg, &dr_reader); - cull_result = dr->get_cull_result(current_thread); - - if (cull_result != (CullResult *)NULL) { - cull_result = cull_result->make_next(); - - } else { - // This DisplayRegion has no cull results; draw it. - cull_result = new CullResult(gsg, dr->get_draw_region_pcollector()); - } - } - - if (scene_setup != (SceneSetup *)NULL) { - BinCullHandler cull_handler(cull_result); - CallbackObject *cbobj = dr->get_cull_callback(); - if (cbobj != (CallbackObject *)NULL) { - // Issue the cull callback on this DisplayRegion. - DisplayRegionCullCallbackData cbdata(&cull_handler, scene_setup); - cbobj->do_callback(&cbdata); - - // The callback has taken care of the culling. - - } else { - // Perform the cull normally. - dr->do_cull(&cull_handler, scene_setup, gsg, current_thread); - } - - PStatTimer timer(_cull_sort_pcollector, current_thread); - cull_result->finish_cull(scene_setup, current_thread); - } - - // Save the results for next frame. - dr->set_cull_result(MOVE(cull_result), MOVE(scene_setup), current_thread); + PStatTimer timer(_cull_sort_pcollector, current_thread); + cull_result->finish_cull(scene_setup, current_thread); } /** @@ -1538,7 +1521,7 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { for (int i = 0; i < num_display_regions; ++i) { DisplayRegion *dr = win->get_active_display_region(i); if (dr != (DisplayRegion *)NULL) { - draw_bins(win, dr, current_thread); + do_draw(win, gsg, dr, current_thread); } } } @@ -1582,22 +1565,6 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { } } -/** - * This variant on draw_bins() is only called from draw_bins(), above. It - * draws the cull result for a particular DisplayRegion. - */ -void GraphicsEngine:: -draw_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) { - GraphicsStateGuardian *gsg = win->get_gsg(); - if (gsg == (GraphicsStateGuardian *)NULL) { - return; - } - - PT(CullResult) cull_result = dr->get_cull_result(current_thread); - PT(SceneSetup) scene_setup = dr->get_scene_setup(current_thread); - do_draw(cull_result, scene_setup, win, dr, current_thread); -} - /** * Called in the draw thread, this calls make_context() on each window on the * list to guarantee its gsg and graphics context both get created. @@ -1882,16 +1849,6 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) { CPT(TransformState) cs_world_transform = cs_transform->compose(world_transform); scene_setup->set_cs_world_transform(cs_world_transform); - // Make sure that the GSG has a ShaderGenerator for the munger to use. We - // have to do this here because the ShaderGenerator needs a host window - // pointer. Hopefully we'll be able to eliminate that requirement in the - // future. -#ifdef HAVE_CG - if (gsg->get_shader_generator() == NULL) { - gsg->set_shader_generator(new ShaderGenerator(gsg, window)); - } -#endif - return scene_setup; } @@ -1899,15 +1856,20 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) { * Draws the previously-culled scene. */ void GraphicsEngine:: -do_draw(CullResult *cull_result, SceneSetup *scene_setup, - GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) { - - CallbackObject *cbobj; - GraphicsStateGuardian *gsg = win->get_gsg(); - +do_draw(GraphicsOutput *win, GraphicsStateGuardian *gsg, DisplayRegion *dr, Thread *current_thread) { // Statistics PStatGPUTimer timer(gsg, dr->get_draw_region_pcollector(), current_thread); + PT(CullResult) cull_result; + PT(SceneSetup) scene_setup; + { + DisplayRegion::CDCullReader cdata(dr->_cycler_cull, current_thread); + cull_result = cdata->_cull_result; + scene_setup = cdata->_scene_setup; + } + + CallbackObject *cbobj; + { DisplayRegionPipelineReader dr_reader(dr, current_thread); win->change_scenes(&dr_reader); diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 89cfb7c616..d18d86f690 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -52,7 +52,7 @@ class Texture; */ class EXPCL_PANDA_DISPLAY GraphicsEngine : public ReferenceCount { PUBLISHED: - GraphicsEngine(Pipeline *pipeline = NULL); + explicit GraphicsEngine(Pipeline *pipeline = NULL); BLOCKING ~GraphicsEngine(); void set_threading_model(const GraphicsThreadingModel &threading_model); @@ -142,14 +142,15 @@ private: void set_window_sort(GraphicsOutput *window, int sort); - void cull_and_draw_together(const Windows &wlist, Thread *current_thread); + void cull_and_draw_together(Windows wlist, Thread *current_thread); void cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread); - void cull_to_bins(const Windows &wlist, Thread *current_thread); - void cull_to_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread); + void cull_to_bins(Windows wlist, Thread *current_thread); + void cull_to_bins(GraphicsOutput *win, GraphicsStateGuardian *gsg, + DisplayRegion *dr, SceneSetup *scene_setup, + CullResult *cull_result, Thread *current_thread); void draw_bins(const Windows &wlist, Thread *current_thread); - void draw_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread); void make_contexts(const Windows &wlist, Thread *current_thread); void process_events(const Windows &wlist, Thread *current_thread); @@ -162,8 +163,8 @@ private: PT(SceneSetup) setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr); - void do_draw(CullResult *cull_result, SceneSetup *scene_setup, - GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread); + void do_draw(GraphicsOutput *win, GraphicsStateGuardian *gsg, + DisplayRegion *dr, Thread *current_thread); void do_add_window(GraphicsOutput *window, const GraphicsThreadingModel &threading_model); diff --git a/panda/src/display/graphicsOutput.I b/panda/src/display/graphicsOutput.I index 40a90af57e..393dbaadb5 100644 --- a/panda/src/display/graphicsOutput.I +++ b/panda/src/display/graphicsOutput.I @@ -489,10 +489,16 @@ get_child_sort() const { /** * When the GraphicsOutput is in triggered copy mode, this function triggers * the copy (at the end of the next frame). + * @returns a future that can be awaited. */ -INLINE void GraphicsOutput:: -trigger_copy() { - _trigger_copy = true; +INLINE AsyncFuture *GraphicsOutput:: +trigger_copy() { + AsyncFuture *future = _trigger_copy; + if (future == nullptr) { + future = new AsyncFuture; + _trigger_copy = future; + } + return future; } /** diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx index afdaddf3d4..4d8a1e27a1 100644 --- a/panda/src/display/graphicsOutput.cxx +++ b/panda/src/display/graphicsOutput.cxx @@ -115,7 +115,6 @@ GraphicsOutput(GraphicsEngine *engine, GraphicsPipe *pipe, _sbs_left_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f); _sbs_right_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f); _delete_flag = false; - _trigger_copy = false; if (_fb_properties.is_single_buffered()) { _draw_buffer_type = RenderBuffer::T_front; @@ -227,14 +226,20 @@ clear_render_textures() { * You can specify a bitplane to attach the texture to. the legal choices * are: * - * * RTP_depth * RTP_depth_stencil * RTP_color * RTP_aux_rgba_0 * - * RTP_aux_rgba_1 * RTP_aux_rgba_2 * RTP_aux_rgba_3 + * - RTP_depth + * - RTP_depth_stencil + * - RTP_color + * - RTP_aux_rgba_0 + * - RTP_aux_rgba_1 + * - RTP_aux_rgba_2 + * - RTP_aux_rgba_3 * * If you do not specify a bitplane to attach the texture to, this routine * will use a default based on the texture's format: * - * * F_depth_component attaches to RTP_depth * F_depth_stencil attaches to - * RTP_depth_stencil * all other formats attach to RTP_color. + * - F_depth_component attaches to RTP_depth + * - F_depth_stencil attaches to RTP_depth_stencil + * - all other formats attach to RTP_color. * * The texture's format will be changed to match the format of the bitplane to * which it is attached. For example, if you pass in an F_rgba texture and @@ -270,12 +275,21 @@ add_render_texture(Texture *tex, RenderTextureMode mode, // Choose a default bitplane. if (plane == RTP_COUNT) { - if (tex->get_format() == Texture::F_depth_stencil) { + switch (tex->get_format()) { + case Texture::F_depth_stencil: plane = RTP_depth_stencil; - } else if (tex->get_format() == Texture::F_depth_component) { + break; + + case Texture::F_depth_component: + case Texture::F_depth_component16: + case Texture::F_depth_component24: + case Texture::F_depth_component32: plane = RTP_depth; - } else { + break; + + default: plane = RTP_color; + break; } } @@ -283,32 +297,41 @@ add_render_texture(Texture *tex, RenderTextureMode mode, // bitplane, while we're at it). if (plane == RTP_depth) { - tex->set_format(Texture::F_depth_component); + _fb_properties.setup_depth_texture(tex); tex->set_match_framebuffer_format(true); + } else if (plane == RTP_depth_stencil) { tex->set_format(Texture::F_depth_stencil); - tex->set_component_type(Texture::T_unsigned_int_24_8); + if (_fb_properties.get_float_depth()) { + tex->set_component_type(Texture::T_float); + } else { + tex->set_component_type(Texture::T_unsigned_int_24_8); + } tex->set_match_framebuffer_format(true); - } else if ((plane == RTP_color)|| - (plane == RTP_aux_rgba_0)|| - (plane == RTP_aux_rgba_1)|| - (plane == RTP_aux_rgba_2)|| - (plane == RTP_aux_rgba_3)) { - tex->set_format(Texture::F_rgba); + + } else if (plane == RTP_color || + plane == RTP_aux_rgba_0 || + plane == RTP_aux_rgba_1 || + plane == RTP_aux_rgba_2 || + plane == RTP_aux_rgba_3) { + _fb_properties.setup_color_texture(tex); tex->set_match_framebuffer_format(true); - } else if ((plane == RTP_aux_hrgba_0)|| - (plane == RTP_aux_hrgba_1)|| - (plane == RTP_aux_hrgba_2)|| - (plane == RTP_aux_hrgba_3)) { + + } else if (plane == RTP_aux_hrgba_0 || + plane == RTP_aux_hrgba_1 || + plane == RTP_aux_hrgba_2 || + plane == RTP_aux_hrgba_3) { tex->set_format(Texture::F_rgba16); tex->set_match_framebuffer_format(true); - } else if ((plane == RTP_aux_float_0)|| - (plane == RTP_aux_float_1)|| - (plane == RTP_aux_float_2)|| - (plane == RTP_aux_float_3)) { + + } else if (plane == RTP_aux_float_0 || + plane == RTP_aux_float_1 || + plane == RTP_aux_float_2 || + plane == RTP_aux_float_3) { tex->set_format(Texture::F_rgba32); tex->set_component_type(Texture::T_float); tex->set_match_framebuffer_format(true); + } else { display_cat.error() << "add_render_texture: invalid bitplane specified.\n"; @@ -1420,7 +1443,10 @@ copy_to_textures() { } } } - _trigger_copy = false; + if (_trigger_copy != nullptr) { + _trigger_copy->set_result(nullptr); + _trigger_copy = nullptr; + } return okflag; } diff --git a/panda/src/display/graphicsOutput.h b/panda/src/display/graphicsOutput.h index 295467f458..2f9eff8683 100644 --- a/panda/src/display/graphicsOutput.h +++ b/panda/src/display/graphicsOutput.h @@ -40,6 +40,7 @@ #include "cycleDataWriter.h" #include "pipelineCycler.h" #include "updateSeq.h" +#include "asyncFuture.h" class PNMImage; class GraphicsEngine; @@ -197,7 +198,7 @@ PUBLISHED: INLINE int get_child_sort() const; MAKE_PROPERTY(child_sort, get_child_sort, set_child_sort); - INLINE void trigger_copy(); + INLINE AsyncFuture *trigger_copy(); INLINE DisplayRegion *make_display_region(); INLINE DisplayRegion *make_display_region(PN_stdfloat l, PN_stdfloat r, PN_stdfloat b, PN_stdfloat t); @@ -330,7 +331,7 @@ protected: int _target_tex_view; DisplayRegion *_prev_page_dr; PT(GeomNode) _texture_card; - bool _trigger_copy; + PT(AsyncFuture) _trigger_copy; class RenderTexture { public: diff --git a/panda/src/display/graphicsPipe.cxx b/panda/src/display/graphicsPipe.cxx index 996b5694ff..ad5d1aea62 100644 --- a/panda/src/display/graphicsPipe.cxx +++ b/panda/src/display/graphicsPipe.cxx @@ -18,6 +18,93 @@ #include "mutexHolder.h" #include "displayInformation.h" +#ifdef IS_LINUX +#include +#include +#endif + +#if defined(IS_OSX) || defined(IS_FREEBSD) +#include +#include +#endif + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + +// CPUID is only available on i386 and x86-64 architectures. +#if defined(__i386) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64) + +#if defined(__GNUC__) && !defined(__APPLE__) +// GCC and Clang offer a useful cpuid.h header. +#include +#endif + +#ifdef _MSC_VER +// MSVC has a __cpuid intrinsic. +#include +#endif + +union cpuid_info { + char str[16]; + struct { + uint32_t eax, ebx, ecx, edx; + }; +}; + +/** + * Returns the highest cpuid leaf that is supported by the CPU. + */ +static inline uint32_t get_cpuid_max(uint32_t leaf) { +#if defined(__GNUC__) && !defined(__APPLE__) + return __get_cpuid_max(leaf, 0); +#elif defined(_MSC_VER) + uint32_t p[4] = {0}; + __cpuid((int *)p, leaf); + return p[0]; +#else + unsigned int eax = 0; + __asm__ ("cpuid\n\t" + : "=a" (eax) + : "0" (leaf)); + return eax; +#endif +} + +/** + * Gets cpuid info for the given leaf. + */ +static inline void get_cpuid(uint32_t leaf, cpuid_info &info) { +#if defined(__GNUC__) && !defined(__APPLE__) + __cpuid(leaf, info.eax, info.ebx, info.ecx, info.edx); +#elif defined(_MSC_VER) + __cpuid((int *)info.str, leaf); +#else + __asm__ ("cpuid\n\t" + : "=a" (info.eax), "=b" (info.ebx), "=c" (info.ecx), "=d" (info.edx) + : "0" (leaf)); +#endif +} +#endif + +#ifdef IS_LINUX +/** + * Updates the current memory usage statistics in the DisplayInformation. + */ +static void update_memory_info(DisplayInformation *info) { + struct sysinfo meminfo; + if (sysinfo(&meminfo) == 0) { + info->_physical_memory = meminfo.totalram; + info->_available_physical_memory = meminfo.freeram; + info->_page_file_size = meminfo.totalswap; + info->_available_page_file_size = meminfo.freeswap; + } +} +#endif + TypeHandle GraphicsPipe::_type_handle; /** @@ -38,24 +125,81 @@ GraphicsPipe() : _display_width = 0; _display_height = 0; - _display_information = new DisplayInformation ( ); -} + _display_information = new DisplayInformation(); -/** - * Don't try to copy GraphicsPipes. - */ -GraphicsPipe:: -GraphicsPipe(const GraphicsPipe &) { - _is_valid = false; - nassertv(false); -} +#if defined(__i386) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64) + cpuid_info info; + const uint32_t max_cpuid = get_cpuid_max(0); + const uint32_t max_extended = get_cpuid_max(0x80000000); -/** - * Don't try to copy GraphicsPipes. - */ -void GraphicsPipe:: -operator = (const GraphicsPipe &) { - nassertv(false); + if (max_cpuid >= 1) { + get_cpuid(0, info); + swap(info.ecx, info.edx); + _display_information->_cpu_vendor_string = string(info.str + 4, 12); + + get_cpuid(1, info); + _display_information->_cpu_version_information = info.eax; + _display_information->_cpu_brand_index = info.ebx & 0xff; + } + + if (max_extended >= 0x80000004) { + char brand[49]; + get_cpuid(0x80000002, info); + memcpy(brand, info.str, 16); + get_cpuid(0x80000003, info); + memcpy(brand + 16, info.str, 16); + get_cpuid(0x80000004, info); + memcpy(brand + 32, info.str, 16); + brand[48] = 0; + _display_information->_cpu_brand_string = brand; + } +#endif + +#if defined(IS_OSX) + // macOS exposes a lot of useful information through sysctl. + size_t len = sizeof(uint64_t); + sysctlbyname("hw.memsize", &_display_information->_physical_memory, &len, NULL, 0); + len = sizeof(uint64_t); + sysctlbyname("hw.cpufrequency", &_display_information->_cpu_frequency, &len, NULL, 0); + len = sizeof(uint64_t); + sysctlbyname("hw.cpufrequency", &_display_information->_current_cpu_frequency, &len, NULL, 0); + len = sizeof(uint64_t); + sysctlbyname("hw.cpufrequency_max", &_display_information->_maximum_cpu_frequency, &len, NULL, 0); + len = sizeof(int); + sysctlbyname("hw.physicalcpu", &_display_information->_num_cpu_cores, &len, NULL, 0); + len = sizeof(int); + sysctlbyname("hw.logicalcpu", &_display_information->_num_logical_cpus, &len, NULL, 0); + +#elif defined(IS_LINUX) + _display_information->_get_memory_information_function = &update_memory_info; + update_memory_info(_display_information); + +#elif defined(IS_FREEBSD) + size_t len = sizeof(uint64_t); + sysctlbyname("hw.physmem", &_display_information->_physical_memory, &len, NULL, 0); + len = sizeof(uint64_t); + sysctlbyname("vm.swap_total", &_display_information->_page_file_size, &len, NULL, 0); + +#elif defined(_WIN32) + MEMORYSTATUSEX status; + status.dwLength = sizeof(MEMORYSTATUSEX); + if (GlobalMemoryStatusEx(&status)) { + _display_information->_physical_memory = status.ullTotalPhys; + _display_information->_available_physical_memory = status.ullAvailPhys; + _display_information->_page_file_size = status.ullTotalPageFile; + _display_information->_available_page_file_size = status.ullAvailPageFile; + _display_information->_process_virtual_memory = status.ullTotalVirtual; + _display_information->_available_process_virtual_memory = status.ullAvailVirtual; + _display_information->_memory_load = status.dwMemoryLoad; + } +#endif + +#if defined(IS_LINUX) || defined(IS_FREEBSD) + long nproc = sysconf(_SC_NPROCESSORS_CONF); + if (nproc > 0) { + _display_information->_num_logical_cpus = nproc; + } +#endif } /** diff --git a/panda/src/display/graphicsPipe.h b/panda/src/display/graphicsPipe.h index 462fa3afcf..f7994b92e1 100644 --- a/panda/src/display/graphicsPipe.h +++ b/panda/src/display/graphicsPipe.h @@ -53,8 +53,8 @@ class EXPCL_PANDA_DISPLAY GraphicsPipe : public TypedReferenceCount { protected: GraphicsPipe(); private: - GraphicsPipe(const GraphicsPipe ©); - void operator = (const GraphicsPipe ©); + GraphicsPipe(const GraphicsPipe ©) DELETED; + GraphicsPipe &operator = (const GraphicsPipe ©) DELETED_ASSIGN; PUBLISHED: virtual ~GraphicsPipe(); diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I index 15f95a19f4..94188b85be 100644 --- a/panda/src/display/graphicsStateGuardian.I +++ b/panda/src/display/graphicsStateGuardian.I @@ -538,6 +538,14 @@ get_supports_depth_stencil() const { return _supports_depth_stencil; } +/** + * Returns true if this particular GSG supports luminance textures. + */ +INLINE bool GraphicsStateGuardian:: +get_supports_luminance_texture() const { + return _supports_luminance_texture; +} + /** * Returns true if this particular GSG supports the filter mode FT_shadow for * depth textures. diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index d5b2f769a0..2d07f2c971 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -44,6 +44,7 @@ #include "ambientLight.h" #include "directionalLight.h" #include "pointLight.h" +#include "sphereLight.h" #include "spotlight.h" #include "textureReloadRequest.h" #include "shaderAttrib.h" @@ -253,6 +254,9 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system, _supports_geometry_instancing = false; _supports_indirect_draw = false; + // We are safe assuming it has luminance support + _supports_luminance_texture = true; + // Assume a maximum of 1 render target in absence of MRT. _max_color_targets = 1; _supports_dual_source_blending = false; @@ -277,6 +281,11 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system, _gamma = 1.0f; _texture_quality_override = Texture::QL_default; + + // Give it a unique identifier. Unlike a pointer, we can guarantee that + // this value will never be reused. + static size_t next_index = 0; + _id = next_index++; } /** @@ -286,6 +295,19 @@ GraphicsStateGuardian:: ~GraphicsStateGuardian() { remove_gsg(this); GeomMunger::unregister_mungers_for_gsg(this); + + // Remove the munged states for this GSG. This requires going through all + // states, although destructing a GSG should be rare enough for this not to + // matter too much. + // Note that if uniquify-states is false, we can't iterate over all the + // states, and some GSGs will linger. Let's hope this isn't a problem. + LightReMutexHolder holder(*RenderState::_states_lock); + size_t size = RenderState::_states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { + const RenderState *state = RenderState::_states->get_key(si); + state->_mungers.remove(_id); + state->_munged_states.remove(_id); + } } /** @@ -739,7 +761,7 @@ get_geom_munger(const RenderState *state, Thread *current_thread) { // multiple times during a frame. Also, this might well be the only GSG // in the world anyway. int mi = state->_last_mi; - if (mi >= 0 && mungers.has_element(mi) && mungers.get_key(mi) == this) { + if (mi >= 0 && mi < mungers.get_num_entries() && mungers.get_key(mi) == _id) { PT(GeomMunger) munger = mungers.get_data(mi); if (munger->is_registered()) { return munger; @@ -747,7 +769,7 @@ get_geom_munger(const RenderState *state, Thread *current_thread) { } // Nope, we have to look it up in the map. - mi = mungers.find(this); + mi = mungers.find(_id); if (mi >= 0) { PT(GeomMunger) munger = mungers.get_data(mi); if (munger->is_registered()) { @@ -765,7 +787,7 @@ get_geom_munger(const RenderState *state, Thread *current_thread) { nassertr(munger != (GeomMunger *)NULL && munger->is_registered(), munger); nassertr(munger->is_of_type(StateMunger::get_class_type()), munger); - state->_last_mi = mungers.store(this, munger); + state->_last_mi = mungers.store(_id, munger); return munger; } @@ -900,16 +922,10 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, case Shader::SMO_identity: { return &LMatrix4::ident_mat(); } - case Shader::SMO_window_size: { - t = LMatrix4::translate_mat(_current_display_region->get_pixel_width(), - _current_display_region->get_pixel_height(), - 0.0); - return &t; - } + case Shader::SMO_window_size: case Shader::SMO_pixel_size: { - t = LMatrix4::translate_mat(_current_display_region->get_pixel_width(), - _current_display_region->get_pixel_height(), - 0.0); + LVecBase2i pixel_size = _current_display_region->get_pixel_size(); + t = LMatrix4::translate_mat(pixel_size[0], pixel_size[1], 0); return &t; } case Shader::SMO_frame_time: { @@ -1046,7 +1062,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, LColor const &c = lt->get_color(); LColor const &s = lt->get_specular_color(); t = np.get_net_transform()->get_mat() * - get_scene()->get_world_transform()->get_mat(); + _scene_setup->get_world_transform()->get_mat(); LVecBase3 d = -(t.xform_vec(lt->get_direction())); d.normalize(); LVecBase3 h = d + LVecBase3(0,-1,0); @@ -1063,11 +1079,12 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, LColor const &c = lt->get_color(); LColor const &s = lt->get_specular_color(); t = np.get_net_transform()->get_mat() * - get_scene()->get_world_transform()->get_mat(); + _scene_setup->get_world_transform()->get_mat(); LVecBase3 p = (t.xform_point(lt->get_point())); LVecBase3 a = lt->get_attenuation(); - PN_stdfloat lnear = lt->get_lens(0)->get_near(); - PN_stdfloat lfar = lt->get_lens(0)->get_far(); + Lens *lens = lt->get_lens(0); + PN_stdfloat lnear = lens->get_near(); + PN_stdfloat lfar = lens->get_far(); t = LMatrix4(c[0],c[1],c[2],c[3],s[0],s[1],s[2],s[3],p[0],p[1],p[2],lnear,a[0],a[1],a[2],lfar); return &t; } @@ -1083,7 +1100,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, LColor const &s = lt->get_specular_color(); PN_stdfloat cutoff = ccos(deg_2_rad(lens->get_hfov() * 0.5f)); t = np.get_net_transform()->get_mat() * - get_scene()->get_world_transform()->get_mat(); + _scene_setup->get_world_transform()->get_mat(); LVecBase3 p = t.xform_point(lens->get_nodal_point()); LVecBase3 d = -(t.xform_vec(lens->get_view_vector())); t = LMatrix4(c[0],c[1],c[2],c[3],s[0],s[1],s[2],s[3],p[0],p[1],p[2],0,d[0],d[1],d[2],cutoff); @@ -1094,23 +1111,12 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, const LightAttrib *target_light = (const LightAttrib *) _target_rs->get_attrib_def(LightAttrib::get_class_slot()); - int num_on_lights = target_light->get_num_on_lights(); - if (num_on_lights == 0) { + if (!target_light->has_any_on_light()) { // There are no lights at all. This means, to follow the fixed- // function model, we pretend there is an all-white ambient light. t.set_row(3, LVecBase4(1, 1, 1, 1)); } else { - for (int li = 0; li < num_on_lights; li++) { - NodePath light = target_light->get_on_light(li); - nassertr(!light.is_empty(), &LMatrix4::zeros_mat()); - Light *light_obj = light.node()->as_light(); - nassertr(light_obj != (Light *)NULL, &LMatrix4::zeros_mat()); - - if (light_obj->get_type() == AmbientLight::get_class_type()) { - cur_ambient_light += light_obj->get_color(); - } - } - t.set_row(3, cur_ambient_light); + t.set_row(3, target_light->get_ambient_contribution()); } return &t; } @@ -1135,6 +1141,28 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, return &LMatrix4::ident_mat(); } } + case Shader::SMO_texscale_i: { + const TexMatrixAttrib *tma; + const TextureAttrib *ta; + if (_target_rs->get_attrib(ta) && _target_rs->get_attrib(tma) && + index < ta->get_num_on_stages()) { + LVecBase3 scale = tma->get_transform(ta->get_on_stage(index))->get_scale(); + t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,scale[0],scale[1],scale[2],0); + return &t; + } else { + return &LMatrix4::ident_mat(); + } + } + case Shader::SMO_texcolor_i: { + const TextureAttrib *ta; + if (_target_rs->get_attrib(ta) && index < ta->get_num_on_stages()) { + TextureStage *ts = ta->get_on_stage(index); + t.set_row(3, ts->get_color()); + return &t; + } else { + return &LMatrix4::zeros_mat(); + } + } case Shader::SMO_tex_is_alpha_i: { // This is a hack so we can support both F_alpha and other formats in the // default shader, to fix font rendering in GLES2 @@ -1152,27 +1180,36 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, case Shader::SMO_plane_x: { const NodePath &np = _target_shader->get_shader_input_nodepath(name); nassertr(!np.is_empty(), &LMatrix4::zeros_mat()); - nassertr(np.node()->is_of_type(PlaneNode::get_class_type()), &LMatrix4::zeros_mat()); - LPlane p = DCAST(PlaneNode, np.node())->get_plane(); + const PlaneNode *plane_node; + DCAST_INTO_R(plane_node, np.node(), &LMatrix4::zeros_mat()); + LPlane p = plane_node->get_plane(); t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,p[0],p[1],p[2],p[3]); return &t; } case Shader::SMO_clipplane_x: { - const ClipPlaneAttrib *cpa = DCAST(ClipPlaneAttrib, _target_rs->get_attrib_def(ClipPlaneAttrib::get_class_slot())); + const ClipPlaneAttrib *cpa; + _target_rs->get_attrib_def(cpa); int planenr = atoi(name->get_name().c_str()); if (planenr >= cpa->get_num_on_planes()) { return &LMatrix4::zeros_mat(); } const NodePath &np = cpa->get_on_plane(planenr); nassertr(!np.is_empty(), &LMatrix4::zeros_mat()); - nassertr(np.node()->is_of_type(PlaneNode::get_class_type()), &LMatrix4::zeros_mat()); - LPlane p (DCAST(PlaneNode, np.node())->get_plane()); - p.xform(np.get_net_transform()->get_mat()); // World-space - t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,p[0],p[1],p[2],p[3]); + const PlaneNode *plane_node; + DCAST_INTO_R(plane_node, np.node(), &LMatrix4::zeros_mat()); + + // Transform plane to world space + CPT(TransformState) transform = np.get_net_transform(); + LPlane plane = plane_node->get_plane(); + if (!transform->is_identity()) { + plane.xform(transform->get_mat()); + } + t.set_row(3, plane); return &t; } case Shader::SMO_apiview_clipplane_i: { - const ClipPlaneAttrib *cpa = DCAST(ClipPlaneAttrib, _target_rs->get_attrib_def(ClipPlaneAttrib::get_class_slot())); + const ClipPlaneAttrib *cpa; + _target_rs->get_attrib_def(cpa); if (index >= cpa->get_num_on_planes()) { return &LMatrix4::zeros_mat(); } @@ -1183,7 +1220,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, DCAST_INTO_R(plane_node, plane.node(), &LMatrix4::zeros_mat()); CPT(TransformState) transform = - get_scene()->get_cs_world_transform()->compose( + _scene_setup->get_cs_world_transform()->compose( plane.get_transform(_scene_setup->get_scene_root().get_parent())); LPlane xformed_plane = plane_node->get_plane() * transform->get_mat(); @@ -1203,24 +1240,23 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, return &t; } case Shader::SMO_world_to_view: { - return &(get_scene()->get_world_transform()->get_mat()); - break; + return &(_scene_setup->get_world_transform()->get_mat()); } case Shader::SMO_view_to_world: { - return &(get_scene()->get_camera_transform()->get_mat()); + return &(_scene_setup->get_camera_transform()->get_mat()); } case Shader::SMO_model_to_view: { - return &(get_external_transform()->get_mat()); + return &(_inv_cs_transform->compose(_internal_transform)->get_mat()); } case Shader::SMO_model_to_apiview: { - return &(get_internal_transform()->get_mat()); + return &(_internal_transform->get_mat()); } case Shader::SMO_view_to_model: { - t = get_external_transform()->get_inverse()->get_mat(); + t = _internal_transform->invert_compose(_cs_transform)->get_mat(); return &t; } case Shader::SMO_apiview_to_model: { - t = get_internal_transform()->get_inverse()->get_mat(); + t = _internal_transform->get_inverse()->get_mat(); return &t; } case Shader::SMO_apiview_to_view: { @@ -1265,13 +1301,13 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, const NodePath &np = _target_shader->get_shader_input_nodepath(name); nassertr(!np.is_empty(), &LMatrix4::ident_mat()); t = np.get_net_transform()->get_mat() * - get_scene()->get_world_transform()->get_mat(); + _scene_setup->get_world_transform()->get_mat(); return &t; } case Shader::SMO_view_to_view_x: { const NodePath &np = _target_shader->get_shader_input_nodepath(name); nassertr(!np.is_empty(), &LMatrix4::ident_mat()); - t = get_scene()->get_camera_transform()->get_mat() * + t = _scene_setup->get_camera_transform()->get_mat() * np.get_net_transform()->get_inverse()->get_mat(); return &t; } @@ -1280,13 +1316,13 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, nassertr(!np.is_empty(), &LMatrix4::ident_mat()); t = LMatrix4::convert_mat(_internal_coordinate_system, _coordinate_system) * np.get_net_transform()->get_mat() * - get_scene()->get_world_transform()->get_mat(); + _scene_setup->get_world_transform()->get_mat(); return &t; } case Shader::SMO_view_to_apiview_x: { const NodePath &np = _target_shader->get_shader_input_nodepath(name); nassertr(!np.is_empty(), &LMatrix4::ident_mat()); - t = (get_scene()->get_camera_transform()->get_mat() * + t = (_scene_setup->get_camera_transform()->get_mat() * np.get_net_transform()->get_inverse()->get_mat() * LMatrix4::convert_mat(_coordinate_system, _internal_coordinate_system)); return &t; @@ -1294,20 +1330,22 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, case Shader::SMO_clip_x_to_view: { const NodePath &np = _target_shader->get_shader_input_nodepath(name); nassertr(!np.is_empty(), &LMatrix4::ident_mat()); - nassertr(np.node()->is_of_type(LensNode::get_class_type()), &LMatrix4::ident_mat()); - Lens *lens = DCAST(LensNode, np.node())->get_lens(); + const LensNode *node; + DCAST_INTO_R(node, np.node(), &LMatrix4::ident_mat()); + const Lens *lens = node->get_lens(); t = lens->get_projection_mat_inv(_current_stereo_channel) * LMatrix4::convert_mat(lens->get_coordinate_system(), _coordinate_system) * np.get_net_transform()->get_mat() * - get_scene()->get_world_transform()->get_mat(); + _scene_setup->get_world_transform()->get_mat(); return &t; } case Shader::SMO_view_to_clip_x: { const NodePath &np = _target_shader->get_shader_input_nodepath(name); nassertr(!np.is_empty(), &LMatrix4::ident_mat()); - nassertr(np.node()->is_of_type(LensNode::get_class_type()), &LMatrix4::ident_mat()); - Lens *lens = DCAST(LensNode, np.node())->get_lens(); - t = get_scene()->get_camera_transform()->get_mat() * + const LensNode *node; + DCAST_INTO_R(node, np.node(), &LMatrix4::ident_mat()); + const Lens *lens = node->get_lens(); + t = _scene_setup->get_camera_transform()->get_mat() * np.get_net_transform()->get_inverse()->get_mat() * LMatrix4::convert_mat(_coordinate_system, lens->get_coordinate_system()) * lens->get_projection_mat(_current_stereo_channel); @@ -1316,20 +1354,22 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, case Shader::SMO_apiclip_x_to_view: { const NodePath &np = _target_shader->get_shader_input_nodepath(name); nassertr(!np.is_empty(), &LMatrix4::ident_mat()); - nassertr(np.node()->is_of_type(LensNode::get_class_type()), &LMatrix4::ident_mat()); - Lens *lens = DCAST(LensNode, np.node())->get_lens(); + const LensNode *node; + DCAST_INTO_R(node, np.node(), &LMatrix4::ident_mat()); + const Lens *lens = node->get_lens(); t = calc_projection_mat(lens)->get_inverse()->get_mat() * get_cs_transform_for(lens->get_coordinate_system())->get_inverse()->get_mat() * np.get_net_transform()->get_mat() * - get_scene()->get_world_transform()->get_mat(); + _scene_setup->get_world_transform()->get_mat(); return &t; } case Shader::SMO_view_to_apiclip_x: { const NodePath &np = _target_shader->get_shader_input_nodepath(name); nassertr(!np.is_empty(), &LMatrix4::ident_mat()); - nassertr(np.node()->is_of_type(LensNode::get_class_type()), &LMatrix4::ident_mat()); - Lens *lens = DCAST(LensNode, np.node())->get_lens(); - t = get_scene()->get_camera_transform()->get_mat() * + const LensNode *node; + DCAST_INTO_R(node, np.node(), &LMatrix4::ident_mat()); + const Lens *lens = node->get_lens(); + t = _scene_setup->get_camera_transform()->get_mat() * np.get_net_transform()->get_inverse()->get_mat() * get_cs_transform_for(lens->get_coordinate_system())->get_mat() * calc_projection_mat(lens)->get_mat(); @@ -1368,28 +1408,20 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, const LightAttrib *target_light; _target_rs->get_attrib_def(target_light); - // We want to ignore ambient lights. To that effect, iterate through the - // list of lights. In the future, we will improve this system, by also - // filtering down to the number of lights specified by the shader. - int i = 0; - - int num_on_lights = target_light->get_num_on_lights(); - for (int li = 0; li < num_on_lights; li++) { - NodePath light = target_light->get_on_light(li); + // We don't count ambient lights, which would be pretty silly to handle + // via this mechanism. + size_t num_lights = target_light->get_num_non_ambient_lights(); + if (index >= 0 && (size_t)index < num_lights) { + NodePath light = target_light->get_on_light((size_t)index); nassertr(!light.is_empty(), &LMatrix4::ident_mat()); Light *light_obj = light.node()->as_light(); - nassertr(light_obj != (Light *)NULL, &LMatrix4::ident_mat()); + nassertr(light_obj != nullptr, &LMatrix4::ident_mat()); - if (light_obj->get_type() != AmbientLight::get_class_type()) { - if (i++ == index) { - return fetch_specified_member(light, name, t); - } - } - } + return fetch_specified_member(light, name, t); - // Apply the default OpenGL lights otherwise. - // Special exception for light 0, which defaults to white. - if (index == 0) { + } else if (index == 0) { + // Apply the default OpenGL lights otherwise. + // Special exception for light 0, which defaults to white. string basename = name->get_basename(); if (basename == "color" || basename == "diffuse") { t.set_row(3, _light_color_scale); @@ -1400,6 +1432,62 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, } return fetch_specified_member(NodePath(), name, t); } + case Shader::SMO_light_source_i_packed: { + // The light matrix contains COLOR, ATTENUATION, POSITION, VIEWVECTOR + const LightAttrib *target_light; + _target_rs->get_attrib_def(target_light); + + // We don't count ambient lights, which would be pretty silly to handle + // via this mechanism. + size_t num_lights = target_light->get_num_non_ambient_lights(); + if (index >= 0 && (size_t)index < num_lights) { + NodePath np = target_light->get_on_light((size_t)index); + nassertr(!np.is_empty(), &LMatrix4::ident_mat()); + PandaNode *node = np.node(); + Light *light = node->as_light(); + nassertr(light != nullptr, &LMatrix4::zeros_mat()); + t.set_row(0, light->get_color()); + t.set_row(1, light->get_attenuation()); + + LMatrix4 mat = np.get_net_transform()->get_mat() * + _scene_setup->get_world_transform()->get_mat(); + + if (node->is_of_type(DirectionalLight::get_class_type())) { + LVecBase3 d = mat.xform_vec(((const DirectionalLight *)node)->get_direction()); + d.normalize(); + t.set_row(2, LVecBase4(d, 0)); + t.set_row(3, LVecBase4(-d, 0)); + + } else if (node->is_of_type(LightLensNode::get_class_type())) { + const Lens *lens = ((const LightLensNode *)node)->get_lens(); + + LPoint3 p = mat.xform_point(lens->get_nodal_point()); + t.set_row(3, LVecBase4(p)); + + // For shadowed point light we need to store near/far. + // For spotlight we need to store cutoff angle. + if (node->is_of_type(Spotlight::get_class_type())) { + PN_stdfloat cutoff = ccos(deg_2_rad(lens->get_hfov() * 0.5f)); + LVecBase3 d = -(mat.xform_vec(lens->get_view_vector())); + t.set_cell(1, 3, ((const Spotlight *)node)->get_exponent()); + t.set_row(2, LVecBase4(d, cutoff)); + + } else if (node->is_of_type(PointLight::get_class_type())) { + t.set_cell(1, 3, lens->get_far()); + t.set_cell(3, 3, lens->get_near()); + + if (node->is_of_type(SphereLight::get_class_type())) { + t.set_cell(2, 3, ((const SphereLight *)node)->get_radius()); + } + } + } + } else if (index == 0) { + // Apply the default OpenGL lights otherwise. + // Special exception for light 0, which defaults to white. + t.set_row(0, _light_color_scale); + } + return &t; + } default: nassertr(false /*should never get here*/, &LMatrix4::ident_mat()); return &LMatrix4::ident_mat(); @@ -1427,7 +1515,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) static const CPT_InternalName IN_constantAttenuation("constantAttenuation"); static const CPT_InternalName IN_linearAttenuation("linearAttenuation"); static const CPT_InternalName IN_quadraticAttenuation("quadraticAttenuation"); - static const CPT_InternalName IN_shadowMatrix("shadowMatrix"); + static const CPT_InternalName IN_shadowViewMatrix("shadowViewMatrix"); PandaNode *node = NULL; if (!np.is_empty()) { @@ -1451,7 +1539,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) } Light *light = node->as_light(); nassertr(light != (Light *)NULL, &LMatrix4::ident_mat()); - if (node->is_of_type(AmbientLight::get_class_type())) { + if (node->is_ambient_light()) { LColor c = light->get_color(); c.componentwise_mult(_light_color_scale); t.set_row(3, c); @@ -1467,7 +1555,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) } Light *light = node->as_light(); nassertr(light != (Light *)NULL, &LMatrix4::ones_mat()); - if (node->is_of_type(AmbientLight::get_class_type())) { + if (node->is_ambient_light()) { // Ambient light has no diffuse color. t.set_row(3, LColor(0.0f, 0.0f, 0.0f, 1.0f)); } else { @@ -1490,7 +1578,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) if (np.is_empty()) { t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0); return &t; - } else if (node->is_of_type(AmbientLight::get_class_type())) { + } else if (node->is_ambient_light()) { // Ambient light has no position. t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); return &t; @@ -1500,7 +1588,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent()); LVector3 dir = -(light->get_direction() * transform->get_mat()); - dir *= get_scene()->get_cs_world_transform()->get_mat(); + dir *= _scene_setup->get_cs_world_transform()->get_mat(); t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,dir[0],dir[1],dir[2],0); return &t; } else { @@ -1510,7 +1598,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat()); CPT(TransformState) transform = - get_scene()->get_cs_world_transform()->compose( + _scene_setup->get_cs_world_transform()->compose( np.get_transform(_scene_setup->get_scene_root().get_parent())); const LMatrix4 &light_mat = transform->get_mat(); @@ -1523,7 +1611,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) if (np.is_empty()) { t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0); return &t; - } else if (node->is_of_type(AmbientLight::get_class_type())) { + } else if (node->is_ambient_light()) { // Ambient light has no half-vector. t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); return &t; @@ -1533,7 +1621,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent()); LVector3 dir = -(light->get_direction() * transform->get_mat()); - dir *= get_scene()->get_cs_world_transform()->get_mat(); + dir *= _scene_setup->get_cs_world_transform()->get_mat(); dir.normalize(); dir += LVector3(0, 0, 1); dir.normalize(); @@ -1546,7 +1634,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat()); CPT(TransformState) transform = - get_scene()->get_cs_world_transform()->compose( + _scene_setup->get_cs_world_transform()->compose( np.get_transform(_scene_setup->get_scene_root().get_parent())); const LMatrix4 &light_mat = transform->get_mat(); @@ -1562,7 +1650,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) if (node == (PandaNode *)NULL) { t.set_row(3, LVector3(0.0f, 0.0f, -1.0f)); return &t; - } else if (node->is_of_type(AmbientLight::get_class_type())) { + } else if (node->is_ambient_light()) { // Ambient light has no spot direction. t.set_row(3, LVector3(0.0f, 0.0f, 0.0f)); return &t; @@ -1573,7 +1661,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat()); CPT(TransformState) transform = - get_scene()->get_cs_world_transform()->compose( + _scene_setup->get_cs_world_transform()->compose( np.get_transform(_scene_setup->get_scene_root().get_parent())); const LMatrix4 &light_mat = transform->get_mat(); @@ -1667,7 +1755,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) t.set_row(3, LVecBase4(light->get_attenuation()[2])); return &t; - } else if (attrib == IN_shadowMatrix) { + } else if (attrib == IN_shadowViewMatrix) { static const LMatrix4 biasmat(0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, @@ -1681,11 +1769,14 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) DCAST_INTO_R(lnode, node, &LMatrix4::ident_mat()); Lens *lens = lnode->get_lens(); - t = get_external_transform()->get_mat() * - get_scene()->get_camera_transform()->get_mat() * + t = _inv_cs_transform->get_mat() * + _scene_setup->get_camera_transform()->get_mat() * np.get_net_transform()->get_inverse()->get_mat() * - LMatrix4::convert_mat(_coordinate_system, lens->get_coordinate_system()) * - lens->get_projection_mat() * biasmat; + LMatrix4::convert_mat(_coordinate_system, lens->get_coordinate_system()); + + if (!node->is_of_type(PointLight::get_class_type())) { + t *= lens->get_projection_mat() * biasmat; + } return &t; } else { @@ -1767,27 +1858,27 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler, const LightAttrib *target_light; _target_rs->get_attrib_def(target_light); - // We want to ignore ambient lights. To that effect, iterate through - // the list of lights. In the future, we will improve this system, by - // also filtering down to the number of lights specified by the shader. - int i = 0; - - int num_on_lights = target_light->get_num_on_lights(); - for (int li = 0; li < num_on_lights; li++) { - NodePath light = target_light->get_on_light(li); - nassertr(!light.is_empty(), NULL); + // We don't count ambient lights, which would be pretty silly to handle + // via this mechanism. + size_t num_lights = target_light->get_num_non_ambient_lights(); + if (spec._stage >= 0 && (size_t)spec._stage < num_lights) { + NodePath light = target_light->get_on_light((size_t)spec._stage); + nassertr(!light.is_empty(), nullptr); Light *light_obj = light.node()->as_light(); - nassertr(light_obj != (Light *)NULL, NULL); + nassertr(light_obj != nullptr, nullptr); - if (light_obj->get_type() != AmbientLight::get_class_type()) { - if (i++ == spec._stage) { - PT(Texture) tex = get_shadow_map(light); - if (tex != (Texture *)NULL) { - sampler = tex->get_default_sampler(); - } - return tex; - } + PT(Texture) tex = get_shadow_map(light); + if (tex != nullptr) { + sampler = tex->get_default_sampler(); } + return tex; + } else { + // There is no such light assigned. Bind a dummy shadow map. + PT(Texture) tex = get_dummy_shadow_map((Texture::TextureType)spec._desired_type); + if (tex != nullptr) { + sampler = tex->get_default_sampler(); + } + return tex; } } break; @@ -2273,10 +2364,8 @@ finish_decal() { */ bool GraphicsStateGuardian:: begin_draw_primitives(const GeomPipelineReader *geom_reader, - const GeomMunger *munger, const GeomVertexDataPipelineReader *data_reader, bool force) { - _munger = munger; _data_reader = data_reader; // Always draw if we have a shader, since the shader might use a different @@ -2347,7 +2436,6 @@ draw_points(const GeomPrimitivePipelineReader *, bool) { */ void GraphicsStateGuardian:: end_draw_primitives() { - _munger = NULL; _data_reader = NULL; } @@ -2629,7 +2717,7 @@ do_issue_light() { int i; int num_enabled = 0; - int num_on_lights = 0; + bool any_on_lights = false; const LightAttrib *target_light; _target_rs->get_attrib_def(target_light); @@ -2638,18 +2726,16 @@ do_issue_light() { display_cat.spam() << "do_issue_light: " << target_light << "\n"; } - if (target_light != (LightAttrib *)NULL) { - CPT(LightAttrib) new_light = target_light->filter_to_max(_max_lights); - if (display_cat.is_spam()) { - new_light->write(display_cat.spam(false), 2); - } - - num_on_lights = new_light->get_num_on_lights(); - for (int li = 0; li < num_on_lights; li++) { - NodePath light = new_light->get_on_light(li); + if (target_light != nullptr) { + // LightAttrib guarantees that the on lights are sorted, and that + // non-ambient lights come before ambient lights. + any_on_lights = target_light->has_any_on_light(); + size_t filtered_lights = min((size_t)_max_lights, target_light->get_num_non_ambient_lights()); + for (size_t li = 0; li < filtered_lights; ++li) { + NodePath light = target_light->get_on_light(li); nassertv(!light.is_empty()); Light *light_obj = light.node()->as_light(); - nassertv(light_obj != (Light *)NULL); + nassertv(light_obj != nullptr); // Lighting should be enabled before we apply any lights. if (!_lighting_enabled) { @@ -2657,23 +2743,16 @@ do_issue_light() { _lighting_enabled = true; } - if (light_obj->get_type() == AmbientLight::get_class_type()) { - // Ambient lights don't require specific light ids; simply add in the - // ambient contribution to the current total - cur_ambient_light += light_obj->get_color(); - - } else { - const LColor &color = light_obj->get_color(); - // Don't bother binding the light if it has no color to contribute. - if (color[0] != 0.0 || color[1] != 0.0 || color[2] != 0.0) { - enable_light(num_enabled, true); - if (num_enabled == 0) { - begin_bind_lights(); - } - - light_obj->bind(this, light, num_enabled); - num_enabled++; + const LColor &color = light_obj->get_color(); + // Don't bother binding the light if it has no color to contribute. + if (color[0] != 0.0 || color[1] != 0.0 || color[2] != 0.0) { + enable_light(num_enabled, true); + if (num_enabled == 0) { + begin_bind_lights(); } + + light_obj->bind(this, light, num_enabled); + num_enabled++; } } } @@ -2684,7 +2763,7 @@ do_issue_light() { _num_lights_enabled = num_enabled; // If no lights were set, disable lighting - if (num_on_lights == 0) { + if (!any_on_lights) { if (_color_scale_via_lighting && (_has_material_force_color || _light_color_scale != LVecBase4(1.0f, 1.0f, 1.0f, 1.0f))) { // Unless we need lighting anyway to apply a color or color scale. if (!_lighting_enabled) { @@ -2701,7 +2780,7 @@ do_issue_light() { } } else { - set_ambient_light(cur_ambient_light); + set_ambient_light(target_light->get_ambient_contribution()); } if (num_enabled != 0) { @@ -2960,6 +3039,19 @@ determine_target_texture() { nassertv(_target_texture->get_num_on_stages() <= max_texture_stages); } +/** + * Assigns _target_shader based on the _target_rs. + */ +void GraphicsStateGuardian:: +determine_target_shader() { + if (_target_rs->_generated_shader != nullptr) { + _target_shader = (const ShaderAttrib *)_target_rs->_generated_shader.p(); + } else { + _target_shader = (const ShaderAttrib *) + _target_rs->get_attrib_def(ShaderAttrib::get_class_slot()); + } +} + /** * Frees some memory that was explicitly allocated within the glgsg. */ @@ -3092,15 +3184,15 @@ get_untextured_state() { * Should be called when a texture is encountered that needs to have its RAM * image reloaded, and get_incomplete_render() is true. This will fire off a * thread on the current Loader object that will request the texture to load - * its image. The image will be available at some point in the future (no - * event will be generated). + * its image. The image will be available at some point in the future. + * @returns a future object that can be used to check its status. */ -void GraphicsStateGuardian:: +AsyncFuture *GraphicsStateGuardian:: async_reload_texture(TextureContext *tc) { - nassertv(_loader != (Loader *)NULL); + nassertr(_loader != nullptr, nullptr); int priority = 0; - if (_current_display_region != (DisplayRegion *)NULL) { + if (_current_display_region != nullptr) { priority = _current_display_region->get_texture_reload_priority(); } @@ -3109,15 +3201,15 @@ async_reload_texture(TextureContext *tc) { // See if we are already loading this task. AsyncTaskCollection orig_tasks = task_mgr->find_tasks(task_name); - int num_tasks = orig_tasks.get_num_tasks(); - for (int ti = 0; ti < num_tasks; ++ti) { + size_t num_tasks = orig_tasks.get_num_tasks(); + for (size_t ti = 0; ti < num_tasks; ++ti) { AsyncTask *task = orig_tasks.get_task(ti); if (task->is_exact_type(TextureReloadRequest::get_class_type()) && - DCAST(TextureReloadRequest, task)->get_texture() == tc->get_texture()) { + ((TextureReloadRequest *)task)->get_texture() == tc->get_texture()) { // This texture is already queued to be reloaded. Don't queue it again, // just make sure the priority is updated, and return. task->set_priority(max(task->get_priority(), priority)); - return; + return (AsyncFuture *)task; } } @@ -3128,6 +3220,7 @@ async_reload_texture(TextureContext *tc) { _supports_compressed_texture); request->set_priority(priority); _loader->load_async(request); + return (AsyncFuture *)request.p(); } /** @@ -3137,14 +3230,20 @@ async_reload_texture(TextureContext *tc) { */ PT(Texture) GraphicsStateGuardian:: get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) { - nassertr(light_np.node()->is_of_type(DirectionalLight::get_class_type()) || - light_np.node()->is_of_type(PointLight::get_class_type()) || - light_np.node()->is_of_type(Spotlight::get_class_type()), NULL); + PandaNode *node = light_np.node(); + nassertr(node->is_of_type(DirectionalLight::get_class_type()) || + node->is_of_type(PointLight::get_class_type()) || + node->is_of_type(Spotlight::get_class_type()), NULL); - PT(LightLensNode) light = DCAST(LightLensNode, light_np.node()); - if (light == NULL || !light->_shadow_caster) { - // TODO: return dummy shadow map (all white). - return NULL; + LightLensNode *light = (LightLensNode *)node; + if (light == nullptr || !light->_shadow_caster) { + // This light does not have a shadow caster. Return a dummy shadow map + // that is filled with a depth value of 1. + if (node->is_of_type(PointLight::get_class_type())) { + return get_dummy_shadow_map(Texture::TT_cube_map); + } else { + return get_dummy_shadow_map(Texture::TT_2d_texture); + } } // See if we already have a buffer. If not, create one. @@ -3163,6 +3262,42 @@ get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) { } } +/** + * Returns a dummy shadow map that can be used for a light of the given type + * that does not cast shadows. + */ +PT(Texture) GraphicsStateGuardian:: +get_dummy_shadow_map(Texture::TextureType texture_type) const { + if (texture_type != Texture::TT_cube_map) { + static PT(Texture) dummy_2d; + if (dummy_2d == nullptr) { + dummy_2d = new Texture("dummy-shadow-2d"); + dummy_2d->setup_2d_texture(1, 1, Texture::T_unsigned_byte, Texture::F_depth_component); + dummy_2d->set_clear_color(1); + if (get_supports_shadow_filter()) { + // If we have the ARB_shadow extension, enable shadow filtering. + dummy_2d->set_minfilter(SamplerState::FT_shadow); + dummy_2d->set_magfilter(SamplerState::FT_shadow); + } else { + dummy_2d->set_minfilter(SamplerState::FT_linear); + dummy_2d->set_magfilter(SamplerState::FT_linear); + } + } + return dummy_2d; + } else { + static PT(Texture) dummy_cube; + if (dummy_cube == nullptr) { + dummy_cube = new Texture("dummy-shadow-cube"); + dummy_cube->setup_cube_map(1, Texture::T_unsigned_byte, Texture::F_depth_component); + dummy_cube->set_clear_color(1); + // Note: cube map shadow filtering doesn't seem to work in Cg. + dummy_cube->set_minfilter(SamplerState::FT_linear); + dummy_cube->set_magfilter(SamplerState::FT_linear); + } + return dummy_cube; + } +} + /** * Creates a depth buffer for shadow mapping. This is a convenience function * for the ShaderGenerator; putting this directly in the ShaderGenerator would @@ -3171,11 +3306,12 @@ get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) { PT(Texture) GraphicsStateGuardian:: make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) { // Make sure everything is valid. - nassertr(light_np.node()->is_of_type(DirectionalLight::get_class_type()) || - light_np.node()->is_of_type(PointLight::get_class_type()) || - light_np.node()->is_of_type(Spotlight::get_class_type()), NULL); + PandaNode *node = light_np.node(); + nassertr(node->is_of_type(DirectionalLight::get_class_type()) || + node->is_of_type(PointLight::get_class_type()) || + node->is_of_type(Spotlight::get_class_type()), NULL); - PT(LightLensNode) light = DCAST(LightLensNode, light_np.node()); + LightLensNode *light = (LightLensNode *)node; if (light == NULL || !light->_shadow_caster) { return NULL; } @@ -3260,6 +3396,43 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) { return tex; } +/** + * Ensures that an appropriate shader has been generated for the given state. + * This is stored in the _generated_shader field on the RenderState. + */ +void GraphicsStateGuardian:: +ensure_generated_shader(const RenderState *state) { +#ifdef HAVE_CG + const ShaderAttrib *shader_attrib; + state->get_attrib_def(shader_attrib); + + if (shader_attrib->auto_shader()) { + if (_shader_generator == nullptr) { + if (!_supports_basic_shaders) { + return; + } + _shader_generator = new ShaderGenerator(this); + } + if (state->_generated_shader == nullptr || + state->_generated_shader_seq != _generated_shader_seq) { + GeomVertexAnimationSpec spec; + + // Currently we overload this flag to request vertex animation for the + // shader generator. + const ShaderAttrib *sattr; + state->get_attrib_def(sattr); + if (sattr->get_flag(ShaderAttrib::F_hardware_skinning)) { + spec.set_hardware(4, true); + } + + // Cache the generated ShaderAttrib on the shader state. + state->_generated_shader = _shader_generator->synthesize_shader(state, spec); + state->_generated_shader_seq = _generated_shader_seq; + } + } +#endif +} + /** * Returns true if the GSG implements the extension identified by the given * string. This currently is only implemented by the OpenGL back-end. diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 81643a1f11..8a0ca541d0 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -154,6 +154,7 @@ PUBLISHED: INLINE bool get_supports_generate_mipmap() const; INLINE bool get_supports_depth_texture() const; INLINE bool get_supports_depth_stencil() const; + INLINE bool get_supports_luminance_texture() const; INLINE bool get_supports_shadow_filter() const; INLINE bool get_supports_sampler_objects() const; INLINE bool get_supports_basic_shaders() const; @@ -203,6 +204,7 @@ PUBLISHED: MAKE_PROPERTY(supports_generate_mipmap, get_supports_generate_mipmap); MAKE_PROPERTY(supports_depth_texture, get_supports_depth_texture); MAKE_PROPERTY(supports_depth_stencil, get_supports_depth_stencil); + MAKE_PROPERTY(supports_luminance_texture, get_supports_luminance_texture); MAKE_PROPERTY(supports_shadow_filter, get_supports_shadow_filter); MAKE_PROPERTY(supports_sampler_objects, get_supports_sampler_objects); MAKE_PROPERTY(supports_basic_shaders, get_supports_basic_shaders); @@ -284,7 +286,7 @@ PUBLISHED: MAKE_PROPERTY(driver_shader_version_minor, get_driver_shader_version_minor); bool set_scene(SceneSetup *scene_setup); - virtual SceneSetup *get_scene() const; + virtual SceneSetup *get_scene() const FINAL; MAKE_PROPERTY(scene, get_scene, set_scene); public: @@ -365,7 +367,6 @@ public: virtual void finish_decal(); virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, - const GeomMunger *munger, const GeomVertexDataPipelineReader *data_reader, bool force); virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, @@ -422,8 +423,11 @@ public: static void create_gamma_table (PN_stdfloat gamma, unsigned short *red_table, unsigned short *green_table, unsigned short *blue_table); PT(Texture) get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host=NULL); + PT(Texture) get_dummy_shadow_map(Texture::TextureType texture_type) const; PT(Texture) make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host); + virtual void ensure_generated_shader(const RenderState *state); + #ifdef DO_PSTATS static void init_frame_pstats(); #endif @@ -444,6 +448,7 @@ protected: virtual void end_bind_clip_planes(); void determine_target_texture(); + void determine_target_shader(); virtual void free_pointers(); virtual void close_gsg(); @@ -455,7 +460,7 @@ protected: static CPT(RenderState) get_unclipped_state(); static CPT(RenderState) get_untextured_state(); - void async_reload_texture(TextureContext *tc); + AsyncFuture *async_reload_texture(TextureContext *tc); protected: PT(SceneSetup) _scene_null; @@ -493,9 +498,8 @@ protected: CPT(ShaderAttrib) _state_shader; CPT(ShaderAttrib) _target_shader; - // These are set by begin_draw_primitives(), and are only valid between + // This is set by begin_draw_primitives(), and are only valid between // begin_draw_primitives() and end_draw_primitives(). - CPT(GeomMunger) _munger; const GeomVertexDataPipelineReader *_data_reader; unsigned int _color_write_mask; @@ -597,6 +601,7 @@ protected: bool _supports_generate_mipmap; bool _supports_depth_texture; bool _supports_depth_stencil; + bool _supports_luminance_texture; bool _supports_shadow_filter; bool _supports_sampler_objects; bool _supports_basic_shaders; diff --git a/panda/src/display/mouseAndKeyboard.h b/panda/src/display/mouseAndKeyboard.h index 158e2dbaef..c4c31aff84 100644 --- a/panda/src/display/mouseAndKeyboard.h +++ b/panda/src/display/mouseAndKeyboard.h @@ -40,7 +40,7 @@ */ class EXPCL_PANDA_DEVICE MouseAndKeyboard : public DataNode { PUBLISHED: - MouseAndKeyboard(GraphicsWindow *window, int device, const string &name); + explicit MouseAndKeyboard(GraphicsWindow *window, int device, const string &name); void set_source(GraphicsWindow *window, int device); PT(GraphicsWindow) get_source_window() const; diff --git a/panda/src/display/standardMunger.cxx b/panda/src/display/standardMunger.cxx index b6bc1ada2a..cdc2f0bda3 100644 --- a/panda/src/display/standardMunger.cxx +++ b/panda/src/display/standardMunger.cxx @@ -38,7 +38,17 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state, _auto_shader(false), _shader_skinning(false) { - if (!get_gsg()->get_runtime_color_scale()) { + const ShaderAttrib *shader_attrib; + state->get_attrib_def(shader_attrib); +#ifdef HAVE_CG + _auto_shader = shader_attrib->auto_shader(); +#endif + if (shader_attrib->get_flag(ShaderAttrib::F_hardware_skinning)) { + _shader_skinning = true; + } + + if (!get_gsg()->get_runtime_color_scale() && !_auto_shader && + shader_attrib->get_shader() == nullptr) { // We might need to munge the colors. const ColorAttrib *color_attrib; const ColorScaleAttrib *color_scale_attrib; @@ -60,6 +70,7 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state, _color[3] * cs[3]); } _munge_color = true; + _should_munge_state = true; } } else if (state->get_attrib(color_scale_attrib) && @@ -74,6 +85,7 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state, if ((color_scale_attrib->has_rgb_scale() && !get_gsg()->get_color_scale_via_lighting()) || (color_scale_attrib->has_alpha_scale() && !get_gsg()->get_alpha_scale_via_texture(tex_attrib))) { _munge_color_scale = true; + _should_munge_state = true; } // Known bug: if there is a material on an object that would obscure the @@ -82,15 +94,6 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state, // effort to detect this contrived situation and handle it correctly. } } - - const ShaderAttrib *shader_attrib = (const ShaderAttrib *) - state->get_attrib_def(ShaderAttrib::get_class_slot()); - if (shader_attrib->auto_shader()) { - _auto_shader = true; - } - if (shader_attrib->get_flag(ShaderAttrib::F_hardware_skinning)) { - _shader_skinning = true; - } } /** @@ -341,32 +344,5 @@ munge_state_impl(const RenderState *state) { munged_state = munged_state->remove_attrib(ColorScaleAttrib::get_class_slot()); } -#ifdef HAVE_CG - if (_auto_shader) { - CPT(RenderState) shader_state = munged_state->get_auto_shader_state(); - ShaderGenerator *shader_generator = get_gsg()->get_shader_generator(); - if (shader_generator == NULL) { - pgraph_cat.error() - << "auto_shader enabled, but GSG has no shader generator assigned!\n"; - return munged_state; - } - if (shader_state->_generated_shader == NULL) { - // Cache the generated ShaderAttrib on the shader state. - GeomVertexAnimationSpec spec; - - // Currently we overload this flag to request vertex animation for the - // shader generator. - const ShaderAttrib *sattr; - shader_state->get_attrib_def(sattr); - if (sattr->get_flag(ShaderAttrib::F_hardware_skinning)) { - spec.set_hardware(4, true); - } - - shader_state->_generated_shader = shader_generator->synthesize_shader(shader_state, spec); - } - munged_state = munged_state->set_attrib(shader_state->_generated_shader); - } -#endif - return munged_state; } diff --git a/panda/src/display/windowProperties.h b/panda/src/display/windowProperties.h index efb58255b6..bd5e964f52 100644 --- a/panda/src/display/windowProperties.h +++ b/panda/src/display/windowProperties.h @@ -49,6 +49,8 @@ PUBLISHED: static WindowProperties get_default(); static void set_default(const WindowProperties &default_properties); static void clear_default(); + MAKE_PROPERTY(config_properties, get_config_properties); + MAKE_PROPERTY(default, get_default, set_default); static WindowProperties size(int x_size, int y_size); diff --git a/panda/src/distort/projectionScreen.cxx b/panda/src/distort/projectionScreen.cxx index fd319031e6..1991bfc120 100644 --- a/panda/src/distort/projectionScreen.cxx +++ b/panda/src/distort/projectionScreen.cxx @@ -103,7 +103,7 @@ make_copy() const { bool ProjectionScreen:: cull_callback(CullTraverser *, CullTraverserData &data) { if (_auto_recompute) { - recompute_if_stale(data._node_path.get_node_path()); + recompute_if_stale(data.get_node_path()); } return true; } diff --git a/panda/src/distort/projectionScreen.h b/panda/src/distort/projectionScreen.h index 963e38370a..86f9484379 100644 --- a/panda/src/distort/projectionScreen.h +++ b/panda/src/distort/projectionScreen.h @@ -47,7 +47,7 @@ class WorkingNodePath; */ class EXPCL_PANDAFX ProjectionScreen : public PandaNode { PUBLISHED: - ProjectionScreen(const string &name = ""); + explicit ProjectionScreen(const string &name = ""); virtual ~ProjectionScreen(); protected: diff --git a/panda/src/downloader/bioPtr.I b/panda/src/downloader/bioPtr.I index f09ee7bbc8..a1e1f13e1d 100644 --- a/panda/src/downloader/bioPtr.I +++ b/panda/src/downloader/bioPtr.I @@ -18,14 +18,6 @@ INLINE BioPtr:: BioPtr(BIO *bio) : _bio(bio) { } -/** - * - */ -INLINE bool BioPtr:: -should_retry() const { - return (_bio != NULL) && BIO_should_retry(_bio); -} - /** * */ diff --git a/panda/src/downloader/bioPtr.cxx b/panda/src/downloader/bioPtr.cxx index 15fe997c02..b86f37f816 100644 --- a/panda/src/downloader/bioPtr.cxx +++ b/panda/src/downloader/bioPtr.cxx @@ -18,6 +18,9 @@ #include "urlSpec.h" #include "config_downloader.h" +#include "openSSLWrapper.h" // must be included before any other openssl. +#include "openssl/ssl.h" + #ifdef _WIN32 #include #else @@ -199,7 +202,7 @@ connect() { if (result != 0 && BIO_sock_should_retry(-1)) { // It's still in progress; we should retry later. This causes - // should_reply() to return true. + // should_retry() to return true. BIO_set_flags(_bio, BIO_FLAGS_SHOULD_RETRY); _connecting = true; return false; @@ -218,6 +221,14 @@ connect() { return true; } +/** + * + */ +bool BioPtr:: +should_retry() const { + return (_bio != NULL) && BIO_should_retry(_bio); +} + /** * */ diff --git a/panda/src/downloader/bioPtr.h b/panda/src/downloader/bioPtr.h index 332a1fc715..090ae20608 100644 --- a/panda/src/downloader/bioPtr.h +++ b/panda/src/downloader/bioPtr.h @@ -19,13 +19,7 @@ // This module is not compiled if OpenSSL is not available. #ifdef HAVE_OPENSSL -#ifndef OPENSSL_NO_KRB5 -#define OPENSSL_NO_KRB5 -#endif - #include "referenceCount.h" -#include "openSSLWrapper.h" // must be included before any other openssl. -#include "openssl/ssl.h" #ifdef _WIN32 #include @@ -35,6 +29,8 @@ #include #endif +typedef struct bio_st BIO; + class URLSpec; /** @@ -52,7 +48,7 @@ public: void set_nbio(bool nbio); bool connect(); - INLINE bool should_retry() const; + bool should_retry() const; INLINE BIO &operator *() const; INLINE BIO *operator -> () const; diff --git a/panda/src/downloader/bioStreamBuf.h b/panda/src/downloader/bioStreamBuf.h index c9174b30b1..0378d9db65 100644 --- a/panda/src/downloader/bioStreamBuf.h +++ b/panda/src/downloader/bioStreamBuf.h @@ -19,14 +19,8 @@ // This module is not compiled if OpenSSL is not available. #ifdef HAVE_OPENSSL -#ifndef OPENSSL_NO_KRB5 -#define OPENSSL_NO_KRB5 -#endif - #include "bioPtr.h" #include "pointerTo.h" -#include "openSSLWrapper.h" // must be included before any other openssl. -#include "openssl/ssl.h" /** * The streambuf object that implements IBioStream. diff --git a/panda/src/downloader/bioStreamPtr.h b/panda/src/downloader/bioStreamPtr.h index aa5c8a28bf..3f23040d20 100644 --- a/panda/src/downloader/bioStreamPtr.h +++ b/panda/src/downloader/bioStreamPtr.h @@ -19,14 +19,8 @@ // This module is not compiled if OpenSSL is not available. #ifdef HAVE_OPENSSL -#ifndef OPENSSL_NO_KRB5 -#define OPENSSL_NO_KRB5 -#endif - #include "bioStream.h" #include "referenceCount.h" -#include "openSSLWrapper.h" // must be included before any other openssl. -#include "openssl/ssl.h" /** * A wrapper around an BioStream object to make a reference-counting pointer diff --git a/panda/src/downloader/downloadDb.h b/panda/src/downloader/downloadDb.h index e1605f6295..02adedb3d6 100644 --- a/panda/src/downloader/downloadDb.h +++ b/panda/src/downloader/downloadDb.h @@ -72,8 +72,8 @@ PUBLISHED: }; DownloadDb(); - DownloadDb(Ramfile &server_file, Filename &client_file); - DownloadDb(Filename &server_file, Filename &client_file); + explicit DownloadDb(Ramfile &server_file, Filename &client_file); + explicit DownloadDb(Filename &server_file, Filename &client_file); ~DownloadDb(); void output(ostream &out) const; diff --git a/panda/src/downloader/httpChannel.cxx b/panda/src/downloader/httpChannel.cxx index 96980fdfaa..a7eb5f58d3 100644 --- a/panda/src/downloader/httpChannel.cxx +++ b/panda/src/downloader/httpChannel.cxx @@ -27,6 +27,8 @@ #ifdef HAVE_OPENSSL +#include "openSSLWrapper.h" + #if defined(WIN32_VC) || defined(WIN64_VC) #include #include // for select() @@ -1474,6 +1476,14 @@ run_setup_ssl() { return false; } + string hostname = _request.get_url().get_server(); + result = SSL_set_tlsext_host_name(ssl, hostname.c_str()); + if (result == 0) { + downloader_cat.error() + << _NOTIFY_HTTP_CHANNEL_ID + << "Could not set TLS SNI hostname to '" << hostname << "'\n"; + } + /* * It would be nice to use something like SSL_set_client_cert_cb() here to set * a callback to provide the certificate should it be requested, or even to diff --git a/panda/src/downloader/httpChannel.h b/panda/src/downloader/httpChannel.h index 4c4ddfa312..b9131eba9a 100644 --- a/panda/src/downloader/httpChannel.h +++ b/panda/src/downloader/httpChannel.h @@ -22,10 +22,6 @@ #ifdef HAVE_OPENSSL -#ifndef OPENSSL_NO_KRB5 -#define OPENSSL_NO_KRB5 -#endif - #include "httpClient.h" #include "httpEnum.h" #include "urlSpec.h" @@ -37,10 +33,10 @@ #include "pointerTo.h" #include "config_downloader.h" #include "filename.h" -#include "openSSLWrapper.h" // must be included before any other openssl. -#include "openssl/ssl.h" #include "typedReferenceCount.h" +typedef struct bio_st BIO; + class Ramfile; class HTTPClient; diff --git a/panda/src/downloader/httpClient.cxx b/panda/src/downloader/httpClient.cxx index 63ba1c6258..4770d6916d 100644 --- a/panda/src/downloader/httpClient.cxx +++ b/panda/src/downloader/httpClient.cxx @@ -24,6 +24,8 @@ #ifdef HAVE_OPENSSL +#include "openSSLWrapper.h" + PT(HTTPClient) HTTPClient::_global_ptr; /** @@ -68,6 +70,68 @@ tokenize(const string &str, vector_string &words, const string &delimiters) { words.push_back(string()); } +#ifndef NDEBUG +/** + * This method is attached as a callback for SSL messages only when debug + * output is enabled. + */ +static void +ssl_msg_callback(int write_p, int version, int content_type, + const void *, size_t len, SSL *, void *) { + ostringstream describe; + if (write_p) { + describe << "sent "; + } else { + describe << "received "; + } + switch (version) { + case SSL2_VERSION: + describe << "SSL 2.0 "; + break; + + case SSL3_VERSION: + describe << "SSL 3.0 "; + break; + + case TLS1_VERSION: + describe << "TLS 1.0 "; + break; + + default: + describe << "unknown protocol "; + } + + describe << "message: "; + + if (version != SSL2_VERSION) { + switch (content_type) { + case 20: + describe << "change cipher spec, "; + break; + + case 21: + describe << "alert, "; + break; + + case 22: + describe << "handshake, "; + break; + + case 23: + describe << "application data, "; + break; + + default: + describe << "unknown content type, "; + } + } + + describe << len << " bytes.\n"; + + downloader_cat.debug() << describe.str(); +} +#endif // !defined(NDEBUG) + /** * */ @@ -807,7 +871,7 @@ load_client_certificate() { // Rewind the "file" to the beginning in order to read the public key // (which might appear first in the file). - BIO_reset(mbio); + (void)BIO_reset(mbio); ERR_clear_error(); _client_certificate_pub = @@ -1564,68 +1628,6 @@ split_whitespace(string &a, string &b, const string &c) { b = c.substr(p); } -#ifndef NDEBUG -/** - * This method is attached as a callback for SSL messages only when debug - * output is enabled. - */ -void HTTPClient:: -ssl_msg_callback(int write_p, int version, int content_type, - const void *, size_t len, SSL *, void *) { - ostringstream describe; - if (write_p) { - describe << "sent "; - } else { - describe << "received "; - } - switch (version) { - case SSL2_VERSION: - describe << "SSL 2.0 "; - break; - - case SSL3_VERSION: - describe << "SSL 3.0 "; - break; - - case TLS1_VERSION: - describe << "TLS 1.0 "; - break; - - default: - describe << "unknown protocol "; - } - - describe << "message: "; - - if (version != SSL2_VERSION) { - switch (content_type) { - case 20: - describe << "change cipher spec, "; - break; - - case 21: - describe << "alert, "; - break; - - case 22: - describe << "handshake, "; - break; - - case 23: - describe << "application data, "; - break; - - default: - describe << "unknown content type, "; - } - } - - describe << len << " bytes.\n"; - - downloader_cat.debug() << describe.str(); -} -#endif // !defined(NDEBUG) - /** * */ diff --git a/panda/src/downloader/httpClient.h b/panda/src/downloader/httpClient.h index d901066357..1efab58460 100644 --- a/panda/src/downloader/httpClient.h +++ b/panda/src/downloader/httpClient.h @@ -32,7 +32,11 @@ #include "pmap.h" #include "pset.h" #include "referenceCount.h" -#include "openSSLWrapper.h" + +typedef struct ssl_ctx_st SSL_CTX; +typedef struct x509_st X509; +typedef struct X509_name_st X509_NAME; +typedef struct evp_pkey_st EVP_PKEY; class Filename; class HTTPChannel; @@ -155,12 +159,6 @@ private: static void split_whitespace(string &a, string &b, const string &c); -#ifndef NDEBUG - static void ssl_msg_callback(int write_p, int version, int content_type, - const void *buf, size_t len, SSL *ssl, - void *arg); -#endif - typedef pvector Proxies; typedef pmap ProxiesByScheme; ProxiesByScheme _proxies_by_scheme; diff --git a/panda/src/downloader/httpCookie.h b/panda/src/downloader/httpCookie.h index 5a458f9af2..af460ec3c2 100644 --- a/panda/src/downloader/httpCookie.h +++ b/panda/src/downloader/httpCookie.h @@ -32,8 +32,9 @@ class EXPCL_PANDAEXPRESS HTTPCookie { PUBLISHED: INLINE HTTPCookie(); - INLINE HTTPCookie(const string &format, const URLSpec &url); - INLINE HTTPCookie(const string &name, const string &path, const string &domain); + INLINE explicit HTTPCookie(const string &format, const URLSpec &url); + INLINE explicit HTTPCookie(const string &name, const string &path, + const string &domain); INLINE ~HTTPCookie(); INLINE void set_name(const string &name); diff --git a/panda/src/downloader/patcher.h b/panda/src/downloader/patcher.h index a480f079bb..bf4ffdd351 100644 --- a/panda/src/downloader/patcher.h +++ b/panda/src/downloader/patcher.h @@ -28,7 +28,7 @@ class EXPCL_PANDAEXPRESS Patcher { PUBLISHED: Patcher(); - Patcher(PT(Buffer) buffer); + explicit Patcher(PT(Buffer) buffer); virtual ~Patcher(); int initiate(Filename &patch, Filename &infile); diff --git a/panda/src/downloader/virtualFileMountHTTP.h b/panda/src/downloader/virtualFileMountHTTP.h index 0759d35fdd..996905a913 100644 --- a/panda/src/downloader/virtualFileMountHTTP.h +++ b/panda/src/downloader/virtualFileMountHTTP.h @@ -30,7 +30,7 @@ */ class EXPCL_PANDAEXPRESS VirtualFileMountHTTP : public VirtualFileMount { PUBLISHED: - VirtualFileMountHTTP(const URLSpec &root, HTTPClient *http = HTTPClient::get_global_ptr()); + explicit VirtualFileMountHTTP(const URLSpec &root, HTTPClient *http = HTTPClient::get_global_ptr()); virtual ~VirtualFileMountHTTP(); INLINE HTTPClient *get_http_client() const; diff --git a/panda/src/dxgsg9/README.md b/panda/src/dxgsg9/README.md new file mode 100644 index 0000000000..72da6ba2f5 --- /dev/null +++ b/panda/src/dxgsg9/README.md @@ -0,0 +1,2 @@ +This package handles all communication with the DirectX backend, and +manages state to minimize redundant state changes. diff --git a/panda/src/dxgsg9/config_dxgsg9.cxx b/panda/src/dxgsg9/config_dxgsg9.cxx index 037d8dd919..64831d08c9 100644 --- a/panda/src/dxgsg9/config_dxgsg9.cxx +++ b/panda/src/dxgsg9/config_dxgsg9.cxx @@ -265,8 +265,3 @@ init_libdxgsg9() { PandaSystem *ps = PandaSystem::get_global_ptr(); ps->add_system("DirectX9"); } - -// Necessary to allow use of dxerr from MSVC 2015 -#if _MSC_VER >= 1900 -int (WINAPIV * __vsnprintf)(char *, size_t, const char*, va_list) = _vsnprintf; -#endif diff --git a/panda/src/dxgsg9/dxGeomMunger9.cxx b/panda/src/dxgsg9/dxGeomMunger9.cxx index aaadacb119..44729ae0cd 100644 --- a/panda/src/dxgsg9/dxGeomMunger9.cxx +++ b/panda/src/dxgsg9/dxGeomMunger9.cxx @@ -164,7 +164,7 @@ munge_format_impl(const GeomVertexFormat *orig, // Now go through the remaining arrays and make sure they are tightly // packed. If not, repack them. - for (int i = 0; i < new_format->get_num_arrays(); ++i) { + for (size_t i = 0; i < new_format->get_num_arrays(); ++i) { CPT(GeomVertexArrayFormat) orig_a = new_format->get_array(i); if (orig_a->count_unused_space() != 0) { PT(GeomVertexArrayFormat) new_a = new GeomVertexArrayFormat; @@ -267,7 +267,7 @@ premunge_format_impl(const GeomVertexFormat *orig) { // Now go through the remaining arrays and make sure they are tightly // packed. If not, repack them. - for (int i = 0; i < new_format->get_num_arrays(); ++i) { + for (size_t i = 0; i < new_format->get_num_arrays(); ++i) { CPT(GeomVertexArrayFormat) orig_a = new_format->get_array(i); if (orig_a->count_unused_space() != 0) { PT(GeomVertexArrayFormat) new_a = new GeomVertexArrayFormat; diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index 4b5efc2b8c..b51d67c564 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -863,7 +863,7 @@ prepare_display_region(DisplayRegionPipelineReader *dr) { dr->get_region_pixels_i(l, u, w, h); // Create the viewport - D3DVIEWPORT9 vp = { l, u, w, h, 0.0f, 1.0f }; + D3DVIEWPORT9 vp = { (DWORD)l, (DWORD)u, (DWORD)w, (DWORD)h, 0.0f, 1.0f }; _current_viewport = vp; HRESULT hr = _d3d_device->SetViewport(&_current_viewport); if (FAILED(hr)) { @@ -1141,11 +1141,9 @@ end_frame(Thread *current_thread) { */ bool DXGraphicsStateGuardian9:: begin_draw_primitives(const GeomPipelineReader *geom_reader, - const GeomMunger *munger, const GeomVertexDataPipelineReader *data_reader, bool force) { - if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger, - data_reader, force)) { + if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, data_reader, force)) { return false; } nassertr(_data_reader != (GeomVertexDataPipelineReader *)NULL, false); @@ -1185,7 +1183,7 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader, const TransformTable *table = data_reader->get_transform_table(); if (table != (TransformTable *)NULL) { - for (int i = 0; i < table->get_num_transforms(); i++) { + for (size_t i = 0; i < table->get_num_transforms(); ++i) { LMatrix4 mat; table->get_transform(i)->mult_matrix(mat, _internal_transform->get_mat()); const D3DMATRIX *d3d_mat = (const D3DMATRIX *)mat.get_data(); @@ -3076,7 +3074,7 @@ set_state_and_transform(const RenderState *target, } _target_rs = target; - _target_shader = DCAST(ShaderAttrib, _target_rs->get_attrib_def(ShaderAttrib::get_class_slot())); + determine_target_shader(); int alpha_test_slot = AlphaTestAttrib::get_class_slot(); if (_target_rs->get_attrib(alpha_test_slot) != _state_rs->get_attrib(alpha_test_slot) || @@ -4440,7 +4438,7 @@ set_texture_blend_mode(int i, const TextureStage *stage) { set_texture_stage_state(i, D3DTSS_RESULTARG, D3DTA_CURRENT); } - if (stage->uses_color()) { + if (stage->uses_color() || stage->involves_color_scale()) { // Set up the constant color for this stage. D3DCOLOR constant_color; diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h index 21bb0d7265..fa31ac12fd 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h @@ -106,7 +106,6 @@ public: virtual void end_frame(Thread *current_thread); virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, - const GeomMunger *munger, const GeomVertexDataPipelineReader *data_reader, bool force); virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, diff --git a/panda/src/dxgsg9/dxIndexBufferContext9.cxx b/panda/src/dxgsg9/dxIndexBufferContext9.cxx index 6cf6a78c84..a29dff17b3 100644 --- a/panda/src/dxgsg9/dxIndexBufferContext9.cxx +++ b/panda/src/dxgsg9/dxIndexBufferContext9.cxx @@ -132,8 +132,7 @@ allocate_ibuffer(DXScreenData &scrn, dxgsg9_cat.debug() << "creating index buffer " << _ibuffer << ": " << reader->get_num_vertices() << " indices (" - << reader->get_vertices_reader()->get_array_format()->get_column(0)->get_numeric_type() - << ")\n"; + << reader->get_index_type() << ")\n"; } } } @@ -169,7 +168,7 @@ upload_data(const GeomPrimitivePipelineReader *reader, bool force) { if (data_pointer == NULL) { return false; } - int data_size = reader->get_data_size_bytes(); + size_t data_size = (size_t)reader->get_data_size_bytes(); if (reader->get_index_type() == GeomEnums::NT_uint8) { // We widen 8-bits indices to 16-bits. diff --git a/panda/src/egg/eggAnimData.h b/panda/src/egg/eggAnimData.h index 745f98595b..1548f3f0b7 100644 --- a/panda/src/egg/eggAnimData.h +++ b/panda/src/egg/eggAnimData.h @@ -29,8 +29,7 @@ */ class EXPCL_PANDAEGG EggAnimData : public EggNode { PUBLISHED: - - INLINE EggAnimData(const string &name = ""); + INLINE explicit EggAnimData(const string &name = ""); INLINE EggAnimData(const EggAnimData ©); INLINE EggAnimData &operator = (const EggAnimData ©); diff --git a/panda/src/egg/eggAnimPreload.h b/panda/src/egg/eggAnimPreload.h index 10374caf9f..9dc2352e5f 100644 --- a/panda/src/egg/eggAnimPreload.h +++ b/panda/src/egg/eggAnimPreload.h @@ -23,7 +23,7 @@ */ class EXPCL_PANDAEGG EggAnimPreload : public EggNode { PUBLISHED: - INLINE EggAnimPreload(const string &name = ""); + INLINE explicit EggAnimPreload(const string &name = ""); INLINE EggAnimPreload(const EggAnimPreload ©); INLINE EggAnimPreload &operator = (const EggAnimPreload ©); diff --git a/panda/src/egg/eggBin.h b/panda/src/egg/eggBin.h index 7ea017d8c6..3553f467fb 100644 --- a/panda/src/egg/eggBin.h +++ b/panda/src/egg/eggBin.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDAEGG EggBin : public EggGroup { PUBLISHED: - EggBin(const string &name = ""); + explicit EggBin(const string &name = ""); EggBin(const EggGroup ©); EggBin(const EggBin ©); diff --git a/panda/src/egg/eggComment.h b/panda/src/egg/eggComment.h index d0b7632876..63d5eb82d8 100644 --- a/panda/src/egg/eggComment.h +++ b/panda/src/egg/eggComment.h @@ -23,7 +23,7 @@ */ class EXPCL_PANDAEGG EggComment : public EggNode { PUBLISHED: - INLINE EggComment(const string &node_name, const string &comment); + INLINE explicit EggComment(const string &node_name, const string &comment); INLINE EggComment(const EggComment ©); // You can use the string operators to directly set and manipulate the diff --git a/panda/src/egg/eggCompositePrimitive.I b/panda/src/egg/eggCompositePrimitive.I index a3c805f4a0..9a0d054100 100644 --- a/panda/src/egg/eggCompositePrimitive.I +++ b/panda/src/egg/eggCompositePrimitive.I @@ -38,7 +38,7 @@ operator = (const EggCompositePrimitive ©) { * Returns the number of individual component triangles within the composite. * Each one of these might have a different set of attributes. */ -INLINE int EggCompositePrimitive:: +INLINE size_t EggCompositePrimitive:: get_num_components() const { return _components.size(); } @@ -47,8 +47,8 @@ get_num_components() const { * Returns the attributes for the nth component triangle. */ INLINE const EggAttributes *EggCompositePrimitive:: -get_component(int i) const { - nassertr(i >= 0 && i < (int)_components.size(), NULL); +get_component(size_t i) const { + nassertr(i < _components.size(), nullptr); return _components[i]; } @@ -56,8 +56,8 @@ get_component(int i) const { * Returns the attributes for the nth component triangle. */ INLINE EggAttributes *EggCompositePrimitive:: -get_component(int i) { - nassertr(i >= 0 && i < (int)_components.size(), NULL); +get_component(size_t i) { + nassertr(i < _components.size(), nullptr); return _components[i]; } @@ -65,8 +65,8 @@ get_component(int i) { * Changes the attributes for the nth component triangle. */ INLINE void EggCompositePrimitive:: -set_component(int i, const EggAttributes *attrib) { - nassertv(i >= 0 && i < (int)_components.size()); +set_component(size_t i, const EggAttributes *attrib) { + nassertv(i < _components.size()); _components[i] = new EggAttributes(*attrib); } diff --git a/panda/src/egg/eggCompositePrimitive.h b/panda/src/egg/eggCompositePrimitive.h index b816d816c7..9b5e0fa58d 100644 --- a/panda/src/egg/eggCompositePrimitive.h +++ b/panda/src/egg/eggCompositePrimitive.h @@ -25,18 +25,18 @@ */ class EXPCL_PANDAEGG EggCompositePrimitive : public EggPrimitive { PUBLISHED: - INLINE EggCompositePrimitive(const string &name = ""); + INLINE explicit EggCompositePrimitive(const string &name = ""); INLINE EggCompositePrimitive(const EggCompositePrimitive ©); INLINE EggCompositePrimitive &operator = (const EggCompositePrimitive ©); virtual ~EggCompositePrimitive(); virtual Shading get_shading() const; - INLINE int get_num_components() const; - INLINE const EggAttributes *get_component(int i) const; - INLINE EggAttributes *get_component(int i); + INLINE size_t get_num_components() const; + INLINE const EggAttributes *get_component(size_t i) const; + INLINE EggAttributes *get_component(size_t i); MAKE_SEQ(get_components, get_num_components, get_component); - INLINE void set_component(int i, const EggAttributes *attrib); + INLINE void set_component(size_t i, const EggAttributes *attrib); MAKE_SEQ_PROPERTY(components, get_num_components, get_component, set_component); diff --git a/panda/src/egg/eggCurve.h b/panda/src/egg/eggCurve.h index fa43f9700a..6ea17bc77a 100644 --- a/panda/src/egg/eggCurve.h +++ b/panda/src/egg/eggCurve.h @@ -23,7 +23,7 @@ */ class EXPCL_PANDAEGG EggCurve : public EggPrimitive { PUBLISHED: - INLINE EggCurve(const string &name = ""); + INLINE explicit EggCurve(const string &name = ""); INLINE EggCurve(const EggCurve ©); INLINE EggCurve &operator = (const EggCurve ©); diff --git a/panda/src/egg/eggExternalReference.h b/panda/src/egg/eggExternalReference.h index 48ea165b9b..799c794bd1 100644 --- a/panda/src/egg/eggExternalReference.h +++ b/panda/src/egg/eggExternalReference.h @@ -24,7 +24,7 @@ */ class EXPCL_PANDAEGG EggExternalReference : public EggFilenameNode { PUBLISHED: - EggExternalReference(const string &node_name, const string &filename); + explicit EggExternalReference(const string &node_name, const string &filename); EggExternalReference(const EggExternalReference ©); EggExternalReference &operator = (const EggExternalReference ©); diff --git a/panda/src/egg/eggFilenameNode.h b/panda/src/egg/eggFilenameNode.h index d4ac734ce7..46734f6a24 100644 --- a/panda/src/egg/eggFilenameNode.h +++ b/panda/src/egg/eggFilenameNode.h @@ -27,7 +27,7 @@ class EXPCL_PANDAEGG EggFilenameNode : public EggNode { PUBLISHED: INLINE EggFilenameNode(); - INLINE EggFilenameNode(const string &node_name, const Filename &filename); + INLINE explicit EggFilenameNode(const string &node_name, const Filename &filename); INLINE EggFilenameNode(const EggFilenameNode ©); INLINE EggFilenameNode &operator = (const EggFilenameNode ©); diff --git a/panda/src/egg/eggGroup.h b/panda/src/egg/eggGroup.h index 69d72807a9..1266e42a49 100644 --- a/panda/src/egg/eggGroup.h +++ b/panda/src/egg/eggGroup.h @@ -132,7 +132,7 @@ PUBLISHED: BO_one_minus_alpha_scale, }; - EggGroup(const string &name = ""); + explicit EggGroup(const string &name = ""); EggGroup(const EggGroup ©); EggGroup &operator = (const EggGroup ©); ~EggGroup(); diff --git a/panda/src/egg/eggGroupNode.h b/panda/src/egg/eggGroupNode.h index 62dda7dd12..85956cd32b 100644 --- a/panda/src/egg/eggGroupNode.h +++ b/panda/src/egg/eggGroupNode.h @@ -58,7 +58,7 @@ private: // Here begins the actual public interface to EggGroupNode. PUBLISHED: - EggGroupNode(const string &name = "") : EggNode(name) { } + explicit EggGroupNode(const string &name = "") : EggNode(name) { } EggGroupNode(const EggGroupNode ©); EggGroupNode &operator = (const EggGroupNode ©); virtual ~EggGroupNode(); diff --git a/panda/src/egg/eggGroupUniquifier.h b/panda/src/egg/eggGroupUniquifier.h index 97c3a12226..af9e3b8f77 100644 --- a/panda/src/egg/eggGroupUniquifier.h +++ b/panda/src/egg/eggGroupUniquifier.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDAEGG EggGroupUniquifier : public EggNameUniquifier { PUBLISHED: - EggGroupUniquifier(bool filter_names = true); + explicit EggGroupUniquifier(bool filter_names = true); virtual string get_category(EggNode *node); virtual string filter_name(EggNode *node); diff --git a/panda/src/egg/eggLine.h b/panda/src/egg/eggLine.h index 059dfa7648..4e5eef098c 100644 --- a/panda/src/egg/eggLine.h +++ b/panda/src/egg/eggLine.h @@ -24,14 +24,14 @@ */ class EXPCL_PANDAEGG EggLine : public EggCompositePrimitive { PUBLISHED: - INLINE EggLine(const string &name = ""); + INLINE explicit EggLine(const string &name = ""); INLINE EggLine(const EggLine ©); INLINE EggLine &operator = (const EggLine ©); virtual ~EggLine(); - virtual EggLine *make_copy() const OVERRIDE; + virtual EggLine *make_copy() const override; - virtual void write(ostream &out, int indent_level) const; + virtual void write(ostream &out, int indent_level) const override; INLINE bool has_thick() const; INLINE double get_thick() const; @@ -39,7 +39,7 @@ PUBLISHED: INLINE void clear_thick(); protected: - virtual int get_num_lead_vertices() const; + virtual int get_num_lead_vertices() const override; private: double _thick; @@ -54,10 +54,13 @@ public: register_type(_type_handle, "EggLine", EggCompositePrimitive::get_class_type()); } - virtual TypeHandle get_type() const { + virtual TypeHandle get_type() const override { + return get_class_type(); + } + virtual TypeHandle force_init_type() override { + init_type(); return get_class_type(); } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} private: static TypeHandle _type_handle; diff --git a/panda/src/egg/eggMaterial.h b/panda/src/egg/eggMaterial.h index a28d779699..c1008ae95c 100644 --- a/panda/src/egg/eggMaterial.h +++ b/panda/src/egg/eggMaterial.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDAEGG EggMaterial : public EggNode { PUBLISHED: - EggMaterial(const string &mref_name); + explicit EggMaterial(const string &mref_name); EggMaterial(const EggMaterial ©); virtual void write(ostream &out, int indent_level) const; diff --git a/panda/src/egg/eggNamedObject.h b/panda/src/egg/eggNamedObject.h index 9f002961cb..140c989237 100644 --- a/panda/src/egg/eggNamedObject.h +++ b/panda/src/egg/eggNamedObject.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDAEGG EggNamedObject : public EggObject, public Namable { PUBLISHED: - INLINE EggNamedObject(const string &name = ""); + INLINE explicit EggNamedObject(const string &name = ""); INLINE EggNamedObject(const EggNamedObject ©); INLINE EggNamedObject &operator = (const EggNamedObject ©); diff --git a/panda/src/egg/eggNode.h b/panda/src/egg/eggNode.h index 0eab76d40d..f4a5df934f 100644 --- a/panda/src/egg/eggNode.h +++ b/panda/src/egg/eggNode.h @@ -34,7 +34,7 @@ class EggTextureCollection; */ class EXPCL_PANDAEGG EggNode : public EggNamedObject { PUBLISHED: - INLINE EggNode(const string &name = ""); + INLINE explicit EggNode(const string &name = ""); INLINE EggNode(const EggNode ©); INLINE EggNode &operator = (const EggNode ©); diff --git a/panda/src/egg/eggNurbsCurve.h b/panda/src/egg/eggNurbsCurve.h index 9797a12ea8..b6b5bc4b55 100644 --- a/panda/src/egg/eggNurbsCurve.h +++ b/panda/src/egg/eggNurbsCurve.h @@ -25,11 +25,11 @@ */ class EXPCL_PANDAEGG EggNurbsCurve : public EggCurve { PUBLISHED: - INLINE EggNurbsCurve(const string &name = ""); + INLINE explicit EggNurbsCurve(const string &name = ""); INLINE EggNurbsCurve(const EggNurbsCurve ©); INLINE EggNurbsCurve &operator = (const EggNurbsCurve ©); - virtual EggNurbsCurve *make_copy() const OVERRIDE; + virtual EggNurbsCurve *make_copy() const override; void setup(int order, int num_knots); @@ -50,7 +50,7 @@ PUBLISHED: INLINE double get_knot(int k) const; MAKE_SEQ(get_knots, get_num_knots, get_knot); - virtual void write(ostream &out, int indent_level) const; + virtual void write(ostream &out, int indent_level) const override; MAKE_PROPERTY(order, get_order, set_order); MAKE_PROPERTY(degree, get_degree); @@ -72,10 +72,13 @@ public: register_type(_type_handle, "EggNurbsCurve", EggCurve::get_class_type()); } - virtual TypeHandle get_type() const { + virtual TypeHandle get_type() const override { + return get_class_type(); + } + virtual TypeHandle force_init_type() override { + init_type(); return get_class_type(); } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} private: static TypeHandle _type_handle; diff --git a/panda/src/egg/eggNurbsSurface.h b/panda/src/egg/eggNurbsSurface.h index 8e09d43fbd..ffadc3cab7 100644 --- a/panda/src/egg/eggNurbsSurface.h +++ b/panda/src/egg/eggNurbsSurface.h @@ -32,11 +32,11 @@ PUBLISHED: typedef Loops Trim; typedef plist Trims; - INLINE EggNurbsSurface(const string &name = ""); + INLINE explicit EggNurbsSurface(const string &name = ""); INLINE EggNurbsSurface(const EggNurbsSurface ©); INLINE EggNurbsSurface &operator = (const EggNurbsSurface ©); - virtual EggNurbsSurface *make_copy() const OVERRIDE; + virtual EggNurbsSurface *make_copy() const override; void setup(int u_order, int v_order, int num_u_knots, int num_v_knots); @@ -75,14 +75,14 @@ PUBLISHED: MAKE_SEQ(get_v_knots, get_num_v_knots, get_v_knot); INLINE EggVertex *get_cv(int ui, int vi) const; - virtual void write(ostream &out, int indent_level) const; + virtual void write(ostream &out, int indent_level) const override; public: Curves _curves_on_surface; Trims _trims; protected: - virtual void r_apply_texmats(EggTextureCollection &textures); + virtual void r_apply_texmats(EggTextureCollection &textures) override; private: typedef vector_double Knots; @@ -101,10 +101,13 @@ public: register_type(_type_handle, "EggNurbsSurface", EggSurface::get_class_type()); } - virtual TypeHandle get_type() const { + virtual TypeHandle get_type() const override { + return get_class_type(); + } + virtual TypeHandle force_init_type() override { + init_type(); return get_class_type(); } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} private: static TypeHandle _type_handle; diff --git a/panda/src/egg/eggPatch.h b/panda/src/egg/eggPatch.h index 63cfc61353..9e93d0ec80 100644 --- a/panda/src/egg/eggPatch.h +++ b/panda/src/egg/eggPatch.h @@ -24,13 +24,13 @@ */ class EXPCL_PANDAEGG EggPatch : public EggPrimitive { PUBLISHED: - INLINE EggPatch(const string &name = ""); + INLINE explicit EggPatch(const string &name = ""); INLINE EggPatch(const EggPatch ©); INLINE EggPatch &operator = (const EggPatch ©); - virtual EggPatch *make_copy() const OVERRIDE; + virtual EggPatch *make_copy() const override; - virtual void write(ostream &out, int indent_level) const; + virtual void write(ostream &out, int indent_level) const override; public: static TypeHandle get_class_type() { @@ -41,10 +41,13 @@ public: register_type(_type_handle, "EggPatch", EggPrimitive::get_class_type()); } - virtual TypeHandle get_type() const { + virtual TypeHandle get_type() const override { + return get_class_type(); + } + virtual TypeHandle force_init_type() override { + init_type(); return get_class_type(); } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} private: static TypeHandle _type_handle; diff --git a/panda/src/egg/eggPoint.h b/panda/src/egg/eggPoint.h index ae7d7e1d34..96e8b63fe6 100644 --- a/panda/src/egg/eggPoint.h +++ b/panda/src/egg/eggPoint.h @@ -24,11 +24,11 @@ */ class EXPCL_PANDAEGG EggPoint : public EggPrimitive { PUBLISHED: - INLINE EggPoint(const string &name = ""); + INLINE explicit EggPoint(const string &name = ""); INLINE EggPoint(const EggPoint ©); INLINE EggPoint &operator = (const EggPoint ©); - virtual EggPoint *make_copy() const OVERRIDE; + virtual EggPoint *make_copy() const override; INLINE bool has_thick() const; INLINE double get_thick() const; @@ -40,9 +40,9 @@ PUBLISHED: INLINE void set_perspective(bool perspective); INLINE void clear_perspective(); - virtual bool cleanup(); + virtual bool cleanup() override; - virtual void write(ostream &out, int indent_level) const; + virtual void write(ostream &out, int indent_level) const override; private: enum Flags { @@ -64,10 +64,13 @@ public: register_type(_type_handle, "EggPoint", EggPrimitive::get_class_type()); } - virtual TypeHandle get_type() const { + virtual TypeHandle get_type() const override { + return get_class_type(); + } + virtual TypeHandle force_init_type() override { + init_type(); return get_class_type(); } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} private: static TypeHandle _type_handle; diff --git a/panda/src/egg/eggPolygon.h b/panda/src/egg/eggPolygon.h index a549af3bf7..e5be0e25f0 100644 --- a/panda/src/egg/eggPolygon.h +++ b/panda/src/egg/eggPolygon.h @@ -23,13 +23,13 @@ */ class EXPCL_PANDAEGG EggPolygon : public EggPrimitive { PUBLISHED: - INLINE EggPolygon(const string &name = ""); + INLINE explicit EggPolygon(const string &name = ""); INLINE EggPolygon(const EggPolygon ©); INLINE EggPolygon &operator = (const EggPolygon ©); - virtual EggPolygon *make_copy() const OVERRIDE; + virtual EggPolygon *make_copy() const override; - virtual bool cleanup(); + virtual bool cleanup() override; bool calculate_normal(LNormald &result, CoordinateSystem cs = CS_default) const; bool is_planar() const; @@ -39,7 +39,7 @@ PUBLISHED: INLINE bool triangulate_into(EggGroupNode *container, bool convex_also) const; PT(EggPolygon) triangulate_in_place(bool convex_also); - virtual void write(ostream &out, int indent_level) const; + virtual void write(ostream &out, int indent_level) const override; private: bool decomp_concave(EggGroupNode *container, int asum, int x, int y) const; @@ -55,10 +55,13 @@ public: register_type(_type_handle, "EggPolygon", EggPrimitive::get_class_type()); } - virtual TypeHandle get_type() const { + virtual TypeHandle get_type() const override { + return get_class_type(); + } + virtual TypeHandle force_init_type() override { + init_type(); return get_class_type(); } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} private: static TypeHandle _type_handle; diff --git a/panda/src/egg/eggPrimitive.I b/panda/src/egg/eggPrimitive.I index d849d23882..8ae8a1eecf 100644 --- a/panda/src/egg/eggPrimitive.I +++ b/panda/src/egg/eggPrimitive.I @@ -372,6 +372,17 @@ set_vertex(size_t index, EggVertex *vertex) { replace(begin() + index, vertex); } +/** + * Inserts a vertex at the given position. + */ +INLINE void EggPrimitive:: +insert_vertex(size_t index, EggVertex *vertex) { + if (index > _vertices.size()) { + index = _vertices.size(); + } + _vertices.insert(_vertices.begin() + index, vertex); +} + /** * Returns a particular index based on its index number. */ @@ -381,7 +392,6 @@ get_vertex(size_t index) const { return *(begin() + index); } - /** * Returns the vertex pool associated with the vertices of the primitive, or * NULL if the primitive has no vertices. diff --git a/panda/src/egg/eggPrimitive.h b/panda/src/egg/eggPrimitive.h index 905e049596..9ec9d9cf79 100644 --- a/panda/src/egg/eggPrimitive.h +++ b/panda/src/egg/eggPrimitive.h @@ -67,7 +67,7 @@ PUBLISHED: S_per_vertex }; - INLINE EggPrimitive(const string &name = ""); + INLINE explicit EggPrimitive(const string &name = ""); INLINE EggPrimitive(const EggPrimitive ©); INLINE EggPrimitive &operator = (const EggPrimitive ©); INLINE ~EggPrimitive(); @@ -181,13 +181,14 @@ PUBLISHED: // These are shorthands if you don't want to use the iterators. INLINE size_t get_num_vertices() const; - INLINE void set_vertex(size_t index, EggVertex *vertex); INLINE EggVertex *get_vertex(size_t index) const; + INLINE void set_vertex(size_t index, EggVertex *vertex); + INLINE void insert_vertex(size_t index, EggVertex *vertex); MAKE_SEQ(get_vertices, get_num_vertices, get_vertex); INLINE EggVertexPool *get_pool() const; - MAKE_SEQ_PROPERTY(vertices, get_num_vertices, get_vertex, set_vertex, remove_vertex); + MAKE_SEQ_PROPERTY(vertices, get_num_vertices, get_vertex, set_vertex, remove_vertex, insert_vertex); MAKE_PROPERTY(pool, get_pool); virtual void write(ostream &out, int indent_level) const=0; diff --git a/panda/src/egg/eggSAnimData.h b/panda/src/egg/eggSAnimData.h index b09000f55d..2862b94622 100644 --- a/panda/src/egg/eggSAnimData.h +++ b/panda/src/egg/eggSAnimData.h @@ -24,7 +24,7 @@ */ class EXPCL_PANDAEGG EggSAnimData : public EggAnimData { PUBLISHED: - INLINE EggSAnimData(const string &name = ""); + INLINE explicit EggSAnimData(const string &name = ""); INLINE EggSAnimData(const EggSAnimData ©); INLINE EggSAnimData &operator = (const EggSAnimData ©); diff --git a/panda/src/egg/eggSurface.h b/panda/src/egg/eggSurface.h index eea88f18a8..3085cb1c48 100644 --- a/panda/src/egg/eggSurface.h +++ b/panda/src/egg/eggSurface.h @@ -23,7 +23,7 @@ */ class EXPCL_PANDAEGG EggSurface : public EggPrimitive { PUBLISHED: - INLINE EggSurface(const string &name = ""); + INLINE explicit EggSurface(const string &name = ""); INLINE EggSurface(const EggSurface ©); INLINE EggSurface &operator = (const EggSurface ©); diff --git a/panda/src/egg/eggSwitchCondition.h b/panda/src/egg/eggSwitchCondition.h index aec6a5f545..a1d16db2ca 100644 --- a/panda/src/egg/eggSwitchCondition.h +++ b/panda/src/egg/eggSwitchCondition.h @@ -60,8 +60,8 @@ private: */ class EXPCL_PANDAEGG EggSwitchConditionDistance : public EggSwitchCondition { PUBLISHED: - EggSwitchConditionDistance(double switch_in, double switch_out, - const LPoint3d ¢er, double fade = 0.0); + explicit EggSwitchConditionDistance(double switch_in, double switch_out, + const LPoint3d ¢er, double fade = 0.0); virtual EggSwitchCondition *make_copy() const; virtual void write(ostream &out, int indent_level) const; diff --git a/panda/src/egg/eggTable.h b/panda/src/egg/eggTable.h index 848973d261..e2a55d7a3e 100644 --- a/panda/src/egg/eggTable.h +++ b/panda/src/egg/eggTable.h @@ -32,7 +32,7 @@ PUBLISHED: TT_bundle, }; - INLINE EggTable(const string &name = ""); + INLINE explicit EggTable(const string &name = ""); INLINE EggTable(const EggTable ©); INLINE EggTable &operator = (const EggTable ©); diff --git a/panda/src/egg/eggTexture.h b/panda/src/egg/eggTexture.h index 66636b77bf..174b7c99db 100644 --- a/panda/src/egg/eggTexture.h +++ b/panda/src/egg/eggTexture.h @@ -29,7 +29,7 @@ */ class EXPCL_PANDAEGG EggTexture : public EggFilenameNode, public EggRenderMode, public EggTransform { PUBLISHED: - EggTexture(const string &tref_name, const Filename &filename); + explicit EggTexture(const string &tref_name, const Filename &filename); EggTexture(const EggTexture ©); EggTexture &operator = (const EggTexture ©); virtual ~EggTexture(); diff --git a/panda/src/egg/eggTriangleFan.h b/panda/src/egg/eggTriangleFan.h index 0a5e3f1e35..6435650284 100644 --- a/panda/src/egg/eggTriangleFan.h +++ b/panda/src/egg/eggTriangleFan.h @@ -24,19 +24,19 @@ */ class EXPCL_PANDAEGG EggTriangleFan : public EggCompositePrimitive { PUBLISHED: - INLINE EggTriangleFan(const string &name = ""); + INLINE explicit EggTriangleFan(const string &name = ""); INLINE EggTriangleFan(const EggTriangleFan ©); INLINE EggTriangleFan &operator = (const EggTriangleFan ©); virtual ~EggTriangleFan(); - virtual EggTriangleFan *make_copy() const OVERRIDE; + virtual EggTriangleFan *make_copy() const override; - virtual void write(ostream &out, int indent_level) const; - virtual void apply_first_attribute(); + virtual void write(ostream &out, int indent_level) const override; + virtual void apply_first_attribute() override; protected: - virtual int get_num_lead_vertices() const; - virtual bool do_triangulate(EggGroupNode *container) const; + virtual int get_num_lead_vertices() const override; + virtual bool do_triangulate(EggGroupNode *container) const override; public: static TypeHandle get_class_type() { @@ -47,10 +47,13 @@ public: register_type(_type_handle, "EggTriangleFan", EggCompositePrimitive::get_class_type()); } - virtual TypeHandle get_type() const { + virtual TypeHandle get_type() const override { + return get_class_type(); + } + virtual TypeHandle force_init_type() override { + init_type(); return get_class_type(); } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} private: static TypeHandle _type_handle; diff --git a/panda/src/egg/eggTriangleStrip.h b/panda/src/egg/eggTriangleStrip.h index 202172d1cd..fa371d4185 100644 --- a/panda/src/egg/eggTriangleStrip.h +++ b/panda/src/egg/eggTriangleStrip.h @@ -24,18 +24,18 @@ */ class EXPCL_PANDAEGG EggTriangleStrip : public EggCompositePrimitive { PUBLISHED: - INLINE EggTriangleStrip(const string &name = ""); + INLINE explicit EggTriangleStrip(const string &name = ""); INLINE EggTriangleStrip(const EggTriangleStrip ©); INLINE EggTriangleStrip &operator = (const EggTriangleStrip ©); virtual ~EggTriangleStrip(); - virtual EggTriangleStrip *make_copy() const OVERRIDE; + virtual EggTriangleStrip *make_copy() const override; - virtual void write(ostream &out, int indent_level) const; + virtual void write(ostream &out, int indent_level) const override; protected: - virtual int get_num_lead_vertices() const; - virtual bool do_triangulate(EggGroupNode *container) const; + virtual int get_num_lead_vertices() const override; + virtual bool do_triangulate(EggGroupNode *container) const override; public: static TypeHandle get_class_type() { @@ -46,10 +46,13 @@ public: register_type(_type_handle, "EggTriangleStrip", EggCompositePrimitive::get_class_type()); } - virtual TypeHandle get_type() const { + virtual TypeHandle get_type() const override { + return get_class_type(); + } + virtual TypeHandle force_init_type() override { + init_type(); return get_class_type(); } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} private: static TypeHandle _type_handle; diff --git a/panda/src/egg/eggVertexAux.h b/panda/src/egg/eggVertexAux.h index 1d87fe8ad5..b0338cdec3 100644 --- a/panda/src/egg/eggVertexAux.h +++ b/panda/src/egg/eggVertexAux.h @@ -29,7 +29,7 @@ */ class EXPCL_PANDAEGG EggVertexAux : public EggNamedObject { PUBLISHED: - EggVertexAux(const string &name, const LVecBase4d &aux); + explicit EggVertexAux(const string &name, const LVecBase4d &aux); EggVertexAux(const EggVertexAux ©); EggVertexAux &operator = (const EggVertexAux ©); virtual ~EggVertexAux(); diff --git a/panda/src/egg/eggVertexPool.h b/panda/src/egg/eggVertexPool.h index 71e3ac4798..2b44ad38d2 100644 --- a/panda/src/egg/eggVertexPool.h +++ b/panda/src/egg/eggVertexPool.h @@ -65,7 +65,7 @@ public: // Here begins the actual public interface to EggVertexPool. PUBLISHED: - EggVertexPool(const string &name); + explicit EggVertexPool(const string &name); EggVertexPool(const EggVertexPool ©); ~EggVertexPool(); diff --git a/panda/src/egg/eggVertexUV.h b/panda/src/egg/eggVertexUV.h index 8a5bbc03a7..21b6e5b7c2 100644 --- a/panda/src/egg/eggVertexUV.h +++ b/panda/src/egg/eggVertexUV.h @@ -28,8 +28,8 @@ */ class EXPCL_PANDAEGG EggVertexUV : public EggNamedObject { PUBLISHED: - EggVertexUV(const string &name, const LTexCoordd &uv); - EggVertexUV(const string &name, const LTexCoord3d &uvw); + explicit EggVertexUV(const string &name, const LTexCoordd &uv); + explicit EggVertexUV(const string &name, const LTexCoord3d &uvw); EggVertexUV(const EggVertexUV ©); EggVertexUV &operator = (const EggVertexUV ©); virtual ~EggVertexUV(); diff --git a/panda/src/egg/eggXfmAnimData.h b/panda/src/egg/eggXfmAnimData.h index ab4d485481..dac7762931 100644 --- a/panda/src/egg/eggXfmAnimData.h +++ b/panda/src/egg/eggXfmAnimData.h @@ -28,8 +28,8 @@ */ class EXPCL_PANDAEGG EggXfmAnimData : public EggAnimData { PUBLISHED: - INLINE EggXfmAnimData(const string &name = "", - CoordinateSystem cs = CS_default); + INLINE explicit EggXfmAnimData(const string &name = "", + CoordinateSystem cs = CS_default); EggXfmAnimData(const EggXfmSAnim &convert_from); INLINE EggXfmAnimData(const EggXfmAnimData ©); diff --git a/panda/src/egg/eggXfmSAnim.h b/panda/src/egg/eggXfmSAnim.h index 7e46af5f5c..eae0349559 100644 --- a/panda/src/egg/eggXfmSAnim.h +++ b/panda/src/egg/eggXfmSAnim.h @@ -27,8 +27,8 @@ class EggXfmAnimData; */ class EXPCL_PANDAEGG EggXfmSAnim : public EggGroupNode { PUBLISHED: - INLINE EggXfmSAnim(const string &name = "", - CoordinateSystem cs = CS_default); + INLINE explicit EggXfmSAnim(const string &name = "", + CoordinateSystem cs = CS_default); EggXfmSAnim(const EggXfmAnimData &convert_from); INLINE EggXfmSAnim(const EggXfmSAnim ©); diff --git a/panda/src/egg2pg/animBundleMaker.h b/panda/src/egg2pg/animBundleMaker.h index 48d97635ba..fa95030718 100644 --- a/panda/src/egg2pg/animBundleMaker.h +++ b/panda/src/egg2pg/animBundleMaker.h @@ -34,7 +34,7 @@ class AnimChannelMatrixXfmTable; */ class EXPCL_PANDAEGG AnimBundleMaker { public: - AnimBundleMaker(EggTable *root); + explicit AnimBundleMaker(EggTable *root); AnimBundleNode *make_node(); diff --git a/panda/src/egg2pg/eggSaver.cxx b/panda/src/egg2pg/eggSaver.cxx index 3976aa0714..5eeb98f42c 100644 --- a/panda/src/egg2pg/eggSaver.cxx +++ b/panda/src/egg2pg/eggSaver.cxx @@ -482,7 +482,7 @@ convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path, } else if (child->is_of_type(CollisionSphere::get_class_type())) { CPT(CollisionSphere) sphere = DCAST(CollisionSphere, child); LPoint3 center = sphere->get_center(); - LVector3 offset(sphere->get_radius(), 0, 0); + PN_stdfloat radius = sphere->get_radius(); EggGroup *egg_sphere; if (num_solids == 1) { @@ -499,15 +499,19 @@ convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path, } egg_sphere->set_collide_flags(flags); - EggVertex ev1, ev2; - ev1.set_pos(LCAST(double, (center + offset) * net_mat)); - ev2.set_pos(LCAST(double, (center - offset) * net_mat)); + EggVertex ev1, ev2, ev3, ev4; + ev1.set_pos(LCAST(double, (center + LVector3(radius, 0, 0)) * net_mat)); + ev2.set_pos(LCAST(double, (center + LVector3(0, radius, 0)) * net_mat)); + ev3.set_pos(LCAST(double, (center + LVector3(-radius, 0, 0)) * net_mat)); + ev4.set_pos(LCAST(double, (center + LVector3(0, -radius, 0)) * net_mat)); EggPolygon *egg_poly = new EggPolygon; egg_sphere->add_child(egg_poly); egg_poly->add_vertex(cvpool->create_unique_vertex(ev1)); egg_poly->add_vertex(cvpool->create_unique_vertex(ev2)); + egg_poly->add_vertex(cvpool->create_unique_vertex(ev3)); + egg_poly->add_vertex(cvpool->create_unique_vertex(ev4)); } else if (child->is_of_type(CollisionPlane::get_class_type())) { LPlane plane = DCAST(CollisionPlane, child)->get_plane(); @@ -936,6 +940,9 @@ apply_node_properties(EggGroup *egg_group, PandaNode *node, bool allow_backstage ModelNode *model_node = DCAST(ModelNode, node); switch (model_node->get_preserve_transform()) { case ModelNode::PT_none: + egg_group->set_model_flag(true); + break; + case ModelNode::PT_drop_node: break; diff --git a/panda/src/event/asyncFuture.I b/panda/src/event/asyncFuture.I new file mode 100644 index 0000000000..07191a34c9 --- /dev/null +++ b/panda/src/event/asyncFuture.I @@ -0,0 +1,109 @@ +/** + * 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 asyncFuture.I + * @author rdb + * @date 2017-11-28 + */ + +/** + * Initializes the future in the pending state. + */ +INLINE AsyncFuture:: +AsyncFuture() : + _manager(nullptr), + _cvar(nullptr), + _future_state(FS_pending), + _result(nullptr) { +} + +/** + * Returns true if the future is done or has been cancelled. It is always + * safe to call this. + */ +INLINE bool AsyncFuture:: +done() const { + return (FutureState)AtomicAdjust::get(_future_state) != FS_pending; +} + +/** + * Returns true if the future was cancelled. It is always safe to call this. + */ +INLINE bool AsyncFuture:: +cancelled() const { + return (FutureState)AtomicAdjust::get(_future_state) == FS_cancelled; +} + +/** + * Sets the event name that will be triggered when the future finishes. Will + * not be triggered if the future is cancelled, but it will be triggered for + * a coroutine task that exits with an exception. + */ +INLINE void AsyncFuture:: +set_done_event(const string &done_event) { + _done_event = done_event; +} + +/** + * Returns the event name that will be triggered when the future finishes. + * See set_done_event(). + */ +INLINE const string &AsyncFuture:: +get_done_event() const { + return _done_event; +} + +/** + * Returns this future's result. Can only be called if done() returns true. + */ +INLINE TypedObject *AsyncFuture:: +get_result() const { + // This is thread safe, since _result may no longer be modified after the + // state is changed to "done". + nassertr_always(done(), nullptr); + return _result; +} + +/** + * Returns this future's result as a pair of TypedObject, ReferenceCount + * pointers. Can only be called if done() returns true. + */ +INLINE void AsyncFuture:: +get_result(TypedObject *&ptr, ReferenceCount *&ref_ptr) const { + // This is thread safe, since _result may no longer be modified after the + // state is changed to "done". + nassertd(done()) { + ptr = nullptr; + ref_ptr = nullptr; + } + ptr = _result; + ref_ptr = _result_ref.p(); +} + +/** + * Sets this future's result. Can only be called if done() returns false. + */ +INLINE void AsyncFuture:: +set_result(nullptr_t) { + set_result(nullptr, nullptr); +} + +INLINE void AsyncFuture:: +set_result(TypedObject *result) { + set_result(result, nullptr); +} + +INLINE void AsyncFuture:: +set_result(TypedReferenceCount *result) { + set_result(result, result); +} + +INLINE void AsyncFuture:: +set_result(TypedWritableReferenceCount *result) { + set_result(result, result); +} diff --git a/panda/src/event/asyncFuture.cxx b/panda/src/event/asyncFuture.cxx new file mode 100644 index 0000000000..bb299239d7 --- /dev/null +++ b/panda/src/event/asyncFuture.cxx @@ -0,0 +1,195 @@ +/** + * 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 asyncFuture.cxx + * @author rdb + * @date 2017-11-28 + */ + +#include "asyncFuture.h" +#include "asyncTask.h" +#include "asyncTaskManager.h" +#include "conditionVarFull.h" +#include "config_event.h" +#include "pStatTimer.h" +#include "throw_event.h" + +TypeHandle AsyncFuture::_type_handle; + +/** + * Destroys the future. Assumes notify_done() has already been called. + */ +AsyncFuture:: +~AsyncFuture() { + delete _cvar; + nassertv(_waiting_tasks.empty()); +} + +/** + * Cancels the future. Returns true if it was cancelled, or false if the + * future was already done. + * In the case of a task, this is equivalent to remove(). + */ +bool AsyncFuture:: +cancel() { + if (!done()) { + if (_manager == nullptr) { + _manager = AsyncTaskManager::get_global_ptr(); + } + MutexHolder holder(_manager->_lock); + notify_done(false); + return true; + } else { + return false; + } +} + +/** + * + */ +void AsyncFuture:: +output(ostream &out) const { + out << get_type(); +} + +/** + * Waits until the future is done. + */ +void AsyncFuture:: +wait() { + if (done()) { + return; + } + + // If we don't have a manager, use the global one. + if (_manager == nullptr) { + _manager = AsyncTaskManager::get_global_ptr(); + } + + MutexHolder holder(_manager->_lock); + if (!done()) { + if (_cvar == nullptr) { + _cvar = new ConditionVarFull(_manager->_lock); + } + if (task_cat.is_debug()) { + task_cat.debug() + << "Waiting for future " << *this << "\n"; + } + PStatTimer timer(AsyncTaskChain::_wait_pcollector); + while (!done()) { + _cvar->wait(); + } + } +} + +/** + * Waits until the future is done, or until the timeout is reached. + */ +void AsyncFuture:: +wait(double timeout) { + if (done()) { + return; + } + + // If we don't have a manager, use the global one. + if (_manager == nullptr) { + _manager = AsyncTaskManager::get_global_ptr(); + } + + MutexHolder holder(_manager->_lock); + if (!done()) { + if (_cvar == nullptr) { + _cvar = new ConditionVarFull(_manager->_lock); + } + if (task_cat.is_debug()) { + task_cat.debug() + << "Waiting up to " << timeout << " seconds for future " << *this << "\n"; + } + PStatTimer timer(AsyncTaskChain::_wait_pcollector); + _cvar->wait(timeout); + } +} + +/** + * Schedules the done callbacks. Assumes the manager's lock is held, and that + * the future is currently in the 'pending' state. + * @param clean_exit true if finished successfully, false if cancelled. + */ +void AsyncFuture:: +notify_done(bool clean_exit) { + nassertv(_manager != nullptr); + nassertv(_manager->_lock.debug_is_locked()); + nassertv(_future_state == FS_pending); + + pvector::iterator it; + for (it = _waiting_tasks.begin(); it != _waiting_tasks.end(); ++it) { + AsyncTask *task = *it; + nassertd(task->_manager == _manager) continue; + task->_state = AsyncTask::S_active; + task->_chain->_active.push_back(task); + --task->_chain->_num_awaiting_tasks; + unref_delete(task); + } + _waiting_tasks.clear(); + + // For historical reasons, we don't send the "done event" if the future was + // cancelled. + if (clean_exit && !_done_event.empty()) { + PT_Event event = new Event(_done_event); + event->add_parameter(EventParameter(this)); + throw_event(move(event)); + } + + nassertv_always(FS_pending == + (FutureState)AtomicAdjust::compare_and_exchange( + _future_state, + (AtomicAdjust::Integer)FS_pending, + (AtomicAdjust::Integer)(clean_exit ? FS_finished : FS_cancelled))); + + // Finally, notify any threads that may be waiting. + if (_cvar != nullptr) { + _cvar->notify_all(); + } +} + +/** + * 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. + * + * This variant takes two pointers; the second one is only set if this object + * inherits from ReferenceCount, so that a reference can be held. + * + * Assumes the manager's lock is *not* held. + */ +void AsyncFuture:: +set_result(TypedObject *ptr, ReferenceCount *ref_ptr) { + nassertv(!done()); + // If we don't have a manager, use the global one. + if (_manager == nullptr) { + _manager = AsyncTaskManager::get_global_ptr(); + } + MutexHolder holder(_manager->_lock); + _result = ptr; + _result_ref = ref_ptr; + notify_done(true); +} + +/** + * Indicates that the given task is waiting for this future to complete. When + * the future is done, it will reactivate the given task. + * Assumes the manager's lock is held. + */ +void AsyncFuture:: +add_waiting_task(AsyncTask *task) { + nassertv(!done()); + nassertv(_manager != nullptr); + nassertv(_manager->_lock.debug_is_locked()); + task->ref(); + _waiting_tasks.push_back(task); + nassertv(task->_manager == _manager); +} diff --git a/panda/src/event/asyncFuture.h b/panda/src/event/asyncFuture.h new file mode 100644 index 0000000000..456c872c04 --- /dev/null +++ b/panda/src/event/asyncFuture.h @@ -0,0 +1,135 @@ +/** + * 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 asyncFuture.h + * @author rdb + * @date 2017-11-28 + */ + +#ifndef ASYNCFUTURE_H +#define ASYNCFUTURE_H + +#include "pandabase.h" +#include "typedReferenceCount.h" +#include "typedWritableReferenceCount.h" +#include "conditionVar.h" +#include "atomicAdjust.h" + +class AsyncTaskManager; +class AsyncTask; +class ConditionVarFull; + +/** + * This class represents a thread-safe handle to a promised future result of + * an asynchronous operation, providing methods to query its status and result + * as well as register callbacks for this future's completion. + * + * An AsyncFuture can be awaited from within a coroutine or task. It keeps + * track of a list of tasks waiting for this future so that they are + * automatically reactivated upon this future's completion. + * + * A task itself is also a subclass of AsyncFuture. Other subclasses are + * possible, although subclassing is not necessary for most purposes. + * + * The `done()` method is used to check whether the future has completed and + * a result is available (whether it is cancelled or finished). + * + * To get the result of the future in C++, use `wait()` and `get_result()`. + * In Python, the functionality of both of those calls is wrapped into the + * `result()` method, which waits for the future to complete before either + * returning the result or throwing an exception if the future was cancelled. + * However, it is preferable to use the `await` keyword when running from a + * coroutine. + * + * This API aims to mirror and be compatible with Python's Future class. + */ +class EXPCL_PANDA_EVENT AsyncFuture : public TypedReferenceCount { +PUBLISHED: + INLINE AsyncFuture(); + virtual ~AsyncFuture(); + + EXTENSION(static PyObject *__await__(PyObject *self)); + EXTENSION(static PyObject *__iter__(PyObject *self)); + + INLINE bool done() const; + INLINE bool cancelled() const; + EXTENSION(PyObject *result(double timeout = -1.0) const); + + virtual bool cancel(); + + INLINE void set_done_event(const string &done_event); + INLINE const string &get_done_event() const; + MAKE_PROPERTY(done_event, get_done_event, set_done_event); + + virtual void output(ostream &out) const; + + void wait(); + void wait(double timeout); + + INLINE void set_result(nullptr_t); + INLINE void set_result(TypedObject *result); + INLINE void set_result(TypedReferenceCount *result); + INLINE void set_result(TypedWritableReferenceCount *result); + +public: + INLINE TypedObject *get_result() const; + INLINE void get_result(TypedObject *&ptr, ReferenceCount *&ref_ptr) const; + + void notify_done(bool clean_exit); + +private: + void set_result(TypedObject *ptr, ReferenceCount *ref_ptr); + void add_waiting_task(AsyncTask *task); + +protected: + enum FutureState { + FS_pending, + FS_finished, + FS_cancelled, + }; + + AsyncTaskManager *_manager; + ConditionVarFull *_cvar; + TypedObject *_result; + PT(ReferenceCount) _result_ref; + AtomicAdjust::Integer _future_state; + + string _done_event; + + // Tasks waiting for this one to complete. These are reference counted, but + // we can't store them in a PointerTo for circular dependency reasons. + pvector _waiting_tasks; + + friend class PythonTask; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "AsyncFuture", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const AsyncFuture &fut) { + fut.output(out); + return out; +}; + +#include "asyncFuture.I" + +#endif diff --git a/panda/src/event/asyncFuture_ext.cxx b/panda/src/event/asyncFuture_ext.cxx new file mode 100644 index 0000000000..49b1ab4d66 --- /dev/null +++ b/panda/src/event/asyncFuture_ext.cxx @@ -0,0 +1,175 @@ +/** + * 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 asyncFuture_ext.h + * @author rdb + * @date 2017-10-29 + */ + +#include "asyncFuture_ext.h" +#include "pythonTask.h" + +#ifdef HAVE_PYTHON + +#ifndef CPPPARSER +extern struct Dtool_PyTypedObject Dtool_AsyncFuture; +extern struct Dtool_PyTypedObject Dtool_TypedObject; +#endif + +/** + * Get the result of a future, which may be a PythonTask. Assumes that the + * future is already done. + */ +static PyObject *get_done_result(const AsyncFuture *future) { + if (!future->cancelled()) { + if (future->is_of_type(PythonTask::get_class_type())) { + // If it's a PythonTask, defer to its get_result(), since it may store + // any PyObject value or raise an exception. + const PythonTask *task = (const PythonTask *)future; + return task->get_result(); + } else { + ReferenceCount *ref_ptr; + TypedObject *ptr; + future->get_result(ptr, ref_ptr); + + if (ptr == nullptr) { + Py_INCREF(Py_None); + return Py_None; + } + + if (ref_ptr != nullptr) { + ref_ptr->ref(); + } + + return DTool_CreatePyInstanceTyped + ((void *)ptr, Dtool_TypedObject, (ref_ptr != nullptr), false, + ptr->get_type_index()); + } + } else { + // If the future was cancelled, we should raise an exception. + static PyObject *exc_type = nullptr; + if (exc_type == nullptr) { + // Get the CancelledError that asyncio uses, too. + PyObject *module = PyImport_ImportModule("concurrent.futures._base"); + if (module != nullptr) { + exc_type = PyObject_GetAttrString(module, "CancelledError"); + Py_DECREF(module); + } + // If we can't get that, we should pretend and make our own. + if (exc_type == nullptr) { + exc_type = PyErr_NewExceptionWithDoc("concurrent.futures._base.CancelledError", + "The Future was cancelled.", + nullptr, nullptr); + } + } + Py_INCREF(exc_type); + PyErr_Restore(exc_type, nullptr, nullptr); + return nullptr; + } +} + +/** + * Yields continuously until the task has finished. + */ +static PyObject *gen_next(PyObject *self) { + const AsyncFuture *future = nullptr; + if (!Dtool_Call_ExtractThisPointer(self, Dtool_AsyncFuture, (void **)&future)) { + return nullptr; + } + + if (!future->done()) { + // Continue awaiting the result. + Py_INCREF(self); + return self; + } else { + PyObject *result = get_done_result(future); + if (result != nullptr) { + Py_INCREF(PyExc_StopIteration); + PyErr_Restore(PyExc_StopIteration, result, nullptr); + } + return nullptr; + } +} + +/** + * Returns a generator that continuously yields an awaitable until the task + * has finished. This allows syntax like `model = await loader.load...` to be + * used in a Python coroutine. + */ +PyObject *Extension:: +__await__(PyObject *self) { + Dtool_GeneratorWrapper *gen; + gen = (Dtool_GeneratorWrapper *)PyType_GenericAlloc(&Dtool_GeneratorWrapper_Type, 0); + if (gen != nullptr) { + Py_INCREF(self); + gen->_base._self = self; + gen->_iternext_func = &gen_next; + } + return (PyObject *)gen; +} + +/** + * Returns the result of this future, unless it was cancelled, in which case + * it returns CancelledError. + * If the future is not yet done, waits until the result is available. If a + * timeout is passed and the future is not done within the given timeout, + * raises TimeoutError. + */ +PyObject *Extension:: +result(double timeout) const { + if (!_this->done()) { + // Not yet done? Wait until it is done, or until a timeout occurs. But + // first check to make sure we're not trying to deadlock the thread. + Thread *current_thread = Thread::get_current_thread(); + if (_this == (const AsyncFuture *)current_thread->get_current_task()) { + PyErr_SetString(PyExc_RuntimeError, "cannot call task.result() from within the task"); + return nullptr; + } + + // Release the GIL for the duration. +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS +#endif + //TODO: check why gcc and clang don't like infinity timeout. + if (cinf(timeout) || timeout < 0.0) { + _this->wait(); + } else { + _this->wait(timeout); + } +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + Py_BLOCK_THREADS +#endif + + if (!_this->done()) { + // It timed out. Raise an exception. + static PyObject *exc_type = nullptr; + if (exc_type == nullptr) { + // Get the TimeoutError that asyncio uses, too. + PyObject *module = PyImport_ImportModule("concurrent.futures._base"); + if (module != nullptr) { + exc_type = PyObject_GetAttrString(module, "TimeoutError"); + Py_DECREF(module); + } + // If we can't get that, we should pretend and make our own. + if (exc_type == nullptr) { + exc_type = PyErr_NewExceptionWithDoc("concurrent.futures._base.TimeoutError", + "The operation exceeded the given deadline.", + nullptr, nullptr); + } + } + Py_INCREF(exc_type); + PyErr_Restore(exc_type, nullptr, nullptr); + return nullptr; + } + } + + return get_done_result(_this); +} + +#endif diff --git a/panda/src/event/asyncFuture_ext.h b/panda/src/event/asyncFuture_ext.h new file mode 100644 index 0000000000..498225478d --- /dev/null +++ b/panda/src/event/asyncFuture_ext.h @@ -0,0 +1,37 @@ +/** + * 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 asyncFuture_ext.h + * @author rdb + * @date 2017-10-29 + */ + +#ifndef ASYNCFUTURE_EXT_H +#define ASYNCFUTURE_EXT_H + +#include "extension.h" +#include "py_panda.h" +#include "modelLoadRequest.h" + +#ifdef HAVE_PYTHON + +/** + * Extension class for AsyncFuture + */ +template<> +class Extension : public ExtensionBase { +public: + static PyObject *__await__(PyObject *self); + static PyObject *__iter__(PyObject *self) { return __await__(self); } + + PyObject *result(double timeout = -1.0) const; +}; + +#endif // HAVE_PYTHON + +#endif // ASYNCFUTURE_EXT_H diff --git a/panda/src/event/asyncTask.I b/panda/src/event/asyncTask.I index ae411d3d7c..869b0b33f7 100644 --- a/panda/src/event/asyncTask.I +++ b/panda/src/event/asyncTask.I @@ -33,6 +33,7 @@ is_alive() const { case S_servicing: case S_sleeping: case S_active_nested: + case S_awaiting: return true; case S_inactive: @@ -180,15 +181,6 @@ set_done_event(const string &done_event) { _done_event = done_event; } -/** - * Returns the event name that will be triggered when the task finishes. See - * set_done_event(). - */ -INLINE const string &AsyncTask:: -get_done_event() const { - return _done_event; -} - /** * Returns the amount of time elapsed during the task's previous run cycle, in * seconds. diff --git a/panda/src/event/asyncTask.cxx b/panda/src/event/asyncTask.cxx index fb251aebd6..17f0a0630d 100644 --- a/panda/src/event/asyncTask.cxx +++ b/panda/src/event/asyncTask.cxx @@ -35,7 +35,6 @@ AsyncTask(const string &name) : _priority(0), _state(S_inactive), _servicing_thread(NULL), - _manager(NULL), _chain(NULL), _start_time(0.0), _start_frame(0), @@ -68,11 +67,27 @@ AsyncTask:: * S_inactive (or possible S_servicing_removed). This is a no-op if the state * is already S_inactive. */ -void AsyncTask:: +bool AsyncTask:: remove() { - if (_manager != (AsyncTaskManager *)NULL) { - _manager->remove(this); + AsyncTaskManager *manager = _manager; + if (manager != nullptr) { + nassertr(_chain->_manager == manager, false); + if (task_cat.is_debug()) { + task_cat.debug() + << "Removing " << *this << "\n"; + } + MutexHolder holder(manager->_lock); + if (_chain->do_remove(this, true)) { + return true; + } else { + if (task_cat.is_debug()) { + task_cat.debug() + << " (unable to remove " << *this << ")\n"; + } + return false; + } } + return false; } /** @@ -379,11 +394,26 @@ jump_to_task_chain(AsyncTaskManager *manager) { */ AsyncTask::DoneStatus AsyncTask:: unlock_and_do_task() { - nassertr(_manager != (AsyncTaskManager *)NULL, DS_done); + nassertr(_manager != nullptr, DS_done); PT(ClockObject) clock = _manager->get_clock(); + // Indicate that this task is now the current task running on the thread. Thread *current_thread = Thread::get_current_thread(); - record_task(current_thread); + nassertr(current_thread->_current_task == nullptr, DS_interrupt); + + void *ptr = AtomicAdjust::compare_and_exchange_ptr + ((void * TVOLATILE &)current_thread->_current_task, + (void *)nullptr, (void *)this); + + // If the return value is other than nullptr, someone else must have + // assigned the task first, in another thread. That shouldn't be possible. + + // But different versions of gcc appear to have problems compiling these + // assertions correctly. +#ifndef __GNUC__ + nassertr(ptr == nullptr, DS_interrupt); + nassertr(current_thread->_current_task == this, DS_interrupt); +#endif // __GNUC__ // It's important to release the lock while the task is being serviced. _manager->_lock.release(); @@ -403,11 +433,36 @@ unlock_and_do_task() { _chain->_time_in_frame += _dt; - clear_task(current_thread); + // Now indicate that this is no longer the current task. + nassertr(current_thread->_current_task == this, status); + + ptr = AtomicAdjust::compare_and_exchange_ptr + ((void * TVOLATILE &)current_thread->_current_task, + (void *)this, (void *)nullptr); + + // If the return value is other than this, someone else must have assigned + // the task first, in another thread. That shouldn't be possible. + + // But different versions of gcc appear to have problems compiling these + // assertions correctly. +#ifndef __GNUC__ + nassertr(ptr == this, DS_interrupt); + nassertr(current_thread->_current_task == nullptr, DS_interrupt); +#endif // __GNUC__ return status; } +/** + * Cancels this task. This is equivalent to remove(). + */ +bool AsyncTask:: +cancel() { + bool result = remove(); + nassertr(done(), false); + return result; +} + /** * Override this function to return true if the task can be successfully * executed, false if it cannot. Mainly intended as a sanity check when @@ -477,22 +532,16 @@ upon_birth(AsyncTaskManager *manager) { * task has been removed because it exited normally (returning DS_done), or * false if it was removed for some other reason (e.g. * AsyncTaskManager::remove()). By the time this method is called, _manager - * has been cleared, so the parameter manager indicates the original + * may have been cleared, so the parameter manager indicates the original * AsyncTaskManager that owned this task. * - * The normal behavior is to throw the done_event only if clean_exit is true. - * * This function is called with the lock *not* held. */ void AsyncTask:: upon_death(AsyncTaskManager *manager, bool clean_exit) { - if (clean_exit && !_done_event.empty()) { - PT_Event event = new Event(_done_event); - event->add_parameter(EventParameter(this)); - throw_event(event); - } + //NB. done_event is now being thrown in AsyncFuture::notify_done(). - // Also throw a generic remove event for the manager. + // Throw a generic remove event for the manager. if (manager != (AsyncTaskManager *)NULL) { string remove_name = manager->get_name() + "-removeTask"; PT_Event event = new Event(remove_name); diff --git a/panda/src/event/asyncTask.h b/panda/src/event/asyncTask.h index 97176df0a3..a941cd4b36 100644 --- a/panda/src/event/asyncTask.h +++ b/panda/src/event/asyncTask.h @@ -15,8 +15,8 @@ #define ASYNCTASK_H #include "pandabase.h" - -#include "asyncTaskBase.h" +#include "asyncFuture.h" +#include "namable.h" #include "pmutex.h" #include "conditionVar.h" #include "pStatCollector.h" @@ -29,7 +29,7 @@ class AsyncTaskChain; * Normally, you would subclass from this class, and override do_task(), to * define the functionality you wish to have the task perform. */ -class EXPCL_PANDA_EVENT AsyncTask : public AsyncTaskBase { +class EXPCL_PANDA_EVENT AsyncTask : public AsyncFuture, public Namable { public: AsyncTask(const string &name = string()); ALLOC_DELETED_CHAIN(AsyncTask); @@ -45,6 +45,7 @@ PUBLISHED: DS_exit, // stop the enclosing sequence DS_pause, // pause, then exit (useful within a sequence) DS_interrupt, // interrupt the task manager, but run task again + DS_await, // await a different task's completion }; enum State { @@ -54,13 +55,14 @@ PUBLISHED: S_servicing_removed, // Still servicing, but wants removal from manager. S_sleeping, S_active_nested, // active within a sequence. + S_awaiting, // Waiting for a dependent task to complete }; INLINE State get_state() const; INLINE bool is_alive() const; INLINE AsyncTaskManager *get_manager() const; - void remove(); + bool remove(); INLINE void set_delay(double delay); INLINE void clear_delay(); @@ -90,7 +92,6 @@ PUBLISHED: INLINE int get_priority() const; INLINE void set_done_event(const string &done_event); - INLINE const string &get_done_event() const; INLINE double get_dt() const; INLINE double get_max_dt() const; @@ -102,6 +103,8 @@ protected: void jump_to_task_chain(AsyncTaskManager *manager); DoneStatus unlock_and_do_task(); + virtual bool cancel() FINAL; + virtual bool is_runnable(); virtual DoneStatus do_task(); virtual void upon_birth(AsyncTaskManager *manager); @@ -115,11 +118,9 @@ protected: double _wake_time; int _sort; int _priority; - string _done_event; State _state; Thread *_servicing_thread; - AsyncTaskManager *_manager; AsyncTaskChain *_chain; double _start_time; @@ -135,14 +136,16 @@ protected: static PStatCollector _show_code_pcollector; PStatCollector _task_pcollector; + friend class PythonTask; + public: static TypeHandle get_class_type() { return _type_handle; } static void init_type() { - AsyncTaskBase::init_type(); + AsyncFuture::init_type(); register_type(_type_handle, "AsyncTask", - AsyncTaskBase::get_class_type()); + AsyncFuture::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); @@ -152,6 +155,7 @@ public: private: static TypeHandle _type_handle; + friend class AsyncFuture; friend class AsyncTaskManager; friend class AsyncTaskChain; friend class AsyncTaskSequence; diff --git a/panda/src/event/asyncTaskChain.cxx b/panda/src/event/asyncTaskChain.cxx index bd5c391857..bda6113bd1 100644 --- a/panda/src/event/asyncTaskChain.cxx +++ b/panda/src/event/asyncTaskChain.cxx @@ -44,6 +44,7 @@ AsyncTaskChain(AsyncTaskManager *manager, const string &name) : _frame_sync(false), _num_busy_threads(0), _num_tasks(0), + _num_awaiting_tasks(0), _state(S_initial), _current_sort(-INT_MAX), _pickup_mode(false), @@ -455,24 +456,21 @@ do_add(AsyncTask *task) { /** * Removes the indicated task from this chain. Returns true if removed, false * otherwise. Assumes the lock is already held. The task->upon_death() - * method is *not* called. + * method is called with clean_exit=false if upon_death is given. */ bool AsyncTaskChain:: -do_remove(AsyncTask *task) { - bool removed = false; - +do_remove(AsyncTask *task, bool upon_death) { nassertr(task->_chain == this, false); switch (task->_state) { case AsyncTask::S_servicing: - // This task is being serviced. + // This task is being serviced. upon_death will be called afterwards. task->_state = AsyncTask::S_servicing_removed; - removed = true; - break; + return true; case AsyncTask::S_servicing_removed: - // Being serviced, though it will be removed later. - break; + // Being serviced, though it is already marked to be removed afterwards. + return false; case AsyncTask::S_sleeping: // Sleeping, easy. @@ -481,10 +479,9 @@ do_remove(AsyncTask *task) { nassertr(index != -1, false); _sleeping.erase(_sleeping.begin() + index); make_heap(_sleeping.begin(), _sleeping.end(), AsyncTaskSortWakeTime()); - removed = true; - cleanup_task(task, false, false); + cleanup_task(task, upon_death, false); } - break; + return true; case AsyncTask::S_active: { @@ -502,15 +499,15 @@ do_remove(AsyncTask *task) { nassertr(index != -1, false); } } - removed = true; - cleanup_task(task, false, false); + cleanup_task(task, upon_death, false); + return true; } default: break; } - return removed; + return false; } /** @@ -726,6 +723,13 @@ service_one_task(AsyncTaskChain::AsyncTaskChainThread *thread) { } break; + case AsyncTask::DS_await: + // The task wants to wait for another one to finish. + task->_state = AsyncTask::S_awaiting; + _cvar.notify_all(); + ++_num_awaiting_tasks; + break; + default: // The task has finished. cleanup_task(task, true, true); @@ -768,13 +772,18 @@ cleanup_task(AsyncTask *task, bool upon_death, bool clean_exit) { PT(AsyncTask) hold_task = task; task->_state = AsyncTask::S_inactive; - task->_chain = NULL; - task->_manager = NULL; + task->_chain = nullptr; --_num_tasks; --(_manager->_num_tasks); _manager->remove_task_by_name(task); + if (upon_death && !task->done()) { + task->notify_done(clean_exit); + } + + task->_manager = nullptr; + if (upon_death) { _manager->_lock.release(); task->upon_death(_manager, clean_exit); @@ -899,7 +908,7 @@ finish_sort_group() { filter_timeslice_priority(); } - nassertr((size_t)_num_tasks == _active.size() + _this_active.size() + _next_active.size() + _sleeping.size(), true); + nassertr((size_t)_num_tasks == _active.size() + _this_active.size() + _next_active.size() + _sleeping.size() + (size_t)_num_awaiting_tasks, true); make_heap(_active.begin(), _active.end(), AsyncTaskSortPriority()); _current_sort = -INT_MAX; diff --git a/panda/src/event/asyncTaskChain.h b/panda/src/event/asyncTaskChain.h index 4f98ef284a..512a7b9fa0 100644 --- a/panda/src/event/asyncTaskChain.h +++ b/panda/src/event/asyncTaskChain.h @@ -96,7 +96,7 @@ protected: typedef pvector< PT(AsyncTask) > TaskHeap; void do_add(AsyncTask *task); - bool do_remove(AsyncTask *task); + bool do_remove(AsyncTask *task, bool upon_death=false); void do_wait_for_tasks(); void do_cleanup(); @@ -172,6 +172,7 @@ protected: bool _frame_sync; int _num_busy_threads; int _num_tasks; + int _num_awaiting_tasks; TaskHeap _active; TaskHeap _this_active; TaskHeap _next_active; @@ -205,6 +206,7 @@ public: private: static TypeHandle _type_handle; + friend class AsyncFuture; friend class AsyncTaskChainThread; friend class AsyncTask; friend class AsyncTaskManager; diff --git a/panda/src/event/asyncTaskCollection.cxx b/panda/src/event/asyncTaskCollection.cxx index 38a07c6875..80999362e1 100644 --- a/panda/src/event/asyncTaskCollection.cxx +++ b/panda/src/event/asyncTaskCollection.cxx @@ -63,14 +63,15 @@ add_task(AsyncTask *task) { */ bool AsyncTaskCollection:: remove_task(AsyncTask *task) { - int task_index = -1; - for (int i = 0; task_index == -1 && i < (int)_tasks.size(); i++) { + size_t task_index = (size_t)-1; + for (size_t i = 0; i < _tasks.size(); ++i) { if (_tasks[i] == task) { task_index = i; + break; } } - if (task_index == -1) { + if (task_index == (size_t)-1) { // The indicated task was not a member of the collection. return false; } @@ -129,12 +130,12 @@ void AsyncTaskCollection:: remove_duplicate_tasks() { AsyncTasks new_tasks; - int num_tasks = get_num_tasks(); - for (int i = 0; i < num_tasks; i++) { + size_t num_tasks = get_num_tasks(); + for (size_t i = 0; i < num_tasks; i++) { PT(AsyncTask) task = get_task(i); bool duplicated = false; - for (int j = 0; j < i && !duplicated; j++) { + for (size_t j = 0; j < i && !duplicated; j++) { duplicated = (task == get_task(j)); } @@ -152,7 +153,7 @@ remove_duplicate_tasks() { */ bool AsyncTaskCollection:: has_task(AsyncTask *task) const { - for (int i = 0; i < get_num_tasks(); i++) { + for (size_t i = 0; i < get_num_tasks(); i++) { if (task == get_task(i)) { return true; } @@ -174,8 +175,8 @@ clear() { */ AsyncTask *AsyncTaskCollection:: find_task(const string &name) const { - int num_tasks = get_num_tasks(); - for (int i = 0; i < num_tasks; i++) { + size_t num_tasks = get_num_tasks(); + for (size_t i = 0; i < num_tasks; ++i) { AsyncTask *task = get_task(i); if (task->get_name() == name) { return task; @@ -187,7 +188,7 @@ find_task(const string &name) const { /** * Returns the number of AsyncTasks in the collection. */ -int AsyncTaskCollection:: +size_t AsyncTaskCollection:: get_num_tasks() const { return _tasks.size(); } @@ -196,8 +197,8 @@ get_num_tasks() const { * Returns the nth AsyncTask in the collection. */ AsyncTask *AsyncTaskCollection:: -get_task(int index) const { - nassertr(index >= 0 && index < (int)_tasks.size(), NULL); +get_task(size_t index) const { + nassertr(index < _tasks.size(), nullptr); return _tasks[index]; } @@ -206,7 +207,7 @@ get_task(int index) const { * Removes the nth AsyncTask from the collection. */ void AsyncTaskCollection:: -remove_task(int index) { +remove_task(size_t index) { // If the pointer to our internal array is shared by any other // AsyncTaskCollections, we have to copy the array now so we won't // inadvertently modify any of our brethren AsyncTaskCollection objects. @@ -217,7 +218,7 @@ remove_task(int index) { _tasks.v() = old_tasks.v(); } - nassertv(index >= 0 && index < (int)_tasks.size()); + nassertv(index < _tasks.size()); _tasks.erase(_tasks.begin() + index); } @@ -226,9 +227,8 @@ remove_task(int index) { * get_task(), but it may be a more convenient way to access it. */ AsyncTask *AsyncTaskCollection:: -operator [] (int index) const { - nassertr(index >= 0 && index < (int)_tasks.size(), NULL); - +operator [] (size_t index) const { + nassertr(index < _tasks.size(), nullptr); return _tasks[index]; } @@ -236,7 +236,7 @@ operator [] (int index) const { * Returns the number of tasks in the collection. This is the same thing as * get_num_tasks(). */ -int AsyncTaskCollection:: +size_t AsyncTaskCollection:: size() const { return _tasks.size(); } @@ -260,7 +260,7 @@ output(ostream &out) const { */ void AsyncTaskCollection:: write(ostream &out, int indent_level) const { - for (int i = 0; i < get_num_tasks(); i++) { + for (size_t i = 0; i < get_num_tasks(); i++) { indent(out, indent_level) << *get_task(i) << "\n"; } } diff --git a/panda/src/event/asyncTaskCollection.h b/panda/src/event/asyncTaskCollection.h index 824ec433c2..4ed6f6a59b 100644 --- a/panda/src/event/asyncTaskCollection.h +++ b/panda/src/event/asyncTaskCollection.h @@ -41,12 +41,12 @@ PUBLISHED: AsyncTask *find_task(const string &name) const; - int get_num_tasks() const; - AsyncTask *get_task(int index) const; + size_t get_num_tasks() const; + AsyncTask *get_task(size_t index) const; MAKE_SEQ(get_tasks, get_num_tasks, get_task); - void remove_task(int index); - AsyncTask *operator [] (int index) const; - int size() const; + void remove_task(size_t index); + AsyncTask *operator [] (size_t index) const; + size_t size() const; INLINE void operator += (const AsyncTaskCollection &other); INLINE AsyncTaskCollection operator + (const AsyncTaskCollection &other) const; diff --git a/panda/src/event/asyncTaskManager.I b/panda/src/event/asyncTaskManager.I index 20294f63dc..c1e5775a55 100644 --- a/panda/src/event/asyncTaskManager.I +++ b/panda/src/event/asyncTaskManager.I @@ -36,7 +36,7 @@ get_clock() { * Returns the number of tasks that are currently active or sleeping within * the task manager. */ -INLINE int AsyncTaskManager:: +INLINE size_t AsyncTaskManager:: get_num_tasks() const { MutexHolder holder(_lock); return _num_tasks; diff --git a/panda/src/event/asyncTaskManager.cxx b/panda/src/event/asyncTaskManager.cxx index b95ddbf109..5e1bf9601c 100644 --- a/panda/src/event/asyncTaskManager.cxx +++ b/panda/src/event/asyncTaskManager.cxx @@ -307,25 +307,20 @@ find_tasks_matching(const GlobPattern &pattern) const { */ bool AsyncTaskManager:: remove(AsyncTask *task) { - // We pass this up to the multi-task remove() flavor. Do we care about the - // tiny cost of creating an AsyncTaskCollection here? Probably not. - AsyncTaskCollection tasks; - tasks.add_task(task); - return remove(tasks) != 0; + return task->remove(); } /** * Removes all of the tasks in the AsyncTaskCollection. Returns the number of * tasks removed. */ -int AsyncTaskManager:: +size_t AsyncTaskManager:: remove(const AsyncTaskCollection &tasks) { MutexHolder holder(_lock); - int num_removed = 0; + size_t num_removed = 0; - int num_tasks = tasks.get_num_tasks(); - int i; - for (i = 0; i < num_tasks; ++i) { + size_t num_tasks = tasks.get_num_tasks(); + for (size_t i = 0; i < num_tasks; ++i) { PT(AsyncTask) task = tasks.get_task(i); if (task->_manager != this) { @@ -337,10 +332,7 @@ remove(const AsyncTaskCollection &tasks) { task_cat.debug() << "Removing " << *task << "\n"; } - if (task->_chain->do_remove(task)) { - _lock.release(); - task->upon_death(this, false); - _lock.acquire(); + if (task->_chain->do_remove(task, true)) { ++num_removed; } else { if (task_cat.is_debug()) { diff --git a/panda/src/event/asyncTaskManager.h b/panda/src/event/asyncTaskManager.h index 967476c4af..d843508de7 100644 --- a/panda/src/event/asyncTaskManager.h +++ b/panda/src/event/asyncTaskManager.h @@ -47,7 +47,7 @@ */ class EXPCL_PANDA_EVENT AsyncTaskManager : public TypedReferenceCount, public Namable { PUBLISHED: - AsyncTaskManager(const string &name); + explicit AsyncTaskManager(const string &name); BLOCKING virtual ~AsyncTaskManager(); BLOCKING void cleanup(); @@ -71,13 +71,13 @@ PUBLISHED: AsyncTaskCollection find_tasks_matching(const GlobPattern &pattern) const; bool remove(AsyncTask *task); - int remove(const AsyncTaskCollection &tasks); + size_t remove(const AsyncTaskCollection &tasks); BLOCKING void wait_for_tasks(); BLOCKING void stop_threads(); void start_threads(); - INLINE int get_num_tasks() const; + INLINE size_t get_num_tasks() const; AsyncTaskCollection get_tasks() const; AsyncTaskCollection get_active_tasks() const; @@ -126,7 +126,7 @@ protected: typedef ov_set > TaskChains; TaskChains _task_chains; - int _num_tasks; + size_t _num_tasks; TasksByName _tasks_by_name; PT(ClockObject) _clock; @@ -151,10 +151,12 @@ public: private: static TypeHandle _type_handle; + friend class AsyncFuture; friend class AsyncTaskChain; friend class AsyncTaskChain::AsyncTaskChainThread; friend class AsyncTask; friend class AsyncTaskSequence; + friend class PythonTask; }; INLINE ostream &operator << (ostream &out, const AsyncTaskManager &manager) { diff --git a/panda/src/event/asyncTaskSequence.cxx b/panda/src/event/asyncTaskSequence.cxx index e8713ca05e..fe306c26a3 100644 --- a/panda/src/event/asyncTaskSequence.cxx +++ b/panda/src/event/asyncTaskSequence.cxx @@ -106,6 +106,7 @@ do_task() { case DS_pickup: case DS_exit: case DS_interrupt: + case DS_await: // Just return these results through. return result; } diff --git a/panda/src/event/asyncTaskSequence.h b/panda/src/event/asyncTaskSequence.h index 4cd4d64d58..de0127a1d8 100644 --- a/panda/src/event/asyncTaskSequence.h +++ b/panda/src/event/asyncTaskSequence.h @@ -32,7 +32,7 @@ class AsyncTaskManager; */ class EXPCL_PANDA_EVENT AsyncTaskSequence : public AsyncTask, public AsyncTaskCollection { PUBLISHED: - AsyncTaskSequence(const string &name); + explicit AsyncTaskSequence(const string &name); virtual ~AsyncTaskSequence(); ALLOC_DELETED_CHAIN(AsyncTaskSequence); diff --git a/panda/src/event/config_event.cxx b/panda/src/event/config_event.cxx index 0f335981aa..86a8df8361 100644 --- a/panda/src/event/config_event.cxx +++ b/panda/src/event/config_event.cxx @@ -12,6 +12,7 @@ */ #include "config_event.h" +#include "asyncFuture.h" #include "asyncTask.h" #include "asyncTaskChain.h" #include "asyncTaskManager.h" @@ -31,6 +32,7 @@ NotifyCategoryDef(event, ""); NotifyCategoryDef(task, ""); ConfigureFn(config_event) { + AsyncFuture::init_type(); AsyncTask::init_type(); AsyncTaskChain::init_type(); AsyncTaskManager::init_type(); diff --git a/panda/src/event/eventHandler.cxx b/panda/src/event/eventHandler.cxx index a71e7fc12c..020ba905d5 100644 --- a/panda/src/event/eventHandler.cxx +++ b/panda/src/event/eventHandler.cxx @@ -27,6 +27,26 @@ EventHandler:: EventHandler(EventQueue *ev_queue) : _queue(*ev_queue) { } +/** + * Returns a pending future that will be marked as done when the event is next + * fired. + */ +AsyncFuture *EventHandler:: +get_future(const string &event_name) { + Futures::iterator fi; + fi = _futures.find(event_name); + + // If we already have a future, but someone cancelled it, we need to create + // a new future instead. + if (fi != _futures.end() && !fi->second->cancelled()) { + return fi->second; + } else { + AsyncFuture *fut = new AsyncFuture; + _futures[event_name] = fut; + return fut; + } +} + /** * The main processing loop of the EventHandler. This function must be called * periodically to service events. Walks through each pending event and calls @@ -81,6 +101,18 @@ dispatch_event(const Event *event) { ((*cfi).first)(event, (*cfi).second); } } + + // Finally, check for futures that need to be triggered. + Futures::iterator fi; + fi = _futures.find(event->get_name()); + + if (fi != _futures.end()) { + AsyncFuture *fut = (*fi).second; + if (!fut->done()) { + fut->set_result((TypedReferenceCount *)event); + } + _futures.erase(fi); + } } @@ -182,6 +214,46 @@ has_hook(const string &event_name) const { } +/** + * Returns true if there is the hook added on the indicated event name and + * function pointer, false otherwise. + */ +bool EventHandler:: +has_hook(const string &event_name, EventFunction *function) const { + assert(!event_name.empty()); + Hooks::const_iterator hi; + hi = _hooks.find(event_name); + if (hi != _hooks.end()) { + const Functions& functions = (*hi).second; + if (functions.find(function) != functions.end()) { + return true; + } + } + + return false; +} + + +/** + * Returns true if there is the hook added on the indicated event name, + * function pointer and callback data, false otherwise. + */ +bool EventHandler:: +has_hook(const string &event_name, EventCallbackFunction *function, void *data) const { + assert(!event_name.empty()); + CallbackHooks::const_iterator chi; + chi = _cbhooks.find(event_name); + if (chi != _cbhooks.end()) { + const CallbackFunctions& cbfunctions = (*chi).second; + if (cbfunctions.find(CallbackFunction(function, data)) != cbfunctions.end()) { + return true; + } + } + + return false; +} + + /** * Removes the indicated function from the named event hook. Returns true if * the hook was removed, false if it wasn't there in the first place. diff --git a/panda/src/event/eventHandler.h b/panda/src/event/eventHandler.h index 452082549e..39d84475a4 100644 --- a/panda/src/event/eventHandler.h +++ b/panda/src/event/eventHandler.h @@ -18,6 +18,7 @@ #include "event.h" #include "pt_Event.h" +#include "asyncFuture.h" #include "pset.h" #include "pmap.h" @@ -40,11 +41,14 @@ public: typedef void EventCallbackFunction(const Event *, void *); PUBLISHED: - EventHandler(EventQueue *ev_queue); + explicit EventHandler(EventQueue *ev_queue); + ~EventHandler() {} + + AsyncFuture *get_future(const string &event_name); void process_events(); - virtual void dispatch_event(const Event *); + virtual void dispatch_event(const Event *event); void write(ostream &out) const; @@ -55,6 +59,9 @@ public: bool add_hook(const string &event_name, EventCallbackFunction *function, void *data); bool has_hook(const string &event_name) const; + bool has_hook(const string &event_name, EventFunction *function) const; + bool has_hook(const string &event_name, EventCallbackFunction *function, + void *data) const; bool remove_hook(const string &event_name, EventFunction *function); bool remove_hook(const string &event_name, EventCallbackFunction *function, void *data); @@ -71,9 +78,11 @@ protected: typedef pair CallbackFunction; typedef pset CallbackFunctions; typedef pmap CallbackHooks; + typedef pmap Futures; Hooks _hooks; CallbackHooks _cbhooks; + Futures _futures; EventQueue &_queue; static EventHandler *_global_event_handler; diff --git a/panda/src/event/eventParameter.I b/panda/src/event/eventParameter.I index a6f512d72f..14727106e6 100644 --- a/panda/src/event/eventParameter.I +++ b/panda/src/event/eventParameter.I @@ -11,13 +11,6 @@ * @date 1999-02-08 */ -/** - * Defines an EventParameter that stores nothing: the "empty" parameter. - */ -INLINE EventParameter:: -EventParameter() { -} - /** * Defines an EventParameter that stores a pointer to any kind of * TypedWritableReferenceCount object. This is the most general constructor. diff --git a/panda/src/event/eventParameter.h b/panda/src/event/eventParameter.h index ea960c156e..b0cbcc1376 100644 --- a/panda/src/event/eventParameter.h +++ b/panda/src/event/eventParameter.h @@ -34,7 +34,8 @@ */ class EXPCL_PANDA_EVENT EventParameter { PUBLISHED: - INLINE EventParameter(); + INLINE EventParameter() DEFAULT_CTOR; + INLINE EventParameter(nullptr_t) {}; INLINE EventParameter(const TypedWritableReferenceCount *ptr); INLINE EventParameter(const TypedReferenceCount *ptr); INLINE EventParameter(int value); diff --git a/panda/src/event/p3event_composite1.cxx b/panda/src/event/p3event_composite1.cxx index 57cc83159c..dc72d2b03f 100644 --- a/panda/src/event/p3event_composite1.cxx +++ b/panda/src/event/p3event_composite1.cxx @@ -1,3 +1,4 @@ +#include "asyncFuture.cxx" #include "asyncTask.cxx" #include "asyncTaskChain.cxx" #include "asyncTaskCollection.cxx" diff --git a/panda/src/event/pythonTask.I b/panda/src/event/pythonTask.I index b2f9a0ef07..78f8e30cc7 100644 --- a/panda/src/event/pythonTask.I +++ b/panda/src/event/pythonTask.I @@ -10,3 +10,47 @@ * @author drose * @date 2008-09-16 */ + +/** + * Returns the function that is called when the task runs. + */ +INLINE PyObject *PythonTask:: +get_function() { + Py_INCREF(_function); + return _function; +} + +/** + * Returns the function that is called when the task finishes. + */ +INLINE PyObject *PythonTask:: +get_upon_death() { + Py_INCREF(_upon_death); + return _upon_death; +} + +/** + * Returns the "owner" object. See set_owner(). + */ +INLINE PyObject *PythonTask:: +get_owner() const { + Py_INCREF(_owner); + return _owner; +} + +/** + * Sets the "result" of this task. This is the value returned from an "await" + * expression on this task. + * This can only be called while the task is still alive. + */ +INLINE void PythonTask:: +set_result(PyObject *result) { + // Note that we don't call notify_done() here since the done status will be + // automatically notified upon the task's completion. + nassertv(is_alive()); + nassertv(!done()); + nassertv(_exception == nullptr); + Py_INCREF(result); + Py_XDECREF(_exc_value); + _exc_value = result; +} diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index 3ca6178803..bda2706407 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -19,28 +19,52 @@ #include "py_panda.h" #include "pythonThread.h" +#include "asyncTaskManager.h" TypeHandle PythonTask::_type_handle; #ifndef CPPPARSER extern struct Dtool_PyTypedObject Dtool_TypedReferenceCount; +extern struct Dtool_PyTypedObject Dtool_AsyncFuture; +extern struct Dtool_PyTypedObject Dtool_PythonTask; #endif /** * */ PythonTask:: -PythonTask(PyObject *function, const string &name) : - AsyncTask(name) -{ - _function = NULL; - _args = NULL; - _upon_death = NULL; - _owner = NULL; - _registered_to_owner = false; - _generator = NULL; +PythonTask(PyObject *func_or_coro, const string &name) : + AsyncTask(name), + _function(nullptr), + _args(nullptr), + _upon_death(nullptr), + _owner(nullptr), + _registered_to_owner(false), + _exception(nullptr), + _exc_value(nullptr), + _exc_traceback(nullptr), + _generator(nullptr), + _future_done(nullptr), + _retrieved_exception(false) { + + nassertv(func_or_coro != nullptr); + if (func_or_coro == Py_None || PyCallable_Check(func_or_coro)) { + _function = func_or_coro; + Py_INCREF(_function); +#if PY_VERSION_HEX >= 0x03050000 + } else if (PyCoro_CheckExact(func_or_coro)) { + // We also allow passing in a coroutine, because why not. + _generator = func_or_coro; + Py_INCREF(_generator); +#endif + } else if (PyGen_CheckExact(func_or_coro)) { + // Something emulating a coroutine. + _generator = func_or_coro; + Py_INCREF(_generator); + } else { + nassert_raise("Invalid function passed to PythonTask"); + } - set_function(function); set_args(Py_None, true); set_upon_death(Py_None); set_owner(Py_None); @@ -60,9 +84,24 @@ PythonTask(PyObject *function, const string &name) : */ PythonTask:: ~PythonTask() { - Py_DECREF(_function); +#ifndef NDEBUG + // If the coroutine threw an exception, and there was no opportunity to + // handle it, let the user know. + if (_exception != nullptr && !_retrieved_exception) { + task_cat.error() + << *this << " exception was never retrieved:\n"; + PyErr_Restore(_exception, _exc_value, _exc_traceback); + PyErr_Print(); + PyErr_Restore(nullptr, nullptr, nullptr); + } +#endif + + Py_XDECREF(_function); Py_DECREF(_args); Py_DECREF(__dict__); + Py_XDECREF(_exception); + Py_XDECREF(_exc_value); + Py_XDECREF(_exc_traceback); Py_XDECREF(_generator); Py_XDECREF(_owner); Py_XDECREF(_upon_death); @@ -83,15 +122,6 @@ set_function(PyObject *function) { } } -/** - * Returns the function that is called when the task runs. - */ -PyObject *PythonTask:: -get_function() { - Py_INCREF(_function); - return _function; -} - /** * Replaces the argument list that is passed to the task function. The * parameter should be a tuple or list of arguments, or None to indicate the @@ -166,15 +196,6 @@ set_upon_death(PyObject *upon_death) { } } -/** - * Returns the function that is called when the task finishes. - */ -PyObject *PythonTask:: -get_upon_death() { - Py_INCREF(_upon_death); - return _upon_death; -} - /** * Specifies a Python object that serves as the "owner" for the task. This * owner object must have two methods: _addTask() and _clearTask(), which will @@ -212,14 +233,46 @@ set_owner(PyObject *owner) { } /** - * Returns the "owner" object. See set_owner(). + * Returns the result of this task's execution, as set by set_result() within + * the task or returned from a coroutine added to the task manager. If an + * exception occurred within this task, it is raised instead. */ PyObject *PythonTask:: -get_owner() { - Py_INCREF(_owner); - return _owner; +get_result() const { + nassertr(done(), nullptr); + + if (_exception == nullptr) { + // The result of the call is stored in _exc_value. + Py_XINCREF(_exc_value); + return _exc_value; + } else { + _retrieved_exception = true; + Py_INCREF(_exception); + Py_XINCREF(_exc_value); + Py_XINCREF(_exc_traceback); + PyErr_Restore(_exception, _exc_value, _exc_traceback); + return nullptr; + } } +/** + * If an exception occurred during execution of this task, returns it. This + * is only set if this task returned a coroutine or generator. + */ +/*PyObject *PythonTask:: +exception() const { + if (_exception == nullptr) { + Py_INCREF(Py_None); + return Py_None; + } else if (_exc_value == nullptr || _exc_value == Py_None) { + return _PyObject_CallNoArg(_exception); + } else if (PyTuple_Check(_exc_value)) { + return PyObject_Call(_exception, _exc_value, nullptr); + } else { + return PyObject_CallFunctionObjArgs(_exception, _exc_value, nullptr); + } +}*/ + /** * Maps from an expression like "task.attr_name = v". This is customized here * so we can support some traditional task interfaces that supported directly @@ -396,16 +449,30 @@ do_task() { */ AsyncTask::DoneStatus PythonTask:: do_python_task() { - PyObject *result = NULL; + PyObject *result = nullptr; - if (_generator == (PyObject *)NULL) { + // Are we waiting for a future to finish? + if (_future_done != nullptr) { + PyObject *is_done = PyObject_CallObject(_future_done, nullptr); + if (!PyObject_IsTrue(is_done)) { + // Nope, ask again next frame. + Py_DECREF(is_done); + return DS_cont; + } + Py_DECREF(is_done); + Py_DECREF(_future_done); + _future_done = nullptr; + } + + if (_generator == nullptr) { // We are calling the function directly. + nassertr(_function != nullptr, DS_interrupt); + PyObject *args = get_args(); result = PythonThread::call_python_func(_function, args); Py_DECREF(args); -#ifdef PyGen_Check - if (result != (PyObject *)NULL && PyGen_Check(result)) { + if (result != nullptr && PyGen_Check(result)) { // The function has yielded a generator. We will call into that // henceforth, instead of calling the function from the top again. if (task_cat.is_debug()) { @@ -423,30 +490,166 @@ do_python_task() { Py_DECREF(str); } _generator = result; - result = NULL; - } + result = nullptr; + +#if PY_VERSION_HEX >= 0x03050000 + } else if (result != nullptr && Py_TYPE(result)->tp_as_async != nullptr) { + // The function yielded a coroutine, or something of the sort. + if (task_cat.is_debug()) { + PyObject *str = PyObject_ASCII(_function); + PyObject *str2 = PyObject_ASCII(result); + task_cat.debug() + << PyUnicode_AsUTF8(str) << " in " << *this + << " yielded an awaitable: " << PyUnicode_AsUTF8(str2) << "\n"; + Py_DECREF(str); + Py_DECREF(str2); + } + if (PyCoro_CheckExact(result)) { + // If a coroutine, am_await is possible but senseless, since we can + // just call send(None) on the coroutine itself. + _generator = result; + } else { + unaryfunc await = Py_TYPE(result)->tp_as_async->am_await; + _generator = await(result); + Py_DECREF(result); + } + result = nullptr; #endif + } } - if (_generator != (PyObject *)NULL) { - // We are calling a generator. - PyObject *func = PyObject_GetAttrString(_generator, "next"); - nassertr(func != (PyObject *)NULL, DS_interrupt); - - result = PyObject_CallObject(func, NULL); + if (_generator != nullptr) { + // We are calling a generator. Use "send" rather than PyIter_Next since + // we need to be able to read the value from a StopIteration exception. + PyObject *func = PyObject_GetAttrString(_generator, "send"); + nassertr(func != nullptr, DS_interrupt); + result = PyObject_CallFunctionObjArgs(func, Py_None, nullptr); Py_DECREF(func); - if (result == (PyObject *)NULL && PyErr_Occurred() && - PyErr_ExceptionMatches(PyExc_StopIteration)) { - // "Catch" StopIteration and treat it like DS_done. - PyErr_Clear(); + if (result == nullptr) { + // An error happened. If StopIteration, that indicates the task has + // returned. Otherwise, we need to save it so that it can be re-raised + // in the function that awaited this task. Py_DECREF(_generator); - _generator = NULL; - return DS_done; + _generator = nullptr; + +#if PY_VERSION_HEX >= 0x03030000 + if (_PyGen_FetchStopIterationValue(&result) == 0) { +#else + if (PyErr_ExceptionMatches(PyExc_StopIteration)) { + result = Py_None; + Py_INCREF(result); +#endif + PyErr_Restore(nullptr, nullptr, nullptr); + + // If we passed a coroutine into the task, eg. something like: + // taskMgr.add(my_async_function()) + // then we cannot rerun the task, so the return value is always + // assumed to be DS_done. Instead, we pass the return value to the + // result of the `await` expression. + if (_function == nullptr) { + if (task_cat.is_debug()) { + task_cat.debug() + << *this << " received StopIteration from coroutine.\n"; + } + // Store the result in _exc_value because that's not used anyway. + Py_XDECREF(_exc_value); + _exc_value = result; + return DS_done; + } + } else if (_function == nullptr) { + // We got an exception. If this is a scheduled coroutine, we will + // keep it and instead throw it into whatever 'awaits' this task. + // Otherwise, fall through and handle it the regular way. + Py_XDECREF(_exception); + Py_XDECREF(_exc_value); + Py_XDECREF(_exc_traceback); + PyErr_Fetch(&_exception, &_exc_value, &_exc_traceback); + _retrieved_exception = false; + + if (task_cat.is_debug()) { + if (_exception != nullptr && Py_TYPE(_exception) == &PyType_Type) { + task_cat.debug() + << *this << " received " << ((PyTypeObject *)_exception)->tp_name << " from coroutine.\n"; + } else { + task_cat.debug() + << *this << " received exception from coroutine.\n"; + } + } + + // Tell the task chain we want to kill ourselves. We indicate this is + // a "clean exit" because we still want to run the done callbacks on + // exception. + return DS_done; + } + + } else if (DtoolInstance_Check(result)) { + // We are waiting for an AsyncFuture (eg. other task) to finish. + AsyncFuture *fut = (AsyncFuture *)DtoolInstance_UPCAST(result, Dtool_AsyncFuture); + if (fut != nullptr) { + // Suspend execution of this task until this other task has completed. + AsyncTaskManager *manager = fut->_manager; + if (manager == nullptr) { + manager = _manager; + fut->_manager = manager; + } + nassertr(manager == _manager, DS_interrupt); + MutexHolder holder(manager->_lock); + if (fut != (AsyncFuture *)this) { + if (!fut->done()) { + if (task_cat.is_debug()) { + task_cat.debug() + << *this << " is now awaiting <" << *fut << ">.\n"; + } + fut->add_waiting_task(this); + } else { + // The task is already done. Continue at next opportunity. + if (task_cat.is_debug()) { + task_cat.debug() + << *this << " would await <" << *fut << ">, were it not already done.\n"; + } + Py_DECREF(result); + return DS_cont; + } + } else { + // This is an error. If we wanted to be fancier we could also + // detect deeper circular dependencies. + task_cat.error() + << *this << " cannot await itself\n"; + } + Py_DECREF(result); + return DS_await; + } + } else { + // We are waiting for a non-Panda future to finish. We currently + // implement this by checking every frame whether the future is done. + PyObject *check = PyObject_GetAttrString(result, "_asyncio_future_blocking"); + if (check != nullptr && check != Py_None) { + Py_DECREF(check); + // Next frame, check whether this future is done. + _future_done = PyObject_GetAttrString(result, "done"); + if (_future_done == nullptr || !PyCallable_Check(_future_done)) { + task_cat.error() + << "future.done is not callable\n"; + return DS_interrupt; + } +#if PY_MAJOR_VERSION >= 3 + if (task_cat.is_debug()) { + PyObject *str = PyObject_ASCII(result); + task_cat.debug() + << *this << " is now polling " << PyUnicode_AsUTF8(str) << ".done()\n"; + Py_DECREF(str); + } +#endif + Py_DECREF(result); + return DS_cont; + } + PyErr_Clear(); + Py_XDECREF(check); } } - if (result == (PyObject *)NULL) { + if (result == nullptr) { if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) { // Don't print an error message for SystemExit. Or rather, make it a // debug message. @@ -500,6 +703,24 @@ do_python_task() { } } + // This is unfortunate, but some are returning task.done, which nowadays + // conflicts with the AsyncFuture method. Check if that is being returned. + PyMethodDef *meth = nullptr; + if (PyCFunction_Check(result)) { + meth = ((PyCFunctionObject *)result)->m_ml; +#if PY_MAJOR_VERSION >= 3 + } else if (Py_TYPE(result) == &PyMethodDescr_Type) { +#else + } else if (strcmp(Py_TYPE(result)->tp_name, "method_descriptor") == 0) { +#endif + meth = ((PyMethodDescrObject *)result)->d_method; + } + + if (meth != nullptr && strcmp(meth->ml_name, "done") == 0) { + Py_DECREF(result); + return DS_done; + } + ostringstream strm; #if PY_MAJOR_VERSION >= 3 PyObject *str = PyObject_ASCII(result); diff --git a/panda/src/event/pythonTask.h b/panda/src/event/pythonTask.h index d97be5795e..869a0691ab 100644 --- a/panda/src/event/pythonTask.h +++ b/panda/src/event/pythonTask.h @@ -20,10 +20,11 @@ #ifdef HAVE_PYTHON #include "py_panda.h" +#include "extension.h" /** - * This class exists to allow association of a Python function with the - * AsyncTaskManager. + * This class exists to allow association of a Python function or coroutine + * with the AsyncTaskManager. */ class PythonTask : public AsyncTask { PUBLISHED: @@ -32,17 +33,26 @@ PUBLISHED: ALLOC_DELETED_CHAIN(PythonTask); void set_function(PyObject *function); - PyObject *get_function(); + INLINE PyObject *get_function(); void set_args(PyObject *args, bool append_task); PyObject *get_args(); void set_upon_death(PyObject *upon_death); - PyObject *get_upon_death(); + INLINE PyObject *get_upon_death(); void set_owner(PyObject *owner); - PyObject *get_owner(); + INLINE PyObject *get_owner() const; + INLINE void set_result(PyObject *result); + +public: + // This is exposed only for the result() function in asyncFuture_ext.cxx + // to use, which is why it is not published. + PyObject *get_result() const; + //PyObject *exception() const; + +PUBLISHED: int __setattr__(PyObject *self, PyObject *attr, PyObject *v); int __delattr__(PyObject *self, PyObject *attr); PyObject *__getattr__(PyObject *attr) const; @@ -102,12 +112,21 @@ private: private: PyObject *_function; PyObject *_args; - bool _append_task; PyObject *_upon_death; PyObject *_owner; - bool _registered_to_owner; + + PyObject *_exception; + PyObject *_exc_value; + PyObject *_exc_traceback; PyObject *_generator; + PyObject *_future_done; + + bool _append_task; + bool _registered_to_owner; + mutable bool _retrieved_exception; + + friend class Extension; public: static TypeHandle get_class_type() { diff --git a/panda/src/express/config_express.N b/panda/src/express/config_express.N index 454c630ed2..e68b43fbfd 100644 --- a/panda/src/express/config_express.N +++ b/panda/src/express/config_express.N @@ -1,60 +1,4 @@ -defconstruct TypeHandle TypeHandle(TypeHandle::none()) - -forcetype PandaSystem -forcetype DSearchPath -forcetype DSearchPath::Results -forcetype ExecutionEnvironment -forcetype TextEncoder -forcetype Filename -forcetype GlobPattern -forcetype Notify -forcetype NotifyCategory -forcetype NotifySeverity -forcetype TypedObject -forcetype TypeHandle -forcetype TypeRegistry -forcetype StreamReader -forcetype StreamWriter -forcetype NeverFreeMemory -forcetype IFileStream -forcetype OFileStream -forcetype FileStream -forcetype IDecryptStream -forcetype OEncryptStream -forcetype LineStream - -forcetype ofstream -forcetype ifstream -forcetype fstream - forcetype DConfig -forcetype ConfigFlags -forcetype ConfigPage -forcetype ConfigPageManager -forcetype ConfigDeclaration -forcetype ConfigVariableCore -forcetype ConfigVariable -forcetype ConfigVariableBase -forcetype ConfigVariableBool -forcetype ConfigVariableDouble -forcetype ConfigVariableFilename -forcetype ConfigVariableInt -forcetype ConfigVariableInt64 -forcetype ConfigVariableList -forcetype ConfigVariableManager -forcetype ConfigVariableSearchPath -forcetype ConfigVariableString - -forcetype ios_base -forcetype ios -forcetype istream -forcetype ostream -forcetype iostream - -forcetype StreamWrapperBase -forcetype IStreamWrapper -forcetype OStreamWrapper -forcetype StreamWrapper forcetype PTA_uchar forcetype CPTA_uchar diff --git a/panda/src/express/datagramIterator.I b/panda/src/express/datagramIterator.I index 6f85656b47..cefde399f2 100644 --- a/panda/src/express/datagramIterator.I +++ b/panda/src/express/datagramIterator.I @@ -30,36 +30,13 @@ DatagramIterator(const Datagram &datagram, size_t offset) : nassertv(_current_index <= _datagram->get_length()); } -/** - * - */ -INLINE DatagramIterator:: -DatagramIterator(const DatagramIterator ©) : - _datagram(copy._datagram), - _current_index(copy._current_index) { -} - -/** - * - */ -INLINE void DatagramIterator:: -operator = (const DatagramIterator ©) { - _datagram = copy._datagram; - _current_index = copy._current_index; -} /** * direct Assignment to a Datagram */ -INLINE void DatagramIterator::assign(Datagram &datagram, size_t offset) -{ - _datagram =&datagram; - _current_index = offset; -} -/** - * - */ -INLINE DatagramIterator:: -~DatagramIterator() { +INLINE void DatagramIterator:: +assign(Datagram &datagram, size_t offset) { + _datagram = &datagram; + _current_index = offset; } // Various ways to get data and increment the iterator... Cut-and-paste-orama diff --git a/panda/src/express/datagramIterator.h b/panda/src/express/datagramIterator.h index b3a94c6670..7126901324 100644 --- a/panda/src/express/datagramIterator.h +++ b/panda/src/express/datagramIterator.h @@ -31,9 +31,6 @@ public: PUBLISHED: INLINE DatagramIterator(); INLINE DatagramIterator(const Datagram &datagram, size_t offset = 0); - INLINE DatagramIterator(const DatagramIterator ©); - INLINE void operator = (const DatagramIterator ©); - INLINE ~DatagramIterator(); INLINE bool get_bool(); INLINE int8_t get_int8(); diff --git a/panda/src/express/datagramSink.h b/panda/src/express/datagramSink.h index 0c1b74449c..c72cf7595d 100644 --- a/panda/src/express/datagramSink.h +++ b/panda/src/express/datagramSink.h @@ -40,6 +40,10 @@ PUBLISHED: virtual const Filename &get_filename(); virtual const FileReference *get_file(); virtual streampos get_file_pos(); + + MAKE_PROPERTY(filename, get_filename); + MAKE_PROPERTY(file, get_file); + MAKE_PROPERTY(file_pos, get_file_pos); }; #include "datagramSink.I" diff --git a/panda/src/express/hashGeneratorBase.I b/panda/src/express/hashGeneratorBase.I index f6a65c756b..58bf0b8344 100644 --- a/panda/src/express/hashGeneratorBase.I +++ b/panda/src/express/hashGeneratorBase.I @@ -19,13 +19,6 @@ HashGeneratorBase() { _hash = 0; } -/** - * - */ -INLINE HashGeneratorBase:: -~HashGeneratorBase() { -} - /** * Returns the hash number generated. */ diff --git a/panda/src/express/hashGeneratorBase.h b/panda/src/express/hashGeneratorBase.h index 97ddb7fe08..86145a74df 100644 --- a/panda/src/express/hashGeneratorBase.h +++ b/panda/src/express/hashGeneratorBase.h @@ -30,7 +30,6 @@ class EXPCL_PANDAEXPRESS HashGeneratorBase { public: INLINE HashGeneratorBase(); - INLINE ~HashGeneratorBase(); INLINE size_t get_hash() const; diff --git a/panda/src/express/memoryInfo.h b/panda/src/express/memoryInfo.h index 238cec05e3..c65b890887 100644 --- a/panda/src/express/memoryInfo.h +++ b/panda/src/express/memoryInfo.h @@ -75,6 +75,9 @@ private: #include "memoryInfo.I" +#else +class MemoryInfo; + #endif // DO_MEMORY_USAGE #endif diff --git a/panda/src/express/memoryUsage.I b/panda/src/express/memoryUsage.I index 619da5a736..a4c2447189 100644 --- a/panda/src/express/memoryUsage.I +++ b/panda/src/express/memoryUsage.I @@ -16,9 +16,13 @@ * to true, indicating that this class will be in effect. If this returns * false, the user has indicated not to do any of this. */ -INLINE bool MemoryUsage:: +ALWAYS_INLINE bool MemoryUsage:: get_track_memory_usage() { +#ifdef DO_MEMORY_USAGE return get_global_ptr()->_track_memory_usage; +#else + return false; +#endif } /** @@ -26,7 +30,19 @@ get_track_memory_usage() { */ INLINE void MemoryUsage:: record_pointer(ReferenceCount *ptr) { +#ifdef DO_MEMORY_USAGE get_global_ptr()->ns_record_pointer(ptr); +#endif +} + +/** + * Indicates that the given pointer has been recently allocated. + */ +INLINE void MemoryUsage:: +record_pointer(void *ptr, TypeHandle type) { +#ifdef DO_MEMORY_USAGE + get_global_ptr()->ns_record_pointer(ptr, type); +#endif } /** @@ -37,7 +53,9 @@ record_pointer(ReferenceCount *ptr) { */ INLINE void MemoryUsage:: update_type(ReferenceCount *ptr, TypeHandle type) { - get_global_ptr()->ns_update_type(ptr, type); +#ifdef DO_MEMORY_USAGE + get_global_ptr()->ns_update_type((void *)ptr, type); +#endif } /** @@ -48,7 +66,21 @@ update_type(ReferenceCount *ptr, TypeHandle type) { */ INLINE void MemoryUsage:: update_type(ReferenceCount *ptr, TypedObject *typed_ptr) { - get_global_ptr()->ns_update_type(ptr, typed_ptr); +#ifdef DO_MEMORY_USAGE + get_global_ptr()->ns_update_type((void *)ptr, typed_ptr); +#endif +} + +/** + * Associates the indicated type with the given pointer. This should be + * called by functions (e.g. the constructor) that know more specifically + * what type of thing we've got. + */ +INLINE void MemoryUsage:: +update_type(void *ptr, TypeHandle type) { +#ifdef DO_MEMORY_USAGE + get_global_ptr()->ns_update_type(ptr, type); +#endif } /** @@ -56,7 +88,9 @@ update_type(ReferenceCount *ptr, TypedObject *typed_ptr) { */ INLINE void MemoryUsage:: remove_pointer(ReferenceCount *ptr) { +#ifdef DO_MEMORY_USAGE get_global_ptr()->ns_remove_pointer(ptr); +#endif } /** @@ -65,7 +99,11 @@ remove_pointer(ReferenceCount *ptr) { */ INLINE bool MemoryUsage:: is_tracking() { +#ifdef DO_MEMORY_USAGE return get_global_ptr()->_track_memory_usage; +#else + return false; +#endif } /** @@ -75,7 +113,11 @@ is_tracking() { */ INLINE bool MemoryUsage:: is_counting() { +#ifdef DO_MEMORY_USAGE return get_global_ptr()->_count_memory_usage; +#else + return false; +#endif } /** @@ -84,7 +126,11 @@ is_counting() { */ INLINE size_t MemoryUsage:: get_current_cpp_size() { +#ifdef DO_MEMORY_USAGE return get_global_ptr()->_current_cpp_size; +#else + return 0; +#endif } /** @@ -93,7 +139,11 @@ get_current_cpp_size() { */ INLINE size_t MemoryUsage:: get_total_cpp_size() { +#ifdef DO_MEMORY_USAGE return get_global_ptr()->_total_cpp_size; +#else + return 0; +#endif } /** @@ -102,7 +152,11 @@ get_total_cpp_size() { */ INLINE size_t MemoryUsage:: get_panda_heap_single_size() { +#ifdef DO_MEMORY_USAGE return (size_t)AtomicAdjust::get(get_global_ptr()->_total_heap_single_size); +#else + return 0; +#endif } /** @@ -111,7 +165,11 @@ get_panda_heap_single_size() { */ INLINE size_t MemoryUsage:: get_panda_heap_array_size() { +#ifdef DO_MEMORY_USAGE return (size_t)AtomicAdjust::get(get_global_ptr()->_total_heap_array_size); +#else + return 0; +#endif } /** @@ -121,7 +179,7 @@ get_panda_heap_array_size() { */ INLINE size_t MemoryUsage:: get_panda_heap_overhead() { -#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2) +#if defined(DO_MEMORY_USAGE) && (defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)) MemoryUsage *mu = get_global_ptr(); return (size_t)(AtomicAdjust::get(mu->_requested_heap_size) - AtomicAdjust::get(mu->_total_heap_single_size) - AtomicAdjust::get(mu->_total_heap_array_size)); #else @@ -135,7 +193,11 @@ get_panda_heap_overhead() { */ INLINE size_t MemoryUsage:: get_panda_mmap_size() { +#ifdef DO_MEMORY_USAGE return (size_t)AtomicAdjust::get(get_global_ptr()->_total_mmap_size); +#else + return 0; +#endif } /** @@ -152,6 +214,7 @@ get_panda_mmap_size() { */ INLINE size_t MemoryUsage:: get_external_size() { +#ifdef DO_MEMORY_USAGE MemoryUsage *mu = get_global_ptr(); if (mu->_count_memory_usage) { // We can only possibly know this with memory counting, which tracks every @@ -169,6 +232,9 @@ get_external_size() { } else { return 0; } +#else + return 0; +#endif } /** @@ -177,6 +243,7 @@ get_external_size() { */ INLINE size_t MemoryUsage:: get_total_size() { +#ifdef DO_MEMORY_USAGE MemoryUsage *mu = get_global_ptr(); if (mu->_count_memory_usage) { return mu->_total_size + (size_t)mu->_requested_heap_size; @@ -187,6 +254,9 @@ get_total_size() { return (size_t)(AtomicAdjust::get(mu->_total_heap_single_size) + AtomicAdjust::get(mu->_total_heap_array_size)); #endif } +#else + return 0; +#endif } /** @@ -194,7 +264,11 @@ get_total_size() { */ INLINE int MemoryUsage:: get_num_pointers() { +#ifdef DO_MEMORY_USAGE return get_global_ptr()->ns_get_num_pointers(); +#else + return 0; +#endif } /** @@ -203,7 +277,9 @@ get_num_pointers() { */ INLINE void MemoryUsage:: get_pointers(MemoryUsagePointers &result) { +#ifdef DO_MEMORY_USAGE get_global_ptr()->ns_get_pointers(result); +#endif } /** @@ -212,7 +288,9 @@ get_pointers(MemoryUsagePointers &result) { */ INLINE void MemoryUsage:: get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) { +#ifdef DO_MEMORY_USAGE get_global_ptr()->ns_get_pointers_of_type(result, type); +#endif } /** @@ -221,7 +299,9 @@ get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) { */ INLINE void MemoryUsage:: get_pointers_of_age(MemoryUsagePointers &result, double from, double to) { +#ifdef DO_MEMORY_USAGE get_global_ptr()->ns_get_pointers_of_age(result, from, to); +#endif } /** @@ -242,7 +322,9 @@ get_pointers_of_age(MemoryUsagePointers &result, double from, double to) { */ INLINE void MemoryUsage:: get_pointers_with_zero_count(MemoryUsagePointers &result) { +#ifdef DO_MEMORY_USAGE get_global_ptr()->ns_get_pointers_with_zero_count(result); +#endif } /** @@ -253,7 +335,9 @@ get_pointers_with_zero_count(MemoryUsagePointers &result) { */ INLINE void MemoryUsage:: freeze() { +#ifdef DO_MEMORY_USAGE get_global_ptr()->ns_freeze(); +#endif } /** @@ -261,7 +345,9 @@ freeze() { */ INLINE void MemoryUsage:: show_current_types() { +#ifdef DO_MEMORY_USAGE get_global_ptr()->ns_show_current_types(); +#endif } /** @@ -270,7 +356,9 @@ show_current_types() { */ INLINE void MemoryUsage:: show_trend_types() { +#ifdef DO_MEMORY_USAGE get_global_ptr()->ns_show_trend_types(); +#endif } /** @@ -278,7 +366,9 @@ show_trend_types() { */ INLINE void MemoryUsage:: show_current_ages() { +#ifdef DO_MEMORY_USAGE get_global_ptr()->ns_show_current_ages(); +#endif } /** @@ -287,7 +377,9 @@ show_current_ages() { */ INLINE void MemoryUsage:: show_trend_ages() { +#ifdef DO_MEMORY_USAGE get_global_ptr()->ns_show_trend_ages(); +#endif } /** @@ -295,11 +387,18 @@ show_trend_ages() { */ INLINE MemoryUsage *MemoryUsage:: get_global_ptr() { - if (_global_ptr == (MemoryUsage *)NULL) { - init_memory_hook(); - _global_ptr = new MemoryUsage(*memory_hook); - memory_hook = _global_ptr; +#ifdef DO_MEMORY_USAGE +#ifdef __GNUC__ + // Tell the compiler that this is an unlikely branch. + if (__builtin_expect(_global_ptr == nullptr, 0)) { +#else + if (_global_ptr == nullptr) { +#endif + init_memory_usage(); } return _global_ptr; +#else + return nullptr; +#endif } diff --git a/panda/src/express/memoryUsage.cxx b/panda/src/express/memoryUsage.cxx index 1aea4fe825..178de476fa 100644 --- a/panda/src/express/memoryUsage.cxx +++ b/panda/src/express/memoryUsage.cxx @@ -12,9 +12,6 @@ */ #include "memoryUsage.h" - -#ifdef DO_MEMORY_USAGE - #include "memoryUsagePointers.h" #include "trueClock.h" #include "typedReferenceCount.h" @@ -45,16 +42,17 @@ double MemoryUsage::AgeHistogram::_cutoff[MemoryUsage::AgeHistogram::num_buckets 60.0, }; - /** * Adds a single entry to the histogram. */ void MemoryUsage::TypeHistogram:: add_info(TypeHandle type, MemoryInfo *info) { +#ifdef DO_MEMORY_USAGE _counts[type].add_info(info); +#endif } - +#ifdef DO_MEMORY_USAGE // This class is a temporary class used only in TypeHistogram::show(), below, // to sort the types in descending order by counts. class TypeHistogramCountSorter { @@ -71,12 +69,14 @@ public: MemoryUsagePointerCounts _count; TypeHandle _type; }; +#endif /** * Shows the contents of the histogram to nout. */ void MemoryUsage::TypeHistogram:: show() const { +#ifdef DO_MEMORY_USAGE // First, copy the relevant information to a vector so we can sort by // counts. Don't use a pvector. typedef vector CountSorter; @@ -99,6 +99,7 @@ show() const { } nout << " : " << (*vi)._count << "\n"; } +#endif } /** @@ -122,9 +123,11 @@ AgeHistogram() { */ void MemoryUsage::AgeHistogram:: add_info(double age, MemoryInfo *info) { +#ifdef DO_MEMORY_USAGE int bucket = choose_bucket(age); nassertv(bucket >= 0 && bucket < num_buckets); _counts[bucket].add_info(info); +#endif } /** @@ -132,6 +135,7 @@ add_info(double age, MemoryInfo *info) { */ void MemoryUsage::AgeHistogram:: show() const { +#ifdef DO_MEMORY_USAGE for (int i = 0; i < num_buckets - 1; i++) { nout << _cutoff[i] << " to " << _cutoff[i + 1] << " seconds old : "; _counts[i].output(nout); @@ -140,6 +144,7 @@ show() const { nout << _cutoff[num_buckets - 1] << " seconds old and up : "; _counts[num_buckets - 1].output(nout); nout << "\n"; +#endif } /** @@ -147,9 +152,11 @@ show() const { */ void MemoryUsage::AgeHistogram:: clear() { +#ifdef DO_MEMORY_USAGE for (int i = 0; i < num_buckets; i++) { _counts[i].clear(); } +#endif } /** @@ -157,6 +164,7 @@ clear() { */ int MemoryUsage::AgeHistogram:: choose_bucket(double age) const { +#ifdef DO_MEMORY_USAGE for (int i = num_buckets - 1; i >= 0; i--) { if (age >= _cutoff[i]) { return i; @@ -164,6 +172,7 @@ choose_bucket(double age) const { } express_cat.error() << "No suitable bucket for age " << age << "\n"; +#endif return 0; } @@ -173,6 +182,7 @@ choose_bucket(double age) const { */ void *MemoryUsage:: heap_alloc_single(size_t size) { +#ifdef DO_MEMORY_USAGE void *ptr; if (_recursion_protect) { @@ -202,6 +212,9 @@ heap_alloc_single(size_t size) { } return ptr; +#else + return MemoryHook::heap_alloc_single(size); +#endif } /** @@ -209,6 +222,7 @@ heap_alloc_single(size_t size) { */ void MemoryUsage:: heap_free_single(void *ptr) { +#ifdef DO_MEMORY_USAGE if (_recursion_protect) { if (express_cat.is_spam()) { express_cat.spam() @@ -231,6 +245,9 @@ heap_free_single(void *ptr) { MemoryHook::heap_free_single(ptr); } } +#else + MemoryHook::heap_free_single(ptr); +#endif } /** @@ -239,6 +256,7 @@ heap_free_single(void *ptr) { */ void *MemoryUsage:: heap_alloc_array(size_t size) { +#ifdef DO_MEMORY_USAGE void *ptr; if (_recursion_protect) { @@ -268,6 +286,9 @@ heap_alloc_array(size_t size) { } return ptr; +#else + return MemoryHook::heap_alloc_array(size); +#endif } /** @@ -275,6 +296,7 @@ heap_alloc_array(size_t size) { */ void *MemoryUsage:: heap_realloc_array(void *ptr, size_t size) { +#ifdef DO_MEMORY_USAGE if (_recursion_protect) { ptr = MemoryHook::heap_realloc_array(ptr, size); if (express_cat.is_spam()) { @@ -303,6 +325,9 @@ heap_realloc_array(void *ptr, size_t size) { } return ptr; +#else + return MemoryHook::heap_realloc_array(ptr, size); +#endif } /** @@ -310,6 +335,7 @@ heap_realloc_array(void *ptr, size_t size) { */ void MemoryUsage:: heap_free_array(void *ptr) { +#ifdef DO_MEMORY_USAGE if (_recursion_protect) { if (express_cat.is_spam()) { express_cat.spam() @@ -332,6 +358,9 @@ heap_free_array(void *ptr) { MemoryHook::heap_free_array(ptr); } } +#else + MemoryHook::heap_free_array(ptr); +#endif } /** @@ -343,6 +372,7 @@ heap_free_array(void *ptr) { */ void MemoryUsage:: mark_pointer(void *ptr, size_t size, ReferenceCount *ref_ptr) { +#ifdef DO_MEMORY_USAGE if (_recursion_protect || !_track_memory_usage) { return; } @@ -391,6 +421,7 @@ mark_pointer(void *ptr, size_t size, ReferenceCount *ref_ptr) { // We're removing this pointer from use. ns_remove_void_pointer(ptr); } +#endif } #if (defined(WIN32_VC) || defined (WIN64_VC))&& defined(_DEBUG) @@ -430,7 +461,23 @@ win32_malloc_hook(int alloc_type, void *ptr, * */ MemoryUsage:: -MemoryUsage(const MemoryHook ©) : MemoryHook(copy) { +MemoryUsage(const MemoryHook ©) : + MemoryHook(copy), + _info_set_dirty(false), + _freeze_index(0), + _count(0), + _current_cpp_size(0), + _total_cpp_size(0), + _total_size(0), + + _track_memory_usage(false), + _startup_track_memory_usage(false), + _count_memory_usage(false), + _report_memory_usage(false), + _report_memory_interval(0.0), + _last_report_time(0.0) { + +#ifdef DO_MEMORY_USAGE // We must get these variables here instead of in config_express.cxx, // because we need to know it at static init time, and who knows when the // code in config_express will be executed. @@ -456,9 +503,6 @@ MemoryUsage(const MemoryHook ©) : MemoryHook(copy) { ("report-memory-interval", 5.0, PRC_DESC("This is the interval, in seconds, for reports of currently allocated " "memory, when report-memory-usage is true.")); - _last_report_time = 0.0; - - _count_memory_usage = false; int64_t max_heap_size = ConfigVariableInt64 ("max-heap-size", 0, @@ -480,13 +524,24 @@ MemoryUsage(const MemoryHook ©) : MemoryHook(copy) { _CrtSetAllocHook(&win32_malloc_hook); _count_memory_usage = true; #endif +#endif // DO_MEMORY_USAGE +} - _info_set_dirty = false; - _freeze_index = 0; - _count = 0; - _current_cpp_size = 0; - _total_cpp_size = 0; - _total_size = 0; +/** + * Initializes the global MemoryUsage pointer. + */ +void MemoryUsage:: +init_memory_usage() { +#ifdef DO_MEMORY_USAGE + init_memory_hook(); + _global_ptr = new MemoryUsage(*memory_hook); + memory_hook = _global_ptr; +#else + // If this gets called, we still need to initialize the global_ptr with a + // stub even if we don't compile with memory usage tracking enabled, for ABI + // stability. However, we won't replace the memory hook. + _global_ptr = new MemoryUsage(*memory_hook); +#endif } /** @@ -497,6 +552,7 @@ MemoryUsage(const MemoryHook ©) : MemoryHook(copy) { */ void MemoryUsage:: overflow_heap_size() { +#ifdef DO_MEMORY_USAGE MemoryHook::overflow_heap_size(); express_cat.error() @@ -514,6 +570,7 @@ overflow_heap_size() { // Turn on spamful debugging. _track_memory_usage = true; _report_memory_usage = true; +#endif } /** @@ -521,12 +578,13 @@ overflow_heap_size() { */ void MemoryUsage:: ns_record_pointer(ReferenceCount *ptr) { +#ifdef DO_MEMORY_USAGE if (_track_memory_usage) { // We have to protect modifications to the table from recursive calls by // toggling _recursion_protect while we adjust it. _recursion_protect = true; pair insert_result = - _table.insert(Table::value_type((void *)ptr, (MemoryInfo *)NULL)); + _table.insert(Table::value_type((void *)ptr, nullptr)); // This shouldn't fail. assert(insert_result.first != _table.end()); @@ -565,6 +623,61 @@ ns_record_pointer(ReferenceCount *ptr) { } } } +#endif +} + + +/** + * Indicates that the given pointer has been recently allocated. + */ +void MemoryUsage:: +ns_record_pointer(void *ptr, TypeHandle type) { +#ifdef DO_MEMORY_USAGE + if (_track_memory_usage) { + // We have to protect modifications to the table from recursive calls by + // toggling _recursion_protect while we adjust it. + _recursion_protect = true; + pair insert_result = + _table.insert(Table::value_type(ptr, nullptr)); + + // This shouldn't fail. + assert(insert_result.first != _table.end()); + + if (insert_result.second) { + (*insert_result.first).second = new MemoryInfo; + _info_set_dirty = true; + ++_count; + } + + MemoryInfo *info = (*insert_result.first).second; + + // We should already have a pointer, thanks to a previous call to + // mark_pointer(). + nassertv(info->_void_ptr == ptr && info->_ref_ptr == nullptr); + + info->_void_ptr = ptr; + info->_static_type = type; + info->_dynamic_type = type; + info->_time = TrueClock::get_global_ptr()->get_long_time(); + info->_freeze_index = _freeze_index; + info->_flags |= MemoryInfo::F_reconsider_dynamic_type; + + // We close the recursion_protect flag all the way down here, so that we + // also protect ourselves against a possible recursive call in + // TrueClock::get_global_ptr(). + _recursion_protect = false; + + if (_report_memory_usage) { + double now = TrueClock::get_global_ptr()->get_long_time(); + if (now - _last_report_time > _report_memory_interval) { + _last_report_time = now; + express_cat.info() + << "*** Current memory usage: " << get_total_size() << "\n"; + show_current_types(); + } + } + } +#endif } /** @@ -574,7 +687,8 @@ ns_record_pointer(ReferenceCount *ptr) { * only that it's a "ReferenceCount". */ void MemoryUsage:: -ns_update_type(ReferenceCount *ptr, TypeHandle type) { +ns_update_type(void *ptr, TypeHandle type) { +#ifdef DO_MEMORY_USAGE if (_track_memory_usage) { Table::iterator ti; ti = _table.find(ptr); @@ -582,7 +696,7 @@ ns_update_type(ReferenceCount *ptr, TypeHandle type) { if (_startup_track_memory_usage) { express_cat.error() << "Attempt to update type to " << type << " for unrecorded pointer " - << (void *)ptr << "!\n"; + << ptr << "!\n"; nassertv(false); } return; @@ -595,6 +709,7 @@ ns_update_type(ReferenceCount *ptr, TypeHandle type) { consolidate_void_ptr(info); } +#endif } /** @@ -604,7 +719,8 @@ ns_update_type(ReferenceCount *ptr, TypeHandle type) { * the pointer as a TypedObject it doesn't need any more help. */ void MemoryUsage:: -ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) { +ns_update_type(void *ptr, TypedObject *typed_ptr) { +#ifdef DO_MEMORY_USAGE if (_track_memory_usage) { Table::iterator ti; ti = _table.find(ptr); @@ -613,7 +729,7 @@ ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) { express_cat.error() << "Attempt to update type to " << typed_ptr->get_type() << " for unrecorded pointer " - << (void *)ptr << "!\n"; + << ptr << "!\n"; } return; } @@ -624,6 +740,7 @@ ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) { consolidate_void_ptr(info); } +#endif } /** @@ -631,6 +748,7 @@ ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) { */ void MemoryUsage:: ns_remove_pointer(ReferenceCount *ptr) { +#ifdef DO_MEMORY_USAGE if (_track_memory_usage) { Table::iterator ti; ti = _table.find(ptr); @@ -695,6 +813,7 @@ ns_remove_pointer(ReferenceCount *ptr) { } } } +#endif } /** @@ -703,6 +822,7 @@ ns_remove_pointer(ReferenceCount *ptr) { */ void MemoryUsage:: ns_record_void_pointer(void *ptr, size_t size) { +#ifdef DO_MEMORY_USAGE if (_track_memory_usage) { if (express_cat.is_spam()) { express_cat.spam() @@ -714,7 +834,7 @@ ns_record_void_pointer(void *ptr, size_t size) { _recursion_protect = true; pair insert_result = - _table.insert(Table::value_type((void *)ptr, (MemoryInfo *)NULL)); + _table.insert(Table::value_type((void *)ptr, nullptr)); assert(insert_result.first != _table.end()); @@ -751,6 +871,7 @@ ns_record_void_pointer(void *ptr, size_t size) { // TrueClock::get_global_ptr(). _recursion_protect = false; } +#endif } /** @@ -758,6 +879,7 @@ ns_record_void_pointer(void *ptr, size_t size) { */ void MemoryUsage:: ns_remove_void_pointer(void *ptr) { +#ifdef DO_MEMORY_USAGE if (_track_memory_usage) { if (express_cat.is_spam()) { express_cat.spam() @@ -813,6 +935,7 @@ ns_remove_void_pointer(void *ptr) { _info_set_dirty = true; delete info; } +#endif } /** @@ -820,8 +943,12 @@ ns_remove_void_pointer(void *ptr) { */ int MemoryUsage:: ns_get_num_pointers() { +#ifdef DO_MEMORY_USAGE nassertr(_track_memory_usage, 0); return _count; +#else + return 0; +#endif } /** @@ -830,6 +957,7 @@ ns_get_num_pointers() { */ void MemoryUsage:: ns_get_pointers(MemoryUsagePointers &result) { +#ifdef DO_MEMORY_USAGE nassertv(_track_memory_usage); result.clear(); @@ -847,6 +975,7 @@ ns_get_pointers(MemoryUsagePointers &result) { now - info->_time); } } +#endif } /** @@ -855,6 +984,7 @@ ns_get_pointers(MemoryUsagePointers &result) { */ void MemoryUsage:: ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) { +#ifdef DO_MEMORY_USAGE nassertv(_track_memory_usage); result.clear(); @@ -876,6 +1006,7 @@ ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) { } } } +#endif } /** @@ -885,6 +1016,7 @@ ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) { void MemoryUsage:: ns_get_pointers_of_age(MemoryUsagePointers &result, double from, double to) { +#ifdef DO_MEMORY_USAGE nassertv(_track_memory_usage); result.clear(); @@ -905,6 +1037,7 @@ ns_get_pointers_of_age(MemoryUsagePointers &result, } } } +#endif } /** @@ -925,6 +1058,7 @@ ns_get_pointers_of_age(MemoryUsagePointers &result, */ void MemoryUsage:: ns_get_pointers_with_zero_count(MemoryUsagePointers &result) { +#ifdef DO_MEMORY_USAGE nassertv(_track_memory_usage); result.clear(); @@ -945,6 +1079,7 @@ ns_get_pointers_with_zero_count(MemoryUsagePointers &result) { } } } +#endif } /** @@ -955,11 +1090,13 @@ ns_get_pointers_with_zero_count(MemoryUsagePointers &result) { */ void MemoryUsage:: ns_freeze() { +#ifdef DO_MEMORY_USAGE _count = 0; _current_cpp_size = 0; _trend_types.clear(); _trend_ages.clear(); _freeze_index++; +#endif } /** @@ -967,6 +1104,7 @@ ns_freeze() { */ void MemoryUsage:: ns_show_current_types() { +#ifdef DO_MEMORY_USAGE nassertv(_track_memory_usage); TypeHistogram hist; @@ -984,6 +1122,7 @@ ns_show_current_types() { } hist.show(); _recursion_protect = false; +#endif } /** @@ -992,7 +1131,9 @@ ns_show_current_types() { */ void MemoryUsage:: ns_show_trend_types() { +#ifdef DO_MEMORY_USAGE _trend_types.show(); +#endif } /** @@ -1000,6 +1141,7 @@ ns_show_trend_types() { */ void MemoryUsage:: ns_show_current_ages() { +#ifdef DO_MEMORY_USAGE nassertv(_track_memory_usage); AgeHistogram hist; @@ -1016,6 +1158,7 @@ ns_show_current_ages() { hist.show(); _recursion_protect = false; +#endif } /** @@ -1027,6 +1170,8 @@ ns_show_trend_ages() { _trend_ages.show(); } +#ifdef DO_MEMORY_USAGE + /** * If the size information has not yet been determined for this pointer, * checks to see if it has possibly been recorded under the TypedObject @@ -1116,5 +1261,4 @@ refresh_info_set() { _info_set_dirty = false; } - #endif // DO_MEMORY_USAGE diff --git a/panda/src/express/memoryUsage.h b/panda/src/express/memoryUsage.h index b52f71f7ea..9742348cac 100644 --- a/panda/src/express/memoryUsage.h +++ b/panda/src/express/memoryUsage.h @@ -15,9 +15,6 @@ #define MEMORYUSAGE_H #include "pandabase.h" - -#ifdef DO_MEMORY_USAGE - #include "typedObject.h" #include "memoryInfo.h" #include "memoryUsagePointerCounts.h" @@ -33,18 +30,22 @@ class MemoryUsagePointers; * every such object currently allocated. * * When compiled with NDEBUG set, this entire class does nothing and compiles - * to nothing. + * to a stub. */ class EXPCL_PANDAEXPRESS MemoryUsage : public MemoryHook { public: - INLINE static bool get_track_memory_usage(); + ALWAYS_INLINE static bool get_track_memory_usage(); INLINE static void record_pointer(ReferenceCount *ptr); + INLINE static void record_pointer(void *ptr, TypeHandle type); INLINE static void update_type(ReferenceCount *ptr, TypeHandle type); INLINE static void update_type(ReferenceCount *ptr, TypedObject *typed_ptr); + INLINE static void update_type(void *ptr, TypeHandle type); INLINE static void remove_pointer(ReferenceCount *ptr); -public: +protected: + // These are not marked public, but they can be accessed via the MemoryHook + // base class. virtual void *heap_alloc_single(size_t size); virtual void heap_free_single(void *ptr); @@ -88,6 +89,19 @@ PUBLISHED: INLINE static void show_current_ages(); INLINE static void show_trend_ages(); +PUBLISHED: + MAKE_PROPERTY(tracking, is_tracking); + MAKE_PROPERTY(counting, is_counting); + MAKE_PROPERTY(current_cpp_size, get_current_cpp_size); + MAKE_PROPERTY(total_cpp_size, get_total_cpp_size); + + MAKE_PROPERTY(panda_heap_single_size, get_panda_heap_single_size); + MAKE_PROPERTY(panda_heap_array_size, get_panda_heap_array_size); + MAKE_PROPERTY(panda_heap_overhead, get_panda_heap_overhead); + MAKE_PROPERTY(panda_mmap_size, get_panda_mmap_size); + MAKE_PROPERTY(external_size, get_external_size); + MAKE_PROPERTY(total_size, get_total_size); + protected: virtual void overflow_heap_size(); @@ -95,9 +109,12 @@ private: MemoryUsage(const MemoryHook ©); INLINE static MemoryUsage *get_global_ptr(); + static void init_memory_usage(); + void ns_record_pointer(ReferenceCount *ptr); - void ns_update_type(ReferenceCount *ptr, TypeHandle type); - void ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr); + void ns_record_pointer(void *ptr, TypeHandle type); + void ns_update_type(void *ptr, TypeHandle type); + void ns_update_type(void *ptr, TypedObject *typed_ptr); void ns_remove_pointer(ReferenceCount *ptr); void ns_record_void_pointer(void *ptr, size_t size); @@ -118,8 +135,10 @@ private: void ns_show_current_ages(); void ns_show_trend_ages(); +#ifdef DO_MEMORY_USAGE void consolidate_void_ptr(MemoryInfo *info); void refresh_info_set(); +#endif static MemoryUsage *_global_ptr; @@ -194,6 +213,4 @@ private: #include "memoryUsage.I" -#endif // DO_MEMORY_USAGE - #endif diff --git a/panda/src/express/memoryUsagePointerCounts.cxx b/panda/src/express/memoryUsagePointerCounts.cxx index d218d57589..b91be61bcc 100644 --- a/panda/src/express/memoryUsagePointerCounts.cxx +++ b/panda/src/express/memoryUsagePointerCounts.cxx @@ -12,9 +12,6 @@ */ #include "memoryUsagePointerCounts.h" - -#ifdef DO_MEMORY_USAGE - #include "memoryInfo.h" /** @@ -22,6 +19,7 @@ */ void MemoryUsagePointerCounts:: add_info(MemoryInfo *info) { +#ifdef DO_MEMORY_USAGE _count++; if (info->is_size_known()) { @@ -29,6 +27,7 @@ add_info(MemoryInfo *info) { } else { _unknown_size_count++; } +#endif } /** @@ -36,6 +35,7 @@ add_info(MemoryInfo *info) { */ void MemoryUsagePointerCounts:: output(ostream &out) const { +#ifdef DO_MEMORY_USAGE out << _count << " pointers"; if (_unknown_size_count < _count) { out << ", "; @@ -48,6 +48,7 @@ output(ostream &out) const { out << " (" << _unknown_size_count << " of unknown size)"; } } +#endif } /** @@ -56,6 +57,7 @@ output(ostream &out) const { */ void MemoryUsagePointerCounts:: output_bytes(ostream &out, size_t size) { +#ifdef DO_MEMORY_USAGE if (size < 4 * 1024) { out << size << " bytes"; @@ -65,6 +67,5 @@ output_bytes(ostream &out, size_t size) { } else { out << size / (1024 * 1024) << " Mb"; } +#endif } - -#endif // DO_MEMORY_USAGE diff --git a/panda/src/express/memoryUsagePointerCounts.h b/panda/src/express/memoryUsagePointerCounts.h index 935538fdee..2d95052ad8 100644 --- a/panda/src/express/memoryUsagePointerCounts.h +++ b/panda/src/express/memoryUsagePointerCounts.h @@ -16,8 +16,6 @@ #include "pandabase.h" -#ifdef DO_MEMORY_USAGE - class MemoryInfo; /** @@ -55,6 +53,4 @@ INLINE ostream &operator << (ostream &out, const MemoryUsagePointerCounts &c); #include "memoryUsagePointerCounts.I" -#endif // DO_MEMORY_USAGE - #endif diff --git a/panda/src/express/memoryUsagePointers.cxx b/panda/src/express/memoryUsagePointers.cxx index fff8199c5a..dd412958da 100644 --- a/panda/src/express/memoryUsagePointers.cxx +++ b/panda/src/express/memoryUsagePointers.cxx @@ -12,9 +12,6 @@ */ #include "memoryUsagePointers.h" - -#ifdef DO_MEMORY_USAGE - #include "config_express.h" #include "referenceCount.h" #include "typedReferenceCount.h" @@ -38,7 +35,11 @@ MemoryUsagePointers:: */ size_t MemoryUsagePointers:: get_num_pointers() const { +#ifdef DO_MEMORY_USAGE return _entries.size(); +#else + return 0; +#endif } /** @@ -46,21 +47,26 @@ get_num_pointers() const { */ ReferenceCount *MemoryUsagePointers:: get_pointer(size_t n) const { - nassertr(n < get_num_pointers(), NULL); +#ifdef DO_MEMORY_USAGE + nassertr(n < get_num_pointers(), nullptr); return _entries[n]._ref_ptr; +#else + return nullptr; +#endif } /** * Returns the nth pointer of the set, typecast to a TypedObject if possible. * If the pointer is not a TypedObject or if the cast cannot be made, returns - * NULL. + * nullptr. */ TypedObject *MemoryUsagePointers:: get_typed_pointer(size_t n) const { - nassertr(n < get_num_pointers(), NULL); +#ifdef DO_MEMORY_USAGE + nassertr(n < get_num_pointers(), nullptr); TypedObject *typed_ptr = _entries[n]._typed_ptr; - if (typed_ptr != (TypedObject *)NULL) { + if (typed_ptr != nullptr) { return typed_ptr; } @@ -85,7 +91,8 @@ get_typed_pointer(size_t n) const { type.is_derived_from(TypedReferenceCount::get_class_type())) { return (TypedReferenceCount *)ref_ptr; } - return NULL; +#endif + return nullptr; } /** @@ -93,8 +100,12 @@ get_typed_pointer(size_t n) const { */ TypeHandle MemoryUsagePointers:: get_type(size_t n) const { +#ifdef DO_MEMORY_USAGE nassertr(n < get_num_pointers(), TypeHandle::none()); return _entries[n]._type; +#else + return TypeHandle::none(); +#endif } /** @@ -102,8 +113,12 @@ get_type(size_t n) const { */ string MemoryUsagePointers:: get_type_name(size_t n) const { +#ifdef DO_MEMORY_USAGE nassertr(n < get_num_pointers(), ""); return get_type(n).get_name(); +#else + return ""; +#endif } /** @@ -113,8 +128,12 @@ get_type_name(size_t n) const { */ double MemoryUsagePointers:: get_age(size_t n) const { +#ifdef DO_MEMORY_USAGE nassertr(n < get_num_pointers(), 0.0); return _entries[n]._age; +#else + return 0.0; +#endif } /** @@ -122,7 +141,9 @@ get_age(size_t n) const { */ void MemoryUsagePointers:: clear() { +#ifdef DO_MEMORY_USAGE _entries.clear(); +#endif } /** @@ -130,7 +151,9 @@ clear() { */ void MemoryUsagePointers:: output(ostream &out) const { +#ifdef DO_MEMORY_USAGE out << _entries.size() << " pointers."; +#endif } /** @@ -139,13 +162,12 @@ output(ostream &out) const { void MemoryUsagePointers:: add_entry(ReferenceCount *ref_ptr, TypedObject *typed_ptr, TypeHandle type, double age) { +#ifdef DO_MEMORY_USAGE // We can't safely add pointers with a zero reference count. They might be // statically-allocated or something, and if we try to add them they'll try // to destruct when the PointerTo later goes away. if (ref_ptr->get_ref_count() != 0) { _entries.push_back(Entry(ref_ptr, typed_ptr, type, age)); } +#endif } - - -#endif // DO_MEMORY_USAGE diff --git a/panda/src/express/memoryUsagePointers.h b/panda/src/express/memoryUsagePointers.h index 317910760b..344c2edc6b 100644 --- a/panda/src/express/memoryUsagePointers.h +++ b/panda/src/express/memoryUsagePointers.h @@ -15,9 +15,6 @@ #define MEMORYUSAGEPOINTERS_H #include "pandabase.h" - -#ifdef DO_MEMORY_USAGE - #include "typedObject.h" #include "pointerTo.h" #include "referenceCount.h" @@ -53,7 +50,9 @@ PUBLISHED: string get_type_name(size_t n) const; double get_age(size_t n) const; +#ifdef DO_MEMORY_USAGE EXTENSION(PyObject *get_python_pointer(size_t n) const); +#endif void clear(); @@ -94,6 +93,4 @@ INLINE ostream &operator << (ostream &out, const MemoryUsagePointers &mup) { #include "memoryUsagePointers.I" -#endif // MEMORY_USAGE_POINTERS - #endif diff --git a/panda/src/express/multifile.cxx b/panda/src/express/multifile.cxx index 2f7cda439b..0d98849efe 100644 --- a/panda/src/express/multifile.cxx +++ b/panda/src/express/multifile.cxx @@ -26,6 +26,8 @@ #include #include +#include "openSSLWrapper.h" + // This sequence of bytes begins each Multifile to identify it as a Multifile. const char Multifile::_header[] = "pmf\0\n\r"; const size_t Multifile::_header_size = 6; @@ -768,43 +770,6 @@ add_signature(const Filename &composite, const string &password) { } #endif // HAVE_OPENSSL -#ifdef HAVE_OPENSSL -/** - * Adds a new signature to the Multifile. This signature associates the - * indicated certificate with the current contents of the Multifile. When the - * Multifile is read later, the signature will still be present only if the - * Multifile is unchanged; any subsequent changes to the Multifile will - * automatically invalidate and remove the signature. - * - * If chain is non-NULL, it represents the certificate chain that validates - * the certificate. - * - * The specified private key must match the certificate, and the Multifile - * must be open in read-write mode. The private key is only used for - * generating the signature; it is not written to the Multifile and cannot be - * retrieved from the Multifile later. (However, the certificate *can* be - * retrieved from the Multifile later, to identify the entity that created the - * signature.) - * - * This implicitly causes a repack() operation if one is needed. Returns true - * on success, false on failure. - */ -bool Multifile:: -add_signature(X509 *certificate, STACK_OF(X509) *chain, EVP_PKEY *pkey) { - // Convert the certificate and chain into our own CertChain structure. - CertChain cert_chain; - cert_chain.push_back(CertRecord(certificate)); - if (chain != NULL) { - int num = sk_X509_num(chain); - for (int i = 0; i < num; ++i) { - cert_chain.push_back(CertRecord((X509 *)sk_X509_value(chain, i))); - } - } - - return add_signature(cert_chain, pkey); -} -#endif // HAVE_OPENSSL - #ifdef HAVE_OPENSSL /** * Adds a new signature to the Multifile. This signature associates the diff --git a/panda/src/express/multifile.h b/panda/src/express/multifile.h index 578ac70d6d..f290c98602 100644 --- a/panda/src/express/multifile.h +++ b/panda/src/express/multifile.h @@ -24,7 +24,11 @@ #include "indirectLess.h" #include "referenceCount.h" #include "pvector.h" -#include "openSSLWrapper.h" + +#ifdef HAVE_OPENSSL +typedef struct x509_st X509; +typedef struct evp_pkey_st EVP_PKEY; +#endif /** * A file that contains a set of files. @@ -132,6 +136,7 @@ PUBLISHED: void ls(ostream &out = cout) const; static INLINE string get_magic_number(); + MAKE_PROPERTY(magic_number, get_magic_number); void set_header_prefix(const string &header_prefix); INLINE const string &get_header_prefix() const; @@ -148,7 +153,6 @@ public: }; typedef pvector CertChain; - bool add_signature(X509 *certificate, STACK_OF(X509) *chain, EVP_PKEY *pkey); bool add_signature(const CertChain &chain, EVP_PKEY *pkey); const CertChain &get_signature(int n) const; diff --git a/panda/src/express/namable.I b/panda/src/express/namable.I index e1be80d872..75aa846087 100644 --- a/panda/src/express/namable.I +++ b/panda/src/express/namable.I @@ -20,35 +20,6 @@ Namable(const string &initial_name) : { } -/** - * - */ -INLINE Namable:: -Namable(const Namable ©) : - _name(copy._name) -{ -} - -/** - * - */ -INLINE Namable &Namable:: -operator = (const Namable &other) { - _name = other._name; - return *this; -} - -#ifdef USE_MOVE_SEMANTICS -/** - * - */ -INLINE Namable &Namable:: -operator = (Namable &&other) NOEXCEPT { - _name = MOVE(other._name); - return *this; -} -#endif - /** * */ diff --git a/panda/src/express/namable.h b/panda/src/express/namable.h index 435426fc1b..19a712d9d3 100644 --- a/panda/src/express/namable.h +++ b/panda/src/express/namable.h @@ -26,12 +26,6 @@ class EXPCL_PANDAEXPRESS Namable : public MemoryBase { PUBLISHED: INLINE explicit Namable(const string &initial_name = ""); - INLINE Namable(const Namable ©); - INLINE Namable &operator = (const Namable &other); - -#ifdef USE_MOVE_SEMANTICS - INLINE Namable &operator = (Namable &&other) NOEXCEPT; -#endif INLINE void set_name(const string &name); INLINE void clear_name(); diff --git a/panda/src/express/nodePointerTo.I b/panda/src/express/nodePointerTo.I index e56dd902b6..5485adcaaa 100644 --- a/panda/src/express/nodePointerTo.I +++ b/panda/src/express/nodePointerTo.I @@ -33,16 +33,6 @@ NodePointerTo(const NodePointerTo ©) : } #endif // CPPPARSER -#ifndef CPPPARSER -/** - * - */ -template -INLINE NodePointerTo:: -~NodePointerTo() { -} -#endif // CPPPARSER - #ifdef USE_MOVE_SEMANTICS #ifndef CPPPARSER /** @@ -177,16 +167,6 @@ NodeConstPointerTo(const NodeConstPointerTo ©) : } #endif // CPPPARSER -#ifndef CPPPARSER -/** - * - */ -template -INLINE NodeConstPointerTo:: -~NodeConstPointerTo() { -} -#endif // CPPPARSER - #ifdef USE_MOVE_SEMANTICS #ifndef CPPPARSER /** diff --git a/panda/src/express/nodePointerTo.h b/panda/src/express/nodePointerTo.h index 7fc773852d..ca9832d424 100644 --- a/panda/src/express/nodePointerTo.h +++ b/panda/src/express/nodePointerTo.h @@ -31,7 +31,6 @@ public: typedef TYPENAME NodePointerToBase::To To; INLINE NodePointerTo(To *ptr = (To *)NULL); INLINE NodePointerTo(const NodePointerTo ©); - INLINE ~NodePointerTo(); #ifdef USE_MOVE_SEMANTICS INLINE NodePointerTo(NodePointerTo &&from) NOEXCEPT; @@ -66,7 +65,6 @@ public: INLINE NodeConstPointerTo(const To *ptr = (const To *)NULL); INLINE NodeConstPointerTo(const NodePointerTo ©); INLINE NodeConstPointerTo(const NodeConstPointerTo ©); - INLINE ~NodeConstPointerTo(); #ifdef USE_MOVE_SEMANTICS INLINE NodeConstPointerTo(NodePointerTo &&from) NOEXCEPT; diff --git a/panda/src/express/ordered_vector.I b/panda/src/express/ordered_vector.I index 68a70b39a3..287998b85c 100644 --- a/panda/src/express/ordered_vector.I +++ b/panda/src/express/ordered_vector.I @@ -33,36 +33,6 @@ ordered_vector(const Compare &compare, TypeHandle type_handle) : { } -/** - * - */ -template -INLINE ordered_vector:: -ordered_vector(const ordered_vector ©) : - _compare(copy._compare), - _vector(copy._vector) -{ -} - -/** - * - */ -template -INLINE ordered_vector &ordered_vector:: -operator = (const ordered_vector ©) { - _compare = copy._compare; - _vector = copy._vector; - return *this; -} - -/** - * - */ -template -INLINE ordered_vector:: -~ordered_vector() { -} - /** * Returns the iterator that marks the first element in the ordered vector. */ @@ -139,6 +109,44 @@ rend() const { return _vector.rend(); } +/** + * Returns the iterator that marks the first element in the ordered vector. + */ +template +INLINE TYPENAME ordered_vector::CONST_ITERATOR ordered_vector:: +cbegin() const { + return _vector.begin(); +} + +/** + * Returns the iterator that marks the end of the ordered vector. + */ +template +INLINE TYPENAME ordered_vector::CONST_ITERATOR ordered_vector:: +cend() const { + return _vector.end(); +} + +/** + * Returns the iterator that marks the first element in the ordered vector, + * when viewed in reverse order. + */ +template +INLINE TYPENAME ordered_vector::CONST_REVERSE_ITERATOR ordered_vector:: +crbegin() const { + return _vector.rbegin(); +} + +/** + * Returns the iterator that marks the end of the ordered vector, when viewed + * in reverse order. + */ +template +INLINE TYPENAME ordered_vector::CONST_REVERSE_ITERATOR ordered_vector:: +crend() const { + return _vector.rend(); +} + /** * Returns the nth element. */ @@ -157,6 +165,54 @@ operator [] (TYPENAME ordered_vector::SIZE_TYPE n) const { return _vector[n]; } +/** + * Returns a reference to the first element. + */ +template +INLINE TYPENAME ordered_vector::REFERENCE ordered_vector:: +front() { +#ifdef _DEBUG + assert(!_vector.empty()); +#endif + return _vector[0]; +} + +/** + * Returns a const reference to the first element. + */ +template +INLINE TYPENAME ordered_vector::CONST_REFERENCE ordered_vector:: +front() const { +#ifdef _DEBUG + assert(!_vector.empty()); +#endif + return _vector[0]; +} + +/** + * Returns a reference to the first element. + */ +template +INLINE TYPENAME ordered_vector::REFERENCE ordered_vector:: +back() { +#ifdef _DEBUG + assert(!_vector.empty()); +#endif + return _vector[_vector.size() - 1]; +} + +/** + * Returns a const reference to the last element. + */ +template +INLINE TYPENAME ordered_vector::CONST_REFERENCE ordered_vector:: +back() const { +#ifdef _DEBUG + assert(!_vector.empty()); +#endif + return _vector[_vector.size() - 1]; +} + /** * Returns the number of elements in the ordered vector. */ @@ -560,6 +616,18 @@ push_back(const value_type &key) { _vector.push_back(key); } +/** + * Adds the new element to the end of the vector without regard for proper + * sorting. This is a bad idea to do except to populate the vector the first + * time; be sure to call sort() after you have added all the elements. + */ +template +INLINE void ordered_vector:: +push_back(value_type &&key) { + TAU_PROFILE("ordered_vector::push_back()", " ", TAU_USER); + _vector.push_back(move(key)); +} + /** * Removes the last element at the end of the vector. */ @@ -636,26 +704,6 @@ ov_set(const Compare &compare, TypeHandle type_handle) : { } -/** - * - */ -template -INLINE ov_set:: -ov_set(const ov_set ©) : - ordered_vector(copy) -{ -} - -/** - * - */ -template -INLINE ov_set &ov_set:: -operator = (const ov_set ©) { - ordered_vector::operator = (copy); - return *this; -} - /** * Maps to insert_unique(). */ @@ -713,26 +761,6 @@ ov_multiset(const Compare &compare, TypeHandle type_handle) : { } -/** - * - */ -template -INLINE ov_multiset:: -ov_multiset(const ov_multiset ©) : - ordered_vector(copy) -{ -} - -/** - * - */ -template -INLINE ov_multiset &ov_multiset:: -operator = (const ov_multiset ©) { - ordered_vector::operator = (copy); - return *this; -} - /** * Maps to insert_nonunique(). */ diff --git a/panda/src/express/ordered_vector.h b/panda/src/express/ordered_vector.h index aa84cc4511..e9e3a03693 100644 --- a/panda/src/express/ordered_vector.h +++ b/panda/src/express/ordered_vector.h @@ -135,9 +135,6 @@ public: INLINE ordered_vector(TypeHandle type_handle = ov_set_type_handle); INLINE ordered_vector(const Compare &compare, TypeHandle type_handle = ov_set_type_handle); - INLINE ordered_vector(const ordered_vector ©); - INLINE ordered_vector &operator = (const ordered_vector ©); - INLINE ~ordered_vector(); // Iterator access. INLINE ITERATOR begin(); @@ -150,10 +147,21 @@ public: INLINE CONST_REVERSE_ITERATOR rbegin() const; INLINE CONST_REVERSE_ITERATOR rend() const; + INLINE CONST_ITERATOR cbegin() const; + INLINE CONST_ITERATOR cend() const; + INLINE CONST_REVERSE_ITERATOR crbegin() const; + INLINE CONST_REVERSE_ITERATOR crend() const; + // Random access. INLINE reference operator [] (SIZE_TYPE n); INLINE const_reference operator [] (SIZE_TYPE n) const; + INLINE reference front(); + INLINE const_reference front() const; + + INLINE reference back(); + INLINE const_reference back() const; + // Size information. INLINE SIZE_TYPE size() const; INLINE SIZE_TYPE max_size() const; @@ -204,6 +212,7 @@ public: bool verify_list_nonunique() const; INLINE void push_back(const VALUE_TYPE &key); + INLINE void push_back(VALUE_TYPE &&key); INLINE void pop_back(); INLINE void resize(SIZE_TYPE n); INLINE void resize(SIZE_TYPE n, const VALUE_TYPE &value); @@ -265,8 +274,6 @@ public: INLINE ov_set(TypeHandle type_handle = ov_set_type_handle); INLINE ov_set(const Compare &compare, TypeHandle type_handle = ov_set_type_handle); - INLINE ov_set(const ov_set ©); - INLINE ov_set &operator = (const ov_set ©); INLINE ITERATOR insert(ITERATOR position, const VALUE_TYPE &key0); INLINE pair insert(const VALUE_TYPE &key0); @@ -288,8 +295,6 @@ public: INLINE ov_multiset(TypeHandle type_handle = ov_set_type_handle); INLINE ov_multiset(const Compare &compare, TypeHandle type_handle = ov_set_type_handle); - INLINE ov_multiset(const ov_multiset ©); - INLINE ov_multiset &operator = (const ov_multiset ©); INLINE ITERATOR insert(ITERATOR position, const VALUE_TYPE &key); INLINE ITERATOR insert(const VALUE_TYPE &key); diff --git a/panda/src/express/p3express_ext_composite.cxx b/panda/src/express/p3express_ext_composite.cxx index e034261ae3..ee104077cb 100644 --- a/panda/src/express/p3express_ext_composite.cxx +++ b/panda/src/express/p3express_ext_composite.cxx @@ -1,9 +1,4 @@ -#include "filename_ext.cxx" -#include "globPattern_ext.cxx" #include "memoryUsagePointers_ext.cxx" #include "ramfile_ext.cxx" -#include "streamReader_ext.cxx" -#include "streamWriter_ext.cxx" -#include "typeHandle_ext.cxx" #include "virtualFileSystem_ext.cxx" #include "virtualFile_ext.cxx" diff --git a/panda/src/express/patchfile.h b/panda/src/express/patchfile.h index c67b5a039e..c32c1d59e7 100644 --- a/panda/src/express/patchfile.h +++ b/panda/src/express/patchfile.h @@ -39,7 +39,7 @@ class EXPCL_PANDAEXPRESS Patchfile { PUBLISHED: Patchfile(); - Patchfile(PT(Buffer) buffer); + explicit Patchfile(PT(Buffer) buffer); ~Patchfile(); bool build(Filename file_orig, Filename file_new, Filename patch_name); diff --git a/panda/src/express/pointerTo.I b/panda/src/express/pointerTo.I index a3d842e374..62962a83e3 100644 --- a/panda/src/express/pointerTo.I +++ b/panda/src/express/pointerTo.I @@ -16,7 +16,7 @@ */ template ALWAYS_INLINE PointerTo:: -PointerTo(To *ptr) : PointerToBase(ptr) { +PointerTo(To *ptr) NOEXCEPT : PointerToBase(ptr) { } /** @@ -55,16 +55,8 @@ operator = (PointerTo &&from) NOEXCEPT { * */ template -INLINE PointerTo:: -~PointerTo() { -} - -/** - * - */ -template -INLINE TYPENAME PointerTo::To &PointerTo:: -operator *() const { +CONSTEXPR TYPENAME PointerTo::To &PointerTo:: +operator *() const NOEXCEPT { return *((To *)(this->_void_ptr)); } @@ -72,8 +64,8 @@ operator *() const { * */ template -INLINE TYPENAME PointerTo::To *PointerTo:: -operator -> () const { +CONSTEXPR TYPENAME PointerTo::To *PointerTo:: +operator -> () const NOEXCEPT { return (To *)(this->_void_ptr); } @@ -84,8 +76,8 @@ operator -> () const { * goes because either will be correct. */ template -INLINE PointerTo:: -operator T * () const { +CONSTEXPR PointerTo:: +operator T * () const NOEXCEPT { return (To *)(this->_void_ptr); } @@ -107,8 +99,8 @@ cheat() { * compiler problems, particularly for implicit upcasts. */ template -INLINE TYPENAME PointerTo::To *PointerTo:: -p() const { +CONSTEXPR TYPENAME PointerTo::To *PointerTo:: +p() const NOEXCEPT { return (To *)(this->_void_ptr); } @@ -137,7 +129,7 @@ operator = (const PointerTo ©) { */ template ALWAYS_INLINE ConstPointerTo:: -ConstPointerTo(const TYPENAME ConstPointerTo::To *ptr) : +ConstPointerTo(const TYPENAME ConstPointerTo::To *ptr) NOEXCEPT : PointerToBase((TYPENAME ConstPointerTo::To *)ptr) { } @@ -152,14 +144,6 @@ ConstPointerTo(const PointerTo ©) : { } -/** - * - */ -template -INLINE ConstPointerTo:: -~ConstPointerTo() { -} - /** * */ @@ -216,8 +200,8 @@ operator = (ConstPointerTo &&from) NOEXCEPT { * */ template -INLINE const TYPENAME ConstPointerTo::To &ConstPointerTo:: -operator *() const { +CONSTEXPR const TYPENAME ConstPointerTo::To &ConstPointerTo:: +operator *() const NOEXCEPT { return *((To *)(this->_void_ptr)); } @@ -225,8 +209,8 @@ operator *() const { * */ template -INLINE const TYPENAME ConstPointerTo::To *ConstPointerTo:: -operator -> () const { +CONSTEXPR const TYPENAME ConstPointerTo::To *ConstPointerTo:: +operator -> () const NOEXCEPT { return (To *)(this->_void_ptr); } @@ -237,8 +221,8 @@ operator -> () const { * don't care which way it goes because either will be correct. */ template -INLINE ConstPointerTo:: -operator const T * () const { +CONSTEXPR ConstPointerTo:: +operator const T * () const NOEXCEPT { return (To *)(this->_void_ptr); } @@ -260,8 +244,8 @@ cheat() { * around compiler problems, particularly for implicit upcasts. */ template -INLINE const TYPENAME ConstPointerTo::To *ConstPointerTo:: -p() const { +CONSTEXPR const TYPENAME ConstPointerTo::To *ConstPointerTo:: +p() const NOEXCEPT { return (To *)(this->_void_ptr); } diff --git a/panda/src/express/pointerTo.h b/panda/src/express/pointerTo.h index 50634f6473..62abdf8e57 100644 --- a/panda/src/express/pointerTo.h +++ b/panda/src/express/pointerTo.h @@ -14,35 +14,47 @@ #ifndef POINTERTO_H #define POINTERTO_H -/* +/** * This file defines the classes PointerTo and ConstPointerTo (and their * abbreviations, PT and CPT). These should be used in place of traditional - * C-style pointers wherever implicit reference counting is desired. The - * syntax is: instead of: PointerTo p; - * MyClass *p; PT(MyClass) p; ConstPointerTo p; const MyClass - * *p; CPT(MyClass) p; PointerTo and ConstPointerTo will automatically - * increment the object's reference count while the pointer is kept. When the - * PointerTo object is reassigned or goes out of scope, the reference count is - * automatically decremented. If the reference count reaches zero, the object - * is freed. Note that const PointerTo is different from + * C-style pointers wherever implicit reference counting is desired. + * + * The syntax is: instead of: + * + * PointerTo p; MyClass *p; + * PT(MyClass) p; + * + * ConstPointerTo p; const MyClass *p; + * CPT(MyClass) p; + * + * PointerTo and ConstPointerTo will automatically increment the object's + * reference count while the pointer is kept. When the PointerTo object is + * reassigned or goes out of scope, the reference count is automatically + * decremented. If the reference count reaches zero, the object is freed. + * + * Note that const PointerTo is different from * ConstPointerTo. A const PointerTo may not reassign its pointer, * but it may still modify the contents at that address. On the other hand, a * ConstPointerTo may reassign its pointer at will, but may not modify the - * contents. It is like the difference between (MyClass * const) and (const - * MyClass *). In order to use PointerTo, it is necessary that the thing - * pointed to--MyClass in the above example--either inherits from - * ReferenceCount, or is a proxy built with RefCountProxy or RefCountObj (see - * referenceCount.h). However, also see PointerToArray, which does not have - * this restriction. It is crucial that the PointerTo object is only used to - * refer to objects allocated from the free store, for which delete is a - * sensible thing to do. If you assign a PointerTo to an automatic variable - * (allocated from the stack, for instance), bad things will certainly happen - * when the reference count reaches zero and it tries to delete it. It's also - * important to remember that, as always, a virtual destructor is required if - * you plan to support polymorphism. That is, if you define a PointerTo to - * some base type, and assign to it instances of a class derived from that - * base class, the base class must have a virtual destructor in order to - * properly destruct the derived object when it is deleted. + * contents. It is like the difference between (MyClass * const) and + * (const MyClass *). + * + * In order to use PointerTo, it is necessary that the thing pointed to + * --MyClass in the above example--either inherits from ReferenceCount, or is + * a proxy built with RefCountProxy or RefCountObj (see referenceCount.h). + * However, also see PointerToArray, which does not have this restriction. + * + * It is crucial that the PointerTo object is only used to refer to objects + * allocated from the free store, for which delete is a sensible thing to do. + * If you assign a PointerTo to an automatic variable (allocated from the + * stack, for instance), bad things will certainly happen when the reference + * count reaches zero and it tries to delete it. + * + * It's also important to remember that, as always, a virtual destructor is + * required if you plan to support polymorphism. That is, if you define a + * PointerTo to some base type, and assign to it instances of a class derived + * from that base class, the base class must have a virtual destructor in + * order to properly destruct the derived object when it is deleted. */ #include "pandabase.h" @@ -58,10 +70,9 @@ class PointerTo : public PointerToBase { public: typedef TYPENAME PointerToBase::To To; PUBLISHED: - ALWAYS_INLINE PointerTo() DEFAULT_CTOR; - ALWAYS_INLINE PointerTo(To *ptr); + ALWAYS_INLINE_CONSTEXPR PointerTo() NOEXCEPT DEFAULT_CTOR; + ALWAYS_INLINE PointerTo(To *ptr) NOEXCEPT; INLINE PointerTo(const PointerTo ©); - INLINE ~PointerTo(); public: #ifdef USE_MOVE_SEMANTICS @@ -69,10 +80,10 @@ public: INLINE PointerTo &operator = (PointerTo &&from) NOEXCEPT; #endif - INLINE To &operator *() const; - INLINE To *operator -> () const; + CONSTEXPR To &operator *() const NOEXCEPT; + CONSTEXPR To *operator -> () const NOEXCEPT; // MSVC.NET 2005 insists that we use T *, and not To *, here. - INLINE operator T *() const; + CONSTEXPR operator T *() const NOEXCEPT; INLINE T *&cheat(); @@ -89,7 +100,7 @@ PUBLISHED: // the DCAST macro defined in typedObject.h instead, e.g. DCAST(MyType, // ptr). This provides a clean downcast that doesn't require .p() or any // double-casting, and it can be run-time checked for correctness. - INLINE To *p() const; + CONSTEXPR To *p() const NOEXCEPT; INLINE PointerTo &operator = (To *ptr); INLINE PointerTo &operator = (const PointerTo ©); @@ -98,8 +109,10 @@ PUBLISHED: // anyway just to help out interrogate (which doesn't seem to want to // automatically export the PointerToBase class). When this works again in // interrogate, we can remove these. - INLINE bool is_null() const { return PointerToBase::is_null(); } - INLINE void clear() { PointerToBase::clear(); } +#ifdef CPPPARSER + INLINE bool is_null() const; + INLINE void clear(); +#endif }; @@ -120,11 +133,10 @@ class ConstPointerTo : public PointerToBase { public: typedef TYPENAME PointerToBase::To To; PUBLISHED: - ALWAYS_INLINE ConstPointerTo() DEFAULT_CTOR; - ALWAYS_INLINE ConstPointerTo(const To *ptr); + ALWAYS_INLINE_CONSTEXPR ConstPointerTo() NOEXCEPT DEFAULT_CTOR; + ALWAYS_INLINE ConstPointerTo(const To *ptr) NOEXCEPT; INLINE ConstPointerTo(const PointerTo ©); INLINE ConstPointerTo(const ConstPointerTo ©); - INLINE ~ConstPointerTo(); public: #ifdef USE_MOVE_SEMANTICS @@ -134,14 +146,14 @@ public: INLINE ConstPointerTo &operator = (ConstPointerTo &&from) NOEXCEPT; #endif - INLINE const To &operator *() const; - INLINE const To *operator -> () const; - INLINE operator const T *() const; + CONSTEXPR const To &operator *() const NOEXCEPT; + CONSTEXPR const To *operator -> () const NOEXCEPT; + CONSTEXPR operator const T *() const NOEXCEPT; INLINE const T *&cheat(); PUBLISHED: - INLINE const To *p() const; + CONSTEXPR const To *p() const NOEXCEPT; INLINE ConstPointerTo &operator = (const To *ptr); INLINE ConstPointerTo &operator = (const PointerTo ©); @@ -151,7 +163,9 @@ PUBLISHED: // anyway just to help out interrogate (which doesn't seem to want to // automatically export the PointerToBase class). When this works again in // interrogate, we can remove this. - INLINE void clear() { PointerToBase::clear(); } +#ifdef CPPPARSER + INLINE void clear(); +#endif }; diff --git a/panda/src/express/pointerToArray.I b/panda/src/express/pointerToArray.I index 568b379105..7bf8b89d4e 100644 --- a/panda/src/express/pointerToArray.I +++ b/panda/src/express/pointerToArray.I @@ -68,6 +68,17 @@ PointerToArray(const PointerToArray ©) : { } +/** + * Initializes a PointerToArray by copying existing elements. + */ +template +INLINE PointerToArray:: +PointerToArray(const Element *begin, const Element *end, TypeHandle type_handle) : + PointerToArrayBase(new ReferenceCountedVector(begin, end, type_handle)), + _type_handle(type_handle) +{ +} + #ifdef USE_MOVE_SEMANTICS /** * @@ -627,6 +638,17 @@ ConstPointerToArray(TypeHandle type_handle) : { } +/** + * Initializes a ConstPointerToArray by copying existing elements. + */ +template +INLINE ConstPointerToArray:: +ConstPointerToArray(const Element *begin, const Element *end, TypeHandle type_handle) : + PointerToArrayBase(new ReferenceCountedVector(begin, end, type_handle)), + _type_handle(type_handle) +{ +} + /** * */ diff --git a/panda/src/express/pointerToArray.h b/panda/src/express/pointerToArray.h index 004e15fdea..e3dcc2f110 100644 --- a/panda/src/express/pointerToArray.h +++ b/panda/src/express/pointerToArray.h @@ -105,9 +105,9 @@ PUBLISHED: INLINE void set_element(size_type n, const Element &value); EXTENSION(const Element &__getitem__(size_type n) const); EXTENSION(void __setitem__(size_type n, const Element &value)); - INLINE string get_data() const; - INLINE void set_data(const string &data); - INLINE string get_subdata(size_type n, size_type count) const; + EXTENSION(PyObject *get_data() const); + EXTENSION(void set_data(PyObject *data)); + EXTENSION(PyObject *get_subdata(size_type n, size_type count) const); INLINE void set_subdata(size_type n, size_type count, const string &data); INLINE int get_ref_count() const; INLINE int get_node_ref_count() const; @@ -134,6 +134,7 @@ public: INLINE PointerToArray(TypeHandle type_handle = get_type_handle(Element)); INLINE static PointerToArray empty_array(size_type n, TypeHandle type_handle = get_type_handle(Element)); INLINE PointerToArray(size_type n, const Element &value, TypeHandle type_handle = get_type_handle(Element)); + INLINE PointerToArray(const Element *begin, const Element *end, TypeHandle type_handle = get_type_handle(Element)); INLINE PointerToArray(const PointerToArray ©); #ifdef USE_MOVE_SEMANTICS @@ -246,6 +247,8 @@ private: template class ConstPointerToArray : public PointerToArrayBase { public: + INLINE ConstPointerToArray(TypeHandle type_handle = get_type_handle(Element)); + // By hiding this template from interrogate, we would improve compile-time // speed and memory utilization. However, we do want to export a minimal // subset of this class. So we define just the exportable interface here. @@ -254,14 +257,12 @@ PUBLISHED: INLINE ConstPointerToArray(const PointerToArray ©); INLINE ConstPointerToArray(const ConstPointerToArray ©); - EXTENSION(ConstPointerToArray(PyObject *self, PyObject *source)); - typedef TYPENAME pvector::size_type size_type; INLINE size_type size() const; INLINE const Element &get_element(size_type n) const; EXTENSION(const Element &__getitem__(size_type n) const); - INLINE string get_data() const; - INLINE string get_subdata(size_type n, size_type count) const; + EXTENSION(PyObject *get_data() const); + EXTENSION(PyObject *get_subdata(size_type n, size_type count) const); INLINE int get_ref_count() const; INLINE int get_node_ref_count() const; @@ -288,7 +289,7 @@ PUBLISHED: typedef TYPENAME pvector::difference_type difference_type; typedef TYPENAME pvector::size_type size_type; - INLINE ConstPointerToArray(TypeHandle type_handle = get_type_handle(Element)); + INLINE ConstPointerToArray(const Element *begin, const Element *end, TypeHandle type_handle = get_type_handle(Element)); INLINE ConstPointerToArray(const PointerToArray ©); INLINE ConstPointerToArray(const ConstPointerToArray ©); diff --git a/panda/src/express/pointerToArrayBase.I b/panda/src/express/pointerToArrayBase.I index 885d759aed..89acba1206 100644 --- a/panda/src/express/pointerToArrayBase.I +++ b/panda/src/express/pointerToArrayBase.I @@ -19,17 +19,6 @@ INLINE ReferenceCountedVector:: ReferenceCountedVector(TypeHandle type_handle) : pvector(type_handle) { } -/** - * - */ -template -INLINE ReferenceCountedVector:: -ReferenceCountedVector(const ReferenceCountedVector ©) : - NodeReferenceCount(copy), - pvector(copy) -{ -} - /** * Creates an array of initial_size elements. */ @@ -41,11 +30,13 @@ ReferenceCountedVector(TYPENAME ReferenceCountedVector::size_type initi } /** - * + * Creates an array with all elements from begin up to but not including end. */ template INLINE ReferenceCountedVector:: -~ReferenceCountedVector() { +ReferenceCountedVector(const Element *begin, const Element *end, TypeHandle type_handle) : + pvector(begin, end, type_handle) +{ } /** diff --git a/panda/src/express/pointerToArrayBase.h b/panda/src/express/pointerToArrayBase.h index 291ffed656..3220ca4c70 100644 --- a/panda/src/express/pointerToArrayBase.h +++ b/panda/src/express/pointerToArrayBase.h @@ -41,9 +41,8 @@ public: typedef TYPENAME pvector::size_type size_type; INLINE ReferenceCountedVector(TypeHandle type_handle); - INLINE ReferenceCountedVector(const ReferenceCountedVector ©); INLINE ReferenceCountedVector(size_type initial_size, TypeHandle type_handle); - INLINE ~ReferenceCountedVector(); + INLINE ReferenceCountedVector(const Element *begin, const Element *end, TypeHandle type_handle); ALLOC_DELETED_CHAIN(ReferenceCountedVector); INLINE size_type size() const; diff --git a/panda/src/express/pointerToArray_ext.I b/panda/src/express/pointerToArray_ext.I index 7502334e6e..e2356c7a4b 100644 --- a/panda/src/express/pointerToArray_ext.I +++ b/panda/src/express/pointerToArray_ext.I @@ -11,6 +11,64 @@ * @date 2015-02-08 */ +/** + * This is a helper function to set most attributes of a Py_buffer in a manner + * that accommodates square matrices (in accordance with PEP 3118). It is tested + * for use with NumPy. The resulting array will be of shape + * (num_matrices, size, size) where size is the number of matrix rows (=columns) + */ +INLINE void set_matrix_view(Py_buffer &view, int flags, int length, int size, bool double_prec, bool read_only) { + int item_size, mat_size; + const char *format; + + if (double_prec) { + item_size = sizeof(double); + format = get_format_code(double); + } else { + item_size = sizeof(float); + format = get_format_code(float); + } + + if (size == 3 && !double_prec) { + mat_size = sizeof(LMatrix3f); + } else if (size == 3 && double_prec) { + mat_size = sizeof(LMatrix3d); + } else if (size == 4 && !double_prec) { + mat_size = sizeof(UnalignedLMatrix4f); + } else if (size == 4 && double_prec) { + mat_size = sizeof(UnalignedLMatrix4d); + } + + view.len = length * mat_size; + view.readonly = (read_only ? 1 : 0); + view.itemsize = item_size; + view.format = NULL; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { + view.format = (char*) format; + } + view.ndim = 3; + view.shape = NULL; + if ((flags & PyBUF_ND) == PyBUF_ND) { + // This leaks, which sucks, but __releasebuffer__ doesn't give us the same + // pointer, so we would need to store it elsewhere if we wanted to delete + // it there. Eh, it's just an int, who cares. + Py_ssize_t* shape = new Py_ssize_t[3]; + shape[0] = length; + shape[1] = size; + shape[2] = size; + view.shape = shape; + } + view.strides = NULL; + if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { + Py_ssize_t* strides = new Py_ssize_t[3]; + strides[0] = mat_size; + strides[1] = item_size * size; + strides[2] = item_size; + view.strides = strides; + } + view.suboffsets = NULL; +} + /** * This special constructor accepts a Python list of elements, or a Python * string (or a bytes object, in Python 3), or any object that supports the @@ -21,114 +79,48 @@ INLINE void Extension >:: __init__(PyObject *self, PyObject *source) { #if PY_VERSION_HEX >= 0x02060000 if (PyObject_CheckBuffer(source)) { - // User passed a buffer object. - Py_buffer view; - if (PyObject_GetBuffer(source, &view, PyBUF_CONTIG_RO) == -1) { - PyErr_SetString(PyExc_TypeError, - "PointerToArray constructor requires a contiguous buffer"); - return; - } - - if (view.itemsize != 1 && view.itemsize != sizeof(Element)) { - PyErr_SetString(PyExc_TypeError, - "buffer.itemsize does not match PointerToArray element size"); - return; - } - - if (view.len % sizeof(Element) != 0) { - PyErr_Format(PyExc_ValueError, - "byte buffer is not a multiple of %zu bytes", - sizeof(Element)); - return; - } - - if (view.len > 0) { - this->_this->resize(view.len / sizeof(Element)); - memcpy(this->_this->p(), view.buf, view.len); - } - - PyBuffer_Release(&view); +#else + if (PyString_CheckExact(source)) { +#endif + // It's a byte sequence, or any object that exports the buffer protocol. + this->set_data(source); return; } -#endif - if (!PySequence_Check(source)) { + // Don't allow a unicode object even though it's a sequence. + if (!PySequence_Check(source) || PyUnicode_CheckExact(source)) { // If passed with a non-sequence, this isn't the right constructor. PyErr_SetString(PyExc_TypeError, "PointerToArray constructor requires a sequence or buffer object"); return; } - // If we were passed a Python string, then instead of storing it character- - // at-a-time, just load the whole string as a data buffer. Not sure if this - // case is still necessary - don't Python strbytes objects export the buffer - // protocol, as above? -#if PY_MAJOR_VERSION >= 3 - if (PyBytes_Check(source)) { - int size = PyBytes_Size(source); - if (size % sizeof(Element) != 0) { - PyErr_Format(PyExc_ValueError, - "bytes object is not a multiple of %zu bytes", - sizeof(Element)); - return; - } - - int num_elements = size / sizeof(Element); - this->_this->insert(this->_this->begin(), num_elements, Element()); - - // Hope there aren't any constructors or destructors involved here. - if (size != 0) { - const char *data = PyBytes_AsString(source); - memcpy(this->_this->p(), data, size); - } - return; - } -#else - if (PyString_CheckExact(source)) { - int size = PyString_Size(source); - if (size % sizeof(Element) != 0) { - PyErr_Format(PyExc_ValueError, - "str object is not a multiple of %zu bytes", - sizeof(Element)); - return; - } - - int num_elements = size / sizeof(Element); - this->_this->insert(this->_this->begin(), num_elements, Element()); - - // Hope there aren't any constructors or destructors involved here. - if (size != 0) { - const char *data = PyString_AsString(source); - memcpy(this->_this->p(), data, size); - } - return; - } -#endif - // Now construct the internal list by copying the elements one-at-a-time // from Python. - PyObject *push_back = PyObject_GetAttrString(self, "push_back"); + PyObject *dict = DtoolInstance_TYPE(self)->_PyType.tp_dict; + PyObject *push_back = PyDict_GetItemString(dict, "push_back"); if (push_back == NULL) { PyErr_BadArgument(); return; } // We need to initialize the this pointer before we can call push_back. - ((Dtool_PyInstDef *)self)->_ptr_to_object = (void *)this->_this; + DtoolInstance_INIT_PTR(self, this->_this); - int size = PySequence_Size(source); - for (int i = 0; i < size; ++i) { + Py_ssize_t size = PySequence_Size(source); + this->_this->reserve(size); + for (Py_ssize_t i = 0; i < size; ++i) { PyObject *item = PySequence_GetItem(source, i); if (item == NULL) { return; } - PyObject *result = PyObject_CallFunctionObjArgs(push_back, item, NULL); + PyObject *result = PyObject_CallFunctionObjArgs(push_back, self, item, NULL); Py_DECREF(item); if (result == NULL) { // Unable to add item--probably it wasn't of the appropriate type. PyErr_Print(); PyErr_Format(PyExc_TypeError, - "Element %d in sequence passed to PointerToArray " + "Element %zd in sequence passed to PointerToArray " "constructor could not be added", i); return; } @@ -155,15 +147,110 @@ __setitem__(size_t n, const Element &value) { } /** - * This special constructor accepts a Python list of elements, or a Python - * string (or a bytes object, in Python 3). + * This returns the entire contents of the vector as a block of raw data in a + * string (or bytes object, in Python 3). + * + * @deprecated use memoryview(pta) or bytearray(pta) instead. */ template -INLINE void Extension >:: -__init__(PyObject *self, PyObject *source) { - PointerToArray array; - invoke_extension(&array).__init__(self, source); - *(this->_this) = MOVE(array); +INLINE PyObject *Extension >:: +get_data() const { +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size()); +#else + return PyString_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size()); +#endif +} + +/** + * This method exists mainly to access the data of the array easily from a + * high-level language such as Python. + * + * This replaces the entire contents of the vector from a block of raw data + * in a string (or bytes object, in Python 3). + */ +template +INLINE void Extension >:: +set_data(PyObject *data) { +#if PY_VERSION_HEX >= 0x02060000 + if (PyObject_CheckBuffer(data)) { + // User passed a buffer object. + Py_buffer view; + if (PyObject_GetBuffer(data, &view, PyBUF_CONTIG_RO) == -1) { + PyErr_SetString(PyExc_TypeError, + "PointerToArray.set_data() requires a contiguous buffer"); + return; + } + + if (view.itemsize != 1 && view.itemsize != sizeof(Element)) { + PyErr_SetString(PyExc_TypeError, + "buffer.itemsize does not match PointerToArray element size"); + return; + } + + if (view.len % sizeof(Element) != 0) { + PyErr_Format(PyExc_ValueError, + "byte buffer is not a multiple of %zu bytes", + sizeof(Element)); + return; + } + + if (view.len > 0) { + this->_this->resize(view.len / sizeof(Element)); + memcpy(this->_this->p(), view.buf, view.len); + } else { + this->_this->clear(); + } + + PyBuffer_Release(&view); + } else { + Dtool_Raise_TypeError("PointerToArray.set_data() requires a buffer object"); + } +#else + // In Python 2.5 we didn't have the new buffer protocol, only str. + if (PyString_CheckExact(data)) { + int size = PyString_Size(data); + if (size % sizeof(Element) != 0) { + PyErr_Format(PyExc_ValueError, + "str object is not a multiple of %zu bytes", + sizeof(Element)); + return; + } + + int num_elements = size / sizeof(Element); + this->_this->insert(this->_this->begin(), num_elements, Element()); + + // Hope there aren't any constructors or destructors involved here. + if (size != 0) { + const char *ptr = PyString_AsString(data); + memcpy(this->_this->p(), ptr, size); + } else { + this->_this->clear(); + } + } else { + Dtool_Raise_TypeError("PointerToArray.set_data() requires a str"); + } +#endif +} + +/** + * This returns the contents of a portion of the vector--from element (n) + * through element (n + count - 1)--as a block of raw data in a string (or + * bytes object, in Python 3). + * + * @deprecated use memoryview(pta) or bytearray(pta) instead. + */ +template +INLINE PyObject *Extension >:: +get_subdata(size_t n, size_t count) const { + n = min(n, this->_this->size()); + count = max(count, n); + count = min(count, this->_this->size() - n); +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count); +#else + return PyString_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count); +#endif } /** @@ -175,6 +262,42 @@ __getitem__(size_t n) const { return (*this->_this)[n]; } +/** + * This returns the entire contents of the vector as a block of raw data in a + * string (or bytes object, in Python 3). + * + * @deprecated use memoryview(pta) or bytearray(pta) instead. + */ +template +INLINE PyObject *Extension >:: +get_data() const { +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size()); +#else + return PyString_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size()); +#endif +} + +/** + * This returns the contents of a portion of the vector--from element (n) + * through element (n + count - 1)--as a block of raw data in a string (or + * bytes object, in Python 3). + * + * @deprecated use memoryview(pta) or bytearray(pta) instead. + */ +template +INLINE PyObject *Extension >:: +get_subdata(size_t n, size_t count) const { + n = min(n, this->_this->size()); + count = max(count, n); + count = min(count, this->_this->size() - n); +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count); +#else + return PyString_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count); +#endif +} + /** * This is used to implement the buffer protocol, in order to allow efficient * access to the array data through a Python multiview object. @@ -226,6 +349,110 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) { #endif } +/** + * This is used to implement the buffer protocol, in order to allow efficient + * access to the array data through a Python memoryview object. + */ +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) { +#if PY_VERSION_HEX >= 0x02060000 + if (self != NULL) { + Py_INCREF(self); + } + view->obj = self; + view->buf = (void*) this->_this->p(); + set_matrix_view(*view, flags, this->_this->size(), 3, false, false); + + // Store a reference to ourselves on the Py_buffer object as a reminder that + // we have increased our refcount. + this->_this->ref(); + view->internal = (void*) this->_this; + + return 0; +#else + return -1; +#endif +} + +/** + * This is used to implement the buffer protocol, in order to allow efficient + * access to the array data through a Python memoryview object. + */ +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) { +#if PY_VERSION_HEX >= 0x02060000 + if (self != NULL) { + Py_INCREF(self); + } + view->obj = self; + view->buf = (void*) this->_this->p(); + set_matrix_view(*view, flags, this->_this->size(), 3, true, false); + + // Store a reference to ourselves on the Py_buffer object as a reminder that + // we have increased our refcount. + this->_this->ref(); + view->internal = (void*) this->_this; + + return 0; +#else + return -1; +#endif +} + +/** + * This is used to implement the buffer protocol, in order to allow efficient + * access to the array data through a Python memoryview object. + */ +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) { +#if PY_VERSION_HEX >= 0x02060000 + if (self != NULL) { + Py_INCREF(self); + } + view->obj = self; + view->buf = (void*) this->_this->p(); + set_matrix_view(*view, flags, this->_this->size(), 4, false, false); + + // Store a reference to ourselves on the Py_buffer object as a reminder that + // we have increased our refcount. + this->_this->ref(); + view->internal = (void*) this->_this; + + return 0; +#else + return -1; +#endif +} + +/** + * This is used to implement the buffer protocol, in order to allow efficient + * access to the array data through a Python memoryview object. + */ +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) { +#if PY_VERSION_HEX >= 0x02060000 + if (self != NULL) { + Py_INCREF(self); + } + view->obj = self; + view->buf = (void*) this->_this->p(); + set_matrix_view(*view, flags, this->_this->size(), 4, true, false); + + // Store a reference to ourselves on the Py_buffer object as a reminder that + // we have increased our refcount. + this->_this->ref(); + view->internal = (void*) this->_this; + + return 0; +#else + return -1; +#endif +} + /** * Releases the buffer allocated by __getbuffer__. */ @@ -299,6 +526,126 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const { #endif } +/** + * Specialization on __getbuffer__ for LMatrix3f. + */ +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const { +#if PY_VERSION_HEX >= 0x02060000 + if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) { + PyErr_SetString(PyExc_BufferError, + "Object is not writable."); + return -1; + } + if (self != NULL) { + Py_INCREF(self); + } + view->obj = self; + view->buf = (void*) this->_this->p(); + set_matrix_view(*view, flags, this->_this->size(), 3, false, true); + + // Store a reference to ourselves on the Py_buffer object as a reminder that + // we have increased our refcount. + this->_this->ref(); + view->internal = (void*) this->_this; + + return 0; +#else + return -1; +#endif +} + +/** + * Specialization on __getbuffer__ for LMatrix3d. + */ +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const { +#if PY_VERSION_HEX >= 0x02060000 + if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) { + PyErr_SetString(PyExc_BufferError, + "Object is not writable."); + return -1; + } + if (self != NULL) { + Py_INCREF(self); + } + view->obj = self; + view->buf = (void*) this->_this->p(); + set_matrix_view(*view, flags, this->_this->size(), 3, true, true); + + // Store a reference to ourselves on the Py_buffer object as a reminder that + // we have increased our refcount. + this->_this->ref(); + view->internal = (void*) this->_this; + + return 0; +#else + return -1; +#endif +} + +/** + * Specialization on __getbuffer__ for UnalignedLMatrix4f. + */ +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const { +#if PY_VERSION_HEX >= 0x02060000 + if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) { + PyErr_SetString(PyExc_BufferError, + "Object is not writable."); + return -1; + } + if (self != NULL) { + Py_INCREF(self); + } + view->obj = self; + view->buf = (void*) this->_this->p(); + set_matrix_view(*view, flags, this->_this->size(), 4, false, true); + + // Store a reference to ourselves on the Py_buffer object as a reminder that + // we have increased our refcount. + this->_this->ref(); + view->internal = (void*) this->_this; + + return 0; +#else + return -1; +#endif +} + +/** + * Specialization on __getbuffer__ for UnalignedLMatrix4d. + */ +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const { +#if PY_VERSION_HEX >= 0x02060000 + if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) { + PyErr_SetString(PyExc_BufferError, + "Object is not writable."); + return -1; + } + if (self != NULL) { + Py_INCREF(self); + } + view->obj = self; + view->buf = (void*) this->_this->p(); + set_matrix_view(*view, flags, this->_this->size(), 4, true, true); + + // Store a reference to ourselves on the Py_buffer object as a reminder that + // we have increased our refcount. + this->_this->ref(); + view->internal = (void*) this->_this; + + return 0; +#else + return -1; +#endif +} + /** * Releases the buffer allocated by __getbuffer__. */ diff --git a/panda/src/express/pointerToArray_ext.h b/panda/src/express/pointerToArray_ext.h index 73ec780675..469b449fed 100644 --- a/panda/src/express/pointerToArray_ext.h +++ b/panda/src/express/pointerToArray_ext.h @@ -19,6 +19,7 @@ #include "extension.h" #include "py_panda.h" #include "pointerToArray.h" +#include "luse.h" /** * This class defines the extension methods for PointerToArray, which are @@ -35,10 +36,30 @@ public: INLINE const Element &__getitem__(size_t n) const; INLINE void __setitem__(size_t n, const Element &value); + INLINE PyObject *get_data() const; + INLINE void set_data(PyObject *data); + INLINE PyObject *get_subdata(size_t n, size_t count) const; + INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags); INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const; }; +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags); + +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags); + +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags); + +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags); + /** * This class defines the extension methods for ConstPointerToArray, which are * called instead of any C++ methods with the same prototype. @@ -49,14 +70,31 @@ public: template class Extension > : public ExtensionBase > { public: - INLINE void __init__(PyObject *self, PyObject *source); - INLINE const Element &__getitem__(size_t n) const; + INLINE PyObject *get_data() const; + INLINE PyObject *get_subdata(size_t n, size_t count) const; + INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const; INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const; }; +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const; + +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const; + +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const; + +template<> +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const; + #ifdef _MSC_VER // Ugh... MSVC needs this because they still don't have a decent linker. #include "PTA_uchar.h" @@ -104,6 +142,20 @@ define_format_code("Q", unsigned long long); define_format_code("f", float); define_format_code("d", double); +define_format_code("2f", LVecBase2f); +define_format_code("2d", LVecBase2d); +define_format_code("2i", LVecBase2i); +define_format_code("3f", LVecBase3f); +define_format_code("3d", LVecBase3d); +define_format_code("3i", LVecBase3i); +define_format_code("4f", UnalignedLVecBase4f); +define_format_code("4d", UnalignedLVecBase4d); +define_format_code("4i", UnalignedLVecBase4i); +// define_format_code("9f", LMatrix3f); +// define_format_code("9d", LMatrix3d); +// define_format_code("16f", UnalignedLMatrix4f); +// define_format_code("16d", UnalignedLMatrix4d); + #include "pointerToArray_ext.I" #endif // CPPPARSER diff --git a/panda/src/express/pointerToBase.I b/panda/src/express/pointerToBase.I index 296dc5d3bb..bf4ff877f0 100644 --- a/panda/src/express/pointerToBase.I +++ b/panda/src/express/pointerToBase.I @@ -21,9 +21,7 @@ PointerToBase(To *ptr) { if (ptr != (To *)NULL) { ptr->ref(); #ifdef DO_MEMORY_USAGE - if (MemoryUsage::get_track_memory_usage()) { - update_type(ptr); - } + update_type(ptr); #endif } } @@ -38,11 +36,6 @@ PointerToBase(const PointerToBase ©) { if (_void_ptr != NULL) { To *ptr = (To *)_void_ptr; ptr->ref(); -#ifdef DO_MEMORY_USAGE - if (MemoryUsage::get_track_memory_usage()) { - update_type(ptr); - } -#endif } } @@ -108,17 +101,15 @@ reassign(To *ptr) { To *old_ptr = (To *)_void_ptr; _void_ptr = (void *)ptr; - if (ptr != (To *)NULL) { + if (ptr != nullptr) { ptr->ref(); #ifdef DO_MEMORY_USAGE - if (MemoryUsage::get_track_memory_usage()) { - update_type(ptr); - } + update_type(ptr); #endif } // Now delete the old pointer. - if (old_ptr != (To *)NULL) { + if (old_ptr != nullptr) { unref_delete(old_ptr); } } @@ -130,28 +121,46 @@ reassign(To *ptr) { template INLINE void PointerToBase:: reassign(const PointerToBase ©) { - reassign((To *)copy._void_ptr); + if (copy._void_ptr != _void_ptr) { + // First save the old pointer; we won't delete it until we have assigned + // the new one. We do this just in case there are cascading effects from + // deleting this pointer that might inadvertently delete the new one. + // (Don't laugh--it's happened!) + To *old_ptr = (To *)_void_ptr; + To *new_ptr = (To *)copy._void_ptr; + + _void_ptr = copy._void_ptr; + if (new_ptr != nullptr) { + new_ptr->ref(); + } + + // Now delete the old pointer. + if (old_ptr != nullptr) { + unref_delete(old_ptr); + } + } } -#ifdef DO_MEMORY_USAGE /** * Ensures that the MemoryUsage record for the pointer has the right type of * object, if we know the type ourselves. */ template -void PointerToBase:: +INLINE void PointerToBase:: update_type(To *ptr) { - TypeHandle type = get_type_handle(To); - if (type == TypeHandle::none()) { - do_init_type(To); - type = get_type_handle(To); +#ifdef DO_MEMORY_USAGE + if (MemoryUsage::get_track_memory_usage()) { + TypeHandle type = get_type_handle(To); + if (type == TypeHandle::none()) { + do_init_type(To); + type = get_type_handle(To); + } + if (type != TypeHandle::none()) { + MemoryUsage::update_type(ptr, type); + } } - if (type != TypeHandle::none()) { - MemoryUsage::update_type(ptr, type); - } -} #endif // DO_MEMORY_USAGE - +} /** * A convenient way to set the PointerTo object to NULL. (Assignment to a NULL diff --git a/panda/src/express/pointerToBase.h b/panda/src/express/pointerToBase.h index cbafa5587b..919f596c33 100644 --- a/panda/src/express/pointerToBase.h +++ b/panda/src/express/pointerToBase.h @@ -31,7 +31,7 @@ public: typedef T To; protected: - ALWAYS_INLINE PointerToBase() DEFAULT_CTOR; + ALWAYS_INLINE_CONSTEXPR PointerToBase() NOEXCEPT DEFAULT_CTOR; INLINE PointerToBase(To *ptr); INLINE PointerToBase(const PointerToBase ©); INLINE ~PointerToBase(); @@ -44,9 +44,7 @@ protected: INLINE void reassign(To *ptr); INLINE void reassign(const PointerToBase ©); -#ifdef DO_MEMORY_USAGE - void update_type(To *ptr); -#endif // DO_MEMORY_USAGE + INLINE void update_type(To *ptr); // No assignment or retrieval functions are declared in PointerToBase, // because we will have to specialize on const vs. non-const later. diff --git a/panda/src/express/pointerToVoid.I b/panda/src/express/pointerToVoid.I index 6ae942b645..ee3a36e303 100644 --- a/panda/src/express/pointerToVoid.I +++ b/panda/src/express/pointerToVoid.I @@ -14,34 +14,25 @@ /** * */ -INLINE PointerToVoid:: -PointerToVoid() { - _void_ptr = (void *)NULL; +CONSTEXPR PointerToVoid:: +PointerToVoid() NOEXCEPT : _void_ptr(nullptr) { } /** * */ -INLINE PointerToVoid:: -~PointerToVoid() { - nassertv(_void_ptr == (void *)NULL); -} - -/** - * Don't use this constructor. - */ -INLINE PointerToVoid:: -PointerToVoid(const PointerToVoid &) { - nassertv(false); -} +//INLINE PointerToVoid:: +//~PointerToVoid() { +// nassertv(_void_ptr == nullptr); +//} /** * Returns true if the PointerTo is a NULL pointer, false otherwise. (Direct * comparison to a NULL pointer also works.) */ -ALWAYS_INLINE bool PointerToVoid:: +CONSTEXPR bool PointerToVoid:: is_null() const { - return (_void_ptr == (void *)NULL); + return _void_ptr == nullptr; } /** diff --git a/panda/src/express/pointerToVoid.h b/panda/src/express/pointerToVoid.h index 25189a4e0a..0d2c1d33e9 100644 --- a/panda/src/express/pointerToVoid.h +++ b/panda/src/express/pointerToVoid.h @@ -32,14 +32,14 @@ */ class EXPCL_PANDAEXPRESS PointerToVoid : public MemoryBase { protected: - INLINE PointerToVoid(); - INLINE ~PointerToVoid(); + CONSTEXPR PointerToVoid() NOEXCEPT; + //INLINE ~PointerToVoid(); private: - INLINE PointerToVoid(const PointerToVoid ©); + PointerToVoid(const PointerToVoid ©) DELETED; PUBLISHED: - ALWAYS_INLINE bool is_null() const; + CONSTEXPR bool is_null() const; INLINE size_t get_hash() const; public: diff --git a/panda/src/express/profileTimer.h b/panda/src/express/profileTimer.h index 48c9f16ad1..94eb39bd01 100644 --- a/panda/src/express/profileTimer.h +++ b/panda/src/express/profileTimer.h @@ -40,7 +40,7 @@ class EXPCL_PANDAEXPRESS ProfileTimer { enum { MaxEntriesDefault=4096 }; PUBLISHED: - ProfileTimer(const char* name=0, int maxEntries=MaxEntriesDefault); + explicit ProfileTimer(const char* name=0, int maxEntries=MaxEntriesDefault); ProfileTimer(const ProfileTimer& other); ~ProfileTimer(); diff --git a/panda/src/express/subStream.h b/panda/src/express/subStream.h index b8a45aa4ac..3d071b9c7f 100644 --- a/panda/src/express/subStream.h +++ b/panda/src/express/subStream.h @@ -30,7 +30,7 @@ class EXPCL_PANDAEXPRESS ISubStream : public istream { PUBLISHED: INLINE ISubStream(); - INLINE ISubStream(IStreamWrapper *source, streampos start, streampos end); + INLINE explicit ISubStream(IStreamWrapper *source, streampos start, streampos end); #if _MSC_VER >= 1800 INLINE ISubStream(const ISubStream ©) = delete; @@ -55,7 +55,7 @@ private: class EXPCL_PANDAEXPRESS OSubStream : public ostream { PUBLISHED: INLINE OSubStream(); - INLINE OSubStream(OStreamWrapper *dest, streampos start, streampos end, bool append = false); + INLINE explicit OSubStream(OStreamWrapper *dest, streampos start, streampos end, bool append = false); #if _MSC_VER >= 1800 INLINE OSubStream(const OSubStream ©) = delete; @@ -74,7 +74,7 @@ private: class EXPCL_PANDAEXPRESS SubStream : public iostream { PUBLISHED: INLINE SubStream(); - INLINE SubStream(StreamWrapper *nested, streampos start, streampos end, bool append = false); + INLINE explicit SubStream(StreamWrapper *nested, streampos start, streampos end, bool append = false); #if _MSC_VER >= 1800 INLINE SubStream(const SubStream ©) = delete; diff --git a/panda/src/express/subfileInfo.h b/panda/src/express/subfileInfo.h index 389263ab76..de2f85fa3e 100644 --- a/panda/src/express/subfileInfo.h +++ b/panda/src/express/subfileInfo.h @@ -26,8 +26,8 @@ class EXPCL_PANDAEXPRESS SubfileInfo { PUBLISHED: INLINE SubfileInfo(); - INLINE SubfileInfo(const FileReference *file, streampos start, streamsize size); - INLINE SubfileInfo(const Filename &filename, streampos start, streamsize size); + INLINE explicit SubfileInfo(const FileReference *file, streampos start, streamsize size); + INLINE explicit SubfileInfo(const Filename &filename, streampos start, streamsize size); INLINE SubfileInfo(const SubfileInfo ©); INLINE void operator = (const SubfileInfo ©); diff --git a/panda/src/express/temporaryFile.h b/panda/src/express/temporaryFile.h index 14874c9cca..8bc66da12b 100644 --- a/panda/src/express/temporaryFile.h +++ b/panda/src/express/temporaryFile.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDAEXPRESS TemporaryFile : public FileReference { PUBLISHED: - INLINE TemporaryFile(const Filename &filename); + INLINE explicit TemporaryFile(const Filename &filename); virtual ~TemporaryFile(); public: diff --git a/panda/src/express/threadSafePointerToBase.I b/panda/src/express/threadSafePointerToBase.I index db8166b78a..4d200a483a 100644 --- a/panda/src/express/threadSafePointerToBase.I +++ b/panda/src/express/threadSafePointerToBase.I @@ -89,7 +89,6 @@ reassign(const ThreadSafePointerToBase ©) { reassign((To *)copy._void_ptr); } -#ifdef DO_MEMORY_USAGE /** * Ensures that the MemoryUsage record for the pointer has the right type of * object, if we know the type ourselves. @@ -97,6 +96,7 @@ reassign(const ThreadSafePointerToBase ©) { template void ThreadSafePointerToBase:: update_type(To *ptr) { +#ifdef DO_MEMORY_USAGE TypeHandle type = get_type_handle(To); if (type == TypeHandle::none()) { do_init_type(To); @@ -105,9 +105,8 @@ update_type(To *ptr) { if (type != TypeHandle::none()) { MemoryUsage::update_type(ptr, type); } -} #endif // DO_MEMORY_USAGE - +} /** * A convenient way to set the ThreadSafePointerTo object to NULL. (Assignment diff --git a/panda/src/express/threadSafePointerToBase.h b/panda/src/express/threadSafePointerToBase.h index dedcfd08ab..78c6d75cca 100644 --- a/panda/src/express/threadSafePointerToBase.h +++ b/panda/src/express/threadSafePointerToBase.h @@ -40,9 +40,7 @@ protected: INLINE void reassign(To *ptr); INLINE void reassign(const ThreadSafePointerToBase ©); -#ifdef DO_MEMORY_USAGE void update_type(To *ptr); -#endif // DO_MEMORY_USAGE // No assignment or retrieval functions are declared in // ThreadSafePointerToBase, because we will have to specialize on const vs. diff --git a/panda/src/express/virtualFile.h b/panda/src/express/virtualFile.h index 0719f3f8d1..5322fce670 100644 --- a/panda/src/express/virtualFile.h +++ b/panda/src/express/virtualFile.h @@ -56,12 +56,12 @@ PUBLISHED: BLOCKING void ls(ostream &out = cout) const; BLOCKING void ls_all(ostream &out = cout) const; - EXTENSION(BLOCKING PyObject *read_file(bool auto_unwrap) const); + EXTENSION(PyObject *read_file(bool auto_unwrap) const); BLOCKING virtual istream *open_read_file(bool auto_unwrap) const; BLOCKING virtual void close_read_file(istream *stream) const; virtual bool was_read_successful() const; - EXTENSION(BLOCKING PyObject *write_file(PyObject *data, bool auto_wrap)); + EXTENSION(PyObject *write_file(PyObject *data, bool auto_wrap)); BLOCKING virtual ostream *open_write_file(bool auto_wrap, bool truncate); BLOCKING virtual ostream *open_append_file(); BLOCKING virtual void close_write_file(ostream *stream); diff --git a/panda/src/express/virtualFileSimple.cxx b/panda/src/express/virtualFileSimple.cxx index a036c0df4b..1c521b6459 100644 --- a/panda/src/express/virtualFileSimple.cxx +++ b/panda/src/express/virtualFileSimple.cxx @@ -150,7 +150,16 @@ copy_file(VirtualFile *new_file) { // Different mount point, or the mount doesn't support copying. Do it by // hand. ostream *out = new_file->open_write_file(false, true); + if (out == nullptr) { + return false; + } + istream *in = open_read_file(false); + if (in == nullptr) { + new_file->close_write_file(out); + new_file->delete_file(); + return false; + } static const size_t buffer_size = 4096; char buffer[buffer_size]; diff --git a/panda/src/express/virtualFileSystem.cxx b/panda/src/express/virtualFileSystem.cxx index c331dd1055..da7797ec9d 100644 --- a/panda/src/express/virtualFileSystem.cxx +++ b/panda/src/express/virtualFileSystem.cxx @@ -1226,6 +1226,24 @@ do_get_file(const Filename &filename, int open_flags) const { } } +#if defined(_WIN32) && !defined(NDEBUG) + if (!found_file) { + // The file could not be found. Perhaps this is because the user passed + // in a Windows-style path where a Unix-style path was expected? + if (filename.length() > 2 && isalpha(filename[0]) && filename[1] == ':' && + (filename[2] == '\\' || filename[2] == '/')) { + + Filename corrected_fn = Filename::from_os_specific(filename); + if (corrected_fn.exists()) { + express_cat.warning() + << "Filename uses Windows-style path: " << filename << "\n"; + express_cat.warning() + << " expected Unix-style path: " << corrected_fn << "\n"; + } + } + } +#endif + return found_file; } diff --git a/panda/src/express/virtualFileSystem.h b/panda/src/express/virtualFileSystem.h index 22d6316ce7..2ff1fa4e95 100644 --- a/panda/src/express/virtualFileSystem.h +++ b/panda/src/express/virtualFileSystem.h @@ -95,11 +95,11 @@ PUBLISHED: static VirtualFileSystem *get_global_ptr(); - EXTENSION(BLOCKING PyObject *read_file(const Filename &filename, bool auto_unwrap) const); + EXTENSION(PyObject *read_file(const Filename &filename, bool auto_unwrap) const); BLOCKING istream *open_read_file(const Filename &filename, bool auto_unwrap) const; BLOCKING static void close_read_file(istream *stream); - EXTENSION(BLOCKING PyObject *write_file(const Filename &filename, PyObject *data, bool auto_wrap)); + EXTENSION(PyObject *write_file(const Filename &filename, PyObject *data, bool auto_wrap)); BLOCKING ostream *open_write_file(const Filename &filename, bool auto_wrap, bool truncate); BLOCKING ostream *open_append_file(const Filename &filename); BLOCKING static void close_write_file(ostream *stream); diff --git a/panda/src/express/virtualFileSystem_ext.cxx b/panda/src/express/virtualFileSystem_ext.cxx index dc50751c52..6bc375c0d9 100644 --- a/panda/src/express/virtualFileSystem_ext.cxx +++ b/panda/src/express/virtualFileSystem_ext.cxx @@ -26,9 +26,22 @@ */ PyObject *Extension:: read_file(const Filename &filename, bool auto_unwrap) const { + // Release the GIL while we do this potentially slow operation. +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS +#endif + vector_uchar pv; bool okflag = _this->read_file(filename, pv, auto_unwrap); - nassertr(okflag, NULL); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + Py_BLOCK_THREADS +#endif + + if (!okflag) { + return PyErr_Format(PyExc_IOError, "Failed to read file: '%s'", filename.c_str()); + } #if PY_MAJOR_VERSION >= 3 if (pv.empty()) { diff --git a/panda/src/express/virtualFile_ext.cxx b/panda/src/express/virtualFile_ext.cxx index d81672cc50..db1a9a62ab 100644 --- a/panda/src/express/virtualFile_ext.cxx +++ b/panda/src/express/virtualFile_ext.cxx @@ -26,9 +26,23 @@ */ PyObject *Extension:: read_file(bool auto_unwrap) const { + // Release the GIL while we do this potentially slow operation. +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS +#endif + vector_uchar pv; bool okflag = _this->read_file(pv, auto_unwrap); - nassertr(okflag, NULL); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + Py_BLOCK_THREADS +#endif + + if (!okflag) { + Filename fn = _this->get_filename(); + return PyErr_Format(PyExc_IOError, "Failed to read file: '%s'", fn.c_str()); + } #if PY_MAJOR_VERSION >= 3 if (pv.empty()) { @@ -68,7 +82,18 @@ write_file(PyObject *data, bool auto_wrap) { } #endif + // Release the GIL while we do this potentially slow operation. +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyThreadState *_save; + Py_UNBLOCK_THREADS +#endif + bool result = _this->write_file((const unsigned char *)buffer, length, auto_wrap); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + Py_BLOCK_THREADS +#endif + return PyBool_FromLong(result); } diff --git a/panda/src/express/weakPointerToVoid.I b/panda/src/express/weakPointerToVoid.I index f55cf499cc..bc778630b7 100644 --- a/panda/src/express/weakPointerToVoid.I +++ b/panda/src/express/weakPointerToVoid.I @@ -15,16 +15,9 @@ * */ INLINE WeakPointerToVoid:: -WeakPointerToVoid() { - _ptr_was_deleted = false; - _callback = NULL; -} - -/** - * - */ -INLINE WeakPointerToVoid:: -~WeakPointerToVoid() { +WeakPointerToVoid() : + _ptr_was_deleted(false), + _callback(NULL) { } /** diff --git a/panda/src/express/weakPointerToVoid.h b/panda/src/express/weakPointerToVoid.h index e82ec63420..98a481e49e 100644 --- a/panda/src/express/weakPointerToVoid.h +++ b/panda/src/express/weakPointerToVoid.h @@ -25,7 +25,6 @@ class EXPCL_PANDAEXPRESS WeakPointerToVoid : public PointerToVoid { protected: INLINE WeakPointerToVoid(); - INLINE ~WeakPointerToVoid(); public: INLINE void mark_deleted(); diff --git a/panda/src/express/zStream.h b/panda/src/express/zStream.h index d060edba6c..b5fcd2d7a5 100644 --- a/panda/src/express/zStream.h +++ b/panda/src/express/zStream.h @@ -34,7 +34,7 @@ class EXPCL_PANDAEXPRESS IDecompressStream : public istream { PUBLISHED: INLINE IDecompressStream(); - INLINE IDecompressStream(istream *source, bool owns_source); + INLINE explicit IDecompressStream(istream *source, bool owns_source); #if _MSC_VER >= 1800 INLINE IDecompressStream(const IDecompressStream ©) = delete; @@ -60,8 +60,8 @@ private: class EXPCL_PANDAEXPRESS OCompressStream : public ostream { PUBLISHED: INLINE OCompressStream(); - INLINE OCompressStream(ostream *dest, bool owns_dest, - int compression_level = 6); + INLINE explicit OCompressStream(ostream *dest, bool owns_dest, + int compression_level = 6); #if _MSC_VER >= 1800 INLINE OCompressStream(const OCompressStream ©) = delete; diff --git a/panda/src/ffmpeg/ffmpegVideoCursor.cxx b/panda/src/ffmpeg/ffmpegVideoCursor.cxx index 8e2d0b0e47..8a23853811 100644 --- a/panda/src/ffmpeg/ffmpegVideoCursor.cxx +++ b/panda/src/ffmpeg/ffmpegVideoCursor.cxx @@ -40,6 +40,10 @@ PStatCollector FfmpegVideoCursor::_export_frame_pcollector("*:FFMPEG Convert Vid #define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO #endif +#if LIBAVCODEC_VERSION_MAJOR < 54 +#define AV_CODEC_ID_VP8 CODEC_ID_VP8 +#endif + #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 74, 100) #define AV_PIX_FMT_NONE PIX_FMT_NONE #define AV_PIX_FMT_BGR24 PIX_FMT_BGR24 @@ -116,6 +120,7 @@ init_from(FfmpegVideo *source) { _eof_known = false; _eof_frame = 0; +#if LIBAVUTIL_VERSION_MAJOR >= 52 // Check if we got an alpha format. Please note that some video codecs // (eg. libvpx) change the pix_fmt after decoding the first frame, which is // why we didn't do this earlier. @@ -123,7 +128,9 @@ init_from(FfmpegVideo *source) { if (desc && (desc->flags & AV_PIX_FMT_FLAG_ALPHA) != 0) { _num_components = 4; _pixel_format = (int)AV_PIX_FMT_BGRA; - } else { + } else +#endif + { _num_components = 3; _pixel_format = (int)AV_PIX_FMT_BGR24; } @@ -320,13 +327,13 @@ set_time(double timestamp, int loop_count) { if (_eof_known) { if (loop_count == 0) { - frame = frame % _eof_frame; + frame = frame % (_eof_frame + 1); } else { - int last_frame = _eof_frame * loop_count; + int last_frame = (_eof_frame + 1) * loop_count; if (frame < last_frame) { - frame = frame % _eof_frame; + frame = frame % (_eof_frame + 1); } else { - frame = _eof_frame - 1; + frame = _eof_frame; } } } diff --git a/panda/src/glstuff/glCgShaderContext_src.I b/panda/src/glstuff/glCgShaderContext_src.I index b2c176f7d1..08c947f0fb 100644 --- a/panda/src/glstuff/glCgShaderContext_src.I +++ b/panda/src/glstuff/glCgShaderContext_src.I @@ -10,37 +10,3 @@ * @author rdb * @date 2014-06-27 */ - -#ifndef OPENGLES_1 - -/** - * Returns true if the shader is "valid", ie, if the compilation was - * successful. The compilation could fail if there is a syntax error in the - * shader, or if the current video card isn't shader-capable, or if no shader - * languages are compiled into panda. - */ -INLINE bool CLP(CgShaderContext):: -valid() { - if (_shader->get_error_flag()) return false; - if (_shader->get_language() != Shader::SL_Cg) return false; - return (_cg_program != 0); -} - -/** - * Returns true if the shader may need to access standard vertex attributes as - * passed by glVertexPointer and the like. - */ -INLINE bool CLP(CgShaderContext):: -uses_standard_vertex_arrays() { - return false; -} - -/** - * Always true, for now. - */ -INLINE bool CLP(CgShaderContext):: -uses_custom_vertex_arrays() { - return true; -} - -#endif // OPENGLES_1 diff --git a/panda/src/glstuff/glCgShaderContext_src.cxx b/panda/src/glstuff/glCgShaderContext_src.cxx index c857948b97..9f6e242968 100644 --- a/panda/src/glstuff/glCgShaderContext_src.cxx +++ b/panda/src/glstuff/glCgShaderContext_src.cxx @@ -145,7 +145,7 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte if (cgGetParameterBaseResource(p) == CG_ATTR0) { // The Cg toolkit claims that it is bound to a generic vertex attribute. - if (_glsl_program != 0) { + if (_glgsg->has_fixed_function_pipeline() && _glsl_program != 0) { // This is where the Cg glslv compiler lies, making the stupid // assumption that we're using an NVIDIA card where generic attributes // are aliased with conventional vertex attributes. Instead, it @@ -246,7 +246,7 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte // A conventional texture coordinate set. loc = CA_texcoord + cgGetParameterResourceIndex(p); - } else { + } else if (_glgsg->has_fixed_function_pipeline()) { // Some other conventional vertex attribute. switch (res) { case CG_POSITION0: @@ -276,6 +276,14 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte GLCAT.error(false) << ".\n"; loc = CA_unknown; } + } else { + GLCAT.error() + << "Cg varying " << cgGetParameterName(p); + if (cgGetParameterSemantic(p)) { + GLCAT.error(false) << " : " << cgGetParameterSemantic(p); + } + GLCAT.error(false) << " is bound to a conventional vertex attribute, " + "but the compatibility profile is not enabled.\n"; } #ifndef NDEBUG @@ -349,6 +357,20 @@ release_resources() { _glgsg->report_my_gl_errors(); } +/** + * Returns true if the shader is "valid", ie, if the compilation was + * successful. The compilation could fail if there is a syntax error in the + * shader, or if the current video card isn't shader-capable, or if no shader + * languages are compiled into panda. + */ +bool CLP(CgShaderContext):: +valid() { + if (_shader == nullptr || _shader->get_error_flag()) { + return false; + } + return (_cg_program != 0); +} + /** * This function is to be called to enable a new shader. It also initializes * all of the shader's input parameters. @@ -391,6 +413,7 @@ unbind() { void CLP(CgShaderContext):: set_state_and_transform(const RenderState *target_rs, const TransformState *modelview_transform, + const TransformState *camera_transform, const TransformState *projection_transform) { if (!valid()) { @@ -402,6 +425,10 @@ set_state_and_transform(const RenderState *target_rs, if (_modelview_transform != modelview_transform) { _modelview_transform = modelview_transform; + altered |= (Shader::SSD_transform & ~Shader::SSD_view_transform); + } + if (_camera_transform != camera_transform) { + _camera_transform = camera_transform; altered |= Shader::SSD_transform; } if (_projection_transform != projection_transform) { @@ -916,8 +943,7 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) { } else { // There is no vertex column with this name; disable the attribute // array. -#ifdef SUPPORT_FIXED_FUNCTION - if (p == 0) { + if (_glgsg->has_fixed_function_pipeline() && p == 0) { // NOTE: if we disable attribute 0 in compatibility profile, the // object will disappear. In GLSL we fix this by forcing the vertex // column to be at 0, but we don't have control over that with Cg. @@ -930,9 +956,7 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) { _glgsg->_glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); } - } else -#endif // SUPPORT_FIXED_FUNCTION - if (p >= 0) { + } else if (p >= 0) { _glgsg->disable_vertex_attrib_array(p); if (p == _color_attrib_index) { @@ -1068,8 +1092,7 @@ update_shader_texture_bindings(ShaderContext *prev) { if (tex.is_null()) { // Apply a white texture in order to make it easier to use a shader that // takes a texture on a model that doesn't have a texture applied. - _glgsg->set_active_texture_stage(i); - _glgsg->apply_white_texture(); + _glgsg->apply_white_texture(i); continue; } diff --git a/panda/src/glstuff/glCgShaderContext_src.h b/panda/src/glstuff/glCgShaderContext_src.h index ae76989d77..d5bc721e61 100644 --- a/panda/src/glstuff/glCgShaderContext_src.h +++ b/panda/src/glstuff/glCgShaderContext_src.h @@ -25,7 +25,7 @@ class CLP(GraphicsStateGuardian); /** * xyz */ -class EXPCL_GL CLP(CgShaderContext) : public ShaderContext { +class EXPCL_GL CLP(CgShaderContext) FINAL : public ShaderContext { public: friend class CLP(GraphicsStateGuardian); @@ -33,24 +33,25 @@ public: ~CLP(CgShaderContext)(); ALLOC_DELETED_CHAIN(CLP(CgShaderContext)); - INLINE bool valid(void); - void bind() OVERRIDE; - void unbind() OVERRIDE; + bool valid(void) override; + void bind() override; + void unbind() override; void set_state_and_transform(const RenderState *state, const TransformState *modelview_transform, - const TransformState *projection_transform); + const TransformState *camera_transform, + const TransformState *projection_transform) override; - void issue_parameters(int altered) OVERRIDE; + void issue_parameters(int altered) override; void update_transform_table(const TransformTable *table); void update_slider_table(const SliderTable *table); - void disable_shader_vertex_arrays() OVERRIDE; - bool update_shader_vertex_arrays(ShaderContext *prev, bool force) OVERRIDE; - void disable_shader_texture_bindings() OVERRIDE; - void update_shader_texture_bindings(ShaderContext *prev) OVERRIDE; + void disable_shader_vertex_arrays() override; + bool update_shader_vertex_arrays(ShaderContext *prev, bool force) override; + void disable_shader_texture_bindings() override; + void update_shader_texture_bindings(ShaderContext *prev) override; - INLINE bool uses_standard_vertex_arrays(void); - INLINE bool uses_custom_vertex_arrays(void); + bool uses_standard_vertex_arrays(void) override { return false; } + bool uses_custom_vertex_arrays(void) override { return true; } // Special values for location to indicate conventional attrib slots. enum ConventionalAttrib { @@ -77,6 +78,7 @@ private: WCPT(RenderState) _state_rs; CPT(TransformState) _modelview_transform; + CPT(TransformState) _camera_transform; CPT(TransformState) _projection_transform; GLint _frame_number; @@ -93,10 +95,10 @@ public: register_type(_type_handle, CLASSPREFIX_QUOTED "CgShaderContext", ShaderContext::get_class_type()); } - virtual TypeHandle get_type() const { + virtual TypeHandle get_type() const override { return get_class_type(); } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + virtual TypeHandle force_init_type() override {init_type(); return get_class_type();} private: static TypeHandle _type_handle; diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index ad09596f9c..e76f8fccb9 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -95,8 +95,7 @@ clear(Thread *current_thread) { return; } - CLP(GraphicsStateGuardian) *glgsg; - DCAST_INTO_V(glgsg, _gsg); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); if (glgsg->_glClearBufferfv == NULL) { // We can't efficiently clear the buffer. Fall back to the inefficient @@ -254,8 +253,7 @@ begin_frame(FrameMode mode, Thread *current_thread) { // until we call glBlitFramebuffer. #ifndef OPENGLES_1 if (gl_enable_memory_barriers && _fbo_multisample == 0) { - CLP(GraphicsStateGuardian) *glgsg; - DCAST_INTO_R(glgsg, _gsg, false); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); TextureContexts::iterator it; for (it = _texture_contexts.begin(); it != _texture_contexts.end(); ++it) { @@ -269,6 +267,9 @@ begin_frame(FrameMode mode, Thread *current_thread) { } } #endif + } else if (mode == FM_refresh) { + // Just bind the FBO. + rebuild_bitplanes(); } _gsg->set_current_properties(&get_fb_properties()); @@ -282,8 +283,7 @@ begin_frame(FrameMode mode, Thread *current_thread) { */ bool CLP(GraphicsBuffer):: check_fbo() { - CLP(GraphicsStateGuardian) *glgsg; - DCAST_INTO_R(glgsg, _gsg, false); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); GLenum status = glgsg->_glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { @@ -338,8 +338,7 @@ rebuild_bitplanes() { return; } - CLP(GraphicsStateGuardian) *glgsg; - DCAST_INTO_V(glgsg, _gsg); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); if (!_needs_rebuild) { if (_fbo_multisample != 0) { @@ -682,8 +681,7 @@ rebuild_bitplanes() { */ void CLP(GraphicsBuffer):: bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, GLenum attachpoint) { - CLP(GraphicsStateGuardian) *glgsg; - DCAST_INTO_V(glgsg, _gsg); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); Texture *tex = attach[slot]; @@ -1017,8 +1015,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, */ void CLP(GraphicsBuffer):: bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot, GLenum attachpoint) { - CLP(GraphicsStateGuardian) *glgsg; - DCAST_INTO_V(glgsg, _gsg); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); if ((_rbm[slot] != 0)&&(!rb_resize)) { return; @@ -1141,8 +1138,7 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot, */ void CLP(GraphicsBuffer):: attach_tex(int layer, int view, Texture *attach, GLenum attachpoint) { - CLP(GraphicsStateGuardian) *glgsg; - DCAST_INTO_V(glgsg, _gsg); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); if (view >= attach->get_num_views()) { attach->set_num_views(view + 1); @@ -1162,8 +1158,10 @@ attach_tex(int layer, int view, Texture *attach, GLenum attachpoint) { glgsg->apply_texture(gtc); #if !defined(OPENGLES) && defined(SUPPORT_FIXED_FUNCTION) - GLclampf priority = 1.0f; - glPrioritizeTextures(1, >c->_index, &priority); + if (glgsg->has_fixed_function_pipeline()) { + GLclampf priority = 1.0f; + glPrioritizeTextures(1, >c->_index, &priority); + } #endif #ifndef OPENGLES @@ -1210,8 +1208,7 @@ generate_mipmaps() { return; } - CLP(GraphicsStateGuardian) *glgsg; - DCAST_INTO_V(glgsg, _gsg); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); // PStatGPUTimer timer(glgsg, _generate_mipmap_pcollector); @@ -1248,8 +1245,7 @@ end_frame(FrameMode mode, Thread *current_thread) { // Unbind the FBO. TODO: calling bind_fbo is slow, so we should probably // move this to begin_frame to prevent unnecessary calls. - CLP(GraphicsStateGuardian) *glgsg; - DCAST_INTO_V(glgsg, _gsg); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); glgsg->bind_fbo(0); _bound_tex_page = -1; @@ -1288,8 +1284,7 @@ void CLP(GraphicsBuffer):: select_target_tex_page(int page) { nassertv(page >= 0 && page < _fbo.size()); - CLP(GraphicsStateGuardian) *glgsg; - DCAST_INTO_V(glgsg, _gsg); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); bool switched_page = (_bound_tex_page != page); @@ -1506,6 +1501,16 @@ open_buffer() { return true; } +/** + * This is normally called only from within make_texture_buffer(). When + * called on a ParasiteBuffer, it returns the host of that buffer; but when + * called on some other buffer, it returns the buffer itself. + */ +GraphicsOutput *CLP(GraphicsBuffer):: +get_host() { + return _host; +} + /** * Closes the buffer right now. Called from the window thread. */ @@ -1684,8 +1689,7 @@ report_my_errors(int line, const char *file) { GLCAT.error() << file << ", line " << line << ": GL error " << (int)error_code << "\n"; } } else { - CLP(GraphicsStateGuardian) *glgsg; - DCAST_INTO_V(glgsg, _gsg); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); glgsg->report_my_errors(line, file); } } @@ -1718,8 +1722,7 @@ void CLP(GraphicsBuffer):: resolve_multisamples() { nassertv(_fbo.size() > 0); - CLP(GraphicsStateGuardian) *glgsg; - DCAST_INTO_V(glgsg, _gsg); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); PStatGPUTimer timer(glgsg, _resolve_multisample_pcollector); diff --git a/panda/src/glstuff/glGraphicsBuffer_src.h b/panda/src/glstuff/glGraphicsBuffer_src.h index 9f99850a7f..d119148247 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.h +++ b/panda/src/glstuff/glGraphicsBuffer_src.h @@ -77,6 +77,8 @@ public: void unregister_shared_depth_buffer(GraphicsOutput *graphics_output); protected: + virtual GraphicsOutput *get_host(); + virtual void close_buffer(); virtual bool open_buffer(); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.I b/panda/src/glstuff/glGraphicsStateGuardian_src.I index 221aff21b9..5ce7902917 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.I +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.I @@ -130,6 +130,33 @@ get_gl_version_minor() const { return _gl_version_minor; } +/** + * Returns whether a core profile or a compatibility mode is considered. + */ +/*INLINE bool CLP(GraphicsStateGuardian):: +has_core_profile() const { + return _core_profile; +}*/ + +/** + * Returns whether the fixed function pipeline is supported. + */ +INLINE bool CLP(GraphicsStateGuardian):: +has_fixed_function_pipeline() const { +#ifndef SUPPORT_FIXED_FUNCTION + return false; +#elif defined(OPENGLES_1) + return true; +#elif defined(OPENGLES) + return false; +#else + // Otherwise, we can just check whether we are using a core profile or a + // compatibility mode. The variable _core_profile is already taking into + // account if a GL < 3.2 is considered (becoming false) + return !_core_profile; +#endif +} + /** * Calls glFinish() if the config variable gl-finish is set True. */ @@ -363,7 +390,7 @@ enable_line_smooth(bool val) { INLINE void CLP(GraphicsStateGuardian):: enable_point_smooth(bool val) { #ifdef SUPPORT_FIXED_FUNCTION - if (_point_smooth_enabled != val) { + if (has_fixed_function_pipeline() && _point_smooth_enabled != val) { _state_mask.clear_bit(TransparencyAttrib::get_class_slot()); _point_smooth_enabled = val; if (val) { diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 98c21610c5..dce9cc7d89 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -174,7 +174,7 @@ static const string default_vshader = "void main(void) {\n" " gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;\n" " texcoord = p3d_MultiTexCoord0;\n" - " color = p3d_Color;\n" + " color = p3d_Color * p3d_ColorScale;\n" "}\n"; static const string default_fshader = @@ -369,6 +369,12 @@ CLP(GraphicsStateGuardian)(GraphicsEngine *engine, GraphicsPipe *pipe) : _gl_shadlang_ver_major = 0; _gl_shadlang_ver_minor = 0; + // Let's say we have a core profile, to be checked later (Otherwise, if we are + // wrong the user may ask for some non-available functions) +#ifndef OPENGLES + _core_profile = true; +#endif + // Hack. Turn on the flag that we turned off at a higher level, since we // know this works properly in OpenGL, and we want the performance benefit // it gives us. @@ -547,6 +553,7 @@ reset() { GLCAT.debug() << "Using compatibility profile\n"; } } + _core_profile = core_profile; #elif defined(OPENGLES_1) static const bool core_profile = false; #else @@ -723,7 +730,7 @@ reset() { #endif #if !defined(OPENGLES) && defined(SUPPORT_FIXED_FUNCTION) - if (is_at_least_gl_version(1, 4)) { + if (has_fixed_function_pipeline() && is_at_least_gl_version(1, 4)) { _glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC) get_extension_func("glSecondaryColorPointer"); @@ -922,7 +929,7 @@ reset() { #endif #ifndef OPENGLES - if (is_at_least_gl_version(3, 0)) { + if (is_at_least_gl_version(3, 1)) { _glTexBuffer = (PFNGLTEXBUFFERPROC)get_extension_func("glTexBuffer"); _supports_buffer_texture = true; @@ -1105,12 +1112,13 @@ reset() { _supports_rescale_normal = true; #else _supports_rescale_normal = - !core_profile && gl_support_rescale_normal && + has_fixed_function_pipeline() && gl_support_rescale_normal && (is_at_least_gl_version(1, 2) || has_extension("GL_EXT_rescale_normal")); #endif #ifndef OPENGLES - _use_separate_specular_color = gl_separate_specular_color && + _use_separate_specular_color = + has_fixed_function_pipeline() && gl_separate_specular_color && (is_at_least_gl_version(1, 2) || has_extension("GL_EXT_separate_specular_color")); #endif #endif // SUPPORT_FIXED_FUNCTION @@ -1131,7 +1139,7 @@ reset() { _supports_multisample = false; #else _supports_multisample = - has_extension("GL_ARB_multisample") || is_at_least_gl_version(1, 3); + is_at_least_gl_version(1, 3) || has_extension("GL_ARB_multisample"); #endif #ifdef OPENGLES_1 @@ -1162,8 +1170,10 @@ reset() { get_extension_func("glActiveTexture"); #ifdef SUPPORT_FIXED_FUNCTION - _glClientActiveTexture = (PFNGLACTIVETEXTUREPROC) - get_extension_func("glClientActiveTexture"); + if (has_fixed_function_pipeline()) { + _glClientActiveTexture = (PFNGLACTIVETEXTUREPROC) + get_extension_func("glClientActiveTexture"); + } #endif #ifdef SUPPORT_IMMEDIATE_MODE @@ -1192,8 +1202,10 @@ reset() { get_extension_func("glActiveTextureARB"); #ifdef SUPPORT_FIXED_FUNCTION - _glClientActiveTexture = (PFNGLACTIVETEXTUREPROC) - get_extension_func("glClientActiveTextureARB"); + if (has_fixed_function_pipeline()) { + _glClientActiveTexture = (PFNGLACTIVETEXTUREPROC) + get_extension_func("glClientActiveTextureARB"); + } #endif #ifdef SUPPORT_IMMEDIATE_MODE @@ -1221,7 +1233,7 @@ reset() { if (supports_multitexture) { if (_glActiveTexture == NULL #ifdef SUPPORT_FIXED_FUNCTION - || _glClientActiveTexture == NULL + || (has_fixed_function_pipeline() && _glClientActiveTexture == NULL) #endif #ifdef SUPPORT_IMMEDIATE_MODE || GLf(_glMultiTexCoord1) == NULL || GLf(_glMultiTexCoord2) == NULL @@ -1240,7 +1252,7 @@ reset() { } #ifdef SUPPORT_FIXED_FUNCTION - if (!supports_multitexture || core_profile) { + if (!supports_multitexture || !has_fixed_function_pipeline()) { _glClientActiveTexture = null_glActiveTexture; } #endif @@ -1251,6 +1263,7 @@ reset() { _supports_depth_stencil = has_extension("GL_OES_packed_depth_stencil"); _supports_depth24 = has_extension("GL_OES_depth24"); _supports_depth32 = has_extension("GL_OES_depth32"); + _supports_luminance_texture = true; #elif defined(OPENGLES) if (is_at_least_gles_version(3, 0)) { @@ -1271,12 +1284,16 @@ reset() { _supports_depth24 = has_extension("GL_OES_depth24"); _supports_depth32 = has_extension("GL_OES_depth32"); } + _supports_luminance_texture = true; #else _supports_depth_texture = (is_at_least_gl_version(1, 4) || has_extension("GL_ARB_depth_texture")); _supports_depth_stencil = (is_at_least_gl_version(3, 0) || has_extension("GL_ARB_framebuffer_object") || has_extension("GL_EXT_packed_depth_stencil")); + + // OpenGL 3 deprecates luminance, luminance-alpha and alpha textures. + _supports_luminance_texture = !core_profile; #endif #ifdef OPENGLES_2 @@ -1288,7 +1305,7 @@ reset() { if (gl_support_shadow_filter && _supports_depth_texture && (is_at_least_gl_version(1, 4) || has_extension("GL_ARB_shadow")) && - has_extension("GL_ARB_fragment_program_shadow")) { + (is_at_least_gl_version(2, 0) || has_extension("GL_ARB_fragment_program_shadow"))) { _supports_shadow_filter = true; } #endif @@ -1300,12 +1317,11 @@ reset() { _supports_shadow_filter = false; }*/ -#ifndef SUPPORT_FIXED_FUNCTION _supports_texture_combine = false; _supports_texture_saved_result = false; _supports_texture_dot3 = false; -#else - if (!core_profile) { +#ifdef SUPPORT_FIXED_FUNCTION + if (has_fixed_function_pipeline()) { _supports_texture_combine = is_at_least_gl_version(1, 3) || is_at_least_gles_version(1, 1) || @@ -1786,12 +1802,8 @@ reset() { // We need to have a default shader to apply in case something didn't happen // to have a shader applied, or if it failed to compile. This default // shader just outputs a red color, indicating that something went wrong. -#ifndef SUPPORT_FIXED_FUNCTION - if (_default_shader == NULL) { - _default_shader = Shader::make(Shader::SL_GLSL, default_vshader, default_fshader); - } -#elif !defined(OPENGLES) - if (_default_shader == NULL && core_profile) { +#ifndef OPENGLES_1 + if (_default_shader == NULL && !has_fixed_function_pipeline()) { _default_shader = Shader::make(Shader::SL_GLSL, default_vshader, default_fshader); } #endif @@ -2123,7 +2135,7 @@ reset() { _glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) get_extension_func("glGenerateMipmap"); _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) - get_extension_func("glRenderbufferStorageMultisampleEXT"); + get_extension_func("glRenderbufferStorageMultisample"); _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) get_extension_func("glBlitFramebuffer"); @@ -2538,8 +2550,8 @@ reset() { _border_clamp = _edge_clamp; #ifndef OPENGLES if (gl_support_clamp_to_border && - (has_extension("GL_ARB_texture_border_clamp") || - is_at_least_gl_version(1, 3))) { + (is_at_least_gl_version(1, 3) || + has_extension("GL_ARB_texture_border_clamp"))) { _border_clamp = GL_CLAMP_TO_BORDER; } #endif @@ -2568,6 +2580,11 @@ reset() { _mirror_clamp = GL_MIRROR_CLAMP_EXT; _mirror_edge_clamp = GL_MIRROR_CLAMP_TO_EDGE_EXT; _mirror_border_clamp = GL_MIRROR_CLAMP_TO_BORDER_EXT; + + } else if (is_at_least_gl_version(4, 4) || + has_extension("GL_ARB_texture_mirror_clamp_to_edge")) { + _mirror_clamp = GL_MIRROR_CLAMP_TO_EDGE; + _mirror_edge_clamp = GL_MIRROR_CLAMP_TO_EDGE; } #endif @@ -2943,7 +2960,7 @@ reset() { glDisable(GL_LINE_SMOOTH); #endif #ifdef SUPPORT_FIXED_FUNCTION - if (!core_profile) { + if (has_fixed_function_pipeline()) { glDisable(GL_POINT_SMOOTH); } #endif @@ -2995,7 +3012,7 @@ reset() { // Count the max number of lights _max_lights = 0; #ifdef SUPPORT_FIXED_FUNCTION - if (!core_profile) { + if (has_fixed_function_pipeline()) { GLint max_lights = 0; glGetIntegerv(GL_MAX_LIGHTS, &max_lights); _max_lights = max_lights; @@ -3010,7 +3027,7 @@ reset() { // Count the max number of clipping planes _max_clip_planes = 0; #ifdef SUPPORT_FIXED_FUNCTION - if (!core_profile) { + if (has_fixed_function_pipeline()) { GLint max_clip_planes = 0; glGetIntegerv(GL_MAX_CLIP_PLANES, &max_clip_planes); _max_clip_planes = max_clip_planes; @@ -3024,7 +3041,7 @@ reset() { _max_texture_stages = 1; #ifdef SUPPORT_FIXED_FUNCTION - if (supports_multitexture && !core_profile) { + if (supports_multitexture && has_fixed_function_pipeline()) { GLint max_texture_stages = 0; glGetIntegerv(GL_MAX_TEXTURE_UNITS, &max_texture_stages); _max_texture_stages = max_texture_stages; @@ -3057,7 +3074,7 @@ reset() { report_my_gl_errors(); #ifdef SUPPORT_FIXED_FUNCTION - if (!core_profile) { + if (has_fixed_function_pipeline()) { if (gl_cheap_textures) { GLCAT.info() << "Setting glHint() for fastest textures.\n"; @@ -3070,7 +3087,7 @@ reset() { #endif #ifdef SUPPORT_FIXED_FUNCTION - if (!core_profile) { + if (has_fixed_function_pipeline()) { GLint num_red_bits = 0; glGetIntegerv(GL_RED_BITS, &num_red_bits); if (num_red_bits < 8) { @@ -3110,6 +3127,10 @@ reset() { } #endif + // Do we guarantee that we can apply the color scale via a shader? We set + // this false if there is a chance that the fixed-function pipeline is used. + _runtime_color_scale = !has_fixed_function_pipeline(); + #ifndef OPENGLES if (_gl_shadlang_ver_major >= 4 || has_extension("GL_NV_gpu_program5")) { // gp5fp - OpenGL fragment profile for GeForce 400 Series and up @@ -3538,7 +3559,9 @@ prepare_display_region(DisplayRegionPipelineReader *dr) { void CLP(GraphicsStateGuardian):: clear_before_callback() { #ifdef SUPPORT_FIXED_FUNCTION - disable_standard_vertex_arrays(); + if (has_fixed_function_pipeline()) { + disable_standard_vertex_arrays(); + } #endif #ifndef OPENGLES_1 if (_vertex_array_shader_context != 0) { @@ -3620,16 +3643,18 @@ calc_projection_mat(const Lens *lens) { bool CLP(GraphicsStateGuardian):: prepare_lens() { #ifdef SUPPORT_FIXED_FUNCTION - if (GLCAT.is_spam()) { - GLCAT.spam() - << "glMatrixMode(GL_PROJECTION): " << _projection_mat->get_mat() << endl; + if (has_fixed_function_pipeline()) { + if (GLCAT.is_spam()) { + GLCAT.spam() + << "glMatrixMode(GL_PROJECTION): " << _projection_mat->get_mat() << endl; + } + + glMatrixMode(GL_PROJECTION); + call_glLoadMatrix(_projection_mat->get_mat()); + report_my_gl_errors(); + + do_point_size(); } - - glMatrixMode(GL_PROJECTION); - call_glLoadMatrix(_projection_mat->get_mat()); - report_my_gl_errors(); - - do_point_size(); #endif #ifndef OPENGLES_1 @@ -3762,7 +3787,7 @@ end_frame(Thread *current_thread) { // will just mean that we'll count everything as resident until the user // connects PStats, at which point it will then correct the assessment. No // harm done. - if (PStatClient::is_connected()) { + if (has_fixed_function_pipeline() && PStatClient::is_connected()) { check_nonresident_texture(_prepared_objects->_texture_residency.get_inactive_resident()); check_nonresident_texture(_prepared_objects->_texture_residency.get_active_resident()); @@ -3813,7 +3838,7 @@ end_frame(Thread *current_thread) { // Now is a good time to delete any pending display lists. #ifndef OPENGLES #ifdef SUPPORT_FIXED_FUNCTION - if (display_lists) { + if (has_fixed_function_pipeline() && display_lists) { LightMutexHolder holder(_lock); if (!_deleted_display_lists.empty()) { DeletedNames::iterator ddli; @@ -3913,7 +3938,6 @@ end_frame(Thread *current_thread) { */ bool CLP(GraphicsStateGuardian):: begin_draw_primitives(const GeomPipelineReader *geom_reader, - const GeomMunger *munger, const GeomVertexDataPipelineReader *data_reader, bool force) { #ifndef NDEBUG @@ -3922,15 +3946,17 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader, } #endif // NDEBUG -#ifndef SUPPORT_FIXED_FUNCTION - // We can't draw without a shader bound in OpenGL ES 2. This shouldn't - // happen anyway unless the default shader failed to compile somehow. - if (_current_shader_context == NULL) { - return false; +#ifndef OPENGLES_1 + if (!has_fixed_function_pipeline()) { + // We can't draw without a shader bound in OpenGL ES 2. This shouldn't + // happen anyway unless the default shader failed to compile somehow. + if (_current_shader_context == NULL) { + return false; + } } #endif - if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger, data_reader, force)) { + if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, data_reader, force)) { return false; } nassertr(_data_reader != (GeomVertexDataPipelineReader *)NULL, false); @@ -3967,7 +3993,7 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader, } #ifdef SUPPORT_FIXED_FUNCTION - if (_data_reader->is_vertex_transformed()) { + if (has_fixed_function_pipeline() && _data_reader->is_vertex_transformed()) { // If the vertex data claims to be already transformed into clip // coordinates, wipe out the current projection and modelview matrix (so // we don't attempt to transform it again). @@ -3981,7 +4007,8 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader, #endif #if !defined(OPENGLES) && defined(SUPPORT_FIXED_FUNCTION) // Display lists not supported by OpenGL ES. - if (/*geom_reader->get_usage_hint() == Geom::UH_static &&*/ + if (has_fixed_function_pipeline() && + /*geom_reader->get_usage_hint() == Geom::UH_static &&*/ _data_reader->get_usage_hint() == Geom::UH_static && display_lists) { // If the geom claims to be totally static, try to build it into a display @@ -3994,10 +4021,10 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader, GeomContext *gc = geom_reader->prepare_now(get_prepared_objects(), this); nassertr(gc != (GeomContext *)NULL, false); CLP(GeomContext) *ggc = DCAST(CLP(GeomContext), gc); - const CLP(GeomMunger) *gmunger = DCAST(CLP(GeomMunger), _munger); + //const CLP(GeomMunger) *gmunger = DCAST(CLP(GeomMunger), _munger); UpdateSeq modified = max(geom_reader->get_modified(), _data_reader->get_modified()); - if (ggc->get_display_list(_geom_display_list, gmunger, modified)) { + if (ggc->get_display_list(_geom_display_list, nullptr, modified)) { // If it hasn't been modified, just play the display list again. if (GLCAT.is_spam()) { GLCAT.spam() @@ -4072,33 +4099,35 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader, _vertex_array_shader_context->disable_shader_vertex_arrays(); } #ifdef SUPPORT_FIXED_FUNCTION - if (!update_standard_vertex_arrays(force)) { + if (has_fixed_function_pipeline() && !update_standard_vertex_arrays(force)) { return false; } #endif } else { #ifdef SUPPORT_FIXED_FUNCTION - // Shader. - if (_vertex_array_shader_context == 0 || - _vertex_array_shader_context->uses_standard_vertex_arrays()) { - // Previous shader used standard arrays. - if (_current_shader_context->uses_standard_vertex_arrays()) { - // So does the current, so update them. - if (!update_standard_vertex_arrays(force)) { - return false; + if (has_fixed_function_pipeline()) { + // Shader. + if (_vertex_array_shader_context == 0 || + _vertex_array_shader_context->uses_standard_vertex_arrays()) { + // Previous shader used standard arrays. + if (_current_shader_context->uses_standard_vertex_arrays()) { + // So does the current, so update them. + if (!update_standard_vertex_arrays(force)) { + return false; + } + } else { + // The current shader does not, so disable them entirely. + disable_standard_vertex_arrays(); } - } else { - // The current shader does not, so disable them entirely. - disable_standard_vertex_arrays(); } - } #ifdef HAVE_CG - else if (_vertex_array_shader_context->is_of_type(CLP(CgShaderContext)::get_class_type())) { - // The previous shader was a Cg shader, which can leave a messy - // situation. - _vertex_array_shader_context->disable_shader_vertex_arrays(); - } + else if (_vertex_array_shader_context->is_of_type(CLP(CgShaderContext)::get_class_type())) { + // The previous shader was a Cg shader, which can leave a messy + // situation. + _vertex_array_shader_context->disable_shader_vertex_arrays(); + } #endif + } #endif // SUPPORT_FIXED_FUNCTION // Now update the vertex arrays for the current shader. if (!_current_shader_context-> @@ -4334,7 +4363,9 @@ unbind_buffers() { #endif #ifdef SUPPORT_FIXED_FUNCTION - disable_standard_vertex_arrays(); + if (has_fixed_function_pipeline()) { + disable_standard_vertex_arrays(); + } #endif } @@ -5040,7 +5071,7 @@ draw_points(const GeomPrimitivePipelineReader *reader, bool force) { void CLP(GraphicsStateGuardian):: end_draw_primitives() { #if !defined(OPENGLES) && defined(SUPPORT_FIXED_FUNCTION) // Display lists not supported by OpenGL ES. - if (_geom_display_list != 0) { + if (has_fixed_function_pipeline() && _geom_display_list != 0) { // If we were building a display list, close it now. glEndList(); _load_display_list_pcollector.stop(); @@ -5054,12 +5085,12 @@ end_draw_primitives() { #endif #ifdef SUPPORT_FIXED_FUNCTION - if (_transform_stale) { + if (has_fixed_function_pipeline() && _transform_stale) { glMatrixMode(GL_MODELVIEW); call_glLoadMatrix(_internal_transform->get_mat()); } - if (_data_reader->is_vertex_transformed()) { + if (has_fixed_function_pipeline() && _data_reader->is_vertex_transformed()) { // Restore the matrices that we pushed above. glMatrixMode(GL_PROJECTION); glPopMatrix(); @@ -5436,7 +5467,9 @@ prepare_geom(Geom *geom) { void CLP(GraphicsStateGuardian):: release_geom(GeomContext *gc) { CLP(GeomContext) *ggc = DCAST(CLP(GeomContext), gc); - ggc->release_display_lists(); + if (has_fixed_function_pipeline()) { + ggc->release_display_lists(); + } report_my_gl_errors(); delete ggc; @@ -6422,6 +6455,18 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z, } break; + case Texture::F_depth_component16: + component_type = Texture::T_unsigned_short; + break; + + case Texture::F_depth_component24: + component_type = Texture::T_unsigned_int; + break; + + case Texture::F_depth_component32: + component_type = Texture::T_float; + break; + default: if (_current_properties->get_srgb_color()) { if (_current_properties->get_alpha_bits()) { @@ -6594,17 +6639,19 @@ apply_fog(Fog *fog) { void CLP(GraphicsStateGuardian):: do_issue_transform() { #ifdef SUPPORT_FIXED_FUNCTION - // OpenGL ES 2 does not support glLoadMatrix. + if (has_fixed_function_pipeline()) { + // OpenGL ES 2 does not support glLoadMatrix. - const TransformState *transform = _internal_transform; - if (GLCAT.is_spam()) { - GLCAT.spam() - << "glLoadMatrix(GL_MODELVIEW): " << transform->get_mat() << endl; + const TransformState *transform = _internal_transform; + if (GLCAT.is_spam()) { + GLCAT.spam() + << "glLoadMatrix(GL_MODELVIEW): " << transform->get_mat() << endl; + } + + DO_PSTATS_STUFF(_transform_state_pcollector.add_level(1)); + glMatrixMode(GL_MODELVIEW); + call_glLoadMatrix(transform->get_mat()); } - - DO_PSTATS_STUFF(_transform_state_pcollector.add_level(1)); - glMatrixMode(GL_MODELVIEW); - call_glLoadMatrix(transform->get_mat()); #endif _transform_stale = false; @@ -6643,13 +6690,11 @@ do_issue_shader() { ShaderContext *context = 0; Shader *shader = (Shader *)_target_shader->get_shader(); -#ifndef SUPPORT_FIXED_FUNCTION // If we don't have a shader, apply the default shader. - if (!shader) { + if (!has_fixed_function_pipeline() && !shader) { shader = _default_shader; nassertv(shader != NULL); } -#endif if (shader) { if (_current_shader != shader) { @@ -6659,9 +6704,9 @@ do_issue_shader() { } } -#ifndef SUPPORT_FIXED_FUNCTION // If it failed, try applying the default shader. - if (shader != _default_shader && (context == 0 || !context->valid())) { + if (_default_shader != nullptr && shader != _default_shader && + (context == 0 || !context->valid())) { shader = _default_shader; nassertv(shader != NULL); if (_current_shader != shader) { @@ -6670,7 +6715,6 @@ do_issue_shader() { context = _current_shader_context; } } -#endif if (context == 0 || !context->valid()) { if (_current_shader_context != 0) { @@ -6762,7 +6806,9 @@ do_issue_render_mode() { report_my_gl_errors(); #ifdef SUPPORT_FIXED_FUNCTION - do_point_size(); + if (has_fixed_function_pipeline()) { + do_point_size(); + } #endif } @@ -7372,6 +7418,8 @@ do_issue_blending() { */ void CLP(GraphicsStateGuardian):: bind_light(PointLight *light_obj, const NodePath &light, int light_id) { + nassertv(has_fixed_function_pipeline()); + // static PStatCollector // _draw_set_state_light_bind_point_pcollector("Draw:Set // State:Light:Bind:Point"); PStatGPUTimer timer(this, @@ -7416,6 +7464,8 @@ bind_light(PointLight *light_obj, const NodePath &light, int light_id) { */ void CLP(GraphicsStateGuardian):: bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) { + nassertv(has_fixed_function_pipeline()); + // static PStatCollector // _draw_set_state_light_bind_directional_pcollector("Draw:Set // State:Light:Bind:Directional"); PStatGPUTimer timer(this, @@ -7467,6 +7517,8 @@ bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) { */ void CLP(GraphicsStateGuardian):: bind_light(Spotlight *light_obj, const NodePath &light, int light_id) { + nassertv(has_fixed_function_pipeline()); + // static PStatCollector // _draw_set_state_light_bind_spotlight_pcollector("Draw:Set // State:Light:Bind:Spotlight"); PStatGPUTimer timer(this, @@ -8701,10 +8753,10 @@ get_external_image_format(Texture *tex) const { #endif case Texture::F_alpha: -#if defined(SUPPORT_FIXED_FUNCTION) || defined(OPENGLES) +#ifdef OPENGLES return GL_ALPHA; #else - return GL_RED; + return _supports_luminance_texture ? GL_ALPHA : GL_RED; #endif #ifndef OPENGLES_1 @@ -8744,18 +8796,18 @@ get_external_image_format(Texture *tex) const { case Texture::F_luminance: case Texture::F_sluminance: -#if defined(SUPPORT_FIXED_FUNCTION) || defined(OPENGLES) +#ifdef OPENGLES return GL_LUMINANCE; #else - return GL_RED; + return _supports_luminance_texture ? GL_LUMINANCE : GL_RED; #endif case Texture::F_luminance_alphamask: case Texture::F_luminance_alpha: case Texture::F_sluminance_alpha: -#if defined(SUPPORT_FIXED_FUNCTION) || defined(OPENGLES) +#ifdef OPENGLES return GL_LUMINANCE_ALPHA; #else - return GL_RG; + return _supports_luminance_texture ? GL_LUMINANCE_ALPHA : GL_RG; #endif #ifndef OPENGLES_1 @@ -9228,18 +9280,22 @@ get_internal_image_format(Texture *tex, bool force_sized) const { return GL_RGBA16F; } else #endif -#ifndef OPENGLES +#ifdef OPENGLES + { + // In OpenGL ES, the internal format must match the external format. + return _supports_bgr ? GL_BGRA : GL_RGBA; + } +#else if (tex->get_component_type() == Texture::T_unsigned_short) { return GL_RGBA16; } else if (tex->get_component_type() == Texture::T_short) { return GL_RGBA16_SNORM; } else if (tex->get_component_type() == Texture::T_byte) { return GL_RGBA8_SNORM; - } else -#endif - { + } else { return force_sized ? GL_RGBA8 : GL_RGBA; } +#endif case Texture::F_rgba4: return GL_RGBA4; @@ -9402,42 +9458,76 @@ get_internal_image_format(Texture *tex, bool force_sized) const { #endif case Texture::F_alpha: -#if defined(SUPPORT_FIXED_FUNCTION) || defined(OPENGLES) +#ifdef OPENGLES return force_sized ? GL_ALPHA8 : GL_ALPHA; #else - return force_sized ? GL_R8 : GL_RED; + if (_supports_luminance_texture) { + return force_sized ? GL_ALPHA8 : GL_ALPHA; + } else { + return force_sized ? GL_R8 : GL_RED; + } #endif case Texture::F_luminance: -#if defined(SUPPORT_FIXED_FUNCTION) || defined(OPENGLES) -#ifndef OPENGLES - if (tex->get_component_type() == Texture::T_float) { - return GL_LUMINANCE16F_ARB; - } else if (tex->get_component_type() == Texture::T_short) { - return GL_LUMINANCE16_SNORM; - } else if (tex->get_component_type() == Texture::T_unsigned_short) { - return GL_LUMINANCE16; - } else -#endif // OPENGLES - { - return force_sized ? GL_LUMINANCE8 : GL_LUMINANCE; - } +#ifdef OPENGLES + return force_sized ? GL_LUMINANCE8 : GL_LUMINANCE; #else - return force_sized ? GL_R8 : GL_RED; + if (_supports_luminance_texture) { + switch (tex->get_component_type()) { + case Texture::T_float: + case Texture::T_half_float: + return GL_LUMINANCE16F_ARB; + case Texture::T_short: + return GL_LUMINANCE16_SNORM; + case Texture::T_unsigned_short: + return GL_LUMINANCE16; + default: + return force_sized ? GL_LUMINANCE8 : GL_LUMINANCE; + } + } else { + switch (tex->get_component_type()) { + case Texture::T_float: + case Texture::T_half_float: + return GL_R16F; + case Texture::T_short: + return GL_R16_SNORM; + case Texture::T_unsigned_short: + return GL_R16; + default: + return force_sized ? GL_R8 : GL_RED; + } + } #endif case Texture::F_luminance_alpha: case Texture::F_luminance_alphamask: -#if defined(SUPPORT_FIXED_FUNCTION) || defined(OPENGLES) -#ifndef OPENGLES - if (tex->get_component_type() == Texture::T_float || tex->get_component_type() == Texture::T_unsigned_short) { - return GL_LUMINANCE_ALPHA16F_ARB; - } else -#endif // OPENGLES - { - return force_sized ? GL_LUMINANCE8_ALPHA8 : GL_LUMINANCE_ALPHA; - } +#ifdef OPENGLES + return force_sized ? GL_LUMINANCE8_ALPHA8 : GL_LUMINANCE_ALPHA; #else - return force_sized ? GL_RG8 : GL_RG; + if (_supports_luminance_texture) { + switch (tex->get_component_type()) { + case Texture::T_float: + case Texture::T_half_float: + return GL_LUMINANCE_ALPHA16F_ARB; + case Texture::T_short: + return GL_LUMINANCE16_SNORM; + case Texture::T_unsigned_short: + return GL_LUMINANCE16_ALPHA16; + default: + return force_sized ? GL_LUMINANCE8_ALPHA8 : GL_LUMINANCE_ALPHA; + } + } else { + switch (tex->get_component_type()) { + case Texture::T_float: + case Texture::T_half_float: + return GL_RG16F; + case Texture::T_short: + return GL_RG16_SNORM; + case Texture::T_unsigned_short: + return GL_RG16; + default: + return force_sized ? GL_RG8 : GL_RG; + } + } #endif #ifndef OPENGLES_1 @@ -10019,6 +10109,8 @@ reissue_transforms() { */ void CLP(GraphicsStateGuardian):: enable_lighting(bool enable) { + nassertv(has_fixed_function_pipeline()); + // static PStatCollector // _draw_set_state_light_enable_lighting_pcollector("Draw:Set // State:Light:Enable lighting"); PStatGPUTimer timer(this, @@ -10040,6 +10132,8 @@ enable_lighting(bool enable) { */ void CLP(GraphicsStateGuardian):: set_ambient_light(const LColor &color) { + nassertv(has_fixed_function_pipeline()); + // static PStatCollector _draw_set_state_light_ambient_pcollector("Draw:Set // State:Light:Ambient"); PStatGPUTimer timer(this, // _draw_set_state_light_ambient_pcollector); @@ -10061,6 +10155,8 @@ set_ambient_light(const LColor &color) { */ void CLP(GraphicsStateGuardian):: enable_light(int light_id, bool enable) { + nassertv(has_fixed_function_pipeline()); + // static PStatCollector // _draw_set_state_light_enable_light_pcollector("Draw:Set // State:Light:Enable light"); PStatGPUTimer timer(this, @@ -10085,6 +10181,8 @@ enable_light(int light_id, bool enable) { */ void CLP(GraphicsStateGuardian):: begin_bind_lights() { + nassertv(has_fixed_function_pipeline()); + // static PStatCollector // _draw_set_state_light_begin_bind_pcollector("Draw:Set State:Light:Begin // bind"); PStatGPUTimer timer(this, @@ -10115,6 +10213,8 @@ begin_bind_lights() { */ void CLP(GraphicsStateGuardian):: end_bind_lights() { + nassertv(has_fixed_function_pipeline()); + // static PStatCollector _draw_set_state_light_end_bind_pcollector("Draw:Set // State:Light:End bind"); PStatGPUTimer timer(this, // _draw_set_state_light_end_bind_pcollector); @@ -10132,6 +10232,8 @@ end_bind_lights() { */ void CLP(GraphicsStateGuardian):: enable_clip_plane(int plane_id, bool enable) { + nassertv(has_fixed_function_pipeline()); + if (enable) { glEnable(get_clip_plane_id(plane_id)); } else { @@ -10151,6 +10253,8 @@ enable_clip_plane(int plane_id, bool enable) { */ void CLP(GraphicsStateGuardian):: begin_bind_clip_planes() { + nassertv(has_fixed_function_pipeline()); + // We need to temporarily load a new matrix so we can define the clip_plane // in a known coordinate system. We pick the transform of the root. // (Alternatively, we could leave the current transform where it is and @@ -10175,6 +10279,8 @@ begin_bind_clip_planes() { */ void CLP(GraphicsStateGuardian):: bind_clip_plane(const NodePath &plane, int plane_id) { + nassertv(has_fixed_function_pipeline()); + GLenum id = get_clip_plane_id(plane_id); CPT(TransformState) transform = plane.get_transform(_scene_setup->get_scene_root().get_parent()); @@ -10205,6 +10311,8 @@ bind_clip_plane(const NodePath &plane, int plane_id) { */ void CLP(GraphicsStateGuardian):: end_bind_clip_planes() { + nassertv(has_fixed_function_pipeline()); + glMatrixMode(GL_MODELVIEW); glPopMatrix(); } @@ -10249,8 +10357,7 @@ set_state_and_transform(const RenderState *target, _target_rs = target; #ifndef OPENGLES_1 - _target_shader = (const ShaderAttrib *) - _target_rs->get_attrib_def(ShaderAttrib::get_class_slot()); + determine_target_shader(); _instance_count = _target_shader->get_instance_count(); if (_target_shader != _state_shader) { @@ -10259,31 +10366,31 @@ set_state_and_transform(const RenderState *target, _state_shader = _target_shader; _state_mask.clear_bit(TextureAttrib::get_class_slot()); } -#ifndef SUPPORT_FIXED_FUNCTION - else if (_current_shader == NULL) { // In the case of OpenGL ES 2.x, we need to glUseShader before we draw anything. + else if (!has_fixed_function_pipeline() && _current_shader == NULL) { // In the case of OpenGL ES 2.x, we need to glUseShader before we draw anything. do_issue_shader(); _state_mask.clear_bit(TextureAttrib::get_class_slot()); } -#endif // Update all of the state that is bound to the shader program. if (_current_shader_context != NULL) { - _current_shader_context->set_state_and_transform(target, transform, _projection_mat); + _current_shader_context->set_state_and_transform(target, transform, _scene_setup->get_camera_transform(), _projection_mat); } #endif #ifdef SUPPORT_FIXED_FUNCTION - int alpha_test_slot = AlphaTestAttrib::get_class_slot(); - if (_target_rs->get_attrib(alpha_test_slot) != _state_rs->get_attrib(alpha_test_slot) || - !_state_mask.get_bit(alpha_test_slot) + if (has_fixed_function_pipeline()) { + int alpha_test_slot = AlphaTestAttrib::get_class_slot(); + if (_target_rs->get_attrib(alpha_test_slot) != _state_rs->get_attrib(alpha_test_slot) || + !_state_mask.get_bit(alpha_test_slot) #ifndef OPENGLES_1 - || (_target_shader->get_flag(ShaderAttrib::F_subsume_alpha_test) != - _state_shader->get_flag(ShaderAttrib::F_subsume_alpha_test)) + || (_target_shader->get_flag(ShaderAttrib::F_subsume_alpha_test) != + _state_shader->get_flag(ShaderAttrib::F_subsume_alpha_test)) #endif - ) { - // PStatGPUTimer timer(this, _draw_set_state_alpha_test_pcollector); - do_issue_alpha_test(); - _state_mask.set_bit(alpha_test_slot); + ) { + // PStatGPUTimer timer(this, _draw_set_state_alpha_test_pcollector); + do_issue_alpha_test(); + _state_mask.set_bit(alpha_test_slot); + } } #endif @@ -10357,22 +10464,22 @@ set_state_and_transform(const RenderState *target, } #ifdef SUPPORT_FIXED_FUNCTION - int rescale_normal_slot = RescaleNormalAttrib::get_class_slot(); - if (_target_rs->get_attrib(rescale_normal_slot) != _state_rs->get_attrib(rescale_normal_slot) || - !_state_mask.get_bit(rescale_normal_slot)) { - // PStatGPUTimer timer(this, _draw_set_state_rescale_normal_pcollector); - do_issue_rescale_normal(); - _state_mask.set_bit(rescale_normal_slot); - } -#endif + if (has_fixed_function_pipeline()) { + int rescale_normal_slot = RescaleNormalAttrib::get_class_slot(); + if (_target_rs->get_attrib(rescale_normal_slot) != _state_rs->get_attrib(rescale_normal_slot) || + !_state_mask.get_bit(rescale_normal_slot)) { + // PStatGPUTimer timer(this, _draw_set_state_rescale_normal_pcollector); + do_issue_rescale_normal(); + _state_mask.set_bit(rescale_normal_slot); + } -#ifdef SUPPORT_FIXED_FUNCTION - int shade_model_slot = ShadeModelAttrib::get_class_slot(); - if (_target_rs->get_attrib(shade_model_slot) != _state_rs->get_attrib(shade_model_slot) || - !_state_mask.get_bit(shade_model_slot)) { - // PStatGPUTimer timer(this, _draw_set_state_shade_model_pcollector); - do_issue_shade_model(); - _state_mask.set_bit(shade_model_slot); + int shade_model_slot = ShadeModelAttrib::get_class_slot(); + if (_target_rs->get_attrib(shade_model_slot) != _state_rs->get_attrib(shade_model_slot) || + !_state_mask.get_bit(shade_model_slot)) { + // PStatGPUTimer timer(this, _draw_set_state_shade_model_pcollector); + do_issue_shade_model(); + _state_mask.set_bit(shade_model_slot); + } } #endif @@ -10444,7 +10551,9 @@ set_state_and_transform(const RenderState *target, !_state_mask.get_bit(tex_matrix_slot)) { // PStatGPUTimer timer(this, _draw_set_state_tex_matrix_pcollector); #ifdef SUPPORT_FIXED_FUNCTION - do_issue_tex_matrix(); + if (has_fixed_function_pipeline()) { + do_issue_tex_matrix(); + } #endif _state_mask.set_bit(tex_matrix_slot); #ifndef OPENGLES_1 @@ -10455,35 +10564,41 @@ set_state_and_transform(const RenderState *target, } #ifdef SUPPORT_FIXED_FUNCTION - int tex_gen_slot = TexGenAttrib::get_class_slot(); - if (_target_tex_gen != _state_tex_gen || - !_state_mask.get_bit(tex_gen_slot)) { - // PStatGPUTimer timer(this, _draw_set_state_tex_gen_pcollector); - do_issue_tex_gen(); - _state_tex_gen = _target_tex_gen; - _state_mask.set_bit(tex_gen_slot); - } -#endif + if (has_fixed_function_pipeline()) { + int tex_gen_slot = TexGenAttrib::get_class_slot(); + if (_target_tex_gen != _state_tex_gen || + !_state_mask.get_bit(tex_gen_slot)) { + // PStatGPUTimer timer(this, _draw_set_state_tex_gen_pcollector); + do_issue_tex_gen(); + _state_tex_gen = _target_tex_gen; + _state_mask.set_bit(tex_gen_slot); + } - int material_slot = MaterialAttrib::get_class_slot(); - if (_target_rs->get_attrib(material_slot) != _state_rs->get_attrib(material_slot) || - !_state_mask.get_bit(material_slot)) { - // PStatGPUTimer timer(this, _draw_set_state_material_pcollector); -#ifdef SUPPORT_FIXED_FUNCTION - do_issue_material(); -#endif - _state_mask.set_bit(material_slot); - } + int material_slot = MaterialAttrib::get_class_slot(); + if (_target_rs->get_attrib(material_slot) != _state_rs->get_attrib(material_slot) || + !_state_mask.get_bit(material_slot)) { + // PStatGPUTimer timer(this, _draw_set_state_material_pcollector); + do_issue_material(); + _state_mask.set_bit(material_slot); + } - int light_slot = LightAttrib::get_class_slot(); - if (_target_rs->get_attrib(light_slot) != _state_rs->get_attrib(light_slot) || - !_state_mask.get_bit(light_slot)) { - // PStatGPUTimer timer(this, _draw_set_state_light_pcollector); -#ifdef SUPPORT_FIXED_FUNCTION - do_issue_light(); -#endif - _state_mask.set_bit(light_slot); + int light_slot = LightAttrib::get_class_slot(); + if (_target_rs->get_attrib(light_slot) != _state_rs->get_attrib(light_slot) || + !_state_mask.get_bit(light_slot)) { + // PStatGPUTimer timer(this, _draw_set_state_light_pcollector); + do_issue_light(); + _state_mask.set_bit(light_slot); + } + + int fog_slot = FogAttrib::get_class_slot(); + if (_target_rs->get_attrib(fog_slot) != _state_rs->get_attrib(fog_slot) || + !_state_mask.get_bit(fog_slot)) { + // PStatGPUTimer timer(this, _draw_set_state_fog_pcollector); + do_issue_fog(); + _state_mask.set_bit(fog_slot); + } } +#endif int stencil_slot = StencilAttrib::get_class_slot(); if (_target_rs->get_attrib(stencil_slot) != _state_rs->get_attrib(stencil_slot) || @@ -10493,16 +10608,6 @@ set_state_and_transform(const RenderState *target, _state_mask.set_bit(stencil_slot); } - int fog_slot = FogAttrib::get_class_slot(); - if (_target_rs->get_attrib(fog_slot) != _state_rs->get_attrib(fog_slot) || - !_state_mask.get_bit(fog_slot)) { - // PStatGPUTimer timer(this, _draw_set_state_fog_pcollector); -#ifdef SUPPORT_FIXED_FUNCTION - do_issue_fog(); -#endif - _state_mask.set_bit(fog_slot); - } - int scissor_slot = ScissorAttrib::get_class_slot(); if (_target_rs->get_attrib(scissor_slot) != _state_rs->get_attrib(scissor_slot) || !_state_mask.get_bit(scissor_slot)) { @@ -10546,12 +10651,16 @@ do_issue_texture() { _texture_binding_shader_context->disable_shader_texture_bindings(); } #ifdef SUPPORT_FIXED_FUNCTION - update_standard_texture_bindings(); + if (has_fixed_function_pipeline()) { + update_standard_texture_bindings(); + } #endif } else { if (_texture_binding_shader_context == 0) { #ifdef SUPPORT_FIXED_FUNCTION - disable_standard_texture_bindings(); + if (has_fixed_function_pipeline()) { + disable_standard_texture_bindings(); + } #endif _current_shader_context->update_shader_texture_bindings(NULL); } else { @@ -10811,25 +10920,20 @@ update_standard_texture_bindings() { /** * Applies a white dummy texture. This is useful to bind to a texture slot - * when a texture is missing. + * when a texture is missing. Also binds the default sampler to the unit. */ void CLP(GraphicsStateGuardian):: -apply_white_texture() { - if (_white_texture != 0) { - glBindTexture(GL_TEXTURE_2D, _white_texture); - return; +apply_white_texture(GLuint unit) { + set_active_texture_stage(unit); + glBindTexture(GL_TEXTURE_2D, get_white_texture()); + + // Also apply the default sampler, if there's a chance we'd applied anything + // else. +#ifndef OPENGLES_1 + if (_supports_sampler_objects) { + _glBindSampler(unit, 0); } - - glGenTextures(1, &_white_texture); - glBindTexture(GL_TEXTURE_2D, _white_texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - unsigned char data[] = {0xff, 0xff, 0xff, 0xff}; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, - GL_RGBA, GL_UNSIGNED_BYTE, data); +#endif } /** @@ -10839,7 +10943,16 @@ apply_white_texture() { GLuint CLP(GraphicsStateGuardian):: get_white_texture() { if (_white_texture == 0) { - apply_white_texture(); + glGenTextures(1, &_white_texture); + glBindTexture(GL_TEXTURE_2D, _white_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + unsigned char data[] = {0xff, 0xff, 0xff, 0xff}; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); } return _white_texture; } @@ -10881,20 +10994,22 @@ update_show_usage_texture_bindings(int show_stage_index) { } #ifdef SUPPORT_FIXED_FUNCTION - // Disable all texture stages. - for (i = 0; i < _num_active_texture_stages; i++) { - set_active_texture_stage(i); + if (has_fixed_function_pipeline()) { + // Disable all texture stages. + for (i = 0; i < _num_active_texture_stages; i++) { + set_active_texture_stage(i); #ifndef OPENGLES - glDisable(GL_TEXTURE_1D); + glDisable(GL_TEXTURE_1D); #endif // OPENGLES - glDisable(GL_TEXTURE_2D); - if (_supports_3d_texture) { + glDisable(GL_TEXTURE_2D); + if (_supports_3d_texture) { #ifndef OPENGLES_1 - glDisable(GL_TEXTURE_3D); + glDisable(GL_TEXTURE_3D); #endif // OPENGLES_1 - } - if (_supports_cube_map) { - glDisable(GL_TEXTURE_CUBE_MAP); + } + if (_supports_cube_map) { + glDisable(GL_TEXTURE_CUBE_MAP); + } } } #endif @@ -10917,7 +11032,9 @@ update_show_usage_texture_bindings(int show_stage_index) { // Choose the corresponding usage texture and apply it. set_active_texture_stage(i); #ifdef SUPPORT_FIXED_FUNCTION - glEnable(GL_TEXTURE_2D); + if (has_fixed_function_pipeline()) { + glEnable(GL_TEXTURE_2D); + } #endif UsageTextureKey key(texture->get_x_size(), texture->get_y_size()); @@ -11445,7 +11562,9 @@ specify_texture(CLP(TextureContext) *gtc, const SamplerState &sampler) { tex->get_format() == Texture::F_depth_component24 || tex->get_format() == Texture::F_depth_component32) { #ifdef SUPPORT_FIXED_FUNCTION - glTexParameteri(target, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY); + if (has_fixed_function_pipeline()) { + glTexParameteri(target, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY); + } #endif if (_supports_shadow_filter) { if ((sampler.get_magfilter() == SamplerState::FT_shadow) || @@ -11893,33 +12012,35 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { } #endif -#if !defined(SUPPORT_FIXED_FUNCTION) && !defined(OPENGLES) - // Do we need to apply a swizzle mask to emulate these deprecated texture - // formats? - switch (tex->get_format()) { - case Texture::F_alpha: - glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_ZERO); - glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, GL_ZERO); - glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_ZERO); - glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, GL_RED); - break; +#ifndef OPENGLES + if (!_supports_luminance_texture) { + // Do we need to apply a swizzle mask to emulate these deprecated texture + // formats? + switch (tex->get_format()) { + case Texture::F_alpha: + glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_ZERO); + glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, GL_ZERO); + glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_ZERO); + glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, GL_RED); + break; - case Texture::F_luminance: - glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_RED); - glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, GL_RED); - glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED); - glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, GL_ONE); - break; + case Texture::F_luminance: + glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, GL_ONE); + break; - case Texture::F_luminance_alpha: - glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_RED); - glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, GL_RED); - glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED); - glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, GL_GREEN); - break; + case Texture::F_luminance_alpha: + glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, GL_GREEN); + break; - default: - break; + default: + break; + } } #endif @@ -12608,6 +12729,9 @@ upload_simple_texture(CLP(TextureContext) *gtc) { _data_transferred_pcollector.add_level(image_size); #endif +#ifdef OPENGLES + internal_format = external_format; +#endif glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, external_format, component_type, image_ptr); @@ -12676,9 +12800,8 @@ get_texture_memory_size(CLP(TextureContext) *gtc) { } // OK, get the noncompressed size. - GLint red_size, green_size, blue_size, alpha_size, - luminance_size, intensity_size; - GLint depth_size = 0; + GLint red_size, green_size, blue_size, alpha_size; + GLint depth_size = 0, luminance_size = 0, intensity_size = 0; glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_RED_SIZE, &red_size); glGetTexLevelParameteriv(page_target, 0, @@ -12687,11 +12810,13 @@ get_texture_memory_size(CLP(TextureContext) *gtc) { GL_TEXTURE_BLUE_SIZE, &blue_size); glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_ALPHA_SIZE, &alpha_size); - glGetTexLevelParameteriv(page_target, 0, - GL_TEXTURE_LUMINANCE_SIZE, &luminance_size); - glGetTexLevelParameteriv(page_target, 0, - GL_TEXTURE_INTENSITY_SIZE, &intensity_size); - if (_supports_depth_texture) { + if (get_supports_luminance_texture()) { + glGetTexLevelParameteriv(page_target, 0, + GL_TEXTURE_LUMINANCE_SIZE, &luminance_size); + glGetTexLevelParameteriv(page_target, 0, + GL_TEXTURE_INTENSITY_SIZE, &intensity_size); + } + if (get_supports_depth_texture()) { glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_DEPTH_SIZE, &depth_size); } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 85290cd6f2..e4da7cade1 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -284,7 +284,6 @@ public: virtual void end_frame(Thread *current_thread); virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, - const GeomMunger *munger, const GeomVertexDataPipelineReader *data_reader, bool force); virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, @@ -399,6 +398,7 @@ public: INLINE const string &get_gl_version() const; INLINE int get_gl_version_major() const; INLINE int get_gl_version_minor() const; + INLINE bool has_fixed_function_pipeline() const; virtual void set_state_and_transform(const RenderState *state, const TransformState *transform); @@ -566,7 +566,7 @@ protected: void update_shader_vertex_format(const GeomVertexFormat *format); #endif - void apply_white_texture(); + void apply_white_texture(GLuint unit); GLuint get_white_texture(); #ifndef NDEBUG @@ -719,6 +719,11 @@ protected: pset _extensions; +#ifndef OPENGLES + // True for non-compatibility GL 3.2+ contexts. + bool _core_profile; +#endif + public: bool _supports_point_parameters; PFNGLPOINTPARAMETERFVPROC _glPointParameterfv; diff --git a/panda/src/glstuff/glShaderContext_src.I b/panda/src/glstuff/glShaderContext_src.I index 00b8460ba8..aa14237510 100644 --- a/panda/src/glstuff/glShaderContext_src.I +++ b/panda/src/glstuff/glShaderContext_src.I @@ -10,38 +10,3 @@ * @author jyelon * @date 2005-09-01 */ - -/** - * Returns true if the shader is "valid", ie, if the compilation was - * successful. The compilation could fail if there is a syntax error in the - * shader, or if the current video card isn't shader-capable, or if no shader - * languages are compiled into panda. - */ -INLINE bool CLP(ShaderContext):: -valid() { - if (_shader->get_error_flag()) return false; - if (_shader->get_language() != Shader::SL_GLSL) { - return false; - } - if (_glsl_program != 0) { - return true; - } - return false; -} - -/** - * Returns true if the shader may need to access standard vertex attributes as - * passed by glVertexPointer and the like. - */ -INLINE bool CLP(ShaderContext):: -uses_standard_vertex_arrays() { - return _uses_standard_vertex_arrays; -} - -/** - * Always true, for now. - */ -INLINE bool CLP(ShaderContext):: -uses_custom_vertex_arrays() { - return true; -} diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index 6947a39235..f2c3b30a9b 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -874,7 +874,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) { matrix_name.substr(0, 12) == "LightSource[" && sscanf(matrix_name.c_str(), "LightSource[%d].%s", &bind._index, name_buffer) == 2) { // A matrix member of a p3d_LightSource struct. - if (strncmp(name_buffer, "shadowMatrix", 127) == 0) { + if (strncmp(name_buffer, "shadowViewMatrix", 127) == 0) { if (inverse) { // Tack inverse back onto the end. strcpy(name_buffer + strlen(name_buffer), "Inverse"); @@ -884,7 +884,25 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) { bind._part[0] = Shader::SMO_light_source_i_attrib; bind._arg[0] = InternalName::make(name_buffer); bind._part[1] = Shader::SMO_identity; + bind._arg[1] = NULL; + } else if (strncmp(name_buffer, "shadowMatrix", 127) == 0) { + // Only supported for backward compatibility: includes the model + // matrix. Not very efficient to do this. + bind._func = Shader::SMF_compose; + bind._part[0] = Shader::SMO_model_to_apiview; + bind._arg[0] = NULL; + bind._part[1] = Shader::SMO_light_source_i_attrib; + bind._arg[1] = InternalName::make("shadowViewMatrix"); + + static bool warned = false; + if (!warned) { + warned = true; + GLCAT.warning() + << "p3d_LightSource[].shadowMatrix is deprecated; use " + "shadowViewMatrix instead, which transforms from view space " + "instead of model space.\n"; + } } else { GLCAT.error() << "p3d_LightSource struct does not provide a matrix named " << matrix_name << "!\n"; return; @@ -1163,11 +1181,16 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) { bind._index = index; bind._part[0] = Shader::SMO_light_source_i_attrib; bind._arg[0] = InternalName::make(member_name); - bind._dep[0] = Shader::SSD_general | Shader::SSD_light | Shader::SSD_frame | Shader::SSD_transform; + bind._dep[0] = Shader::SSD_general | Shader::SSD_light | Shader::SSD_frame; bind._part[1] = Shader::SMO_identity; bind._arg[1] = NULL; bind._dep[1] = Shader::SSD_NONE; + if (member_name == "position" || member_name == "halfVector" || + member_name == "spotDirection") { + bind._dep[0] |= Shader::SSD_view_transform; + } + switch (param_type) { case GL_FLOAT: bind._piece = Shader::SMP_row3x1; @@ -1250,7 +1273,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) { bind._func = Shader::SMF_compose; bind._part[0] = Shader::SMO_world_to_view; bind._part[1] = Shader::SMO_view_to_apiview; - bind._dep[0] = Shader::SSD_general | Shader::SSD_transform; + bind._dep[0] = Shader::SSD_general | Shader::SSD_view_transform; bind._dep[1] = Shader::SSD_general; _shader->_mat_spec.push_back(bind); _shader->_mat_deps |= bind._dep[0] | bind._dep[1]; @@ -1262,7 +1285,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) { bind._part[0] = Shader::SMO_apiview_to_view; bind._part[1] = Shader::SMO_view_to_world; bind._dep[0] = Shader::SSD_general; - bind._dep[1] = Shader::SSD_general | Shader::SSD_transform; + bind._dep[1] = Shader::SSD_general | Shader::SSD_view_transform; _shader->_mat_spec.push_back(bind); _shader->_mat_deps |= bind._dep[0] | bind._dep[1]; return; @@ -1383,22 +1406,43 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) { bind._id = arg_id; bind._piece = Shader::SMP_whole; bind._func = Shader::SMF_first; + bind._part[1] = Shader::SMO_identity; + bind._arg[1] = NULL; + bind._dep[1] = Shader::SSD_NONE; PT(InternalName) iname = InternalName::make(param_name); if (iname->get_parent() != InternalName::get_root()) { // It might be something like an attribute of a shader input, like a // light parameter. It might also just be a custom struct // parameter. We can't know yet, sadly. - bind._part[0] = Shader::SMO_mat_constant_x_attrib; - bind._arg[0] = InternalName::make(param_name); - bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_transform; + if (iname->get_basename() == "shadowMatrix") { + // Special exception for shadowMatrix, which is deprecated, + // because it includes the model transformation. It is far more + // efficient to do that in the shader instead. + static bool warned = false; + if (!warned) { + warned = true; + GLCAT.warning() + << "light.shadowMatrix inputs are deprecated; use " + "shadowViewMatrix instead, which transforms from view " + "space instead of model space.\n"; + } + bind._func = Shader::SMF_compose; + bind._part[0] = Shader::SMO_model_to_apiview; + bind._arg[0] = NULL; + bind._dep[0] = Shader::SSD_general | Shader::SSD_transform; + bind._part[1] = Shader::SMO_mat_constant_x_attrib; + bind._arg[1] = iname->get_parent()->append("shadowViewMatrix"); + bind._dep[1] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_view_transform; + } else { + bind._part[0] = Shader::SMO_mat_constant_x_attrib; + bind._arg[0] = InternalName::make(param_name); + bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_view_transform; + } } else { bind._part[0] = Shader::SMO_mat_constant_x; bind._arg[0] = InternalName::make(param_name); bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame; } - bind._part[1] = Shader::SMO_identity; - bind._arg[1] = NULL; - bind._dep[1] = Shader::SSD_NONE; _shader->_mat_spec.push_back(bind); _shader->_mat_deps |= bind._dep[0]; return; @@ -1430,9 +1474,9 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) { bind._func = Shader::SMF_first; bind._part[0] = Shader::SMO_vec_constant_x_attrib; bind._arg[0] = iname; - // We need SSD_transform since some attributes (eg. light position) - // have to be transformed to view space. - bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_transform; + // We need SSD_view_transform since some attributes (eg. light + // position) have to be transformed to view space. + bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_view_transform; bind._part[1] = Shader::SMO_identity; bind._arg[1] = NULL; bind._dep[1] = Shader::SSD_NONE; @@ -1777,6 +1821,20 @@ release_resources() { _glgsg->report_my_gl_errors(); } +/** + * Returns true if the shader is "valid", ie, if the compilation was + * successful. The compilation could fail if there is a syntax error in the + * shader, or if the current video card isn't shader-capable, or if no shader + * languages are compiled into panda. + */ +bool CLP(ShaderContext):: +valid() { + if (_shader->get_error_flag()) { + return false; + } + return (_glsl_program != 0); +} + /** * This function is to be called to enable a new shader. It also initializes * all of the shader's input parameters. @@ -1822,6 +1880,7 @@ unbind() { void CLP(ShaderContext):: set_state_and_transform(const RenderState *target_rs, const TransformState *modelview_transform, + const TransformState *camera_transform, const TransformState *projection_transform) { // Find out which state properties have changed. @@ -1829,6 +1888,10 @@ set_state_and_transform(const RenderState *target_rs, if (_modelview_transform != modelview_transform) { _modelview_transform = modelview_transform; + altered |= (Shader::SSD_transform & ~Shader::SSD_view_transform); + } + if (_camera_transform != camera_transform) { + _camera_transform = camera_transform; altered |= Shader::SSD_transform; } if (_projection_transform != projection_transform) { @@ -2128,7 +2191,7 @@ update_slider_table(const SliderTable *table) { */ void CLP(ShaderContext):: disable_shader_vertex_arrays() { - if (!valid()) { + if (_glsl_program == 0) { return; } @@ -2151,7 +2214,7 @@ disable_shader_vertex_arrays() { */ bool CLP(ShaderContext):: update_shader_vertex_arrays(ShaderContext *prev, bool force) { - if (!valid()) { + if (_glsl_program == 0) { return true; } @@ -2194,7 +2257,7 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) { // Figure out which attributes to enable or disable. BitMask32 enabled_attribs = _enabled_attribs; if (_color_attrib_index != -1 && - color_attrib->get_type() != ColorAttrib::T_vertex) { + color_attrib->get_color_type() != ColorAttrib::T_vertex) { // Vertex colours are disabled. enabled_attribs.clear_bit(_color_attrib_index); @@ -2247,7 +2310,7 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) { // Don't apply vertex colors if they are disabled with a ColorAttrib. int num_elements, element_stride, divisor; bool normalized; - if ((p != _color_attrib_index || color_attrib->get_type() == ColorAttrib::T_vertex) && + if ((p != _color_attrib_index || color_attrib->get_color_type() == ColorAttrib::T_vertex) && _glgsg->_data_reader->get_array_info(name, array_reader, num_values, numeric_type, normalized, start, stride, divisor, @@ -2324,7 +2387,7 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) { */ void CLP(ShaderContext):: disable_shader_texture_bindings() { - if (!valid()) { + if (_glsl_program == 0) { return; } @@ -2424,7 +2487,7 @@ void CLP(ShaderContext):: update_shader_texture_bindings(ShaderContext *prev) { // if (prev) { prev->disable_shader_texture_bindings(); } - if (!valid()) { + if (_glsl_program == 0) { return; } @@ -2440,17 +2503,17 @@ update_shader_texture_bindings(ShaderContext *prev) { const ParamTextureImage *param = NULL; Texture *tex; - const ShaderInput *sinp = _glgsg->_target_shader->get_shader_input(input._name); - switch (sinp->get_value_type()) { + const ShaderInput &sinp = _glgsg->_target_shader->get_shader_input(input._name); + switch (sinp.get_value_type()) { case ShaderInput::M_texture_image: - param = (const ParamTextureImage *)sinp->get_param(); + param = (const ParamTextureImage *)sinp.get_param(); tex = param->get_texture(); break; case ShaderInput::M_texture: // People find it convenient to be able to pass a texture without // further ado. - tex = sinp->get_texture(); + tex = sinp.get_texture(); break; case ShaderInput::M_invalid: @@ -2568,8 +2631,7 @@ update_shader_texture_bindings(ShaderContext *prev) { textures[i] = _glgsg->get_white_texture(); samplers[i] = 0; } else { - _glgsg->set_active_texture_stage(i); - _glgsg->apply_white_texture(); + _glgsg->apply_white_texture(i); } continue; } diff --git a/panda/src/glstuff/glShaderContext_src.h b/panda/src/glstuff/glShaderContext_src.h index e143774ed6..9ecfe9ed39 100644 --- a/panda/src/glstuff/glShaderContext_src.h +++ b/panda/src/glstuff/glShaderContext_src.h @@ -26,7 +26,7 @@ class CLP(GraphicsStateGuardian); /** * xyz */ -class EXPCL_GL CLP(ShaderContext) : public ShaderContext { +class EXPCL_GL CLP(ShaderContext) FINAL : public ShaderContext { public: friend class CLP(GraphicsStateGuardian); @@ -40,25 +40,30 @@ public: void reflect_uniform(int i, char *name_buffer, GLsizei name_buflen); bool get_sampler_texture_type(int &out, GLenum param_type); - INLINE bool valid(void); - void bind(); - void unbind(); + bool valid(void) override; + void bind() override; + void unbind() override; void set_state_and_transform(const RenderState *state, const TransformState *modelview_transform, - const TransformState *projection_transform); + const TransformState *camera_transform, + const TransformState *projection_transform) override; - void issue_parameters(int altered); + void issue_parameters(int altered) override; void update_transform_table(const TransformTable *table); void update_slider_table(const SliderTable *table); - void disable_shader_vertex_arrays(); - bool update_shader_vertex_arrays(ShaderContext *prev, bool force); - void disable_shader_texture_bindings() OVERRIDE; - void update_shader_texture_bindings(ShaderContext *prev) OVERRIDE; - void update_shader_buffer_bindings(ShaderContext *prev) OVERRIDE; + void disable_shader_vertex_arrays() override; + bool update_shader_vertex_arrays(ShaderContext *prev, bool force) override; + void disable_shader_texture_bindings() override; + void update_shader_texture_bindings(ShaderContext *prev) override; + void update_shader_buffer_bindings(ShaderContext *prev) override; - INLINE bool uses_standard_vertex_arrays(void); - INLINE bool uses_custom_vertex_arrays(void); + bool uses_standard_vertex_arrays(void) override { + return _uses_standard_vertex_arrays; + } + bool uses_custom_vertex_arrays(void) override { + return true; + } private: bool _validated; @@ -68,6 +73,7 @@ private: WCPT(RenderState) _state_rs; CPT(TransformState) _modelview_transform; + CPT(TransformState) _camera_transform; CPT(TransformState) _projection_transform; /* @@ -126,10 +132,10 @@ public: register_type(_type_handle, CLASSPREFIX_QUOTED "ShaderContext", ShaderContext::get_class_type()); } - virtual TypeHandle get_type() const { + virtual TypeHandle get_type() const override { return get_class_type(); } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + virtual TypeHandle force_init_type() override {init_type(); return get_class_type();} private: static TypeHandle _type_handle; diff --git a/panda/src/glstuff/glTextureContext_src.cxx b/panda/src/glstuff/glTextureContext_src.cxx index a56d6a20a4..e36f8f756e 100644 --- a/panda/src/glstuff/glTextureContext_src.cxx +++ b/panda/src/glstuff/glTextureContext_src.cxx @@ -95,6 +95,27 @@ reset_data() { #endif } +/** + * Returns an implementation-defined handle or pointer that can be used + * to interface directly with the underlying API. + * Returns 0 if the underlying implementation does not support this. + */ +uint64_t CLP(TextureContext):: +get_native_id() const { + return _index; +} + +/** + * Similar to get_native_id, but some implementations use a separate + * identifier for the buffer object associated with buffer textures. + * Returns 0 if the underlying implementation does not support this, or + * if this is not a buffer texture. + */ +uint64_t CLP(TextureContext):: +get_native_buffer_id() const { + return _buffer; +} + /** * */ diff --git a/panda/src/glstuff/glTextureContext_src.h b/panda/src/glstuff/glTextureContext_src.h index 309550eced..9a345b397d 100644 --- a/panda/src/glstuff/glTextureContext_src.h +++ b/panda/src/glstuff/glTextureContext_src.h @@ -33,6 +33,9 @@ public: virtual void evict_lru(); void reset_data(); + virtual uint64_t get_native_id() const; + virtual uint64_t get_native_buffer_id() const; + #ifndef OPENGLES void make_handle_resident(); GLuint64 get_handle(); diff --git a/panda/src/gobj/adaptiveLru.cxx b/panda/src/gobj/adaptiveLru.cxx index 8b68f0e9b2..a74fcfcb0d 100644 --- a/panda/src/gobj/adaptiveLru.cxx +++ b/panda/src/gobj/adaptiveLru.cxx @@ -114,12 +114,16 @@ update_page(AdaptiveLruPage *page) { update_frames = (_current_frame_identifier - page->_update_frame_identifier); if (update_frames > 0) { - PN_stdfloat update_average_frame_utilization = - (PN_stdfloat) (page->_update_total_usage) / (PN_stdfloat)update_frames; + if (page->_update_total_usage > 0) { + PN_stdfloat update_average_frame_utilization = + (PN_stdfloat) (page->_update_total_usage) / (PN_stdfloat)update_frames; - page->_average_frame_utilization = - calculate_exponential_moving_average(update_average_frame_utilization, - page->_average_frame_utilization); + page->_average_frame_utilization = + calculate_exponential_moving_average(update_average_frame_utilization, + page->_average_frame_utilization); + } else { + page->_average_frame_utilization *= 1.0f - _weight; + } target_priority = page->_priority; if (page->_average_frame_utilization >= 1.0f) { diff --git a/panda/src/gobj/adaptiveLru.h b/panda/src/gobj/adaptiveLru.h index cc55b6e706..99b78b482d 100644 --- a/panda/src/gobj/adaptiveLru.h +++ b/panda/src/gobj/adaptiveLru.h @@ -44,7 +44,7 @@ public: */ class EXPCL_PANDA_GOBJ AdaptiveLru : public Namable { PUBLISHED: - AdaptiveLru(const string &name, size_t max_size); + explicit AdaptiveLru(const string &name, size_t max_size); ~AdaptiveLru(); INLINE size_t get_total_size() const; @@ -134,7 +134,7 @@ private: */ class EXPCL_PANDA_GOBJ AdaptiveLruPage : public AdaptiveLruPageDynamicList, public AdaptiveLruPageStaticList { PUBLISHED: - AdaptiveLruPage(size_t lru_size); + explicit AdaptiveLruPage(size_t lru_size); AdaptiveLruPage(const AdaptiveLruPage ©); void operator = (const AdaptiveLruPage ©); diff --git a/panda/src/gobj/animateVerticesRequest.I b/panda/src/gobj/animateVerticesRequest.I index 01226e4694..eb6ee589d0 100644 --- a/panda/src/gobj/animateVerticesRequest.I +++ b/panda/src/gobj/animateVerticesRequest.I @@ -16,15 +16,16 @@ */ INLINE AnimateVerticesRequest:: AnimateVerticesRequest(GeomVertexData *geom_vertex_data) : - _geom_vertex_data(geom_vertex_data), - _is_ready(false) + _geom_vertex_data(geom_vertex_data) { } /** * Returns true if this request has completed, false if it is still pending. + * Equivalent to `req.done() and not req.cancelled()`. + * @see done() */ INLINE bool AnimateVerticesRequest:: is_ready() const { - return _is_ready; + return (FutureState)AtomicAdjust::get(_future_state) == FS_finished; } diff --git a/panda/src/gobj/animateVerticesRequest.cxx b/panda/src/gobj/animateVerticesRequest.cxx index 7c152406ed..f2ded8cdd1 100644 --- a/panda/src/gobj/animateVerticesRequest.cxx +++ b/panda/src/gobj/animateVerticesRequest.cxx @@ -26,7 +26,6 @@ do_task() { // There is no need to store or return a result. The GeomVertexData caches // the result and it will be used later in the rendering process. _geom_vertex_data->animate_vertices(true, current_thread); - _is_ready = true; // Don't continue the task; we're done. return AsyncTask::DS_done; diff --git a/panda/src/gobj/animateVerticesRequest.h b/panda/src/gobj/animateVerticesRequest.h index 27e9ba11b2..be20651a13 100644 --- a/panda/src/gobj/animateVerticesRequest.h +++ b/panda/src/gobj/animateVerticesRequest.h @@ -35,16 +35,15 @@ public: ALLOC_DELETED_CHAIN(AnimateVerticesRequest); PUBLISHED: - INLINE AnimateVerticesRequest(GeomVertexData *geom_vertex_data); + INLINE explicit AnimateVerticesRequest(GeomVertexData *geom_vertex_data); INLINE bool is_ready() const; protected: - virtual AsyncTask::DoneStatus do_task(); + virtual AsyncTask::DoneStatus do_task(); private: PT(GeomVertexData) _geom_vertex_data; - bool _is_ready; public: static TypeHandle get_class_type() { diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index 4df77f0527..84876a2b8f 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -572,6 +572,7 @@ ConfigureFn(config_gobj) { ParamTextureImage::init_type(); ParamTextureSampler::init_type(); PerspectiveLens::init_type(); + PreparedGraphicsObjects::EnqueuedObject::init_type(); QueryContext::init_type(); SamplerContext::init_type(); SamplerState::init_type(); diff --git a/panda/src/gobj/geom.I b/panda/src/gobj/geom.I index 33132b3be5..d553c12977 100644 --- a/panda/src/gobj/geom.I +++ b/panda/src/gobj/geom.I @@ -51,7 +51,7 @@ get_geom_rendering() const { INLINE CPT(GeomVertexData) Geom:: get_vertex_data(Thread *current_thread) const { CDReader cdata(_cycler, current_thread); - return cdata->_data.get_read_pointer(); + return cdata->_data.get_read_pointer(current_thread); } /** @@ -68,7 +68,7 @@ is_empty() const { * Returns the number of GeomPrimitive objects stored within the Geom, each of * which represents a number of primitives of a particular type. */ -INLINE int Geom:: +INLINE size_t Geom:: get_num_primitives() const { CDReader cdata(_cycler); return cdata->_primitives.size(); @@ -80,9 +80,9 @@ get_num_primitives() const { * or set_primitive() if you want to modify it. */ INLINE CPT(GeomPrimitive) Geom:: -get_primitive(int i) const { +get_primitive(size_t i) const { CDReader cdata(_cycler); - nassertr(i >= 0 && i < (int)cdata->_primitives.size(), NULL); + nassertr(i < cdata->_primitives.size(), nullptr); return cdata->_primitives[i].get_read_pointer(); } @@ -95,15 +95,28 @@ get_primitive(int i) const { * away other changes you might have recently made in an upstream thread. */ INLINE PT(GeomPrimitive) Geom:: -modify_primitive(int i) { +modify_primitive(size_t i) { Thread *current_thread = Thread::get_current_thread(); CDWriter cdata(_cycler, true, current_thread); - nassertr(i >= 0 && i < (int)cdata->_primitives.size(), NULL); + nassertr(i < cdata->_primitives.size(), NULL); cdata->_modified = Geom::get_next_modified(); clear_cache_stage(current_thread); return cdata->_primitives[i].get_write_pointer(); } +/** + * Inserts a new GeomPrimitive structure to the Geom object. This specifies a + * particular subset of vertices that are used to define geometric primitives + * of the indicated type. + * + * Don't call this in a downstream thread unless you don't mind it blowing + * away other changes you might have recently made in an upstream thread. + */ +INLINE void Geom:: +add_primitive(const GeomPrimitive *primitive) { + insert_primitive((size_t)-1, primitive); +} + /** * Decomposes all of the primitives within this Geom, returning the result. * See GeomPrimitive::decompose(). @@ -514,6 +527,18 @@ CData(const Geom::CData ©) : { } + +/** + * + */ +INLINE GeomPipelineReader:: +GeomPipelineReader(Thread *current_thread) : + _object(nullptr), + _current_thread(current_thread), + _cdata(nullptr) +{ +} + /** * */ @@ -531,34 +556,22 @@ GeomPipelineReader(const Geom *object, Thread *current_thread) : #endif // DO_PIPELINING } -/** - * Don't attempt to copy these objects. - */ -INLINE GeomPipelineReader:: -GeomPipelineReader(const GeomPipelineReader &) { - nassertv(false); -} - -/** - * Don't attempt to copy these objects. - */ -INLINE void GeomPipelineReader:: -operator = (const GeomPipelineReader &) { - nassertv(false); -} - /** * */ INLINE GeomPipelineReader:: ~GeomPipelineReader() { #ifdef _DEBUG - nassertv(_object->test_ref_count_nonzero()); + if (_object != nullptr) { + nassertv(_object->test_ref_count_nonzero()); + } #endif // _DEBUG // _object->_cycler.release_read(_cdata); #ifdef DO_PIPELINING - unref_delete((CycleData *)_cdata); + if (_cdata != nullptr) { + unref_delete((CycleData *)_cdata); + } #endif // DO_PIPELINING #ifdef _DEBUG @@ -567,6 +580,30 @@ INLINE GeomPipelineReader:: #endif // _DEBUG } +/** + * + */ +INLINE void GeomPipelineReader:: +set_object(const Geom *object) { + if (object != _object) { + // _object->_cycler.release_read(_cdata); + +#ifdef DO_PIPELINING + if (_cdata != NULL) { + unref_delete((CycleData *)_cdata); + } +#endif // DO_PIPELINING + + _cdata = object->_cycler.read_unlocked(_current_thread); + +#ifdef DO_PIPELINING + _cdata->ref(); +#endif // DO_PIPELINING + + _object = object; + } +} + /** * */ diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index 1d3e0a2a45..8590342010 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -236,7 +236,7 @@ make_nonindexed(bool composite_only) { int num_changed = 0; CDWriter cdata(_cycler, true, current_thread); - CPT(GeomVertexData) orig_data = cdata->_data.get_read_pointer(); + CPT(GeomVertexData) orig_data = cdata->_data.get_read_pointer(current_thread); PT(GeomVertexData) new_data = new GeomVertexData(*orig_data); new_data->clear_rows(); @@ -247,7 +247,7 @@ make_nonindexed(bool composite_only) { Primitives new_prims; new_prims.reserve(cdata->_primitives.size()); for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - PT(GeomPrimitive) primitive = (*pi).get_read_pointer()->make_copy(); + PT(GeomPrimitive) primitive = (*pi).get_read_pointer(current_thread)->make_copy(); new_prims.push_back(primitive.p()); // GeomPoints are considered "composite" for the purposes of making @@ -294,11 +294,11 @@ make_nonindexed(bool composite_only) { * away other changes you might have recently made in an upstream thread. */ void Geom:: -set_primitive(int i, const GeomPrimitive *primitive) { +set_primitive(size_t i, const GeomPrimitive *primitive) { Thread *current_thread = Thread::get_current_thread(); CDWriter cdata(_cycler, true, current_thread); - nassertv(i >= 0 && i < (int)cdata->_primitives.size()); - nassertv(primitive->check_valid(cdata->_data.get_read_pointer())); + nassertv(i < cdata->_primitives.size()); + nassertv(primitive->check_valid(cdata->_data.get_read_pointer(current_thread))); // All primitives within a particular Geom must have the same fundamental // primitive type (triangles, points, or lines). @@ -327,7 +327,7 @@ set_primitive(int i, const GeomPrimitive *primitive) { } /** - * Adds a new GeomPrimitive structure to the Geom object. This specifies a + * Inserts a new GeomPrimitive structure to the Geom object. This specifies a * particular subset of vertices that are used to define geometric primitives * of the indicated type. * @@ -335,11 +335,11 @@ set_primitive(int i, const GeomPrimitive *primitive) { * away other changes you might have recently made in an upstream thread. */ void Geom:: -add_primitive(const GeomPrimitive *primitive) { +insert_primitive(size_t i, const GeomPrimitive *primitive) { Thread *current_thread = Thread::get_current_thread(); CDWriter cdata(_cycler, true, current_thread); - nassertv(primitive->check_valid(cdata->_data.get_read_pointer())); + nassertv(primitive->check_valid(cdata->_data.get_read_pointer(current_thread))); // All primitives within a particular Geom must have the same fundamental // primitive type (triangles, points, or lines). @@ -348,9 +348,13 @@ add_primitive(const GeomPrimitive *primitive) { // They also should have a compatible shade model. CPT(GeomPrimitive) compat = primitive->match_shade_model(cdata->_shade_model); - nassertv_always(compat != (GeomPrimitive *)NULL); + nassertv_always(compat != nullptr); - cdata->_primitives.push_back((GeomPrimitive *)compat.p()); + if (i >= cdata->_primitives.size()) { + cdata->_primitives.push_back((GeomPrimitive *)compat.p()); + } else { + cdata->_primitives.insert(cdata->_primitives.begin() + i, (GeomPrimitive *)compat.p()); + } PrimitiveType new_primitive_type = compat->get_primitive_type(); if (new_primitive_type != cdata->_primitive_type) { cdata->_primitive_type = new_primitive_type; @@ -374,10 +378,10 @@ add_primitive(const GeomPrimitive *primitive) { * away other changes you might have recently made in an upstream thread. */ void Geom:: -remove_primitive(int i) { +remove_primitive(size_t i) { Thread *current_thread = Thread::get_current_thread(); CDWriter cdata(_cycler, true, current_thread); - nassertv(i >= 0 && i < (int)cdata->_primitives.size()); + nassertv(i < cdata->_primitives.size()); cdata->_primitives.erase(cdata->_primitives.begin() + i); if (cdata->_primitives.empty()) { cdata->_primitive_type = PT_none; @@ -426,11 +430,11 @@ decompose_in_place() { #endif Primitives::iterator pi; for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer()->decompose(); + CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->decompose(); (*pi) = (GeomPrimitive *)new_prim.p(); #ifndef NDEBUG - if (!new_prim->check_valid(cdata->_data.get_read_pointer())) { + if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) { all_is_valid = false; } #endif @@ -460,11 +464,11 @@ doubleside_in_place() { #endif Primitives::iterator pi; for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer()->doubleside(); + CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->doubleside(); (*pi) = (GeomPrimitive *)new_prim.p(); #ifndef NDEBUG - if (!new_prim->check_valid(cdata->_data.get_read_pointer())) { + if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) { all_is_valid = false; } #endif @@ -494,11 +498,11 @@ reverse_in_place() { #endif Primitives::iterator pi; for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer()->reverse(); + CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->reverse(); (*pi) = (GeomPrimitive *)new_prim.p(); #ifndef NDEBUG - if (!new_prim->check_valid(cdata->_data.get_read_pointer())) { + if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) { all_is_valid = false; } #endif @@ -528,11 +532,11 @@ rotate_in_place() { #endif Primitives::iterator pi; for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer()->rotate(); + CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->rotate(); (*pi) = (GeomPrimitive *)new_prim.p(); #ifndef NDEBUG - if (!new_prim->check_valid(cdata->_data.get_read_pointer())) { + if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) { all_is_valid = false; } #endif @@ -596,7 +600,7 @@ unify_in_place(int max_indices, bool preserve_order) { Primitives::const_iterator pi; for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) primitive = (*pi).get_read_pointer(); + CPT(GeomPrimitive) primitive = (*pi).get_read_pointer(current_thread); NewPrims::iterator npi = new_prims.find(primitive->get_type()); if (npi == new_prims.end()) { // This is the first primitive of this type. @@ -648,35 +652,80 @@ unify_in_place(int max_indices, bool preserve_order) { for (npi = new_prims.begin(); npi != new_prims.end(); ++npi) { GeomPrimitive *prim = (*npi).second; - nassertv(prim->check_valid(cdata->_data.get_read_pointer())); + nassertv(prim->check_valid(cdata->_data.get_read_pointer(current_thread))); // Each new primitive, naturally, inherits the Geom's overall shade model. prim->set_shade_model(cdata->_shade_model); // Should we split it up again to satisfy max_indices? if (prim->get_num_vertices() > max_indices) { + // Copy prim into smaller prims, no one of which has more than + // max_indices vertices. + GeomPrimitivePipelineReader reader(prim, current_thread); + // Copy prim into smaller prims, no one of which has more than // max_indices vertices. int i = 0; + int num_primitives = reader.get_num_primitives(); + int num_vertices_per_primitive = prim->get_num_vertices_per_primitive(); + int num_unused_vertices_per_primitive = prim->get_num_unused_vertices_per_primitive(); + if (num_vertices_per_primitive != 0) { + // This is a simple primitive type like a triangle, where all the + // primitives share the same number of vertices. + int total_vertices_per_primitive = num_vertices_per_primitive + num_unused_vertices_per_primitive; + int max_primitives = max_indices / total_vertices_per_primitive; + const unsigned char *ptr = reader.get_read_pointer(true); + size_t stride = reader.get_index_stride(); - while (i < prim->get_num_primitives()) { - PT(GeomPrimitive) smaller = prim->make_copy(); - smaller->clear_vertices(); - while (i < prim->get_num_primitives() && - smaller->get_num_vertices() + prim->get_primitive_num_vertices(i) < max_indices) { - int start = prim->get_primitive_start(i); - int end = prim->get_primitive_end(i); - for (int n = start; n < end; ++n) { - smaller->add_vertex(prim->get_vertex(n)); + while (i < num_primitives) { + PT(GeomPrimitive) smaller = prim->make_copy(); + smaller->clear_vertices(); + + // Since the number of vertices is consistent, we can calculate how + // many primitives will fit, and copy them all in one go. + int copy_primitives = min((num_primitives - i), max_primitives); + int num_vertices = copy_primitives * total_vertices_per_primitive; + nassertv(num_vertices > 0); + { + smaller->set_index_type(reader.get_index_type()); + GeomVertexArrayDataHandle writer(smaller->modify_vertices(), current_thread); + writer.unclean_set_num_rows(num_vertices); + memcpy(writer.get_write_pointer(), ptr, stride * (size_t)(num_vertices - num_unused_vertices_per_primitive)); } - smaller->close_primitive(); - ++i; + cdata->_primitives.push_back(smaller.p()); + + ptr += stride * (size_t)num_vertices; + i += copy_primitives; } + } else { + // This is a complex primitive type like a triangle strip. + CPTA_int ends = reader.get_ends(); + int start = 0; + int end = ends[0]; - cdata->_primitives.push_back(smaller.p()); + while (i < num_primitives) { + PT(GeomPrimitive) smaller = prim->make_copy(); + smaller->clear_vertices(); + + while (smaller->get_num_vertices() + (end - start) < max_indices) { + for (int n = start; n < end; ++n) { + smaller->add_vertex(reader.get_vertex(n)); + } + smaller->close_primitive(); + + ++i; + if (i >= num_primitives) { + break; + } + + start = end + num_unused_vertices_per_primitive; + end = ends[i]; + } + + cdata->_primitives.push_back(smaller.p()); + } } - } else { // The prim has few enough vertices; keep it. cdata->_primitives.push_back(prim); @@ -706,11 +755,11 @@ make_lines_in_place() { #endif Primitives::iterator pi; for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer()->make_lines(); + CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->make_lines(); (*pi) = (GeomPrimitive *)new_prim.p(); #ifndef NDEBUG - if (!new_prim->check_valid(cdata->_data.get_read_pointer())) { + if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) { all_is_valid = false; } #endif @@ -740,11 +789,11 @@ make_points_in_place() { #endif Primitives::iterator pi; for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer()->make_points(); + CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->make_points(); (*pi) = (GeomPrimitive *)new_prim.p(); #ifndef NDEBUG - if (!new_prim->check_valid(cdata->_data.get_read_pointer())) { + if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) { all_is_valid = false; } #endif @@ -774,11 +823,11 @@ make_patches_in_place() { #endif Primitives::iterator pi; for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer()->make_patches(); + CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->make_patches(); (*pi) = (GeomPrimitive *)new_prim.p(); #ifndef NDEBUG - if (!new_prim->check_valid(cdata->_data.get_read_pointer())) { + if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) { all_is_valid = false; } #endif @@ -865,7 +914,9 @@ get_num_bytes() const { */ bool Geom:: request_resident() const { - CDReader cdata(_cycler); + Thread *current_thread = Thread::get_current_thread(); + + CDReader cdata(_cycler, current_thread); bool resident = true; @@ -873,7 +924,7 @@ request_resident() const { for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - if (!(*pi).get_read_pointer()->request_resident()) { + if (!(*pi).get_read_pointer(current_thread)->request_resident()) { resident = false; } } @@ -923,7 +974,8 @@ bool Geom:: check_valid() const { Thread *current_thread = Thread::get_current_thread(); GeomPipelineReader geom_reader(this, current_thread); - GeomVertexDataPipelineReader data_reader(geom_reader.get_vertex_data(), current_thread); + CPT(GeomVertexData) vertex_data = geom_reader.get_vertex_data(); + GeomVertexDataPipelineReader data_reader(vertex_data, current_thread); data_reader.check_array_readers(); return geom_reader.check_valid(&data_reader); } @@ -1164,14 +1216,13 @@ prepare_now(PreparedGraphicsObjects *prepared_objects, * is passed true, it will wait for the data to become resident if necessary. */ bool Geom:: -draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger, - const GeomVertexData *vertex_data, bool force, - Thread *current_thread) const { +draw(GraphicsStateGuardianBase *gsg, const GeomVertexData *vertex_data, + bool force, Thread *current_thread) const { GeomPipelineReader geom_reader(this, current_thread); GeomVertexDataPipelineReader data_reader(vertex_data, current_thread); data_reader.check_array_readers(); - return geom_reader.draw(gsg, munger, &data_reader, force); + return geom_reader.draw(gsg, &data_reader, force); } /** @@ -1197,7 +1248,7 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { int num_vertices = 0; // Get the vertex data, after animation. - CPT(GeomVertexData) vertex_data = cdata->_data.get_read_pointer(); + CPT(GeomVertexData) vertex_data = cdata->_data.get_read_pointer(current_thread); vertex_data = vertex_data->animate_vertices(true, current_thread); // Now actually compute the bounding volume. We do this by using @@ -1216,6 +1267,9 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { } if (found_any) { + nassertv(!pmin.is_nan()); + nassertv(!pmax.is_nan()); + // Then we put the bounding volume around both of those points. PN_stdfloat avg_box_area; switch (btype) { @@ -1296,7 +1350,7 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) prim = (*pi).get_read_pointer(); + CPT(GeomPrimitive) prim = (*pi).get_read_pointer(current_thread); num_vertices += prim->get_num_vertices(); } @@ -1327,7 +1381,7 @@ do_calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) prim = (*pi).get_read_pointer(); + CPT(GeomPrimitive) prim = (*pi).get_read_pointer(current_thread); prim->calc_tight_bounds(min_point, max_point, sq_center_dist, found_any, vertex_data, got_mat, mat, column_name, current_thread); @@ -1345,7 +1399,7 @@ do_calc_sphere_radius(const LPoint3 ¢er, PN_stdfloat &sq_radius, for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) prim = (*pi).get_read_pointer(); + CPT(GeomPrimitive) prim = (*pi).get_read_pointer(current_thread); prim->calc_sphere_radius(center, sq_radius, found_any, vertex_data, current_thread); } @@ -1469,8 +1523,10 @@ combine_primitives(GeomPrimitive *a_prim, const GeomPrimitive *b_prim, a_prim->append_unused_vertices(a_vertices, b_vertex); } - PT(GeomVertexArrayDataHandle) a_handle = a_vertices->modify_handle(current_thread); - CPT(GeomVertexArrayDataHandle) b_handle = b_vertices->get_handle(current_thread); + PT(GeomVertexArrayDataHandle) a_handle = + new GeomVertexArrayDataHandle(move(a_vertices), current_thread); + CPT(GeomVertexArrayDataHandle) b_handle = + new GeomVertexArrayDataHandle(move(b_vertices), current_thread); size_t orig_a_vertices = a_handle->get_num_rows(); @@ -1683,8 +1739,7 @@ check_valid(const GeomVertexDataPipelineReader *data_reader) const { for (pi = _cdata->_primitives.begin(); pi != _cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) primitive = (*pi).get_read_pointer(); - GeomPrimitivePipelineReader reader(primitive, _current_thread); + GeomPrimitivePipelineReader reader((*pi).get_read_pointer(_current_thread), _current_thread); reader.check_minmax(); if (!reader.check_valid(data_reader)) { return false; @@ -1698,21 +1753,20 @@ check_valid(const GeomVertexDataPipelineReader *data_reader) const { * The implementation of Geom::draw(). */ bool GeomPipelineReader:: -draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger, +draw(GraphicsStateGuardianBase *gsg, const GeomVertexDataPipelineReader *data_reader, bool force) const { PStatTimer timer(Geom::_draw_primitive_setup_pcollector); - bool all_ok = gsg->begin_draw_primitives(this, munger, data_reader, force); + bool all_ok = gsg->begin_draw_primitives(this, data_reader, force); if (all_ok) { Geom::Primitives::const_iterator pi; for (pi = _cdata->_primitives.begin(); pi != _cdata->_primitives.end(); ++pi) { - CPT(GeomPrimitive) primitive = (*pi).get_read_pointer(); - GeomPrimitivePipelineReader reader(primitive, _current_thread); + GeomPrimitivePipelineReader reader((*pi).get_read_pointer(_current_thread), _current_thread); if (reader.get_num_vertices() != 0) { reader.check_minmax(); nassertr(reader.check_valid(data_reader), false); - if (!primitive->draw(gsg, &reader, force)) { + if (!reader.draw(gsg, force)) { all_ok = false; } } diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h index cd02493d79..8b220a5a4a 100644 --- a/panda/src/gobj/geom.h +++ b/panda/src/gobj/geom.h @@ -87,15 +87,16 @@ PUBLISHED: INLINE bool is_empty() const; - INLINE int get_num_primitives() const; - INLINE CPT(GeomPrimitive) get_primitive(int i) const; + INLINE size_t get_num_primitives() const; + INLINE CPT(GeomPrimitive) get_primitive(size_t i) const; MAKE_SEQ(get_primitives, get_num_primitives, get_primitive); - INLINE PT(GeomPrimitive) modify_primitive(int i); - void set_primitive(int i, const GeomPrimitive *primitive); - void add_primitive(const GeomPrimitive *primitive); - void remove_primitive(int i); + INLINE PT(GeomPrimitive) modify_primitive(size_t i); + void set_primitive(size_t i, const GeomPrimitive *primitive); + void insert_primitive(size_t i, const GeomPrimitive *primitive); + INLINE void add_primitive(const GeomPrimitive *primitive); + void remove_primitive(size_t i); void clear_primitives(); - MAKE_SEQ_PROPERTY(primitives, get_num_primitives, get_primitive, set_primitive, remove_primitive); + MAKE_SEQ_PROPERTY(primitives, get_num_primitives, get_primitive, set_primitive, remove_primitive, insert_primitive); INLINE PT(Geom) decompose() const; INLINE PT(Geom) doubleside() const; @@ -153,7 +154,6 @@ PUBLISHED: public: bool draw(GraphicsStateGuardianBase *gsg, - const GeomMunger *munger, const GeomVertexData *vertex_data, bool force, Thread *current_thread) const; @@ -401,15 +401,17 @@ private: */ class EXPCL_PANDA_GOBJ GeomPipelineReader : public GeomEnums { public: + INLINE GeomPipelineReader(Thread *current_thread); INLINE GeomPipelineReader(const Geom *object, Thread *current_thread); private: - INLINE GeomPipelineReader(const GeomPipelineReader ©); - INLINE void operator = (const GeomPipelineReader ©); + GeomPipelineReader(const GeomPipelineReader ©) DELETED; + GeomPipelineReader &operator = (const GeomPipelineReader ©) DELETED_ASSIGN; public: INLINE ~GeomPipelineReader(); ALLOC_DELETED_CHAIN(GeomPipelineReader); + INLINE void set_object(const Geom *object); INLINE const Geom *get_object() const; INLINE Thread *get_current_thread() const; @@ -427,7 +429,7 @@ public: INLINE GeomContext *prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg) const; - bool draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger, + bool draw(GraphicsStateGuardianBase *gsg, const GeomVertexDataPipelineReader *data_reader, bool force) const; diff --git a/panda/src/gobj/geomCacheEntry.cxx b/panda/src/gobj/geomCacheEntry.cxx index 27ab5a9e20..d16a7f6a08 100644 --- a/panda/src/gobj/geomCacheEntry.cxx +++ b/panda/src/gobj/geomCacheEntry.cxx @@ -95,8 +95,8 @@ PT(GeomCacheEntry) GeomCacheEntry:: erase() { nassertr(_next != (GeomCacheEntry *)NULL && _prev != (GeomCacheEntry *)NULL, NULL); - PT(GeomCacheEntry) keepme = this; - unref(); + PT(GeomCacheEntry) keepme; + keepme.cheat() = this; if (gobj_cat.is_debug()) { gobj_cat.debug() diff --git a/panda/src/gobj/geomLines.h b/panda/src/gobj/geomLines.h index c6ecfe9c3e..e05a4605c6 100644 --- a/panda/src/gobj/geomLines.h +++ b/panda/src/gobj/geomLines.h @@ -22,7 +22,7 @@ */ class EXPCL_PANDA_GOBJ GeomLines : public GeomPrimitive { PUBLISHED: - GeomLines(UsageHint usage_hint); + explicit GeomLines(UsageHint usage_hint); GeomLines(const GeomLines ©); virtual ~GeomLines(); ALLOC_DELETED_CHAIN(GeomLines); diff --git a/panda/src/gobj/geomLinestrips.h b/panda/src/gobj/geomLinestrips.h index a97251c0d3..ae8956eb3d 100644 --- a/panda/src/gobj/geomLinestrips.h +++ b/panda/src/gobj/geomLinestrips.h @@ -22,7 +22,7 @@ */ class EXPCL_PANDA_GOBJ GeomLinestrips : public GeomPrimitive { PUBLISHED: - GeomLinestrips(UsageHint usage_hint); + explicit GeomLinestrips(UsageHint usage_hint); GeomLinestrips(const GeomLinestrips ©); virtual ~GeomLinestrips(); ALLOC_DELETED_CHAIN(GeomLinestrips); diff --git a/panda/src/gobj/geomPatches.h b/panda/src/gobj/geomPatches.h index a77b627a12..85cda81e59 100644 --- a/panda/src/gobj/geomPatches.h +++ b/panda/src/gobj/geomPatches.h @@ -23,7 +23,7 @@ */ class EXPCL_PANDA_GOBJ GeomPatches : public GeomPrimitive { PUBLISHED: - GeomPatches(int num_vertices_per_patch, UsageHint usage_hint); + explicit GeomPatches(int num_vertices_per_patch, UsageHint usage_hint); GeomPatches(const GeomPatches ©); virtual ~GeomPatches(); ALLOC_DELETED_CHAIN(GeomPatches); diff --git a/panda/src/gobj/geomPoints.h b/panda/src/gobj/geomPoints.h index 0561e18fa6..17116d4166 100644 --- a/panda/src/gobj/geomPoints.h +++ b/panda/src/gobj/geomPoints.h @@ -22,7 +22,7 @@ */ class EXPCL_PANDA_GOBJ GeomPoints : public GeomPrimitive { PUBLISHED: - GeomPoints(UsageHint usage_hint); + explicit GeomPoints(UsageHint usage_hint); GeomPoints(const GeomPoints ©); virtual ~GeomPoints(); ALLOC_DELETED_CHAIN(GeomPoints); diff --git a/panda/src/gobj/geomPrimitive.I b/panda/src/gobj/geomPrimitive.I index 029063f864..9863701e21 100644 --- a/panda/src/gobj/geomPrimitive.I +++ b/panda/src/gobj/geomPrimitive.I @@ -124,8 +124,19 @@ get_vertex(int i) const { */ INLINE int GeomPrimitive:: get_num_primitives() const { - GeomPrimitivePipelineReader reader(this, Thread::get_current_thread()); - return reader.get_num_primitives(); + int num_vertices_per_primitive = get_num_vertices_per_primitive(); + + if (num_vertices_per_primitive == 0) { + // This is a complex primitive type like a triangle strip: each primitive + // uses a different number of vertices. + CDReader cdata(_cycler); + return cdata->_ends.size(); + + } else { + // This is a simple primitive type like a triangle: each primitive uses + // the same number of vertices. + return (get_num_vertices() / num_vertices_per_primitive); + } } /** @@ -235,6 +246,24 @@ get_vertices() const { return cdata->_vertices.get_read_pointer(); } +/** + * Equivalent to get_vertices().get_handle(). + */ +INLINE CPT(GeomVertexArrayDataHandle) GeomPrimitive:: +get_vertices_handle(Thread *current_thread) const { + CDReader cdata(_cycler, current_thread); + return new GeomVertexArrayDataHandle(cdata->_vertices.get_read_pointer(current_thread), current_thread); +} + +/** + * Equivalent to modify_vertices().get_handle(). + */ +INLINE PT(GeomVertexArrayDataHandle) GeomPrimitive:: +modify_vertices_handle(Thread *current_thread) { + CDWriter cdata(_cycler, true, current_thread); + return new GeomVertexArrayDataHandle(do_modify_vertices(cdata), current_thread); +} + /** * A convenience function to return the gap between successive index numbers, * in bytes, of the index data. @@ -414,38 +443,32 @@ CData(const GeomPrimitive::CData ©) : * */ INLINE GeomPrimitivePipelineReader:: -GeomPrimitivePipelineReader(const GeomPrimitive *object, +GeomPrimitivePipelineReader(CPT(GeomPrimitive) object, Thread *current_thread) : - _object(object), + _object(move(object)), _current_thread(current_thread), - _cdata(object->_cycler.read_unlocked(current_thread)), - _vertices_reader(NULL) +#ifndef CPPPARSER + _cdata(_object->_cycler.read_unlocked(current_thread)), +#endif + _vertices_cdata(NULL) { nassertv(_object->test_ref_count_nonzero()); #ifdef DO_PIPELINING _cdata->ref(); #endif // DO_PIPELINING + if (!_cdata->_vertices.is_null()) { - _vertices_reader = _cdata->_vertices.get_read_pointer()->get_handle(); + _vertices = _cdata->_vertices.get_read_pointer(current_thread); + _vertices_cdata = _vertices->_cycler.read_unlocked(current_thread); +#ifdef DO_PIPELINING + _vertices_cdata->ref(); +#endif // DO_PIPELINING + // We must grab the lock *after* we have incremented the reference count, + // above. + _vertices_cdata->_rw_lock.acquire(); } } -/** - * Don't attempt to copy these objects. - */ -INLINE GeomPrimitivePipelineReader:: -GeomPrimitivePipelineReader(const GeomPrimitivePipelineReader &) { - nassertv(false); -} - -/** - * Don't attempt to copy these objects. - */ -INLINE void GeomPrimitivePipelineReader:: -operator = (const GeomPrimitivePipelineReader &) { - nassertv(false); -} - /** * */ @@ -460,8 +483,17 @@ INLINE GeomPrimitivePipelineReader:: unref_delete((CycleData *)_cdata); #endif // DO_PIPELINING + if (_vertices_cdata != nullptr) { + // We must release the lock *before* we decrement the reference count, + // below. + _vertices_cdata->_rw_lock.release(); + +#ifdef DO_PIPELINING + unref_delete((CycleData *)_vertices_cdata); +#endif // DO_PIPELINING + } + #ifdef _DEBUG - _vertices_reader = NULL; _object = NULL; _cdata = NULL; #endif // _DEBUG @@ -512,7 +544,7 @@ get_index_type() const { */ INLINE bool GeomPrimitivePipelineReader:: is_indexed() const { - return (!_cdata->_vertices.is_null()); + return (!_vertices.is_null()); } /** @@ -523,8 +555,10 @@ get_num_vertices() const { if (_cdata->_num_vertices != -1) { return _cdata->_num_vertices; } else { - nassertr(!_cdata->_vertices.is_null(), 0); - return _vertices_reader->get_num_rows(); + nassertr(!_vertices.is_null(), 0); + size_t stride = _vertices->_array_format->get_stride(); + nassertr(stride != 0, 0); + return get_data_size_bytes() / stride; } } @@ -551,7 +585,7 @@ get_max_vertex() const { */ INLINE int GeomPrimitivePipelineReader:: get_data_size_bytes() const { - return _vertices_reader->get_data_size_bytes(); + return _vertices_cdata->_buffer.get_size(); } /** @@ -568,15 +602,7 @@ get_modified() const { INLINE int GeomPrimitivePipelineReader:: get_index_stride() const { nassertr(is_indexed(), 0); - return _cdata->_vertices.get_read_pointer()->get_array_format()->get_stride(); -} - -/** - * - */ -INLINE const GeomVertexArrayDataHandle *GeomPrimitivePipelineReader:: -get_vertices_reader() const { - return _vertices_reader; + return _vertices->_array_format->get_stride(); } /** @@ -584,7 +610,8 @@ get_vertices_reader() const { */ INLINE const unsigned char *GeomPrimitivePipelineReader:: get_read_pointer(bool force) const { - return _vertices_reader->get_read_pointer(force); + ((GeomVertexArrayData *)_vertices.p())->mark_used(); + return _vertices_cdata->_buffer.get_read_pointer(force); } /** @@ -632,6 +659,14 @@ prepare_now(PreparedGraphicsObjects *prepared_objects, return ((GeomPrimitive *)_object.p())->prepare_now(prepared_objects, gsg); } +/** + * Calls the appropriate method on the GSG to draw the primitive. + */ +INLINE bool GeomPrimitivePipelineReader:: +draw(GraphicsStateGuardianBase *gsg, bool force) const { + return _object->draw(gsg, this, force); +} + INLINE ostream & operator << (ostream &out, const GeomPrimitive &obj) { obj.output(out); diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index 6f918eefdb..23a905f8e3 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -165,16 +165,17 @@ add_vertex(int vertex) { consider_elevate_index_type(cdata, vertex); - int num_primitives = get_num_primitives(); - if (num_primitives > 0 && - requires_unused_vertices() && - get_num_vertices() == get_primitive_end(num_primitives - 1)) { - // If we are beginning a new primitive, give the derived class a chance to - // insert some degenerate vertices. - if (cdata->_vertices.is_null()) { - do_make_indexed(cdata); + if (requires_unused_vertices()) { + int num_primitives = get_num_primitives(); + if (num_primitives > 0 && + get_num_vertices() == get_primitive_end(num_primitives - 1)) { + // If we are beginning a new primitive, give the derived class a chance to + // insert some degenerate vertices. + if (cdata->_vertices.is_null()) { + do_make_indexed(cdata); + } + append_unused_vertices(cdata->_vertices.get_write_pointer(), vertex); } - append_unused_vertices(cdata->_vertices.get_write_pointer(), vertex); } if (cdata->_vertices.is_null()) { @@ -199,11 +200,28 @@ add_vertex(int vertex) { do_make_indexed(cdata); } - PT(GeomVertexArrayData) array_obj = cdata->_vertices.get_write_pointer(); - GeomVertexWriter index(array_obj, 0); - index.set_row_unsafe(array_obj->get_num_rows()); + { + GeomVertexArrayDataHandle handle(cdata->_vertices.get_write_pointer(), + Thread::get_current_thread()); + int num_rows = handle.get_num_rows(); + handle.set_num_rows(num_rows + 1); - index.add_data1i(vertex); + unsigned char *ptr = handle.get_write_pointer(); + switch (cdata->_index_type) { + case GeomEnums::NT_uint8: + ((uint8_t *)ptr)[num_rows] = vertex; + break; + case GeomEnums::NT_uint16: + ((uint16_t *)ptr)[num_rows] = vertex; + break; + case GeomEnums::NT_uint32: + ((uint32_t *)ptr)[num_rows] = vertex; + break; + default: + nassertv(false); + break; + } + } cdata->_modified = Geom::get_next_modified(); cdata->_got_minmax = false; @@ -888,21 +906,9 @@ make_points() const { // First, get a list of all of the vertices referenced by the original // primitive. BitArray bits; - int num_vertices = get_num_vertices(); - if (is_indexed()) { - CPT(GeomVertexArrayData) vertices = get_vertices(); - int strip_cut_index = get_strip_cut_index(); - GeomVertexReader index(vertices, 0); - for (int vi = 0; vi < num_vertices; ++vi) { - nassertr(!index.is_at_end(), NULL); - int vertex = index.get_data1i(); - if (vertex != strip_cut_index) { - bits.set_bit(vertex); - } - } - } else { - int first_vertex = get_first_vertex(); - bits.set_range(first_vertex, num_vertices); + { + GeomPrimitivePipelineReader reader(this, Thread::get_current_thread()); + reader.get_referenced_vertices(bits); } // Now construct a new index array with just those bits. @@ -1036,23 +1042,23 @@ get_num_bytes() const { * shortly; try again later. */ bool GeomPrimitive:: -request_resident() const { - CDReader cdata(_cycler); +request_resident(Thread *current_thread) const { + CDReader cdata(_cycler, current_thread); bool resident = true; if (!cdata->_vertices.is_null() && - !cdata->_vertices.get_read_pointer()->request_resident()) { + !cdata->_vertices.get_read_pointer(current_thread)->request_resident(current_thread)) { resident = false; } if (is_composite() && cdata->_got_minmax) { if (!cdata->_mins.is_null() && - !cdata->_mins.get_read_pointer()->request_resident()) { + !cdata->_mins.get_read_pointer(current_thread)->request_resident(current_thread)) { resident = false; } if (!cdata->_maxs.is_null() && - !cdata->_maxs.get_read_pointer()->request_resident()) { + !cdata->_maxs.get_read_pointer(current_thread)->request_resident(current_thread)) { resident = false; } } @@ -2178,14 +2184,17 @@ check_minmax() const { */ int GeomPrimitivePipelineReader:: get_first_vertex() const { - if (_cdata->_vertices.is_null()) { + if (_vertices.is_null()) { return _cdata->_first_vertex; - } else if (_vertices_reader->get_num_rows() == 0) { - return 0; - } else { - GeomVertexReader index(_cdata->_vertices.get_read_pointer(), 0); - return index.get_data1i(); } + + size_t size = _vertices_cdata->_buffer.get_size(); + if (size == 0) { + return 0; + } + + GeomVertexReader index(_vertices, 0); + return index.get_data1i(); } /** @@ -2193,13 +2202,25 @@ get_first_vertex() const { */ int GeomPrimitivePipelineReader:: get_vertex(int i) const { - if (!_cdata->_vertices.is_null()) { + if (!_vertices.is_null()) { // The indexed case. - nassertr(i >= 0 && i < _vertices_reader->get_num_rows(), -1); + nassertr(i >= 0 && i < get_num_vertices(), -1); - GeomVertexReader index(_cdata->_vertices.get_read_pointer(), 0); - index.set_row_unsafe(i); - return index.get_data1i(); + const unsigned char *ptr = get_read_pointer(true); + switch (_cdata->_index_type) { + case GeomEnums::NT_uint8: + return ((uint8_t *)ptr)[i]; + break; + case GeomEnums::NT_uint16: + return ((uint16_t *)ptr)[i]; + break; + case GeomEnums::NT_uint32: + return ((uint32_t *)ptr)[i]; + break; + default: + nassertr(false, -1); + return -1; + } } else { // The nonindexed case. @@ -2226,6 +2247,52 @@ get_num_primitives() const { } } +/** + * Turns on all the bits corresponding to the vertices that are referenced + * by this GeomPrimitive. + */ +void GeomPrimitivePipelineReader:: +get_referenced_vertices(BitArray &bits) const { + int num_vertices = get_num_vertices(); + + if (is_indexed()) { + int strip_cut_index = get_strip_cut_index(); + const unsigned char *ptr = get_read_pointer(true); + switch (get_index_type()) { + case GeomEnums::NT_uint8: + for (int vi = 0; vi < num_vertices; ++vi) { + int index = ((const uint8_t *)ptr)[vi]; + if (index != strip_cut_index) { + bits.set_bit(index); + } + } + break; + case GeomEnums::NT_uint16: + for (int vi = 0; vi < num_vertices; ++vi) { + int index = ((const uint16_t *)ptr)[vi]; + if (index != strip_cut_index) { + bits.set_bit(index); + } + } + break; + case GeomEnums::NT_uint32: + for (int vi = 0; vi < num_vertices; ++vi) { + int index = ((const uint32_t *)ptr)[vi]; + if (index != strip_cut_index) { + bits.set_bit(index); + } + } + break; + default: + nassertv(false); + break; + } + } else { + // Nonindexed case. + bits.set_range(get_first_vertex(), num_vertices); + } +} + /** * */ diff --git a/panda/src/gobj/geomPrimitive.h b/panda/src/gobj/geomPrimitive.h index 9c24af0c45..d596ae7a8e 100644 --- a/panda/src/gobj/geomPrimitive.h +++ b/panda/src/gobj/geomPrimitive.h @@ -59,7 +59,7 @@ protected: virtual PT(CopyOnWriteObject) make_cow_copy(); PUBLISHED: - GeomPrimitive(UsageHint usage_hint); + explicit GeomPrimitive(UsageHint usage_hint); GeomPrimitive(const GeomPrimitive ©); void operator = (const GeomPrimitive ©); virtual ~GeomPrimitive(); @@ -142,7 +142,7 @@ PUBLISHED: MAKE_PROPERTY(data_size_bytes, get_data_size_bytes); MAKE_PROPERTY(modified, get_modified); - bool request_resident() const; + bool request_resident(Thread *current_thread = Thread::get_current_thread()) const; INLINE bool check_valid(const GeomVertexData *vertex_data) const; @@ -162,7 +162,9 @@ PUBLISHED: */ INLINE CPT(GeomVertexArrayData) get_vertices() const; + INLINE CPT(GeomVertexArrayDataHandle) get_vertices_handle(Thread *current_thread) const; PT(GeomVertexArrayData) modify_vertices(int num_vertices = -1); + INLINE PT(GeomVertexArrayDataHandle) modify_vertices_handle(Thread *current_thread); void set_vertices(const GeomVertexArrayData *vertices, int num_vertices = -1); void set_nonindexed_vertices(int first_vertex, int num_vertices); @@ -347,10 +349,10 @@ private: */ class EXPCL_PANDA_GOBJ GeomPrimitivePipelineReader : public GeomEnums { public: - INLINE GeomPrimitivePipelineReader(const GeomPrimitive *object, Thread *current_thread); + INLINE GeomPrimitivePipelineReader(CPT(GeomPrimitive) object, Thread *current_thread); private: - INLINE GeomPrimitivePipelineReader(const GeomPrimitivePipelineReader ©); - INLINE void operator = (const GeomPrimitivePipelineReader ©); + GeomPrimitivePipelineReader(const GeomPrimitivePipelineReader ©) DELETED; + GeomPrimitivePipelineReader &operator = (const GeomPrimitivePipelineReader ©) DELETED_ASSIGN; public: INLINE ~GeomPrimitivePipelineReader(); @@ -369,13 +371,13 @@ public: INLINE int get_num_vertices() const; int get_vertex(int i) const; int get_num_primitives() const; + void get_referenced_vertices(BitArray &bits) const; INLINE int get_min_vertex() const; INLINE int get_max_vertex() const; INLINE int get_data_size_bytes() const; INLINE UpdateSeq get_modified() const; bool check_valid(const GeomVertexDataPipelineReader *data_reader) const; INLINE int get_index_stride() const; - INLINE const GeomVertexArrayDataHandle *get_vertices_reader() const; INLINE const unsigned char *get_read_pointer(bool force) const; INLINE int get_strip_cut_index() const; INLINE CPTA_int get_ends() const; @@ -384,13 +386,15 @@ public: INLINE IndexBufferContext *prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg) const; + INLINE bool draw(GraphicsStateGuardianBase *gsg, bool force) const; private: CPT(GeomPrimitive) _object; Thread *_current_thread; const GeomPrimitive::CData *_cdata; - CPT(GeomVertexArrayDataHandle) _vertices_reader; + CPT(GeomVertexArrayData) _vertices; + const GeomVertexArrayData::CData *_vertices_cdata; public: static TypeHandle get_class_type() { diff --git a/panda/src/gobj/geomTriangles.h b/panda/src/gobj/geomTriangles.h index f2e4d4de73..d78b783b40 100644 --- a/panda/src/gobj/geomTriangles.h +++ b/panda/src/gobj/geomTriangles.h @@ -22,7 +22,7 @@ */ class EXPCL_PANDA_GOBJ GeomTriangles : public GeomPrimitive { PUBLISHED: - GeomTriangles(UsageHint usage_hint); + explicit GeomTriangles(UsageHint usage_hint); GeomTriangles(const GeomTriangles ©); virtual ~GeomTriangles(); ALLOC_DELETED_CHAIN(GeomTriangles); diff --git a/panda/src/gobj/geomTrifans.h b/panda/src/gobj/geomTrifans.h index 0a3380faa8..69f8b43fba 100644 --- a/panda/src/gobj/geomTrifans.h +++ b/panda/src/gobj/geomTrifans.h @@ -22,7 +22,7 @@ */ class EXPCL_PANDA_GOBJ GeomTrifans : public GeomPrimitive { PUBLISHED: - GeomTrifans(UsageHint usage_hint); + explicit GeomTrifans(UsageHint usage_hint); GeomTrifans(const GeomTrifans ©); virtual ~GeomTrifans(); ALLOC_DELETED_CHAIN(GeomTrifans); diff --git a/panda/src/gobj/geomTristrips.h b/panda/src/gobj/geomTristrips.h index 5bffb872d2..43e7364b22 100644 --- a/panda/src/gobj/geomTristrips.h +++ b/panda/src/gobj/geomTristrips.h @@ -22,7 +22,7 @@ */ class EXPCL_PANDA_GOBJ GeomTristrips : public GeomPrimitive { PUBLISHED: - GeomTristrips(UsageHint usage_hint); + explicit GeomTristrips(UsageHint usage_hint); GeomTristrips(const GeomTristrips ©); virtual ~GeomTristrips(); ALLOC_DELETED_CHAIN(GeomTristrips); diff --git a/panda/src/gobj/geomVertexArrayData.I b/panda/src/gobj/geomVertexArrayData.I index 3ee8d9311f..351f56cd53 100644 --- a/panda/src/gobj/geomVertexArrayData.I +++ b/panda/src/gobj/geomVertexArrayData.I @@ -130,9 +130,25 @@ get_modified() const { * back into memory shortly; try again later. */ INLINE bool GeomVertexArrayData:: -request_resident() const { - CPT(GeomVertexArrayDataHandle) handle = get_handle(); - return handle->request_resident(); +request_resident(Thread *current_thread) const { + const GeomVertexArrayData::CData *cdata = _cycler.read_unlocked(current_thread); + +#ifdef DO_PIPELINING + cdata->ref(); +#endif + + cdata->_rw_lock.acquire(); + + ((GeomVertexArrayData *)this)->mark_used(); + bool is_resident = (cdata->_buffer.get_read_pointer(false) != nullptr); + + cdata->_rw_lock.release(); + +#ifdef DO_PIPELINING + unref_delete((CycleData *)cdata); +#endif + + return is_resident; } /** @@ -143,9 +159,7 @@ request_resident() const { */ INLINE CPT(GeomVertexArrayDataHandle) GeomVertexArrayData:: get_handle(Thread *current_thread) const { - const CData *cdata = _cycler.read_unlocked(current_thread); - return new GeomVertexArrayDataHandle(this, current_thread, - cdata, false); + return new GeomVertexArrayDataHandle(this, current_thread); } /** @@ -156,9 +170,7 @@ get_handle(Thread *current_thread) const { */ INLINE PT(GeomVertexArrayDataHandle) GeomVertexArrayData:: modify_handle(Thread *current_thread) { - CData *cdata = _cycler.write_upstream(true, current_thread); - return new GeomVertexArrayDataHandle(this, current_thread, - cdata, true); + return new GeomVertexArrayDataHandle(PT(GeomVertexArrayData)(this), current_thread); } /** @@ -202,6 +214,17 @@ set_lru_size(size_t lru_size) { } } +/** + */ +INLINE void GeomVertexArrayData:: +mark_used() { + if ((int)get_lru_size() <= vertex_data_small_size) { + SimpleLruPage::mark_used_lru(&_small_lru); + } else { + SimpleLruPage::mark_used_lru(&_independent_lru); + } +} + /** * */ @@ -234,18 +257,93 @@ operator = (const GeomVertexArrayData::CData ©) { _modified = copy._modified; } +/** + * + */ +INLINE GeomVertexArrayDataHandle:: +GeomVertexArrayDataHandle(CPT(GeomVertexArrayData) object, + Thread *current_thread) : + _current_thread(current_thread), + _cdata((GeomVertexArrayData::CData *)object->_cycler.read_unlocked(current_thread)), + _writable(false) +{ + _object.swap(object); + +#ifdef _DEBUG + nassertv(_object->test_ref_count_nonzero()); +#endif // _DEBUG +#ifdef DO_PIPELINING + _cdata->ref(); +#endif // DO_PIPELINING + // We must grab the lock *after* we have incremented the reference count, + // above. + _cdata->_rw_lock.acquire(); +#ifdef DO_MEMORY_USAGE + MemoryUsage::update_type(this, get_class_type()); +#endif +} + /** * */ INLINE GeomVertexArrayDataHandle:: GeomVertexArrayDataHandle(const GeomVertexArrayData *object, - Thread *current_thread, - const GeomVertexArrayData::CData *cdata, - bool writable) : + Thread *current_thread) : _object((GeomVertexArrayData *)object), _current_thread(current_thread), - _cdata((GeomVertexArrayData::CData *)cdata), - _writable(writable) + _cdata((GeomVertexArrayData::CData *)object->_cycler.read_unlocked(current_thread)), + _writable(false) +{ +#ifdef _DEBUG + nassertv(_object->test_ref_count_nonzero()); +#endif // _DEBUG +#ifdef DO_PIPELINING + _cdata->ref(); +#endif // DO_PIPELINING + // We must grab the lock *after* we have incremented the reference count, + // above. + _cdata->_rw_lock.acquire(); +#ifdef DO_MEMORY_USAGE + MemoryUsage::update_type(this, get_class_type()); +#endif +} + +/** + * + */ +INLINE GeomVertexArrayDataHandle:: +GeomVertexArrayDataHandle(PT(GeomVertexArrayData) object, + Thread *current_thread) : + _current_thread(current_thread), + _cdata(object->_cycler.write_upstream(true, current_thread)), + _writable(true) +{ + _object.swap(object); + +#ifdef _DEBUG + nassertv(_object->test_ref_count_nonzero()); +#endif // _DEBUG +#ifdef DO_PIPELINING + _cdata->ref(); +#endif // DO_PIPELINING + // We must grab the lock *after* we have incremented the reference count, + // above. + _cdata->_rw_lock.acquire(); +#ifdef DO_MEMORY_USAGE + MemoryUsage::update_type(this, get_class_type()); +#endif +} + +/** + * + */ +INLINE GeomVertexArrayDataHandle:: +GeomVertexArrayDataHandle(GeomVertexArrayData *object, + Thread *current_thread) : + _object(object), + _current_thread(current_thread), + _cdata(object->_cycler.write_upstream(true, current_thread)), + _writable(true) { #ifdef _DEBUG nassertv(_object->test_ref_count_nonzero()); @@ -265,7 +363,8 @@ GeomVertexArrayDataHandle(const GeomVertexArrayData *object, * Don't attempt to copy these objects. */ INLINE GeomVertexArrayDataHandle:: -GeomVertexArrayDataHandle(const GeomVertexArrayDataHandle ©) { +GeomVertexArrayDataHandle(const GeomVertexArrayDataHandle ©) + : _current_thread(copy._current_thread) { nassertv(false); } @@ -448,7 +547,7 @@ get_subdata(size_t start, size_t size) const { */ void GeomVertexArrayDataHandle:: mark_used() const { - _object->set_lru_size(_object->get_lru_size()); + _object->mark_used(); } INLINE ostream & diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h index e52876d376..c16c5831d9 100644 --- a/panda/src/gobj/geomVertexArrayData.h +++ b/panda/src/gobj/geomVertexArrayData.h @@ -95,7 +95,7 @@ PUBLISHED: void output(ostream &out) const; void write(ostream &out, int indent_level = 0) const; - INLINE bool request_resident() const; + INLINE bool request_resident(Thread *current_thread = Thread::get_current_thread()) const; INLINE CPT(GeomVertexArrayDataHandle) get_handle(Thread *current_thread = Thread::get_current_thread()) const; INLINE PT(GeomVertexArrayDataHandle) modify_handle(Thread *current_thread = Thread::get_current_thread()); @@ -124,6 +124,7 @@ public: private: INLINE void set_lru_size(size_t lru_size); + INLINE void mark_used(); void clear_prepared(PreparedGraphicsObjects *prepared_objects); void reverse_data_endianness(unsigned char *dest, @@ -230,6 +231,7 @@ private: friend class GeomVertexData; friend class PreparedGraphicsObjects; friend class GeomVertexArrayDataHandle; + friend class GeomPrimitivePipelineReader; }; /** @@ -246,10 +248,14 @@ private: */ class EXPCL_PANDA_GOBJ GeomVertexArrayDataHandle : public ReferenceCount, public GeomEnums { private: + INLINE GeomVertexArrayDataHandle(CPT(GeomVertexArrayData) object, + Thread *current_thread); INLINE GeomVertexArrayDataHandle(const GeomVertexArrayData *object, - Thread *current_thread, - const GeomVertexArrayData::CData *_cdata, - bool writable); + Thread *current_thread); + INLINE GeomVertexArrayDataHandle(PT(GeomVertexArrayData) object, + Thread *current_thread); + INLINE GeomVertexArrayDataHandle(GeomVertexArrayData *object, + Thread *current_thread); INLINE GeomVertexArrayDataHandle(const GeomVertexArrayDataHandle &); INLINE void operator = (const GeomVertexArrayDataHandle &); @@ -261,8 +267,8 @@ public: INLINE Thread *get_current_thread() const; - INLINE const unsigned char *get_read_pointer(bool force) const; - unsigned char *get_write_pointer(); + INLINE const unsigned char *get_read_pointer(bool force) const RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT); + unsigned char *get_write_pointer() RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT); PUBLISHED: INLINE const GeomVertexArrayData *get_object() const; @@ -316,7 +322,7 @@ PUBLISHED: private: PT(GeomVertexArrayData) _object; - Thread *_current_thread; + Thread *const _current_thread; GeomVertexArrayData::CData *_cdata; bool _writable; @@ -333,6 +339,11 @@ public: private: static TypeHandle _type_handle; + friend class Geom; + friend class GeomPrimitive; + friend class GeomVertexData; + friend class GeomVertexDataPipelineReader; + friend class GeomVertexDataPipelineWriter; friend class GeomVertexArrayData; }; diff --git a/panda/src/gobj/geomVertexColumn.h b/panda/src/gobj/geomVertexColumn.h index 8fa91dadfe..5e8df9821e 100644 --- a/panda/src/gobj/geomVertexColumn.h +++ b/panda/src/gobj/geomVertexColumn.h @@ -38,10 +38,10 @@ class EXPCL_PANDA_GOBJ GeomVertexColumn : public GeomEnums { private: INLINE GeomVertexColumn(); PUBLISHED: - INLINE GeomVertexColumn(CPT_InternalName name, int num_components, - NumericType numeric_type, Contents contents, - int start, int column_alignment = 0, - int num_elements = 0, int element_stride = 0); + INLINE explicit GeomVertexColumn(CPT_InternalName name, int num_components, + NumericType numeric_type, Contents contents, + int start, int column_alignment = 0, + int num_elements = 0, int element_stride = 0); INLINE GeomVertexColumn(const GeomVertexColumn ©); void operator = (const GeomVertexColumn ©); INLINE ~GeomVertexColumn(); diff --git a/panda/src/gobj/geomVertexData.I b/panda/src/gobj/geomVertexData.I index dfb2138c1a..53c1c76367 100644 --- a/panda/src/gobj/geomVertexData.I +++ b/panda/src/gobj/geomVertexData.I @@ -129,7 +129,7 @@ reserve_num_rows(int n) { * Returns the number of individual arrays stored within the data. This must * match get_format()->get_num_arrays(). */ -INLINE int GeomVertexData:: +INLINE size_t GeomVertexData:: get_num_arrays() const { CDReader cdata(_cycler); return cdata->_arrays.size(); @@ -141,12 +141,23 @@ get_num_arrays() const { * data. */ INLINE CPT(GeomVertexArrayData) GeomVertexData:: -get_array(int i) const { +get_array(size_t i) const { CDReader cdata(_cycler); - nassertr(i >= 0 && i < (int)cdata->_arrays.size(), NULL); + nassertr(i < cdata->_arrays.size(), nullptr); return cdata->_arrays[i].get_read_pointer(); } +/** + * Equivalent to get_array(i).get_handle(). + */ +INLINE CPT(GeomVertexArrayDataHandle) GeomVertexData:: +get_array_handle(size_t i) const { + Thread *current_thread = Thread::get_current_thread(); + CDReader cdata(_cycler, current_thread); + nassertr(i < cdata->_arrays.size(), nullptr); + return new GeomVertexArrayDataHandle(cdata->_arrays[i].get_read_pointer(), current_thread); +} + /** * Returns a modifiable pointer to the indicated vertex array, so that * application code may directly manipulate the data. You should avoid @@ -157,11 +168,21 @@ get_array(int i) const { * away other changes you might have recently made in an upstream thread. */ INLINE PT(GeomVertexArrayData) GeomVertexData:: -modify_array(int i) { +modify_array(size_t i) { GeomVertexDataPipelineWriter writer(this, true, Thread::get_current_thread()); return writer.modify_array(i); } +/** + * Equivalent to modify_array(i).modify_handle(). + */ +INLINE PT(GeomVertexArrayDataHandle) GeomVertexData:: +modify_array_handle(size_t i) { + Thread *current_thread = Thread::get_current_thread(); + GeomVertexDataPipelineWriter writer(this, true, current_thread); + return new GeomVertexArrayDataHandle(writer.modify_array(i), current_thread); +} + /** * Replaces the indicated vertex data array with a completely new array. You * should be careful that the new array has the same length and format as the @@ -171,7 +192,7 @@ modify_array(int i) { * away other changes you might have recently made in an upstream thread. */ INLINE void GeomVertexData:: -set_array(int i, const GeomVertexArrayData *array) { +set_array(size_t i, const GeomVertexArrayData *array) { GeomVertexDataPipelineWriter writer(this, true, Thread::get_current_thread()); writer.set_array(i, array); } @@ -575,6 +596,17 @@ CData(const GeomVertexData::CData ©) : { } +/** + * + */ +INLINE GeomVertexDataPipelineBase:: +GeomVertexDataPipelineBase(Thread *current_thread) : + _object(nullptr), + _current_thread(current_thread), + _cdata(nullptr) +{ +} + /** * */ @@ -600,11 +632,15 @@ GeomVertexDataPipelineBase(GeomVertexData *object, INLINE GeomVertexDataPipelineBase:: ~GeomVertexDataPipelineBase() { #ifdef _DEBUG - nassertv(_object->test_ref_count_nonzero()); + if (_object != nullptr) { + nassertv(_object->test_ref_count_nonzero()); + } #endif // _DEBUG #ifdef DO_PIPELINING - unref_delete((CycleData *)_cdata); + if (_cdata != nullptr) { + unref_delete((CycleData *)_cdata); + } #endif // DO_PIPELINING #ifdef _DEBUG @@ -694,6 +730,16 @@ get_modified() const { return _cdata->_modified; } +/** + * + */ +INLINE GeomVertexDataPipelineReader:: +GeomVertexDataPipelineReader(Thread *current_thread) : + GeomVertexDataPipelineBase(current_thread), + _got_array_readers(false) +{ +} + /** * */ @@ -706,33 +752,23 @@ GeomVertexDataPipelineReader(const GeomVertexData *object, { } -/** - * Don't attempt to copy these objects. - */ -INLINE GeomVertexDataPipelineReader:: -GeomVertexDataPipelineReader(const GeomVertexDataPipelineReader ©) : - GeomVertexDataPipelineBase(copy) -{ - nassertv(false); -} - -/** - * Don't attempt to copy these objects. - */ -INLINE void GeomVertexDataPipelineReader:: -operator = (const GeomVertexDataPipelineReader &) { - nassertv(false); -} - /** * */ -INLINE GeomVertexDataPipelineReader:: -~GeomVertexDataPipelineReader() { - if (_got_array_readers) { - delete_array_readers(); +INLINE void GeomVertexDataPipelineReader:: +set_object(const GeomVertexData *object) { +#ifdef DO_PIPELINING + if (_cdata != NULL) { + unref_delete((CycleData *)_cdata); } - // _object->_cycler.release_read(_cdata); +#endif // DO_PIPELINING + _array_readers.clear(); + + _object = (GeomVertexData *)object; + _cdata = (GeomVertexData::CData *)_object->_cycler.read_unlocked(_current_thread); + _got_array_readers = false; + + _cdata->ref(); } /** @@ -819,24 +855,6 @@ GeomVertexDataPipelineWriter(GeomVertexData *object, bool force_to_0, #endif // _DEBUG } -/** - * Don't attempt to copy these objects. - */ -INLINE GeomVertexDataPipelineWriter:: -GeomVertexDataPipelineWriter(const GeomVertexDataPipelineWriter ©) : - GeomVertexDataPipelineBase(copy) -{ - nassertv(false); -} - -/** - * Don't attempt to copy these objects. - */ -INLINE void GeomVertexDataPipelineWriter:: -operator = (const GeomVertexDataPipelineWriter &) { - nassertv(false); -} - /** * */ @@ -870,9 +888,9 @@ check_array_writers() const { * */ INLINE GeomVertexArrayDataHandle *GeomVertexDataPipelineWriter:: -get_array_writer(int i) const { +get_array_writer(size_t i) const { nassertr(_got_array_writers, NULL); - nassertr(i >= 0 && i < (int)_array_writers.size(), NULL); + nassertr(i < _array_writers.size(), nullptr); return _array_writers[i]; } diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx index 6b73e4fe0f..339f38114e 100644 --- a/panda/src/gobj/geomVertexData.cxx +++ b/panda/src/gobj/geomVertexData.cxx @@ -516,9 +516,7 @@ copy_from(const GeomVertexData *source, bool keep_data_objects, if (keep_data_objects) { // Copy the data, but keep the same GeomVertexArrayData object. - PT(GeomVertexArrayData) dest_data = modify_array(dest_i); - CPT(GeomVertexArrayData) source_data = source->get_array(source_i); - dest_data->modify_handle()->copy_data_from(source_data->get_handle()); + modify_array_handle(dest_i)->copy_data_from(source->get_array_handle(source_i)); } else { // Copy the GeomVertexArrayData object. if (get_array(dest_i) != source->get_array(source_i)) { @@ -533,13 +531,16 @@ copy_from(const GeomVertexData *source, bool keep_data_objects, } // Now make sure the arrays we didn't share are all filled in. - reserve_num_rows(num_rows); - set_num_rows(num_rows); + { + GeomVertexDataPipelineWriter writer(this, true, Thread::get_current_thread()); + writer.check_array_writers(); + writer.reserve_num_rows(num_rows); + writer.set_num_rows(num_rows); + } // Now go back through and copy any data that's left over. for (source_i = 0; source_i < num_arrays; ++source_i) { - CPT(GeomVertexArrayData) array_obj = source->get_array(source_i); - CPT(GeomVertexArrayDataHandle) array_handle = array_obj->get_handle(); + CPT(GeomVertexArrayDataHandle) array_handle = source->get_array_handle(source_i); const unsigned char *array_data = array_handle->get_read_pointer(true); const GeomVertexArrayFormat *source_array_format = source_format->get_array(source_i); int num_columns = source_array_format->get_num_columns(); @@ -557,8 +558,7 @@ copy_from(const GeomVertexData *source, bool keep_data_objects, if (dest_column->is_bytewise_equivalent(*source_column)) { // We can do a quick bytewise copy. - PT(GeomVertexArrayData) dest_array_obj = modify_array(dest_i); - PT(GeomVertexArrayDataHandle) dest_handle = dest_array_obj->modify_handle(); + PT(GeomVertexArrayDataHandle) dest_handle = modify_array_handle(dest_i); unsigned char *dest_array_data = dest_handle->get_write_pointer(); bytewise_copy(dest_array_data + dest_column->get_start(), @@ -569,8 +569,7 @@ copy_from(const GeomVertexData *source, bool keep_data_objects, } else if (dest_column->is_packed_argb() && source_column->is_uint8_rgba()) { // A common special case: OpenGL color to DirectX color. - PT(GeomVertexArrayData) dest_array_obj = modify_array(dest_i); - PT(GeomVertexArrayDataHandle) dest_handle = dest_array_obj->modify_handle(); + PT(GeomVertexArrayDataHandle) dest_handle = modify_array_handle(dest_i); unsigned char *dest_array_data = dest_handle->get_write_pointer(); uint8_rgba_to_packed_argb @@ -582,8 +581,7 @@ copy_from(const GeomVertexData *source, bool keep_data_objects, } else if (dest_column->is_uint8_rgba() && source_column->is_packed_argb()) { // Another common special case: DirectX color to OpenGL color. - PT(GeomVertexArrayData) dest_array_obj = modify_array(dest_i); - PT(GeomVertexArrayDataHandle) dest_handle = dest_array_obj->modify_handle(); + PT(GeomVertexArrayDataHandle) dest_handle = modify_array_handle(dest_i); unsigned char *dest_array_data = dest_handle->get_write_pointer(); packed_argb_to_uint8_rgba @@ -700,12 +698,10 @@ copy_row_from(int dest_row, const GeomVertexData *source, int num_arrays = source_format->get_num_arrays(); for (int i = 0; i < num_arrays; ++i) { - PT(GeomVertexArrayData) dest_array_obj = modify_array(i); - PT(GeomVertexArrayDataHandle) dest_handle = dest_array_obj->modify_handle(); + PT(GeomVertexArrayDataHandle) dest_handle = modify_array_handle(i); unsigned char *dest_array_data = dest_handle->get_write_pointer(); - CPT(GeomVertexArrayData) source_array_obj = source->get_array(i); - CPT(GeomVertexArrayDataHandle) source_array_handle = source_array_obj->get_handle(); + CPT(GeomVertexArrayDataHandle) source_array_handle = source->get_array_handle(i); const unsigned char *source_array_data = source_array_handle->get_read_pointer(true); const GeomVertexArrayFormat *array_format = source_format->get_array(i); @@ -1144,8 +1140,7 @@ do_set_color(GeomVertexData *vdata, const LColor &color) { packer->set_data4f(buffer, color); #endif - PT(GeomVertexArrayDataHandle) handle = - vdata->modify_array(array_index)->modify_handle(); + PT(GeomVertexArrayDataHandle) handle = vdata->modify_array_handle(array_index); unsigned char *write_ptr = handle->get_write_pointer(); unsigned char *end_ptr = write_ptr + handle->get_data_size_bytes(); write_ptr += column->get_start(); @@ -1586,7 +1581,7 @@ update_animated_vertices(GeomVertexData::CData *cdata, Thread *current_thread) { } // Then apply the transforms. - CPT(TransformBlendTable) tb_table = cdata->_transform_blend_table.get_read_pointer(); + CPT(TransformBlendTable) tb_table = cdata->_transform_blend_table.get_read_pointer(current_thread); if (tb_table != (TransformBlendTable *)NULL) { // Recompute all the blends up front, so we don't have to test each one // for staleness at each vertex. @@ -1617,7 +1612,8 @@ update_animated_vertices(GeomVertexData::CData *cdata, Thread *current_thread) { if (blend_array_format->get_stride() == 2 && blend_array_format->get_column(0)->get_component_bytes() == 2) { // The blend indices are a table of ushorts. Optimize this common case. - CPT(GeomVertexArrayDataHandle) blend_array_handle = cdata->_arrays[blend_array_index].get_read_pointer()->get_handle(current_thread); + CPT(GeomVertexArrayDataHandle) blend_array_handle = + new GeomVertexArrayDataHandle(cdata->_arrays[blend_array_index].get_read_pointer(current_thread), current_thread); const unsigned short *blendt = (const unsigned short *)blend_array_handle->get_read_pointer(true); size_t ci; @@ -2399,24 +2395,12 @@ make_array_readers() { _array_readers.reserve(_cdata->_arrays.size()); GeomVertexData::Arrays::const_iterator ai; for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) { - CPT(GeomVertexArrayData) array_obj = (*ai).get_read_pointer(); - _array_readers.push_back(array_obj->get_handle(_current_thread)); + _array_readers.push_back(new GeomVertexArrayDataHandle((*ai).get_read_pointer(_current_thread), _current_thread)); } _got_array_readers = true; } -/** - * - */ -void GeomVertexDataPipelineReader:: -delete_array_readers() { - nassertv(_got_array_readers); - - _array_readers.clear(); - _got_array_readers = false; -} - /** * */ @@ -2581,8 +2565,8 @@ reserve_num_rows(int n) { * */ PT(GeomVertexArrayData) GeomVertexDataPipelineWriter:: -modify_array(int i) { - nassertr(i >= 0 && i < (int)_cdata->_arrays.size(), NULL); +modify_array(size_t i) { + nassertr(i < _cdata->_arrays.size(), nullptr); PT(GeomVertexArrayData) new_data; if (_got_array_writers) { @@ -2602,15 +2586,15 @@ modify_array(int i) { * */ void GeomVertexDataPipelineWriter:: -set_array(int i, const GeomVertexArrayData *array) { - nassertv(i >= 0 && i < (int)_cdata->_arrays.size()); +set_array(size_t i, const GeomVertexArrayData *array) { + nassertv(i < _cdata->_arrays.size()); _cdata->_arrays[i] = (GeomVertexArrayData *)array; _object->clear_cache_stage(); _cdata->_modified = Geom::get_next_modified(); _cdata->_animated_vertices_modified = UpdateSeq(); if (_got_array_writers) { - _array_writers[i] = _cdata->_arrays[i].get_write_pointer()->modify_handle(_current_thread); + _array_writers[i] = new GeomVertexArrayDataHandle(_cdata->_arrays[i].get_write_pointer(), _current_thread); } } @@ -2624,8 +2608,7 @@ make_array_writers() { _array_writers.reserve(_cdata->_arrays.size()); GeomVertexData::Arrays::iterator ai; for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) { - PT(GeomVertexArrayData) array_obj = (*ai).get_write_pointer(); - _array_writers.push_back(array_obj->modify_handle(_current_thread)); + _array_writers.push_back(new GeomVertexArrayDataHandle((*ai).get_write_pointer(), _current_thread)); } _object->clear_cache_stage(); diff --git a/panda/src/gobj/geomVertexData.h b/panda/src/gobj/geomVertexData.h index cfb592529d..f2858d5a16 100644 --- a/panda/src/gobj/geomVertexData.h +++ b/panda/src/gobj/geomVertexData.h @@ -105,11 +105,13 @@ PUBLISHED: INLINE bool reserve_num_rows(int n); void clear_rows(); - INLINE int get_num_arrays() const; - INLINE CPT(GeomVertexArrayData) get_array(int i) const; + INLINE size_t get_num_arrays() const; + INLINE CPT(GeomVertexArrayData) get_array(size_t i) const; + INLINE CPT(GeomVertexArrayDataHandle) get_array_handle(size_t i) const; MAKE_SEQ(get_arrays, get_num_arrays, get_array); - INLINE PT(GeomVertexArrayData) modify_array(int i); - INLINE void set_array(int i, const GeomVertexArrayData *array); + INLINE PT(GeomVertexArrayData) modify_array(size_t i); + INLINE PT(GeomVertexArrayDataHandle) modify_array_handle(size_t i); + INLINE void set_array(size_t i, const GeomVertexArrayData *array); MAKE_SEQ_PROPERTY(arrays, get_num_arrays, get_array, set_array); INLINE const TransformTable *get_transform_table() const; @@ -402,10 +404,15 @@ private: */ class EXPCL_PANDA_GOBJ GeomVertexDataPipelineBase : public GeomEnums { protected: + INLINE GeomVertexDataPipelineBase(Thread *current_thread); INLINE GeomVertexDataPipelineBase(GeomVertexData *object, Thread *current_thread, GeomVertexData::CData *cdata); +private: + GeomVertexDataPipelineBase(const GeomVertexDataPipelineBase ©) DELETED; + GeomVertexDataPipelineBase &operator = (const GeomVertexDataPipelineBase ©) DELETED_ASSIGN; + public: INLINE ~GeomVertexDataPipelineBase(); @@ -425,7 +432,7 @@ public: INLINE UpdateSeq get_modified() const; protected: - PT(GeomVertexData) _object; + GeomVertexData *_object; Thread *_current_thread; GeomVertexData::CData *_cdata; }; @@ -433,18 +440,17 @@ protected: /** * Encapsulates the data from a GeomVertexData, pre-fetched for one stage of * the pipeline. + * Does not hold a reference to the GeomVertexData, so make sure it does not + * go out of scope. */ class EXPCL_PANDA_GOBJ GeomVertexDataPipelineReader : public GeomVertexDataPipelineBase { public: + INLINE GeomVertexDataPipelineReader(Thread *current_thread); INLINE GeomVertexDataPipelineReader(const GeomVertexData *object, Thread *current_thread); -private: - INLINE GeomVertexDataPipelineReader(const GeomVertexDataPipelineReader ©); - INLINE void operator = (const GeomVertexDataPipelineReader ©); -public: - INLINE ~GeomVertexDataPipelineReader(); ALLOC_DELETED_CHAIN(GeomVertexDataPipelineReader); + INLINE void set_object(const GeomVertexData *object); INLINE const GeomVertexData *get_object() const; INLINE void check_array_readers() const; @@ -480,7 +486,6 @@ public: private: void make_array_readers(); - void delete_array_readers(); bool _got_array_readers; typedef pvector ArrayReaders; @@ -501,26 +506,24 @@ private: /** * Encapsulates the data from a GeomVertexData, pre-fetched for one stage of * the pipeline. + * Does not hold a reference to the GeomVertexData, so make sure it does not + * go out of scope. */ class EXPCL_PANDA_GOBJ GeomVertexDataPipelineWriter : public GeomVertexDataPipelineBase { public: INLINE GeomVertexDataPipelineWriter(GeomVertexData *object, bool force_to_0, Thread *current_thread); -private: - INLINE GeomVertexDataPipelineWriter(const GeomVertexDataPipelineWriter ©); - INLINE void operator = (const GeomVertexDataPipelineWriter ©); -public: INLINE ~GeomVertexDataPipelineWriter(); ALLOC_DELETED_CHAIN(GeomVertexDataPipelineWriter); INLINE GeomVertexData *get_object() const; INLINE void check_array_writers() const; - INLINE GeomVertexArrayDataHandle *get_array_writer(int i) const; + INLINE GeomVertexArrayDataHandle *get_array_writer(size_t i) const; - PT(GeomVertexArrayData) modify_array(int i); - void set_array(int i, const GeomVertexArrayData *array); + PT(GeomVertexArrayData) modify_array(size_t i); + void set_array(size_t i, const GeomVertexArrayData *array); int get_num_rows() const; bool set_num_rows(int n); diff --git a/panda/src/gobj/geomVertexFormat.cxx b/panda/src/gobj/geomVertexFormat.cxx index 0b1795c093..f3652e8321 100644 --- a/panda/src/gobj/geomVertexFormat.cxx +++ b/panda/src/gobj/geomVertexFormat.cxx @@ -310,7 +310,9 @@ add_array(const GeomVertexArrayFormat *array_format) { void GeomVertexFormat:: insert_array(size_t array, const GeomVertexArrayFormat *array_format) { nassertv(!is_registered()); - nassertv(array <= _arrays.size()); + if (array > _arrays.size()) { + array = _arrays.size(); + } _arrays.insert(_arrays.begin() + array, (GeomVertexArrayFormat *)array_format); } @@ -377,6 +379,22 @@ get_column(size_t i) const { return NULL; } +/** + * Returns the name of the ith column, across all arrays. + */ +const InternalName *GeomVertexFormat:: +get_column_name(size_t i) const { + Arrays::const_iterator ai; + for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) { + if (i < (size_t)(*ai)->get_num_columns()) { + return (*ai)->get_column(i)->get_name(); + } + i -= (*ai)->get_num_columns(); + } + + return nullptr; +} + /** * Returns the index number of the array with the ith column. * diff --git a/panda/src/gobj/geomVertexFormat.h b/panda/src/gobj/geomVertexFormat.h index c7451225e2..aa2ef4606a 100644 --- a/panda/src/gobj/geomVertexFormat.h +++ b/panda/src/gobj/geomVertexFormat.h @@ -92,6 +92,7 @@ PUBLISHED: int get_array_with(const InternalName *name) const; const GeomVertexColumn *get_column(const InternalName *name) const; INLINE bool has_column(const InternalName *name) const; + const InternalName *get_column_name(size_t i) const; MAKE_SEQ(get_columns, get_num_columns, get_column); @@ -120,11 +121,14 @@ PUBLISHED: MAKE_SEQ(get_morph_bases, get_num_morphs, get_morph_base); MAKE_SEQ(get_morph_deltas, get_num_morphs, get_morph_delta); - MAKE_SEQ_PROPERTY(arrays, get_num_arrays, get_array, set_array, remove_array); - MAKE_SEQ_PROPERTY(columns, get_num_columns, get_column); + MAKE_SEQ_PROPERTY(arrays, get_num_arrays, get_array, set_array, remove_array, insert_array); MAKE_SEQ_PROPERTY(points, get_num_points, get_point); MAKE_SEQ_PROPERTY(vectors, get_num_vectors, get_vector); + // We also define this as a mapping interface, for lookups by name. + MAKE_MAP_PROPERTY(columns, has_column, get_column); + MAKE_MAP_KEYS_SEQ(columns, get_num_columns, get_column_name); + void output(ostream &out) const; void write(ostream &out, int indent_level = 0) const; void write_with_data(ostream &out, int indent_level, diff --git a/panda/src/gobj/geomVertexWriter.cxx b/panda/src/gobj/geomVertexWriter.cxx index b80a30fe67..ef05c3bc44 100644 --- a/panda/src/gobj/geomVertexWriter.cxx +++ b/panda/src/gobj/geomVertexWriter.cxx @@ -14,7 +14,7 @@ #include "geomVertexWriter.h" -#ifndef NDEBUG +#ifdef _DEBUG // This is defined just for the benefit of having something non-NULL to // return from a nassertr() call. unsigned char GeomVertexWriter::empty_buffer[100] = { 0 }; diff --git a/panda/src/gobj/geomVertexWriter.h b/panda/src/gobj/geomVertexWriter.h index 815ca6435f..aaedaf565b 100644 --- a/panda/src/gobj/geomVertexWriter.h +++ b/panda/src/gobj/geomVertexWriter.h @@ -212,7 +212,7 @@ private: int _start_row; -#ifndef NDEBUG +#ifdef _DEBUG // This is defined just for the benefit of having something non-NULL to // return from a nassertr() call. static unsigned char empty_buffer[100]; diff --git a/panda/src/gobj/internalName.h b/panda/src/gobj/internalName.h index 7485e6f758..eff928b668 100644 --- a/panda/src/gobj/internalName.h +++ b/panda/src/gobj/internalName.h @@ -178,6 +178,10 @@ private: static TypeHandle _texcoord_type_handle; }; +// We can safely redefine this as a no-op. +template<> +INLINE void PointerToBase::update_type(To *ptr) {} + INLINE ostream &operator << (ostream &out, const InternalName &tcn); /** diff --git a/panda/src/gobj/internalName_ext.cxx b/panda/src/gobj/internalName_ext.cxx index 24c2852968..b98bcd9c12 100644 --- a/panda/src/gobj/internalName_ext.cxx +++ b/panda/src/gobj/internalName_ext.cxx @@ -26,7 +26,7 @@ make(PyUnicodeObject *str) { if (!PyUnicode_CHECK_INTERNED(str)) { // Not an interned string; don't bother. Py_ssize_t len = 0; - char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len); + const char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len); if (c_str == NULL) { return NULL; } @@ -43,7 +43,7 @@ make(PyUnicodeObject *str) { } else { Py_ssize_t len = 0; - char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len); + const char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len); string name(c_str, len); #else diff --git a/panda/src/gobj/material.I b/panda/src/gobj/material.I index a408f2a0e4..777a555200 100644 --- a/panda/src/gobj/material.I +++ b/panda/src/gobj/material.I @@ -327,3 +327,11 @@ INLINE void Material:: set_attrib_lock() { _flags |= F_attrib_lock; } + +/** + * + */ +INLINE int Material:: +get_flags() const { + return _flags; +} diff --git a/panda/src/gobj/material.h b/panda/src/gobj/material.h index 1a37415294..17344df46a 100644 --- a/panda/src/gobj/material.h +++ b/panda/src/gobj/material.h @@ -127,18 +127,8 @@ PUBLISHED: MAKE_PROPERTY(local, get_local, set_local); MAKE_PROPERTY(twoside, get_twoside, set_twoside); -private: - LColor _base_color; - LColor _ambient; - LColor _diffuse; - LColor _specular; - LColor _emission; - PN_stdfloat _shininess; - PN_stdfloat _roughness; - PN_stdfloat _metallic; - PN_stdfloat _refractive_index; - - static PT(Material) _default; +public: + INLINE int get_flags() const; enum Flags { F_ambient = 0x001, @@ -153,6 +143,20 @@ private: F_base_color = 0x200, F_refractive_index = 0x400, }; + +private: + LColor _base_color; + LColor _ambient; + LColor _diffuse; + LColor _specular; + LColor _emission; + PN_stdfloat _shininess; + PN_stdfloat _roughness; + PN_stdfloat _metallic; + PN_stdfloat _refractive_index; + + static PT(Material) _default; + int _flags; public: diff --git a/panda/src/gobj/perspectiveLens.h b/panda/src/gobj/perspectiveLens.h index 556b3b2fd0..dfda234645 100644 --- a/panda/src/gobj/perspectiveLens.h +++ b/panda/src/gobj/perspectiveLens.h @@ -25,7 +25,7 @@ class EXPCL_PANDA_GOBJ PerspectiveLens : public Lens { PUBLISHED: INLINE PerspectiveLens(); - INLINE PerspectiveLens(PN_stdfloat hfov, PN_stdfloat vfov); + INLINE explicit PerspectiveLens(PN_stdfloat hfov, PN_stdfloat vfov); public: INLINE PerspectiveLens(const PerspectiveLens ©); diff --git a/panda/src/gobj/preparedGraphicsObjects.cxx b/panda/src/gobj/preparedGraphicsObjects.cxx index 5c9ec855e3..233edb46b3 100644 --- a/panda/src/gobj/preparedGraphicsObjects.cxx +++ b/panda/src/gobj/preparedGraphicsObjects.cxx @@ -27,6 +27,8 @@ #include "config_gobj.h" #include "throw_event.h" +TypeHandle PreparedGraphicsObjects::EnqueuedObject::_type_handle; + int PreparedGraphicsObjects::_name_index = 0; /** @@ -191,7 +193,25 @@ void PreparedGraphicsObjects:: enqueue_texture(Texture *tex) { ReMutexHolder holder(_lock); - _enqueued_textures.insert(tex); + _enqueued_textures.insert(EnqueuedTextures::value_type(tex, nullptr)); +} + +/** + * Like enqueue_texture, but returns an AsyncFuture that can be used to query + * the status of the texture's preparation. + */ +PT(PreparedGraphicsObjects::EnqueuedObject) PreparedGraphicsObjects:: +enqueue_texture_future(Texture *tex) { + ReMutexHolder holder(_lock); + + pair result = + _enqueued_textures.insert(EnqueuedTextures::value_type(tex, nullptr)); + if (result.first->second == nullptr) { + result.first->second = new EnqueuedObject(this, tex); + } + PT(EnqueuedObject) fut = result.first->second; + nassertr(!fut->cancelled(), fut) + return fut; } /** @@ -220,6 +240,9 @@ dequeue_texture(Texture *tex) { EnqueuedTextures::iterator qi = _enqueued_textures.find(tex); if (qi != _enqueued_textures.end()) { + if (qi->second != nullptr) { + qi->second->notify_removed(); + } _enqueued_textures.erase(qi); return true; } @@ -291,6 +314,17 @@ release_all_textures() { } _prepared_textures.clear(); + + // Mark any futures as cancelled. + EnqueuedTextures::iterator qti; + for (qti = _enqueued_textures.begin(); + qti != _enqueued_textures.end(); + ++qti) { + if (qti->second != nullptr) { + qti->second->notify_removed(); + } + } + _enqueued_textures.clear(); return num_textures; @@ -665,10 +699,28 @@ prepare_geom_now(Geom *geom, GraphicsStateGuardianBase *gsg) { * when the GSG is next ready to do this (presumably at the next frame). */ void PreparedGraphicsObjects:: -enqueue_shader(Shader *se) { +enqueue_shader(Shader *shader) { ReMutexHolder holder(_lock); - _enqueued_shaders.insert(se); + _enqueued_shaders.insert(EnqueuedShaders::value_type(shader, nullptr)); +} + +/** + * Like enqueue_shader, but returns an AsyncFuture that can be used to query + * the status of the shader's preparation. + */ +PT(PreparedGraphicsObjects::EnqueuedObject) PreparedGraphicsObjects:: +enqueue_shader_future(Shader *shader) { + ReMutexHolder holder(_lock); + + pair result = + _enqueued_shaders.insert(EnqueuedShaders::value_type(shader, nullptr)); + if (result.first->second == nullptr) { + result.first->second = new EnqueuedObject(this, shader); + } + PT(EnqueuedObject) fut = result.first->second; + nassertr(!fut->cancelled(), fut) + return fut; } /** @@ -697,6 +749,9 @@ dequeue_shader(Shader *se) { EnqueuedShaders::iterator qi = _enqueued_shaders.find(se); if (qi != _enqueued_shaders.end()) { + if (qi->second != nullptr) { + qi->second->notify_removed(); + } _enqueued_shaders.erase(qi); return true; } @@ -759,6 +814,17 @@ release_all_shaders() { } _prepared_shaders.clear(); + + // Mark any futures as cancelled. + EnqueuedShaders::iterator qsi; + for (qsi = _enqueued_shaders.begin(); + qsi != _enqueued_shaders.end(); + ++qsi) { + if (qsi->second != nullptr) { + qsi->second->notify_removed(); + } + } + _enqueued_shaders.clear(); return num_shaders; @@ -1358,6 +1424,73 @@ prepare_shader_buffer_now(ShaderBuffer *data, GraphicsStateGuardianBase *gsg) { return bc; } +/** + * Creates a new future for the given object. + */ +PreparedGraphicsObjects::EnqueuedObject:: +EnqueuedObject(PreparedGraphicsObjects *pgo, TypedWritableReferenceCount *object) : + _pgo(pgo), + _object(object) { +} + +/** + * Indicates that the preparation request is done. + */ +void PreparedGraphicsObjects::EnqueuedObject:: +set_result(SavedContext *context) { + nassertv(!done()); + AsyncFuture::set_result(context); + _pgo = nullptr; +} + +/** + * Called by PreparedGraphicsObjects to indicate that the preparation request + * has been cancelled. + */ +void PreparedGraphicsObjects::EnqueuedObject:: +notify_removed() { + _pgo = nullptr; + nassertv_always(AsyncFuture::cancel()); +} + +/** + * Cancels the pending preparation request. Has no effect if the preparation + * is already complete or was already cancelled. + */ +bool PreparedGraphicsObjects::EnqueuedObject:: +cancel() { + PreparedGraphicsObjects *pgo = _pgo; + if (_object == nullptr || pgo == nullptr) { + nassertr(done(), false); + return false; + } + + // We don't upcall here, because the dequeue function will end up calling + // notify_removed(). + _result = nullptr; + _pgo = nullptr; + + if (_object->is_of_type(Texture::get_class_type())) { + return pgo->dequeue_texture((Texture *)_object.p()); + + } else if (_object->is_of_type(Geom::get_class_type())) { + return pgo->dequeue_geom((Geom *)_object.p()); + + } else if (_object->is_of_type(Shader::get_class_type())) { + return pgo->dequeue_shader((Shader *)_object.p()); + + } else if (_object->is_of_type(GeomVertexArrayData::get_class_type())) { + return pgo->dequeue_vertex_buffer((GeomVertexArrayData *)_object.p()); + + } else if (_object->is_of_type(GeomPrimitive::get_class_type())) { + return pgo->dequeue_index_buffer((GeomPrimitive *)_object.p()); + + } else if (_object->is_of_type(ShaderBuffer::get_class_type())) { + return pgo->dequeue_shader_buffer((ShaderBuffer *)_object.p()); + } + return false; +} + /** * This is called by the GraphicsStateGuardian to indicate that it is about to * begin processing of the frame. @@ -1446,11 +1579,15 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) { for (qti = _enqueued_textures.begin(); qti != _enqueued_textures.end(); ++qti) { - Texture *tex = (*qti); + Texture *tex = qti->first; + TextureContext *first_tc = nullptr; for (int view = 0; view < tex->get_num_views(); ++view) { TextureContext *tc = tex->prepare_now(view, this, gsg); - if (tc != (TextureContext *)NULL) { + if (tc != nullptr) { gsg->update_texture(tc, true); + if (view == 0 && qti->second != nullptr) { + qti->second->set_result(tc); + } } } } @@ -1481,8 +1618,11 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) { for (qsi = _enqueued_shaders.begin(); qsi != _enqueued_shaders.end(); ++qsi) { - Shader *shader = (*qsi); - shader->prepare_now(this, gsg); + Shader *shader = qsi->first; + ShaderContext *sc = shader->prepare_now(this, gsg); + if (qti->second != nullptr) { + qti->second->set_result(sc); + } } _enqueued_shaders.clear(); diff --git a/panda/src/gobj/preparedGraphicsObjects.h b/panda/src/gobj/preparedGraphicsObjects.h index 1dbde2d305..3eaed37167 100644 --- a/panda/src/gobj/preparedGraphicsObjects.h +++ b/panda/src/gobj/preparedGraphicsObjects.h @@ -29,6 +29,7 @@ #include "reMutex.h" #include "bufferResidencyTracker.h" #include "adaptiveLru.h" +#include "asyncFuture.h" class TextureContext; class SamplerContext; @@ -38,6 +39,7 @@ class VertexBufferContext; class IndexBufferContext; class BufferContext; class GraphicsStateGuardianBase; +class SavedContext; /** * A table of objects that are saved within the graphics context for reference @@ -158,6 +160,56 @@ PUBLISHED: GraphicsStateGuardianBase *gsg); public: + /** + * This is a handle to an enqueued object, from which the result can be + * obtained upon completion. + */ + class EXPCL_PANDA_GOBJ EnqueuedObject FINAL : public AsyncFuture { + public: + EnqueuedObject(PreparedGraphicsObjects *pgo, TypedWritableReferenceCount *object); + + TypedWritableReferenceCount *get_object() { return _object.p(); } + SavedContext *get_result() { return (SavedContext *)AsyncFuture::get_result(); } + void set_result(SavedContext *result); + + void notify_removed(); + virtual bool cancel() FINAL; + + PUBLISHED: + MAKE_PROPERTY(object, get_object); + + private: + PreparedGraphicsObjects *_pgo; + PT(TypedWritableReferenceCount) const _object; + + public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + AsyncFuture::init_type(); + register_type(_type_handle, "EnqueuedObject", + AsyncFuture::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + private: + static TypeHandle _type_handle; + }; + + // These are variations of enqueue_xxx that also return a future. They are + // used to implement texture->prepare(), etc. They are only marked public + // so we don't have to define a whole bunch of friend classes. + PT(EnqueuedObject) enqueue_texture_future(Texture *tex); + //PT(EnqueuedObject) enqueue_geom_future(Geom *geom); + PT(EnqueuedObject) enqueue_shader_future(Shader *shader); + //PT(EnqueuedObject) enqueue_vertex_buffer_future(GeomVertexArrayData *data); + //PT(EnqueuedObject) enqueue_index_buffer_future(GeomPrimitive *data); + //PT(EnqueuedObject) enqueue_shader_buffer_future(ShaderBuffer *data); + void begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread); void end_frame(Thread *current_thread); @@ -167,11 +219,11 @@ private: private: typedef phash_set Textures; - typedef phash_set< PT(Texture) > EnqueuedTextures; + typedef phash_map< PT(Texture), PT(EnqueuedObject) > EnqueuedTextures; typedef phash_set Geoms; typedef phash_set< PT(Geom) > EnqueuedGeoms; typedef phash_set Shaders; - typedef phash_set< PT(Shader) > EnqueuedShaders; + typedef phash_map< PT(Shader), PT(EnqueuedObject) > EnqueuedShaders; typedef phash_set Buffers; typedef phash_set< PT(GeomVertexArrayData) > EnqueuedVertexBuffers; typedef phash_set< PT(GeomPrimitive) > EnqueuedIndexBuffers; diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index 27a055a794..a2c54a5d0b 100644 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -390,8 +390,10 @@ cp_dependency(ShaderMatInput inp) { if ((inp == SMO_model_to_view) || (inp == SMO_view_to_model) || (inp == SMO_model_to_apiview) || - (inp == SMO_apiview_to_model) || - (inp == SMO_view_to_world) || + (inp == SMO_apiview_to_model)) { + dep |= SSD_transform; + } + if ((inp == SMO_view_to_world) || (inp == SMO_world_to_view) || (inp == SMO_view_x_to_view) || (inp == SMO_view_to_view_x) || @@ -404,7 +406,7 @@ cp_dependency(ShaderMatInput inp) { (inp == SMO_dlight_x) || (inp == SMO_plight_x) || (inp == SMO_slight_x)) { - dep |= SSD_transform; + dep |= SSD_view_transform; } if ((inp == SMO_texpad_x) || (inp == SMO_texpix_x) || @@ -426,7 +428,9 @@ cp_dependency(ShaderMatInput inp) { (inp == SMO_view_to_apiclip_x)) { dep |= SSD_shaderinputs; - if ((inp == SMO_alight_x) || + if ((inp == SMO_texpad_x) || + (inp == SMO_texpix_x) || + (inp == SMO_alight_x) || (inp == SMO_dlight_x) || (inp == SMO_plight_x) || (inp == SMO_slight_x) || @@ -446,10 +450,12 @@ cp_dependency(ShaderMatInput inp) { } } if ((inp == SMO_light_ambient) || - (inp == SMO_light_source_i_attrib)) { - dep |= SSD_light; - if (inp == SMO_light_source_i_attrib) { - dep |= SSD_transform; + (inp == SMO_light_source_i_attrib) || + (inp == SMO_light_source_i_packed)) { + dep |= SSD_light | SSD_frame; + if (inp == SMO_light_source_i_attrib || + inp == SMO_light_source_i_packed) { + dep |= SSD_view_transform; } } if ((inp == SMO_light_product_i_ambient) || @@ -461,7 +467,7 @@ cp_dependency(ShaderMatInput inp) { (inp == SMO_apiview_clipplane_i)) { dep |= SSD_clip_planes; } - if (inp == SMO_texmat_i || inp == SMO_inv_texmat_i) { + if (inp == SMO_texmat_i || inp == SMO_inv_texmat_i || inp == SMO_texscale_i) { dep |= SSD_tex_matrix; } if ((inp == SMO_window_size) || @@ -479,7 +485,7 @@ cp_dependency(ShaderMatInput inp) { (inp == SMO_apiclip_to_apiview)) { dep |= SSD_projection; } - if (inp == SMO_tex_is_alpha_i) { + if (inp == SMO_tex_is_alpha_i || inp == SMO_texcolor_i) { dep |= SSD_texture | SSD_frame; } @@ -730,6 +736,29 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) { return true; } + if (pieces[0] == "mat" && pieces[1] == "shadow") { + if ((!cp_errchk_parameter_words(p,3))|| + (!cp_errchk_parameter_in(p)) || + (!cp_errchk_parameter_uniform(p))|| + (!cp_errchk_parameter_float(p,16,16))) { + return false; + } + ShaderMatSpec bind; + bind._id = p._id; + bind._piece = SMP_whole; + bind._func = SMF_compose; + bind._part[1] = SMO_light_source_i_attrib; + bind._arg[1] = InternalName::make("shadowViewMatrix"); + bind._part[0] = SMO_view_to_apiview; + bind._arg[0] = NULL; + bind._index = atoi(pieces[2].c_str()); + + cp_optimize_mat_spec(bind); + _mat_spec.push_back(bind); + _mat_deps |= bind._dep[0] | bind._dep[1]; + return true; + } + // Implement some macros. Macros work by altering the contents of the // 'pieces' array, and then falling through. @@ -829,7 +858,13 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) { ShaderMatSpec bind; bind._id = p._id; + bind._piece = SMP_whole; bind._func = SMF_compose; + bind._part[1] = SMO_light_source_i_attrib; + bind._arg[1] = InternalName::make("shadowViewMatrix"); + bind._part[0] = SMO_view_to_apiview; + bind._arg[0] = NULL; + bind._index = atoi(pieces[2].c_str()); int next = 1; pieces.push_back(""); @@ -954,6 +989,30 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) { bind._arg[0] = NULL; bind._part[1] = SMO_identity; bind._arg[1] = NULL; + } else if (pieces[1].compare(0, 5, "light") == 0) { + if (!cp_errchk_parameter_float(p,16,16)) { + return false; + } + bind._id = p._id; + bind._piece = SMP_transpose; + bind._func = SMF_first; + bind._part[0] = SMO_light_source_i_packed; + bind._arg[0] = NULL; + bind._part[1] = SMO_identity; + bind._arg[1] = NULL; + bind._index = atoi(pieces[1].c_str() + 5); + } else if (pieces[1].compare(0, 5, "lspec") == 0) { + if (!cp_errchk_parameter_float(p,3,4)) { + return false; + } + bind._id = p._id; + bind._piece = SMP_row3; + bind._func = SMF_first; + bind._part[0] = SMO_light_source_i_attrib; + bind._arg[0] = InternalName::make("specular"); + bind._part[1] = SMO_identity; + bind._arg[1] = NULL; + bind._index = atoi(pieces[1].c_str() + 5); } else { cp_report_error(p,"Unknown attr parameter."); return false; @@ -1090,6 +1149,52 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) { return true; } + if (pieces[0] == "texscale") { + if ((!cp_errchk_parameter_words(p,2))|| + (!cp_errchk_parameter_in(p)) || + (!cp_errchk_parameter_uniform(p))|| + (!cp_errchk_parameter_float(p,3,4))) { + return false; + } + ShaderMatSpec bind; + bind._id = p._id; + bind._piece = SMP_row3; + bind._func = SMF_first; + bind._part[0] = SMO_texscale_i; + bind._arg[0] = NULL; + bind._part[1] = SMO_identity; + bind._arg[1] = NULL; + bind._index = atoi(pieces[1].c_str()); + + cp_optimize_mat_spec(bind); + _mat_spec.push_back(bind); + _mat_deps |= bind._dep[0] | bind._dep[1]; + return true; + } + + if (pieces[0] == "texcolor") { + if ((!cp_errchk_parameter_words(p,2))|| + (!cp_errchk_parameter_in(p)) || + (!cp_errchk_parameter_uniform(p))|| + (!cp_errchk_parameter_float(p,3,4))) { + return false; + } + ShaderMatSpec bind; + bind._id = p._id; + bind._piece = SMP_row3; + bind._func = SMF_first; + bind._part[0] = SMO_texcolor_i; + bind._arg[0] = NULL; + bind._part[1] = SMO_identity; + bind._arg[1] = NULL; + bind._index = atoi(pieces[1].c_str()); + + cp_optimize_mat_spec(bind); + _mat_spec.push_back(bind); + _mat_deps |= bind._dep[0] | bind._dep[1]; + return true; + } + if (pieces[0] == "plane") { if ((!cp_errchk_parameter_words(p,2))|| (!cp_errchk_parameter_in(p)) || @@ -1229,9 +1334,9 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) { } ShaderTexSpec bind; bind._id = p._id; - bind._name = InternalName::make(pieces[1])->append("shadowMap"); - bind._stage = -1; - bind._part = STO_named_input; + bind._name = nullptr; + bind._stage = atoi(pieces[1].c_str()); + bind._part = STO_light_i_shadow_map; switch (p._type) { case SAT_sampler2d: bind._desired_type = Texture::TT_2d_texture; break; case SAT_sampler_cube: bind._desired_type = Texture::TT_cube_map; break; @@ -3302,9 +3407,10 @@ parse_eof() { * Use this function instead of prepare_now() to preload textures from a user * interface standpoint. */ -void Shader:: +PT(AsyncFuture) Shader:: prepare(PreparedGraphicsObjects *prepared_objects) { - prepared_objects->enqueue_shader(this); + PT(PreparedGraphicsObjects::EnqueuedObject) obj = prepared_objects->enqueue_shader_future(this); + return obj.p(); } /** diff --git a/panda/src/gobj/shader.h b/panda/src/gobj/shader.h index e7e843b9c6..3b23b2d698 100644 --- a/panda/src/gobj/shader.h +++ b/panda/src/gobj/shader.h @@ -32,6 +32,7 @@ #include "pta_LVecBase3.h" #include "pta_LVecBase2.h" #include "epvector.h" +#include "asyncFuture.h" #ifdef HAVE_CG // I don't want to include the Cg header file into panda as a whole. Instead, @@ -109,7 +110,7 @@ PUBLISHED: INLINE bool get_cache_compiled_shader() const; INLINE void set_cache_compiled_shader(bool flag); - void prepare(PreparedGraphicsObjects *prepared_objects); + PT(AsyncFuture) prepare(PreparedGraphicsObjects *prepared_objects); bool is_prepared(PreparedGraphicsObjects *prepared_objects) const; bool release(PreparedGraphicsObjects *prepared_objects); int release_all(); @@ -202,6 +203,17 @@ public: // Hack for text rendering. Don't use in user shaders. SMO_tex_is_alpha_i, + SMO_transform_i, + SMO_slider_i, + + SMO_light_source_i_packed, + + // Texture scale component of texture matrix. + SMO_texscale_i, + + // Color of an M_blend texture stage. + SMO_texcolor_i, + SMO_INVALID }; @@ -287,7 +299,7 @@ public: enum ShaderStateDep { SSD_NONE = 0x000, SSD_general = 0x001, - SSD_transform = 0x002, + SSD_transform = 0x2002, SSD_color = 0x004, SSD_colorscale = 0x008, SSD_material = 0x010, @@ -299,6 +311,7 @@ public: SSD_frame = 0x400, SSD_projection = 0x800, SSD_texture = 0x1000, + SSD_view_transform= 0x2000, }; enum ShaderBug { diff --git a/panda/src/gobj/shaderBuffer.h b/panda/src/gobj/shaderBuffer.h index 549ebdc536..f5fe50dbc1 100644 --- a/panda/src/gobj/shaderBuffer.h +++ b/panda/src/gobj/shaderBuffer.h @@ -32,8 +32,8 @@ private: INLINE ShaderBuffer() DEFAULT_CTOR; PUBLISHED: - INLINE ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint); - INLINE ShaderBuffer(const string &name, pvector initial_data, UsageHint usage_hint); + INLINE explicit ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint); + INLINE explicit ShaderBuffer(const string &name, pvector initial_data, UsageHint usage_hint); public: INLINE uint64_t get_data_size_bytes() const; diff --git a/panda/src/gobj/shaderContext.h b/panda/src/gobj/shaderContext.h index 7a5312faa2..a8d8add8ed 100644 --- a/panda/src/gobj/shaderContext.h +++ b/panda/src/gobj/shaderContext.h @@ -32,7 +32,10 @@ class EXPCL_PANDA_GOBJ ShaderContext: public SavedContext { public: INLINE ShaderContext(Shader *se); - INLINE virtual void set_state_and_transform(const RenderState *, const TransformState *, const TransformState*) {}; + virtual void set_state_and_transform(const RenderState *, + const TransformState *, + const TransformState *, + const TransformState *) {}; INLINE virtual bool valid() { return false; } INLINE virtual void bind() {}; diff --git a/panda/src/gobj/simpleAllocator.h b/panda/src/gobj/simpleAllocator.h index 4bc417c39c..7793fdc3fe 100644 --- a/panda/src/gobj/simpleAllocator.h +++ b/panda/src/gobj/simpleAllocator.h @@ -28,7 +28,7 @@ class SimpleAllocatorBlock; */ class EXPCL_PANDA_GOBJ SimpleAllocator : public LinkedListNode { PUBLISHED: - INLINE SimpleAllocator(size_t max_size, Mutex &lock); + INLINE explicit SimpleAllocator(size_t max_size, Mutex &lock); virtual ~SimpleAllocator(); INLINE SimpleAllocatorBlock *alloc(size_t size); diff --git a/panda/src/gobj/simpleLru.h b/panda/src/gobj/simpleLru.h index 16669e5a12..0eaaced1c8 100644 --- a/panda/src/gobj/simpleLru.h +++ b/panda/src/gobj/simpleLru.h @@ -27,7 +27,7 @@ class SimpleLruPage; */ class EXPCL_PANDA_GOBJ SimpleLru : public LinkedListNode, public Namable { PUBLISHED: - SimpleLru(const string &name, size_t max_size); + explicit SimpleLru(const string &name, size_t max_size); ~SimpleLru(); INLINE size_t get_total_size() const; @@ -64,7 +64,7 @@ private: */ class EXPCL_PANDA_GOBJ SimpleLruPage : public LinkedListNode { PUBLISHED: - INLINE SimpleLruPage(size_t lru_size); + INLINE explicit SimpleLruPage(size_t lru_size); INLINE SimpleLruPage(const SimpleLruPage ©); INLINE void operator = (const SimpleLruPage ©); diff --git a/panda/src/gobj/texture.I b/panda/src/gobj/texture.I index 47ce9e233f..29b1702280 100644 --- a/panda/src/gobj/texture.I +++ b/panda/src/gobj/texture.I @@ -1544,7 +1544,14 @@ INLINE size_t Texture:: get_ram_mipmap_image_size(int n) const { CDReader cdata(_cycler); if (n >= 0 && n < (int)cdata->_ram_images.size()) { - return cdata->_ram_images[n]._image.size(); + if (cdata->_ram_images[n]._pointer_image == nullptr) { + return cdata->_ram_images[n]._image.size(); + } else { + // Calculate it based on the given page size. + return do_get_ram_mipmap_page_size(cdata, n) * + do_get_expected_mipmap_z_size(cdata, n) * + cdata->_num_views; + } } return 0; } diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index 115d9529bf..bddee6510b 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -1417,9 +1417,10 @@ peek() { * Use this function instead of prepare_now() to preload textures from a user * interface standpoint. */ -void Texture:: +PT(AsyncFuture) Texture:: prepare(PreparedGraphicsObjects *prepared_objects) { - prepared_objects->enqueue_texture(this); + PT(PreparedGraphicsObjects::EnqueuedObject) obj = prepared_objects->enqueue_texture_future(this); + return obj.p(); } /** @@ -3441,8 +3442,8 @@ do_load_sub_image(CData *cdata, const PNMImage &image, int x, int y, int z, int nassertr(y >= 0 && y < tex_y_size, false); nassertr(z >= 0 && z < tex_z_size, false); - nassertr(image.get_x_size() + x < tex_x_size, false); - nassertr(image.get_y_size() + y < tex_y_size, false); + nassertr(image.get_x_size() + x <= tex_x_size, false); + nassertr(image.get_y_size() + y <= tex_y_size, false); // Flip y y = cdata->_y_size - (image.get_y_size() + y); diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index e40ca6f8e5..6b649b9ba5 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -43,9 +43,10 @@ #include "colorSpace.h" #include "geomEnums.h" #include "bamCacheRecord.h" +#include "pnmImage.h" +#include "pfmFile.h" +#include "asyncFuture.h" -class PNMImage; -class PfmFile; class TextureContext; class FactoryParams; class PreparedGraphicsObjects; @@ -522,7 +523,7 @@ PUBLISHED: MAKE_PROPERTY(auto_texture_scale, get_auto_texture_scale, set_auto_texture_scale); - void prepare(PreparedGraphicsObjects *prepared_objects); + PT(AsyncFuture) prepare(PreparedGraphicsObjects *prepared_objects); bool is_prepared(PreparedGraphicsObjects *prepared_objects) const; bool was_image_modified(PreparedGraphicsObjects *prepared_objects) const; size_t get_data_size_bytes(PreparedGraphicsObjects *prepared_objects) const; @@ -539,6 +540,8 @@ PUBLISHED: void set_aux_data(const string &key, TypedReferenceCount *aux_data); void clear_aux_data(const string &key); TypedReferenceCount *get_aux_data(const string &key) const; + MAKE_MAP_PROPERTY(aux_data, get_aux_data, get_aux_data, + set_aux_data, clear_aux_data); INLINE static void set_textures_power_2(AutoTextureScale scale); INLINE static AutoTextureScale get_textures_power_2(); diff --git a/panda/src/gobj/textureCollection_ext.cxx b/panda/src/gobj/textureCollection_ext.cxx index ea0fbf75c3..de104e775c 100644 --- a/panda/src/gobj/textureCollection_ext.cxx +++ b/panda/src/gobj/textureCollection_ext.cxx @@ -78,9 +78,7 @@ __reduce__(PyObject *self) const { // Since a TextureCollection is itself an iterator, we can simply pass it as // the fourth tuple component. - PyObject *result = Py_BuildValue("(O()OO)", this_class, Py_None, self); - Py_DECREF(this_class); - return result; + return Py_BuildValue("(N()OO)", this_class, Py_None, self); } #endif // HAVE_PYTHON diff --git a/panda/src/gobj/textureContext.cxx b/panda/src/gobj/textureContext.cxx index b0130bcf24..32224a7bde 100644 --- a/panda/src/gobj/textureContext.cxx +++ b/panda/src/gobj/textureContext.cxx @@ -15,6 +15,27 @@ TypeHandle TextureContext::_type_handle; +/** + * Returns an implementation-defined handle or pointer that can be used + * to interface directly with the underlying API. + * Returns 0 if the underlying implementation does not support this. + */ +uint64_t TextureContext:: +get_native_id() const { + return 0; +} + +/** + * Similar to get_native_id, but some implementations use a separate + * identifier for the buffer object associated with buffer textures. + * Returns 0 if the underlying implementation does not support this, or + * if this is not a buffer texture. + */ +uint64_t TextureContext:: +get_native_buffer_id() const { + return 0; +} + /** * */ diff --git a/panda/src/gobj/textureContext.h b/panda/src/gobj/textureContext.h index 7f5a514c81..813cd37dd9 100644 --- a/panda/src/gobj/textureContext.h +++ b/panda/src/gobj/textureContext.h @@ -37,6 +37,8 @@ public: PUBLISHED: INLINE Texture *get_texture() const; INLINE int get_view() const; + virtual uint64_t get_native_id() const; + virtual uint64_t get_native_buffer_id() const; INLINE bool was_modified() const; INLINE bool was_properties_modified() const; diff --git a/panda/src/gobj/textureReloadRequest.I b/panda/src/gobj/textureReloadRequest.I index 4cc22d6d9c..d17bdc72b4 100644 --- a/panda/src/gobj/textureReloadRequest.I +++ b/panda/src/gobj/textureReloadRequest.I @@ -22,8 +22,7 @@ TextureReloadRequest(const string &name, AsyncTask(name), _pgo(pgo), _texture(texture), - _allow_compressed(allow_compressed), - _is_ready(false) + _allow_compressed(allow_compressed) { nassertv(_pgo != (PreparedGraphicsObjects *)NULL); nassertv(_texture != (Texture *)NULL); @@ -58,8 +57,10 @@ get_allow_compressed() const { /** * Returns true if this request has completed, false if it is still pending. + * Equivalent to `req.done() and not req.cancelled()`. + * @see done() */ INLINE bool TextureReloadRequest:: is_ready() const { - return _is_ready; + return (FutureState)AtomicAdjust::get(_future_state) == FS_finished; } diff --git a/panda/src/gobj/textureReloadRequest.cxx b/panda/src/gobj/textureReloadRequest.cxx index 0b5964e2ef..4c17b550b7 100644 --- a/panda/src/gobj/textureReloadRequest.cxx +++ b/panda/src/gobj/textureReloadRequest.cxx @@ -43,7 +43,6 @@ do_task() { _texture->prepare(_pgo); } } - _is_ready = true; // Don't continue the task; we're done. return DS_done; diff --git a/panda/src/gobj/textureReloadRequest.h b/panda/src/gobj/textureReloadRequest.h index 92f0fdd1f8..1e97c99ea2 100644 --- a/panda/src/gobj/textureReloadRequest.h +++ b/panda/src/gobj/textureReloadRequest.h @@ -33,15 +33,18 @@ public: ALLOC_DELETED_CHAIN(TextureReloadRequest); PUBLISHED: - INLINE TextureReloadRequest(const string &name, - PreparedGraphicsObjects *pgo, Texture *texture, - bool allow_compressed); + INLINE explicit TextureReloadRequest(const string &name, + PreparedGraphicsObjects *pgo, + Texture *texture, + bool allow_compressed); INLINE PreparedGraphicsObjects *get_prepared_graphics_objects() const; INLINE Texture *get_texture() const; INLINE bool get_allow_compressed() const; INLINE bool is_ready() const; + MAKE_PROPERTY(texture, get_texture); + protected: virtual DoneStatus do_task(); @@ -49,7 +52,6 @@ private: PT(PreparedGraphicsObjects) _pgo; PT(Texture) _texture; bool _allow_compressed; - bool _is_ready; public: static TypeHandle get_class_type() { diff --git a/panda/src/gobj/textureStage.I b/panda/src/gobj/textureStage.I index b00d47d699..17034231a3 100644 --- a/panda/src/gobj/textureStage.I +++ b/panda/src/gobj/textureStage.I @@ -15,7 +15,7 @@ * Initialize the texture stage from other */ INLINE TextureStage:: -TextureStage(TextureStage ©) { +TextureStage(const TextureStage ©) { (*this) = copy; } @@ -52,6 +52,10 @@ set_sort(int sort) { // Update the global flag to indicate that all TextureAttribs in the world // must now re-sort their lists. _sort_seq++; + + if (_used_by_auto_shader) { + GraphicsStateGuardianBase::mark_rehash_generated_shaders(); + } } /** @@ -80,6 +84,10 @@ set_priority(int priority) { // Update the global flag to indicate that all TextureAttribs in the world // must now re-sort their lists. _sort_seq++; + + if (_used_by_auto_shader) { + GraphicsStateGuardianBase::mark_rehash_generated_shaders(); + } } /** @@ -99,7 +107,13 @@ get_priority() const { */ INLINE void TextureStage:: set_texcoord_name(InternalName *name) { - _texcoord_name = name; + if (name != _texcoord_name) { + _texcoord_name = name; + + if (_used_by_auto_shader) { + GraphicsStateGuardianBase::mark_rehash_generated_shaders(); + } + } } /** @@ -108,7 +122,7 @@ set_texcoord_name(InternalName *name) { */ INLINE void TextureStage:: set_texcoord_name(const string &name) { - _texcoord_name = InternalName::get_texcoord_name(name); + set_texcoord_name(InternalName::get_texcoord_name(name)); } /** @@ -150,13 +164,16 @@ get_binormal_name() const { */ INLINE void TextureStage:: set_mode(TextureStage::Mode mode) { - _mode = mode; + if (mode != _mode) { + _mode = mode; - if (_mode != M_combine) { - _num_combine_rgb_operands = 0; - _num_combine_alpha_operands = 0; + if (_mode != M_combine) { + _num_combine_rgb_operands = 0; + _num_combine_alpha_operands = 0; + } + + update_color_flags(); } - update_color_flags(); } /** @@ -202,8 +219,14 @@ get_color() const { */ INLINE void TextureStage:: set_rgb_scale(int rgb_scale) { - nassertv(rgb_scale == 1 || rgb_scale == 2 || rgb_scale == 4); - _rgb_scale = rgb_scale; + if (rgb_scale != _rgb_scale) { + nassertv(rgb_scale == 1 || rgb_scale == 2 || rgb_scale == 4); + _rgb_scale = rgb_scale; + + if (_used_by_auto_shader) { + GraphicsStateGuardianBase::mark_rehash_generated_shaders(); + } + } } /** @@ -222,8 +245,14 @@ get_rgb_scale() const { */ INLINE void TextureStage:: set_alpha_scale(int alpha_scale) { - nassertv(alpha_scale == 1 || alpha_scale == 2 || alpha_scale == 4); - _alpha_scale = alpha_scale; + if (alpha_scale != _alpha_scale) { + nassertv(alpha_scale == 1 || alpha_scale == 2 || alpha_scale == 4); + _alpha_scale = alpha_scale; + + if (_used_by_auto_shader) { + GraphicsStateGuardianBase::mark_rehash_generated_shaders(); + } + } } /** @@ -247,7 +276,13 @@ get_alpha_scale() const { */ INLINE void TextureStage:: set_saved_result(bool saved_result) { - _saved_result = saved_result; + if (saved_result != _saved_result) { + _saved_result = saved_result; + + if (_used_by_auto_shader) { + GraphicsStateGuardianBase::mark_rehash_generated_shaders(); + } + } } /** @@ -641,6 +676,14 @@ get_sort_seq() { return _sort_seq; } +/** + * Marks this TextureStage as having been used by the auto shader. + */ +INLINE void TextureStage:: +mark_used_by_auto_shader() const { + _used_by_auto_shader = true; +} + /** * Updates _uses_color, _involves_color_scale, _uses_primary_color and * _uses_last_saved_result appropriately. @@ -658,8 +701,7 @@ update_color_flags() { _combine_alpha_source2 == CS_constant_color_scale))); _uses_color = - (_involves_color_scale || - _mode == M_blend || + (_mode == M_blend || _mode == M_blend_color_scale || (_mode == M_combine && (_combine_rgb_source0 == CS_constant || _combine_rgb_source1 == CS_constant || @@ -685,6 +727,10 @@ update_color_flags() { _combine_alpha_source0 == CS_last_saved_result || _combine_alpha_source1 == CS_last_saved_result || _combine_alpha_source2 == CS_last_saved_result)); + + if (_used_by_auto_shader) { + GraphicsStateGuardianBase::mark_rehash_generated_shaders(); + } } INLINE ostream & diff --git a/panda/src/gobj/textureStage.cxx b/panda/src/gobj/textureStage.cxx index 708b6d140d..120bb4feb3 100644 --- a/panda/src/gobj/textureStage.cxx +++ b/panda/src/gobj/textureStage.cxx @@ -25,7 +25,7 @@ TypeHandle TextureStage::_type_handle; * Initialize the texture stage at construction */ TextureStage:: -TextureStage(const string &name) { +TextureStage(const string &name) : _used_by_auto_shader(false) { _name = name; _sort = 0; _priority = 0; @@ -90,6 +90,8 @@ operator = (const TextureStage &other) { _uses_color = other._uses_color; _involves_color_scale = other._involves_color_scale; + + _used_by_auto_shader = false; } /** diff --git a/panda/src/gobj/textureStage.h b/panda/src/gobj/textureStage.h index 4ba5835910..2414e72991 100644 --- a/panda/src/gobj/textureStage.h +++ b/panda/src/gobj/textureStage.h @@ -21,6 +21,7 @@ #include "typedWritableReferenceCount.h" #include "updateSeq.h" #include "luse.h" +#include "graphicsStateGuardianBase.h" class FactoryParams; @@ -34,7 +35,7 @@ class FactoryParams; class EXPCL_PANDA_GOBJ TextureStage : public TypedWritableReferenceCount { PUBLISHED: explicit TextureStage(const string &name); - INLINE TextureStage(TextureStage ©); + INLINE TextureStage(const TextureStage ©); void operator = (const TextureStage ©); virtual ~TextureStage(); @@ -201,9 +202,13 @@ PUBLISHED: MAKE_PROPERTY(tex_view_offset, get_tex_view_offset, set_tex_view_offset); + MAKE_PROPERTY(default, get_default); + public: INLINE static UpdateSeq get_sort_seq(); + INLINE void mark_used_by_auto_shader() const; + private: INLINE void update_color_flags(); @@ -247,6 +252,8 @@ private: static PT(TextureStage) _default_stage; static UpdateSeq _sort_seq; + mutable bool _used_by_auto_shader; + public: // Datagram stuff static void register_with_read_factory(); diff --git a/panda/src/gobj/textureStagePool.h b/panda/src/gobj/textureStagePool.h index 5dc9012ea9..4db775251d 100644 --- a/panda/src/gobj/textureStagePool.h +++ b/panda/src/gobj/textureStagePool.h @@ -43,6 +43,7 @@ PUBLISHED: INLINE static void set_mode(Mode mode); INLINE static Mode get_mode(); + MAKE_PROPERTY(mode, get_mode, set_mode); INLINE static int garbage_collect(); INLINE static void list_contents(ostream &out); diff --git a/panda/src/gobj/texture_ext.cxx b/panda/src/gobj/texture_ext.cxx index 1e661e960a..260070f058 100644 --- a/panda/src/gobj/texture_ext.cxx +++ b/panda/src/gobj/texture_ext.cxx @@ -33,15 +33,12 @@ set_ram_image(PyObject *image, Texture::CompressionMode compression, nassertv(compression != Texture::CM_default); // Check if perhaps a PointerToArray object was passed in. - if (DtoolCanThisBeAPandaInstance(image)) { - Dtool_PyInstDef *inst = (Dtool_PyInstDef *)image; - - if (inst->_My_Type == &Dtool_ConstPointerToArray_unsigned_char) { - _this->set_ram_image(*(const CPTA_uchar *)inst->_ptr_to_object, compression, page_size); + if (DtoolInstance_Check(image)) { + if (DtoolInstance_TYPE(image) == &Dtool_ConstPointerToArray_unsigned_char) { + _this->set_ram_image(*(const CPTA_uchar *)DtoolInstance_VOID_PTR(image), compression, page_size); return; - - } else if (inst->_My_Type == &Dtool_PointerToArray_unsigned_char) { - _this->set_ram_image(*(const PTA_uchar *)inst->_ptr_to_object, compression, page_size); + } else if (DtoolInstance_TYPE(image) == &Dtool_PointerToArray_unsigned_char) { + _this->set_ram_image(*(const PTA_uchar *)DtoolInstance_VOID_PTR(image), compression, page_size); return; } } @@ -66,7 +63,7 @@ set_ram_image(PyObject *image, Texture::CompressionMode compression, if (view.len % component_width != 0) { PyErr_Format(PyExc_ValueError, - "byte buffer is not a multiple of %zu bytes", + "byte buffer is not a multiple of %d bytes", component_width); return; } @@ -99,15 +96,12 @@ set_ram_image(PyObject *image, Texture::CompressionMode compression, void Extension:: set_ram_image_as(PyObject *image, const string &provided_format) { // Check if perhaps a PointerToArray object was passed in. - if (DtoolCanThisBeAPandaInstance(image)) { - Dtool_PyInstDef *inst = (Dtool_PyInstDef *)image; - - if (inst->_My_Type == &Dtool_ConstPointerToArray_unsigned_char) { - _this->set_ram_image_as(*(const CPTA_uchar *)inst->_ptr_to_object, provided_format); + if (DtoolInstance_Check(image)) { + if (DtoolInstance_TYPE(image) == &Dtool_ConstPointerToArray_unsigned_char) { + _this->set_ram_image_as(*(const CPTA_uchar *)DtoolInstance_VOID_PTR(image), provided_format); return; - - } else if (inst->_My_Type == &Dtool_PointerToArray_unsigned_char) { - _this->set_ram_image_as(*(const PTA_uchar *)inst->_ptr_to_object, provided_format); + } else if (DtoolInstance_TYPE(image) == &Dtool_PointerToArray_unsigned_char) { + _this->set_ram_image_as(*(const PTA_uchar *)DtoolInstance_VOID_PTR(image), provided_format); return; } } @@ -131,7 +125,7 @@ set_ram_image_as(PyObject *image, const string &provided_format) { if (view.len % component_width != 0) { PyErr_Format(PyExc_ValueError, - "byte buffer is not a multiple of %zu bytes", + "byte buffer is not a multiple of %d bytes", component_width); return; } diff --git a/panda/src/gobj/transformBlend.I b/panda/src/gobj/transformBlend.I index 2d1f3624c2..936ca57cc4 100644 --- a/panda/src/gobj/transformBlend.I +++ b/panda/src/gobj/transformBlend.I @@ -142,6 +142,17 @@ get_weight(size_t n) const { return _entries[n]._weight; } +/** + * Removes the nth transform stored in the blend object. + */ +INLINE void TransformBlend:: +remove_transform(size_t n) { + nassertv(n < _entries.size()); + _entries.erase(_entries.begin() + n); + Thread *current_thread = Thread::get_current_thread(); + clear_result(current_thread); +} + /** * Replaces the nth transform stored in the blend object. */ diff --git a/panda/src/gobj/transformBlend.h b/panda/src/gobj/transformBlend.h index 02f39f05d1..45b5793294 100644 --- a/panda/src/gobj/transformBlend.h +++ b/panda/src/gobj/transformBlend.h @@ -62,9 +62,15 @@ PUBLISHED: INLINE const VertexTransform *get_transform(size_t n) const; MAKE_SEQ(get_transforms, get_num_transforms, get_transform); INLINE PN_stdfloat get_weight(size_t n) const; + INLINE void remove_transform(size_t n); INLINE void set_transform(size_t n, const VertexTransform *transform); INLINE void set_weight(size_t n, PN_stdfloat weight); + MAKE_SEQ_PROPERTY(transforms, get_num_transforms, get_transform, + set_transform, remove_transform); + MAKE_MAP_PROPERTY(weights, has_transform, get_weight); + MAKE_MAP_KEYS_SEQ(weights, get_num_transforms, get_transform); + INLINE void update_blend(Thread *current_thread) const; INLINE void get_blend(LMatrix4 &result, Thread *current_thread) const; diff --git a/panda/src/gobj/transformTable.cxx b/panda/src/gobj/transformTable.cxx index 258299fafc..73d0665079 100644 --- a/panda/src/gobj/transformTable.cxx +++ b/panda/src/gobj/transformTable.cxx @@ -65,6 +65,23 @@ set_transform(size_t n, const VertexTransform *transform) { _transforms[n] = transform; } +/** + * Inserts a new transform to the table at the given index position. If the + * index is beyond the end of the table, appends it to the end. Only valid + * for unregistered tables. + * + * This does not automatically uniquify the pointer; if the transform is + * already present in the table, it will be added twice. + */ +void TransformTable:: +insert_transform(size_t n, const VertexTransform *transform) { + nassertv(!_is_registered); + if (n > _transforms.size()) { + n = _transforms.size(); + } + _transforms.insert(_transforms.begin() + n, transform); +} + /** * Removes the nth transform. Only valid for unregistered tables. */ diff --git a/panda/src/gobj/transformTable.h b/panda/src/gobj/transformTable.h index 0483fced99..b55e4106df 100644 --- a/panda/src/gobj/transformTable.h +++ b/panda/src/gobj/transformTable.h @@ -51,6 +51,7 @@ PUBLISHED: INLINE UpdateSeq get_modified(Thread *current_thread = Thread::get_current_thread()) const; void set_transform(size_t n, const VertexTransform *transform); + void insert_transform(size_t n, const VertexTransform *transform); void remove_transform(size_t n); size_t add_transform(const VertexTransform *transform); @@ -58,7 +59,8 @@ PUBLISHED: MAKE_PROPERTY(registered, is_registered); MAKE_PROPERTY(modified, get_modified); - MAKE_SEQ_PROPERTY(transforms, get_num_transforms, get_transform, set_transform, remove_transform); + MAKE_SEQ_PROPERTY(transforms, get_num_transforms, get_transform, set_transform, + remove_transform, insert_transform); private: void do_register(); diff --git a/panda/src/gobj/userVertexSlider.h b/panda/src/gobj/userVertexSlider.h index b4db1ed6a2..aadeafd893 100644 --- a/panda/src/gobj/userVertexSlider.h +++ b/panda/src/gobj/userVertexSlider.h @@ -30,8 +30,8 @@ class FactoryParams; */ class EXPCL_PANDA_GOBJ UserVertexSlider : public VertexSlider { PUBLISHED: - UserVertexSlider(const string &name); - UserVertexSlider(const InternalName *name); + explicit UserVertexSlider(const string &name); + explicit UserVertexSlider(const InternalName *name); INLINE void set_slider(PN_stdfloat slider); virtual PN_stdfloat get_slider() const; diff --git a/panda/src/gobj/userVertexTransform.h b/panda/src/gobj/userVertexTransform.h index 53b95c05f0..377dff2a17 100644 --- a/panda/src/gobj/userVertexTransform.h +++ b/panda/src/gobj/userVertexTransform.h @@ -30,7 +30,7 @@ class FactoryParams; */ class EXPCL_PANDA_GOBJ UserVertexTransform : public VertexTransform { PUBLISHED: - UserVertexTransform(const string &name); + explicit UserVertexTransform(const string &name); INLINE const string &get_name() const; diff --git a/panda/src/gobj/vertexDataBook.h b/panda/src/gobj/vertexDataBook.h index dfbf649a54..9f77316157 100644 --- a/panda/src/gobj/vertexDataBook.h +++ b/panda/src/gobj/vertexDataBook.h @@ -29,7 +29,7 @@ class VertexDataBlock; */ class EXPCL_PANDA_GOBJ VertexDataBook { PUBLISHED: - VertexDataBook(size_t block_size); + explicit VertexDataBook(size_t block_size); ~VertexDataBook(); INLINE VertexDataBlock *alloc(size_t size); diff --git a/panda/src/gobj/vertexDataBuffer.I b/panda/src/gobj/vertexDataBuffer.I index 3652bae864..3bc1f85b80 100644 --- a/panda/src/gobj/vertexDataBuffer.I +++ b/panda/src/gobj/vertexDataBuffer.I @@ -67,17 +67,22 @@ INLINE const unsigned char *VertexDataBuffer:: get_read_pointer(bool force) const { LightMutexHolder holder(_lock); + const unsigned char *ptr; if (_resident_data != (unsigned char *)NULL || _size == 0) { - return _resident_data; + ptr = _resident_data; + } else { + nassertr(_block != (VertexDataBlock *)NULL, NULL); + nassertr(_reserved_size >= _size, NULL); + + // We don't necessarily need to page the buffer all the way into independent + // status; it's sufficient just to return the block's pointer, which will + // force its page to resident status. + ptr = _block->get_pointer(force); } - - nassertr(_block != (VertexDataBlock *)NULL, NULL); - nassertr(_reserved_size >= _size, NULL); - - // We don't necessarily need to page the buffer all the way into independent - // status; it's sufficient just to return the block's pointer, which will - // force its page to resident status. - return _block->get_pointer(force); +#ifdef _DEBUG + assert(((uintptr_t)ptr % MEMORY_HOOK_ALIGNMENT) == 0); +#endif + return (const unsigned char *)ASSUME_ALIGNED(ptr, MEMORY_HOOK_ALIGNMENT); } /** @@ -91,7 +96,10 @@ get_write_pointer() { do_page_in(); } nassertr(_reserved_size >= _size, NULL); - return _resident_data; +#ifdef _DEBUG + assert(((uintptr_t)_resident_data % MEMORY_HOOK_ALIGNMENT) == 0); +#endif + return (unsigned char *)ASSUME_ALIGNED(_resident_data, MEMORY_HOOK_ALIGNMENT); } /** diff --git a/panda/src/gobj/vertexDataBuffer.cxx b/panda/src/gobj/vertexDataBuffer.cxx index 1e3b884893..19f50b3013 100644 --- a/panda/src/gobj/vertexDataBuffer.cxx +++ b/panda/src/gobj/vertexDataBuffer.cxx @@ -27,15 +27,13 @@ operator = (const VertexDataBuffer ©) { if (_resident_data != (unsigned char *)NULL) { nassertv(_reserved_size != 0); - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_reserved_size); - PANDA_FREE_ARRAY(_resident_data); + get_class_type().deallocate_array(_resident_data); _resident_data = NULL; } if (copy._resident_data != (unsigned char *)NULL && copy._size != 0) { // We only allocate _size bytes, not the full _reserved_size allocated by // the original copy. - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)copy._size); - _resident_data = (unsigned char *)PANDA_MALLOC_ARRAY(copy._size); + _resident_data = (unsigned char *)get_class_type().allocate_array(copy._size); memcpy(_resident_data, copy._resident_data, copy._size); } _size = copy._size; @@ -55,17 +53,16 @@ swap(VertexDataBuffer &other) { unsigned char *resident_data = _resident_data; size_t size = _size; size_t reserved_size = _reserved_size; - PT(VertexDataBlock) block = _block; + + _block.swap(other._block); _resident_data = other._resident_data; _size = other._size; _reserved_size = other._reserved_size; - _block = other._block; other._resident_data = resident_data; other._size = size; other._reserved_size = reserved_size; - other._block = block; nassertv(_reserved_size >= _size); } @@ -94,13 +91,12 @@ do_clean_realloc(size_t reserved_size) { do_page_in(); } - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)reserved_size - (int)_reserved_size); if (_reserved_size == 0) { nassertv(_resident_data == (unsigned char *)NULL); - _resident_data = (unsigned char *)PANDA_MALLOC_ARRAY(reserved_size); + _resident_data = (unsigned char *)get_class_type().allocate_array(reserved_size); } else { nassertv(_resident_data != (unsigned char *)NULL); - _resident_data = (unsigned char *)PANDA_REALLOC_ARRAY(_resident_data, reserved_size); + _resident_data = (unsigned char *)get_class_type().reallocate_array(_resident_data, reserved_size); } nassertv(_resident_data != (unsigned char *)NULL); _reserved_size = reserved_size; @@ -129,16 +125,14 @@ do_unclean_realloc(size_t reserved_size) { if (_resident_data != (unsigned char *)NULL) { nassertv(_reserved_size != 0); - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_reserved_size); - PANDA_FREE_ARRAY(_resident_data); + get_class_type().deallocate_array(_resident_data); _resident_data = NULL; _reserved_size = 0; } if (reserved_size != 0) { - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)reserved_size); nassertv(_resident_data == (unsigned char *)NULL); - _resident_data = (unsigned char *)PANDA_MALLOC_ARRAY(reserved_size); + _resident_data = (unsigned char *)get_class_type().allocate_array(reserved_size); } _reserved_size = reserved_size; @@ -166,8 +160,7 @@ do_page_out(VertexDataBook &book) { if (_size == 0) { // It's an empty buffer. Just deallocate it; don't bother to create a // block. - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_reserved_size); - PANDA_FREE_ARRAY(_resident_data); + get_class_type().deallocate_array(_resident_data); _resident_data = NULL; _reserved_size = 0; @@ -180,8 +173,7 @@ do_page_out(VertexDataBook &book) { nassertv(pointer != (unsigned char *)NULL); memcpy(pointer, _resident_data, _size); - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_reserved_size); - PANDA_FREE_ARRAY(_resident_data); + get_class_type().deallocate_array(_resident_data); _resident_data = NULL; _reserved_size = _size; @@ -205,8 +197,7 @@ do_page_in() { nassertv(_block != (VertexDataBlock *)NULL); nassertv(_reserved_size == _size); - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size); - _resident_data = (unsigned char *)PANDA_MALLOC_ARRAY(_size); + _resident_data = (unsigned char *)get_class_type().allocate_array(_size); nassertv(_resident_data != (unsigned char *)NULL); memcpy(_resident_data, _block->get_pointer(true), _size); diff --git a/panda/src/gobj/vertexDataBuffer.h b/panda/src/gobj/vertexDataBuffer.h index 2a18aeda45..f698669f52 100644 --- a/panda/src/gobj/vertexDataBuffer.h +++ b/panda/src/gobj/vertexDataBuffer.h @@ -57,8 +57,8 @@ public: void operator = (const VertexDataBuffer ©); INLINE ~VertexDataBuffer(); - INLINE const unsigned char *get_read_pointer(bool force) const; - INLINE unsigned char *get_write_pointer(); + INLINE const unsigned char *get_read_pointer(bool force) const RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT); + INLINE unsigned char *get_write_pointer() RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT); INLINE size_t get_size() const; INLINE size_t get_reserved_size() const; diff --git a/panda/src/gobj/vertexDataPage.h b/panda/src/gobj/vertexDataPage.h index 5149663e6b..4c7fd02bf2 100644 --- a/panda/src/gobj/vertexDataPage.h +++ b/panda/src/gobj/vertexDataPage.h @@ -64,6 +64,7 @@ PUBLISHED: INLINE static SimpleLru *get_global_lru(RamClass rclass); INLINE static SimpleLru *get_pending_lru(); INLINE static VertexDataSaveFile *get_save_file(); + MAKE_PROPERTY(save_file, get_save_file); INLINE bool save_to_disk(); diff --git a/panda/src/gobj/vertexSlider.h b/panda/src/gobj/vertexSlider.h index 1c4274b437..7c3c135292 100644 --- a/panda/src/gobj/vertexSlider.h +++ b/panda/src/gobj/vertexSlider.h @@ -36,7 +36,7 @@ class SliderTable; */ class EXPCL_PANDA_GOBJ VertexSlider : public TypedWritableReferenceCount { PUBLISHED: - VertexSlider(const InternalName *name); + explicit VertexSlider(const InternalName *name); virtual ~VertexSlider(); INLINE const InternalName *get_name() const; diff --git a/panda/src/grutil/cardMaker.h b/panda/src/grutil/cardMaker.h index c9d28b8f09..ff8e6e2eb6 100644 --- a/panda/src/grutil/cardMaker.h +++ b/panda/src/grutil/cardMaker.h @@ -28,7 +28,7 @@ */ class EXPCL_PANDA_GRUTIL CardMaker : public Namable { PUBLISHED: - INLINE CardMaker(const string &name); + INLINE explicit CardMaker(const string &name); INLINE ~CardMaker(); void reset(); diff --git a/panda/src/grutil/fisheyeMaker.h b/panda/src/grutil/fisheyeMaker.h index f3ce45fe96..d6bb5b7a73 100644 --- a/panda/src/grutil/fisheyeMaker.h +++ b/panda/src/grutil/fisheyeMaker.h @@ -33,7 +33,7 @@ class GeomVertexWriter; */ class EXPCL_PANDA_GRUTIL FisheyeMaker : public Namable { PUBLISHED: - INLINE FisheyeMaker(const string &name); + INLINE explicit FisheyeMaker(const string &name); INLINE ~FisheyeMaker(); void reset(); diff --git a/panda/src/grutil/frameRateMeter.h b/panda/src/grutil/frameRateMeter.h index 1f0c0f29a8..2e3f657819 100644 --- a/panda/src/grutil/frameRateMeter.h +++ b/panda/src/grutil/frameRateMeter.h @@ -36,7 +36,7 @@ class ClockObject; */ class EXPCL_PANDA_GRUTIL FrameRateMeter : public TextNode { PUBLISHED: - FrameRateMeter(const string &name); + explicit FrameRateMeter(const string &name); virtual ~FrameRateMeter(); void setup_window(GraphicsOutput *window); diff --git a/panda/src/grutil/geoMipTerrain.h b/panda/src/grutil/geoMipTerrain.h index 87115496d0..88727e9077 100644 --- a/panda/src/grutil/geoMipTerrain.h +++ b/panda/src/grutil/geoMipTerrain.h @@ -35,7 +35,7 @@ */ class EXPCL_PANDA_GRUTIL GeoMipTerrain : public TypedObject { PUBLISHED: - INLINE GeoMipTerrain(const string &name); + INLINE explicit GeoMipTerrain(const string &name); INLINE ~GeoMipTerrain(); INLINE PNMImage &heightfield(); diff --git a/panda/src/grutil/heightfieldTesselator.h b/panda/src/grutil/heightfieldTesselator.h index f206b3e201..247d1de218 100644 --- a/panda/src/grutil/heightfieldTesselator.h +++ b/panda/src/grutil/heightfieldTesselator.h @@ -57,7 +57,7 @@ class EXPCL_PANDA_GRUTIL HeightfieldTesselator : public Namable { PUBLISHED: - INLINE HeightfieldTesselator(const string &name); + INLINE explicit HeightfieldTesselator(const string &name); INLINE ~HeightfieldTesselator(); INLINE PNMImage &heightfield(); diff --git a/panda/src/grutil/lineSegs.h b/panda/src/grutil/lineSegs.h index c740b524ae..174467da31 100644 --- a/panda/src/grutil/lineSegs.h +++ b/panda/src/grutil/lineSegs.h @@ -32,7 +32,7 @@ */ class EXPCL_PANDA_GRUTIL LineSegs : public Namable { PUBLISHED: - LineSegs(const string &name = "lines"); + explicit LineSegs(const string &name = "lines"); ~LineSegs(); void reset(); diff --git a/panda/src/grutil/meshDrawer.cxx b/panda/src/grutil/meshDrawer.cxx index 02b63e938a..57ac0d9590 100644 --- a/panda/src/grutil/meshDrawer.cxx +++ b/panda/src/grutil/meshDrawer.cxx @@ -33,7 +33,7 @@ TypeHandle MeshDrawer::_type_handle; PN_stdfloat randFloat() { - return ((PN_stdfloat) rand() / (PN_stdfloat) 0x7fffffff); + return ((PN_stdfloat)rand() / (PN_stdfloat)RAND_MAX); } /** @@ -42,39 +42,43 @@ PN_stdfloat randFloat() { void MeshDrawer::generator(int budget) { // create enough triangles for budget: _vdata = new GeomVertexData(_root.get_name(), GeomVertexFormat::get_v3n3c4t2(), Geom::UH_static);//UH_dynamic); - GeomVertexWriter *tvertex = new GeomVertexWriter(_vdata, "vertex"); - GeomVertexWriter *tnormal = new GeomVertexWriter(_vdata, "normal"); - GeomVertexWriter *tuv = new GeomVertexWriter(_vdata, "texcoord"); - GeomVertexWriter *tcolor = new GeomVertexWriter(_vdata, "color"); - _prim = new GeomTriangles(Geom::UH_static); + _vdata->unclean_set_num_rows(budget * 3); - // iterate and fill _up a geom with random data so that it will not be - // optimized out by panda3d system - for(int i = 0; i < budget; i++) { - for( int vert = 0; vert < 3; vert++) { - LVector3 vec3 = LVector3(randFloat()+1000,randFloat(),randFloat())*.001; - LVector4 vec4 = LVector4(1,1,1,randFloat()); - LVector2 vec2 = LVector2(0,randFloat()); - tvertex->add_data3(vec3); - tcolor->add_data4(vec4); - tuv->add_data2(vec2); - tnormal->add_data3(vec3); + { + GeomVertexWriter tvertex(_vdata, "vertex"); + GeomVertexWriter tnormal(_vdata, "normal"); + GeomVertexWriter tuv(_vdata, "texcoord"); + GeomVertexWriter tcolor(_vdata, "color"); + + // iterate and fill _up a geom with random data so that it will not be + // optimized out by panda3d system + for (int i = 0; i < budget; i++) { + for (int vert = 0; vert < 3; vert++) { + LVector3 vec3 = LVector3(randFloat()+1000,randFloat(),randFloat())*.001; + LVector4 vec4 = LVector4(1,1,1,randFloat()); + LVector2 vec2 = LVector2(0,randFloat()); + tvertex.set_data3(vec3); + tcolor.set_data4(vec4); + tuv.set_data2(vec2); + tnormal.set_data3(vec3); + } } - _prim->add_vertices(i * 3, i * 3 + 1, i * 3 + 2); } + // create our node and attach it to this node path + _prim = new GeomTriangles(Geom::UH_static); + _prim->add_next_vertices(budget * 3); _prim->close_primitive(); _geom = new Geom(_vdata); _geom->add_primitive(_prim); - _geomnode = new GeomNode("__MeshDrawer_GeomNode"); + if (_geomnode == NULL) { + _geomnode = new GeomNode("__MeshDrawer_GeomNode"); + _root.attach_new_node(_geomnode); + } else { + _geomnode->remove_all_geoms(); + } _geomnode->add_geom(_geom); - _root.attach_new_node(_geomnode); _last_clear_index = budget; - - delete tvertex; - delete tnormal; - delete tuv; - delete tcolor; } /** @@ -451,8 +455,9 @@ link_segment(const LVector3 &pos, const LVector4 &frame, LVector3 cam_stop3d = _camera.get_relative_point(_render, stop); LPoint2 cam_stop2d = LVector2(); - PT(Camera) camera = DCAST(Camera, _camera.node()); - PT(Lens) lens = camera->get_lens(); + const Camera *camera; + DCAST_INTO_V(camera, _camera.node()); + const Lens *lens = camera->get_lens(); lens->project(cam_start3d, cam_start2d); lens->project(cam_stop3d, cam_stop2d); diff --git a/panda/src/grutil/movieTexture.h b/panda/src/grutil/movieTexture.h index a02b6bf3d3..d719f21d87 100644 --- a/panda/src/grutil/movieTexture.h +++ b/panda/src/grutil/movieTexture.h @@ -32,8 +32,8 @@ */ class EXPCL_PANDA_GRUTIL MovieTexture : public Texture { PUBLISHED: - MovieTexture(const string &name); - MovieTexture(MovieVideo *video); + explicit MovieTexture(const string &name); + explicit MovieTexture(MovieVideo *video); private: MovieTexture(const MovieTexture ©); PUBLISHED: diff --git a/panda/src/grutil/pfmVizzer.I b/panda/src/grutil/pfmVizzer.I index 6a0a5a23bc..2185248829 100644 --- a/panda/src/grutil/pfmVizzer.I +++ b/panda/src/grutil/pfmVizzer.I @@ -19,6 +19,10 @@ get_pfm() { return _pfm; } +INLINE PfmVizzer:: +~PfmVizzer() { +} + /** * Returns the reference to the PfmFile manipulated by this PfmVizzer. */ diff --git a/panda/src/grutil/pfmVizzer.h b/panda/src/grutil/pfmVizzer.h index 7b466f0ee3..e04e8a06bc 100644 --- a/panda/src/grutil/pfmVizzer.h +++ b/panda/src/grutil/pfmVizzer.h @@ -29,7 +29,8 @@ class GeomVertexWriter; */ class EXPCL_PANDA_GRUTIL PfmVizzer { PUBLISHED: - PfmVizzer(PfmFile &pfm); + explicit PfmVizzer(PfmFile &pfm); + INLINE ~PfmVizzer(); INLINE PfmFile &get_pfm(); INLINE const PfmFile &get_pfm() const; diff --git a/panda/src/grutil/pipeOcclusionCullTraverser.cxx b/panda/src/grutil/pipeOcclusionCullTraverser.cxx index 91ebeb6559..1052d5ace5 100644 --- a/panda/src/grutil/pipeOcclusionCullTraverser.cxx +++ b/panda/src/grutil/pipeOcclusionCullTraverser.cxx @@ -464,42 +464,33 @@ void PipeOcclusionCullTraverser:: make_box() { PT(GeomVertexData) vdata = new GeomVertexData ("occlusion_box", GeomVertexFormat::get_v3(), Geom::UH_static); - GeomVertexWriter vertex(vdata, InternalName::get_vertex()); + vdata->unclean_set_num_rows(8); - vertex.add_data3(0.0f, 0.0f, 0.0f); - vertex.add_data3(0.0f, 0.0f, 1.0f); - vertex.add_data3(0.0f, 1.0f, 0.0f); - vertex.add_data3(0.0f, 1.0f, 1.0f); - vertex.add_data3(1.0f, 0.0f, 0.0f); - vertex.add_data3(1.0f, 0.0f, 1.0f); - vertex.add_data3(1.0f, 1.0f, 0.0f); - vertex.add_data3(1.0f, 1.0f, 1.0f); + { + GeomVertexWriter vertex(vdata, InternalName::get_vertex()); + vertex.set_data3(0.0f, 0.0f, 0.0f); + vertex.set_data3(0.0f, 0.0f, 1.0f); + vertex.set_data3(0.0f, 1.0f, 0.0f); + vertex.set_data3(0.0f, 1.0f, 1.0f); + vertex.set_data3(1.0f, 0.0f, 0.0f); + vertex.set_data3(1.0f, 0.0f, 1.0f); + vertex.set_data3(1.0f, 1.0f, 0.0f); + vertex.set_data3(1.0f, 1.0f, 1.0f); + } PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static); tris->add_vertices(0, 4, 5); - tris->close_primitive(); tris->add_vertices(0, 5, 1); - tris->close_primitive(); tris->add_vertices(4, 6, 7); - tris->close_primitive(); tris->add_vertices(4, 7, 5); - tris->close_primitive(); tris->add_vertices(6, 2, 3); - tris->close_primitive(); tris->add_vertices(6, 3, 7); - tris->close_primitive(); tris->add_vertices(2, 0, 1); - tris->close_primitive(); tris->add_vertices(2, 1, 3); - tris->close_primitive(); tris->add_vertices(1, 5, 7); - tris->close_primitive(); tris->add_vertices(1, 7, 3); - tris->close_primitive(); tris->add_vertices(2, 6, 4); - tris->close_primitive(); tris->add_vertices(2, 4, 0); - tris->close_primitive(); _box_geom = new Geom(vdata); _box_geom->add_primitive(tris); diff --git a/panda/src/grutil/pipeOcclusionCullTraverser.h b/panda/src/grutil/pipeOcclusionCullTraverser.h index 7283f5501f..55d2a795ce 100644 --- a/panda/src/grutil/pipeOcclusionCullTraverser.h +++ b/panda/src/grutil/pipeOcclusionCullTraverser.h @@ -41,7 +41,7 @@ class GraphicsStateGuardian; class EXPCL_PANDA_GRUTIL PipeOcclusionCullTraverser : public CullTraverser, public CullHandler { PUBLISHED: - PipeOcclusionCullTraverser(GraphicsOutput *host); + explicit PipeOcclusionCullTraverser(GraphicsOutput *host); PipeOcclusionCullTraverser(const PipeOcclusionCullTraverser ©); virtual void set_scene(SceneSetup *scene_setup, diff --git a/panda/src/grutil/rigidBodyCombiner.h b/panda/src/grutil/rigidBodyCombiner.h index 1f92cda3cb..fca66e0031 100644 --- a/panda/src/grutil/rigidBodyCombiner.h +++ b/panda/src/grutil/rigidBodyCombiner.h @@ -43,7 +43,7 @@ class NodePath; */ class EXPCL_PANDA_GRUTIL RigidBodyCombiner : public PandaNode { PUBLISHED: - RigidBodyCombiner(const string &name); + explicit RigidBodyCombiner(const string &name); protected: RigidBodyCombiner(const RigidBodyCombiner ©); virtual PandaNode *make_copy() const; diff --git a/panda/src/grutil/sceneGraphAnalyzerMeter.h b/panda/src/grutil/sceneGraphAnalyzerMeter.h index c6046255e3..a788ef2f2b 100644 --- a/panda/src/grutil/sceneGraphAnalyzerMeter.h +++ b/panda/src/grutil/sceneGraphAnalyzerMeter.h @@ -38,7 +38,7 @@ class ClockObject; */ class EXPCL_PANDA SceneGraphAnalyzerMeter : public TextNode { PUBLISHED: - SceneGraphAnalyzerMeter(const string &name, PandaNode *node); + explicit SceneGraphAnalyzerMeter(const string &name, PandaNode *node); virtual ~SceneGraphAnalyzerMeter(); void setup_window(GraphicsOutput *window); diff --git a/panda/src/grutil/shaderTerrainMesh.cxx b/panda/src/grutil/shaderTerrainMesh.cxx index 8c2d03cdd4..a7f6b1d8af 100644 --- a/panda/src/grutil/shaderTerrainMesh.cxx +++ b/panda/src/grutil/shaderTerrainMesh.cxx @@ -513,22 +513,22 @@ void ShaderTerrainMesh::add_for_draw(CullTraverser *trav, CullTraverserData &dat nassertv(current_shader_attrib != NULL); current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input( - new ShaderInput("ShaderTerrainMesh.terrain_size", LVecBase2i(_size)) ); + ShaderInput("ShaderTerrainMesh.terrain_size", LVecBase2i(_size))); current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input( - new ShaderInput("ShaderTerrainMesh.chunk_size", LVecBase2i(_chunk_size))); + ShaderInput("ShaderTerrainMesh.chunk_size", LVecBase2i(_chunk_size))); current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input( - new ShaderInput("ShaderTerrainMesh.view_index", LVecBase2i(_current_view_index))); + ShaderInput("ShaderTerrainMesh.view_index", LVecBase2i(_current_view_index))); current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input( - new ShaderInput("ShaderTerrainMesh.data_texture", _data_texture)); + ShaderInput("ShaderTerrainMesh.data_texture", _data_texture)); current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input( - new ShaderInput("ShaderTerrainMesh.heightfield", _heightfield_tex)); + ShaderInput("ShaderTerrainMesh.heightfield", _heightfield_tex)); current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_instance_count( traversal_data.emitted_chunks); state = state->set_attrib(current_shader_attrib, 10000); // Emit chunk - CullableObject *object = new CullableObject(_chunk_geom, state, modelview_transform); + CullableObject *object = new CullableObject(_chunk_geom, move(state), move(modelview_transform)); trav->get_cull_handler()->record_object(object, trav); // After rendering, increment the view index diff --git a/panda/src/gsgbase/config_gsgbase.cxx b/panda/src/gsgbase/config_gsgbase.cxx index b4d6dc90c1..2e5ce60aa9 100644 --- a/panda/src/gsgbase/config_gsgbase.cxx +++ b/panda/src/gsgbase/config_gsgbase.cxx @@ -12,7 +12,6 @@ */ #include "config_gsgbase.h" -#include "displayRegionBase.h" #include "graphicsOutputBase.h" #include "graphicsStateGuardianBase.h" @@ -21,7 +20,6 @@ Configure(config_gsgbase); ConfigureFn(config_gsgbase) { - DisplayRegionBase::init_type(); GraphicsOutputBase::init_type(); GraphicsStateGuardianBase::init_type(); } diff --git a/panda/src/gsgbase/displayRegionBase.I b/panda/src/gsgbase/displayRegionBase.I deleted file mode 100644 index a945da7eb3..0000000000 --- a/panda/src/gsgbase/displayRegionBase.I +++ /dev/null @@ -1,25 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file displayRegionBase.I - * @author drose - * @date 2009-02-20 - */ - -/** - * - */ -INLINE DisplayRegionBase:: -DisplayRegionBase() { -} - -INLINE ostream & -operator << (ostream &out, const DisplayRegionBase &dr) { - dr.output(out); - return out; -} diff --git a/panda/src/gsgbase/displayRegionBase.h b/panda/src/gsgbase/displayRegionBase.h deleted file mode 100644 index 71302281f8..0000000000 --- a/panda/src/gsgbase/displayRegionBase.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file displayRegionBase.h - * @author drose - * @date 2009-02-20 - */ - -#ifndef DISPLAYREGIONBASE_H -#define DISPLAYREGIONBASE_H - -#include "pandabase.h" - -#include "typedReferenceCount.h" - -/** - * An abstract base class for DisplayRegion, mainly so we can store - * DisplayRegion pointers in a Camera. - */ -class EXPCL_PANDA_GSGBASE DisplayRegionBase : public TypedReferenceCount { -protected: - INLINE DisplayRegionBase(); - -public: - virtual ~DisplayRegionBase(); - -PUBLISHED: - virtual void output(ostream &out) const=0; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - TypedReferenceCount::init_type(); - register_type(_type_handle, "DisplayRegionBase", - TypedReferenceCount::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; -}; - -INLINE ostream &operator << (ostream &out, const DisplayRegionBase &dr); - -#include "displayRegionBase.I" - -#endif diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.cxx b/panda/src/gsgbase/graphicsStateGuardianBase.cxx index aa23375418..fefd8037f2 100644 --- a/panda/src/gsgbase/graphicsStateGuardianBase.cxx +++ b/panda/src/gsgbase/graphicsStateGuardianBase.cxx @@ -16,6 +16,7 @@ #include AtomicAdjust::Pointer GraphicsStateGuardianBase::_gsg_list; +UpdateSeq GraphicsStateGuardianBase::_generated_shader_seq; TypeHandle GraphicsStateGuardianBase::_type_handle; /** diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.h b/panda/src/gsgbase/graphicsStateGuardianBase.h index c45f36aca0..45bf35b958 100644 --- a/panda/src/gsgbase/graphicsStateGuardianBase.h +++ b/panda/src/gsgbase/graphicsStateGuardianBase.h @@ -198,7 +198,6 @@ public: // friends of this class. virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, - const GeomMunger *munger, const GeomVertexDataPipelineReader *data_reader, bool force)=0; virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, bool force)=0; @@ -224,6 +223,14 @@ public: virtual void bind_light(Spotlight *light_obj, const NodePath &light, int light_id) { } + virtual void ensure_generated_shader(const RenderState *state)=0; + + static void mark_rehash_generated_shaders() { +#ifdef HAVE_CG + ++_generated_shader_seq; +#endif + } + PUBLISHED: static GraphicsStateGuardianBase *get_default_gsg(); static void set_default_gsg(GraphicsStateGuardianBase *default_gsg); @@ -236,6 +243,8 @@ public: static void add_gsg(GraphicsStateGuardianBase *gsg); static void remove_gsg(GraphicsStateGuardianBase *gsg); + size_t _id; + private: struct GSGList { LightMutex _lock; @@ -246,6 +255,9 @@ private: }; static AtomicAdjust::Pointer _gsg_list; +protected: + static UpdateSeq _generated_shader_seq; + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/gsgbase/p3gsgbase_composite1.cxx b/panda/src/gsgbase/p3gsgbase_composite1.cxx index 5a6cec2b95..b7bcd1368e 100644 --- a/panda/src/gsgbase/p3gsgbase_composite1.cxx +++ b/panda/src/gsgbase/p3gsgbase_composite1.cxx @@ -1,6 +1,5 @@ #include "config_gsgbase.cxx" -#include "displayRegionBase.cxx" #include "graphicsOutputBase.cxx" #include "graphicsStateGuardianBase.cxx" diff --git a/panda/src/linmath/lsimpleMatrix.h b/panda/src/linmath/lsimpleMatrix.h index 0ba2dc1196..3228caf5bb 100644 --- a/panda/src/linmath/lsimpleMatrix.h +++ b/panda/src/linmath/lsimpleMatrix.h @@ -58,7 +58,9 @@ private: #endif // HAVE_EIGEN // This is as good a place as any to define this alignment macro. -#ifdef LINMATH_ALIGN +#if defined(LINMATH_ALIGN) && defined(HAVE_EIGEN) && defined(__AVX__) && defined(STDFLOAT_DOUBLE) +#define ALIGN_LINMATH ALIGN_32BYTE +#elif defined(LINMATH_ALIGN) #define ALIGN_LINMATH ALIGN_16BYTE #else #define ALIGN_LINMATH diff --git a/panda/src/linmath/lvecBase2_src.I b/panda/src/linmath/lvecBase2_src.I index 9ddce8966c..401e2e346a 100644 --- a/panda/src/linmath/lvecBase2_src.I +++ b/panda/src/linmath/lvecBase2_src.I @@ -72,14 +72,6 @@ operator [](int i) { return _v(i); } -/** - * Returns 2: the number of components of a LVecBase2. - */ -CONSTEXPR int FLOATNAME(LVecBase2):: -size() { - return 2; -} - /** * Returns true if any component of the vector is not-a-number, false * otherwise. @@ -178,14 +170,6 @@ get_data() const { return &_v(0); } -/** - * Returns the number of elements in the vector, two. - */ -CONSTEXPR int FLOATNAME(LVecBase2):: -get_num_components() { - return 2; -} - /** * Returns an iterator that may be used to traverse the elements of the * matrix, STL-style. diff --git a/panda/src/linmath/lvecBase2_src.h b/panda/src/linmath/lvecBase2_src.h index 0167855c65..d0c0693f55 100644 --- a/panda/src/linmath/lvecBase2_src.h +++ b/panda/src/linmath/lvecBase2_src.h @@ -50,7 +50,7 @@ PUBLISHED: INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); - CONSTEXPR static int size(); + CONSTEXPR static int size() { return 2; } INLINE_LINMATH bool is_nan() const; @@ -74,7 +74,7 @@ PUBLISHED: INLINE_LINMATH void add_y(FLOATTYPE value); INLINE_LINMATH const FLOATTYPE *get_data() const; - CONSTEXPR static int get_num_components(); + CONSTEXPR static int get_num_components() { return 2; } public: INLINE_LINMATH iterator begin(); diff --git a/panda/src/linmath/lvecBase3_src.I b/panda/src/linmath/lvecBase3_src.I index b7011da0ca..eeaea584d5 100644 --- a/panda/src/linmath/lvecBase3_src.I +++ b/panda/src/linmath/lvecBase3_src.I @@ -89,14 +89,6 @@ operator [](int i) { return _v(i); } -/** - * Returns 3: the number of components of a LVecBase3. - */ -CONSTEXPR int FLOATNAME(LVecBase3):: -size() { - return 3; -} - /** * Returns true if any component of the vector is not-a-number, false * otherwise. @@ -246,14 +238,6 @@ get_data() const { return &_v(0); } -/** - * Returns the number of elements in the vector, three. - */ -CONSTEXPR int FLOATNAME(LVecBase3):: -get_num_components() { - return 3; -} - /** * Returns an iterator that may be used to traverse the elements of the * matrix, STL-style. diff --git a/panda/src/linmath/lvecBase3_src.h b/panda/src/linmath/lvecBase3_src.h index fdc39c7819..fb9b67ca27 100644 --- a/panda/src/linmath/lvecBase3_src.h +++ b/panda/src/linmath/lvecBase3_src.h @@ -52,7 +52,7 @@ PUBLISHED: INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); - CONSTEXPR static int size(); + CONSTEXPR static int size() { return 3; } INLINE_LINMATH bool is_nan() const; @@ -87,7 +87,7 @@ PUBLISHED: INLINE_LINMATH void add_z(FLOATTYPE value); INLINE_LINMATH const FLOATTYPE *get_data() const; - CONSTEXPR static int get_num_components(); + CONSTEXPR static int get_num_components() { return 3; } public: INLINE_LINMATH iterator begin(); diff --git a/panda/src/linmath/lvecBase4_src.I b/panda/src/linmath/lvecBase4_src.I index 7890a6f8f0..464cd6fa12 100644 --- a/panda/src/linmath/lvecBase4_src.I +++ b/panda/src/linmath/lvecBase4_src.I @@ -118,14 +118,6 @@ operator [](int i) { return _v(i); } -/** - * Returns 4: the number of components of a LVecBase4. - */ -CONSTEXPR int FLOATNAME(LVecBase4):: -size() { - return 4; -} - /** * Returns true if any component of the vector is not-a-number, false * otherwise. @@ -288,14 +280,6 @@ get_data() const { return &_v(0); } -/** - * Returns the number of elements in the vector, four. - */ -CONSTEXPR int FLOATNAME(LVecBase4):: -get_num_components() { - return 4; -} - /** * Returns an iterator that may be used to traverse the elements of the * matrix, STL-style. @@ -919,6 +903,14 @@ FLOATNAME(UnalignedLVecBase4)(const FLOATNAME(LVecBase4) ©) { set(copy[0], copy[1], copy[2], copy[3]); } +/** + * + */ +INLINE_LINMATH FLOATNAME(UnalignedLVecBase4):: +FLOATNAME(UnalignedLVecBase4)(FLOATTYPE fill_value) { + fill(fill_value); +} + /** * */ @@ -928,6 +920,19 @@ FLOATNAME(UnalignedLVecBase4)(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z, FLOATTYPE w set(x, y, z, w); } +/** + * Sets each element of the vector to the indicated fill_value. This is + * particularly useful for initializing to zero. + */ +INLINE_LINMATH void FLOATNAME(UnalignedLVecBase4):: +fill(FLOATTYPE fill_value) { + TAU_PROFILE("void UnalignedLVecBase4::fill()", " ", TAU_USER); + _v(0) = fill_value; + _v(1) = fill_value; + _v(2) = fill_value; + _v(3) = fill_value; +} + /** * */ @@ -958,14 +963,6 @@ operator [](int i) { return _v(i); } -/** - * Returns 4: the number of components of a LVecBase4. - */ -CONSTEXPR int FLOATNAME(UnalignedLVecBase4):: -size() { - return 4; -} - /** * Returns the address of the first of the three data elements in the vector. * The remaining elements occupy the next positions consecutively in memory. @@ -974,11 +971,3 @@ INLINE_LINMATH const FLOATTYPE *FLOATNAME(UnalignedLVecBase4):: get_data() const { return &_v(0); } - -/** - * Returns the number of elements in the vector, 4. - */ -CONSTEXPR int FLOATNAME(UnalignedLVecBase4):: -get_num_components() { - return 4; -} diff --git a/panda/src/linmath/lvecBase4_src.h b/panda/src/linmath/lvecBase4_src.h index 1d4a16e961..fdd81dad4b 100644 --- a/panda/src/linmath/lvecBase4_src.h +++ b/panda/src/linmath/lvecBase4_src.h @@ -62,7 +62,7 @@ PUBLISHED: INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); - CONSTEXPR static int size(); + CONSTEXPR static int size() { return 4; } INLINE_LINMATH bool is_nan() const; @@ -100,7 +100,7 @@ PUBLISHED: INLINE_LINMATH void add_w(FLOATTYPE value); INLINE_LINMATH const FLOATTYPE *get_data() const; - CONSTEXPR static int get_num_components(); + CONSTEXPR static int get_num_components() { return 4; } INLINE_LINMATH void extract_data(float*){}; public: @@ -230,16 +230,18 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(UnalignedLVecBase4)() DEFAULT_CTOR; INLINE_LINMATH FLOATNAME(UnalignedLVecBase4)(const FLOATNAME(LVecBase4) ©); + INLINE_LINMATH FLOATNAME(UnalignedLVecBase4)(FLOATTYPE fill_value); INLINE_LINMATH FLOATNAME(UnalignedLVecBase4)(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z, FLOATTYPE w); + INLINE_LINMATH void fill(FLOATTYPE fill_value); INLINE_LINMATH void set(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z, FLOATTYPE w); INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); - CONSTEXPR static int size(); + CONSTEXPR static int size() { return 4; } INLINE_LINMATH const FLOATTYPE *get_data() const; - CONSTEXPR static int get_num_components(); + CONSTEXPR static int get_num_components() { return 4; } public: typedef FLOATTYPE numeric_type; diff --git a/panda/src/mathutil/boundingBox.h b/panda/src/mathutil/boundingBox.h index 53c24c521d..2ba4b273e1 100644 --- a/panda/src/mathutil/boundingBox.h +++ b/panda/src/mathutil/boundingBox.h @@ -29,7 +29,7 @@ class EXPCL_PANDA_MATHUTIL BoundingBox : public FiniteBoundingVolume { PUBLISHED: INLINE_MATHUTIL BoundingBox(); - INLINE_MATHUTIL BoundingBox(const LPoint3 &min, const LPoint3 &max); + INLINE_MATHUTIL explicit BoundingBox(const LPoint3 &min, const LPoint3 &max); ALLOC_DELETED_CHAIN(BoundingBox); public: diff --git a/panda/src/mathutil/boundingLine.h b/panda/src/mathutil/boundingLine.h index c3a089cfc2..15e5f43417 100644 --- a/panda/src/mathutil/boundingLine.h +++ b/panda/src/mathutil/boundingLine.h @@ -31,7 +31,7 @@ public: INLINE_MATHUTIL BoundingLine(); PUBLISHED: - INLINE_MATHUTIL BoundingLine(const LPoint3 &a, const LPoint3 &b); + INLINE_MATHUTIL explicit BoundingLine(const LPoint3 &a, const LPoint3 &b); ALLOC_DELETED_CHAIN(BoundingLine); public: diff --git a/panda/src/mathutil/boundingSphere.h b/panda/src/mathutil/boundingSphere.h index 9a51a015ac..09027e3b8c 100644 --- a/panda/src/mathutil/boundingSphere.h +++ b/panda/src/mathutil/boundingSphere.h @@ -25,7 +25,7 @@ class EXPCL_PANDA_MATHUTIL BoundingSphere : public FiniteBoundingVolume { PUBLISHED: INLINE_MATHUTIL BoundingSphere(); - INLINE_MATHUTIL BoundingSphere(const LPoint3 ¢er, PN_stdfloat radius); + INLINE_MATHUTIL explicit BoundingSphere(const LPoint3 ¢er, PN_stdfloat radius); ALLOC_DELETED_CHAIN(BoundingSphere); public: diff --git a/panda/src/mathutil/geometricBoundingVolume.I b/panda/src/mathutil/geometricBoundingVolume.I index 21a32c4b48..beef98f9ff 100644 --- a/panda/src/mathutil/geometricBoundingVolume.I +++ b/panda/src/mathutil/geometricBoundingVolume.I @@ -16,6 +16,9 @@ */ INLINE_MATHUTIL GeometricBoundingVolume:: GeometricBoundingVolume() { +#ifdef DO_MEMORY_USAGE + MemoryUsage::update_type(this, this); +#endif } /** diff --git a/panda/src/mathutil/geometricBoundingVolume.h b/panda/src/mathutil/geometricBoundingVolume.h index 90c794d832..a27c19a724 100644 --- a/panda/src/mathutil/geometricBoundingVolume.h +++ b/panda/src/mathutil/geometricBoundingVolume.h @@ -83,6 +83,10 @@ private: static TypeHandle _type_handle; }; +// We can safely redefine this as a no-op. +template<> +INLINE void PointerToBase::update_type(To *ptr) {} + #include "geometricBoundingVolume.I" #endif diff --git a/panda/src/mathutil/mersenne.h b/panda/src/mathutil/mersenne.h index 6422c827d7..7bcb00cb8f 100644 --- a/panda/src/mathutil/mersenne.h +++ b/panda/src/mathutil/mersenne.h @@ -61,7 +61,7 @@ class EXPCL_PANDA_MATHUTIL Mersenne { PUBLISHED: - Mersenne(unsigned long seed); + explicit Mersenne(unsigned long seed); unsigned long get_uint31(); enum { diff --git a/panda/src/mathutil/perlinNoise2.h b/panda/src/mathutil/perlinNoise2.h index 11d82aa064..d10555e38b 100644 --- a/panda/src/mathutil/perlinNoise2.h +++ b/panda/src/mathutil/perlinNoise2.h @@ -25,9 +25,9 @@ class EXPCL_PANDA_MATHUTIL PerlinNoise2 : public PerlinNoise { PUBLISHED: INLINE PerlinNoise2(); - INLINE PerlinNoise2(double sx, double sy, - int table_size = 256, - unsigned long seed = 0); + INLINE explicit PerlinNoise2(double sx, double sy, + int table_size = 256, + unsigned long seed = 0); INLINE PerlinNoise2(const PerlinNoise2 ©); INLINE void operator = (const PerlinNoise2 ©); diff --git a/panda/src/mathutil/perlinNoise3.h b/panda/src/mathutil/perlinNoise3.h index 092091ef2f..df47b22b4e 100644 --- a/panda/src/mathutil/perlinNoise3.h +++ b/panda/src/mathutil/perlinNoise3.h @@ -25,8 +25,9 @@ class EXPCL_PANDA_MATHUTIL PerlinNoise3 : public PerlinNoise { PUBLISHED: INLINE PerlinNoise3(); - INLINE PerlinNoise3(double sx, double sy, double sz, - int table_size = 256, unsigned long seed = 0); + INLINE explicit PerlinNoise3(double sx, double sy, double sz, + int table_size = 256, + unsigned long seed = 0); INLINE PerlinNoise3(const PerlinNoise3 ©); INLINE void operator = (const PerlinNoise3 ©); diff --git a/panda/src/mathutil/randomizer.h b/panda/src/mathutil/randomizer.h index 4f0dd0e736..3fd9715f2a 100644 --- a/panda/src/mathutil/randomizer.h +++ b/panda/src/mathutil/randomizer.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDA_MATHUTIL Randomizer { PUBLISHED: - INLINE Randomizer(unsigned long seed = 0); + INLINE explicit Randomizer(unsigned long seed = 0); INLINE Randomizer(const Randomizer ©); INLINE void operator = (const Randomizer ©); diff --git a/panda/src/mathutil/stackedPerlinNoise2.h b/panda/src/mathutil/stackedPerlinNoise2.h index e292e21fe6..9628d39557 100644 --- a/panda/src/mathutil/stackedPerlinNoise2.h +++ b/panda/src/mathutil/stackedPerlinNoise2.h @@ -25,9 +25,9 @@ class EXPCL_PANDA_MATHUTIL StackedPerlinNoise2 { PUBLISHED: INLINE StackedPerlinNoise2(); - StackedPerlinNoise2(double sx, double sy, int num_levels = 2, - double scale_factor = 4.0f, double amp_scale = 0.5f, - int table_size = 256, unsigned long seed = 0); + explicit StackedPerlinNoise2(double sx, double sy, int num_levels = 2, + double scale_factor = 4.0f, double amp_scale = 0.5f, + int table_size = 256, unsigned long seed = 0); StackedPerlinNoise2(const StackedPerlinNoise2 ©); void operator = (const StackedPerlinNoise2 ©); diff --git a/panda/src/mathutil/stackedPerlinNoise3.h b/panda/src/mathutil/stackedPerlinNoise3.h index eb3b0da1e0..b2ff1ccdd2 100644 --- a/panda/src/mathutil/stackedPerlinNoise3.h +++ b/panda/src/mathutil/stackedPerlinNoise3.h @@ -25,9 +25,9 @@ class EXPCL_PANDA_MATHUTIL StackedPerlinNoise3 { PUBLISHED: INLINE StackedPerlinNoise3(); - StackedPerlinNoise3(double sx, double sy, double sz, int num_levels = 3, - double scale_factor = 4.0f, double amp_scale = 0.5f, - int table_size = 256, unsigned long seed = 0); + explicit StackedPerlinNoise3(double sx, double sy, double sz, int num_levels = 3, + double scale_factor = 4.0f, double amp_scale = 0.5f, + int table_size = 256, unsigned long seed = 0); StackedPerlinNoise3(const StackedPerlinNoise3 ©); void operator = (const StackedPerlinNoise3 ©); diff --git a/panda/src/movies/config_movies.cxx b/panda/src/movies/config_movies.cxx index fbd80e275d..4884c15f5b 100644 --- a/panda/src/movies/config_movies.cxx +++ b/panda/src/movies/config_movies.cxx @@ -23,6 +23,8 @@ #include "movieTypeRegistry.h" #include "movieVideo.h" #include "movieVideoCursor.h" +#include "opusAudio.h" +#include "opusAudioCursor.h" #include "userDataAudio.h" #include "userDataAudioCursor.h" #include "vorbisAudio.h" @@ -51,6 +53,11 @@ ConfigVariableList load_video_type "either the name of a module, or a space-separate list of filename " "extensions, followed by the name of the module.")); +ConfigVariableBool opus_enable_seek +("opus-enable-seek", true, + PRC_DESC("Set this to false if you're having trouble with seeking while " + "using the Opus decoder.")); + ConfigVariableBool vorbis_enable_seek ("vorbis-enable-seek", true, PRC_DESC("Set this to false if you're having trouble with seeking while " @@ -91,6 +98,11 @@ init_libmovies() { WavAudio::init_type(); WavAudioCursor::init_type(); +#ifdef HAVE_OPUS + OpusAudio::init_type(); + OpusAudioCursor::init_type(); +#endif + #ifdef HAVE_VORBIS VorbisAudio::init_type(); VorbisAudioCursor::init_type(); @@ -100,6 +112,10 @@ init_libmovies() { reg->register_audio_type(&FlacAudio::make, "flac"); reg->register_audio_type(&WavAudio::make, "wav wave"); +#ifdef HAVE_OPUS + reg->register_audio_type(&OpusAudio::make, "opus"); +#endif + #ifdef HAVE_VORBIS reg->register_audio_type(&VorbisAudio::make, "ogg oga"); #endif diff --git a/panda/src/movies/config_movies.h b/panda/src/movies/config_movies.h index cbfd09a440..1d8d6ae93e 100644 --- a/panda/src/movies/config_movies.h +++ b/panda/src/movies/config_movies.h @@ -27,6 +27,8 @@ NotifyCategoryDecl(movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES); extern ConfigVariableList load_audio_type; extern ConfigVariableList load_video_type; +extern ConfigVariableBool opus_enable_seek; + extern ConfigVariableBool vorbis_enable_seek; extern ConfigVariableBool vorbis_seek_lap; diff --git a/panda/src/movies/flacAudioCursor.h b/panda/src/movies/flacAudioCursor.h index 4f68eb62a8..edae05a674 100644 --- a/panda/src/movies/flacAudioCursor.h +++ b/panda/src/movies/flacAudioCursor.h @@ -30,7 +30,7 @@ class FlacAudio; */ class EXPCL_PANDA_MOVIES FlacAudioCursor : public MovieAudioCursor { PUBLISHED: - FlacAudioCursor(FlacAudio *src, istream *stream); + explicit FlacAudioCursor(FlacAudio *src, istream *stream); virtual ~FlacAudioCursor(); virtual void seek(double offset); diff --git a/panda/src/movies/inkblotVideo.h b/panda/src/movies/inkblotVideo.h index 379d59a850..b5bd7db670 100644 --- a/panda/src/movies/inkblotVideo.h +++ b/panda/src/movies/inkblotVideo.h @@ -22,13 +22,12 @@ class InkblotVideoCursor; * A cellular automaton that generates an amusing pattern of swirling colors. */ class EXPCL_PANDA_MOVIES InkblotVideo : public MovieVideo { - - PUBLISHED: - InkblotVideo(int x, int y, int fps); +PUBLISHED: + explicit InkblotVideo(int x, int y, int fps); virtual ~InkblotVideo(); virtual PT(MovieVideoCursor) open(); - private: +private: int _specified_x; int _specified_y; int _specified_fps; diff --git a/panda/src/pipeline/asyncTaskBase.I b/panda/src/movies/opusAudio.I similarity index 82% rename from panda/src/pipeline/asyncTaskBase.I rename to panda/src/movies/opusAudio.I index 09abcb3a3d..23f144fe3a 100644 --- a/panda/src/pipeline/asyncTaskBase.I +++ b/panda/src/movies/opusAudio.I @@ -6,7 +6,7 @@ * license. You should have received a copy of this license along * with this source code in a file named "LICENSE." * - * @file asyncTaskBase.I - * @author drose - * @date 2010-02-09 + * @file opusAudio.I + * @author rdb + * @date 2017-05-24 */ diff --git a/panda/src/movies/opusAudio.cxx b/panda/src/movies/opusAudio.cxx new file mode 100644 index 0000000000..024560156a --- /dev/null +++ b/panda/src/movies/opusAudio.cxx @@ -0,0 +1,68 @@ +/** + * 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 opusAudio.cxx + * @author rdb + * @date 2017-05-24 + */ + +#include "opusAudio.h" +#include "opusAudioCursor.h" +#include "virtualFileSystem.h" +#include "dcast.h" + +#ifdef HAVE_OPUS + +TypeHandle OpusAudio::_type_handle; + +/** + * xxx + */ +OpusAudio:: +OpusAudio(const Filename &name) : + MovieAudio(name) +{ + _filename = name; +} + +/** + * xxx + */ +OpusAudio:: +~OpusAudio() { +} + +/** + * Open this audio, returning a MovieAudioCursor + */ +PT(MovieAudioCursor) OpusAudio:: +open() { + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + istream *stream = vfs->open_read_file(_filename, true); + + if (stream == nullptr) { + return nullptr; + } else { + PT(OpusAudioCursor) cursor = new OpusAudioCursor(this, stream); + if (cursor == nullptr || !cursor->_is_valid) { + return nullptr; + } else { + return DCAST(MovieAudioCursor, cursor); + } + } +} + +/** + * Obtains a MovieAudio that references a file. + */ +PT(MovieAudio) OpusAudio:: +make(const Filename &name) { + return DCAST(MovieAudio, new OpusAudio(name)); +} + +#endif // HAVE_OPUS diff --git a/panda/src/movies/opusAudio.h b/panda/src/movies/opusAudio.h new file mode 100644 index 0000000000..813a4dbd58 --- /dev/null +++ b/panda/src/movies/opusAudio.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 opusAudio.h + * @author rdb + * @date 2017-05-24 + */ + +#ifndef OPUSAUDIO_H +#define OPUSAUDIO_H + +#include "pandabase.h" +#include "movieAudio.h" + +#ifdef HAVE_OPUS + +class OpusAudioCursor; + +/** + * Interfaces with the libopusfile library to implement decoding of Opus + * audio files. + */ +class EXPCL_PANDA_MOVIES OpusAudio : public MovieAudio { +PUBLISHED: + OpusAudio(const Filename &name); + virtual ~OpusAudio(); + virtual PT(MovieAudioCursor) open(); + + static PT(MovieAudio) make(const Filename &name); + +private: + friend class OpusAudioCursor; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWritableReferenceCount::init_type(); + register_type(_type_handle, "OpusAudio", + MovieAudio::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "opusAudio.I" + +#endif // HAVE_OPUS + +#endif // OPUSAUDIO_H diff --git a/panda/src/gsgbase/displayRegionBase.cxx b/panda/src/movies/opusAudioCursor.I similarity index 58% rename from panda/src/gsgbase/displayRegionBase.cxx rename to panda/src/movies/opusAudioCursor.I index b3573b9e68..1e923c3a80 100644 --- a/panda/src/gsgbase/displayRegionBase.cxx +++ b/panda/src/movies/opusAudioCursor.I @@ -6,19 +6,7 @@ * license. You should have received a copy of this license along * with this source code in a file named "LICENSE." * - * @file displayRegionBase.cxx - * @author drose - * @date 2009-02-20 + * @file opusAudioCursor.I + * @author rdb + * @date 2017-05-24 */ - -#include "displayRegionBase.h" - -TypeHandle DisplayRegionBase::_type_handle; - - -/** - * - */ -DisplayRegionBase:: -~DisplayRegionBase() { -} diff --git a/panda/src/movies/opusAudioCursor.cxx b/panda/src/movies/opusAudioCursor.cxx new file mode 100644 index 0000000000..698648c5cb --- /dev/null +++ b/panda/src/movies/opusAudioCursor.cxx @@ -0,0 +1,224 @@ +/** + * 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 opusAudioCursor.cxx + * @author rdb + * @date 2017-05-24 + */ + +#include "opusAudioCursor.h" +#include "virtualFileSystem.h" + +#ifdef HAVE_OPUS + +#include + +/** + * Callbacks passed to libopusfile to implement file I/O via the + * VirtualFileSystem. + */ +int cb_read(void *stream, unsigned char *ptr, int nbytes) { + istream *in = (istream *)stream; + nassertr(in != nullptr, -1); + + in->read((char *)ptr, nbytes); + + if (in->eof()) { + // Gracefully handle EOF. + in->clear(); + } + + return in->gcount(); +} + +int cb_seek(void *stream, opus_int64 offset, int whence) { + if (!opus_enable_seek) { + return -1; + } + + istream *in = (istream *)stream; + nassertr(in != nullptr, -1); + + switch (whence) { + case SEEK_SET: + in->seekg(offset, ios::beg); + break; + + case SEEK_CUR: + in->seekg(offset, ios::cur); + break; + + case SEEK_END: + in->seekg(offset, ios::end); + break; + + default: + movies_cat.error() + << "Illegal parameter to seek in cb_seek\n"; + return -1; + } + + if (in->fail()) { + movies_cat.error() + << "Failure to seek to byte " << offset; + + switch (whence) { + case SEEK_CUR: + movies_cat.error(false) + << " from current location!\n"; + break; + + case SEEK_END: + movies_cat.error(false) + << " from end of file!\n"; + break; + + default: + movies_cat.error(false) << "!\n"; + } + + return -1; + } + + return 0; +} + +opus_int64 cb_tell(void *stream) { + istream *in = (istream *)stream; + nassertr(in != nullptr, -1); + + return in->tellg(); +} + +int cb_close(void *stream) { + istream *in = (istream *)stream; + nassertr(in != nullptr, EOF); + + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + vfs->close_read_file(in); + return 0; +} + +static const OpusFileCallbacks callbacks = {cb_read, cb_seek, cb_tell, cb_close}; + +TypeHandle OpusAudioCursor::_type_handle; + +/** + * Reads the .wav header from the indicated stream. This leaves the read + * pointer positioned at the start of the data. + */ +OpusAudioCursor:: +OpusAudioCursor(OpusAudio *src, istream *stream) : + MovieAudioCursor(src), + _is_valid(false), + _link(0) +{ + nassertv(stream != nullptr); + nassertv(stream->good()); + + int error = 0; + _op = op_open_callbacks((void *)stream, &callbacks, nullptr, 0, &error); + if (_op == nullptr) { + movies_cat.error() + << "Failed to read Opus file (error code " << error << ").\n"; + return; + } + + ogg_int64_t samples = op_pcm_total(_op, -1); + if (samples != OP_EINVAL) { + // Opus timestamps are fixed at 48 kHz. + _length = (double)samples / 48000.0; + } + + _audio_channels = op_channel_count(_op, -1); + _audio_rate = 48000; + + _can_seek = opus_enable_seek && op_seekable(_op); + _can_seek_fast = _can_seek; + + _is_valid = true; +} + +/** + * xxx + */ +OpusAudioCursor:: +~OpusAudioCursor() { + if (_op != nullptr) { + op_free(_op); + _op = nullptr; + } +} + +/** + * Seeks to a target location. Afterward, the packet_time is guaranteed to be + * less than or equal to the specified time. + */ +void OpusAudioCursor:: +seek(double t) { + if (!opus_enable_seek) { + return; + } + + t = max(t, 0.0); + + // Use op_time_seek_lap if cross-lapping is enabled. + int error = op_pcm_seek(_op, (ogg_int64_t)(t * 48000.0)); + if (error != 0) { + movies_cat.error() + << "Seek failed (error " << error << "). Opus stream may not be seekable.\n"; + return; + } + + _last_seek = op_pcm_tell(_op) / 48000.0; + _samples_read = 0; +} + +/** + * Read audio samples from the stream. N is the number of samples you wish to + * read. Your buffer must be equal in size to N * channels. Multiple-channel + * audio will be interleaved. + */ +void OpusAudioCursor:: +read_samples(int n, int16_t *data) { + int16_t *end = data + (n * _audio_channels); + + while (data < end) { + // op_read gives it to us in the exact format we need. Nifty! + int link; + int read_samples = op_read(_op, data, end - data, &link); + if (read_samples > 0) { + data += read_samples * _audio_channels; + _samples_read += read_samples; + } else { + break; + } + + if (_link != link) { + // It is technically possible for it to change parameters from one link + // to the next. However, we don't offer this flexibility. + int channels = op_channel_count(_op, link); + if (channels != _audio_channels) { + movies_cat.error() + << "Opus file has inconsistent channel count!\n"; + + // We'll change it anyway. Not sure what happens next. + _audio_channels = channels; + } + + _link = link; + } + } + + // Fill the rest of the buffer with silence. + if (data < end) { + memset(data, 0, (unsigned char *)end - (unsigned char *)data); + } +} + +#endif // HAVE_OPUS diff --git a/panda/src/movies/opusAudioCursor.h b/panda/src/movies/opusAudioCursor.h new file mode 100644 index 0000000000..51916fbc29 --- /dev/null +++ b/panda/src/movies/opusAudioCursor.h @@ -0,0 +1,78 @@ +/** + * 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 opusAudioCursor.h + * @author rdb + * @date 2017-05-24 + */ + +#ifndef OPUSAUDIOCURSOR_H +#define OPUSAUDIOCURSOR_H + +#include "pandabase.h" +#include "movieAudioCursor.h" + +#ifdef HAVE_OPUS + +#include + +typedef struct OggOpusFile OggOpusFile; + +class OpusAudio; + +/** + * Interfaces with the libopusfile library to implement decoding of Opus + * audio files. + */ +class EXPCL_PANDA_MOVIES OpusAudioCursor : public MovieAudioCursor { +PUBLISHED: + explicit OpusAudioCursor(OpusAudio *src, istream *stream); + virtual ~OpusAudioCursor(); + virtual void seek(double offset); + +public: + virtual void read_samples(int n, int16_t *data); + + bool _is_valid; + +protected: + OggOpusFile *_op; + + int _link; + double _byte_rate; + int _block_align; + int _bytes_per_sample; + bool _is_float; + + streampos _data_start; + streampos _data_pos; + size_t _data_size; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MovieAudioCursor::init_type(); + register_type(_type_handle, "OpusAudioCursor", + MovieAudioCursor::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "opusAudioCursor.I" + +#endif // HAVE_OPUS + +#endif // OPUSAUDIOCURSOR_H diff --git a/panda/src/movies/p3movies_composite1.cxx b/panda/src/movies/p3movies_composite1.cxx index ea526c30b1..61954c669e 100644 --- a/panda/src/movies/p3movies_composite1.cxx +++ b/panda/src/movies/p3movies_composite1.cxx @@ -10,6 +10,8 @@ #include "movieTypeRegistry.cxx" #include "movieVideo.cxx" #include "movieVideoCursor.cxx" +#include "opusAudio.cxx" +#include "opusAudioCursor.cxx" #include "userDataAudio.cxx" #include "userDataAudioCursor.cxx" #include "vorbisAudio.cxx" diff --git a/panda/src/movies/vorbisAudioCursor.h b/panda/src/movies/vorbisAudioCursor.h index 2d16368dde..4a9bb79847 100644 --- a/panda/src/movies/vorbisAudioCursor.h +++ b/panda/src/movies/vorbisAudioCursor.h @@ -30,7 +30,7 @@ class VorbisAudio; */ class EXPCL_PANDA_MOVIES VorbisAudioCursor : public MovieAudioCursor { PUBLISHED: - VorbisAudioCursor(VorbisAudio *src, istream *stream); + explicit VorbisAudioCursor(VorbisAudio *src, istream *stream); virtual ~VorbisAudioCursor(); virtual void seek(double offset); diff --git a/panda/src/movies/wavAudioCursor.h b/panda/src/movies/wavAudioCursor.h index e03ee5bdde..2f37f37f4e 100644 --- a/panda/src/movies/wavAudioCursor.h +++ b/panda/src/movies/wavAudioCursor.h @@ -26,7 +26,7 @@ class WavAudio; */ class EXPCL_PANDA_MOVIES WavAudioCursor : public MovieAudioCursor { PUBLISHED: - WavAudioCursor(WavAudio *src, istream *stream); + explicit WavAudioCursor(WavAudio *src, istream *stream); virtual ~WavAudioCursor(); virtual void seek(double offset); diff --git a/panda/src/nativenet/buffered_datagramconnection.h b/panda/src/nativenet/buffered_datagramconnection.h index 53b4b5d957..27e5405339 100644 --- a/panda/src/nativenet/buffered_datagramconnection.h +++ b/panda/src/nativenet/buffered_datagramconnection.h @@ -82,7 +82,7 @@ PUBLISHED: inline bool GetMessage(Datagram &val); inline bool DoConnect(void); // all the real state magic is in here inline bool IsConnected(void); - inline Buffered_DatagramConnection(int rbufsize, int wbufsize, int write_flush_point) ; + inline explicit Buffered_DatagramConnection(int rbufsize, int wbufsize, int write_flush_point) ; virtual ~Buffered_DatagramConnection(void) ; // the reason thsi all exists bool SendMessage(const Datagram &msg); diff --git a/panda/src/nativenet/socket_address.h b/panda/src/nativenet/socket_address.h index 20046e3d24..f8141ae987 100644 --- a/panda/src/nativenet/socket_address.h +++ b/panda/src/nativenet/socket_address.h @@ -32,7 +32,7 @@ public: INLINE const struct sockaddr &GetAddressInfo() const { return _addr; } PUBLISHED: - INLINE Socket_Address(unsigned short port = 0); + INLINE explicit Socket_Address(unsigned short port = 0); INLINE Socket_Address(const Socket_Address &inaddr); INLINE virtual ~Socket_Address(); diff --git a/panda/src/net/connection.h b/panda/src/net/connection.h index 92cbf12166..bac0c8def0 100644 --- a/panda/src/net/connection.h +++ b/panda/src/net/connection.h @@ -28,7 +28,7 @@ class NetDatagram; */ class EXPCL_PANDA_NET Connection : public ReferenceCount { PUBLISHED: - Connection(ConnectionManager *manager, Socket_IP *socket); + explicit Connection(ConnectionManager *manager, Socket_IP *socket); ~Connection(); NetAddress get_address() const; diff --git a/panda/src/net/connectionReader.h b/panda/src/net/connectionReader.h index 3f00fa538b..fb9542df4a 100644 --- a/panda/src/net/connectionReader.h +++ b/panda/src/net/connectionReader.h @@ -60,8 +60,8 @@ PUBLISHED: // by a previous call to PR_Poll(), or (b) execute (and possibly block on) a // new call to PR_Poll(). - ConnectionReader(ConnectionManager *manager, int num_threads, - const string &thread_name = string()); + explicit ConnectionReader(ConnectionManager *manager, int num_threads, + const string &thread_name = string()); virtual ~ConnectionReader(); bool add_connection(Connection *connection); diff --git a/panda/src/net/connectionWriter.h b/panda/src/net/connectionWriter.h index 71f74bb0a1..58a0300ef6 100644 --- a/panda/src/net/connectionWriter.h +++ b/panda/src/net/connectionWriter.h @@ -34,8 +34,8 @@ class NetAddress; */ class EXPCL_PANDA_NET ConnectionWriter { PUBLISHED: - ConnectionWriter(ConnectionManager *manager, int num_threads, - const string &thread_name = string()); + explicit ConnectionWriter(ConnectionManager *manager, int num_threads, + const string &thread_name = string()); ~ConnectionWriter(); void set_max_queue_size(int max_size); diff --git a/panda/src/net/datagramGeneratorNet.h b/panda/src/net/datagramGeneratorNet.h index be255a5b10..7926fb8483 100644 --- a/panda/src/net/datagramGeneratorNet.h +++ b/panda/src/net/datagramGeneratorNet.h @@ -32,7 +32,7 @@ EXPORT_TEMPLATE_CLASS(EXPCL_PANDA_NET, EXPTP_PANDA_NET, QueuedReturn); */ class EXPCL_PANDA_NET DatagramGeneratorNet : public DatagramGenerator, public ConnectionReader, public QueuedReturn { PUBLISHED: - DatagramGeneratorNet(ConnectionManager *manager, int num_threads); + explicit DatagramGeneratorNet(ConnectionManager *manager, int num_threads); virtual ~DatagramGeneratorNet(); // Inherited from DatagramGenerator diff --git a/panda/src/net/datagramSinkNet.h b/panda/src/net/datagramSinkNet.h index b188fb6011..927933b118 100644 --- a/panda/src/net/datagramSinkNet.h +++ b/panda/src/net/datagramSinkNet.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDA_NET DatagramSinkNet : public DatagramSink, public ConnectionWriter { PUBLISHED: - DatagramSinkNet(ConnectionManager *manager, int num_threads); + explicit DatagramSinkNet(ConnectionManager *manager, int num_threads); INLINE void set_target(Connection *connection); INLINE Connection *get_target() const; diff --git a/panda/src/net/queuedConnectionListener.h b/panda/src/net/queuedConnectionListener.h index 7aea32afc8..4fbace6c2d 100644 --- a/panda/src/net/queuedConnectionListener.h +++ b/panda/src/net/queuedConnectionListener.h @@ -45,7 +45,7 @@ EXPORT_TEMPLATE_CLASS(EXPCL_PANDA_NET, EXPTP_PANDA_NET, QueuedReturn { PUBLISHED: - QueuedConnectionListener(ConnectionManager *manager, int num_threads); + explicit QueuedConnectionListener(ConnectionManager *manager, int num_threads); virtual ~QueuedConnectionListener(); BLOCKING bool new_connection_available(); diff --git a/panda/src/net/queuedConnectionReader.h b/panda/src/net/queuedConnectionReader.h index 3ce7461558..680876733b 100644 --- a/panda/src/net/queuedConnectionReader.h +++ b/panda/src/net/queuedConnectionReader.h @@ -33,7 +33,7 @@ EXPORT_TEMPLATE_CLASS(EXPCL_PANDA_NET, EXPTP_PANDA_NET, QueuedReturn { PUBLISHED: - QueuedConnectionReader(ConnectionManager *manager, int num_threads); + explicit QueuedConnectionReader(ConnectionManager *manager, int num_threads); virtual ~QueuedConnectionReader(); BLOCKING bool data_available(); diff --git a/panda/src/net/recentConnectionReader.h b/panda/src/net/recentConnectionReader.h index f31f20ac2e..513f0fc6b2 100644 --- a/panda/src/net/recentConnectionReader.h +++ b/panda/src/net/recentConnectionReader.h @@ -31,7 +31,7 @@ */ class EXPCL_PANDA_NET RecentConnectionReader : public ConnectionReader { PUBLISHED: - RecentConnectionReader(ConnectionManager *manager); + explicit RecentConnectionReader(ConnectionManager *manager); virtual ~RecentConnectionReader(); bool data_available(); diff --git a/panda/src/ode/odeTriMeshData.cxx b/panda/src/ode/odeTriMeshData.cxx index afae75b753..5f02cefa56 100644 --- a/panda/src/ode/odeTriMeshData.cxx +++ b/panda/src/ode/odeTriMeshData.cxx @@ -29,7 +29,7 @@ get_data(dGeomID id) { if (iter != data_map.end()) { return iter->second; } - return 0; + return NULL; } void OdeTriMeshData:: diff --git a/panda/src/ode/odeTriMeshGeom.I b/panda/src/ode/odeTriMeshGeom.I index 8bfd233bbf..b0a2e6177c 100644 --- a/panda/src/ode/odeTriMeshGeom.I +++ b/panda/src/ode/odeTriMeshGeom.I @@ -34,7 +34,7 @@ set_tri_mesh_data(OdeTriMeshData &data) { INLINE PT(OdeTriMeshData) OdeTriMeshGeom:: get_tri_mesh_data() const { - nassertr(_id != 0 ,0); + nassertr(_id != 0, NULL); return OdeTriMeshData::get_data(_id); } diff --git a/panda/src/parametrics/hermiteCurve.h b/panda/src/parametrics/hermiteCurve.h index 806ab47ff1..5a3d350b82 100644 --- a/panda/src/parametrics/hermiteCurve.h +++ b/panda/src/parametrics/hermiteCurve.h @@ -163,7 +163,9 @@ public: return _type_handle; } static void init_type() { - register_type(_type_handle, "HermiteCurve"); + PiecewiseCurve::init_type(); + register_type(_type_handle, "HermiteCurve", + PiecewiseCurve::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); diff --git a/panda/src/parametrics/parametricCurveCollection.I b/panda/src/parametrics/parametricCurveCollection.I index c8549fa0b8..07fd5e9f26 100644 --- a/panda/src/parametrics/parametricCurveCollection.I +++ b/panda/src/parametrics/parametricCurveCollection.I @@ -36,6 +36,15 @@ get_curve(int index) const { return _curves[index]; } +/** + * Adds a new ParametricCurve to the collection at the indicated index. + * @deprecated Use insert_curve(index, curve) instead. + */ +INLINE void ParametricCurveCollection:: +add_curve(ParametricCurve *curve, int index) { + insert_curve(max(index, 0), curve); +} + /** * Returns the maximum T value associated with the *last* curve in the * collection. Normally, this will be either the XYZ or HPR curve, or a diff --git a/panda/src/parametrics/parametricCurveCollection.cxx b/panda/src/parametrics/parametricCurveCollection.cxx index 2778161e34..45d61ccfe8 100644 --- a/panda/src/parametrics/parametricCurveCollection.cxx +++ b/panda/src/parametrics/parametricCurveCollection.cxx @@ -42,9 +42,9 @@ add_curve(ParametricCurve *curve) { * Adds a new ParametricCurve to the collection at the indicated index. */ void ParametricCurveCollection:: -add_curve(ParametricCurve *curve, int index) { +insert_curve(size_t index, ParametricCurve *curve) { prepare_add_curve(curve); - index = max(min(index, (int)_curves.size()), 0); + index = min(index, _curves.size()); _curves.insert(_curves.begin() + index, curve); redraw(); } @@ -93,8 +93,8 @@ remove_curve(ParametricCurve *curve) { * number. */ void ParametricCurveCollection:: -remove_curve(int index) { - nassertv(index >= 0 && index < (int)_curves.size()); +remove_curve(size_t index) { + nassertv(index < _curves.size()); PT(ParametricCurve) curve = _curves[index]; prepare_remove_curve(curve); _curves.erase(_curves.begin() + index); @@ -107,8 +107,8 @@ remove_curve(int index) { * number. */ void ParametricCurveCollection:: -set_curve(int index, ParametricCurve *curve) { - nassertv(index >= 0 && index < (int)_curves.size()); +set_curve(size_t index, ParametricCurve *curve) { + nassertv(index < _curves.size()); prepare_remove_curve(_curves[index]); prepare_add_curve(curve); _curves[index] = curve; diff --git a/panda/src/parametrics/parametricCurveCollection.h b/panda/src/parametrics/parametricCurveCollection.h index 42a84c7861..44e588dd89 100644 --- a/panda/src/parametrics/parametricCurveCollection.h +++ b/panda/src/parametrics/parametricCurveCollection.h @@ -11,8 +11,8 @@ * @date 2001-03-04 */ -#ifndef NODEPATHCOLLECTION_H -#define NODEPATHCOLLECTION_H +#ifndef PARAMETRICCURVECOLLECTION_H +#define PARAMETRICCURVECOLLECTION_H #include "pandabase.h" @@ -41,10 +41,11 @@ PUBLISHED: void add_curve(ParametricCurve *curve); void add_curve(ParametricCurve *curve, int index); + void insert_curve(size_t index, ParametricCurve *curve); int add_curves(PandaNode *node); bool remove_curve(ParametricCurve *curve); - void remove_curve(int index); - void set_curve(int index, ParametricCurve *curve); + void remove_curve(size_t index); + void set_curve(size_t index, ParametricCurve *curve); bool has_curve(ParametricCurve *curve) const; void clear(); void clear_timewarps(); diff --git a/panda/src/parametrics/ropeNode.cxx b/panda/src/parametrics/ropeNode.cxx index c799595888..365795adf7 100644 --- a/panda/src/parametrics/ropeNode.cxx +++ b/panda/src/parametrics/ropeNode.cxx @@ -132,9 +132,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { if (curve != (NurbsCurveEvaluator *)NULL) { PT(NurbsCurveResult) result; if (has_matrix()) { - result = curve->evaluate(data._node_path.get_node_path(), get_matrix()); + result = curve->evaluate(data.get_node_path(), get_matrix()); } else { - result = curve->evaluate(data._node_path.get_node_path()); + result = curve->evaluate(data.get_node_path()); } if (result->get_num_segments() > 0) { diff --git a/panda/src/parametrics/ropeNode.h b/panda/src/parametrics/ropeNode.h index 1469ec6174..063070c802 100644 --- a/panda/src/parametrics/ropeNode.h +++ b/panda/src/parametrics/ropeNode.h @@ -33,7 +33,7 @@ class GeomVertexData; */ class EXPCL_PANDA_PARAMETRICS RopeNode : public PandaNode { PUBLISHED: - RopeNode(const string &name); + explicit RopeNode(const string &name); protected: RopeNode(const RopeNode ©); @@ -136,6 +136,23 @@ PUBLISHED: void reset_bound(const NodePath &rel_to); +PUBLISHED: + MAKE_PROPERTY(curve, get_curve, set_curve); + MAKE_PROPERTY(render_mode, get_render_mode, set_render_mode); + MAKE_PROPERTY(uv_mode, get_uv_mode, set_uv_mode); + MAKE_PROPERTY(uv_direction, get_uv_direction, set_uv_direction); + MAKE_PROPERTY(uv_scale, get_uv_scale, set_uv_scale); + MAKE_PROPERTY(normal_mode, get_normal_mode, set_normal_mode); + MAKE_PROPERTY(tube_up, get_tube_up, set_tube_up); + MAKE_PROPERTY(use_vertex_color, get_use_vertex_color, set_use_vertex_color); + MAKE_PROPERTY(vertex_color_dimension, get_vertex_color_dimension); + MAKE_PROPERTY(num_subdiv, get_num_subdiv, set_num_subdiv); + MAKE_PROPERTY(num_slices, get_num_slices, set_num_slices); + MAKE_PROPERTY(use_vertex_thickness, get_use_vertex_thickness, set_use_vertex_thickness); + MAKE_PROPERTY(vertex_thickness_dimension, get_vertex_thickness_dimension); + MAKE_PROPERTY(thickness, get_thickness, set_thickness); + MAKE_PROPERTY2(matrix, has_matrix, get_matrix, set_matrix, clear_matrix); + protected: virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, int &internal_vertices, diff --git a/panda/src/parametrics/sheetNode.cxx b/panda/src/parametrics/sheetNode.cxx index 34496bca68..b4e4cc5a4d 100644 --- a/panda/src/parametrics/sheetNode.cxx +++ b/panda/src/parametrics/sheetNode.cxx @@ -129,7 +129,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { if (get_num_u_subdiv() > 0 && get_num_v_subdiv() > 0) { NurbsSurfaceEvaluator *surface = get_surface(); if (surface != (NurbsSurfaceEvaluator *)NULL) { - PT(NurbsSurfaceResult) result = surface->evaluate(data._node_path.get_node_path()); + PT(NurbsSurfaceResult) result = surface->evaluate(data.get_node_path()); if (result->get_num_u_segments() > 0 && result->get_num_v_segments() > 0) { render_sheet(trav, data, result); diff --git a/panda/src/parametrics/sheetNode.h b/panda/src/parametrics/sheetNode.h index fdf8467212..29374955a7 100644 --- a/panda/src/parametrics/sheetNode.h +++ b/panda/src/parametrics/sheetNode.h @@ -31,7 +31,7 @@ */ class EXPCL_PANDA_PARAMETRICS SheetNode : public PandaNode { PUBLISHED: - SheetNode(const string &name); + explicit SheetNode(const string &name); protected: SheetNode(const SheetNode ©); diff --git a/panda/src/particlesystem/colorInterpolationManager.h b/panda/src/particlesystem/colorInterpolationManager.h index db954e178d..6c01d95b24 100644 --- a/panda/src/particlesystem/colorInterpolationManager.h +++ b/panda/src/particlesystem/colorInterpolationManager.h @@ -225,9 +225,12 @@ private: */ class EXPCL_PANDAPHYSICS ColorInterpolationSegment : public ReferenceCount { -PUBLISHED: +public: ColorInterpolationSegment(ColorInterpolationFunction* function, const PN_stdfloat &time_begin, const PN_stdfloat &time_end, const bool is_modulated, const int id); + +PUBLISHED: ColorInterpolationSegment(const ColorInterpolationSegment &s); + virtual ~ColorInterpolationSegment(); // INLINE ColorInterpolationFunction* get_function() const; diff --git a/panda/src/particlesystem/geomParticleRenderer.h b/panda/src/particlesystem/geomParticleRenderer.h index 4ef60906f0..54e58a2d65 100644 --- a/panda/src/particlesystem/geomParticleRenderer.h +++ b/panda/src/particlesystem/geomParticleRenderer.h @@ -25,8 +25,8 @@ class EXPCL_PANDAPHYSICS GeomParticleRenderer : public BaseParticleRenderer { PUBLISHED: - GeomParticleRenderer(ParticleRendererAlphaMode am = PR_ALPHA_NONE, - PandaNode *geom_node = (PandaNode *) NULL); + explicit GeomParticleRenderer(ParticleRendererAlphaMode am = PR_ALPHA_NONE, + PandaNode *geom_node = (PandaNode *) NULL); GeomParticleRenderer(const GeomParticleRenderer& copy); virtual ~GeomParticleRenderer(); diff --git a/panda/src/particlesystem/lineParticleRenderer.h b/panda/src/particlesystem/lineParticleRenderer.h index a07e04e9a8..ae37a833d5 100644 --- a/panda/src/particlesystem/lineParticleRenderer.h +++ b/panda/src/particlesystem/lineParticleRenderer.h @@ -32,9 +32,9 @@ class EXPCL_PANDAPHYSICS LineParticleRenderer : public BaseParticleRenderer { PUBLISHED: LineParticleRenderer(); LineParticleRenderer(const LineParticleRenderer& copy); - LineParticleRenderer(const LColor& head, - const LColor& tail, - ParticleRendererAlphaMode alpha_mode); + explicit LineParticleRenderer(const LColor& head, + const LColor& tail, + ParticleRendererAlphaMode alpha_mode); virtual ~LineParticleRenderer(); diff --git a/panda/src/particlesystem/particleSystem.h b/panda/src/particlesystem/particleSystem.h index 5411d8e35e..e2689cf361 100644 --- a/panda/src/particlesystem/particleSystem.h +++ b/panda/src/particlesystem/particleSystem.h @@ -41,7 +41,7 @@ class EXPCL_PANDAPHYSICS ParticleSystem : public Physical { PUBLISHED: // constructordestructor - ParticleSystem(int pool_size = 0); + explicit ParticleSystem(int pool_size = 0); ParticleSystem(const ParticleSystem& copy); ~ParticleSystem(); diff --git a/panda/src/particlesystem/particleSystemManager.h b/panda/src/particlesystem/particleSystemManager.h index 5ad7bc710f..1b03fdbc06 100644 --- a/panda/src/particlesystem/particleSystemManager.h +++ b/panda/src/particlesystem/particleSystemManager.h @@ -26,7 +26,7 @@ */ class EXPCL_PANDAPHYSICS ParticleSystemManager { PUBLISHED: - ParticleSystemManager(int every_nth_frame = 1); + explicit ParticleSystemManager(int every_nth_frame = 1); virtual ~ParticleSystemManager(); INLINE void set_frame_stepping(int every_nth_frame); diff --git a/panda/src/particlesystem/pointParticleRenderer.h b/panda/src/particlesystem/pointParticleRenderer.h index 4d1ee48f8c..3f7ed52612 100644 --- a/panda/src/particlesystem/pointParticleRenderer.h +++ b/panda/src/particlesystem/pointParticleRenderer.h @@ -39,12 +39,12 @@ PUBLISHED: }; PointParticleRenderer(const PointParticleRenderer& copy); - PointParticleRenderer(ParticleRendererAlphaMode ad = PR_ALPHA_NONE, - PN_stdfloat point_size = 1.0f, - PointParticleBlendType bt = PP_ONE_COLOR, - ParticleRendererBlendMethod bm = PP_NO_BLEND, - const LColor& sc = LColor(1.0f, 1.0f, 1.0f, 1.0f), - const LColor& ec = LColor(1.0f, 1.0f, 1.0f, 1.0f)); + explicit PointParticleRenderer(ParticleRendererAlphaMode ad = PR_ALPHA_NONE, + PN_stdfloat point_size = 1.0f, + PointParticleBlendType bt = PP_ONE_COLOR, + ParticleRendererBlendMethod bm = PP_NO_BLEND, + const LColor& sc = LColor(1.0f, 1.0f, 1.0f, 1.0f), + const LColor& ec = LColor(1.0f, 1.0f, 1.0f, 1.0f)); virtual ~PointParticleRenderer(); diff --git a/panda/src/particlesystem/sparkleParticleRenderer.h b/panda/src/particlesystem/sparkleParticleRenderer.h index ee5e85a860..367dd0029d 100644 --- a/panda/src/particlesystem/sparkleParticleRenderer.h +++ b/panda/src/particlesystem/sparkleParticleRenderer.h @@ -40,12 +40,12 @@ PUBLISHED: SparkleParticleRenderer(); SparkleParticleRenderer(const SparkleParticleRenderer& copy); - SparkleParticleRenderer(const LColor& center, - const LColor& edge, - PN_stdfloat birth_radius, - PN_stdfloat death_radius, - SparkleParticleLifeScale life_scale, - ParticleRendererAlphaMode alpha_mode); + explicit SparkleParticleRenderer(const LColor& center, + const LColor& edge, + PN_stdfloat birth_radius, + PN_stdfloat death_radius, + SparkleParticleLifeScale life_scale, + ParticleRendererAlphaMode alpha_mode); virtual ~SparkleParticleRenderer(); diff --git a/panda/src/particlesystem/spriteParticleRenderer.h b/panda/src/particlesystem/spriteParticleRenderer.h index e3f9ccc5d7..4ab65c02fb 100644 --- a/panda/src/particlesystem/spriteParticleRenderer.h +++ b/panda/src/particlesystem/spriteParticleRenderer.h @@ -153,7 +153,7 @@ private: */ class EXPCL_PANDAPHYSICS SpriteParticleRenderer : public BaseParticleRenderer { PUBLISHED: - SpriteParticleRenderer(Texture *tex = (Texture *) NULL); + explicit SpriteParticleRenderer(Texture *tex = (Texture *) NULL); SpriteParticleRenderer(const SpriteParticleRenderer ©); virtual ~SpriteParticleRenderer(); diff --git a/panda/src/pgraph/alphaTestAttrib.cxx b/panda/src/pgraph/alphaTestAttrib.cxx index 5a37a23531..a302026623 100644 --- a/panda/src/pgraph/alphaTestAttrib.cxx +++ b/panda/src/pgraph/alphaTestAttrib.cxx @@ -18,6 +18,7 @@ #include "bamWriter.h" #include "datagram.h" #include "datagramIterator.h" +#include "auxBitplaneAttrib.h" TypeHandle AlphaTestAttrib::_type_handle; int AlphaTestAttrib::_attrib_slot; @@ -93,14 +94,6 @@ get_hash_impl() const { return hash; } -/** - * - */ -CPT(RenderAttrib) AlphaTestAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return this; -} - /** * Tells the BamReader how to create objects of type AlphaTestAttrib. */ diff --git a/panda/src/pgraph/alphaTestAttrib.h b/panda/src/pgraph/alphaTestAttrib.h index 5c38727347..d9aae7ec5b 100644 --- a/panda/src/pgraph/alphaTestAttrib.h +++ b/panda/src/pgraph/alphaTestAttrib.h @@ -46,7 +46,6 @@ public: protected: virtual int compare_to_impl(const RenderAttrib *other) const; virtual size_t get_hash_impl() const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: PandaCompareFunc _mode; @@ -59,6 +58,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/antialiasAttrib.h b/panda/src/pgraph/antialiasAttrib.h index 30a72bb535..af149af5f6 100644 --- a/panda/src/pgraph/antialiasAttrib.h +++ b/panda/src/pgraph/antialiasAttrib.h @@ -75,6 +75,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/audioVolumeAttrib.h b/panda/src/pgraph/audioVolumeAttrib.h index 3b2120462f..abf1216130 100644 --- a/panda/src/pgraph/audioVolumeAttrib.h +++ b/panda/src/pgraph/audioVolumeAttrib.h @@ -65,6 +65,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/auxBitplaneAttrib.cxx b/panda/src/pgraph/auxBitplaneAttrib.cxx index 1d8a5b3095..8f8e2d8a9f 100644 --- a/panda/src/pgraph/auxBitplaneAttrib.cxx +++ b/panda/src/pgraph/auxBitplaneAttrib.cxx @@ -97,14 +97,6 @@ get_hash_impl() const { return hash; } -/** - * - */ -CPT(RenderAttrib) AuxBitplaneAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return this; -} - /** * Tells the BamReader how to create objects of type AuxBitplaneAttrib. */ diff --git a/panda/src/pgraph/auxBitplaneAttrib.h b/panda/src/pgraph/auxBitplaneAttrib.h index 4c5866c49c..26ce021110 100644 --- a/panda/src/pgraph/auxBitplaneAttrib.h +++ b/panda/src/pgraph/auxBitplaneAttrib.h @@ -72,7 +72,6 @@ public: protected: virtual int compare_to_impl(const RenderAttrib *other) const; virtual size_t get_hash_impl() const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: int _outputs; @@ -86,6 +85,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/billboardEffect.cxx b/panda/src/pgraph/billboardEffect.cxx index dad0638245..737b370c42 100644 --- a/panda/src/pgraph/billboardEffect.cxx +++ b/panda/src/pgraph/billboardEffect.cxx @@ -164,7 +164,7 @@ has_adjust_transform() const { void BillboardEffect:: adjust_transform(CPT(TransformState) &net_transform, CPT(TransformState) &node_transform, - PandaNode *) const { + const PandaNode *) const { // A BillboardEffect can only affect the net transform when it is to a // particular node. A billboard to a camera is camera-dependent, of course, // so it has no effect in the absence of any particular camera viewing it. diff --git a/panda/src/pgraph/billboardEffect.h b/panda/src/pgraph/billboardEffect.h index 2d528453f9..284182ba91 100644 --- a/panda/src/pgraph/billboardEffect.h +++ b/panda/src/pgraph/billboardEffect.h @@ -60,7 +60,7 @@ public: virtual bool has_adjust_transform() const; virtual void adjust_transform(CPT(TransformState) &net_transform, CPT(TransformState) &node_transform, - PandaNode *node) const; + const PandaNode *node) const; protected: virtual int compare_to_impl(const RenderEffect *other) const; diff --git a/panda/src/pgraph/camera.I b/panda/src/pgraph/camera.I index 7da5a9d4c9..0cfcd6277d 100644 --- a/panda/src/pgraph/camera.I +++ b/panda/src/pgraph/camera.I @@ -54,7 +54,7 @@ get_scene() const { /** * Returns the number of display regions associated with the camera. */ -INLINE int Camera:: +INLINE size_t Camera:: get_num_display_regions() const { return _display_regions.size(); } @@ -62,9 +62,9 @@ get_num_display_regions() const { /** * Returns the nth display region associated with the camera. */ -INLINE DisplayRegionBase *Camera:: -get_display_region(int n) const { - nassertr(n >= 0 && n < (int)_display_regions.size(), (DisplayRegionBase *)NULL); +INLINE DisplayRegion *Camera:: +get_display_region(size_t n) const { + nassertr(n < (int)_display_regions.size(), nullptr); return _display_regions[n]; } diff --git a/panda/src/pgraph/camera.cxx b/panda/src/pgraph/camera.cxx index d15e9a2bf3..aabf56eda2 100644 --- a/panda/src/pgraph/camera.cxx +++ b/panda/src/pgraph/camera.cxx @@ -236,7 +236,7 @@ cleanup_aux_scene_data(Thread *current_thread) { * camera. This is only intended to be called from the DisplayRegion. */ void Camera:: -add_display_region(DisplayRegionBase *display_region) { +add_display_region(DisplayRegion *display_region) { _display_regions.push_back(display_region); } @@ -245,7 +245,7 @@ add_display_region(DisplayRegionBase *display_region) { * by the camera. This is only intended to be called from the DisplayRegion. */ void Camera:: -remove_display_region(DisplayRegionBase *display_region) { +remove_display_region(DisplayRegion *display_region) { DisplayRegions::iterator dri = find(_display_regions.begin(), _display_regions.end(), display_region); if (dri != _display_regions.end()) { diff --git a/panda/src/pgraph/camera.h b/panda/src/pgraph/camera.h index 826000fbae..84b33bf55b 100644 --- a/panda/src/pgraph/camera.h +++ b/panda/src/pgraph/camera.h @@ -25,7 +25,8 @@ #include "pointerTo.h" #include "pmap.h" #include "auxSceneData.h" -#include "displayRegionBase.h" + +class DisplayRegion; /** * A node that can be positioned around in the scene graph to represent a @@ -52,8 +53,8 @@ PUBLISHED: INLINE const NodePath &get_scene() const; MAKE_PROPERTY(scene, get_scene, set_scene); - INLINE int get_num_display_regions() const; - INLINE DisplayRegionBase *get_display_region(int n) const; + INLINE size_t get_num_display_regions() const; + INLINE DisplayRegion *get_display_region(size_t n) const; MAKE_SEQ(get_display_regions, get_num_display_regions, get_display_region); MAKE_SEQ_PROPERTY(display_regions, get_num_display_regions, get_display_region); @@ -90,16 +91,20 @@ PUBLISHED: void clear_tag_states(); bool has_tag_state(const string &tag_state) const; CPT(RenderState) get_tag_state(const string &tag_state) const; + MAKE_MAP_PROPERTY(tag_states, has_tag_state, get_tag_state, + set_tag_state, clear_tag_state); void set_aux_scene_data(const NodePath &node_path, AuxSceneData *data); bool clear_aux_scene_data(const NodePath &node_path); AuxSceneData *get_aux_scene_data(const NodePath &node_path) const; void list_aux_scene_data(ostream &out) const; int cleanup_aux_scene_data(Thread *current_thread = Thread::get_current_thread()); + MAKE_MAP_PROPERTY(aux_scene_data, get_aux_scene_data, get_aux_scene_data, + set_aux_scene_data, clear_aux_scene_data); private: - void add_display_region(DisplayRegionBase *display_region); - void remove_display_region(DisplayRegionBase *display_region); + void add_display_region(DisplayRegion *display_region); + void remove_display_region(DisplayRegion *display_region); bool _active; NodePath _scene; @@ -110,7 +115,7 @@ private: DrawMask _camera_mask; PN_stdfloat _lod_scale; - typedef pvector DisplayRegions; + typedef pvector DisplayRegions; DisplayRegions _display_regions; CPT(RenderState) _initial_state; diff --git a/panda/src/pgraph/clipPlaneAttrib.cxx b/panda/src/pgraph/clipPlaneAttrib.cxx index e62a3d6e24..2b45be735a 100644 --- a/panda/src/pgraph/clipPlaneAttrib.cxx +++ b/panda/src/pgraph/clipPlaneAttrib.cxx @@ -213,7 +213,7 @@ make_default() { /** * Returns the basic operation type of the ClipPlaneAttrib. If this is O_set, * the planes listed here completely replace any planes that were already on. - * If this is O_add, the planes here are added to the set of of planes that + * If this is O_add, the planes here are added to the set of planes that * were already on, and if O_remove, the planes here are removed from the set * of planes that were on. * @@ -487,7 +487,7 @@ filter_to_max(int max_clip_planes) const { CPT(RenderAttrib) ClipPlaneAttrib:: compose_off(const RenderAttrib *other) const { const ClipPlaneAttrib *ta; - DCAST_INTO_R(ta, other, 0); + DCAST_INTO_R(ta, other, NULL); if (_off_all_planes || (!ta->_off_all_planes && ta->_off_planes.empty())) { // If we turn off all planes, or the other turns none off, the result is @@ -701,7 +701,7 @@ get_hash_impl() const { CPT(RenderAttrib) ClipPlaneAttrib:: compose_impl(const RenderAttrib *other) const { const ClipPlaneAttrib *ta; - DCAST_INTO_R(ta, other, 0); + DCAST_INTO_R(ta, other, NULL); if (ta->_off_all_planes) { // If the other type turns off all planes, it doesn't matter what we are. @@ -833,14 +833,6 @@ invert_compose_impl(const RenderAttrib *other) const { return other; } -/** - * - */ -CPT(RenderAttrib) ClipPlaneAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return this; -} - /** * This is patterned after TextureAttrib::sort_on_stages(), but since * planeNodes don't actually require sorting, this only empties the _filtered diff --git a/panda/src/pgraph/clipPlaneAttrib.h b/panda/src/pgraph/clipPlaneAttrib.h index c695e0391d..ca95233b79 100644 --- a/panda/src/pgraph/clipPlaneAttrib.h +++ b/panda/src/pgraph/clipPlaneAttrib.h @@ -98,7 +98,6 @@ protected: virtual size_t get_hash_impl() const; virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const; virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: INLINE void check_filtered() const; @@ -124,6 +123,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/colorAttrib.cxx b/panda/src/pgraph/colorAttrib.cxx index 7e007851fb..42e7c5578d 100644 --- a/panda/src/pgraph/colorAttrib.cxx +++ b/panda/src/pgraph/colorAttrib.cxx @@ -132,19 +132,6 @@ get_hash_impl() const { return hash; } -/** - * - */ -CPT(RenderAttrib) ColorAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - // For a ColorAttrib, the only relevant information is the type: is it flat- - // shaded or vertex-shaded? The actual color value is read by the shader - // from the graphics state. - - ColorAttrib *attrib = new ColorAttrib(_type, LColor(1.0f, 1.0f, 1.0f, 1.0f)); - return return_new(attrib); -} - /** * Quantizes the color color to the nearest multiple of 1000, just to prevent * runaway accumulation of only slightly-different ColorAttribs. diff --git a/panda/src/pgraph/colorAttrib.h b/panda/src/pgraph/colorAttrib.h index 54b57b8abb..df8c1b84ce 100644 --- a/panda/src/pgraph/colorAttrib.h +++ b/panda/src/pgraph/colorAttrib.h @@ -52,7 +52,6 @@ public: protected: virtual int compare_to_impl(const RenderAttrib *other) const; virtual size_t get_hash_impl() const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: void quantize_color(); @@ -70,6 +69,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/colorBlendAttrib.cxx b/panda/src/pgraph/colorBlendAttrib.cxx index 92fe98ab79..9341f2d808 100644 --- a/panda/src/pgraph/colorBlendAttrib.cxx +++ b/panda/src/pgraph/colorBlendAttrib.cxx @@ -148,14 +148,6 @@ get_hash_impl() const { return hash; } -/** - * - */ -CPT(RenderAttrib) ColorBlendAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return this; -} - /** * Tells the BamReader how to create objects of type ColorBlendAttrib. */ diff --git a/panda/src/pgraph/colorBlendAttrib.h b/panda/src/pgraph/colorBlendAttrib.h index 6d66747218..641633561c 100644 --- a/panda/src/pgraph/colorBlendAttrib.h +++ b/panda/src/pgraph/colorBlendAttrib.h @@ -121,7 +121,6 @@ public: protected: virtual int compare_to_impl(const RenderAttrib *other) const; virtual size_t get_hash_impl() const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: Mode _mode; @@ -139,6 +138,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/colorScaleAttrib.cxx b/panda/src/pgraph/colorScaleAttrib.cxx index 0975757604..79394a51f1 100644 --- a/panda/src/pgraph/colorScaleAttrib.cxx +++ b/panda/src/pgraph/colorScaleAttrib.cxx @@ -229,17 +229,6 @@ invert_compose_impl(const RenderAttrib *other) const { return return_new(attrib); } -/** - * - */ -CPT(RenderAttrib) ColorScaleAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - // A ColorScaleAttrib doesn't directly contribute to the auto-shader - // contents--instead, the shader is always written to query attr_colorscale - // at runtime. So the attrib itself means nothing to the shader. - return NULL; -} - /** * Quantizes the color scale to the nearest multiple of 1000, just to prevent * runaway accumulation of only slightly-different ColorScaleAttribs. diff --git a/panda/src/pgraph/colorScaleAttrib.h b/panda/src/pgraph/colorScaleAttrib.h index aa3ace8738..e8dded108b 100644 --- a/panda/src/pgraph/colorScaleAttrib.h +++ b/panda/src/pgraph/colorScaleAttrib.h @@ -55,7 +55,6 @@ protected: virtual size_t get_hash_impl() const; virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const; virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: void quantize_scale(); @@ -75,6 +74,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/colorWriteAttrib.h b/panda/src/pgraph/colorWriteAttrib.h index b7dcb7eb37..a426a01659 100644 --- a/panda/src/pgraph/colorWriteAttrib.h +++ b/panda/src/pgraph/colorWriteAttrib.h @@ -76,6 +76,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static TypeHandle get_class_type() { diff --git a/panda/src/pgraph/compassEffect.cxx b/panda/src/pgraph/compassEffect.cxx index c1e0cf24a8..d972e7f45a 100644 --- a/panda/src/pgraph/compassEffect.cxx +++ b/panda/src/pgraph/compassEffect.cxx @@ -157,7 +157,7 @@ has_adjust_transform() const { void CompassEffect:: adjust_transform(CPT(TransformState) &net_transform, CPT(TransformState) &node_transform, - PandaNode *) const { + const PandaNode *) const { if (_properties == 0) { // Nothing to do. return; diff --git a/panda/src/pgraph/compassEffect.h b/panda/src/pgraph/compassEffect.h index 60ec6d03e9..6600820bb1 100644 --- a/panda/src/pgraph/compassEffect.h +++ b/panda/src/pgraph/compassEffect.h @@ -78,7 +78,7 @@ public: virtual bool has_adjust_transform() const; virtual void adjust_transform(CPT(TransformState) &net_transform, CPT(TransformState) &node_transform, - PandaNode *node) const; + const PandaNode *node) const; protected: virtual int compare_to_impl(const RenderEffect *other) const; diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index 10b9fb04d3..237c1d0011 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -78,7 +78,6 @@ #include "scissorAttrib.h" #include "scissorEffect.h" #include "shadeModelAttrib.h" -#include "shaderInput.h" #include "shaderAttrib.h" #include "shader.h" #include "showBoundsEffect.h" @@ -228,7 +227,7 @@ ConfigVariableBool uniquify_states "are pointerwise equal. This may improve caching performance, " "but also adds additional overhead to maintain the cache, " "including the need to check for a composition cycle in " - "the cache.")); + "the cache. It is highly recommended to keep this on.")); ConfigVariableBool uniquify_attribs ("uniquify-attribs", true, @@ -449,7 +448,6 @@ init_libpgraph() { ScissorAttrib::init_type(); ScissorEffect::init_type(); ShadeModelAttrib::init_type(); - ShaderInput::init_type(); ShaderAttrib::init_type(); ShowBoundsEffect::init_type(); StateMunger::init_type(); @@ -502,7 +500,6 @@ init_libpgraph() { ScissorAttrib::register_with_read_factory(); ScissorEffect::register_with_read_factory(); ShadeModelAttrib::register_with_read_factory(); - ShaderInput::register_with_read_factory(); ShaderAttrib::register_with_read_factory(); ShowBoundsEffect::register_with_read_factory(); TexMatrixAttrib::register_with_read_factory(); diff --git a/panda/src/pgraph/cullBinAttrib.h b/panda/src/pgraph/cullBinAttrib.h index f17a9876c6..b3590ac1c5 100644 --- a/panda/src/pgraph/cullBinAttrib.h +++ b/panda/src/pgraph/cullBinAttrib.h @@ -57,6 +57,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/cullFaceAttrib.h b/panda/src/pgraph/cullFaceAttrib.h index 5f6589a52a..1ed0450db2 100644 --- a/panda/src/pgraph/cullFaceAttrib.h +++ b/panda/src/pgraph/cullFaceAttrib.h @@ -69,6 +69,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/cullPlanes.cxx b/panda/src/pgraph/cullPlanes.cxx index 782637c973..43a249403b 100644 --- a/panda/src/pgraph/cullPlanes.cxx +++ b/panda/src/pgraph/cullPlanes.cxx @@ -315,11 +315,10 @@ do_cull(int &result, CPT(RenderState) &state, result = BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some; - CPT(ClipPlaneAttrib) orig_cpa = DCAST(ClipPlaneAttrib, state->get_attrib(ClipPlaneAttrib::get_class_slot())); - CPT(CullPlanes) new_planes = this; - if (orig_cpa == (ClipPlaneAttrib *)NULL) { + const ClipPlaneAttrib *orig_cpa; + if (!state->get_attrib(orig_cpa)) { // If there are no clip planes in the state, the node is completely in // front of all zero of the clip planes. (This can happen if someone // directly changes the state during the traversal.) diff --git a/panda/src/pgraph/cullPlanes.h b/panda/src/pgraph/cullPlanes.h index 483eb2d30a..4b29fb7794 100644 --- a/panda/src/pgraph/cullPlanes.h +++ b/panda/src/pgraph/cullPlanes.h @@ -74,6 +74,10 @@ private: Occluders _occluders; }; +// We can safely redefine this as a no-op. +template<> +INLINE void PointerToBase::update_type(To *ptr) {} + #include "cullPlanes.I" #endif diff --git a/panda/src/pgraph/cullResult.cxx b/panda/src/pgraph/cullResult.cxx index 53751f07ca..0118888108 100644 --- a/panda/src/pgraph/cullResult.cxx +++ b/panda/src/pgraph/cullResult.cxx @@ -105,6 +105,8 @@ add_object(CullableObject *object, const CullTraverser *traverser) { static const LColor flash_multisample_color(0.78f, 0.05f, 0.81f, 1.0f); static const LColor flash_dual_color(0.92, 0.01f, 0.01f, 1.0f); + nassertv(object->_draw_callback != nullptr || object->_geom != nullptr); + bool force = !traverser->get_effective_incomplete_render(); Thread *current_thread = traverser->get_current_thread(); CullBinManager *bin_manager = CullBinManager::get_global_ptr(); diff --git a/panda/src/pgraph/cullTraverser.I b/panda/src/pgraph/cullTraverser.I index 180b94b37a..5da999d673 100644 --- a/panda/src/pgraph/cullTraverser.I +++ b/panda/src/pgraph/cullTraverser.I @@ -200,20 +200,14 @@ do_traverse(CullTraverserData &data) { if (is_in_view(data)) { if (pgraph_cat.is_spam()) { pgraph_cat.spam() - << "\n" << data._node_path + << "\n" << data.get_node_path() << " " << data._draw_mask << "\n"; } PandaNodePipelineReader *node_reader = data.node_reader(); int fancy_bits = node_reader->get_fancy_bits(); - if ((fancy_bits & (PandaNode::FB_transform | - PandaNode::FB_state | - PandaNode::FB_effects | - PandaNode::FB_tag | - PandaNode::FB_draw_mask | - PandaNode::FB_cull_callback)) == 0 && - data._cull_planes->is_empty()) { + if (fancy_bits == 0 && data._cull_planes->is_empty()) { // Nothing interesting in this node; just move on. } else { diff --git a/panda/src/pgraph/cullTraverser.cxx b/panda/src/pgraph/cullTraverser.cxx index 5d08b4e5dd..7ed990637b 100644 --- a/panda/src/pgraph/cullTraverser.cxx +++ b/panda/src/pgraph/cullTraverser.cxx @@ -113,10 +113,8 @@ traverse(const NodePath &root) { GeometricBoundingVolume *local_frustum = NULL; PT(BoundingVolume) bv = _scene_setup->get_lens()->make_bounds(); - if (bv != (BoundingVolume *)NULL && - bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - - local_frustum = DCAST(GeometricBoundingVolume, bv); + if (bv != nullptr) { + local_frustum = bv->as_geometric_bounding_volume(); } // This local_frustum is in camera space @@ -199,19 +197,18 @@ traverse_below(CullTraverserData &data) { PandaNode::Children children = node_reader->get_children(); node_reader->release(); int num_children = children.get_num_children(); - if (node->has_selective_visibility()) { + if (!node->has_selective_visibility()) { + for (int i = 0; i < num_children; ++i) { + CullTraverserData next_data(data, children.get_child(i)); + do_traverse(next_data); + } + } else { int i = node->get_first_visible_child(); while (i < num_children) { CullTraverserData next_data(data, children.get_child(i)); do_traverse(next_data); i = node->get_next_visible_child(i); } - - } else { - for (int i = 0; i < num_children; i++) { - CullTraverserData next_data(data, children.get_child(i)); - do_traverse(next_data); - } } } @@ -240,7 +237,7 @@ draw_bounding_volume(const BoundingVolume *vol, _cull_handler->record_object(outer_viz, this); CullableObject *inner_viz = - new CullableObject(bounds_viz, get_bounds_inner_viz_state(), + new CullableObject(move(bounds_viz), get_bounds_inner_viz_state(), internal_transform); _cull_handler->record_object(inner_viz, this); } @@ -270,7 +267,7 @@ show_bounds(CullTraverserData &data, bool tight) { if (bounds_viz != (Geom *)NULL) { _geoms_pcollector.add_level(1); CullableObject *outer_viz = - new CullableObject(bounds_viz, get_bounds_outer_viz_state(), + new CullableObject(move(bounds_viz), get_bounds_outer_viz_state(), internal_transform); _cull_handler->record_object(outer_viz, this); } @@ -281,7 +278,7 @@ show_bounds(CullTraverserData &data, bool tight) { if (node->is_geom_node()) { // Also show the bounding volumes of included Geoms. internal_transform = internal_transform->compose(node->get_transform()); - GeomNode *gnode = DCAST(GeomNode, node); + GeomNode *gnode = (GeomNode *)node; int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; ++i) { draw_bounding_volume(gnode->get_geom(i)->get_bounds(), @@ -334,29 +331,31 @@ make_bounds_viz(const BoundingVolume *vol) { const BoundingHexahedron *fvol = DCAST(BoundingHexahedron, vol); PT(GeomVertexData) vdata = new GeomVertexData - ("bounds", GeomVertexFormat::get_v3(), - Geom::UH_stream); - GeomVertexWriter vertex(vdata, InternalName::get_vertex()); + ("bounds", GeomVertexFormat::get_v3(), Geom::UH_stream); + vdata->unclean_set_num_rows(8); - for (int i = 0; i < 8; ++i ) { - vertex.add_data3(fvol->get_point(i)); + { + GeomVertexWriter vertex(vdata, InternalName::get_vertex()); + for (int i = 0; i < 8; ++i) { + vertex.set_data3(fvol->get_point(i)); + } } PT(GeomLines) lines = new GeomLines(Geom::UH_stream); - lines->add_vertices(0, 1); lines->close_primitive(); - lines->add_vertices(1, 2); lines->close_primitive(); - lines->add_vertices(2, 3); lines->close_primitive(); - lines->add_vertices(3, 0); lines->close_primitive(); + lines->add_vertices(0, 1); + lines->add_vertices(1, 2); + lines->add_vertices(2, 3); + lines->add_vertices(3, 0); - lines->add_vertices(4, 5); lines->close_primitive(); - lines->add_vertices(5, 6); lines->close_primitive(); - lines->add_vertices(6, 7); lines->close_primitive(); - lines->add_vertices(7, 4); lines->close_primitive(); + lines->add_vertices(4, 5); + lines->add_vertices(5, 6); + lines->add_vertices(6, 7); + lines->add_vertices(7, 4); - lines->add_vertices(0, 4); lines->close_primitive(); - lines->add_vertices(1, 5); lines->close_primitive(); - lines->add_vertices(2, 6); lines->close_primitive(); - lines->add_vertices(3, 7); lines->close_primitive(); + lines->add_vertices(0, 4); + lines->add_vertices(1, 5); + lines->add_vertices(2, 6); + lines->add_vertices(3, 7); geom = new Geom(vdata); geom->add_primitive(lines); @@ -368,39 +367,29 @@ make_bounds_viz(const BoundingVolume *vol) { box.local_object(); PT(GeomVertexData) vdata = new GeomVertexData - ("bounds", GeomVertexFormat::get_v3(), - Geom::UH_stream); - GeomVertexWriter vertex(vdata, InternalName::get_vertex()); + ("bounds", GeomVertexFormat::get_v3(), Geom::UH_stream); + vdata->unclean_set_num_rows(8); - for (int i = 0; i < 8; ++i ) { - vertex.add_data3(box.get_point(i)); + { + GeomVertexWriter vertex(vdata, InternalName::get_vertex()); + for (int i = 0; i < 8; ++i) { + vertex.set_data3(box.get_point(i)); + } } PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_stream); tris->add_vertices(0, 4, 5); - tris->close_primitive(); tris->add_vertices(0, 5, 1); - tris->close_primitive(); tris->add_vertices(4, 6, 7); - tris->close_primitive(); tris->add_vertices(4, 7, 5); - tris->close_primitive(); tris->add_vertices(6, 2, 3); - tris->close_primitive(); tris->add_vertices(6, 3, 7); - tris->close_primitive(); tris->add_vertices(2, 0, 1); - tris->close_primitive(); tris->add_vertices(2, 1, 3); - tris->close_primitive(); tris->add_vertices(1, 5, 7); - tris->close_primitive(); tris->add_vertices(1, 7, 3); - tris->close_primitive(); tris->add_vertices(2, 6, 4); - tris->close_primitive(); tris->add_vertices(2, 4, 0); - tris->close_primitive(); geom = new Geom(vdata); geom->add_primitive(tris); @@ -430,19 +419,21 @@ make_tight_bounds_viz(PandaNode *node) const { _current_thread); if (found_any) { PT(GeomVertexData) vdata = new GeomVertexData - ("bounds", GeomVertexFormat::get_v3(), - Geom::UH_stream); - GeomVertexWriter vertex(vdata, InternalName::get_vertex(), - _current_thread); + ("bounds", GeomVertexFormat::get_v3(), Geom::UH_stream); + vdata->unclean_set_num_rows(8); - vertex.add_data3(n[0], n[1], n[2]); - vertex.add_data3(n[0], n[1], x[2]); - vertex.add_data3(n[0], x[1], n[2]); - vertex.add_data3(n[0], x[1], x[2]); - vertex.add_data3(x[0], n[1], n[2]); - vertex.add_data3(x[0], n[1], x[2]); - vertex.add_data3(x[0], x[1], n[2]); - vertex.add_data3(x[0], x[1], x[2]); + { + GeomVertexWriter vertex(vdata, InternalName::get_vertex(), + _current_thread); + vertex.set_data3(n[0], n[1], n[2]); + vertex.set_data3(n[0], n[1], x[2]); + vertex.set_data3(n[0], x[1], n[2]); + vertex.set_data3(n[0], x[1], x[2]); + vertex.set_data3(x[0], n[1], n[2]); + vertex.set_data3(x[0], n[1], x[2]); + vertex.set_data3(x[0], x[1], n[2]); + vertex.set_data3(x[0], x[1], x[2]); + } PT(GeomLinestrips) strip = new GeomLinestrips(Geom::UH_stream); diff --git a/panda/src/pgraph/cullTraverserData.I b/panda/src/pgraph/cullTraverserData.I index 4367d1608e..e2a9a64c95 100644 --- a/panda/src/pgraph/cullTraverserData.I +++ b/panda/src/pgraph/cullTraverserData.I @@ -20,7 +20,8 @@ CullTraverserData(const NodePath &start, const RenderState *state, GeometricBoundingVolume *view_frustum, Thread *current_thread) : - _node_path(start), + _next(nullptr), + _start(start._head), _node_reader(start.node(), current_thread), _net_transform(net_transform), _state(state), @@ -34,44 +35,16 @@ CullTraverserData(const NodePath &start, _node_reader.check_cached(check_bounds); } -/** - * - */ -INLINE CullTraverserData:: -CullTraverserData(const CullTraverserData ©) : - _node_path(copy._node_path), - _node_reader(copy._node_reader), - _net_transform(copy._net_transform), - _state(copy._state), - _view_frustum(copy._view_frustum), - _cull_planes(copy._cull_planes), - _draw_mask(copy._draw_mask), - _portal_depth(copy._portal_depth) -{ -} - -/** - * - */ -INLINE void CullTraverserData:: -operator = (const CullTraverserData ©) { - _node_path = copy._node_path; - _node_reader = copy._node_reader; - _net_transform = copy._net_transform; - _state = copy._state; - _view_frustum = copy._view_frustum; - _cull_planes = copy._cull_planes; - _draw_mask = copy._draw_mask; - _portal_depth = copy._portal_depth; -} - /** * This constructor creates a CullTraverserData object that reflects the next * node down in the traversal. */ INLINE CullTraverserData:: CullTraverserData(const CullTraverserData &parent, PandaNode *child) : - _node_path(parent._node_path, child), + _next(&parent), +#ifdef _DEBUG + _start(nullptr), +#endif _node_reader(child, parent._node_reader.get_current_thread()), _net_transform(parent._net_transform), _state(parent._state), @@ -86,19 +59,12 @@ CullTraverserData(const CullTraverserData &parent, PandaNode *child) : _node_reader.check_cached(check_bounds); } -/** - * - */ -INLINE CullTraverserData:: -~CullTraverserData() { -} - /** * Returns the node traversed to so far. */ INLINE PandaNode *CullTraverserData:: node() const { - return _node_path.node(); + return (PandaNode *)_node_reader.get_node(); } /** @@ -117,6 +83,18 @@ node_reader() const { return &_node_reader; } +/** + * Constructs and returns an actual NodePath that represents the same path we + * have just traversed. + */ +INLINE NodePath CullTraverserData:: +get_node_path() const { + NodePath result; + result._head = r_get_node_path(); + nassertr(result._head != nullptr, NodePath::fail()); + return result; +} + /** * Returns the modelview transform: the relative transform from the camera to * the model. diff --git a/panda/src/pgraph/cullTraverserData.cxx b/panda/src/pgraph/cullTraverserData.cxx index d5ad3cba4f..1a8280948f 100644 --- a/panda/src/pgraph/cullTraverserData.cxx +++ b/panda/src/pgraph/cullTraverserData.cxx @@ -46,25 +46,33 @@ apply_transform_and_state(CullTraverser *trav) { } _node_reader.compose_draw_mask(_draw_mask); - apply_transform_and_state(trav, _node_reader.get_transform(), - MOVE(node_state), _node_reader.get_effects(), - _node_reader.get_off_clip_planes()); + const RenderEffects *node_effects = _node_reader.get_effects(); + if (!node_effects->has_cull_callback()) { + apply_transform(_node_reader.get_transform()); + } else { + // The cull callback may decide to modify the node_transform. + CPT(TransformState) node_transform = _node_reader.get_transform(); + node_effects->cull_callback(trav, *this, node_transform, node_state); + apply_transform(node_transform); + } + + if (!node_state->is_empty()) { + _state = _state->compose(node_state); + } + + if (clip_plane_cull) { + _cull_planes = _cull_planes->apply_state(trav, this, + (const ClipPlaneAttrib *)node_state->get_attrib(ClipPlaneAttrib::get_class_slot()), + (const ClipPlaneAttrib *)_node_reader.get_off_clip_planes(), + (const OccluderEffect *)node_effects->get_effect(OccluderEffect::get_class_type())); + } } /** - * Applies the indicated transform and state changes (e.g. as extracted from - * a node) onto the current data. This also evaluates billboards, etc. + * Applies the indicated transform changes onto the current data. */ void CullTraverserData:: -apply_transform_and_state(CullTraverser *trav, - CPT(TransformState) node_transform, - CPT(RenderState) node_state, - CPT(RenderEffects) node_effects, - const RenderAttrib *off_clip_planes) { - if (node_effects->has_cull_callback()) { - node_effects->cull_callback(trav, *this, node_transform, node_state); - } - +apply_transform(const TransformState *node_transform) { if (!node_transform->is_identity()) { _net_transform = _net_transform->compose(node_transform); @@ -95,15 +103,40 @@ apply_transform_and_state(CullTraverser *trav, } } } +} - _state = _state->compose(node_state); - - if (clip_plane_cull) { - _cull_planes = _cull_planes->apply_state(trav, this, - (const ClipPlaneAttrib *)node_state->get_attrib(ClipPlaneAttrib::get_class_slot()), - DCAST(ClipPlaneAttrib, off_clip_planes), - (const OccluderEffect *)node_effects->get_effect(OccluderEffect::get_class_type())); +/** + * The private, recursive implementation of get_node_path(), this returns the + * NodePathComponent representing the NodePath. + */ +PT(NodePathComponent) CullTraverserData:: +r_get_node_path() const { + if (_next == nullptr) { + nassertr(_start != nullptr, nullptr); + return _start; } + +#ifdef _DEBUG + nassertr(_start == nullptr, nullptr); +#endif + nassertr(node() != nullptr, nullptr); + + PT(NodePathComponent) comp = _next->r_get_node_path(); + nassertr(comp != nullptr, nullptr); + + Thread *current_thread = Thread::get_current_thread(); + int pipeline_stage = current_thread->get_pipeline_stage(); + PT(NodePathComponent) result = + PandaNode::get_component(comp, node(), pipeline_stage, current_thread); + if (result == nullptr) { + // This means we found a disconnected chain in the CullTraverserData's + // ancestry: the node above this node isn't connected. In this case, + // don't attempt to go higher; just truncate the NodePath at the bottom of + // the disconnect. + return PandaNode::get_top_component(node(), true, pipeline_stage, current_thread); + } + + return result; } /** @@ -121,7 +154,7 @@ is_in_view_impl() { if (pgraph_cat.is_spam()) { pgraph_cat.spam() - << _node_path << " cull result = " << hex << result << dec << "\n"; + << get_node_path() << " cull result = " << hex << result << dec << "\n"; } if (result == BoundingVolume::IF_no_intersection) { @@ -136,8 +169,7 @@ is_in_view_impl() { // If we have fake view-frustum culling enabled, instead of actually // culling an object we simply force it to be drawn in red wireframe. _view_frustum = (GeometricBoundingVolume *)NULL; - CPT(RenderState) fake_state = get_fake_view_frustum_cull_state(); - _state = _state->compose(fake_state); + _state = _state->compose(get_fake_view_frustum_cull_state()); #endif } else if ((result & BoundingVolume::IF_all) != 0) { @@ -170,7 +202,7 @@ is_in_view_impl() { if (pgraph_cat.is_spam()) { pgraph_cat.spam() - << _node_path << " cull planes cull result = " << hex + << get_node_path() << " cull planes cull result = " << hex << result << dec << "\n"; _cull_planes->write(pgraph_cat.spam(false)); } @@ -182,7 +214,7 @@ is_in_view_impl() { if (pgraph_cat.is_spam()) { pgraph_cat.spam() - << _node_path << " is_final, cull planes disabled, state:\n"; + << get_node_path() << " is_final, cull planes disabled, state:\n"; _state->write(pgraph_cat.spam(false), 2); } } @@ -196,8 +228,7 @@ is_in_view_impl() { return false; } _cull_planes = CullPlanes::make_empty(); - CPT(RenderState) fake_state = get_fake_view_frustum_cull_state(); - _state = _state->compose(fake_state); + _state = _state->compose(get_fake_view_frustum_cull_state()); #endif } else if ((result & BoundingVolume::IF_all) != 0) { @@ -215,15 +246,15 @@ is_in_view_impl() { * Returns a RenderState for rendering stuff in red wireframe, strictly for * the fake_view_frustum_cull effect. */ -CPT(RenderState) CullTraverserData:: +const RenderState *CullTraverserData:: get_fake_view_frustum_cull_state() { #ifdef NDEBUG - return NULL; + return nullptr; #else // Once someone asks for this pointer, we hold its reference count and never // free it. - static CPT(RenderState) state = (const RenderState *)NULL; - if (state == (const RenderState *)NULL) { + static CPT(RenderState) state; + if (state == nullptr) { state = RenderState::make (ColorAttrib::make_flat(LColor(1.0f, 0.0f, 0.0f, 1.0f)), TextureAttrib::make_all_off(), diff --git a/panda/src/pgraph/cullTraverserData.h b/panda/src/pgraph/cullTraverserData.h index 80d523fe0c..e275dcfd93 100644 --- a/panda/src/pgraph/cullTraverserData.h +++ b/panda/src/pgraph/cullTraverserData.h @@ -44,11 +44,8 @@ public: const RenderState *state, GeometricBoundingVolume *view_frustum, Thread *current_thread); - INLINE CullTraverserData(const CullTraverserData ©); - INLINE void operator = (const CullTraverserData ©); INLINE CullTraverserData(const CullTraverserData &parent, PandaNode *child); - INLINE ~CullTraverserData(); PUBLISHED: INLINE PandaNode *node() const; @@ -57,6 +54,8 @@ public: INLINE PandaNodePipelineReader *node_reader(); INLINE const PandaNodePipelineReader *node_reader() const; + INLINE NodePath get_node_path() const; + PUBLISHED: INLINE CPT(TransformState) get_modelview_transform(const CullTraverser *trav) const; INLINE CPT(TransformState) get_internal_transform(const CullTraverser *trav) const; @@ -66,14 +65,15 @@ PUBLISHED: INLINE bool is_this_node_hidden(const DrawMask &camera_mask) const; void apply_transform_and_state(CullTraverser *trav); - void apply_transform_and_state(CullTraverser *trav, - CPT(TransformState) node_transform, - CPT(RenderState) node_state, - CPT(RenderEffects) node_effects, - const RenderAttrib *off_clip_planes); + void apply_transform(const TransformState *node_transform); + +private: + // We store a chain leading all the way to the root, so that we can compose + // a NodePath. We may be able to eliminate this requirement in the future. + const CullTraverserData *_next; + NodePathComponent *_start; public: - WorkingNodePath _node_path; PandaNodePipelineReader _node_reader; CPT(TransformState) _net_transform; CPT(RenderState) _state; @@ -83,8 +83,10 @@ public: int _portal_depth; private: + PT(NodePathComponent) r_get_node_path() const; + bool is_in_view_impl(); - static CPT(RenderState) get_fake_view_frustum_cull_state(); + static const RenderState *get_fake_view_frustum_cull_state(); }; /* okcircular */ diff --git a/panda/src/pgraph/cullableObject.I b/panda/src/pgraph/cullableObject.I index 242496587f..e5e7229d09 100644 --- a/panda/src/pgraph/cullableObject.I +++ b/panda/src/pgraph/cullableObject.I @@ -17,7 +17,7 @@ INLINE CullableObject:: CullableObject() { #ifdef DO_MEMORY_USAGE - MemoryUsage::update_type(this, get_class_type()); + MemoryUsage::record_pointer(this, get_class_type()); #endif } @@ -26,14 +26,14 @@ CullableObject() { * render state and transform. */ INLINE CullableObject:: -CullableObject(const Geom *geom, const RenderState *state, - const TransformState *internal_transform) : - _geom(geom), - _state(state), - _internal_transform(internal_transform) +CullableObject(CPT(Geom) geom, CPT(RenderState) state, + CPT(TransformState) internal_transform) : + _geom(move(geom)), + _state(move(state)), + _internal_transform(move(internal_transform)) { #ifdef DO_MEMORY_USAGE - MemoryUsage::update_type(this, get_class_type()); + MemoryUsage::record_pointer(this, get_class_type()); #endif } @@ -43,13 +43,12 @@ CullableObject(const Geom *geom, const RenderState *state, INLINE CullableObject:: CullableObject(const CullableObject ©) : _geom(copy._geom), - _munger(copy._munger), _munged_data(copy._munged_data), _state(copy._state), _internal_transform(copy._internal_transform) { #ifdef DO_MEMORY_USAGE - MemoryUsage::update_type(this, get_class_type()); + MemoryUsage::record_pointer(this, get_class_type()); #endif } @@ -59,7 +58,6 @@ CullableObject(const CullableObject ©) : INLINE void CullableObject:: operator = (const CullableObject ©) { _geom = copy._geom; - _munger = copy._munger; _munged_data = copy._munged_data; _state = copy._state; _internal_transform = copy._internal_transform; @@ -132,7 +130,22 @@ flush_level() { */ INLINE void CullableObject:: draw_inline(GraphicsStateGuardianBase *gsg, bool force, Thread *current_thread) { - _geom->draw(gsg, _munger, _munged_data, force, current_thread); + _geom->draw(gsg, _munged_data, force, current_thread); +} + +/** + * Invokes the draw callback, assuming one is set. Crashes if not. + */ +INLINE void CullableObject:: +draw_callback(GraphicsStateGuardianBase *gsg, bool force, Thread *current_thread) { + gsg->clear_before_callback(); + gsg->set_state_and_transform(_state, _internal_transform); + GeomDrawCallbackData cbdata(this, gsg, force); + _draw_callback->do_callback(&cbdata); + if (cbdata.get_lost_state()) { + // Tell the GSG to forget its state. + gsg->clear_state_and_transform(); + } } /** diff --git a/panda/src/pgraph/cullableObject.cxx b/panda/src/pgraph/cullableObject.cxx index 0bd7a0fe5f..d38fc4f5a4 100644 --- a/panda/src/pgraph/cullableObject.cxx +++ b/panda/src/pgraph/cullableObject.cxx @@ -34,6 +34,7 @@ CullableObject::FormatMap CullableObject::_format_map; LightMutex CullableObject::_format_lock; +PStatCollector CullableObject::_munge_pcollector("*:Munge"); PStatCollector CullableObject::_munge_geom_pcollector("*:Munge:Geom"); PStatCollector CullableObject::_munge_sprites_pcollector("*:Munge:Sprites"); PStatCollector CullableObject::_munge_sprites_verts_pcollector("*:Munge:Sprites:Verts"); @@ -50,15 +51,13 @@ TypeHandle CullableObject::_type_handle; * have to block while the vertex data is paged in. */ bool CullableObject:: -munge_geom(GraphicsStateGuardianBase *gsg, - GeomMunger *munger, const CullTraverser *traverser, - bool force) { - nassertr(munger != (GeomMunger *)NULL, false); - Thread *current_thread = traverser->get_current_thread(); - PStatTimer timer(_munge_geom_pcollector, current_thread); - if (_geom != (Geom *)NULL) { - _munger = munger; +munge_geom(GraphicsStateGuardianBase *gsg, GeomMunger *munger, + const CullTraverser *traverser, bool force) { + nassertr(munger != nullptr, false); + Thread *current_thread = traverser->get_current_thread(); + PStatTimer timer(_munge_pcollector, current_thread); + if (_geom != nullptr) { GraphicsStateGuardianBase *gsg = traverser->get_gsg(); int gsg_bits = gsg->get_supported_geom_rendering(); if (!hardware_point_sprites) { @@ -125,8 +124,11 @@ munge_geom(GraphicsStateGuardianBase *gsg, // Now invoke the munger to ensure the resulting geometry is in a GSG- // friendly form. - if (!munger->munge_geom(_geom, _munged_data, force, current_thread)) { - return false; + { + PStatTimer timer(_munge_geom_pcollector, current_thread); + if (!munger->munge_geom(_geom, _munged_data, force, current_thread)) { + return false; + } } // If we have prepared it for skinning via the shader generator, mark a @@ -140,10 +142,15 @@ munge_geom(GraphicsStateGuardianBase *gsg, DCAST(ShaderAttrib, ShaderAttrib::make())->set_flag(ShaderAttrib::F_hardware_skinning, true)); _state = _state->compose(state); } - } - StateMunger *state_munger = (StateMunger *)munger; - _state = state_munger->munge_state(_state); + gsg->ensure_generated_shader(_state); + } else { + // We may need to munge the state for the fixed-function pipeline. + StateMunger *state_munger = (StateMunger *)munger; + if (state_munger->should_munge_state()) { + _state = state_munger->munge_state(_state); + } + } // If there is any animation left in the vertex data after it has been // munged--that is, we couldn't arrange to handle the animation in diff --git a/panda/src/pgraph/cullableObject.h b/panda/src/pgraph/cullableObject.h index 82fd0df3a5..b02d7c79be 100644 --- a/panda/src/pgraph/cullableObject.h +++ b/panda/src/pgraph/cullableObject.h @@ -18,11 +18,9 @@ #include "geom.h" #include "geomVertexData.h" -#include "geomMunger.h" #include "renderState.h" #include "transformState.h" #include "pointerTo.h" -#include "referenceCount.h" #include "geomNode.h" #include "cullTraverserData.h" #include "pStatCollector.h" @@ -34,27 +32,23 @@ #include "geomDrawCallbackData.h" class CullTraverser; +class GeomMunger; /** * The smallest atom of cull. This is normally just a Geom and its associated * state, but it also contain a draw callback. */ -class EXPCL_PANDA_PGRAPH CullableObject -#ifdef DO_MEMORY_USAGE - : public ReferenceCount // We inherit from ReferenceCount just to get the memory type tracking that MemoryUsage provides. -#endif // DO_MEMORY_USAGE -{ +class EXPCL_PANDA_PGRAPH CullableObject { public: INLINE CullableObject(); - INLINE CullableObject(const Geom *geom, const RenderState *state, - const TransformState *internal_transform); + INLINE CullableObject(CPT(Geom) geom, CPT(RenderState) state, + CPT(TransformState) internal_transform); INLINE CullableObject(const CullableObject ©); INLINE void operator = (const CullableObject ©); - bool munge_geom(GraphicsStateGuardianBase *gsg, - GeomMunger *munger, const CullTraverser *traverser, - bool force); + bool munge_geom(GraphicsStateGuardianBase *gsg, GeomMunger *munger, + const CullTraverser *traverser, bool force); INLINE void draw(GraphicsStateGuardianBase *gsg, bool force, Thread *current_thread); @@ -63,6 +57,11 @@ public: INLINE void set_draw_callback(CallbackObject *draw_callback); + INLINE void draw_inline(GraphicsStateGuardianBase *gsg, + bool force, Thread *current_thread); + INLINE void draw_callback(GraphicsStateGuardianBase *gsg, + bool force, Thread *current_thread); + public: ALLOC_DELETED_CHAIN(CullableObject); @@ -70,7 +69,6 @@ public: public: CPT(Geom) _geom; - PT(GeomMunger) _munger; CPT(GeomVertexData) _munged_data; CPT(RenderState) _state; CPT(TransformState) _internal_transform; @@ -82,9 +80,6 @@ private: static CPT(RenderState) get_flash_cpu_state(); static CPT(RenderState) get_flash_hardware_state(); - INLINE void draw_inline(GraphicsStateGuardianBase *gsg, - bool force, Thread *current_thread); - private: // This class is used internally by munge_points_to_quads(). class PointData { @@ -113,6 +108,7 @@ private: static FormatMap _format_map; static LightMutex _format_lock; + static PStatCollector _munge_pcollector; static PStatCollector _munge_geom_pcollector; static PStatCollector _munge_sprites_pcollector; static PStatCollector _munge_sprites_verts_pcollector; @@ -124,13 +120,7 @@ public: return _type_handle; } static void init_type() { -#ifdef DO_MEMORY_USAGE - ReferenceCount::init_type(); - register_type(_type_handle, "CullableObject", - ReferenceCount::get_class_type()); -#else register_type(_type_handle, "CullableObject"); -#endif // DO_MEMORY_USAGE } private: diff --git a/panda/src/pgraph/depthOffsetAttrib.h b/panda/src/pgraph/depthOffsetAttrib.h index 0976e2025a..9ae6a6ce6f 100644 --- a/panda/src/pgraph/depthOffsetAttrib.h +++ b/panda/src/pgraph/depthOffsetAttrib.h @@ -86,6 +86,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/depthTestAttrib.h b/panda/src/pgraph/depthTestAttrib.h index 6380a197d9..2dfa4763e1 100644 --- a/panda/src/pgraph/depthTestAttrib.h +++ b/panda/src/pgraph/depthTestAttrib.h @@ -53,6 +53,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/depthWriteAttrib.h b/panda/src/pgraph/depthWriteAttrib.h index 9cd96b40f9..d1cdbdc98f 100644 --- a/panda/src/pgraph/depthWriteAttrib.h +++ b/panda/src/pgraph/depthWriteAttrib.h @@ -59,6 +59,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/fog.h b/panda/src/pgraph/fog.h index 7a7831eb9e..3eb75603ba 100644 --- a/panda/src/pgraph/fog.h +++ b/panda/src/pgraph/fog.h @@ -40,7 +40,7 @@ class TransformState; */ class EXPCL_PANDA_PGRAPH Fog : public PandaNode { PUBLISHED: - Fog(const string &name); + explicit Fog(const string &name); protected: Fog(const Fog ©); diff --git a/panda/src/pgraph/fogAttrib.cxx b/panda/src/pgraph/fogAttrib.cxx index 0606298fa4..87e12086d4 100644 --- a/panda/src/pgraph/fogAttrib.cxx +++ b/panda/src/pgraph/fogAttrib.cxx @@ -100,14 +100,6 @@ get_hash_impl() const { return hash; } -/** - * - */ -CPT(RenderAttrib) FogAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return this; -} - /** * Tells the BamReader how to create objects of type FogAttrib. */ diff --git a/panda/src/pgraph/fogAttrib.h b/panda/src/pgraph/fogAttrib.h index 28a84600e0..7f659b6336 100644 --- a/panda/src/pgraph/fogAttrib.h +++ b/panda/src/pgraph/fogAttrib.h @@ -43,7 +43,6 @@ public: protected: virtual int compare_to_impl(const RenderAttrib *other) const; virtual size_t get_hash_impl() const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: PT(Fog) _fog; @@ -55,6 +54,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/geomDrawCallbackData.cxx b/panda/src/pgraph/geomDrawCallbackData.cxx index 283fe9711c..1b6dc76082 100644 --- a/panda/src/pgraph/geomDrawCallbackData.cxx +++ b/panda/src/pgraph/geomDrawCallbackData.cxx @@ -39,13 +39,13 @@ output(ostream &out) const { void GeomDrawCallbackData:: upcall() { // Go ahead and draw the object, if we have one. - if (_obj->_geom != (Geom *)NULL) { + if (_obj->_geom != nullptr) { if (_lost_state) { // Tell the GSG to forget its state. _gsg->clear_state_and_transform(); } - _obj->_geom->draw(_gsg, _obj->_munger, _obj->_munged_data, _force, + _obj->_geom->draw(_gsg, _obj->_munged_data, _force, Thread::get_current_thread()); } } diff --git a/panda/src/pgraph/geomNode.cxx b/panda/src/pgraph/geomNode.cxx index f0000d4ebb..8c9da5d331 100644 --- a/panda/src/pgraph/geomNode.cxx +++ b/panda/src/pgraph/geomNode.cxx @@ -15,6 +15,7 @@ #include "geom.h" #include "geomTransformer.h" #include "sceneGraphReducer.h" +#include "stateMunger.h" #include "accumulatedAttribs.h" #include "colorAttrib.h" #include "colorScaleAttrib.h" @@ -37,6 +38,7 @@ #include "boundingBox.h" #include "boundingSphere.h" #include "config_mathutil.h" +#include "preparedGraphicsObjects.h" bool allow_flatten_color = ConfigVariableBool @@ -381,43 +383,42 @@ r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, int num_arrays = vdata_reader.get_num_arrays(); for (int i = 0; i < num_arrays; ++i) { CPT(GeomVertexArrayData) array = vdata_reader.get_array(i); - ((GeomVertexArrayData *)array.p())->prepare(prepared_objects); + prepared_objects->enqueue_vertex_buffer((GeomVertexArrayData *)array.p()); } // And also each of the index arrays. int num_primitives = geom->get_num_primitives(); for (int i = 0; i < num_primitives; ++i) { CPT(GeomPrimitive) prim = geom->get_primitive(i); - ((GeomPrimitive *)prim.p())->prepare(prepared_objects); + prepared_objects->enqueue_index_buffer((GeomPrimitive *)prim.p()); + } + + if (munger->is_of_type(StateMunger::get_class_type())) { + StateMunger *state_munger = (StateMunger *)munger.p(); + geom_state = state_munger->munge_state(geom_state); } // And now prepare each of the textures. - const RenderAttrib *attrib = - geom_state->get_attrib(TextureAttrib::get_class_slot()); - if (attrib != (const RenderAttrib *)NULL) { - const TextureAttrib *ta; - DCAST_INTO_V(ta, attrib); + const TextureAttrib *ta; + if (geom_state->get_attrib(ta)) { int num_stages = ta->get_num_on_stages(); for (int i = 0; i < num_stages; ++i) { Texture *texture = ta->get_on_texture(ta->get_on_stage(i)); // TODO: prepare the sampler states, if specified. - if (texture != (Texture *)NULL) { - texture->prepare(prepared_objects); + if (texture != nullptr) { + prepared_objects->enqueue_texture(texture); } } } // As well as the shaders. - attrib = geom_state->get_attrib(ShaderAttrib::get_class_slot()); - if (attrib != (const RenderAttrib *)NULL) { - const ShaderAttrib *sa; - DCAST_INTO_V(sa, attrib); + const ShaderAttrib *sa; + if (geom_state->get_attrib(sa)) { Shader *shader = (Shader *)sa->get_shader(); - if (shader != (Shader *)NULL) { - shader->prepare(prepared_objects); + if (shader != nullptr) { + prepared_objects->enqueue_shader(shader); } - // TODO: prepare the shader inputs. TODO: Invoke the shader generator - // if enabled. + // TODO: prepare the shader inputs. } } @@ -515,7 +516,7 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) { CPT(TransformState) internal_transform = data.get_internal_transform(trav); for (int i = 0; i < num_geoms; i++) { - const Geom *geom = geoms.get_geom(i); + CPT(Geom) geom = geoms.get_geom(i); if (geom->is_empty()) { continue; } @@ -558,7 +559,7 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) { } CullableObject *object = - new CullableObject(geom, state, internal_transform); + new CullableObject(move(geom), move(state), internal_transform); trav->get_cull_handler()->record_object(object, trav); } } diff --git a/panda/src/pgraph/geomNode.h b/panda/src/pgraph/geomNode.h index a490a914b4..d17903027a 100644 --- a/panda/src/pgraph/geomNode.h +++ b/panda/src/pgraph/geomNode.h @@ -89,6 +89,7 @@ PUBLISHED: void write_verbose(ostream &out, int indent_level) const; INLINE static CollideMask get_default_collide_mask(); + MAKE_PROPERTY(default_collide_mask, get_default_collide_mask); public: virtual void output(ostream &out) const; diff --git a/panda/src/pgraph/geomTransformer.cxx b/panda/src/pgraph/geomTransformer.cxx index 10648b7686..65c1ca6c80 100644 --- a/panda/src/pgraph/geomTransformer.cxx +++ b/panda/src/pgraph/geomTransformer.cxx @@ -151,7 +151,7 @@ transform_vertices(GeomNode *node, const LMatrix4 &mat) { GeomNode::GeomEntry &entry = (*gi); PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy(); if (transform_vertices(new_geom, mat)) { - entry._geom = new_geom; + entry._geom = move(new_geom); any_changed = true; } } @@ -1243,16 +1243,14 @@ apply_collect_changes() { void GeomTransformer::NewCollectedData:: append_vdata(const GeomVertexData *vdata, int vertex_offset) { for (int i = 0; i < vdata->get_num_arrays(); ++i) { - PT(GeomVertexArrayData) new_array = _new_data->modify_array(i); - CPT(GeomVertexArrayData) old_array = vdata->get_array(i); + PT(GeomVertexArrayDataHandle) new_handle = _new_data->modify_array_handle(i); + CPT(GeomVertexArrayDataHandle) old_handle = vdata->get_array_handle(i); size_t stride = (size_t)_new_format->get_array(i)->get_stride(); size_t start_byte = (size_t)vertex_offset * stride; - size_t copy_bytes = old_array->get_data_size_bytes(); - nassertv(start_byte + copy_bytes <= new_array->get_data_size_bytes()); + size_t copy_bytes = old_handle->get_data_size_bytes(); + nassertv(start_byte + copy_bytes <= new_handle->get_data_size_bytes()); - new_array->modify_handle()->copy_subdata_from - (start_byte, copy_bytes, - old_array->get_handle(), 0, copy_bytes); + new_handle->copy_subdata_from(start_byte, copy_bytes, old_handle, 0, copy_bytes); } // Also, copy the animation data (if any). This means combining transform @@ -1441,13 +1439,8 @@ remove_unused_vertices(const GeomVertexData *vdata) { any_referenced = true; int num_primitives = geom->get_num_primitives(); for (int i = 0; i < num_primitives; ++i) { - CPT(GeomPrimitive) prim = geom->get_primitive(i); - - GeomPrimitivePipelineReader reader(prim, current_thread); - int num_vertices = reader.get_num_vertices(); - for (int vi = 0; vi < num_vertices; ++vi) { - referenced_vertices.set_bit(reader.get_vertex(vi)); - } + GeomPrimitivePipelineReader reader(geom->get_primitive(i), current_thread); + reader.get_referenced_vertices(referenced_vertices); } } diff --git a/panda/src/pgraph/lensNode.h b/panda/src/pgraph/lensNode.h index dead36fbe3..da9d9ef5f6 100644 --- a/panda/src/pgraph/lensNode.h +++ b/panda/src/pgraph/lensNode.h @@ -28,7 +28,7 @@ */ class EXPCL_PANDA_PGRAPH LensNode : public PandaNode { PUBLISHED: - LensNode(const string &name, Lens *lens = NULL); + explicit LensNode(const string &name, Lens *lens = NULL); protected: LensNode(const LensNode ©); diff --git a/panda/src/pgraph/light.cxx b/panda/src/pgraph/light.cxx index b159450142..82b53f5757 100644 --- a/panda/src/pgraph/light.cxx +++ b/panda/src/pgraph/light.cxx @@ -157,6 +157,20 @@ get_attenuation() const { return no_atten; } +/** + * This is called when the light is added to a LightAttrib. + */ +void Light:: +attrib_ref() { +} + +/** + * This is called when the light is removed from a LightAttrib. + */ +void Light:: +attrib_unref() { +} + /** * Computes the vector from a particular vertex to this light. The exact * vector depends on the type of light (e.g. point lights return a different diff --git a/panda/src/pgraph/light.h b/panda/src/pgraph/light.h index eeca3409bd..ee6e906356 100644 --- a/panda/src/pgraph/light.h +++ b/panda/src/pgraph/light.h @@ -64,6 +64,9 @@ PUBLISHED: MAKE_PROPERTY(priority, get_priority, set_priority); public: + virtual void attrib_ref(); + virtual void attrib_unref(); + virtual void output(ostream &out) const=0; virtual void write(ostream &out, int indent_level) const=0; virtual void bind(GraphicsStateGuardianBase *gsg, const NodePath &light, diff --git a/panda/src/pgraph/lightAttrib.I b/panda/src/pgraph/lightAttrib.I index 5281acc1a3..0c6686181b 100644 --- a/panda/src/pgraph/lightAttrib.I +++ b/panda/src/pgraph/lightAttrib.I @@ -15,37 +15,35 @@ * Use LightAttrib::make() to construct a new LightAttrib object. */ INLINE LightAttrib:: -LightAttrib() { - _off_all_lights = false; -} - -/** - * Use LightAttrib::make() to construct a new LightAttrib object. The copy - * constructor is only defined to facilitate methods like add_on_light(). - */ -INLINE LightAttrib:: -LightAttrib(const LightAttrib ©) : - _on_lights(copy._on_lights), - _off_lights(copy._off_lights), - _off_all_lights(copy._off_all_lights) -{ +LightAttrib() : _off_all_lights(false), _num_non_ambient_lights(0) { } /** * Returns the number of lights that are turned on by the attribute. */ -INLINE int LightAttrib:: +INLINE size_t LightAttrib:: get_num_on_lights() const { - return _on_lights.size(); + check_sorted(); + return _sorted_on_lights.size(); +} + +/** + * Returns the number of non-ambient lights that are turned on by this + * attribute. + */ +INLINE size_t LightAttrib:: +get_num_non_ambient_lights() const { + check_sorted(); + return _num_non_ambient_lights; } /** * Returns the nth light turned on by the attribute, sorted in render order. */ INLINE NodePath LightAttrib:: -get_on_light(int n) const { - nassertr(n >= 0 && n < (int)_on_lights.size(), NodePath::fail()); - return _on_lights[n]; +get_on_light(size_t n) const { + nassertr(n < _sorted_on_lights.size(), NodePath::fail()); + return _sorted_on_lights[n]; } /** @@ -57,10 +55,18 @@ has_on_light(const NodePath &light) const { return _on_lights.find(light) != _on_lights.end(); } +/** + * Returns true if any light is turned on by the attrib, false otherwise. + */ +INLINE bool LightAttrib:: +has_any_on_light() const { + return !_on_lights.empty(); +} + /** * Returns the number of lights that are turned off by the attribute. */ -INLINE int LightAttrib:: +INLINE size_t LightAttrib:: get_num_off_lights() const { return _off_lights.size(); } @@ -70,8 +76,8 @@ get_num_off_lights() const { * (pointer) order. */ INLINE NodePath LightAttrib:: -get_off_light(int n) const { - nassertr(n >= 0 && n < (int)_off_lights.size(), NodePath::fail()); +get_off_light(size_t n) const { + nassertr(n < _off_lights.size(), NodePath::fail()); return _off_lights[n]; } @@ -104,13 +110,11 @@ is_identity() const { } /** - * Confirms whether the _filtered table is still valid. It may become invalid - * if someone calls Light::set_priority(). - * - * If the table is invalid, transparently empties it before returning. + * Makes sure that the on lights are still sorted by priority. It may become + * invalid if someone calls Light::set_priority(). */ INLINE void LightAttrib:: -check_filtered() const { +check_sorted() const { if (_sort_seq != Light::get_sort_seq()) { ((LightAttrib *)this)->sort_on_lights(); } diff --git a/panda/src/pgraph/lightAttrib.cxx b/panda/src/pgraph/lightAttrib.cxx index b80eb3b428..15b010336e 100644 --- a/panda/src/pgraph/lightAttrib.cxx +++ b/panda/src/pgraph/lightAttrib.cxx @@ -29,7 +29,7 @@ int LightAttrib::_attrib_slot; CPT(RenderAttrib) LightAttrib::_all_off_attrib; TypeHandle LightAttrib::_type_handle; -// This STL Function object is used in filter_to_max(), below, to sort a list +// This STL Function object is used in sort_on_lights(), below, to sort a list // of Lights in reverse order by priority. In the case of two lights with // equal priority, the class priority is compared. class CompareLightPriorities { @@ -47,6 +47,44 @@ public: } }; +/** + * Use LightAttrib::make() to construct a new LightAttrib object. The copy + * constructor is only defined to facilitate methods like add_on_light(). + */ +LightAttrib:: +LightAttrib(const LightAttrib ©) : + _on_lights(copy._on_lights), + _off_lights(copy._off_lights), + _off_all_lights(copy._off_all_lights), + _sort_seq(UpdateSeq::old()) +{ + // Increase the attrib_ref of all the lights in this attribute. + Lights::const_iterator it; + for (it = _on_lights.begin(); it != _on_lights.end(); ++it) { + Light *lobj = (*it).node()->as_light(); + nassertd(lobj != nullptr) continue; + lobj->attrib_ref(); + } +} + +/** + * Destructor. + */ +LightAttrib:: +~LightAttrib() { + // Call attrib_unref() on all on lights. + Lights::const_iterator it; + for (it = _on_lights.begin(); it != _on_lights.end(); ++it) { + const NodePath &np = *it; + if (!np.is_empty()) { + Light *lobj = np.node()->as_light(); + if (lobj != nullptr) { + lobj->attrib_unref(); + } + } + } +} + /** * Constructs a new LightAttrib object that turns on (or off, according to op) * the indicated light(s). @@ -219,7 +257,7 @@ make_default() { /** * Returns the basic operation type of the LightAttrib. If this is O_set, the * lights listed here completely replace any lights that were already on. If - * this is O_add, the lights here are added to the set of of lights that were + * this is O_add, the lights here are added to the set of lights that were * already on, and if O_remove, the lights here are removed from the set of * lights that were on. * @@ -376,14 +414,17 @@ make_all_off() { */ CPT(RenderAttrib) LightAttrib:: add_on_light(const NodePath &light) const { - nassertr(!light.is_empty() && light.node()->as_light() != (Light *)NULL, this); + nassertr(!light.is_empty(), this); + Light *lobj = light.node()->as_light(); + nassertr(lobj != nullptr, this); + LightAttrib *attrib = new LightAttrib(*this); - attrib->_on_lights.insert(light); - attrib->_off_lights.erase(light); pair insert_result = attrib->_on_lights.insert(Lights::value_type(light)); if (insert_result.second) { + lobj->attrib_ref(); + // Also ensure it is removed from the off_lights list. attrib->_off_lights.erase(light); } @@ -397,9 +438,14 @@ add_on_light(const NodePath &light) const { */ CPT(RenderAttrib) LightAttrib:: remove_on_light(const NodePath &light) const { - nassertr(!light.is_empty() && light.node()->as_light() != (Light *)NULL, this); + nassertr(!light.is_empty(), this); + Light *lobj = light.node()->as_light(); + nassertr(lobj != nullptr, this); + LightAttrib *attrib = new LightAttrib(*this); - attrib->_on_lights.erase(light); + if (attrib->_on_lights.erase(light)) { + lobj->attrib_unref(); + } return return_new(attrib); } @@ -409,12 +455,17 @@ remove_on_light(const NodePath &light) const { */ CPT(RenderAttrib) LightAttrib:: add_off_light(const NodePath &light) const { - nassertr(!light.is_empty() && light.node()->as_light() != (Light *)NULL, this); + nassertr(!light.is_empty(), this); + Light *lobj = light.node()->as_light(); + nassertr(lobj != nullptr, this); + LightAttrib *attrib = new LightAttrib(*this); if (!_off_all_lights) { attrib->_off_lights.insert(light); } - attrib->_on_lights.erase(light); + if (attrib->_on_lights.erase(light)) { + lobj->attrib_unref(); + } return return_new(attrib); } @@ -430,80 +481,6 @@ remove_off_light(const NodePath &light) const { return return_new(attrib); } -/** - * Returns a new LightAttrib, very much like this one, but with the number of - * on_lights reduced to be no more than max_lights. The number of off_lights - * in the new LightAttrib is undefined. - * - * The number of AmbientLights is not included in the count. All - * AmbientLights in the original attrib are always included in the result, - * regardless of the value of max_lights. - */ -CPT(LightAttrib) LightAttrib:: -filter_to_max(int max_lights) const { - if (max_lights < 0 || (int)_on_lights.size() <= max_lights) { - // Trivial case: this LightAttrib qualifies. - return this; - } - - // Since check_filtered() will clear the _filtered list if we are out of - // date, we should call it first. - check_filtered(); - - Filtered::const_iterator fi; - fi = _filtered.find(max_lights); - if (fi != _filtered.end()) { - // Easy case: we have already computed this for this particular - // LightAttrib. - return (*fi).second; - } - - // Harder case: we have to compute it now. We must choose the n lights with - // the highest priority in our list of lights. - Lights priority_lights, ambient_lights; - - // Separate the list of lights into ambient lights and other lights. - Lights::const_iterator li; - for (li = _on_lights.begin(); li != _on_lights.end(); ++li) { - const NodePath &np = (*li); - nassertr(!np.is_empty() && np.node()->as_light() != (Light *)NULL, this); - if (np.node()->is_ambient_light()) { - ambient_lights.push_back(np); - } else { - priority_lights.push_back(np); - } - } - - // This sort function uses the STL function object defined above. - sort(priority_lights.begin(), priority_lights.end(), - CompareLightPriorities()); - - // Now lop off all of the lights after the first max_lights. - if ((int)priority_lights.size() > max_lights) { - priority_lights.erase(priority_lights.begin() + max_lights, - priority_lights.end()); - } - - // Put the ambient lights back into the list. - for (li = ambient_lights.begin(); li != ambient_lights.end(); ++li) { - priority_lights.push_back(*li); - } - - // And re-sort the ov_set into its proper order. - priority_lights.sort(); - - // Now create a new attrib reflecting these lights. - PT(LightAttrib) attrib = new LightAttrib; - attrib->_on_lights.swap(priority_lights); - - CPT(RenderAttrib) new_attrib = return_new(attrib); - - // Finally, record this newly-created attrib in the map for next time. - CPT(LightAttrib) light_attrib = (const LightAttrib *)new_attrib.p(); - ((LightAttrib *)this)->_filtered[max_lights] = light_attrib; - return light_attrib; -} - /** * Returns the most important light (that is, the light with the highest * priority) in the LightAttrib, excluding any ambient lights. Returns an @@ -511,22 +488,35 @@ filter_to_max(int max_lights) const { */ NodePath LightAttrib:: get_most_important_light() const { - NodePath best; + check_sorted(); - CompareLightPriorities compare; + if (_num_non_ambient_lights > 0) { + return _sorted_on_lights[0]; + } else { + return NodePath(); + } +} + +/** + * Returns the total contribution of all the ambient lights. + */ +LColor LightAttrib:: +get_ambient_contribution() const { + check_sorted(); + + LVecBase4 total(0); Lights::const_iterator li; - for (li = _on_lights.begin(); li != _on_lights.end(); ++li) { + li = _sorted_on_lights.begin() + _num_non_ambient_lights; + for (; li != _sorted_on_lights.end(); ++li) { const NodePath &np = (*li); - nassertr(!np.is_empty() && np.node()->as_light() != (Light *)NULL, NodePath()); - if (!np.node()->is_ambient_light()) { - if (best.is_empty() || compare(np, best)) { - best = np; - } - } + Light *light = np.node()->as_light(); + nassertd(light != nullptr && light->is_ambient_light()) continue; + + total += light->get_color(); } - return best; + return total; } /** @@ -843,6 +833,14 @@ compose_impl(const RenderAttrib *other) const { ++result; } + // Increase the attrib_ref of all the lights in this new attribute. + Lights::const_iterator it; + for (it = new_attrib->_on_lights.begin(); it != new_attrib->_on_lights.end(); ++it) { + Light *lobj = (*it).node()->as_light(); + nassertd(lobj != nullptr) continue; + lobj->attrib_ref(); + } + return return_new(new_attrib); } @@ -861,21 +859,42 @@ invert_compose_impl(const RenderAttrib *other) const { } /** - * - */ -CPT(RenderAttrib) LightAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return this; -} - -/** - * This is patterned after TextureAttrib::sort_on_stages(), but since lights - * don't actually require sorting, this only empties the _filtered map. + * Makes sure the lights are sorted in order of priority. Also counts the + * number of non-ambient lights. */ void LightAttrib:: sort_on_lights() { _sort_seq = Light::get_sort_seq(); - _filtered.clear(); + + // Separate the list of lights into ambient lights and other lights. + _sorted_on_lights.clear(); + OrderedLights ambient_lights; + + Lights::const_iterator li; + for (li = _on_lights.begin(); li != _on_lights.end(); ++li) { + const NodePath &np = (*li); + nassertd(!np.is_empty() && np.node()->as_light() != nullptr) continue; + + if (!np.node()->is_ambient_light()) { + _sorted_on_lights.push_back(np); + } else { + ambient_lights.push_back(np); + } + } + + // Remember how many lights were non-ambient lights, which makes it easier + // to traverse through the list of non-ambient lights. + _num_non_ambient_lights = _sorted_on_lights.size(); + + // This sort function uses the STL function object defined above. + sort(_sorted_on_lights.begin(), _sorted_on_lights.end(), + CompareLightPriorities()); + + // Now insert the ambient lights back at the end. We don't really care + // about their relative priorities, because their contribution will simply + // be summed up in the end anyway. + _sorted_on_lights.insert(_sorted_on_lights.end(), + ambient_lights.begin(), ambient_lights.end()); } /** @@ -992,6 +1011,10 @@ finalize(BamReader *manager) { // If it's in the registry, replace it. _on_lights[i] = areg->get_node(n); } + + Light *lobj = _on_lights[i].node()->as_light(); + nassertd(lobj != nullptr) continue; + lobj->attrib_ref(); } } else { @@ -1024,10 +1047,15 @@ finalize(BamReader *manager) { if (n != -1) { // If it's in the registry, add that NodePath. _on_lights.push_back(areg->get_node(n)); + node = _on_lights.back().node(); } else { // Otherwise, add any arbitrary NodePath. Complain if it's ambiguous. _on_lights.push_back(NodePath(node)); } + + Light *lobj = node->as_light(); + nassertd(lobj != nullptr) continue; + lobj->attrib_ref(); } } @@ -1085,4 +1113,7 @@ fillin(DatagramIterator &scan, BamReader *manager) { aux->_num_on_lights = scan.get_uint16(); manager->read_pointers(scan, aux->_num_on_lights); } + + _sorted_on_lights.clear(); + _sort_seq = UpdateSeq::old(); } diff --git a/panda/src/pgraph/lightAttrib.h b/panda/src/pgraph/lightAttrib.h index 1053dac3fe..8d8b87bfb6 100644 --- a/panda/src/pgraph/lightAttrib.h +++ b/panda/src/pgraph/lightAttrib.h @@ -30,9 +30,10 @@ class EXPCL_PANDA_PGRAPH LightAttrib : public RenderAttrib { protected: INLINE LightAttrib(); - INLINE LightAttrib(const LightAttrib ©); + LightAttrib(const LightAttrib ©); PUBLISHED: + virtual ~LightAttrib(); // This is the old, deprecated interface to LightAttrib. Do not use any of // these methods for new code; these methods will be removed soon. @@ -67,13 +68,15 @@ PUBLISHED: static CPT(RenderAttrib) make(); static CPT(RenderAttrib) make_all_off(); - INLINE int get_num_on_lights() const; - INLINE NodePath get_on_light(int n) const; + INLINE size_t get_num_on_lights() const; + INLINE size_t get_num_non_ambient_lights() const; + INLINE NodePath get_on_light(size_t n) const; MAKE_SEQ(get_on_lights, get_num_on_lights, get_on_light); INLINE bool has_on_light(const NodePath &light) const; + INLINE bool has_any_on_light() const; - INLINE int get_num_off_lights() const; - INLINE NodePath get_off_light(int n) const; + INLINE size_t get_num_off_lights() const; + INLINE NodePath get_off_light(size_t n) const; MAKE_SEQ(get_off_lights, get_num_off_lights, get_off_light); INLINE bool has_off_light(const NodePath &light) const; INLINE bool has_all_off() const; @@ -85,8 +88,11 @@ PUBLISHED: CPT(RenderAttrib) add_off_light(const NodePath &light) const; CPT(RenderAttrib) remove_off_light(const NodePath &light) const; - CPT(LightAttrib) filter_to_max(int max_lights) const; NodePath get_most_important_light() const; + LColor get_ambient_contribution() const; + + MAKE_SEQ_PROPERTY(on_lights, get_num_on_lights, get_on_light); + MAKE_SEQ_PROPERTY(off_lights, get_num_off_lights, get_off_light); public: virtual void output(ostream &out) const; @@ -97,10 +103,9 @@ protected: virtual size_t get_hash_impl() const; virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const; virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: - INLINE void check_filtered() const; + INLINE void check_sorted() const; void sort_on_lights(); private: @@ -108,8 +113,11 @@ private: Lights _on_lights, _off_lights; bool _off_all_lights; - typedef pmap< int, CPT(LightAttrib) > Filtered; - Filtered _filtered; + // These are sorted in descending order of priority, with the ambient lights + // sorted last. + typedef pvector OrderedLights; + OrderedLights _sorted_on_lights; + size_t _num_non_ambient_lights; UpdateSeq _sort_seq; @@ -123,6 +131,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: // This data is only needed when reading from a bam file. diff --git a/panda/src/pgraph/lightRampAttrib.cxx b/panda/src/pgraph/lightRampAttrib.cxx index 78ba5aaef1..7a7637a35f 100644 --- a/panda/src/pgraph/lightRampAttrib.cxx +++ b/panda/src/pgraph/lightRampAttrib.cxx @@ -51,8 +51,14 @@ make_identity() { /** * Constructs a new LightRampAttrib object. This causes the luminance of the * diffuse lighting contribution to be quantized using a single threshold: - * @code if (original_luminance > threshold0) { luminance = level0; } else { - * luminance = 0.0; } @endcode + * + * @code + * if (original_luminance > threshold0) { + * luminance = level0; + * } else { + * luminance = 0.0; + * } + * @endcode */ CPT(RenderAttrib) LightRampAttrib:: make_single_threshold(PN_stdfloat thresh0, PN_stdfloat val0) { @@ -65,10 +71,17 @@ make_single_threshold(PN_stdfloat thresh0, PN_stdfloat val0) { /** * Constructs a new LightRampAttrib object. This causes the luminance of the - * diffuse lighting contribution to be quantized using two thresholds: @code - * if (original_luminance > threshold1) { luminance = level1; } else if - * (original_luminance > threshold0) { luminance = level0; } else { luminance - * = 0.0; } @endcode + * diffuse lighting contribution to be quantized using two thresholds: + * + * @code + * if (original_luminance > threshold1) { + * luminance = level1; + * } else if (original_luminance > threshold0) { + * luminance = level0; + * } else { + * luminance = 0.0; + * } + * @endcode */ CPT(RenderAttrib) LightRampAttrib:: make_double_threshold(PN_stdfloat thresh0, PN_stdfloat val0, PN_stdfloat thresh1, PN_stdfloat val1) { @@ -94,8 +107,11 @@ make_double_threshold(PN_stdfloat thresh0, PN_stdfloat val0, PN_stdfloat thresh1 * However, the monitor has finite contrast. Normally, all of that contrast * is used to represent brightnesses in the range 0-1. The HDR0 tone mapping * operator 'steals' one quarter of that contrast to represent brightnesses in - * the range 1-infinity. @code FINAL_RGB = (RGB^3 + RGB^2 + RGB) / (RGB^3 + - * RGB^2 + RGB + 1) @endcode + * the range 1-infinity. + * + * @code + * FINAL_RGB = (RGB^3 + RGB^2 + RGB) / (RGB^3 + RGB^2 + RGB + 1) + * @endcode */ CPT(RenderAttrib) LightRampAttrib:: make_hdr0() { @@ -117,7 +133,10 @@ make_hdr0() { * However, the monitor has finite contrast. Normally, all of that contrast * is used to represent brightnesses in the range 0-1. The HDR1 tone mapping * operator 'steals' one third of that contrast to represent brightnesses in - * the range 1-infinity. @code FINAL_RGB = (RGB^2 + RGB) / (RGB^2 + RGB + 1) + * the range 1-infinity. + * + * @code + * FINAL_RGB = (RGB^2 + RGB) / (RGB^2 + RGB + 1) * @endcode */ CPT(RenderAttrib) LightRampAttrib:: @@ -140,7 +159,11 @@ make_hdr1() { * However, the monitor has finite contrast. Normally, all of that contrast * is used to represent brightnesses in the range 0-1. The HDR2 tone mapping * operator 'steals' one half of that contrast to represent brightnesses in - * the range 1-infinity. @code FINAL_RGB = (RGB) / (RGB + 1) @endcode + * the range 1-infinity. + * + * @code + * FINAL_RGB = (RGB) / (RGB + 1) + * @endcode */ CPT(RenderAttrib) LightRampAttrib:: make_hdr2() { @@ -231,14 +254,6 @@ get_hash_impl() const { return hash; } -/** - * - */ -CPT(RenderAttrib) LightRampAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return this; -} - /** * Tells the BamReader how to create objects of type LightRampAttrib. */ diff --git a/panda/src/pgraph/lightRampAttrib.h b/panda/src/pgraph/lightRampAttrib.h index b1ac147376..65a894e582 100644 --- a/panda/src/pgraph/lightRampAttrib.h +++ b/panda/src/pgraph/lightRampAttrib.h @@ -61,7 +61,6 @@ public: protected: virtual int compare_to_impl(const RenderAttrib *other) const; virtual size_t get_hash_impl() const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: LightRampMode _mode; @@ -77,6 +76,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/loader.I b/panda/src/pgraph/loader.I index 25295c32dc..c86753b3be 100644 --- a/panda/src/pgraph/loader.I +++ b/panda/src/pgraph/loader.I @@ -135,6 +135,7 @@ stop_threads() { /** * Removes a pending asynchronous load request. Returns true if successful, * false otherwise. + * @deprecated use task.cancel() to cancel the request instead. */ INLINE bool Loader:: remove(AsyncTask *task) { diff --git a/panda/src/pgraph/loader.h b/panda/src/pgraph/loader.h index b39dc952cd..46de9ff8eb 100644 --- a/panda/src/pgraph/loader.h +++ b/panda/src/pgraph/loader.h @@ -70,7 +70,7 @@ PUBLISHED: Files _files; }; - Loader(const string &name = "loader"); + explicit Loader(const string &name = "loader"); INLINE void set_task_manager(AsyncTaskManager *task_manager); INLINE AsyncTaskManager *get_task_manager() const; diff --git a/panda/src/pgraph/logicOpAttrib.cxx b/panda/src/pgraph/logicOpAttrib.cxx index b632bb1b45..28381dfc5b 100644 --- a/panda/src/pgraph/logicOpAttrib.cxx +++ b/panda/src/pgraph/logicOpAttrib.cxx @@ -88,14 +88,6 @@ get_hash_impl() const { return hash; } -/** - * - */ -CPT(RenderAttrib) LogicOpAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return RenderAttribRegistry::quick_get_global_ptr()->get_slot_default(_attrib_slot); -} - /** * Tells the BamReader how to create objects of type LogicOpAttrib. */ diff --git a/panda/src/pgraph/logicOpAttrib.h b/panda/src/pgraph/logicOpAttrib.h index 988836f8d1..99ab79d029 100644 --- a/panda/src/pgraph/logicOpAttrib.h +++ b/panda/src/pgraph/logicOpAttrib.h @@ -64,7 +64,6 @@ public: protected: virtual int compare_to_impl(const RenderAttrib *other) const; virtual size_t get_hash_impl() const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: Operation _op; @@ -76,6 +75,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/materialAttrib.I b/panda/src/pgraph/materialAttrib.I index 2f2652f43c..d0cfc1ad14 100644 --- a/panda/src/pgraph/materialAttrib.I +++ b/panda/src/pgraph/materialAttrib.I @@ -24,7 +24,7 @@ MaterialAttrib() { */ INLINE bool MaterialAttrib:: is_off() const { - return _material == (const Material *)NULL; + return _material == nullptr; } /** diff --git a/panda/src/pgraph/materialAttrib.cxx b/panda/src/pgraph/materialAttrib.cxx index c3facc7bb4..baeac4dc38 100644 --- a/panda/src/pgraph/materialAttrib.cxx +++ b/panda/src/pgraph/materialAttrib.cxx @@ -58,10 +58,10 @@ make_default() { void MaterialAttrib:: output(ostream &out) const { out << get_type() << ":"; - if (is_off()) { - out << "(off)"; - } else { + if (_material != nullptr) { out << *_material; + } else if (is_off()) { + out << "(off)"; } } @@ -98,17 +98,7 @@ compare_to_impl(const RenderAttrib *other) const { */ size_t MaterialAttrib:: get_hash_impl() const { - size_t hash = 0; - hash = pointer_hash::add_hash(hash, _material); - return hash; -} - -/** - * - */ -CPT(RenderAttrib) MaterialAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return this; + return pointer_hash::add_hash(0, _material); } /** @@ -139,9 +129,7 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { int pi = RenderAttrib::complete_pointers(p_list, manager); TypedWritable *material = p_list[pi++]; - if (material != (TypedWritable *)NULL) { - _material = DCAST(Material, material); - } + _material = DCAST(Material, material); return pi; } diff --git a/panda/src/pgraph/materialAttrib.h b/panda/src/pgraph/materialAttrib.h index f984b699b3..88c4a4363d 100644 --- a/panda/src/pgraph/materialAttrib.h +++ b/panda/src/pgraph/materialAttrib.h @@ -45,7 +45,6 @@ public: protected: virtual int compare_to_impl(const RenderAttrib *other) const; virtual size_t get_hash_impl() const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: PT(Material) _material; @@ -57,6 +56,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/modelFlattenRequest.I b/panda/src/pgraph/modelFlattenRequest.I index 248d122c40..75f177e6a8 100644 --- a/panda/src/pgraph/modelFlattenRequest.I +++ b/panda/src/pgraph/modelFlattenRequest.I @@ -18,8 +18,7 @@ INLINE ModelFlattenRequest:: ModelFlattenRequest(PandaNode *orig) : AsyncTask(orig->get_name()), - _orig(orig), - _is_ready(false) + _orig(orig) { } @@ -34,19 +33,22 @@ get_orig() const { /** * Returns true if this request has completed, false if it is still pending. * When this returns true, you may retrieve the model loaded by calling - * get_result(). + * result(). + * Equivalent to `req.done() and not req.cancelled()`. + * @see done() */ INLINE bool ModelFlattenRequest:: is_ready() const { - return _is_ready; + return (FutureState)AtomicAdjust::get(_future_state) == FS_finished; } /** * Returns the flattened copy of the model. It is an error to call this - * unless is_ready() returns true. + * unless done() returns true. + * @deprecated Use result() instead. */ INLINE PandaNode *ModelFlattenRequest:: get_model() const { - nassertr(_is_ready, NULL); - return _model; + nassertr_always(done(), nullptr); + return (PandaNode *)_result; } diff --git a/panda/src/pgraph/modelFlattenRequest.cxx b/panda/src/pgraph/modelFlattenRequest.cxx index 2b02b58f00..0af2cafa15 100644 --- a/panda/src/pgraph/modelFlattenRequest.cxx +++ b/panda/src/pgraph/modelFlattenRequest.cxx @@ -35,8 +35,8 @@ do_task() { np.attach_new_node(_orig); } np.flatten_strong(); - _model = np.get_child(0).node(); - _is_ready = true; + + set_result(np.get_child(0).node()); // Don't continue the task; we're done. return DS_done; diff --git a/panda/src/pgraph/modelFlattenRequest.h b/panda/src/pgraph/modelFlattenRequest.h index 5716635f82..a166490edb 100644 --- a/panda/src/pgraph/modelFlattenRequest.h +++ b/panda/src/pgraph/modelFlattenRequest.h @@ -19,6 +19,7 @@ #include "asyncTask.h" #include "pandaNode.h" #include "pointerTo.h" +#include "nodePath.h" /** * This class object manages a single asynchronous request to flatten a model. @@ -31,7 +32,7 @@ public: ALLOC_DELETED_CHAIN(ModelFlattenRequest); PUBLISHED: - INLINE ModelFlattenRequest(PandaNode *orig); + INLINE explicit ModelFlattenRequest(PandaNode *orig); INLINE PandaNode *get_orig() const; @@ -39,16 +40,12 @@ PUBLISHED: INLINE PandaNode *get_model() const; MAKE_PROPERTY(orig, get_orig); - MAKE_PROPERTY(ready, is_ready); - MAKE_PROPERTY(model, get_model); protected: virtual DoneStatus do_task(); private: PT(PandaNode) _orig; - bool _is_ready; - PT(PandaNode) _model; public: static TypeHandle get_class_type() { diff --git a/panda/src/pgraph/modelLoadRequest.I b/panda/src/pgraph/modelLoadRequest.I index 7392194510..3fb37a1bdc 100644 --- a/panda/src/pgraph/modelLoadRequest.I +++ b/panda/src/pgraph/modelLoadRequest.I @@ -38,21 +38,24 @@ get_loader() const { } /** - * Returns true if this request has completed, false if it is still pending. - * When this returns true, you may retrieve the model loaded by calling - * get_model(). + * Returns true if this request has completed, false if it is still pending or + * if it has been cancelled. When this returns true, you may retrieve the + * model loaded by calling get_model(). + * Equivalent to `req.done() and not req.cancelled()`. + * @see done() */ INLINE bool ModelLoadRequest:: is_ready() const { - return _is_ready; + return (FutureState)AtomicAdjust::get(_future_state) == FS_finished; } /** - * Returns the model that was loaded asynchronously, if any, or NULL if there - * was an error. It is an error to call this unless is_ready() returns true. + * Returns the model that was loaded asynchronously, if any, or null if there + * was an error. It is an error to call this unless done() returns true. + * @deprecated Use result() instead. */ INLINE PandaNode *ModelLoadRequest:: get_model() const { - nassertr(_is_ready, NULL); - return _model; + nassertr_always(done(), nullptr); + return (PandaNode *)_result; } diff --git a/panda/src/pgraph/modelLoadRequest.cxx b/panda/src/pgraph/modelLoadRequest.cxx index bc1b767b5c..4feba702ef 100644 --- a/panda/src/pgraph/modelLoadRequest.cxx +++ b/panda/src/pgraph/modelLoadRequest.cxx @@ -28,8 +28,7 @@ ModelLoadRequest(const string &name, AsyncTask(name), _filename(filename), _options(options), - _loader(loader), - _is_ready(false) + _loader(loader) { } @@ -43,8 +42,8 @@ do_task() { Thread::sleep(delay); } - _model = _loader->load_sync(_filename, _options); - _is_ready = true; + PT(PandaNode) model = _loader->load_sync(_filename, _options); + set_result(model); // Don't continue the task; we're done. return DS_done; diff --git a/panda/src/pgraph/modelLoadRequest.h b/panda/src/pgraph/modelLoadRequest.h index 00f55e8052..e743623fa3 100644 --- a/panda/src/pgraph/modelLoadRequest.h +++ b/panda/src/pgraph/modelLoadRequest.h @@ -11,8 +11,8 @@ * @date 2006-08-29 */ -#ifndef MODELLOADREQUEST -#define MODELLOADREQUEST +#ifndef MODELLOADREQUEST_H +#define MODELLOADREQUEST_H #include "pandabase.h" @@ -22,6 +22,7 @@ #include "pandaNode.h" #include "pointerTo.h" #include "loader.h" +#include "nodePath.h" /** * A class object that manages a single asynchronous model load request. @@ -33,10 +34,10 @@ public: ALLOC_DELETED_CHAIN(ModelLoadRequest); PUBLISHED: - ModelLoadRequest(const string &name, - const Filename &filename, - const LoaderOptions &options, - Loader *loader); + explicit ModelLoadRequest(const string &name, + const Filename &filename, + const LoaderOptions &options, + Loader *loader); INLINE const Filename &get_filename() const; INLINE const LoaderOptions &get_options() const; @@ -48,8 +49,6 @@ PUBLISHED: MAKE_PROPERTY(filename, get_filename); MAKE_PROPERTY(options, get_options); MAKE_PROPERTY(loader, get_loader); - MAKE_PROPERTY(ready, is_ready); - MAKE_PROPERTY(model, get_model); protected: virtual DoneStatus do_task(); @@ -58,8 +57,6 @@ private: Filename _filename; LoaderOptions _options; PT(Loader) _loader; - bool _is_ready; - PT(PandaNode) _model; public: static TypeHandle get_class_type() { diff --git a/panda/src/pgraph/modelNode.h b/panda/src/pgraph/modelNode.h index aa4620f671..177f1cffb6 100644 --- a/panda/src/pgraph/modelNode.h +++ b/panda/src/pgraph/modelNode.h @@ -30,7 +30,7 @@ */ class EXPCL_PANDA_PGRAPH ModelNode : public PandaNode { PUBLISHED: - INLINE ModelNode(const string &name); + explicit INLINE ModelNode(const string &name); protected: INLINE ModelNode(const ModelNode ©); diff --git a/panda/src/pgraph/modelRoot.h b/panda/src/pgraph/modelRoot.h index bff0735abf..7895dcab85 100644 --- a/panda/src/pgraph/modelRoot.h +++ b/panda/src/pgraph/modelRoot.h @@ -26,8 +26,8 @@ */ class EXPCL_PANDA_PGRAPH ModelRoot : public ModelNode { PUBLISHED: - INLINE ModelRoot(const string &name); - INLINE ModelRoot(const Filename &fullpath, time_t timestamp); + INLINE explicit ModelRoot(const string &name); + INLINE explicit ModelRoot(const Filename &fullpath, time_t timestamp); INLINE int get_model_ref_count() const; MAKE_PROPERTY(model_ref_count, get_model_ref_count); diff --git a/panda/src/pgraph/modelSaveRequest.I b/panda/src/pgraph/modelSaveRequest.I index d7ccd1dd09..e84b8929af 100644 --- a/panda/src/pgraph/modelSaveRequest.I +++ b/panda/src/pgraph/modelSaveRequest.I @@ -49,18 +49,20 @@ get_loader() const { * Returns true if this request has completed, false if it is still pending. * When this returns true, you may retrieve the success flag with * get_success(). + * Equivalent to `req.done() and not req.cancelled()`. + * @see done() */ INLINE bool ModelSaveRequest:: is_ready() const { - return _is_ready; + return (FutureState)AtomicAdjust::get(_future_state) == FS_finished; } /** * Returns the true if the model was saved successfully, false otherwise. It - * is an error to call this unless is_ready() returns true. + * is an error to call this unless done() returns true. */ INLINE bool ModelSaveRequest:: get_success() const { - nassertr(_is_ready, false); + nassertr_always(done(), false); return _success; } diff --git a/panda/src/pgraph/modelSaveRequest.cxx b/panda/src/pgraph/modelSaveRequest.cxx index 45f985454a..31c50cfdf6 100644 --- a/panda/src/pgraph/modelSaveRequest.cxx +++ b/panda/src/pgraph/modelSaveRequest.cxx @@ -30,7 +30,6 @@ ModelSaveRequest(const string &name, _options(options), _node(node), _loader(loader), - _is_ready(false), _success(false) { } @@ -46,7 +45,6 @@ do_task() { } _success = _loader->save_sync(_filename, _options, _node); - _is_ready = true; // Don't continue the task; we're done. return DS_done; diff --git a/panda/src/pgraph/modelSaveRequest.h b/panda/src/pgraph/modelSaveRequest.h index 878e28edee..7bbe1e331d 100644 --- a/panda/src/pgraph/modelSaveRequest.h +++ b/panda/src/pgraph/modelSaveRequest.h @@ -11,8 +11,8 @@ * @date 2012-12-19 */ -#ifndef MODELSAVEREQUEST -#define MODELSAVEREQUEST +#ifndef MODELSAVEREQUEST_H +#define MODELSAVEREQUEST_H #include "pandabase.h" @@ -33,10 +33,10 @@ public: ALLOC_DELETED_CHAIN(ModelSaveRequest); PUBLISHED: - ModelSaveRequest(const string &name, - const Filename &filename, - const LoaderOptions &options, - PandaNode *node, Loader *loader); + explicit ModelSaveRequest(const string &name, + const Filename &filename, + const LoaderOptions &options, + PandaNode *node, Loader *loader); INLINE const Filename &get_filename() const; INLINE const LoaderOptions &get_options() const; @@ -50,8 +50,6 @@ PUBLISHED: MAKE_PROPERTY(options, get_options); MAKE_PROPERTY(node, get_node); MAKE_PROPERTY(loader, get_loader); - MAKE_PROPERTY(ready, is_ready); - MAKE_PROPERTY(success, get_success); protected: virtual DoneStatus do_task(); @@ -61,7 +59,6 @@ private: LoaderOptions _options; PT(PandaNode) _node; PT(Loader) _loader; - bool _is_ready; bool _success; public: diff --git a/panda/src/pgraph/nodePath.I b/panda/src/pgraph/nodePath.I index cfcef09112..c7d75da2c1 100644 --- a/panda/src/pgraph/nodePath.I +++ b/panda/src/pgraph/nodePath.I @@ -1080,7 +1080,7 @@ get_sa() const { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const PTA_float &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1088,7 +1088,7 @@ set_shader_input(CPT_InternalName id, const PTA_float &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const PTA_double &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1096,7 +1096,7 @@ set_shader_input(CPT_InternalName id, const PTA_double &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const PTA_int &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1104,7 +1104,7 @@ set_shader_input(CPT_InternalName id, const PTA_int &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const PTA_LVecBase4 &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1112,7 +1112,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase4 &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const PTA_LVecBase3 &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } @@ -1121,7 +1121,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase3 &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const PTA_LVecBase2 &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1129,7 +1129,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase2 &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const LVecBase4 &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1137,7 +1137,7 @@ set_shader_input(CPT_InternalName id, const LVecBase4 &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const LVecBase3 &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1145,7 +1145,7 @@ set_shader_input(CPT_InternalName id, const LVecBase3 &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const LVecBase2 &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1153,7 +1153,7 @@ set_shader_input(CPT_InternalName id, const LVecBase2 &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const PTA_LVecBase4i &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1161,7 +1161,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase4i &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const PTA_LVecBase3i &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } @@ -1170,7 +1170,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase3i &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const PTA_LVecBase2i &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1178,7 +1178,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase2i &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const LVecBase4i &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1186,7 +1186,7 @@ set_shader_input(CPT_InternalName id, const LVecBase4i &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const LVecBase3i &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1194,7 +1194,7 @@ set_shader_input(CPT_InternalName id, const LVecBase3i &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const LVecBase2i &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1202,7 +1202,7 @@ set_shader_input(CPT_InternalName id, const LVecBase2i &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const PTA_LMatrix4 &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1210,7 +1210,7 @@ set_shader_input(CPT_InternalName id, const PTA_LMatrix4 &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const PTA_LMatrix3 &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1218,7 +1218,7 @@ set_shader_input(CPT_InternalName id, const PTA_LMatrix3 &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const LMatrix4 &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1226,7 +1226,7 @@ set_shader_input(CPT_InternalName id, const LMatrix4 &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const LMatrix3 &v, int priority) { - set_shader_input(new ShaderInput(id, v, priority)); + set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -1234,7 +1234,7 @@ set_shader_input(CPT_InternalName id, const LMatrix3 &v, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, Texture *tex, int priority) { - set_shader_input(new ShaderInput(id, tex, priority)); + set_shader_input(ShaderInput(move(id), tex, priority)); } /** @@ -1242,7 +1242,7 @@ set_shader_input(CPT_InternalName id, Texture *tex, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority) { - set_shader_input(new ShaderInput(id, tex, sampler, priority)); + set_shader_input(ShaderInput(move(id), tex, sampler, priority)); } /** @@ -1250,7 +1250,7 @@ set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z, int n, int priority) { - set_shader_input(new ShaderInput(id, tex, read, write, z, n, priority)); + set_shader_input(ShaderInput(move(id), tex, read, write, z, n, priority)); } /** @@ -1258,7 +1258,7 @@ set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority) { - set_shader_input(new ShaderInput(id, buf, priority)); + set_shader_input(ShaderInput(move(id), buf, priority)); } /** @@ -1266,7 +1266,7 @@ set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, const NodePath &np, int priority) { - set_shader_input(new ShaderInput(id, np, priority)); + set_shader_input(ShaderInput(move(id), np, priority)); } /** @@ -1274,7 +1274,7 @@ set_shader_input(CPT_InternalName id, const NodePath &np, int priority) { */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, int n1, int n2, int n3, int n4, int priority) { - set_shader_input(new ShaderInput(id, LVecBase4i(n1, n2, n3, n4), priority)); + set_shader_input(ShaderInput(move(id), LVecBase4i(n1, n2, n3, n4), priority)); } /** @@ -1282,7 +1282,7 @@ set_shader_input(CPT_InternalName id, int n1, int n2, int n3, int n4, int priori */ INLINE void NodePath:: set_shader_input(CPT_InternalName id, PN_stdfloat n1, PN_stdfloat n2, PN_stdfloat n3, PN_stdfloat n4, int priority) { - set_shader_input(new ShaderInput(id, LVecBase4(n1, n2, n3, n4), priority)); + set_shader_input(ShaderInput(move(id), LVecBase4(n1, n2, n3, n4), priority)); } /** @@ -2103,11 +2103,11 @@ get_name() const { * This method is used by __reduce__ to handle streaming of NodePaths to a * pickle file. */ -INLINE string NodePath:: +INLINE vector_uchar NodePath:: encode_to_bam_stream() const { - string data; + vector_uchar data; if (!encode_to_bam_stream(data)) { - return string(); + data.clear(); } return data; } diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index de0cf7c4fb..c9ccfd7c90 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -70,6 +70,7 @@ #include "modelNode.h" #include "bam.h" #include "bamWriter.h" +#include "datagramBuffer.h" // stack seems to overflow on Intel C++ at 7000. If we need more than 7000, // need to increase stack size. @@ -697,8 +698,10 @@ get_state(const NodePath &other, Thread *current_thread) const { return other.get_net_state(current_thread)->invert_compose(RenderState::make_empty()); } +#if defined(_DEBUG) || (defined(HAVE_THREADS) && defined(SIMPLE_THREADS)) nassertr(verify_complete(current_thread), RenderState::make_empty()); nassertr(other.verify_complete(current_thread), RenderState::make_empty()); +#endif int a_count, b_count; if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == (NodePathComponent *)NULL) { @@ -767,8 +770,10 @@ get_transform(const NodePath &other, Thread *current_thread) const { return other.get_net_transform(current_thread)->invert_compose(TransformState::make_identity()); } +#if defined(_DEBUG) || (defined(HAVE_THREADS) && defined(SIMPLE_THREADS)) nassertr(verify_complete(current_thread), TransformState::make_identity()); nassertr(other.verify_complete(current_thread), TransformState::make_identity()); +#endif int a_count, b_count; if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == (NodePathComponent *)NULL) { @@ -852,8 +857,10 @@ get_prev_transform(const NodePath &other, Thread *current_thread) const { return other.get_net_prev_transform(current_thread)->invert_compose(TransformState::make_identity()); } +#if defined(_DEBUG) || (defined(HAVE_THREADS) && defined(SIMPLE_THREADS)) nassertr(verify_complete(current_thread), TransformState::make_identity()); nassertr(other.verify_complete(current_thread), TransformState::make_identity()); +#endif int a_count, b_count; if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == (NodePathComponent *)NULL) { @@ -3255,35 +3262,36 @@ get_shader() const { * */ void NodePath:: -set_shader_input(const ShaderInput *inp) { +set_shader_input(ShaderInput inp) { nassertv_always(!is_empty()); + PandaNode *pnode = node(); const RenderAttrib *attrib = - node()->get_attrib(ShaderAttrib::get_class_slot()); - if (attrib != (const RenderAttrib *)NULL) { - const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib); - node()->set_attrib(sa->set_shader_input(inp)); + pnode->get_attrib(ShaderAttrib::get_class_slot()); + if (attrib != nullptr) { + const ShaderAttrib *sa = (const ShaderAttrib *)attrib; + pnode->set_attrib(sa->set_shader_input(inp)); } else { // Create a new ShaderAttrib for this node. CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make()); - node()->set_attrib(sa->set_shader_input(inp)); + pnode->set_attrib(sa->set_shader_input(inp)); } } /** * */ -const ShaderInput *NodePath:: +ShaderInput NodePath:: get_shader_input(CPT_InternalName id) const { - nassertr_always(!is_empty(), NULL); + nassertr_always(!is_empty(), ShaderInput::get_blank()); const RenderAttrib *attrib = node()->get_attrib(ShaderAttrib::get_class_slot()); - if (attrib != (const RenderAttrib *)NULL) { - const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib); + if (attrib != nullptr) { + const ShaderAttrib *sa = (const ShaderAttrib *)attrib; return sa->get_shader_input(id); } - return NULL; + return ShaderInput::get_blank(); } /** @@ -5569,28 +5577,24 @@ write_bam_stream(ostream &out) const { * calls this function. */ bool NodePath:: -encode_to_bam_stream(string &data, BamWriter *writer) const { +encode_to_bam_stream(vector_uchar &data, BamWriter *writer) const { data.clear(); ostringstream stream; - DatagramOutputFile dout; - if (!dout.open(stream)) { - return false; - } - + DatagramBuffer buffer; BamWriter local_writer; bool used_local_writer = false; if (writer == NULL) { // Create our own writer. - if (!dout.write_header(_bam_header)) { + if (!buffer.write_header(_bam_header)) { return false; } writer = &local_writer; used_local_writer = true; } - writer->set_target(&dout); + writer->set_target(&buffer); int num_nodes = get_num_nodes(); if (used_local_writer && num_nodes > 1) { @@ -5608,7 +5612,7 @@ encode_to_bam_stream(string &data, BamWriter *writer) const { dg.add_uint8(_error_type); dg.add_int32(num_nodes); - if (!dout.put_datagram(dg)) { + if (!buffer.put_datagram(dg)) { writer->set_target(NULL); return false; } @@ -5624,7 +5628,7 @@ encode_to_bam_stream(string &data, BamWriter *writer) const { } writer->set_target(NULL); - data = stream.str(); + buffer.swap_data(data); return true; } @@ -5633,22 +5637,17 @@ encode_to_bam_stream(string &data, BamWriter *writer) const { * extracts and returns the NodePath on that string. Returns NULL on error. */ NodePath NodePath:: -decode_from_bam_stream(const string &data, BamReader *reader) { +decode_from_bam_stream(vector_uchar data, BamReader *reader) { NodePath result; - istringstream stream(data); - - DatagramInputFile din; - if (!din.open(stream)) { - return NodePath::fail(); - } + DatagramBuffer buffer(move(data)); BamReader local_reader; if (reader == NULL) { // Create a local reader. string head; - if (!din.read_header(head, _bam_header.size())) { + if (!buffer.read_header(head, _bam_header.size())) { return NodePath::fail(); } @@ -5659,11 +5658,11 @@ decode_from_bam_stream(const string &data, BamReader *reader) { reader = &local_reader; } - reader->set_source(&din); + reader->set_source(&buffer); // One initial datagram to encode the error type, and the number of nodes. Datagram dg; - if (!din.get_datagram(dg)) { + if (!buffer.get_datagram(dg)) { return NodePath::fail(); } @@ -5786,17 +5785,22 @@ r_get_net_transform(NodePathComponent *comp, Thread *current_thread) const { if (comp == (NodePathComponent *)NULL) { return TransformState::make_identity(); } else { + PandaNode *node = comp->get_node(); int pipeline_stage = current_thread->get_pipeline_stage(); CPT(TransformState) net_transform = r_get_net_transform(comp->get_next(pipeline_stage, current_thread), current_thread); - PandaNode *node = comp->get_node(); - CPT(TransformState) transform = node->get_transform(current_thread); - CPT(RenderEffects) effects = node->get_effects(current_thread); - if (effects->has_adjust_transform()) { - effects->adjust_transform(net_transform, transform, node); + PandaNode::CDReader node_cdata(node->_cycler, current_thread); + if (!node_cdata->_effects->has_adjust_transform()) { + if (node_cdata->_transform->is_identity()) { + return net_transform; + } else { + return net_transform->compose(node_cdata->_transform); + } + } else { + CPT(TransformState) transform = node_cdata->_transform.p(); + node_cdata->_effects->adjust_transform(net_transform, transform, node); + return net_transform->compose(transform); } - - return net_transform->compose(transform); } } @@ -5814,16 +5818,21 @@ r_get_partial_transform(NodePathComponent *comp, int n, if (n == 0 || comp == (NodePathComponent *)NULL) { return TransformState::make_identity(); } else { - if (comp->get_node()->get_effects(current_thread)->has_adjust_transform()) { + PandaNode *node = comp->get_node(); + PandaNode::CDReader node_cdata(node->_cycler, current_thread); + if (node_cdata->_effects->has_adjust_transform()) { return NULL; } - CPT(TransformState) transform = comp->get_node()->get_transform(current_thread); int pipeline_stage = current_thread->get_pipeline_stage(); CPT(TransformState) partial = r_get_partial_transform(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread); if (partial == (const TransformState *)NULL) { return NULL; } - return partial->compose(transform); + if (node_cdata->_transform->is_identity()) { + return partial; + } else { + return partial->compose(node_cdata->_transform); + } } } diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 1bbe0845cf..617aac46a5 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -629,10 +629,13 @@ PUBLISHED: void set_shader_auto(BitMask32 shader_switch, int priority=0); void clear_shader(); - void set_shader_input(const ShaderInput *inp); - INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0); + void set_shader_input(ShaderInput input); + INLINE void set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority=0); INLINE void set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0); + +public: + INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0); INLINE void set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority=0); INLINE void set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0); INLINE void set_shader_input(CPT_InternalName id, const PTA_float &v, int priority=0); @@ -654,16 +657,20 @@ PUBLISHED: INLINE void set_shader_input(CPT_InternalName id, const LVecBase4i &v, int priority=0); INLINE void set_shader_input(CPT_InternalName id, const LVecBase3i &v, int priority=0); INLINE void set_shader_input(CPT_InternalName id, const LVecBase2i &v, int priority=0); - INLINE void set_shader_input(CPT_InternalName id, int n1, int n2=0, int n3=0, +PUBLISHED: + INLINE void set_shader_input(CPT_InternalName id, int n1, int n2, int n3=0, int n4=0, int priority=0); - INLINE void set_shader_input(CPT_InternalName id, PN_stdfloat n1, PN_stdfloat n2=0, + INLINE void set_shader_input(CPT_InternalName id, PN_stdfloat n1, PN_stdfloat n2, PN_stdfloat n3=0, PN_stdfloat n4=0, int priority=0); + EXTENSION(void set_shader_input(CPT_InternalName, PyObject *, int priority=0)); + EXTENSION(void set_shader_inputs(PyObject *args, PyObject *kwargs)); + void clear_shader_input(CPT_InternalName id); void set_instance_count(int instance_count); const Shader *get_shader() const; - const ShaderInput *get_shader_input(CPT_InternalName id) const; + ShaderInput get_shader_input(CPT_InternalName id) const; int get_instance_count() const; void set_tex_transform(TextureStage *stage, const TransformState *transform); @@ -906,7 +913,11 @@ PUBLISHED: INLINE bool has_net_tag(const string &key) const; NodePath find_net_tag(const string &key) const; + MAKE_MAP_PROPERTY(net_tags, has_net_tag, get_net_tag); + + EXTENSION(INLINE PyObject *get_tags() const); EXTENSION(INLINE PyObject *get_tag_keys() const); + MAKE_PROPERTY(tags, get_tags); EXTENSION(PyObject *get_python_tags()); EXTENSION(INLINE void set_python_tag(PyObject *keys, PyObject *value)); @@ -930,9 +941,9 @@ PUBLISHED: BLOCKING bool write_bam_file(const Filename &filename) const; BLOCKING bool write_bam_stream(ostream &out) const; - INLINE string encode_to_bam_stream() const; - bool encode_to_bam_stream(string &data, BamWriter *writer = NULL) const; - static NodePath decode_from_bam_stream(const string &data, BamReader *reader = NULL); + INLINE vector_uchar encode_to_bam_stream() const; + bool encode_to_bam_stream(vector_uchar &data, BamWriter *writer = nullptr) const; + static NodePath decode_from_bam_stream(vector_uchar data, BamReader *reader = nullptr); private: static NodePathComponent * @@ -1027,6 +1038,7 @@ private: friend class NodePathCollection; friend class WorkingNodePath; friend class WeakNodePath; + friend class CullTraverserData; }; INLINE ostream &operator << (ostream &out, const NodePath &node_path); diff --git a/panda/src/pgraph/nodePathCollection.I b/panda/src/pgraph/nodePathCollection.I index 6c7b5df2d6..1ea730f4ab 100644 --- a/panda/src/pgraph/nodePathCollection.I +++ b/panda/src/pgraph/nodePathCollection.I @@ -11,13 +11,6 @@ * @date 2002-03-06 */ -/** - * - */ -INLINE NodePathCollection:: -~NodePathCollection() { -} - /** * Appends the other list onto the end of this one. */ diff --git a/panda/src/pgraph/nodePathCollection.cxx b/panda/src/pgraph/nodePathCollection.cxx index a2310b9e99..40efb80192 100644 --- a/panda/src/pgraph/nodePathCollection.cxx +++ b/panda/src/pgraph/nodePathCollection.cxx @@ -19,30 +19,6 @@ #include "colorAttrib.h" #include "indent.h" -/** - * - */ -NodePathCollection:: -NodePathCollection() { -} - -/** - * - */ -NodePathCollection:: -NodePathCollection(const NodePathCollection ©) : - _node_paths(copy._node_paths) -{ -} - -/** - * - */ -void NodePathCollection:: -operator = (const NodePathCollection ©) { - _node_paths = copy._node_paths; -} - /** * Adds a new NodePath to the collection. */ diff --git a/panda/src/pgraph/nodePathCollection.h b/panda/src/pgraph/nodePathCollection.h index 1d1ba9bd1f..9eb0c1ce90 100644 --- a/panda/src/pgraph/nodePathCollection.h +++ b/panda/src/pgraph/nodePathCollection.h @@ -25,10 +25,7 @@ */ class EXPCL_PANDA_PGRAPH NodePathCollection { PUBLISHED: - NodePathCollection(); - NodePathCollection(const NodePathCollection ©); - void operator = (const NodePathCollection ©); - INLINE ~NodePathCollection(); + NodePathCollection() DEFAULT_CTOR; #ifdef HAVE_PYTHON EXTENSION(NodePathCollection(PyObject *self, PyObject *sequence)); diff --git a/panda/src/pgraph/nodePathCollection_ext.cxx b/panda/src/pgraph/nodePathCollection_ext.cxx index c6f17b7aa4..32f5ac0d13 100644 --- a/panda/src/pgraph/nodePathCollection_ext.cxx +++ b/panda/src/pgraph/nodePathCollection_ext.cxx @@ -46,8 +46,7 @@ __init__(PyObject *self, PyObject *sequence) { } NodePath *path; - DTOOL_Call_ExtractThisPointerForType(item, &Dtool_NodePath, (void **)&path); - if (path == (NodePath *)NULL) { + if (!DtoolInstance_GetPointer(item, path, Dtool_NodePath)) { // Unable to add item--probably it wasn't of the appropriate type. ostringstream stream; stream << "Element " << i << " in sequence passed to NodePathCollection constructor is not a NodePath"; diff --git a/panda/src/pgraph/nodePathComponent.I b/panda/src/pgraph/nodePathComponent.I index fa16ebe2b9..d3abe36c07 100644 --- a/panda/src/pgraph/nodePathComponent.I +++ b/panda/src/pgraph/nodePathComponent.I @@ -75,6 +75,15 @@ has_key() const { return (_key != 0); } +/** + * Returns the next component in the path. + */ +INLINE NodePathComponent *NodePathComponent:: +get_next(int pipeline_stage, Thread *current_thread) const { + CDStageReader cdata(_cycler, pipeline_stage, current_thread); + return cdata->_next; +} + INLINE ostream &operator << (ostream &out, const NodePathComponent &comp) { comp.output(out); return out; diff --git a/panda/src/pgraph/nodePathComponent.cxx b/panda/src/pgraph/nodePathComponent.cxx index 4dbacc4539..a9f8a38788 100644 --- a/panda/src/pgraph/nodePathComponent.cxx +++ b/panda/src/pgraph/nodePathComponent.cxx @@ -92,17 +92,6 @@ get_length(int pipeline_stage, Thread *current_thread) const { return cdata->_length; } -/** - * Returns the next component in the path. - */ -NodePathComponent *NodePathComponent:: -get_next(int pipeline_stage, Thread *current_thread) const { - CDStageReader cdata(_cycler, pipeline_stage, current_thread); - NodePathComponent *next = cdata->_next; - - return next; -} - /** * Checks that the length indicated by the component is one more than the * length of its predecessor. If this is broken, fixes it and returns true diff --git a/panda/src/pgraph/nodePathComponent.h b/panda/src/pgraph/nodePathComponent.h index fbe4625c14..2106430b7d 100644 --- a/panda/src/pgraph/nodePathComponent.h +++ b/panda/src/pgraph/nodePathComponent.h @@ -39,7 +39,7 @@ * graph, and the NodePathComponents are stored in the nodes themselves to * allow the nodes to keep these up to date as the scene graph is manipulated. */ -class EXPCL_PANDA_PGRAPH NodePathComponent : public ReferenceCount { +class EXPCL_PANDA_PGRAPH NodePathComponent FINAL : public ReferenceCount { private: NodePathComponent(PandaNode *node, NodePathComponent *next, int pipeline_stage, Thread *current_thread); @@ -55,7 +55,7 @@ public: int get_key() const; bool is_top_node(int pipeline_stage, Thread *current_thread) const; - NodePathComponent *get_next(int pipeline_stage, Thread *current_thread) const; + INLINE NodePathComponent *get_next(int pipeline_stage, Thread *current_thread) const; int get_length(int pipeline_stage, Thread *current_thread) const; bool fix_length(int pipeline_stage, Thread *current_thread); @@ -125,6 +125,10 @@ private: friend class NodePath; }; +// We can safely redefine this as a no-op. +template<> +INLINE void PointerToBase::update_type(To *ptr) {} + INLINE ostream &operator << (ostream &out, const NodePathComponent &comp); #include "nodePathComponent.I" diff --git a/panda/src/pgraph/nodePath_ext.I b/panda/src/pgraph/nodePath_ext.I index 5bc6c78c5a..01554fb08d 100644 --- a/panda/src/pgraph/nodePath_ext.I +++ b/panda/src/pgraph/nodePath_ext.I @@ -32,6 +32,7 @@ get_python_tags() { /** * This variant on get_tag_keys returns a Python list of strings. Returns * None if the NodePath is empty. + * @deprecated use `np.tags.keys()` instead. */ INLINE PyObject *Extension:: get_tag_keys() const { diff --git a/panda/src/pgraph/nodePath_ext.cxx b/panda/src/pgraph/nodePath_ext.cxx index 081a69a814..1c7576290a 100644 --- a/panda/src/pgraph/nodePath_ext.cxx +++ b/panda/src/pgraph/nodePath_ext.cxx @@ -13,6 +13,8 @@ #include "nodePath_ext.h" #include "typedWritable_ext.h" +#include "shaderInput_ext.h" +#include "shaderAttrib.h" #ifdef HAVE_PYTHON @@ -24,6 +26,8 @@ extern struct Dtool_PyTypedObject Dtool_LPoint3d; #else extern struct Dtool_PyTypedObject Dtool_LPoint3f; #endif +extern struct Dtool_PyTypedObject Dtool_NodePath; +extern struct Dtool_PyTypedObject Dtool_PandaNode; #endif // CPPPARSER /** @@ -110,14 +114,14 @@ __reduce_persist__(PyObject *self, PyObject *pickler) const { // It's OK if there's no bamWriter. PyErr_Clear(); } else { - DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer); + DtoolInstance_GetPointer(py_writer, writer, Dtool_BamWriter); Py_DECREF(py_writer); } } // We have a non-empty NodePath. - string bam_stream; + vector_uchar bam_stream; if (!_this->encode_to_bam_stream(bam_stream, writer)) { ostringstream stream; stream << "Could not bamify " << _this; @@ -127,7 +131,7 @@ __reduce_persist__(PyObject *self, PyObject *pickler) const { } // Start by getting this class object. - PyObject *this_class = PyObject_Type(self); + PyObject *this_class = (PyObject *)Py_TYPE(self); if (this_class == NULL) { return NULL; } @@ -140,29 +144,50 @@ __reduce_persist__(PyObject *self, PyObject *pickler) const { func = Extension::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream_persist"); if (func == NULL) { PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream_persist()"); - Py_DECREF(this_class); return NULL; } } else { // The traditional pickle support: call the non-persistent version of this // function. - func = Extension::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream"); if (func == NULL) { PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream()"); - Py_DECREF(this_class); return NULL; } } -#if PY_MAJOR_VERSION >= 3 - PyObject *result = Py_BuildValue("(O(y#))", func, bam_stream.data(), (Py_ssize_t) bam_stream.size()); -#else - PyObject *result = Py_BuildValue("(O(s#))", func, bam_stream.data(), (Py_ssize_t) bam_stream.size()); -#endif - Py_DECREF(func); - Py_DECREF(this_class); + // PyTuple_SET_ITEM conveniently borrows the reference it is passed. + PyObject *args = PyTuple_New(1); + PyTuple_SET_ITEM(args, 0, Dtool_WrapValue(bam_stream)); + + PyObject *tuple = PyTuple_New(2); + PyTuple_SET_ITEM(tuple, 0, func); + PyTuple_SET_ITEM(tuple, 1, args); + return tuple; +} + +/** + * Returns the associated node's tags. + */ +PyObject *Extension:: +get_tags() const { + // An empty NodePath returns None + if (_this->is_empty()) { + Py_INCREF(Py_None); + return Py_None; + } + + // Just call PandaNode.tags rather than defining a whole new interface. + PT(PandaNode) node = _this->node(); + PyObject *py_node = DTool_CreatePyInstanceTyped + ((void *)node.p(), Dtool_PandaNode, true, false, node->get_type_index()); + + // DTool_CreatePyInstanceTyped() steals a C++ reference. + node.cheat() = nullptr; + + PyObject *result = PyObject_GetAttrString(py_node, "tags"); + Py_DECREF(py_node); return result; } @@ -187,15 +212,15 @@ find_net_python_tag(PyObject *key) const { * This wrapper is defined as a global function to suit pickle's needs. */ NodePath -py_decode_NodePath_from_bam_stream(const string &data) { - return py_decode_NodePath_from_bam_stream_persist(NULL, data); +py_decode_NodePath_from_bam_stream(vector_uchar data) { + return py_decode_NodePath_from_bam_stream_persist(nullptr, move(data)); } /** * This wrapper is defined as a global function to suit pickle's needs. */ NodePath -py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &data) { +py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, vector_uchar data) { BamReader *reader = NULL; if (unpickler != NULL) { PyObject *py_reader = PyObject_GetAttrString(unpickler, "bamReader"); @@ -203,12 +228,80 @@ py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &da // It's OK if there's no bamReader. PyErr_Clear(); } else { - DTOOL_Call_ExtractThisPointerForType(py_reader, &Dtool_BamReader, (void **)&reader); + DtoolInstance_GetPointer(py_reader, reader, Dtool_BamReader); Py_DECREF(py_reader); } } - return NodePath::decode_from_bam_stream(data, reader); + return NodePath::decode_from_bam_stream(move(data), reader); +} + +/** + * Sets a single shader input. + */ +void Extension:: +set_shader_input(CPT_InternalName name, PyObject *value, int priority) { + PT(PandaNode) node = _this->node(); + CPT(RenderAttrib) prev_attrib = node->get_attrib(ShaderAttrib::get_class_slot()); + PT(ShaderAttrib) attrib; + if (prev_attrib == nullptr) { + attrib = new ShaderAttrib(); + } else { + attrib = new ShaderAttrib(*(const ShaderAttrib *)prev_attrib.p()); + } + + ShaderInput &input = attrib->_inputs[name]; + invoke_extension(&input).__init__(move(name), value, priority); + + if (!_PyErr_OCCURRED()) { + node->set_attrib(ShaderAttrib::return_new(attrib)); + } +} + +/** + * Sets multiple shader inputs at the same time. This can be significantly + * more efficient if many inputs need to be set at the same time. + */ +void Extension:: +set_shader_inputs(PyObject *args, PyObject *kwargs) { + if (PyObject_Size(args) > 0) { + Dtool_Raise_TypeError("NodePath.set_shader_inputs takes only keyword arguments"); + return; + } + + PT(PandaNode) node = _this->node(); + CPT(RenderAttrib) prev_attrib = node->get_attrib(ShaderAttrib::get_class_slot()); + PT(ShaderAttrib) attrib; + if (prev_attrib == nullptr) { + attrib = new ShaderAttrib(); + } else { + attrib = new ShaderAttrib(*(const ShaderAttrib *)prev_attrib.p()); + } + + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next(kwargs, &pos, &key, &value)) { + char *buffer; + Py_ssize_t length; +#if PY_MAJOR_VERSION >= 3 + buffer = (char *)PyUnicode_AsUTF8AndSize(key, &length); + if (buffer == nullptr) { +#else + if (PyString_AsStringAndSize(key, &buffer, &length) == -1) { +#endif + Dtool_Raise_TypeError("NodePath.set_shader_inputs accepts only string keywords"); + return; + } + + CPT_InternalName name(string(buffer, length)); + ShaderInput &input = attrib->_inputs[name]; + invoke_extension(&input).__init__(move(name), value); + } + + if (!_PyErr_OCCURRED()) { + node->set_attrib(ShaderAttrib::return_new(attrib)); + } } /** diff --git a/panda/src/pgraph/nodePath_ext.h b/panda/src/pgraph/nodePath_ext.h index 3b0b8db42a..e736b4ed1b 100644 --- a/panda/src/pgraph/nodePath_ext.h +++ b/panda/src/pgraph/nodePath_ext.h @@ -34,6 +34,7 @@ public: PyObject *__reduce__(PyObject *self) const; PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const; + PyObject *get_tags() const; INLINE PyObject *get_tag_keys() const; INLINE PyObject *get_python_tags(); @@ -49,12 +50,15 @@ public: // This is defined to implement cycle detection in Python tags. INLINE int __traverse__(visitproc visit, void *arg); + void set_shader_input(CPT_InternalName id, PyObject *value, int priority=0); + void set_shader_inputs(PyObject *args, PyObject *kwargs); + PyObject *get_tight_bounds(const NodePath &other = NodePath()) const; }; BEGIN_PUBLISH -NodePath py_decode_NodePath_from_bam_stream(const string &data); -NodePath py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &data); +NodePath py_decode_NodePath_from_bam_stream(vector_uchar data); +NodePath py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, vector_uchar data); END_PUBLISH #include "nodePath_ext.I" diff --git a/panda/src/pgraph/occluderNode.h b/panda/src/pgraph/occluderNode.h index e9d8c67165..0bc4381f4d 100644 --- a/panda/src/pgraph/occluderNode.h +++ b/panda/src/pgraph/occluderNode.h @@ -30,7 +30,7 @@ */ class EXPCL_PANDA_PGRAPH OccluderNode : public PandaNode { PUBLISHED: - OccluderNode(const string &name); + explicit OccluderNode(const string &name); protected: OccluderNode(const OccluderNode ©); diff --git a/panda/src/pgraph/p3pgraph_ext_composite.cxx b/panda/src/pgraph/p3pgraph_ext_composite.cxx index 6dc854b914..d16f600c0a 100644 --- a/panda/src/pgraph/p3pgraph_ext_composite.cxx +++ b/panda/src/pgraph/p3pgraph_ext_composite.cxx @@ -2,4 +2,6 @@ #include "nodePathCollection_ext.cxx" #include "pandaNode_ext.cxx" #include "renderState_ext.cxx" +#include "shaderAttrib_ext.cxx" +#include "shaderInput_ext.cxx" #include "transformState_ext.cxx" diff --git a/panda/src/pgraph/pandaNode.I b/panda/src/pgraph/pandaNode.I index 747f322a36..de6682e911 100644 --- a/panda/src/pgraph/pandaNode.I +++ b/panda/src/pgraph/pandaNode.I @@ -348,12 +348,12 @@ has_dirty_prev_transform() const { INLINE string PandaNode:: get_tag(const string &key, Thread *current_thread) const { CDReader cdata(_cycler, current_thread); - TagData::const_iterator ti; - ti = cdata->_tag_data.find(key); - if (ti != cdata->_tag_data.end()) { - return (*ti).second; + int index = cdata->_tag_data.find(key); + if (index >= 0) { + return cdata->_tag_data.get_data((size_t)index); + } else { + return string(); } - return string(); } /** @@ -364,9 +364,25 @@ get_tag(const string &key, Thread *current_thread) const { INLINE bool PandaNode:: has_tag(const string &key, Thread *current_thread) const { CDReader cdata(_cycler, current_thread); - TagData::const_iterator ti; - ti = cdata->_tag_data.find(key); - return (ti != cdata->_tag_data.end()); + return cdata->_tag_data.find(key) >= 0; +} + +/** + * Returns the number of tags applied to this node. + */ +INLINE size_t PandaNode:: +get_num_tags() const { + CDReader cdata(_cycler); + return cdata->_tag_data.size(); +} + +/** + * Returns the key of the nth tag applied to this node. + */ +INLINE string PandaNode:: +get_tag_key(size_t i) const { + CDReader cdata(_cycler); + return cdata->_tag_data.get_key(i); } /** @@ -376,7 +392,7 @@ has_tag(const string &key, Thread *current_thread) const { INLINE bool PandaNode:: has_tags() const { CDReader cdata(_cycler); - if (!cdata->_tag_data.empty()) { + if (!cdata->_tag_data.is_empty()) { return true; } #ifdef HAVE_PYTHON @@ -1448,12 +1464,12 @@ get_prev_transform() const { */ INLINE string PandaNodePipelineReader:: get_tag(const string &key) const { - PandaNode::TagData::const_iterator ti; - ti = _cdata->_tag_data.find(key); - if (ti != _cdata->_tag_data.end()) { - return (*ti).second; + int index = _cdata->_tag_data.find(key); + if (index >= 0) { + return _cdata->_tag_data.get_data((size_t)index); + } else { + return string(); } - return string(); } /** @@ -1463,9 +1479,7 @@ get_tag(const string &key) const { */ INLINE bool PandaNodePipelineReader:: has_tag(const string &key) const { - PandaNode::TagData::const_iterator ti; - ti = _cdata->_tag_data.find(key); - return (ti != _cdata->_tag_data.end()); + return _cdata->_tag_data.find(key) >= 0; } /** @@ -1482,7 +1496,7 @@ get_net_collide_mask() const { * Returns a ClipPlaneAttrib which represents the union of all of the clip * planes that have been turned *off* at this level and below. */ -INLINE CPT(RenderAttrib) PandaNodePipelineReader:: +INLINE const RenderAttrib *PandaNodePipelineReader:: get_off_clip_planes() const { nassertr(_cdata->_last_update == _cdata->_next_update, _cdata->_off_clip_planes); return _cdata->_off_clip_planes; @@ -1493,7 +1507,7 @@ get_off_clip_planes() const { * contains the user bounding volume, the internal bounding volume, and all of * the children's bounding volumes. */ -INLINE CPT(BoundingVolume) PandaNodePipelineReader:: +INLINE const BoundingVolume *PandaNodePipelineReader:: get_bounds() const { nassertr(_cdata->_last_bounds_update == _cdata->_next_update, _cdata->_external_bounds); return _cdata->_external_bounds; diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index f74f7fc24d..0d64cb0828 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -1216,7 +1216,7 @@ set_tag(const string &key, const string &value, Thread *current_thread) { // stages. OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) { CDStageWriter cdata(_cycler, pipeline_stage, current_thread); - cdata->_tag_data[key] = value; + cdata->_tag_data.store(key, value); cdata->set_fancy_bit(FB_tag, true); } CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); @@ -1231,8 +1231,8 @@ void PandaNode:: clear_tag(const string &key, Thread *current_thread) { OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) { CDStageWriter cdata(_cycler, pipeline_stage, current_thread); - cdata->_tag_data.erase(key); - cdata->set_fancy_bit(FB_tag, !cdata->_tag_data.empty()); + cdata->_tag_data.remove(key); + cdata->set_fancy_bit(FB_tag, !cdata->_tag_data.is_empty()); } CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); mark_bam_modified(); @@ -1257,13 +1257,10 @@ copy_tags(PandaNode *other) { CDStageWriter cdataw(_cycler, pipeline_stage, current_thread); CDStageReader cdatar(other->_cycler, pipeline_stage, current_thread); - TagData::const_iterator ti; - for (ti = cdatar->_tag_data.begin(); - ti != cdatar->_tag_data.end(); - ++ti) { - cdataw->_tag_data[(*ti).first] = (*ti).second; + for (size_t n = 0; n < cdatar->_tag_data.size(); ++n) { + cdataw->_tag_data.store(cdatar->_tag_data.get_key(n), cdatar->_tag_data.get_data(n)); } - cdataw->set_fancy_bit(FB_tag, !cdataw->_tag_data.empty()); + cdataw->set_fancy_bit(FB_tag, !cdataw->_tag_data.is_empty()); } CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); @@ -1286,14 +1283,11 @@ copy_tags(PandaNode *other) { void PandaNode:: list_tags(ostream &out, const string &separator) const { CDReader cdata(_cycler); - if (!cdata->_tag_data.empty()) { - TagData::const_iterator ti = cdata->_tag_data.begin(); - out << (*ti).first; - ++ti; - while (ti != cdata->_tag_data.end()) { - out << separator << (*ti).first; - ++ti; + for (size_t n = 0; n < cdata->_tag_data.size(); ++n) { + if (n > 0) { + out << separator; } + out << cdata->_tag_data.get_key(n); } // We used to list the Python tags here. That's a bit awkward, though, @@ -1310,12 +1304,8 @@ list_tags(ostream &out, const string &separator) const { void PandaNode:: get_tag_keys(vector_string &keys) const { CDReader cdata(_cycler); - if (!cdata->_tag_data.empty()) { - TagData::const_iterator ti = cdata->_tag_data.begin(); - while (ti != cdata->_tag_data.end()) { - keys.push_back((*ti).first); - ++ti; - } + for (size_t n = 0; n < cdata->_tag_data.size(); ++n) { + keys.push_back(cdata->_tag_data.get_key(n)); } } @@ -1331,28 +1321,30 @@ compare_tags(const PandaNode *other) const { CDReader cdata(_cycler); CDReader cdata_other(other->_cycler); - TagData::const_iterator ati = cdata->_tag_data.begin(); - TagData::const_iterator bti = cdata_other->_tag_data.begin(); - while (ati != cdata->_tag_data.end() && - bti != cdata_other->_tag_data.end()) { - int cmp = strcmp((*ati).first.c_str(), (*bti).first.c_str()); + const TagData &a_data = cdata->_tag_data; + const TagData &b_data = cdata_other->_tag_data; + + size_t ai = 0; + size_t bi = 0; + while (ai < a_data.size() && bi < b_data.size()) { + int cmp = strcmp(a_data.get_key(ai).c_str(), b_data.get_key(bi).c_str()); if (cmp != 0) { return cmp; } - cmp = strcmp((*ati).second.c_str(), (*bti).second.c_str()); + cmp = strcmp(a_data.get_key(ai).c_str(), b_data.get_key(bi).c_str()); if (cmp != 0) { return cmp; } - ++ati; - ++bti; + ++ai; + ++bi; } - if (ati != cdata->_tag_data.end()) { + if (ai < a_data.size()) { // list A is longer. return 1; } - if (bti != cdata_other->_tag_data.end()) { + if (bi < b_data.size()) { // list B is longer. return -1; } @@ -1412,11 +1404,8 @@ copy_all_properties(PandaNode *other) { // to preserve properties such as the default GeomNode bitmask. cdataw->_into_collide_mask |= cdatar->_into_collide_mask; - TagData::const_iterator ti; - for (ti = cdatar->_tag_data.begin(); - ti != cdatar->_tag_data.end(); - ++ti) { - cdataw->_tag_data[(*ti).first] = (*ti).second; + for (size_t n = 0; n < cdatar->_tag_data.size(); ++n) { + cdataw->_tag_data.store(cdatar->_tag_data.get_key(n), cdatar->_tag_data.get_data(n)); } static const int change_bits = (FB_transform | FB_state | FB_effects | @@ -2124,8 +2113,8 @@ is_ambient_light() const { } /** - * Reads the string created by a previous call to encode_to_bam_stream(), and - * extracts and returns the single object on that string. Returns NULL on + * Reads the bytes created by a previous call to encode_to_bam_stream(), and + * extracts and returns the single object on those bytes. Returns NULL on * error. * * This method is intended to replace decode_raw_from_bam_stream() when you @@ -2134,15 +2123,15 @@ is_ambient_light() const { * responsible for maintaining the reference count on the return value. */ PT(PandaNode) PandaNode:: -decode_from_bam_stream(const string &data, BamReader *reader) { +decode_from_bam_stream(vector_uchar data, BamReader *reader) { TypedWritable *object; ReferenceCount *ref_ptr; - if (!TypedWritable::decode_raw_from_bam_stream(object, ref_ptr, data, reader)) { - return NULL; + if (TypedWritable::decode_raw_from_bam_stream(object, ref_ptr, move(data), reader)) { + return DCAST(PandaNode, object); + } else { + return nullptr; } - - return DCAST(PandaNode, object); } /** @@ -3775,13 +3764,11 @@ write_datagram(BamWriter *manager, Datagram &dg) const { dg.add_uint8(_bounds_type); dg.add_uint32(_tag_data.size()); - TagData::const_iterator ti; - for (ti = _tag_data.begin(); ti != _tag_data.end(); ++ti) { - dg.add_string((*ti).first); - dg.add_string((*ti).second); + for (size_t n = 0; n < _tag_data.size(); ++n) { + dg.add_string(_tag_data.get_key(n)); + dg.add_string(_tag_data.get_data(n)); } - write_up_list(*get_up(), manager, dg); write_down_list(*get_down(), manager, dg); write_down_list(*get_stashed(), manager, dg); @@ -3855,7 +3842,7 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { set_fancy_bit(FB_transform, !_transform->is_identity()); set_fancy_bit(FB_state, !_state->is_empty()); set_fancy_bit(FB_effects, !_effects->is_empty()); - set_fancy_bit(FB_tag, !_tag_data.empty()); + set_fancy_bit(FB_tag, !_tag_data.is_empty()); // Mark the bounds stale. ++_next_update; @@ -3914,7 +3901,7 @@ fillin(DatagramIterator &scan, BamReader *manager) { for (int i = 0; i < num_tags; i++) { string key = scan.get_string(); string value = scan.get_string(); - _tag_data[key] = value; + _tag_data.store(key, value); } diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 0bbc984117..1b42e5ed2d 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -45,6 +45,7 @@ #include "copyOnWritePointer.h" #include "lightReMutex.h" #include "extension.h" +#include "simpleHashMap.h" class NodePathComponent; class CullTraverser; @@ -196,7 +197,15 @@ PUBLISHED: Thread *current_thread = Thread::get_current_thread()) const; void clear_tag(const string &key, Thread *current_thread = Thread::get_current_thread()); + +public: void get_tag_keys(vector_string &keys) const; + INLINE size_t get_num_tags() const; + INLINE string get_tag_key(size_t i) const; + +PUBLISHED: + MAKE_MAP_PROPERTY(tags, has_tag, get_tag, set_tag, clear_tag); + MAKE_MAP_KEYS_SEQ(tags, get_num_tags, get_tag_key); EXTENSION(PyObject *get_tag_keys() const); @@ -234,6 +243,8 @@ PUBLISHED: INLINE static DrawMask get_all_camera_mask(); INLINE bool is_overall_hidden() const; INLINE void set_overall_hidden(bool overall_hidden); + MAKE_PROPERTY(overall_bit, get_overall_bit); + MAKE_PROPERTY(all_camera_mask, get_all_camera_mask); MAKE_PROPERTY(overall_hidden, is_overall_hidden, set_overall_hidden); void adjust_draw_mask(DrawMask show_mask, @@ -319,7 +330,7 @@ PUBLISHED: INLINE int get_fancy_bits(Thread *current_thread = Thread::get_current_thread()) const; PUBLISHED: - static PT(PandaNode) decode_from_bam_stream(const string &data, BamReader *reader = NULL); + static PT(PandaNode) decode_from_bam_stream(vector_uchar data, BamReader *reader = nullptr); protected: class BoundsData; @@ -509,7 +520,7 @@ private: // This is used to maintain a table of keyed data on each node, for the // user's purposes. - typedef phash_map TagData; + typedef SimpleHashMap TagData; // This is actually implemented in pandaNode_ext.h, but defined here so // that we can destruct it from the C++ side. Note that it isn't cycled, @@ -799,12 +810,10 @@ public: return _type_handle; } static void init_type() { - TypedWritable::init_type(); - ReferenceCount::init_type(); + TypedWritableReferenceCount::init_type(); Namable::init_type(); register_type(_type_handle, "PandaNode", - TypedWritable::get_class_type(), - ReferenceCount::get_class_type(), + TypedWritableReferenceCount::get_class_type(), Namable::get_class_type()); CData::init_type(); Down::init_type(); @@ -829,6 +838,7 @@ private: friend class PandaNodePipelineReader; friend class EggLoader; friend class Extension; + friend class CullTraverserData; }; /** @@ -879,8 +889,8 @@ public: INLINE bool has_tag(const string &key) const; INLINE CollideMask get_net_collide_mask() const; - INLINE CPT(RenderAttrib) get_off_clip_planes() const; - INLINE CPT(BoundingVolume) get_bounds() const; + INLINE const RenderAttrib *get_off_clip_planes() const; + INLINE const BoundingVolume *get_bounds() const; INLINE int get_nested_vertices() const; INLINE bool is_final() const; INLINE int get_fancy_bits() const; @@ -908,6 +918,10 @@ private: }; +// We can safely redefine this as a no-op. +template<> +INLINE void PointerToBase::update_type(To *ptr) {} + INLINE ostream &operator << (ostream &out, const PandaNode &node) { node.output(out); return out; diff --git a/panda/src/pgraph/planeNode.h b/panda/src/pgraph/planeNode.h index 1c0a70c9ff..463fc39b22 100644 --- a/panda/src/pgraph/planeNode.h +++ b/panda/src/pgraph/planeNode.h @@ -35,7 +35,7 @@ */ class EXPCL_PANDA_PGRAPH PlaneNode : public PandaNode { PUBLISHED: - PlaneNode(const string &name, const LPlane &plane = LPlane()); + explicit PlaneNode(const string &name, const LPlane &plane = LPlane()); protected: PlaneNode(const PlaneNode ©); diff --git a/panda/src/pgraph/polylightEffect.cxx b/panda/src/pgraph/polylightEffect.cxx index f2c49b7f91..acbd41b18f 100644 --- a/panda/src/pgraph/polylightEffect.cxx +++ b/panda/src/pgraph/polylightEffect.cxx @@ -136,9 +136,9 @@ do_poly_light(const SceneSetup *scene, const CullTraverserData *data, const Tran if (light->is_enabled()) { // if enabled get all the properties PN_stdfloat light_radius = light->get_radius(); // Calculate the distance of the node from the light dist = - // light_iter->second->get_distance(data->_node_path.get_node_path()); + // light_iter->second->get_distance(data->get_node_path()); const NodePath lightnp = *light_iter; - LPoint3 relative_point = data->_node_path.get_node_path().get_relative_point(lightnp, light->get_pos()); + LPoint3 relative_point = data->get_node_path().get_relative_point(lightnp, light->get_pos()); if (_effect_center[2]) { dist = (relative_point - _effect_center).length(); // this counts height difference @@ -155,7 +155,7 @@ do_poly_light(const SceneSetup *scene, const CullTraverserData *data, const Tran // LPoint3 camera_position = camera.get_relative_point(lightnp, // light->get_pos()); LPoint3 camera_position = lightnp.get_relative_point(camera, LPoint3(0,0,0)); - LPoint3 avatar_position = lightnp.get_relative_point(data->_node_path.get_node_path(), LPoint3(0,0,0)); + LPoint3 avatar_position = lightnp.get_relative_point(data->get_node_path(), LPoint3(0,0,0)); LVector3 light_camera = camera_position - light_position; LVector3 light_avatar = avatar_position - light_position; light_camera.normalize(); @@ -263,7 +263,7 @@ do_poly_light(const SceneSetup *scene, const CullTraverserData *data, const Tran if (num_lights) { // was_under_polylight = true; - // data->_node_path.get_node_path().set_color_scale_off(); + // data->get_node_path().set_color_scale_off(); if (polylight_info) pgraph_cat.debug() << "num lights = " << num_lights << endl; @@ -320,8 +320,8 @@ do_poly_light(const SceneSetup *scene, const CullTraverserData *data, const Tran else { if (was_under_polylight) { // under no polylight influence...so clear the color scale - // data->_node_path.get_node_path().clear_color_scale(); - // data->_node_path.get_node_path().set_color_scale(scene_color); + // data->get_node_path().clear_color_scale(); + // data->get_node_path().set_color_scale(scene_color); was_under_polylight = false; } } diff --git a/panda/src/pgraph/polylightNode.h b/panda/src/pgraph/polylightNode.h index 1b8c236e0b..cb01427e58 100644 --- a/panda/src/pgraph/polylightNode.h +++ b/panda/src/pgraph/polylightNode.h @@ -52,7 +52,7 @@ PUBLISHED: AQUADRATIC, }; - PolylightNode(const string &name); + explicit PolylightNode(const string &name); INLINE void enable(); INLINE void disable(); INLINE void set_pos(const LPoint3 &position); diff --git a/panda/src/pgraph/portalClipper.cxx b/panda/src/pgraph/portalClipper.cxx index 7b9e55556b..f6d45aab8f 100644 --- a/panda/src/pgraph/portalClipper.cxx +++ b/panda/src/pgraph/portalClipper.cxx @@ -39,7 +39,7 @@ PortalClipper:: PortalClipper(GeometricBoundingVolume *frustum, SceneSetup *scene_setup): _reduced_viewport_min(-1,-1), _reduced_viewport_max(1,1), -_clip_state(0) +_clip_state(NULL) { _previous = new GeomNode("my_frustum"); diff --git a/panda/src/pgraph/portalClipper.h b/panda/src/pgraph/portalClipper.h index a1c8f5b179..b6cee9e3a7 100644 --- a/panda/src/pgraph/portalClipper.h +++ b/panda/src/pgraph/portalClipper.h @@ -119,7 +119,7 @@ private: LPoint2 _reduced_viewport_max; CPT(RenderState) _clip_state; // each portal node needs to know the clip state of its "parent" portal Node - PortalNode *_portal_node; // current working portal for dereference ease + const PortalNode *_portal_node; // current working portal for dereference ease // int _num_vert; LVertex _coords[4]; diff --git a/panda/src/pgraph/portalNode.cxx b/panda/src/pgraph/portalNode.cxx index d70eacd17e..dccfef23c4 100644 --- a/panda/src/pgraph/portalNode.cxx +++ b/panda/src/pgraph/portalNode.cxx @@ -213,7 +213,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { portal_viewer->get_reduced_viewport(old_reduced_viewport_min, old_reduced_viewport_max); PT(BoundingHexahedron) old_bh = portal_viewer->get_reduced_frustum(); - if (portal_viewer->prepare_portal(data._node_path.get_node_path())) { + if (portal_viewer->prepare_portal(data.get_node_path())) { if ((reduced_frustum = portal_viewer->get_reduced_frustum())) { // remember current clip state, we might change it CPT(RenderState) old_clip_state = portal_viewer->get_clip_state(); @@ -241,7 +241,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { // camera space to this portal node's space (because the clip planes // are attached to this node) PT(BoundingHexahedron) temp_bh = DCAST(BoundingHexahedron, vf->make_copy()); - CPT(TransformState) temp_frustum_transform = data._node_path.get_node_path().get_net_transform()->invert_compose(portal_viewer->_scene_setup->get_cull_center().get_net_transform()); + CPT(TransformState) temp_frustum_transform = data.get_node_path().get_net_transform()->invert_compose(portal_viewer->_scene_setup->get_cull_center().get_net_transform()); portal_cat.spam() << "clipping plane frustum transform " << *temp_frustum_transform << endl; portal_cat.spam() << "frustum before transform " << *temp_bh << endl; diff --git a/panda/src/pgraph/portalNode.h b/panda/src/pgraph/portalNode.h index 3b5c9ae7a3..fefe5b6bdb 100644 --- a/panda/src/pgraph/portalNode.h +++ b/panda/src/pgraph/portalNode.h @@ -29,8 +29,8 @@ */ class EXPCL_PANDA_PGRAPH PortalNode : public PandaNode { PUBLISHED: - PortalNode(const string &name); - PortalNode(const string &name, LPoint3 pos, PN_stdfloat scale=10.0); + explicit PortalNode(const string &name); + explicit PortalNode(const string &name, LPoint3 pos, PN_stdfloat scale=10.0); protected: PortalNode(const PortalNode ©); diff --git a/panda/src/pgraph/renderAttrib.I b/panda/src/pgraph/renderAttrib.I index 9e431d7d6a..5df0d0774e 100644 --- a/panda/src/pgraph/renderAttrib.I +++ b/panda/src/pgraph/renderAttrib.I @@ -82,25 +82,6 @@ get_unique() const { return return_unique((RenderAttrib *)this); } -/** - * Returns the variant of this RenderAttrib that's most relevant for - * associating with an auto-generated shader. This should be a new - * RenderAttrib of the same type as this one, with any superfluous data set to - * neutral. Only the parts of the attrib that contribute to the shader should - * be reflected in the returned attrib. The idea is to associate the auto- - * generated shader with the most neutral form of all states, to allow it to - * be shared across as many RenderState objects as possible. - * - * If this RenderAttrib is completely irrelevant to the auto-shader, this - * should return NULL to indicate that the attrib won't be assocaited with the - * shader at all. In this case the attrib does not contribute to the shader - * meaningfully. - */ -INLINE CPT(RenderAttrib) RenderAttrib:: -get_auto_shader_attrib(const RenderState *state) const { - return get_auto_shader_attrib_impl(state); -} - /** * Calculates a suitable hash value for phash_map. */ diff --git a/panda/src/pgraph/renderAttrib.cxx b/panda/src/pgraph/renderAttrib.cxx index 334898a510..385d7ee740 100644 --- a/panda/src/pgraph/renderAttrib.cxx +++ b/panda/src/pgraph/renderAttrib.cxx @@ -22,7 +22,7 @@ LightReMutex *RenderAttrib::_attribs_lock = NULL; RenderAttrib::Attribs *RenderAttrib::_attribs = NULL; TypeHandle RenderAttrib::_type_handle; -int RenderAttrib::_garbage_index = 0; +size_t RenderAttrib::_garbage_index = 0; PStatCollector RenderAttrib::_garbage_collect_pcollector("*:State Cache:Garbage Collect"); @@ -112,14 +112,6 @@ cull_callback(CullTraverser *, const CullTraverserData &) const { return true; } -/** - * - */ -CPT(RenderAttrib) RenderAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return NULL; -} - /** * This method overrides ReferenceCount::unref() to clear the pointer from the * global object pool when its reference count goes to zero. @@ -194,12 +186,9 @@ void RenderAttrib:: list_attribs(ostream &out) { LightReMutexHolder holder(*_attribs_lock); - out << _attribs->get_num_entries() << " attribs:\n"; - int size = _attribs->get_size(); - for (int si = 0; si < size; ++si) { - if (!_attribs->has_element(si)) { - continue; - } + size_t size = _attribs->get_num_entries(); + out << size << " attribs:\n"; + for (size_t si = 0; si < size; ++si) { const RenderAttrib *attrib = _attribs->get_key(si); attrib->write(out, 2); } @@ -217,43 +206,60 @@ garbage_collect() { LightReMutexHolder holder(*_attribs_lock); PStatTimer timer(_garbage_collect_pcollector); - int orig_size = _attribs->get_num_entries(); + size_t orig_size = _attribs->get_num_entries(); +#ifdef _DEBUG nassertr(_attribs->validate(), 0); +#endif // How many elements to process this pass? - int size = _attribs->get_size(); - int num_this_pass = int(size * garbage_collect_states_rate); + size_t size = orig_size; + size_t num_this_pass = max(0, int(size * garbage_collect_states_rate)); if (num_this_pass <= 0) { return 0; } - num_this_pass = min(num_this_pass, size); - int stop_at_element = (_garbage_index + num_this_pass) % size; - int num_elements = 0; - int si = _garbage_index; + size_t si = _garbage_index; + if (si >= size) { + si = 0; + } + + num_this_pass = min(num_this_pass, size); + size_t stop_at_element = (_garbage_index + num_this_pass) % size; + do { - if (_attribs->has_element(si)) { - ++num_elements; - RenderAttrib *attrib = (RenderAttrib *)_attribs->get_key(si); - if (attrib->get_ref_count() == 1) { - // This attrib has recently been unreffed to 1 (the one we added when - // we stored it in the cache). Now it's time to delete it. This is - // safe, because we're holding the _attribs_lock, so it's not possible - // for some other thread to find the attrib in the cache and ref it - // while we're doing this. - attrib->release_new(); - unref_delete(attrib); - } + RenderAttrib *attrib = (RenderAttrib *)_attribs->get_key(si); + if (attrib->get_ref_count() == 1) { + // This attrib has recently been unreffed to 1 (the one we added when + // we stored it in the cache). Now it's time to delete it. This is + // safe, because we're holding the _attribs_lock, so it's not possible + // for some other thread to find the attrib in the cache and ref it + // while we're doing this. + attrib->release_new(); + unref_delete(attrib); + + // When we removed it from the hash map, it swapped the last element + // with the one we just removed. So the current index contains one we + // still need to visit. + --size; + --si; } si = (si + 1) % size; } while (si != stop_at_element); _garbage_index = si; - nassertr(_attribs->validate(), 0); - int new_size = _attribs->get_num_entries(); - return orig_size - new_size; + nassertr(_attribs->get_num_entries() == size, 0); + +#ifdef _DEBUG + nassertr(_attribs->validate(), 0); +#endif + + // If we just cleaned up a lot of attribs, see if we can reduce the table in + // size. This will help reduce iteration overhead in the future. + _attribs->consider_shrink_table(); + + return (int)orig_size - (int)size; } /** @@ -272,31 +278,22 @@ validate_attribs() { pgraph_cat.error() << "RenderAttrib::_attribs cache is invalid!\n"; - int size = _attribs->get_size(); - for (int si = 0; si < size; ++si) { - if (!_attribs->has_element(si)) { - continue; - } + size_t size = _attribs->get_num_entries(); + for (size_t si = 0; si < size; ++si) { const RenderAttrib *attrib = _attribs->get_key(si); - cerr << si << ": " << attrib << "\n"; + //cerr << si << ": " << attrib << "\n"; attrib->write(cerr, 2); } return false; } - int size = _attribs->get_size(); - int si = 0; - while (si < size && !_attribs->has_element(si)) { - ++si; - } + size_t size = _attribs->get_num_entries(); + size_t si = 0; nassertr(si < size, false); nassertr(_attribs->get_key(si)->get_ref_count() >= 0, false); - int snext = si; + size_t snext = si; ++snext; - while (snext < size && !_attribs->has_element(snext)) { - ++snext; - } while (snext < size) { nassertr(_attribs->get_key(snext)->get_ref_count() >= 0, false); const RenderAttrib *ssi = _attribs->get_key(si); @@ -318,9 +315,6 @@ validate_attribs() { } si = snext; ++snext; - while (snext < size && !_attribs->has_element(snext)) { - ++snext; - } } return true; @@ -395,7 +389,7 @@ return_unique(RenderAttrib *attrib) { // deleted while it's in it. attrib->ref(); } - si = _attribs->store(attrib, Empty()); + si = _attribs->store(attrib, nullptr); // Save the index and return the input attrib. attrib->_saved_entry = si; @@ -511,10 +505,8 @@ release_new() { nassertv(_attribs_lock->debug_is_locked()); if (_saved_entry != -1) { - // nassertv(_attribs->find(this) == _saved_entry); - _saved_entry = _attribs->find(this); - _attribs->remove_element(_saved_entry); _saved_entry = -1; + nassertv_always(_attribs->remove(this)); } } diff --git a/panda/src/pgraph/renderAttrib.h b/panda/src/pgraph/renderAttrib.h index 12dd33b720..5bf54cf32f 100644 --- a/panda/src/pgraph/renderAttrib.h +++ b/panda/src/pgraph/renderAttrib.h @@ -71,7 +71,6 @@ PUBLISHED: INLINE int compare_to(const RenderAttrib &other) const; INLINE size_t get_hash() const; INLINE CPT(RenderAttrib) get_unique() const; - INLINE CPT(RenderAttrib) get_auto_shader_attrib(const RenderState *state) const; virtual bool unref() const FINAL; @@ -84,6 +83,7 @@ PUBLISHED: static bool validate_attribs(); virtual int get_slot() const=0; + MAKE_PROPERTY(slot, get_slot); enum PandaCompareFunc { // intentionally defined to match D3DCMPFUNC M_none=0, // alpha-test disabled (always-draw) @@ -170,7 +170,6 @@ protected: virtual size_t get_hash_impl() const; virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const; virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; void output_comparefunc(ostream &out, PandaCompareFunc fn) const; public: @@ -186,9 +185,7 @@ public: private: // This mutex protects _attribs. static LightReMutex *_attribs_lock; - class Empty { - }; - typedef SimpleHashMap > Attribs; + typedef SimpleHashMap > Attribs; static Attribs *_attribs; int _saved_entry; @@ -196,7 +193,7 @@ private: // This keeps track of our current position through the garbage collection // cycle. - static int _garbage_index; + static size_t _garbage_index; static PStatCollector _garbage_collect_pcollector; diff --git a/panda/src/pgraph/renderAttribRegistry.cxx b/panda/src/pgraph/renderAttribRegistry.cxx index cd011e838d..4c27e4c0a7 100644 --- a/panda/src/pgraph/renderAttribRegistry.cxx +++ b/panda/src/pgraph/renderAttribRegistry.cxx @@ -93,7 +93,7 @@ register_slot(TypeHandle type_handle, int sort, RenderAttrib *default_attrib) { // If this attribute was already registered, something odd is going on. nassertr(RenderAttrib::_attribs->find(default_attrib) == -1, 0); default_attrib->_saved_entry = - RenderAttrib::_attribs->store(default_attrib, RenderAttrib::Empty()); + RenderAttrib::_attribs->store(default_attrib, nullptr); } // It effectively lives forever. Might as well make it official. diff --git a/panda/src/pgraph/renderEffect.cxx b/panda/src/pgraph/renderEffect.cxx index ceada3a109..09572cc445 100644 --- a/panda/src/pgraph/renderEffect.cxx +++ b/panda/src/pgraph/renderEffect.cxx @@ -160,7 +160,7 @@ has_adjust_transform() const { */ void RenderEffect:: adjust_transform(CPT(TransformState) &, CPT(TransformState) &, - PandaNode *) const { + const PandaNode *) const { } /** diff --git a/panda/src/pgraph/renderEffect.h b/panda/src/pgraph/renderEffect.h index f8a55de869..d739f5cee7 100644 --- a/panda/src/pgraph/renderEffect.h +++ b/panda/src/pgraph/renderEffect.h @@ -68,7 +68,7 @@ public: virtual bool has_adjust_transform() const; virtual void adjust_transform(CPT(TransformState) &net_transform, CPT(TransformState) &node_transform, - PandaNode *node) const; + const PandaNode *node) const; PUBLISHED: INLINE int compare_to(const RenderEffect &other) const; diff --git a/panda/src/pgraph/renderEffects.I b/panda/src/pgraph/renderEffects.I index ff42b4ff49..23c00f9a3b 100644 --- a/panda/src/pgraph/renderEffects.I +++ b/panda/src/pgraph/renderEffects.I @@ -97,8 +97,9 @@ is_empty() const { /** * Returns the number of separate effects indicated in the state. + * @deprecated in Python, use len(effects) instead, or effects.size() in C++. */ -INLINE int RenderEffects:: +INLINE size_t RenderEffects:: get_num_effects() const { return _effects.size(); } @@ -107,11 +108,36 @@ get_num_effects() const { * Returns the nth effect in the state. */ INLINE const RenderEffect *RenderEffects:: -get_effect(int n) const { - nassertr(n >= 0 && n < (int)_effects.size(), NULL); +get_effect(size_t n) const { + nassertr(n < _effects.size(), nullptr); return _effects[n]._effect; } +/** + * Returns the number of separate effects indicated in the state. + */ +INLINE size_t RenderEffects:: +size() const { + return _effects.size(); +} + +/** + * Returns the nth effect in the state. + */ +INLINE const RenderEffect *RenderEffects:: +operator [](size_t n) const { + nassertr(n < _effects.size(), nullptr); + return _effects[n]._effect; +} + +/** + * Returns the effect in the state with the given type. + */ +INLINE const RenderEffect *RenderEffects:: +operator [](TypeHandle type) const { + return get_effect(type); +} + /** * This function is provided as an optimization, to speed up the render-time * checking for the existance of a DecalEffect on this state. It returns true diff --git a/panda/src/pgraph/renderEffects.cxx b/panda/src/pgraph/renderEffects.cxx index 18c7565a3f..639f11be9b 100644 --- a/panda/src/pgraph/renderEffects.cxx +++ b/panda/src/pgraph/renderEffects.cxx @@ -492,7 +492,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data, void RenderEffects:: adjust_transform(CPT(TransformState) &net_transform, CPT(TransformState) &node_transform, - PandaNode *node) const { + const PandaNode *node) const { Effects::const_iterator ei; for (ei = _effects.begin(); ei != _effects.end(); ++ei) { (*ei)._effect->adjust_transform(net_transform, node_transform, node); diff --git a/panda/src/pgraph/renderEffects.h b/panda/src/pgraph/renderEffects.h index e1ece7d2b3..1e54c30533 100644 --- a/panda/src/pgraph/renderEffects.h +++ b/panda/src/pgraph/renderEffects.h @@ -58,8 +58,12 @@ PUBLISHED: bool operator < (const RenderEffects &other) const; INLINE bool is_empty() const; - INLINE int get_num_effects() const; - INLINE const RenderEffect *get_effect(int n) const; + INLINE size_t get_num_effects() const; + INLINE const RenderEffect *get_effect(size_t n) const; + + INLINE size_t size() const; + INLINE const RenderEffect *operator [] (size_t n) const; + INLINE const RenderEffect *operator [] (TypeHandle type) const; int find_effect(TypeHandle type) const; @@ -102,7 +106,7 @@ public: INLINE bool has_adjust_transform() const; void adjust_transform(CPT(TransformState) &net_transform, CPT(TransformState) &node_transform, - PandaNode *node) const; + const PandaNode *node) const; static void init_states(); diff --git a/panda/src/pgraph/renderModeAttrib.h b/panda/src/pgraph/renderModeAttrib.h index 631789dd60..21a32ee5de 100644 --- a/panda/src/pgraph/renderModeAttrib.h +++ b/panda/src/pgraph/renderModeAttrib.h @@ -92,6 +92,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/renderState.I b/panda/src/pgraph/renderState.I index c6d0520ec4..185b18b24d 100644 --- a/panda/src/pgraph/renderState.I +++ b/panda/src/pgraph/renderState.I @@ -237,7 +237,7 @@ get_invert_composition_cache_num_entries() const { INLINE size_t RenderState:: get_composition_cache_size() const { LightReMutexHolder holder(*_states_lock); - return _composition_cache.get_size(); + return _composition_cache.get_num_entries(); } /** @@ -251,9 +251,6 @@ get_composition_cache_size() const { INLINE const RenderState *RenderState:: get_composition_cache_source(size_t n) const { LightReMutexHolder holder(*_states_lock); - if (!_composition_cache.has_element(n)) { - return NULL; - } return _composition_cache.get_key(n); } @@ -270,9 +267,6 @@ get_composition_cache_source(size_t n) const { INLINE const RenderState *RenderState:: get_composition_cache_result(size_t n) const { LightReMutexHolder holder(*_states_lock); - if (!_composition_cache.has_element(n)) { - return NULL; - } return _composition_cache.get_data(n)._result; } @@ -288,7 +282,7 @@ get_composition_cache_result(size_t n) const { INLINE size_t RenderState:: get_invert_composition_cache_size() const { LightReMutexHolder holder(*_states_lock); - return _invert_composition_cache.get_size(); + return _invert_composition_cache.get_num_entries(); } /** @@ -302,9 +296,6 @@ get_invert_composition_cache_size() const { INLINE const RenderState *RenderState:: get_invert_composition_cache_source(size_t n) const { LightReMutexHolder holder(*_states_lock); - if (!_invert_composition_cache.has_element(n)) { - return NULL; - } return _invert_composition_cache.get_key(n); } @@ -322,9 +313,6 @@ get_invert_composition_cache_source(size_t n) const { INLINE const RenderState *RenderState:: get_invert_composition_cache_result(size_t n) const { LightReMutexHolder holder(*_states_lock); - if (!_invert_composition_cache.has_element(n)) { - return NULL; - } return _invert_composition_cache.get_data(n)._result; } @@ -522,7 +510,8 @@ INLINE void RenderState:: check_hash() const { // This pretends to be a const function, even though it's not, because it // only updates a transparent cache value. - if ((_flags & F_hash_known) == 0) { + if ((_flags & F_hash_known) != 0) { + } else { ((RenderState *)this)->calc_hash(); } } diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index de3a58b2ba..6f035a6557 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -40,7 +40,7 @@ LightReMutex *RenderState::_states_lock = NULL; RenderState::States *RenderState::_states = NULL; const RenderState *RenderState::_empty_state = NULL; UpdateSeq RenderState::_last_cycle_detect; -int RenderState::_garbage_index = 0; +size_t RenderState::_garbage_index = 0; PStatCollector RenderState::_cache_update_pcollector("*:State Cache:Update"); PStatCollector RenderState::_garbage_collect_pcollector("*:State Cache:Garbage Collect"); @@ -64,7 +64,6 @@ TypeHandle RenderState::_type_handle; RenderState:: RenderState() : _flags(0), - _auto_shader_state(NULL), _lock("RenderState") { if (_states == (States *)NULL) { @@ -75,6 +74,10 @@ RenderState() : _cache_stats.add_num_states(1); _read_overrides = NULL; _generated_shader = NULL; + +#ifdef DO_MEMORY_USAGE + MemoryUsage::update_type(this, this); +#endif } /** @@ -84,7 +87,6 @@ RenderState:: RenderState(const RenderState ©) : _filled_slots(copy._filled_slots), _flags(0), - _auto_shader_state(NULL), _lock("RenderState") { // Copy over the attributes. @@ -97,6 +99,10 @@ RenderState(const RenderState ©) : _cache_stats.add_num_states(1); _read_overrides = NULL; _generated_shader = NULL; + +#ifdef DO_MEMORY_USAGE + MemoryUsage::update_type(this, this); +#endif } /** @@ -123,14 +129,6 @@ RenderState:: nassertv(_saved_entry == -1); nassertv(_composition_cache.is_empty() && _invert_composition_cache.is_empty()); - // Make sure the _auto_shader_state cache pointer is cleared. - if (_auto_shader_state != (const RenderState *)NULL) { - if (_auto_shader_state != this) { - cache_unref_delete(_auto_shader_state); - } - _auto_shader_state = NULL; - } - // If this was true at the beginning of the destructor, but is no longer // true now, probably we've been double-deleted. nassertv(get_ref_count() == 0); @@ -400,13 +398,13 @@ compose(const RenderState *other) const { CPT(RenderState) result = do_compose(other); _cache_stats.add_total_size(1); - _cache_stats.inc_adds(_composition_cache.get_size() == 0); + _cache_stats.inc_adds(_composition_cache.is_empty()); ((RenderState *)this)->_composition_cache[other]._result = result; if (other != this) { _cache_stats.add_total_size(1); - _cache_stats.inc_adds(other->_composition_cache.get_size() == 0); + _cache_stats.inc_adds(other->_composition_cache.is_empty()); ((RenderState *)other)->_composition_cache[this]._result = NULL; } @@ -489,12 +487,12 @@ invert_compose(const RenderState *other) const { CPT(RenderState) result = do_invert_compose(other); _cache_stats.add_total_size(1); - _cache_stats.inc_adds(_invert_composition_cache.get_size() == 0); + _cache_stats.inc_adds(_invert_composition_cache.is_empty()); ((RenderState *)this)->_invert_composition_cache[other]._result = result; if (other != this) { _cache_stats.add_total_size(1); - _cache_stats.inc_adds(other->_invert_composition_cache.get_size() == 0); + _cache_stats.inc_adds(other->_invert_composition_cache.is_empty()); ((RenderState *)other)->_invert_composition_cache[this]._result = NULL; } @@ -617,7 +615,7 @@ adjust_all_priorities(int adjustment) const { */ bool RenderState:: unref() const { - if (!state_cache || garbage_collect_states) { + if (garbage_collect_states || !state_cache) { // If we're not using the cache at all, or if we're relying on garbage // collection, just allow the pointer to unref normally. return ReferenceCount::unref(); @@ -657,24 +655,6 @@ unref() const { return false; } -/** - * Returns the base RenderState that should have the generated_shader stored - * within it, for generated shader states. The returned object might be the - * same as this object, or it might be a different RenderState with certain - * attributes removed, or set to their default values. - * - * The point is to avoid needless regeneration of the shader attrib by storing - * the generated shader on a common RenderState object, with all irrelevant - * attributes removed. - */ -const RenderState *RenderState:: -get_auto_shader_state() const { - if (_auto_shader_state == (const RenderState *)NULL) { - ((RenderState *)this)->assign_auto_shader_state(); - } - return _auto_shader_state; -} - /** * */ @@ -774,40 +754,33 @@ get_num_unused_states() { typedef pmap StateCount; StateCount state_count; - int size = _states->get_size(); - for (int si = 0; si < size; ++si) { - if (!_states->has_element(si)) { - continue; - } + size_t size = _states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { const RenderState *state = _states->get_key(si); - int i; - int cache_size = state->_composition_cache.get_size(); + size_t i; + size_t cache_size = state->_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (state->_composition_cache.has_element(i)) { - const RenderState *result = state->_composition_cache.get_data(i)._result; - if (result != (const RenderState *)NULL && result != state) { - // Here's a RenderState that's recorded in the cache. Count it. - pair ir = - state_count.insert(StateCount::value_type(result, 1)); - if (!ir.second) { - // If the above insert operation fails, then it's already in the - // cache; increment its value. - (*(ir.first)).second++; - } + const RenderState *result = state->_composition_cache.get_data(i)._result; + if (result != (const RenderState *)NULL && result != state) { + // Here's a RenderState that's recorded in the cache. Count it. + pair ir = + state_count.insert(StateCount::value_type(result, 1)); + if (!ir.second) { + // If the above insert operation fails, then it's already in the + // cache; increment its value. + (*(ir.first)).second++; } } } - cache_size = state->_invert_composition_cache.get_size(); + cache_size = state->_invert_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (state->_invert_composition_cache.has_element(i)) { - const RenderState *result = state->_invert_composition_cache.get_data(i)._result; - if (result != (const RenderState *)NULL && result != state) { - pair ir = - state_count.insert(StateCount::value_type(result, 1)); - if (!ir.second) { - (*(ir.first)).second++; - } + const RenderState *result = state->_invert_composition_cache.get_data(i)._result; + if (result != (const RenderState *)NULL && result != state) { + pair ir = + state_count.insert(StateCount::value_type(result, 1)); + if (!ir.second) { + (*(ir.first)).second++; } } } @@ -871,11 +844,8 @@ clear_cache() { TempStates temp_states; temp_states.reserve(orig_size); - int size = _states->get_size(); - for (int si = 0; si < size; ++si) { - if (!_states->has_element(si)) { - continue; - } + size_t size = _states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { const RenderState *state = _states->get_key(si); temp_states.push_back(state); } @@ -886,28 +856,24 @@ clear_cache() { for (ti = temp_states.begin(); ti != temp_states.end(); ++ti) { RenderState *state = (RenderState *)(*ti).p(); - int i; - int cache_size = (int)state->_composition_cache.get_size(); + size_t i; + size_t cache_size = (int)state->_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (state->_composition_cache.has_element(i)) { - const RenderState *result = state->_composition_cache.get_data(i)._result; - if (result != (const RenderState *)NULL && result != state) { - result->cache_unref(); - nassertr(result->get_ref_count() > 0, 0); - } + const RenderState *result = state->_composition_cache.get_data(i)._result; + if (result != (const RenderState *)NULL && result != state) { + result->cache_unref(); + nassertr(result->get_ref_count() > 0, 0); } } _cache_stats.add_total_size(-(int)state->_composition_cache.get_num_entries()); state->_composition_cache.clear(); - cache_size = (int)state->_invert_composition_cache.get_size(); + cache_size = (int)state->_invert_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (state->_invert_composition_cache.has_element(i)) { - const RenderState *result = state->_invert_composition_cache.get_data(i)._result; - if (result != (const RenderState *)NULL && result != state) { - result->cache_unref(); - nassertr(result->get_ref_count() > 0, 0); - } + const RenderState *result = state->_invert_composition_cache.get_data(i)._result; + if (result != (const RenderState *)NULL && result != state) { + result->cache_unref(); + nassertr(result->get_ref_count() > 0, 0); } } _cache_stats.add_total_size(-(int)state->_invert_composition_cache.get_num_entries()); @@ -938,57 +904,75 @@ garbage_collect() { if (_states == (States *)NULL || !garbage_collect_states) { return num_attribs; } + LightReMutexHolder holder(*_states_lock); PStatTimer timer(_garbage_collect_pcollector); - int orig_size = _states->get_num_entries(); + size_t orig_size = _states->get_num_entries(); // How many elements to process this pass? - int size = _states->get_size(); - int num_this_pass = int(size * garbage_collect_states_rate); + size_t size = orig_size; + size_t num_this_pass = max(0, int(size * garbage_collect_states_rate)); if (num_this_pass <= 0) { return num_attribs; } + + bool break_and_uniquify = (auto_break_cycles && uniquify_transforms); + + size_t si = _garbage_index; + if (si >= size) { + si = 0; + } + num_this_pass = min(num_this_pass, size); - int stop_at_element = (_garbage_index + num_this_pass) % size; + size_t stop_at_element = (si + num_this_pass) % size; - int num_elements = 0; - int si = _garbage_index; do { - if (_states->has_element(si)) { - ++num_elements; - RenderState *state = (RenderState *)_states->get_key(si); - if (auto_break_cycles && uniquify_states) { - if (state->get_cache_ref_count() > 0 && - state->get_ref_count() == state->get_cache_ref_count()) { - // If we have removed all the references to this state not in the - // cache, leaving only references in the cache, then we need to - // check for a cycle involving this RenderState and break it if it - // exists. - state->detect_and_break_cycles(); - } + RenderState *state = (RenderState *)_states->get_key(si); + if (break_and_uniquify) { + if (state->get_cache_ref_count() > 0 && + state->get_ref_count() == state->get_cache_ref_count()) { + // If we have removed all the references to this state not in the + // cache, leaving only references in the cache, then we need to + // check for a cycle involving this RenderState and break it if it + // exists. + state->detect_and_break_cycles(); } + } - if (state->get_ref_count() == 1) { - // This state has recently been unreffed to 1 (the one we added when - // we stored it in the cache). Now it's time to delete it. This is - // safe, because we're holding the _states_lock, so it's not possible - // for some other thread to find the state in the cache and ref it - // while we're doing this. - state->release_new(); - state->remove_cache_pointers(); - state->cache_unref(); - delete state; - } + if (state->get_ref_count() == 1) { + // This state has recently been unreffed to 1 (the one we added when + // we stored it in the cache). Now it's time to delete it. This is + // safe, because we're holding the _states_lock, so it's not possible + // for some other thread to find the state in the cache and ref it + // while we're doing this. + state->release_new(); + state->remove_cache_pointers(); + state->cache_unref(); + delete state; + + // When we removed it from the hash map, it swapped the last element + // with the one we just removed. So the current index contains one we + // still need to visit. + --size; + --si; } si = (si + 1) % size; } while (si != stop_at_element); _garbage_index = si; - nassertr(_states->validate(), 0); - int new_size = _states->get_num_entries(); - return orig_size - new_size + num_attribs; + nassertr(_states->get_num_entries() == size, 0); + +#ifdef _DEBUG + nassertr(_states->validate(), 0); +#endif + + // If we just cleaned up a lot of states, see if we can reduce the table in + // size. This will help reduce iteration overhead in the future. + _states->consider_shrink_table(); + + return (int)orig_size - (int)size + num_attribs; } /** @@ -999,13 +983,11 @@ void RenderState:: clear_munger_cache() { LightReMutexHolder holder(*_states_lock); - int size = _states->get_size(); - for (int si = 0; si < size; ++si) { - if (!_states->has_element(si)) { - continue; - } + size_t size = _states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { RenderState *state = (RenderState *)(_states->get_key(si)); state->_mungers.clear(); + state->_munged_states.clear(); state->_last_mi = -1; } } @@ -1034,11 +1016,8 @@ list_cycles(ostream &out) { VisitedStates visited; CompositionCycleDesc cycle_desc; - int size = _states->get_size(); - for (int si = 0; si < size; ++si) { - if (!_states->has_element(si)) { - continue; - } + size_t size = _states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { const RenderState *state = _states->get_key(si); bool inserted = visited.insert(state).second; @@ -1111,13 +1090,9 @@ list_states(ostream &out) { } LightReMutexHolder holder(*_states_lock); - out << _states->get_num_entries() << " states:\n"; - - int size = _states->get_size(); - for (int si = 0; si < size; ++si) { - if (!_states->has_element(si)) { - continue; - } + size_t size = _states->get_num_entries(); + out << size << " states:\n"; + for (size_t si = 0; si < size; ++si) { const RenderState *state = _states->get_key(si); state->write(out, 2); } @@ -1148,18 +1123,12 @@ validate_states() { return false; } - int size = _states->get_size(); - int si = 0; - while (si < size && !_states->has_element(si)) { - ++si; - } + size_t size = _states->get_num_entries(); + size_t si = 0; nassertr(si < size, false); nassertr(_states->get_key(si)->get_ref_count() >= 0, false); - int snext = si; + size_t snext = si; ++snext; - while (snext < size && !_states->has_element(snext)) { - ++snext; - } while (snext < size) { nassertr(_states->get_key(snext)->get_ref_count() >= 0, false); const RenderState *ssi = _states->get_key(si); @@ -1181,9 +1150,6 @@ validate_states() { } si = snext; ++snext; - while (snext < size && !_states->has_element(snext)) { - ++snext; - } } return true; @@ -1266,54 +1232,6 @@ do_calc_hash() { _flags |= F_hash_known; } -/** - * Sets _auto_shader_state to the appropriate RenderState object pointer, - * either the same pointer as this object, or some other (simpler) - * RenderState. - */ -void RenderState:: -assign_auto_shader_state() { - CPT(RenderState) state = do_calc_auto_shader_state(); - - { - LightReMutexHolder holder(*_states_lock); - if (_auto_shader_state == (const RenderState *)NULL) { - _auto_shader_state = state; - if (_auto_shader_state != this) { - _auto_shader_state->cache_ref(); - } - } - } -} - -/** - * Returns the appropriate RenderState that should be used to store the auto - * shader pointer for nodes that shader this RenderState. - */ -CPT(RenderState) RenderState:: -do_calc_auto_shader_state() { - RenderState *state = new RenderState; - - SlotMask mask = _filled_slots; - int slot = mask.get_lowest_on_bit(); - while (slot >= 0) { - const Attribute &attrib = _attributes[slot]; - nassertr(attrib._attrib != (RenderAttrib *)NULL, this); - CPT(RenderAttrib) new_attrib = attrib._attrib->get_auto_shader_attrib(this); - if (new_attrib != NULL) { - nassertr(new_attrib->get_slot() == slot, this); - state->_attributes[slot].set(new_attrib, 0); - state->_filled_slots.set_bit(slot); - } - - mask.clear_bit(slot); - slot = mask.get_lowest_on_bit(); - } - - return return_new(state); -} - - /** * This function is used to share a common RenderState pointer for all * equivalent RenderState objects. @@ -1421,7 +1339,7 @@ return_unique(RenderState *state) { // deleted while it's in it. state->cache_ref(); } - si = _states->store(state, Empty()); + si = _states->store(state, nullptr); // Save the index and return the input state. state->_saved_entry = si; @@ -1575,41 +1493,37 @@ r_detect_cycles(const RenderState *start_state, } ((RenderState *)current_state)->_cycle_detect = this_seq; - int i; - int cache_size = current_state->_composition_cache.get_size(); + size_t i; + size_t cache_size = current_state->_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (current_state->_composition_cache.has_element(i)) { - const RenderState *result = current_state->_composition_cache.get_data(i)._result; - if (result != (const RenderState *)NULL) { - if (r_detect_cycles(start_state, result, length + 1, - this_seq, cycle_desc)) { - // Cycle detected. - if (cycle_desc != (CompositionCycleDesc *)NULL) { - const RenderState *other = current_state->_composition_cache.get_key(i); - CompositionCycleDescEntry entry(other, result, false); - cycle_desc->push_back(entry); - } - return true; + const RenderState *result = current_state->_composition_cache.get_data(i)._result; + if (result != (const RenderState *)NULL) { + if (r_detect_cycles(start_state, result, length + 1, + this_seq, cycle_desc)) { + // Cycle detected. + if (cycle_desc != (CompositionCycleDesc *)NULL) { + const RenderState *other = current_state->_composition_cache.get_key(i); + CompositionCycleDescEntry entry(other, result, false); + cycle_desc->push_back(entry); } + return true; } } } - cache_size = current_state->_invert_composition_cache.get_size(); + cache_size = current_state->_invert_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (current_state->_invert_composition_cache.has_element(i)) { - const RenderState *result = current_state->_invert_composition_cache.get_data(i)._result; - if (result != (const RenderState *)NULL) { - if (r_detect_cycles(start_state, result, length + 1, - this_seq, cycle_desc)) { - // Cycle detected. - if (cycle_desc != (CompositionCycleDesc *)NULL) { - const RenderState *other = current_state->_invert_composition_cache.get_key(i); - CompositionCycleDescEntry entry(other, result, true); - cycle_desc->push_back(entry); - } - return true; + const RenderState *result = current_state->_invert_composition_cache.get_data(i)._result; + if (result != (const RenderState *)NULL) { + if (r_detect_cycles(start_state, result, length + 1, + this_seq, cycle_desc)) { + // Cycle detected. + if (cycle_desc != (CompositionCycleDesc *)NULL) { + const RenderState *other = current_state->_invert_composition_cache.get_key(i); + CompositionCycleDescEntry entry(other, result, true); + cycle_desc->push_back(entry); } + return true; } } } @@ -1638,52 +1552,48 @@ r_detect_reverse_cycles(const RenderState *start_state, } ((RenderState *)current_state)->_cycle_detect = this_seq; - int i; - int cache_size = current_state->_composition_cache.get_size(); + size_t i; + size_t cache_size = current_state->_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (current_state->_composition_cache.has_element(i)) { - const RenderState *other = current_state->_composition_cache.get_key(i); - if (other != current_state) { - int oi = other->_composition_cache.find(current_state); - nassertr(oi != -1, false); + const RenderState *other = current_state->_composition_cache.get_key(i); + if (other != current_state) { + int oi = other->_composition_cache.find(current_state); + nassertr(oi != -1, false); - const RenderState *result = other->_composition_cache.get_data(oi)._result; - if (result != (const RenderState *)NULL) { - if (r_detect_reverse_cycles(start_state, result, length + 1, - this_seq, cycle_desc)) { - // Cycle detected. - if (cycle_desc != (CompositionCycleDesc *)NULL) { - const RenderState *other = current_state->_composition_cache.get_key(i); - CompositionCycleDescEntry entry(other, result, false); - cycle_desc->push_back(entry); - } - return true; + const RenderState *result = other->_composition_cache.get_data(oi)._result; + if (result != (const RenderState *)NULL) { + if (r_detect_reverse_cycles(start_state, result, length + 1, + this_seq, cycle_desc)) { + // Cycle detected. + if (cycle_desc != (CompositionCycleDesc *)NULL) { + const RenderState *other = current_state->_composition_cache.get_key(i); + CompositionCycleDescEntry entry(other, result, false); + cycle_desc->push_back(entry); } + return true; } } } } - cache_size = current_state->_invert_composition_cache.get_size(); + cache_size = current_state->_invert_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (current_state->_invert_composition_cache.has_element(i)) { - const RenderState *other = current_state->_invert_composition_cache.get_key(i); - if (other != current_state) { - int oi = other->_invert_composition_cache.find(current_state); - nassertr(oi != -1, false); + const RenderState *other = current_state->_invert_composition_cache.get_key(i); + if (other != current_state) { + int oi = other->_invert_composition_cache.find(current_state); + nassertr(oi != -1, false); - const RenderState *result = other->_invert_composition_cache.get_data(oi)._result; - if (result != (const RenderState *)NULL) { - if (r_detect_reverse_cycles(start_state, result, length + 1, - this_seq, cycle_desc)) { - // Cycle detected. - if (cycle_desc != (CompositionCycleDesc *)NULL) { - const RenderState *other = current_state->_invert_composition_cache.get_key(i); - CompositionCycleDescEntry entry(other, result, false); - cycle_desc->push_back(entry); - } - return true; + const RenderState *result = other->_invert_composition_cache.get_data(oi)._result; + if (result != (const RenderState *)NULL) { + if (r_detect_reverse_cycles(start_state, result, length + 1, + this_seq, cycle_desc)) { + // Cycle detected. + if (cycle_desc != (CompositionCycleDesc *)NULL) { + const RenderState *other = current_state->_invert_composition_cache.get_key(i); + CompositionCycleDescEntry entry(other, result, false); + cycle_desc->push_back(entry); } + return true; } } } @@ -1704,10 +1614,8 @@ release_new() { nassertv(_states_lock->debug_is_locked()); if (_saved_entry != -1) { - // nassertv(_states->find(this) == _saved_entry); - _saved_entry = _states->find(this); - _states->remove_element(_saved_entry); _saved_entry = -1; + nassertv_always(_states->remove(this)); } } @@ -1722,14 +1630,6 @@ void RenderState:: remove_cache_pointers() { nassertv(_states_lock->debug_is_locked()); - // First, make sure the _auto_shader_state cache pointer is cleared. - if (_auto_shader_state != (const RenderState *)NULL) { - if (_auto_shader_state != this) { - cache_unref_delete(_auto_shader_state); - } - _auto_shader_state = NULL; - } - // Fortunately, since we added CompositionCache records in pairs, we know // exactly the set of RenderState objects that have us in their cache: it's // the same set of RenderState objects that we have in our own cache. @@ -1753,13 +1653,8 @@ remove_cache_pointers() { // There are lots of ways to do this loop wrong. Be very careful if you // need to modify it for any reason. - int i = 0; + size_t i = 0; while (!_composition_cache.is_empty()) { - // Scan for the next used slot in the table. - while (!_composition_cache.has_element(i)) { - ++i; - } - // It is possible that the "other" RenderState object is currently within // its own destructor. We therefore can't use a PT() to hold its pointer; // that could end up calling its destructor twice. Fortunately, we don't @@ -1811,10 +1706,6 @@ remove_cache_pointers() { // A similar bit of code for the invert cache. i = 0; while (!_invert_composition_cache.is_empty()) { - while (!_invert_composition_cache.has_element(i)) { - ++i; - } - RenderState *other = (RenderState *)_invert_composition_cache.get_key(i); nassertv(other != this); Composition comp = _invert_composition_cache.get_data(i); @@ -1975,7 +1866,7 @@ init_states() { // is declared globally, and lives forever. RenderState *state = new RenderState; state->local_object(); - state->_saved_entry = _states->store(state, Empty()); + state->_saved_entry = _states->store(state, nullptr); _empty_state = state; } diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index c15efe68ad..1052687314 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -30,10 +30,8 @@ #include "lightMutex.h" #include "deletedChain.h" #include "simpleHashMap.h" -#include "weakKeyHashMap.h" #include "cacheStats.h" #include "renderAttribRegistry.h" -#include "graphicsStateGuardianBase.h" class FactoryParams; class ShaderAttrib; @@ -109,6 +107,8 @@ PUBLISHED: INLINE int get_override(TypeHandle type) const; INLINE int get_override(int slot) const; + MAKE_MAP_PROPERTY(attribs, has_attrib, get_attrib); + INLINE CPT(RenderState) get_unique() const; virtual bool unref() const; @@ -130,8 +130,6 @@ PUBLISHED: EXTENSION(PyObject *get_composition_cache() const); EXTENSION(PyObject *get_invert_composition_cache() const); - const RenderState *get_auto_shader_state() const; - void output(ostream &out) const; void write(ostream &out, int indent_level) const; @@ -173,8 +171,6 @@ private: INLINE bool do_node_unref() const; INLINE void calc_hash(); void do_calc_hash(); - void assign_auto_shader_state(); - CPT(RenderState) do_calc_auto_shader_state(); class CompositionCycleDescEntry { public: @@ -223,15 +219,14 @@ public: // declare this as a ShaderAttrib because that would create a circular // include-file dependency problem. Aaargh. mutable CPT(RenderAttrib) _generated_shader; + mutable UpdateSeq _generated_shader_seq; private: // This mutex protects _states. It also protects any modification to the // cache, which is encoded in _composition_cache and // _invert_composition_cache. static LightReMutex *_states_lock; - class Empty { - }; - typedef SimpleHashMap > States; + typedef SimpleHashMap > States; static States *_states; static const RenderState *_empty_state; @@ -268,17 +263,22 @@ private: // in the RenderState pointer than vice-versa, since there are likely to be // far fewer GSG's than RenderStates. The code to manage this map lives in // GraphicsStateGuardian::get_geom_munger(). - typedef WeakKeyHashMap Mungers; + typedef SimpleHashMap Mungers; mutable Mungers _mungers; mutable int _last_mi; + // Similarly, this is a cache of munged states. This map is managed by + // StateMunger::munge_state(). + typedef SimpleHashMap MungedStates; + mutable MungedStates _munged_states; + // This is used to mark nodes as we visit them to detect cycles. UpdateSeq _cycle_detect; static UpdateSeq _last_cycle_detect; // This keeps track of our current position through the garbage collection // cycle. - static int _garbage_index; + static size_t _garbage_index; static PStatCollector _cache_update_pcollector; static PStatCollector _garbage_collect_pcollector; @@ -317,8 +317,6 @@ private: int _draw_order; size_t _hash; - const RenderState *_auto_shader_state; - enum Flags { F_checked_bin_index = 0x000001, F_checked_cull_callback = 0x000002, @@ -366,8 +364,14 @@ private: friend class GraphicsStateGuardian; friend class RenderAttribRegistry; friend class Extension; + friend class ShaderGenerator; + friend class StateMunger; }; +// We can safely redefine this as a no-op. +template<> +INLINE void PointerToBase::update_type(To *ptr) {} + INLINE ostream &operator << (ostream &out, const RenderState &state) { state.output(out); return out; diff --git a/panda/src/pgraph/renderState_ext.cxx b/panda/src/pgraph/renderState_ext.cxx index 5a7288f518..0dc0d8acc5 100644 --- a/panda/src/pgraph/renderState_ext.cxx +++ b/panda/src/pgraph/renderState_ext.cxx @@ -30,36 +30,29 @@ PyObject *Extension:: get_composition_cache() const { extern struct Dtool_PyTypedObject Dtool_RenderState; LightReMutexHolder holder(*RenderState::_states_lock); - size_t cache_size = _this->_composition_cache.get_size(); + size_t cache_size = _this->_composition_cache.get_num_entries(); PyObject *list = PyList_New(cache_size); for (size_t i = 0; i < cache_size; ++i) { PyObject *tuple = PyTuple_New(2); PyObject *a, *b; - if (!_this->_composition_cache.has_element(i)) { + const RenderState *source = _this->_composition_cache.get_key(i); + if (source == (RenderState *)NULL) { a = Py_None; Py_INCREF(a); + } else { + source->ref(); + a = DTool_CreatePyInstanceTyped((void *)source, Dtool_RenderState, + true, true, source->get_type_index()); + } + const RenderState *result = _this->_composition_cache.get_data(i)._result; + if (result == (RenderState *)NULL) { b = Py_None; Py_INCREF(b); } else { - const RenderState *source = _this->_composition_cache.get_key(i); - if (source == (RenderState *)NULL) { - a = Py_None; - Py_INCREF(a); - } else { - source->ref(); - a = DTool_CreatePyInstanceTyped((void *)source, Dtool_RenderState, - true, true, source->get_type_index()); - } - const RenderState *result = _this->_composition_cache.get_data(i)._result; - if (result == (RenderState *)NULL) { - b = Py_None; - Py_INCREF(b); - } else { - result->ref(); - b = DTool_CreatePyInstanceTyped((void *)result, Dtool_RenderState, - true, true, result->get_type_index()); - } + result->ref(); + b = DTool_CreatePyInstanceTyped((void *)result, Dtool_RenderState, + true, true, result->get_type_index()); } PyTuple_SET_ITEM(tuple, 0, a); PyTuple_SET_ITEM(tuple, 1, b); @@ -85,36 +78,29 @@ PyObject *Extension:: get_invert_composition_cache() const { extern struct Dtool_PyTypedObject Dtool_RenderState; LightReMutexHolder holder(*RenderState::_states_lock); - size_t cache_size = _this->_invert_composition_cache.get_size(); + size_t cache_size = _this->_invert_composition_cache.get_num_entries(); PyObject *list = PyList_New(cache_size); for (size_t i = 0; i < cache_size; ++i) { PyObject *tuple = PyTuple_New(2); PyObject *a, *b; - if (!_this->_invert_composition_cache.has_element(i)) { + const RenderState *source = _this->_invert_composition_cache.get_key(i); + if (source == (RenderState *)NULL) { a = Py_None; Py_INCREF(a); + } else { + source->ref(); + a = DTool_CreatePyInstanceTyped((void *)source, Dtool_RenderState, + true, true, source->get_type_index()); + } + const RenderState *result = _this->_invert_composition_cache.get_data(i)._result; + if (result == (RenderState *)NULL) { b = Py_None; Py_INCREF(b); } else { - const RenderState *source = _this->_invert_composition_cache.get_key(i); - if (source == (RenderState *)NULL) { - a = Py_None; - Py_INCREF(a); - } else { - source->ref(); - a = DTool_CreatePyInstanceTyped((void *)source, Dtool_RenderState, - true, true, source->get_type_index()); - } - const RenderState *result = _this->_invert_composition_cache.get_data(i)._result; - if (result == (RenderState *)NULL) { - b = Py_None; - Py_INCREF(b); - } else { - result->ref(); - b = DTool_CreatePyInstanceTyped((void *)result, Dtool_RenderState, - true, true, result->get_type_index()); - } + result->ref(); + b = DTool_CreatePyInstanceTyped((void *)result, Dtool_RenderState, + true, true, result->get_type_index()); } PyTuple_SET_ITEM(tuple, 0, a); PyTuple_SET_ITEM(tuple, 1, b); @@ -141,11 +127,8 @@ get_states() { PyObject *list = PyList_New(num_states); size_t i = 0; - int size = RenderState::_states->get_size(); - for (int si = 0; si < size; ++si) { - if (!RenderState::_states->has_element(si)) { - continue; - } + size_t size = RenderState::_states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { const RenderState *state = RenderState::_states->get_key(si); state->ref(); PyObject *a = diff --git a/panda/src/pgraph/rescaleNormalAttrib.cxx b/panda/src/pgraph/rescaleNormalAttrib.cxx index 36af8ebafb..fbc4d59274 100644 --- a/panda/src/pgraph/rescaleNormalAttrib.cxx +++ b/panda/src/pgraph/rescaleNormalAttrib.cxx @@ -80,19 +80,6 @@ get_hash_impl() const { return hash; } -/** - * - */ -CPT(RenderAttrib) RescaleNormalAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - // We currently only support M_normalize in the ShaderGenerator. - if (_mode == M_none || _mode == M_normalize) { - return this; - } else { - return RescaleNormalAttrib::make(M_normalize); - } -} - /** * Tells the BamReader how to create objects of type RescaleNormalAttrib. */ diff --git a/panda/src/pgraph/rescaleNormalAttrib.h b/panda/src/pgraph/rescaleNormalAttrib.h index d76d51e6de..589f5351cc 100644 --- a/panda/src/pgraph/rescaleNormalAttrib.h +++ b/panda/src/pgraph/rescaleNormalAttrib.h @@ -57,7 +57,6 @@ public: protected: virtual int compare_to_impl(const RenderAttrib *other) const; virtual size_t get_hash_impl() const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: Mode _mode; @@ -73,6 +72,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/sceneGraphReducer.h b/panda/src/pgraph/sceneGraphReducer.h index 43bc38d3eb..5d9550856a 100644 --- a/panda/src/pgraph/sceneGraphReducer.h +++ b/panda/src/pgraph/sceneGraphReducer.h @@ -38,7 +38,7 @@ class PandaNode; */ class EXPCL_PANDA_PGRAPH SceneGraphReducer { PUBLISHED: - INLINE SceneGraphReducer(GraphicsStateGuardianBase *gsg = NULL); + INLINE explicit SceneGraphReducer(GraphicsStateGuardianBase *gsg = NULL); INLINE ~SceneGraphReducer(); enum AttribTypes { diff --git a/panda/src/pgraph/scissorAttrib.h b/panda/src/pgraph/scissorAttrib.h index 48a4b87a7c..9ea128e765 100644 --- a/panda/src/pgraph/scissorAttrib.h +++ b/panda/src/pgraph/scissorAttrib.h @@ -70,6 +70,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/shadeModelAttrib.h b/panda/src/pgraph/shadeModelAttrib.h index 067555207a..8f8cd8a570 100644 --- a/panda/src/pgraph/shadeModelAttrib.h +++ b/panda/src/pgraph/shadeModelAttrib.h @@ -59,6 +59,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/shaderAttrib.I b/panda/src/pgraph/shaderAttrib.I index f3667b27d9..6513604ae9 100644 --- a/panda/src/pgraph/shaderAttrib.I +++ b/panda/src/pgraph/shaderAttrib.I @@ -110,7 +110,7 @@ has_shader_input(CPT_InternalName id) const { */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const PTA_float &v, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), v, priority)); + return set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -118,7 +118,7 @@ set_shader_input(CPT_InternalName id, const PTA_float &v, int priority) const { */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const PTA_double &v, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), v, priority)); + return set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -126,7 +126,7 @@ set_shader_input(CPT_InternalName id, const PTA_double &v, int priority) const { */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const PTA_LVecBase4 &v, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), v, priority)); + return set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -134,7 +134,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase4 &v, int priority) cons */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const PTA_LVecBase3 &v, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), v, priority)); + return set_shader_input(ShaderInput(move(id), v, priority)); } @@ -143,7 +143,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase3 &v, int priority) cons */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const PTA_LVecBase2 &v, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), v, priority)); + return set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -151,7 +151,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase2 &v, int priority) cons */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const LVecBase4 &v, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), v, priority)); + return set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -159,7 +159,7 @@ set_shader_input(CPT_InternalName id, const LVecBase4 &v, int priority) const { */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const LVecBase3 &v, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), v, priority)); + return set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -167,7 +167,7 @@ set_shader_input(CPT_InternalName id, const LVecBase3 &v, int priority) const { */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const LVecBase2 &v, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), v, priority)); + return set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -175,7 +175,7 @@ set_shader_input(CPT_InternalName id, const LVecBase2 &v, int priority) const { */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const PTA_LMatrix4 &v, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), v, priority)); + return set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -183,7 +183,7 @@ set_shader_input(CPT_InternalName id, const PTA_LMatrix4 &v, int priority) const */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const PTA_LMatrix3 &v, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), v, priority)); + return set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -191,7 +191,7 @@ set_shader_input(CPT_InternalName id, const PTA_LMatrix3 &v, int priority) const */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const LMatrix4 &v, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), v, priority)); + return set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -199,7 +199,7 @@ set_shader_input(CPT_InternalName id, const LMatrix4 &v, int priority) const { */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const LMatrix3 &v, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), v, priority)); + return set_shader_input(ShaderInput(move(id), v, priority)); } /** @@ -207,7 +207,7 @@ set_shader_input(CPT_InternalName id, const LMatrix3 &v, int priority) const { */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, Texture *tex, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), tex, priority)); + return set_shader_input(ShaderInput(move(id), tex, priority)); } /** @@ -215,7 +215,7 @@ set_shader_input(CPT_InternalName id, Texture *tex, int priority) const { */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, const NodePath &np, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), np, priority)); + return set_shader_input(ShaderInput(move(id), np, priority)); } /** @@ -223,7 +223,7 @@ set_shader_input(CPT_InternalName id, const NodePath &np, int priority) const { */ INLINE CPT(RenderAttrib) ShaderAttrib:: set_shader_input(CPT_InternalName id, double n1, double n2, double n3, double n4, int priority) const { - return set_shader_input(new ShaderInput(MOVE(id), LVecBase4((PN_stdfloat)n1, (PN_stdfloat)n2, (PN_stdfloat)n3, (PN_stdfloat)n4), priority)); + return set_shader_input(ShaderInput(move(id), LVecBase4((PN_stdfloat)n1, (PN_stdfloat)n2, (PN_stdfloat)n3, (PN_stdfloat)n4), priority)); } INLINE bool ShaderAttrib:: diff --git a/panda/src/pgraph/shaderAttrib.cxx b/panda/src/pgraph/shaderAttrib.cxx index 2e6ac7cd77..67bba2d196 100644 --- a/panda/src/pgraph/shaderAttrib.cxx +++ b/panda/src/pgraph/shaderAttrib.cxx @@ -193,13 +193,13 @@ clear_flag(int flag) const { * */ CPT(RenderAttrib) ShaderAttrib:: -set_shader_input(const ShaderInput *input) const { +set_shader_input(ShaderInput input) const { ShaderAttrib *result = new ShaderAttrib(*this); - Inputs::iterator i = result->_inputs.find(input->get_name()); + Inputs::iterator i = result->_inputs.find(input.get_name()); if (i == result->_inputs.end()) { - result->_inputs.insert(Inputs::value_type(input->get_name(), input)); + result->_inputs.insert(Inputs::value_type(input.get_name(), move(input))); } else { - i->second = input; + i->second = move(input); } return return_new(result); } @@ -248,13 +248,13 @@ clear_all_shader_inputs() const { * Returns the ShaderInput of the given name. If no such name is found, this * function does not return NULL --- it returns the "blank" ShaderInput. */ -const ShaderInput *ShaderAttrib:: +const ShaderInput &ShaderAttrib:: get_shader_input(const InternalName *id) const { Inputs::const_iterator i = _inputs.find(id); - if (i == _inputs.end()) { - return ShaderInput::get_blank(); - } else { + if (i != _inputs.end()) { return (*i).second; + } else { + return ShaderInput::get_blank(); } } @@ -262,7 +262,7 @@ get_shader_input(const InternalName *id) const { * Returns the ShaderInput of the given name. If no such name is found, this * function does not return NULL --- it returns the "blank" ShaderInput. */ -const ShaderInput *ShaderAttrib:: +const ShaderInput &ShaderAttrib:: get_shader_input(const string &id) const { return get_shader_input(InternalName::make(id)); } @@ -275,20 +275,21 @@ const NodePath &ShaderAttrib:: get_shader_input_nodepath(const InternalName *id) const { static NodePath resfail; Inputs::const_iterator i = _inputs.find(id); - if (i == _inputs.end()) { - ostringstream strm; - strm << "Shader input " << id->get_name() << " is not present.\n"; - nassert_raise(strm.str()); - return resfail; - } else { - const ShaderInput *p = (*i).second; - if (p->get_value_type() != ShaderInput::M_nodepath) { + if (i != _inputs.end()) { + const ShaderInput &p = (*i).second; + if (p.get_value_type() == ShaderInput::M_nodepath) { + return ((const ParamNodePath *)p.get_value())->get_value(); + } else { ostringstream strm; strm << "Shader input " << id->get_name() << " is not a nodepath.\n"; nassert_raise(strm.str()); return resfail; } - return p->get_nodepath(); + } else { + ostringstream strm; + strm << "Shader input " << id->get_name() << " is not present.\n"; + nassert_raise(strm.str()); + return resfail; } // Satisfy compiler. @@ -303,19 +304,14 @@ LVecBase4 ShaderAttrib:: get_shader_input_vector(InternalName *id) const { static LVecBase4 resfail(0,0,0,0); Inputs::const_iterator i = _inputs.find(id); - if (i == _inputs.end()) { - ostringstream strm; - strm << "Shader input " << id->get_name() << " is not present.\n"; - nassert_raise(strm.str()); - return resfail; - } else { - const ShaderInput *p = (*i).second; + if (i != _inputs.end()) { + const ShaderInput &p = (*i).second; - if (p->get_value_type() == ShaderInput::M_vector) { - return p->get_vector(); + if (p.get_value_type() == ShaderInput::M_vector) { + return p.get_vector(); - } else if (p->get_value_type() == ShaderInput::M_numeric && p->get_ptr()._size <= 4) { - const Shader::ShaderPtrData &ptr = p->get_ptr(); + } else if (p.get_value_type() == ShaderInput::M_numeric && p.get_ptr()._size <= 4) { + const Shader::ShaderPtrData &ptr = p.get_ptr(); switch (ptr._type) { case Shader::SPT_float: @@ -339,19 +335,23 @@ get_shader_input_vector(InternalName *id) const { } } - } else if (p->get_value_type() == ShaderInput::M_param) { + } else if (p.get_value_type() == ShaderInput::M_param) { // Temporary solution until the new param system - ParamValueBase *param = p->get_param(); + TypedWritableReferenceCount *param = p.get_value(); if (param != NULL && param->is_of_type(ParamVecBase4::get_class_type())) { - return ((const ParamVecBase4 *) param)->get_value(); + return ((const ParamVecBase4 *)param)->get_value(); } } ostringstream strm; strm << "Shader input " << id->get_name() << " is not a vector.\n"; nassert_raise(strm.str()); - return resfail; + } else { + ostringstream strm; + strm << "Shader input " << id->get_name() << " is not present.\n"; + nassert_raise(strm.str()); } + return resfail; } /** @@ -361,21 +361,21 @@ get_shader_input_vector(InternalName *id) const { const Shader::ShaderPtrData *ShaderAttrib:: get_shader_input_ptr(const InternalName *id) const { Inputs::const_iterator i = _inputs.find(id); - if (i == _inputs.end()) { - ostringstream strm; - strm << "Shader input " << id->get_name() << " is not present.\n"; - nassert_raise(strm.str()); - return NULL; - } else { - const ShaderInput *p = (*i).second; - if (p->get_value_type() != ShaderInput::M_numeric && - p->get_value_type() != ShaderInput::M_vector) { + if (i != _inputs.end()) { + const ShaderInput &p = (*i).second; + if (p.get_value_type() != ShaderInput::M_numeric && + p.get_value_type() != ShaderInput::M_vector) { ostringstream strm; strm << "Shader input " << id->get_name() << " is not a PTA(float/double) type.\n"; nassert_raise(strm.str()); return NULL; } - return &(p->get_ptr()); + return &(p.get_ptr()); + } else { + ostringstream strm; + strm << "Shader input " << id->get_name() << " is not present.\n"; + nassert_raise(strm.str()); + return NULL; } } @@ -389,24 +389,39 @@ get_shader_input_ptr(const InternalName *id) const { Texture *ShaderAttrib:: get_shader_input_texture(const InternalName *id, SamplerState *sampler) const { Inputs::const_iterator i = _inputs.find(id); - if (i == _inputs.end()) { - ostringstream strm; - strm << "Shader input " << id->get_name() << " is not present.\n"; - nassert_raise(strm.str()); - return NULL; - } else { - const ShaderInput *p = (*i).second; - if (p->get_value_type() != ShaderInput::M_texture && - p->get_value_type() != ShaderInput::M_texture_sampler) { + if (i != _inputs.end()) { + const ShaderInput &p = (*i).second; + switch (p.get_value_type()) { + case ShaderInput::M_texture: + { + Texture *tex = (Texture *)p.get_value(); + if (sampler) { + *sampler = tex->get_default_sampler(); + } + return tex; + } + + case ShaderInput::M_texture_sampler: + { + const ParamTextureSampler *param = (const ParamTextureSampler *)p.get_value(); + if (sampler) { + *sampler = param->get_sampler(); + } + return param->get_texture(); + } + + default: ostringstream strm; strm << "Shader input " << id->get_name() << " is not a texture.\n"; nassert_raise(strm.str()); return NULL; } - if (sampler != NULL) { - *sampler = p->get_sampler(); - } - return p->get_texture(); + + } else { + ostringstream strm; + strm << "Shader input " << id->get_name() << " is not present.\n"; + nassert_raise(strm.str()); + return NULL; } } @@ -417,22 +432,17 @@ get_shader_input_texture(const InternalName *id, SamplerState *sampler) const { const LMatrix4 &ShaderAttrib:: get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const { Inputs::const_iterator i = _inputs.find(id); - if (i == _inputs.end()) { - ostringstream strm; - strm << "Shader input " << id->get_name() << " is not present.\n"; - nassert_raise(strm.str()); - return LMatrix4::ident_mat(); - } else { - const ShaderInput *p = (*i).second; + if (i != _inputs.end()) { + const ShaderInput &p = (*i).second; - if (p->get_value_type() == ShaderInput::M_nodepath) { - const NodePath &np = p->get_nodepath(); + if (p.get_value_type() == ShaderInput::M_nodepath) { + const NodePath &np = p.get_nodepath(); nassertr(!np.is_empty(), LMatrix4::ident_mat()); return np.get_transform()->get_mat(); - } else if (p->get_value_type() == ShaderInput::M_numeric && - p->get_ptr()._size >= 16 && (p->get_ptr()._size & 15) == 0) { - const Shader::ShaderPtrData &ptr = p->get_ptr(); + } else if (p.get_value_type() == ShaderInput::M_numeric && + p.get_ptr()._size >= 16 && (p.get_ptr()._size & 15) == 0) { + const Shader::ShaderPtrData &ptr = p.get_ptr(); switch (ptr._type) { case Shader::SPT_float: { @@ -460,6 +470,11 @@ get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const { strm << "Shader input " << id->get_name() << " is not a NodePath, LMatrix4 or PTA_LMatrix4.\n"; nassert_raise(strm.str()); return LMatrix4::ident_mat(); + } else { + ostringstream strm; + strm << "Shader input " << id->get_name() << " is not present.\n"; + nassert_raise(strm.str()); + return LMatrix4::ident_mat(); } } @@ -476,11 +491,11 @@ get_shader_input_buffer(const InternalName *id) const { nassert_raise(strm.str()); return NULL; } else { - const ShaderInput *p = (*i).second; + const ShaderInput &p = (*i).second; - if (p->get_value_type() == ShaderInput::M_buffer) { + if (p.get_value_type() == ShaderInput::M_buffer) { ShaderBuffer *value; - DCAST_INTO_R(value, p->_value, NULL); + DCAST_INTO_R(value, p._value, NULL); return value; } @@ -508,14 +523,15 @@ void ShaderAttrib:: output(ostream &out) const { out << "ShaderAttrib:"; - if (_has_shader) { - if (_shader == NULL) { + if (_auto_shader) { + out << "auto"; + return; + } else if (_has_shader) { + if (_shader == nullptr) { out << "off"; } else { out << _shader->get_filename().get_basename(); } - } else if (_auto_shader) { - out << "auto"; } out << "," << _inputs.size() << " inputs"; @@ -615,8 +631,7 @@ get_hash_impl() const { Inputs::const_iterator ii; for (ii = _inputs.begin(); ii != _inputs.end(); ++ii) { - hash = pointer_hash::add_hash(hash, (*ii).first); - hash = pointer_hash::add_hash(hash, (*ii).second); + hash = (*ii).second.add_hash(hash); } return hash; @@ -649,13 +664,13 @@ compose_impl(const RenderAttrib *other) const { Inputs::const_iterator iover; for (iover=over->_inputs.begin(); iover!=over->_inputs.end(); ++iover) { const InternalName *id = (*iover).first; - const ShaderInput *dover = (*iover).second; + const ShaderInput &dover = (*iover).second; Inputs::iterator iattr = attr->_inputs.find(id); if (iattr == attr->_inputs.end()) { attr->_inputs.insert(Inputs::value_type(id,dover)); } else { - const ShaderInput *dattr = (*iattr).second; - if (dattr->get_priority() <= dover->get_priority()) { + const ShaderInput &dattr = (*iattr).second; + if (dattr.get_priority() <= dover.get_priority()) { iattr->second = iover->second; } } @@ -680,26 +695,6 @@ compose_impl(const RenderAttrib *other) const { return return_new(attr); } -/** - * - */ -CPT(RenderAttrib) ShaderAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - // For a ShaderAttrib, we only need to preserve the auto-shader flags. - // Custom shaders, and custom shader inputs, aren't relevant to the shader - // generator. - ShaderAttrib *attrib = new ShaderAttrib; - attrib->_auto_shader = _auto_shader; - attrib->_has_shader = _has_shader; - attrib->_auto_normal_on = _auto_normal_on; - attrib->_auto_glow_on = _auto_glow_on; - attrib->_auto_gloss_on = _auto_gloss_on; - attrib->_auto_ramp_on = _auto_ramp_on; - attrib->_auto_shadow_on = _auto_shadow_on; - attrib->_flags = _flags; - return return_new(attrib); -} - /** * Factory method to generate a Shader object */ diff --git a/panda/src/pgraph/shaderAttrib.h b/panda/src/pgraph/shaderAttrib.h index 5a28e929b1..cb1eec37d9 100644 --- a/panda/src/pgraph/shaderAttrib.h +++ b/panda/src/pgraph/shaderAttrib.h @@ -31,6 +31,7 @@ #include "pta_LVecBase4.h" #include "pta_LVecBase3.h" #include "pta_LVecBase2.h" +#include "extension.h" /** * @@ -70,8 +71,9 @@ PUBLISHED: CPT(RenderAttrib) clear_shader() const; // Shader Inputs - CPT(RenderAttrib) set_shader_input(const ShaderInput *inp) const; + CPT(RenderAttrib) set_shader_input(ShaderInput input) const; +public: INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, Texture *tex, int priority=0) const; INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0) const; INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, const PTA_float &v, int priority=0) const; @@ -89,6 +91,10 @@ PUBLISHED: INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, double n1=0, double n2=0, double n3=0, double n4=1, int priority=0) const; +PUBLISHED: + EXTENSION(CPT(RenderAttrib) set_shader_input(CPT_InternalName, PyObject *, int priority=0) const); + EXTENSION(CPT(RenderAttrib) set_shader_inputs(PyObject *args, PyObject *kwargs) const); + CPT(RenderAttrib) set_instance_count(int instance_count) const; CPT(RenderAttrib) set_flag(int flag, bool value) const; @@ -103,8 +109,8 @@ PUBLISHED: INLINE bool has_shader_input(CPT_InternalName id) const; const Shader *get_shader() const; - const ShaderInput *get_shader_input(const InternalName *id) const; - const ShaderInput *get_shader_input(const string &id) const; + const ShaderInput &get_shader_input(const InternalName *id) const; + const ShaderInput &get_shader_input(const string &id) const; const NodePath &get_shader_input_nodepath(const InternalName *id) const; LVecBase4 get_shader_input_vector(InternalName *id) const; @@ -126,7 +132,6 @@ protected: virtual int compare_to_impl(const RenderAttrib *other) const; virtual size_t get_hash_impl() const; virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: @@ -144,9 +149,14 @@ private: bool _auto_ramp_on; bool _auto_shadow_on; - typedef pmap Inputs; + // We don't keep a reference to the InternalName, since this is also already + // stored on the ShaderInput object. + typedef pmap Inputs; Inputs _inputs; + friend class Extension; + friend class Extension; + PUBLISHED: static int get_class_slot() { return _attrib_slot; @@ -154,6 +164,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static TypeHandle get_class_type() { diff --git a/panda/src/pgraph/shaderAttrib_ext.cxx b/panda/src/pgraph/shaderAttrib_ext.cxx new file mode 100644 index 0000000000..b98badf71b --- /dev/null +++ b/panda/src/pgraph/shaderAttrib_ext.cxx @@ -0,0 +1,71 @@ +/** + * 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 shaderAttrib_ext.cxx + * @author rdb + * @date 2017-10-08 + */ + +#include "shaderAttrib_ext.h" +#include "shaderInput_ext.h" + +#ifdef HAVE_PYTHON + +/** + * Returns a new ShaderAttrib with the given shader input set. + */ +CPT(RenderAttrib) Extension:: +set_shader_input(CPT_InternalName name, PyObject *value, int priority) const { + ShaderAttrib *attrib = new ShaderAttrib(*_this); + + ShaderInput &input = attrib->_inputs[name]; + invoke_extension(&input).__init__(move(name), value); + + return ShaderAttrib::return_new(attrib); +} + +/** + * Returns a new ShaderAttrib with the given shader inputs set. This is a + * more efficient way to set multiple shader inputs than calling + * set_shader_input multiple times. + */ +CPT(RenderAttrib) Extension:: +set_shader_inputs(PyObject *args, PyObject *kwargs) const { + if (PyObject_Size(args) > 0) { + Dtool_Raise_TypeError("ShaderAttrib.set_shader_inputs takes only keyword arguments"); + return nullptr; + } + + ShaderAttrib *attrib = new ShaderAttrib(*_this); + + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next(kwargs, &pos, &key, &value)) { + char *buffer; + Py_ssize_t length; +#if PY_MAJOR_VERSION >= 3 + buffer = (char *)PyUnicode_AsUTF8AndSize(key, &length); + if (buffer == nullptr) { +#else + if (PyString_AsStringAndSize(key, &buffer, &length) == -1) { +#endif + Dtool_Raise_TypeError("ShaderAttrib.set_shader_inputs accepts only string keywords"); + delete attrib; + return nullptr; + } + + CPT_InternalName name(string(buffer, length)); + ShaderInput &input = attrib->_inputs[name]; + invoke_extension(&input).__init__(move(name), value); + } + + return ShaderAttrib::return_new(attrib); +} + +#endif // HAVE_PYTHON diff --git a/panda/src/pgraph/shaderAttrib_ext.h b/panda/src/pgraph/shaderAttrib_ext.h new file mode 100644 index 0000000000..abf4fca751 --- /dev/null +++ b/panda/src/pgraph/shaderAttrib_ext.h @@ -0,0 +1,38 @@ +/** + * 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 shaderAttrib_ext.h + * @author rdb + * @date 2017-10-08 + */ + +#ifndef SHADERATTRIB_EXT_H +#define SHADERATTRIB_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "shaderAttrib.h" +#include "py_panda.h" + +/** + * This class defines the extension methods for ShaderAttrib, which are called + * instead of any C++ methods with the same prototype. + */ +template<> +class Extension : public ExtensionBase { +public: + CPT(RenderAttrib) set_shader_input(CPT_InternalName id, PyObject *value, int priority=0) const; + CPT(RenderAttrib) set_shader_inputs(PyObject *args, PyObject *kwargs) const; +}; + +#endif // HAVE_PYTHON + +#endif // SHADERATTRIB_EXT_H diff --git a/panda/src/pgraph/shaderInput.I b/panda/src/pgraph/shaderInput.I index 7344717e47..8c37322082 100644 --- a/panda/src/pgraph/shaderInput.I +++ b/panda/src/pgraph/shaderInput.I @@ -13,13 +13,6 @@ * @date 2010-04-06 */ -/** - * - */ -INLINE ShaderInput:: -~ShaderInput() { -} - /** * */ @@ -424,6 +417,89 @@ ShaderInput(CPT_InternalName name, const LVecBase2i &vec, int priority) : { } +/** + * + */ +INLINE ShaderInput:: +operator bool () const { + return _type != M_invalid; +} + +/** + * + */ +INLINE bool ShaderInput:: +operator == (const ShaderInput &other) const { + if (_type != other._type || _name != other._name || _priority != other._priority) { + return false; + } + switch (_type) { + case M_invalid: + return true; + + case M_vector: + return _stored_vector == other._stored_vector; + + case M_numeric: + return _stored_ptr._ptr == other._stored_ptr._ptr; + + default: + return _value == other._value; + } +} + +/** + * + */ +INLINE bool ShaderInput:: +operator != (const ShaderInput &other) const { + if (_type != other._type || _name != other._name || _priority != other._priority) { + return true; + } + switch (_type) { + case M_invalid: + return false; + + case M_vector: + return _stored_vector != other._stored_vector; + + case M_numeric: + return _stored_ptr._ptr != other._stored_ptr._ptr; + + default: + return _value != other._value; + } +} + +/** + * + */ +INLINE bool ShaderInput:: +operator < (const ShaderInput &other) const { + if (_type != other._type) { + return (_type < other._type); + } + if (_name != other._name) { + return (_name < other._name); + } + if (_priority != other._priority) { + return (_priority < other._priority); + } + switch (_type) { + case M_invalid: + return false; + + case M_vector: + return _stored_vector < other._stored_vector; + + case M_numeric: + return _stored_ptr._ptr < other._stored_ptr._ptr; + + default: + return _value < other._value; + } +} + /** * */ @@ -471,3 +547,11 @@ INLINE ParamValueBase *ShaderInput:: get_param() const { return DCAST(ParamValueBase, _value); } + +/** + * + */ +INLINE TypedWritableReferenceCount *ShaderInput:: +get_value() const { + return _value.p(); +} diff --git a/panda/src/pgraph/shaderInput.cxx b/panda/src/pgraph/shaderInput.cxx index 3ee38ef70a..44fb91cf23 100644 --- a/panda/src/pgraph/shaderInput.cxx +++ b/panda/src/pgraph/shaderInput.cxx @@ -15,18 +15,13 @@ #include "paramNodePath.h" #include "paramTexture.h" -TypeHandle ShaderInput::_type_handle; - /** * Returns a static ShaderInput object with name NULL, priority zero, type * INVALID, and all value-fields cleared. */ -const ShaderInput *ShaderInput:: +const ShaderInput &ShaderInput:: get_blank() { - static CPT(ShaderInput) blank; - if (blank == 0) { - blank = new ShaderInput(NULL, 0); - } + static ShaderInput blank(nullptr, 0); return blank; } @@ -66,6 +61,30 @@ ShaderInput(CPT_InternalName name, Texture *tex, const SamplerState &sampler, in { } +/** + * + */ +size_t ShaderInput:: +add_hash(size_t hash) const { + hash = int_hash::add_hash(hash, _type); + hash = pointer_hash::add_hash(hash, _name); + hash = int_hash::add_hash(hash, _priority); + + switch (_type) { + case M_invalid: + return hash; + + case M_vector: + return _stored_vector.add_hash(hash); + + case M_numeric: + return pointer_hash::add_hash(hash, _stored_ptr._ptr); + + default: + return pointer_hash::add_hash(hash, _value); + } +} + /** * Warning: no error checking is done. This *will* crash if get_value_type() * is not M_nodepath. diff --git a/panda/src/pgraph/shaderInput.h b/panda/src/pgraph/shaderInput.h index c91c790618..39a4e32fe8 100644 --- a/panda/src/pgraph/shaderInput.h +++ b/panda/src/pgraph/shaderInput.h @@ -17,7 +17,6 @@ #define SHADERINPUT_H #include "pandabase.h" -#include "typedWritableReferenceCount.h" #include "pointerTo.h" #include "internalName.h" #include "paramValue.h" @@ -32,15 +31,13 @@ #include "shader.h" #include "texture.h" #include "shaderBuffer.h" +#include "extension.h" /** * This is a small container class that can hold any one of the value types * that can be passed as input to a shader. */ -class EXPCL_PANDA_PGRAPH ShaderInput : public TypedWritableReferenceCount { -public: - INLINE ~ShaderInput(); - +class EXPCL_PANDA_PGRAPH ShaderInput { PUBLISHED: // Used when binding texture images. enum AccessFlags { @@ -49,8 +46,12 @@ PUBLISHED: A_layered = 0x04, }; - static const ShaderInput *get_blank(); - INLINE ShaderInput(CPT_InternalName name, int priority=0); + static const ShaderInput &get_blank(); + INLINE explicit ShaderInput(CPT_InternalName name, int priority=0); + + EXTENSION(explicit ShaderInput(CPT_InternalName name, PyObject *value, int priority=0)); + +public: INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0); INLINE ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority=0); INLINE ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority=0); @@ -87,8 +88,10 @@ PUBLISHED: INLINE ShaderInput(CPT_InternalName name, const LVecBase2i &vec, int priority=0); ShaderInput(CPT_InternalName name, const NodePath &np, int priority=0); - ShaderInput(CPT_InternalName name, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0); - ShaderInput(CPT_InternalName name, Texture *tex, const SamplerState &sampler, int priority=0); + +PUBLISHED: + explicit ShaderInput(CPT_InternalName name, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0); + explicit ShaderInput(CPT_InternalName name, Texture *tex, const SamplerState &sampler, int priority=0); enum ShaderInputType { M_invalid = 0, @@ -102,6 +105,13 @@ PUBLISHED: M_buffer, }; + INLINE operator bool() const; + INLINE bool operator == (const ShaderInput &other) const; + INLINE bool operator != (const ShaderInput &other) const; + INLINE bool operator < (const ShaderInput &other) const; + + size_t add_hash(size_t hash) const; + INLINE const InternalName *get_name() const; INLINE int get_value_type() const; @@ -114,7 +124,10 @@ PUBLISHED: const SamplerState &get_sampler() const; public: + ShaderInput() DEFAULT_CTOR; + INLINE ParamValueBase *get_param() const; + INLINE TypedWritableReferenceCount *get_value() const; static void register_with_read_factory(); @@ -127,26 +140,9 @@ private: int _type; friend class ShaderAttrib; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - TypedWritableReferenceCount::init_type(); - register_type(_type_handle, "ShaderInput", - TypedWritableReferenceCount::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; + friend class Extension; }; - #include "shaderInput.I" #endif // SHADERINPUT_H diff --git a/panda/src/pgraph/shaderInput_ext.cxx b/panda/src/pgraph/shaderInput_ext.cxx new file mode 100644 index 0000000000..d09a971b46 --- /dev/null +++ b/panda/src/pgraph/shaderInput_ext.cxx @@ -0,0 +1,538 @@ +/** + * 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 shaderInput_ext.cxx + * @author rdb + * @date 2017-10-06 + */ + +#include "shaderInput_ext.h" +#include "paramNodePath.h" + +#ifdef HAVE_PYTHON + +#ifndef CPPPARSER +extern struct Dtool_PyTypedObject Dtool_Texture; +extern struct Dtool_PyTypedObject Dtool_NodePath; +extern struct Dtool_PyTypedObject Dtool_PointerToVoid; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_float; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_double; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_int; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4f; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3f; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2f; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLMatrix4f; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_LMatrix3f; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4d; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3d; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2d; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLMatrix4d; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_LMatrix3d; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4i; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3i; +extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2i; +extern struct Dtool_PyTypedObject Dtool_LMatrix4f; +extern struct Dtool_PyTypedObject Dtool_LMatrix3f; +extern struct Dtool_PyTypedObject Dtool_LMatrix4d; +extern struct Dtool_PyTypedObject Dtool_LMatrix3d; +extern struct Dtool_PyTypedObject Dtool_LVecBase4f; +extern struct Dtool_PyTypedObject Dtool_LVecBase3f; +extern struct Dtool_PyTypedObject Dtool_LVecBase2f; +extern struct Dtool_PyTypedObject Dtool_LVecBase4d; +extern struct Dtool_PyTypedObject Dtool_LVecBase3d; +extern struct Dtool_PyTypedObject Dtool_LVecBase2d; +extern struct Dtool_PyTypedObject Dtool_LVecBase4i; +extern struct Dtool_PyTypedObject Dtool_LVecBase3i; +extern struct Dtool_PyTypedObject Dtool_LVecBase2i; +extern struct Dtool_PyTypedObject Dtool_ShaderBuffer; +extern struct Dtool_PyTypedObject Dtool_ParamValueBase; +#endif // CPPPARSER + +/** + * Sets a shader input from an arbitrary Python object. + */ +void Extension:: +__init__(CPT_InternalName name, PyObject *value, int priority) { + _this->_name = move(name); + _this->_priority = priority; + + if (PyTuple_CheckExact(value) && PyTuple_GET_SIZE(value) <= 4) { + // A tuple is interpreted as a vector. + Py_ssize_t size = PyTuple_GET_SIZE(value); + + // If any of them is a float, we are storing it as a float vector. + bool is_float = false; + for (Py_ssize_t i = 0; i < size; ++i) { + if (PyFloat_CheckExact(PyTuple_GET_ITEM(value, i))) { + is_float = true; + break; + } + } + if (is_float) { + LVecBase4 vec(0); + for (Py_ssize_t i = 0; i < size; ++i) { + vec[i] = (PN_stdfloat)PyFloat_AsDouble(PyTuple_GET_ITEM(value, i)); + } + _this->_type = ShaderInput::M_vector; + _this->_stored_ptr = vec; + _this->_stored_vector = vec; + } else { + LVecBase4i vec(0); + for (Py_ssize_t i = 0; i < size; ++i) { + vec[i] = (int)PyLong_AsLong(PyTuple_GET_ITEM(value, i)); + } + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = vec; + _this->_stored_vector = LCAST(PN_stdfloat, vec); + } + + } else if (DtoolInstance_Check(value)) { + void *ptr; + + if ((ptr = DtoolInstance_UPCAST(value, Dtool_Texture))) { + _this->_type = ShaderInput::M_texture; + _this->_value = (Texture *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_NodePath))) { + _this->_type = ShaderInput::M_nodepath; + _this->_value = new ParamNodePath(*(const NodePath *)ptr); + + } else if (DtoolInstance_UPCAST(value, Dtool_PointerToVoid)) { + if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_float))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_float *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_double))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_double *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_int))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_int *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_UnalignedLVecBase4f))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LVecBase4f *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_LVecBase3f))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LVecBase3f *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_LVecBase2f))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LVecBase2f *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_UnalignedLMatrix4f))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LMatrix4f *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_LMatrix3f))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LMatrix3f *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_UnalignedLVecBase4d))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LVecBase4d *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_LVecBase3d))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LVecBase3d *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_LVecBase2d))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LVecBase2d *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_UnalignedLMatrix4d))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LMatrix4d *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_LMatrix3d))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LMatrix3d *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_UnalignedLVecBase4i))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LVecBase4i *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_LVecBase3i))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LVecBase3i *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_PointerToArray_LVecBase2i))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const PTA_LVecBase2i *)ptr; + + } else { + Dtool_Raise_TypeError("unknown type passed to ShaderInput"); + return; + } + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LMatrix4f))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const LMatrix4f *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LMatrix3f))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const LMatrix3f *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LMatrix4d))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const LMatrix4d *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LMatrix3d))) { + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = *(const LMatrix3d *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LVecBase4f))) { + const LVecBase4f &vec = *(const LVecBase4f *)ptr; + _this->_type = ShaderInput::M_vector; + _this->_stored_ptr = vec; + _this->_stored_vector = LCAST(PN_stdfloat, vec); + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LVecBase3f))) { + const LVecBase3f &vec = *(const LVecBase3f *)ptr; + _this->_type = ShaderInput::M_vector; + _this->_stored_ptr = vec; + _this->_stored_vector.set(vec[0], vec[1], vec[2], 0); + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LVecBase2f))) { + const LVecBase2f &vec = *(const LVecBase2f *)ptr; + _this->_type = ShaderInput::M_vector; + _this->_stored_ptr = vec; + _this->_stored_vector.set(vec[0], vec[1], 0, 0); + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LVecBase4d))) { + const LVecBase4d &vec = *(const LVecBase4d *)ptr; + _this->_type = ShaderInput::M_vector; + _this->_stored_ptr = vec; + _this->_stored_vector = LCAST(PN_stdfloat, vec); + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LVecBase3d))) { + const LVecBase3d &vec = *(const LVecBase3d *)ptr; + _this->_type = ShaderInput::M_vector; + _this->_stored_ptr = vec; + _this->_stored_vector.set(vec[0], vec[1], vec[2], 0); + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LVecBase2d))) { + const LVecBase2d &vec = *(const LVecBase2d *)ptr; + _this->_type = ShaderInput::M_vector; + _this->_stored_ptr = vec; + _this->_stored_vector.set(vec[0], vec[1], 0, 0); + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LVecBase4i))) { + const LVecBase4i &vec = *(const LVecBase4i *)ptr; + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = vec; + _this->_stored_vector = LCAST(PN_stdfloat, vec); + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LVecBase3i))) { + const LVecBase3i &vec = *(const LVecBase3i *)ptr; + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = vec; + _this->_stored_vector.set(vec[0], vec[1], vec[2], 0); + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_LVecBase2i))) { + const LVecBase2i &vec = *(const LVecBase2i *)ptr; + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = vec; + _this->_stored_vector.set(vec[0], vec[1], 0, 0); + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_ShaderBuffer))) { + _this->_type = ShaderInput::M_buffer; + _this->_value = (ShaderBuffer *)ptr; + + } else if ((ptr = DtoolInstance_UPCAST(value, Dtool_ParamValueBase))) { + _this->_type = ShaderInput::M_param; + _this->_value = (ParamValueBase *)ptr; + + } else { + Dtool_Raise_TypeError("unknown type passed to ShaderInput"); + return; + } + + } else if (PyFloat_Check(value)) { + LVecBase4 vec(PyFloat_AS_DOUBLE(value), 0, 0, 0); + _this->_type = ShaderInput::M_vector; + _this->_stored_ptr = vec; + _this->_stored_vector = vec; + +#if PY_MAJOR_VERSION < 3 + } else if (PyInt_Check(value)) { + LVecBase4i vec((int)PyInt_AS_LONG(value), 0, 0, 0); + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = vec; + _this->_stored_vector.set((PN_stdfloat)vec[0], 0, 0, 0); +#endif + + } else if (PyLong_Check(value)) { + LVecBase4i vec((int)PyLong_AsLong(value), 0, 0, 0); + _this->_type = ShaderInput::M_numeric; + _this->_stored_ptr = vec; + _this->_stored_vector.set((PN_stdfloat)vec[0], 0, 0, 0); + + } else if (PySequence_Check(value) && !PyUnicode_CheckExact(value)) { + // Iterate over the sequence to make sure all have the same type. + PyObject *fast = PySequence_Fast(value, "unknown type passed to ShaderInput"); + if (fast == nullptr) { + return; + } + + Py_ssize_t num_items = PySequence_Fast_GET_SIZE(value); + if (num_items <= 0) { + // We can't determine the type of a list of size 0. + _this->_type = ShaderInput::M_numeric; + Py_DECREF(fast); + return; + } + + bool has_float = false; + Py_ssize_t known_itemsize = -1; + + PyObject **items = PySequence_Fast_ITEMS(fast); + for (Py_ssize_t i = 0; i < num_items; ++i) { + PyObject *item = items[i]; + + if (PySequence_Check(item)) { + Py_ssize_t itemsize = PySequence_Size(item); + if (known_itemsize >= 0 && itemsize != known_itemsize) { + Dtool_Raise_TypeError("inconsistent sequence length among elements of sequence passed to ShaderInput"); + Py_DECREF(fast); + return; + } + known_itemsize = itemsize; + + // Check their types. + for (Py_ssize_t j = 0; j < itemsize; ++j) { + PyObject *subitem = PySequence_ITEM(item, j); + if (PyFloat_CheckExact(subitem)) { + Py_DECREF(subitem); + has_float = true; + break; + } else if (PyLongOrInt_Check(subitem)) { + } else { + Dtool_Raise_TypeError("unknown element type in sequence passed as element of sequence passed to ShaderInput"); + Py_DECREF(subitem); + Py_DECREF(fast); + break; + } + Py_DECREF(subitem); + } + } else if (PyFloat_CheckExact(item)) { + has_float = true; + } else if (PyLongOrInt_Check(item)) { + } else { + Dtool_Raise_TypeError("unknown element type in sequence passed to ShaderInput"); + Py_DECREF(fast); + return; + } + } + + // Now that we have verified the dimensions and type of the PTA, we can + // read in the actual elements. + switch (known_itemsize) { + case -1: + if (has_float) { + PTA_float pta; + pta.reserve(num_items); + for (Py_ssize_t i = 0; i < num_items; ++i) { + pta.push_back(PyFloat_AsDouble(items[i])); + } + _this->_stored_ptr = pta; + } else { + PTA_int pta; + pta.reserve(num_items); + for (Py_ssize_t i = 0; i < num_items; ++i) { + pta.push_back((int)PyLongOrInt_AS_LONG(items[i])); + } + _this->_stored_ptr = pta; + } + break; + + case 1: + if (has_float) { + PTA_float pta; + pta.reserve(num_items); + for (Py_ssize_t i = 0; i < num_items; ++i) { + PyObject *item = items[i]; + if (PySequence_Check(item)) { + PyObject *subitem = PySequence_ITEM(item, 0); + pta.push_back(PyFloat_AsDouble(subitem)); + Py_DECREF(subitem); + } else { + pta.push_back(PyFloat_AsDouble(item)); + } + } + _this->_stored_ptr = pta; + } else { + PTA_int pta; + pta.reserve(num_items); + for (Py_ssize_t i = 0; i < num_items; ++i) { + PyObject *item = items[i]; + if (PySequence_Check(item)) { + PyObject *subitem = PySequence_ITEM(item, 0); + pta.push_back((int)PyLongOrInt_AS_LONG(subitem)); + Py_DECREF(subitem); + } else { + pta.push_back((int)PyLongOrInt_AS_LONG(item)); + } + } + _this->_stored_ptr = pta; + } + break; + + case 2: + if (has_float) { + PTA_LVecBase2f pta; + pta.reserve(num_items); + for (Py_ssize_t i = 0; i < num_items; ++i) { + PyObject *item = items[i]; + if (PySequence_Check(item)) { + PyObject *subitem0 = PySequence_ITEM(item, 0); + PyObject *subitem1 = PySequence_ITEM(item, 1); + pta.push_back(LVecBase2f(PyFloat_AsDouble(subitem0), + PyFloat_AsDouble(subitem1))); + Py_DECREF(subitem0); + Py_DECREF(subitem1); + } else { + pta.push_back(PyFloat_AsDouble(item)); + } + } + _this->_stored_ptr = pta; + } else { + PTA_LVecBase2i pta; + pta.reserve(num_items); + for (Py_ssize_t i = 0; i < num_items; ++i) { + PyObject *item = items[i]; + if (PySequence_Check(item)) { + PyObject *subitem0 = PySequence_ITEM(item, 0); + PyObject *subitem1 = PySequence_ITEM(item, 1); + pta.push_back(LVecBase2i((int)PyLongOrInt_AS_LONG(subitem0), + (int)PyLongOrInt_AS_LONG(subitem1))); + Py_DECREF(subitem0); + Py_DECREF(subitem1); + } else { + pta.push_back((int)PyLongOrInt_AS_LONG(item)); + } + } + _this->_stored_ptr = pta; + } + break; + + case 3: + if (has_float) { + PTA_LVecBase3f pta; + pta.reserve(num_items); + for (Py_ssize_t i = 0; i < num_items; ++i) { + PyObject *item = items[i]; + if (PySequence_Check(item)) { + PyObject *subitem0 = PySequence_ITEM(item, 0); + PyObject *subitem1 = PySequence_ITEM(item, 1); + PyObject *subitem2 = PySequence_ITEM(item, 2); + pta.push_back(LVecBase3f(PyFloat_AsDouble(subitem0), + PyFloat_AsDouble(subitem1), + PyFloat_AsDouble(subitem2))); + Py_DECREF(subitem0); + Py_DECREF(subitem1); + Py_DECREF(subitem2); + } else { + pta.push_back(PyFloat_AsDouble(item)); + } + } + _this->_stored_ptr = pta; + } else { + PTA_LVecBase3i pta; + pta.reserve(num_items); + for (Py_ssize_t i = 0; i < num_items; ++i) { + PyObject *item = items[i]; + if (PySequence_Check(item)) { + PyObject *subitem0 = PySequence_ITEM(item, 0); + PyObject *subitem1 = PySequence_ITEM(item, 1); + PyObject *subitem2 = PySequence_ITEM(item, 2); + pta.push_back(LVecBase3i((int)PyLongOrInt_AS_LONG(subitem0), + (int)PyLongOrInt_AS_LONG(subitem1), + (int)PyLongOrInt_AS_LONG(subitem2))); + Py_DECREF(subitem0); + Py_DECREF(subitem1); + Py_DECREF(subitem2); + } else { + pta.push_back((int)PyLongOrInt_AS_LONG(item)); + } + } + _this->_stored_ptr = pta; + } + break; + + case 4: + if (has_float) { + PTA_LVecBase4f pta; + pta.reserve(num_items); + for (Py_ssize_t i = 0; i < num_items; ++i) { + PyObject *item = items[i]; + if (PySequence_Check(item)) { + PyObject *subitem0 = PySequence_ITEM(item, 0); + PyObject *subitem1 = PySequence_ITEM(item, 1); + PyObject *subitem2 = PySequence_ITEM(item, 2); + PyObject *subitem3 = PySequence_ITEM(item, 3); + pta.push_back(LVecBase4f(PyFloat_AsDouble(subitem0), + PyFloat_AsDouble(subitem1), + PyFloat_AsDouble(subitem2), + PyFloat_AsDouble(subitem3))); + Py_DECREF(subitem0); + Py_DECREF(subitem1); + Py_DECREF(subitem2); + Py_DECREF(subitem3); + } else { + pta.push_back(PyFloat_AsDouble(item)); + } + } + _this->_stored_ptr = pta; + } else { + PTA_LVecBase4i pta; + pta.reserve(num_items); + for (Py_ssize_t i = 0; i < num_items; ++i) { + PyObject *item = items[i]; + if (PySequence_Check(item)) { + PyObject *subitem0 = PySequence_ITEM(item, 0); + PyObject *subitem1 = PySequence_ITEM(item, 1); + PyObject *subitem2 = PySequence_ITEM(item, 2); + PyObject *subitem3 = PySequence_ITEM(item, 3); + pta.push_back(LVecBase4i((int)PyLongOrInt_AS_LONG(subitem0), + (int)PyLongOrInt_AS_LONG(subitem1), + (int)PyLongOrInt_AS_LONG(subitem2), + (int)PyLongOrInt_AS_LONG(subitem3))); + Py_DECREF(subitem0); + Py_DECREF(subitem1); + Py_DECREF(subitem2); + Py_DECREF(subitem3); + } else { + pta.push_back((int)PyLongOrInt_AS_LONG(item)); + } + } + _this->_stored_ptr = pta; + } + break; + + case 0: + Dtool_Raise_TypeError("sequence passed to ShaderInput contains an empty sequence"); + break; + + default: + Dtool_Raise_TypeError("sequence passed to ShaderInput contains a sequence of more than 4 elements"); + break; + } + + _this->_type = ShaderInput::M_numeric; + + Py_DECREF(fast); + + } else { + Dtool_Raise_TypeError("unknown type passed to ShaderInput"); + } +} + +#endif // HAVE_PYTHON diff --git a/panda/src/pgraph/shaderInput_ext.h b/panda/src/pgraph/shaderInput_ext.h new file mode 100644 index 0000000000..c624a45ef8 --- /dev/null +++ b/panda/src/pgraph/shaderInput_ext.h @@ -0,0 +1,37 @@ +/** + * 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 shaderInput_ext.h + * @author rdb + * @date 2017-10-06 + */ + +#ifndef SHADERINPUT_EXT_H +#define SHADERINPUT_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "shaderInput.h" +#include "py_panda.h" + +/** + * This class defines the extension methods for NodePath, which are called + * instead of any C++ methods with the same prototype. + */ +template<> +class Extension : public ExtensionBase { +public: + void __init__(CPT_InternalName name, PyObject *value, int priority=0); +}; + +#endif // HAVE_PYTHON + +#endif // SHADERINPUT_EXT_H diff --git a/panda/src/pgraph/stateMunger.I b/panda/src/pgraph/stateMunger.I index edd3f6669b..7e35a2450c 100644 --- a/panda/src/pgraph/stateMunger.I +++ b/panda/src/pgraph/stateMunger.I @@ -16,6 +16,15 @@ */ INLINE StateMunger:: StateMunger(GraphicsStateGuardianBase *gsg) : - GeomMunger(gsg) + GeomMunger(gsg), + _should_munge_state(false) { } + +/** + * Returns true if this munger has something interesting to do to the state. + */ +INLINE bool StateMunger:: +should_munge_state() const { + return _should_munge_state; +} diff --git a/panda/src/pgraph/stateMunger.cxx b/panda/src/pgraph/stateMunger.cxx index c57366133d..db94c81ba8 100644 --- a/panda/src/pgraph/stateMunger.cxx +++ b/panda/src/pgraph/stateMunger.cxx @@ -27,15 +27,20 @@ StateMunger:: */ CPT(RenderState) StateMunger:: munge_state(const RenderState *state) { - int mi = _state_map.find(state); + RenderState::MungedStates &munged_states = state->_munged_states; + + int id = get_gsg()->_id; + int mi = munged_states.find(id); if (mi != -1) { - if (!_state_map.get_data(mi).was_deleted()) { - return _state_map.get_data(mi).p(); + if (!munged_states.get_data(mi).was_deleted()) { + return munged_states.get_data(mi).p(); + } else { + munged_states.remove_element(mi); } } CPT(RenderState) result = munge_state_impl(state); - _state_map.store(state, result.p()); + munged_states.store(id, result.p()); return result; } diff --git a/panda/src/pgraph/stateMunger.h b/panda/src/pgraph/stateMunger.h index b246d67413..c7fa6ef862 100644 --- a/panda/src/pgraph/stateMunger.h +++ b/panda/src/pgraph/stateMunger.h @@ -30,11 +30,12 @@ public: virtual ~StateMunger(); CPT(RenderState) munge_state(const RenderState *state); + INLINE bool should_munge_state() const; + protected: virtual CPT(RenderState) munge_state_impl(const RenderState *state); - typedef WeakKeyHashMap StateMap; - StateMap _state_map; + bool _should_munge_state; public: static TypeHandle get_class_type() { diff --git a/panda/src/pgraph/stencilAttrib.h b/panda/src/pgraph/stencilAttrib.h index 9e9086928b..6b0b773581 100644 --- a/panda/src/pgraph/stencilAttrib.h +++ b/panda/src/pgraph/stencilAttrib.h @@ -155,6 +155,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/texGenAttrib.cxx b/panda/src/pgraph/texGenAttrib.cxx index c7ace978c0..451eacab83 100644 --- a/panda/src/pgraph/texGenAttrib.cxx +++ b/panda/src/pgraph/texGenAttrib.cxx @@ -432,14 +432,6 @@ invert_compose_impl(const RenderAttrib *other) const { return return_new(attrib); } -/** - * - */ -CPT(RenderAttrib) TexGenAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return this; -} - /** * This method is to be called after the _stages map has been built up * internally through some artificial means; it copies the appropriate diff --git a/panda/src/pgraph/texGenAttrib.h b/panda/src/pgraph/texGenAttrib.h index c2b0e87132..b9e1cd273a 100644 --- a/panda/src/pgraph/texGenAttrib.h +++ b/panda/src/pgraph/texGenAttrib.h @@ -68,7 +68,6 @@ protected: virtual size_t get_hash_impl() const; virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const; virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: class ModeDef; @@ -115,6 +114,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/texMatrixAttrib.cxx b/panda/src/pgraph/texMatrixAttrib.cxx index 84538033fd..4e2e1adba8 100644 --- a/panda/src/pgraph/texMatrixAttrib.cxx +++ b/panda/src/pgraph/texMatrixAttrib.cxx @@ -415,28 +415,6 @@ invert_compose_impl(const RenderAttrib *other) const { return return_new(attrib); } -/** - * - */ -CPT(RenderAttrib) TexMatrixAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - // For a TexMatrixAttrib, the particular matrix per TextureStage isn't - // important, just whether there is a matrix at all. So we create a new - // state with an identity matrix everywhere there is a matrix at all in the - // original. - - TexMatrixAttrib *attrib = new TexMatrixAttrib; - - Stages::const_iterator ai; - for (ai = _stages.begin(); ai != _stages.end(); ++ai) { - StageNode sn((*ai)._stage); - sn._transform = TransformState::make_identity(); - attrib->_stages.insert(attrib->_stages.end(), sn); - } - - return return_new(attrib); -} - /** * Tells the BamReader how to create objects of type TexMatrixAttrib. */ diff --git a/panda/src/pgraph/texMatrixAttrib.h b/panda/src/pgraph/texMatrixAttrib.h index b0b7790851..f68238cf9f 100644 --- a/panda/src/pgraph/texMatrixAttrib.h +++ b/panda/src/pgraph/texMatrixAttrib.h @@ -67,7 +67,6 @@ protected: virtual size_t get_hash_impl() const; virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const; virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: INLINE void check_stage_list() const; @@ -102,6 +101,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/textureAttrib.cxx b/panda/src/pgraph/textureAttrib.cxx index 157bf1d146..9b30906e1f 100644 --- a/panda/src/pgraph/textureAttrib.cxx +++ b/panda/src/pgraph/textureAttrib.cxx @@ -106,6 +106,8 @@ find_on_stage(const TextureStage *stage) const { */ CPT(RenderAttrib) TextureAttrib:: add_on_stage(TextureStage *stage, Texture *tex, int override) const { + nassertr(tex != nullptr, this); + TextureAttrib *attrib = new TextureAttrib(*this); Stages::iterator si = attrib->_on_stages.insert(StageNode(stage)).first; (*si)._override = override; @@ -127,6 +129,8 @@ add_on_stage(TextureStage *stage, Texture *tex, int override) const { */ CPT(RenderAttrib) TextureAttrib:: add_on_stage(TextureStage *stage, Texture *tex, const SamplerState &sampler, int override) const { + nassertr(tex != nullptr, this); + TextureAttrib *attrib = new TextureAttrib(*this); Stages::iterator si = attrib->_on_stages.insert(StageNode(stage)).first; (*si)._override = override; @@ -381,10 +385,9 @@ output(ostream &out) const { const StageNode &sn = *(*ri); TextureStage *stage = sn._stage; Texture *tex = sn._texture; - if (tex != NULL) { - out << " " << stage->get_name() << ":" << tex->get_name(); - } else { - out << " " << stage->get_name(); + out << " " << stage->get_name(); + if (tex != nullptr) { + out << ":" << tex->get_name(); } if (sn._override != 0) { out << "^" << sn._override; @@ -734,14 +737,6 @@ invert_compose_impl(const RenderAttrib *other) const { return other; } -/** - * - */ -CPT(RenderAttrib) TextureAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return this; -} - /** * Tells the BamReader how to create objects of type TextureAttrib. */ diff --git a/panda/src/pgraph/textureAttrib.h b/panda/src/pgraph/textureAttrib.h index 2cc5270536..0938df8d96 100644 --- a/panda/src/pgraph/textureAttrib.h +++ b/panda/src/pgraph/textureAttrib.h @@ -64,12 +64,22 @@ PUBLISHED: int find_on_stage(const TextureStage *stage) const; + MAKE_SEQ_PROPERTY(on_stages, get_num_on_stages, get_on_stage); + + MAKE_MAP_PROPERTY(textures, has_on_stage, get_on_texture); + MAKE_MAP_KEYS_SEQ(textures, get_num_on_stages, get_on_stage); + + MAKE_MAP_PROPERTY(samplers, has_on_stage, get_on_sampler); + MAKE_MAP_KEYS_SEQ(samplers, get_num_on_stages, get_on_stage); + INLINE int get_num_off_stages() const; INLINE TextureStage *get_off_stage(int n) const; MAKE_SEQ(get_off_stages, get_num_off_stages, get_off_stage); INLINE bool has_off_stage(TextureStage *stage) const; INLINE bool has_all_off() const; + MAKE_SEQ_PROPERTY(off_stages, get_num_off_stages, get_off_stage); + INLINE bool is_identity() const; CPT(RenderAttrib) add_on_stage(TextureStage *stage, Texture *tex, int override = 0) const; @@ -94,7 +104,6 @@ protected: virtual size_t get_hash_impl() const; virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const; virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: INLINE void check_sorted() const; @@ -158,6 +167,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/transformState.I b/panda/src/pgraph/transformState.I index 3decc64a51..474b78891f 100644 --- a/panda/src/pgraph/transformState.I +++ b/panda/src/pgraph/transformState.I @@ -664,7 +664,7 @@ get_invert_composition_cache_num_entries() const { INLINE size_t TransformState:: get_composition_cache_size() const { LightReMutexHolder holder(*_states_lock); - return _composition_cache.get_size(); + return _composition_cache.get_num_entries(); } /** @@ -678,9 +678,6 @@ get_composition_cache_size() const { INLINE const TransformState *TransformState:: get_composition_cache_source(size_t n) const { LightReMutexHolder holder(*_states_lock); - if (!_composition_cache.has_element(n)) { - return NULL; - } return _composition_cache.get_key(n); } @@ -698,9 +695,6 @@ get_composition_cache_source(size_t n) const { INLINE const TransformState *TransformState:: get_composition_cache_result(size_t n) const { LightReMutexHolder holder(*_states_lock); - if (!_composition_cache.has_element(n)) { - return NULL; - } return _composition_cache.get_data(n)._result; } @@ -716,7 +710,7 @@ get_composition_cache_result(size_t n) const { INLINE size_t TransformState:: get_invert_composition_cache_size() const { LightReMutexHolder holder(*_states_lock); - return _invert_composition_cache.get_size(); + return _invert_composition_cache.get_num_entries(); } /** @@ -730,9 +724,6 @@ get_invert_composition_cache_size() const { INLINE const TransformState *TransformState:: get_invert_composition_cache_source(size_t n) const { LightReMutexHolder holder(*_states_lock); - if (!_invert_composition_cache.has_element(n)) { - return NULL; - } return _invert_composition_cache.get_key(n); } @@ -750,9 +741,6 @@ get_invert_composition_cache_source(size_t n) const { INLINE const TransformState *TransformState:: get_invert_composition_cache_result(size_t n) const { LightReMutexHolder holder(*_states_lock); - if (!_invert_composition_cache.has_element(n)) { - return NULL; - } return _invert_composition_cache.get_data(n)._result; } diff --git a/panda/src/pgraph/transformState.cxx b/panda/src/pgraph/transformState.cxx index 4d1aade0ec..bff497298e 100644 --- a/panda/src/pgraph/transformState.cxx +++ b/panda/src/pgraph/transformState.cxx @@ -29,7 +29,7 @@ TransformState::States *TransformState::_states = NULL; CPT(TransformState) TransformState::_identity_state; CPT(TransformState) TransformState::_invalid_state; UpdateSeq TransformState::_last_cycle_detect; -int TransformState::_garbage_index = 0; +size_t TransformState::_garbage_index = 0; bool TransformState::_uniquify_matrix = true; PStatCollector TransformState::_cache_update_pcollector("*:State Cache:Update"); @@ -62,6 +62,10 @@ TransformState() : _lock("TransformState") { _flags = F_is_identity | F_singular_known | F_is_2d; _inv_mat = (LMatrix4 *)NULL; _cache_stats.add_num_states(1); + +#ifdef DO_MEMORY_USAGE + MemoryUsage::update_type(this, this); +#endif } /** @@ -608,33 +612,74 @@ compose(const TransformState *other) const { return do_compose(other); } - // Is this composition already cached? - CPT(TransformState) result; - { - LightReMutexHolder holder(*_states_lock); - int index = _composition_cache.find(other); - if (index != -1) { - const Composition &comp = _composition_cache.get_data(index); - result = comp._result; - } - if (result != (TransformState *)NULL) { - _cache_stats.inc_hits(); - } - } + LightReMutexHolder holder(*_states_lock); - if (result != (TransformState *)NULL) { - // Success! - return result; + // Is this composition already cached? + int index = _composition_cache.find(other); + if (index != -1) { + const Composition &comp = _composition_cache.get_data(index); + if (comp._result != nullptr) { + // Success! + _cache_stats.inc_hits(); + return comp._result; + } } // Not in the cache. Compute a new result. It's important that we don't // hold the lock while we do this, or we lose the benefit of // parallelization. - result = do_compose(other); + CPT(TransformState) result = do_compose(other); - // It's OK to cast away the constness of this pointer, because the cache is - // a transparent property of the class. - return ((TransformState *)this)->store_compose(other, result); + if (index != -1) { + Composition &comp = _composition_cache.modify_data(index); + // Well, it wasn't cached already, but we already had an entry (probably + // created for the reverse direction), so use the same entry to store + // the new result. + comp._result = result; + + if (result != (const TransformState *)this) { + // See the comments below about the need to up the reference count + // only when the result is not the same as this. + result->cache_ref(); + } + // Here's the cache! + _cache_stats.inc_hits(); + return result; + } + _cache_stats.inc_misses(); + + // We need to make a new cache entry, both in this object and in the other + // object. We make both records so the other TransformState object will + // know to delete the entry from this object when it destructs, and vice- + // versa. + + // The cache entry in this object is the only one that indicates the result; + // the other will be NULL for now. + _cache_stats.add_total_size(1); + _cache_stats.inc_adds(_composition_cache.is_empty()); + + _composition_cache[other]._result = result; + + if (other != this) { + _cache_stats.add_total_size(1); + _cache_stats.inc_adds(other->_composition_cache.is_empty()); + other->_composition_cache[this]._result = NULL; + } + + if (result != (TransformState *)this) { + // If the result of do_compose() is something other than this, explicitly + // increment the reference count. We have to be sure to decrement it + // again later, when the composition entry is removed from the cache. + result->cache_ref(); + + // (If the result was just this again, we still store the result, but we + // don't increment the reference count, since that would be a self- + // referential leak.) + } + + _cache_stats.maybe_report("TransformState"); + + return result; } /** @@ -676,32 +721,69 @@ invert_compose(const TransformState *other) const { LightReMutexHolder holder(*_states_lock); - CPT(TransformState) result; - { - LightReMutexHolder holder(*_states_lock); - int index = _invert_composition_cache.find(other); - if (index != -1) { - const Composition &comp = _invert_composition_cache.get_data(index); - result = comp._result; - } - if (result != (TransformState *)NULL) { + int index = _invert_composition_cache.find(other); + if (index != -1) { + const Composition &comp = _invert_composition_cache.get_data(index); + if (comp._result != nullptr) { + // Success! _cache_stats.inc_hits(); + return comp._result; } } - if (result != (TransformState *)NULL) { - // Success! - return result; - } - // Not in the cache. Compute a new result. It's important that we don't // hold the lock while we do this, or we lose the benefit of // parallelization. - result = do_invert_compose(other); + CPT(TransformState) result = do_invert_compose(other); - // It's OK to cast away the constness of this pointer, because the cache is - // a transparent property of the class. - return ((TransformState *)this)->store_invert_compose(other, result); + // Is this composition already cached? + if (index != -1) { + Composition &comp = _invert_composition_cache.modify_data(index); + // Well, it wasn't cached already, but we already had an entry (probably + // created for the reverse direction), so use the same entry to store + // the new result. + comp._result = result; + + if (result != (const TransformState *)this) { + // See the comments below about the need to up the reference count + // only when the result is not the same as this. + result->cache_ref(); + } + // Here's the cache! + _cache_stats.inc_hits(); + return result; + } + _cache_stats.inc_misses(); + + // We need to make a new cache entry, both in this object and in the other + // object. We make both records so the other TransformState object will + // know to delete the entry from this object when it destructs, and vice- + // versa. + + // The cache entry in this object is the only one that indicates the result; + // the other will be NULL for now. + _cache_stats.add_total_size(1); + _cache_stats.inc_adds(_invert_composition_cache.is_empty()); + _invert_composition_cache[other]._result = result; + + if (other != this) { + _cache_stats.add_total_size(1); + _cache_stats.inc_adds(other->_invert_composition_cache.is_empty()); + other->_invert_composition_cache[this]._result = NULL; + } + + if (result != (TransformState *)this) { + // If the result of compose() is something other than this, explicitly + // increment the reference count. We have to be sure to decrement it + // again later, when the composition entry is removed from the cache. + result->cache_ref(); + + // (If the result was just this again, we still store the result, but we + // don't increment the reference count, since that would be a self- + // referential leak.) + } + + return result; } /** @@ -712,7 +794,7 @@ invert_compose(const TransformState *other) const { */ bool TransformState:: unref() const { - if (!transform_cache || garbage_collect_states) { + if (garbage_collect_states || !transform_cache) { // If we're not using the cache at all, or if we're relying on garbage // collection, just allow the pointer to unref normally. return ReferenceCount::unref(); @@ -760,11 +842,8 @@ bool TransformState:: validate_composition_cache() const { LightReMutexHolder holder(*_states_lock); - int size = _composition_cache.get_size(); - for (int i = 0; i < size; ++i) { - if (!_composition_cache.has_element(i)) { - continue; - } + size_t size = _composition_cache.get_num_entries(); + for (size_t i = 0; i < size; ++i) { const TransformState *source = _composition_cache.get_key(i); if (source != (TransformState *)NULL) { // Check that the source also has a pointer back to this one. We always @@ -783,11 +862,8 @@ validate_composition_cache() const { } } - size = _invert_composition_cache.get_size(); - for (int i = 0; i < size; ++i) { - if (!_invert_composition_cache.has_element(i)) { - continue; - } + size = _invert_composition_cache.get_num_entries(); + for (size_t i = 0; i < size; ++i) { const TransformState *source = _invert_composition_cache.get_key(i); if (source != (TransformState *)NULL) { // Check that the source also has a pointer back to this one. We always @@ -955,40 +1031,33 @@ get_num_unused_states() { typedef pmap StateCount; StateCount state_count; - int size = _states->get_size(); - for (int si = 0; si < size; ++si) { - if (!_states->has_element(si)) { - continue; - } + size_t size = _states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { const TransformState *state = _states->get_key(si); - int i; - int cache_size = state->_composition_cache.get_size(); + size_t i; + size_t cache_size = state->_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (state->_composition_cache.has_element(i)) { - const TransformState *result = state->_composition_cache.get_data(i)._result; - if (result != (const TransformState *)NULL && result != state) { - // Here's a TransformState that's recorded in the cache. Count it. - pair ir = - state_count.insert(StateCount::value_type(result, 1)); - if (!ir.second) { - // If the above insert operation fails, then it's already in the - // cache; increment its value. - (*(ir.first)).second++; - } + const TransformState *result = state->_composition_cache.get_data(i)._result; + if (result != (const TransformState *)NULL && result != state) { + // Here's a TransformState that's recorded in the cache. Count it. + pair ir = + state_count.insert(StateCount::value_type(result, 1)); + if (!ir.second) { + // If the above insert operation fails, then it's already in the + // cache; increment its value. + (*(ir.first)).second++; } } } - cache_size = state->_invert_composition_cache.get_size(); + cache_size = state->_invert_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (state->_invert_composition_cache.has_element(i)) { - const TransformState *result = state->_invert_composition_cache.get_data(i)._result; - if (result != (const TransformState *)NULL && result != state) { - pair ir = - state_count.insert(StateCount::value_type(result, 1)); - if (!ir.second) { - (*(ir.first)).second++; - } + const TransformState *result = state->_invert_composition_cache.get_data(i)._result; + if (result != (const TransformState *)NULL && result != state) { + pair ir = + state_count.insert(StateCount::value_type(result, 1)); + if (!ir.second) { + (*(ir.first)).second++; } } } @@ -1053,11 +1122,8 @@ clear_cache() { TempStates temp_states; temp_states.reserve(orig_size); - int size = _states->get_size(); - for (int si = 0; si < size; ++si) { - if (!_states->has_element(si)) { - continue; - } + size_t size = _states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { const TransformState *state = _states->get_key(si); temp_states.push_back(state); } @@ -1068,28 +1134,24 @@ clear_cache() { for (ti = temp_states.begin(); ti != temp_states.end(); ++ti) { TransformState *state = (TransformState *)(*ti).p(); - int i; - int cache_size = (int)state->_composition_cache.get_size(); + size_t i; + size_t cache_size = state->_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (state->_composition_cache.has_element(i)) { - const TransformState *result = state->_composition_cache.get_data(i)._result; - if (result != (const TransformState *)NULL && result != state) { - result->cache_unref(); - nassertr(result->get_ref_count() > 0, 0); - } + const TransformState *result = state->_composition_cache.get_data(i)._result; + if (result != (const TransformState *)NULL && result != state) { + result->cache_unref(); + nassertr(result->get_ref_count() > 0, 0); } } _cache_stats.add_total_size(-(int)state->_composition_cache.get_num_entries()); state->_composition_cache.clear(); - cache_size = state->_invert_composition_cache.get_size(); + cache_size = state->_invert_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (state->_invert_composition_cache.has_element(i)) { - const TransformState *result = state->_invert_composition_cache.get_data(i)._result; - if (result != (const TransformState *)NULL && result != state) { - result->cache_unref(); - nassertr(result->get_ref_count() > 0, 0); - } + const TransformState *result = state->_invert_composition_cache.get_data(i)._result; + if (result != (const TransformState *)NULL && result != state) { + result->cache_unref(); + nassertr(result->get_ref_count() > 0, 0); } } _cache_stats.add_total_size(-(int)state->_invert_composition_cache.get_num_entries()); @@ -1116,57 +1178,75 @@ garbage_collect() { if (_states == (States *)NULL || !garbage_collect_states) { return 0; } + LightReMutexHolder holder(*_states_lock); PStatTimer timer(_garbage_collect_pcollector); - int orig_size = _states->get_num_entries(); + size_t orig_size = _states->get_num_entries(); // How many elements to process this pass? - int size = _states->get_size(); - int num_this_pass = int(size * garbage_collect_states_rate); + size_t size = orig_size; + size_t num_this_pass = max(0, int(size * garbage_collect_states_rate)); if (num_this_pass <= 0) { return 0; } + + bool break_and_uniquify = (auto_break_cycles && uniquify_transforms); + + size_t si = _garbage_index; + if (si >= size) { + si = 0; + } + num_this_pass = min(num_this_pass, size); - int stop_at_element = (_garbage_index + num_this_pass) % size; + size_t stop_at_element = (si + num_this_pass) % size; - int num_elements = 0; - int si = _garbage_index; do { - if (_states->has_element(si)) { - ++num_elements; - TransformState *state = (TransformState *)_states->get_key(si); - if (auto_break_cycles && uniquify_transforms) { - if (state->get_cache_ref_count() > 0 && - state->get_ref_count() == state->get_cache_ref_count()) { - // If we have removed all the references to this state not in the - // cache, leaving only references in the cache, then we need to - // check for a cycle involving this TransformState and break it if - // it exists. - state->detect_and_break_cycles(); - } + TransformState *state = (TransformState *)_states->get_key(si); + if (break_and_uniquify) { + if (state->get_cache_ref_count() > 0 && + state->get_ref_count() == state->get_cache_ref_count()) { + // If we have removed all the references to this state not in the + // cache, leaving only references in the cache, then we need to + // check for a cycle involving this TransformState and break it if + // it exists. + state->detect_and_break_cycles(); } + } - if (state->get_ref_count() == 1) { - // This state has recently been unreffed to 1 (the one we added when - // we stored it in the cache). Now it's time to delete it. This is - // safe, because we're holding the _states_lock, so it's not possible - // for some other thread to find the state in the cache and ref it - // while we're doing this. - state->release_new(); - state->remove_cache_pointers(); - state->cache_unref(); - delete state; - } + if (state->get_ref_count() == 1) { + // This state has recently been unreffed to 1 (the one we added when + // we stored it in the cache). Now it's time to delete it. This is + // safe, because we're holding the _states_lock, so it's not possible + // for some other thread to find the state in the cache and ref it + // while we're doing this. + state->release_new(); + state->remove_cache_pointers(); + state->cache_unref(); + delete state; + + // When we removed it from the hash map, it swapped the last element + // with the one we just removed. So the current index contains one we + // still need to visit. + --size; + --si; } si = (si + 1) % size; } while (si != stop_at_element); _garbage_index = si; - nassertr(_states->validate(), 0); - int new_size = _states->get_num_entries(); - return orig_size - new_size; + nassertr(_states->get_num_entries() == size, 0); + +#ifdef _DEBUG + nassertr(_states->validate(), 0); +#endif + + // If we just cleaned up a lot of states, see if we can reduce the table in + // size. This will help reduce iteration overhead in the future. + _states->consider_shrink_table(); + + return (int)orig_size - (int)size; } /** @@ -1193,11 +1273,8 @@ list_cycles(ostream &out) { VisitedStates visited; CompositionCycleDesc cycle_desc; - int size = _states->get_size(); - for (int si = 0; si < size; ++si) { - if (!_states->has_element(si)) { - continue; - } + size_t size = _states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { const TransformState *state = _states->get_key(si); bool inserted = visited.insert(state).second; @@ -1270,13 +1347,9 @@ list_states(ostream &out) { } LightReMutexHolder holder(*_states_lock); - out << _states->get_num_entries() << " states:\n"; - - int size = _states->get_size(); - for (int si = 0; si < size; ++si) { - if (!_states->has_element(si)) { - continue; - } + size_t size = _states->get_num_entries(); + out << size << " states:\n"; + for (size_t si = 0; si < size; ++si) { const TransformState *state = _states->get_key(si); state->write(out, 2); } @@ -1307,18 +1380,12 @@ validate_states() { return false; } - int size = _states->get_size(); - int si = 0; - while (si < size && !_states->has_element(si)) { - ++si; - } + size_t size = _states->get_num_entries(); + size_t si = 0; nassertr(si < size, false); nassertr(_states->get_key(si)->get_ref_count() >= 0, false); - int snext = si; + size_t snext = si; ++snext; - while (snext < size && !_states->has_element(snext)) { - ++snext; - } while (snext < size) { nassertr(_states->get_key(snext)->get_ref_count() >= 0, false); const TransformState *ssi = _states->get_key(si); @@ -1341,9 +1408,6 @@ validate_states() { } si = snext; ++snext; - while (snext < size && !_states->has_element(snext)) { - ++snext; - } } return true; @@ -1448,7 +1512,7 @@ return_unique(TransformState *state) { // deleted while it's in it. state->cache_ref(); } - si = _states->store(state, Empty()); + si = _states->store(state, nullptr); // Save the index and return the input state. state->_saved_entry = si; @@ -1533,150 +1597,6 @@ do_compose(const TransformState *other) const { } } -/** - * Stores the result of a composition in the cache. Returns the stored result - * (it may be a different object than the one passed in, due to another thread - * having computed the composition first). - */ -CPT(TransformState) TransformState:: -store_compose(const TransformState *other, const TransformState *result) { - // Identity should have already been screened. - nassertr(!is_identity(), other); - nassertr(!other->is_identity(), this); - - // So should have validity. - nassertr(!is_invalid(), this); - nassertr(!other->is_invalid(), other); - - LightReMutexHolder holder(*_states_lock); - - // Is this composition already cached? - int index = _composition_cache.find(other); - if (index != -1) { - Composition &comp = _composition_cache.modify_data(index); - if (comp._result == (const TransformState *)NULL) { - // Well, it wasn't cached already, but we already had an entry (probably - // created for the reverse direction), so use the same entry to store - // the new result. - comp._result = result; - - if (result != (const TransformState *)this) { - // See the comments below about the need to up the reference count - // only when the result is not the same as this. - result->cache_ref(); - } - } - // Here's the cache! - _cache_stats.inc_hits(); - return comp._result; - } - _cache_stats.inc_misses(); - - // We need to make a new cache entry, both in this object and in the other - // object. We make both records so the other TransformState object will - // know to delete the entry from this object when it destructs, and vice- - // versa. - - // The cache entry in this object is the only one that indicates the result; - // the other will be NULL for now. - _cache_stats.add_total_size(1); - _cache_stats.inc_adds(_composition_cache.get_size() == 0); - - _composition_cache[other]._result = result; - - if (other != this) { - _cache_stats.add_total_size(1); - _cache_stats.inc_adds(other->_composition_cache.get_size() == 0); - ((TransformState *)other)->_composition_cache[this]._result = NULL; - } - - if (result != (TransformState *)this) { - // If the result of do_compose() is something other than this, explicitly - // increment the reference count. We have to be sure to decrement it - // again later, when the composition entry is removed from the cache. - result->cache_ref(); - - // (If the result was just this again, we still store the result, but we - // don't increment the reference count, since that would be a self- - // referential leak.) - } - - _cache_stats.maybe_report("TransformState"); - - return result; -} - -/** - * Stores the result of a composition in the cache. Returns the stored result - * (it may be a different object than the one passed in, due to another thread - * having computed the composition first). - */ -CPT(TransformState) TransformState:: -store_invert_compose(const TransformState *other, const TransformState *result) { - // Identity should have already been screened. - nassertr(!is_identity(), other); - - // So should have validity. - nassertr(!is_invalid(), this); - nassertr(!other->is_invalid(), other); - - nassertr(other != this, make_identity()); - - LightReMutexHolder holder(*_states_lock); - - // Is this composition already cached? - int index = _invert_composition_cache.find(other); - if (index != -1) { - Composition &comp = ((TransformState *)this)->_invert_composition_cache.modify_data(index); - if (comp._result == (const TransformState *)NULL) { - // Well, it wasn't cached already, but we already had an entry (probably - // created for the reverse direction), so use the same entry to store - // the new result. - comp._result = result; - - if (result != (const TransformState *)this) { - // See the comments below about the need to up the reference count - // only when the result is not the same as this. - result->cache_ref(); - } - } - // Here's the cache! - _cache_stats.inc_hits(); - return comp._result; - } - _cache_stats.inc_misses(); - - // We need to make a new cache entry, both in this object and in the other - // object. We make both records so the other TransformState object will - // know to delete the entry from this object when it destructs, and vice- - // versa. - - // The cache entry in this object is the only one that indicates the result; - // the other will be NULL for now. - _cache_stats.add_total_size(1); - _cache_stats.inc_adds(_invert_composition_cache.get_size() == 0); - _invert_composition_cache[other]._result = result; - - if (other != this) { - _cache_stats.add_total_size(1); - _cache_stats.inc_adds(other->_invert_composition_cache.get_size() == 0); - ((TransformState *)other)->_invert_composition_cache[this]._result = NULL; - } - - if (result != (TransformState *)this) { - // If the result of compose() is something other than this, explicitly - // increment the reference count. We have to be sure to decrement it - // again later, when the composition entry is removed from the cache. - result->cache_ref(); - - // (If the result was just this again, we still store the result, but we - // don't increment the reference count, since that would be a self- - // referential leak.) - } - - return result; -} - /** * The private implemention of invert_compose(). */ @@ -1857,41 +1777,37 @@ r_detect_cycles(const TransformState *start_state, } ((TransformState *)current_state)->_cycle_detect = this_seq; - int i; - int cache_size = current_state->_composition_cache.get_size(); + size_t i; + size_t cache_size = current_state->_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (current_state->_composition_cache.has_element(i)) { - const TransformState *result = current_state->_composition_cache.get_data(i)._result; - if (result != (const TransformState *)NULL) { - if (r_detect_cycles(start_state, result, length + 1, - this_seq, cycle_desc)) { - // Cycle detected. - if (cycle_desc != (CompositionCycleDesc *)NULL) { - const TransformState *other = current_state->_composition_cache.get_key(i); - CompositionCycleDescEntry entry(other, result, false); - cycle_desc->push_back(entry); - } - return true; + const TransformState *result = current_state->_composition_cache.get_data(i)._result; + if (result != (const TransformState *)NULL) { + if (r_detect_cycles(start_state, result, length + 1, + this_seq, cycle_desc)) { + // Cycle detected. + if (cycle_desc != (CompositionCycleDesc *)NULL) { + const TransformState *other = current_state->_composition_cache.get_key(i); + CompositionCycleDescEntry entry(other, result, false); + cycle_desc->push_back(entry); } + return true; } } } - cache_size = current_state->_invert_composition_cache.get_size(); + cache_size = current_state->_invert_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (current_state->_invert_composition_cache.has_element(i)) { - const TransformState *result = current_state->_invert_composition_cache.get_data(i)._result; - if (result != (const TransformState *)NULL) { - if (r_detect_cycles(start_state, result, length + 1, - this_seq, cycle_desc)) { - // Cycle detected. - if (cycle_desc != (CompositionCycleDesc *)NULL) { - const TransformState *other = current_state->_invert_composition_cache.get_key(i); - CompositionCycleDescEntry entry(other, result, true); - cycle_desc->push_back(entry); - } - return true; + const TransformState *result = current_state->_invert_composition_cache.get_data(i)._result; + if (result != (const TransformState *)NULL) { + if (r_detect_cycles(start_state, result, length + 1, + this_seq, cycle_desc)) { + // Cycle detected. + if (cycle_desc != (CompositionCycleDesc *)NULL) { + const TransformState *other = current_state->_invert_composition_cache.get_key(i); + CompositionCycleDescEntry entry(other, result, true); + cycle_desc->push_back(entry); } + return true; } } } @@ -1920,52 +1836,48 @@ r_detect_reverse_cycles(const TransformState *start_state, } ((TransformState *)current_state)->_cycle_detect = this_seq; - int i; - int cache_size = current_state->_composition_cache.get_size(); + size_t i; + size_t cache_size = current_state->_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (current_state->_composition_cache.has_element(i)) { - const TransformState *other = current_state->_composition_cache.get_key(i); - if (other != current_state) { - int oi = other->_composition_cache.find(current_state); - nassertr(oi != -1, false); + const TransformState *other = current_state->_composition_cache.get_key(i); + if (other != current_state) { + int oi = other->_composition_cache.find(current_state); + nassertr(oi != -1, false); - const TransformState *result = other->_composition_cache.get_data(oi)._result; - if (result != (const TransformState *)NULL) { - if (r_detect_reverse_cycles(start_state, result, length + 1, - this_seq, cycle_desc)) { - // Cycle detected. - if (cycle_desc != (CompositionCycleDesc *)NULL) { - const TransformState *other = current_state->_composition_cache.get_key(i); - CompositionCycleDescEntry entry(other, result, false); - cycle_desc->push_back(entry); - } - return true; + const TransformState *result = other->_composition_cache.get_data(oi)._result; + if (result != (const TransformState *)NULL) { + if (r_detect_reverse_cycles(start_state, result, length + 1, + this_seq, cycle_desc)) { + // Cycle detected. + if (cycle_desc != (CompositionCycleDesc *)NULL) { + const TransformState *other = current_state->_composition_cache.get_key(i); + CompositionCycleDescEntry entry(other, result, false); + cycle_desc->push_back(entry); } + return true; } } } } - cache_size = current_state->_invert_composition_cache.get_size(); + cache_size = current_state->_invert_composition_cache.get_num_entries(); for (i = 0; i < cache_size; ++i) { - if (current_state->_invert_composition_cache.has_element(i)) { - const TransformState *other = current_state->_invert_composition_cache.get_key(i); - if (other != current_state) { - int oi = other->_invert_composition_cache.find(current_state); - nassertr(oi != -1, false); + const TransformState *other = current_state->_invert_composition_cache.get_key(i); + if (other != current_state) { + int oi = other->_invert_composition_cache.find(current_state); + nassertr(oi != -1, false); - const TransformState *result = other->_invert_composition_cache.get_data(oi)._result; - if (result != (const TransformState *)NULL) { - if (r_detect_reverse_cycles(start_state, result, length + 1, - this_seq, cycle_desc)) { - // Cycle detected. - if (cycle_desc != (CompositionCycleDesc *)NULL) { - const TransformState *other = current_state->_invert_composition_cache.get_key(i); - CompositionCycleDescEntry entry(other, result, false); - cycle_desc->push_back(entry); - } - return true; + const TransformState *result = other->_invert_composition_cache.get_data(oi)._result; + if (result != (const TransformState *)NULL) { + if (r_detect_reverse_cycles(start_state, result, length + 1, + this_seq, cycle_desc)) { + // Cycle detected. + if (cycle_desc != (CompositionCycleDesc *)NULL) { + const TransformState *other = current_state->_invert_composition_cache.get_key(i); + CompositionCycleDescEntry entry(other, result, false); + cycle_desc->push_back(entry); } + return true; } } } @@ -1987,10 +1899,8 @@ release_new() { nassertv(_states_lock->debug_is_locked()); if (_saved_entry != -1) { - // nassertv(_states->find(this) == _saved_entry); - _saved_entry = _states->find(this); - _states->remove_element(_saved_entry); _saved_entry = -1; + nassertv_always(_states->remove(this)); } } @@ -2029,13 +1939,8 @@ remove_cache_pointers() { // There are lots of ways to do this loop wrong. Be very careful if you // need to modify it for any reason. - int i = 0; + size_t i = 0; while (!_composition_cache.is_empty()) { - // Scan for the next used slot in the table. - while (!_composition_cache.has_element(i)) { - ++i; - } - // It is possible that the "other" TransformState object is currently // within its own destructor. We therefore can't use a PT() to hold its // pointer; that could end up calling its destructor twice. Fortunately, @@ -2088,10 +1993,6 @@ remove_cache_pointers() { // A similar bit of code for the invert cache. i = 0; while (!_invert_composition_cache.is_empty()) { - while (!_invert_composition_cache.has_element(i)) { - ++i; - } - TransformState *other = (TransformState *)_invert_composition_cache.get_key(i); nassertv(other != this); Composition comp = _invert_composition_cache.get_data(i); diff --git a/panda/src/pgraph/transformState.h b/panda/src/pgraph/transformState.h index 3287fb1853..b770e7bf52 100644 --- a/panda/src/pgraph/transformState.h +++ b/panda/src/pgraph/transformState.h @@ -234,9 +234,7 @@ private: static CPT(TransformState) return_unique(TransformState *state); CPT(TransformState) do_compose(const TransformState *other) const; - CPT(TransformState) store_compose(const TransformState *other, const TransformState *result); CPT(TransformState) do_invert_compose(const TransformState *other) const; - CPT(TransformState) store_invert_compose(const TransformState *other, const TransformState *result); void detect_and_break_cycles(); static bool r_detect_cycles(const TransformState *start_state, const TransformState *current_state, @@ -255,9 +253,7 @@ private: // cache, which is encoded in _composition_cache and // _invert_composition_cache. static LightReMutex *_states_lock; - class Empty { - }; - typedef SimpleHashMap > States; + typedef SimpleHashMap > States; static States *_states; static CPT(TransformState) _identity_state; static CPT(TransformState) _invalid_state; @@ -288,8 +284,8 @@ private: }; typedef SimpleHashMap CompositionCache; - CompositionCache _composition_cache; - CompositionCache _invert_composition_cache; + mutable CompositionCache _composition_cache; + mutable CompositionCache _invert_composition_cache; // This is used to mark nodes as we visit them to detect cycles. UpdateSeq _cycle_detect; @@ -297,7 +293,7 @@ private: // This keeps track of our current position through the garbage collection // cycle. - static int _garbage_index; + static size_t _garbage_index; static bool _uniquify_matrix; @@ -408,6 +404,10 @@ private: friend class Extension; }; +// We can safely redefine this as a no-op. +template<> +INLINE void PointerToBase::update_type(To *ptr) {} + INLINE ostream &operator << (ostream &out, const TransformState &state) { state.output(out); return out; diff --git a/panda/src/pgraph/transformState_ext.cxx b/panda/src/pgraph/transformState_ext.cxx index e037cd2a16..44ddfd71f8 100644 --- a/panda/src/pgraph/transformState_ext.cxx +++ b/panda/src/pgraph/transformState_ext.cxx @@ -35,12 +35,8 @@ get_composition_cache() const { PyObject *list = PyList_New(num_states); size_t i = 0; - int size = _this->_composition_cache.get_size(); - for (int si = 0; si < size; ++si) { - if (!_this->_composition_cache.has_element(si)) { - continue; - } - + size_t size = _this->_composition_cache.get_num_entries(); + for (size_t si = 0; si < size; ++si) { PyObject *tuple = PyTuple_New(2); PyObject *a, *b; @@ -94,12 +90,8 @@ get_invert_composition_cache() const { PyObject *list = PyList_New(num_states); size_t i = 0; - int size = _this->_invert_composition_cache.get_size(); - for (int si = 0; si < size; ++si) { - if (!_this->_invert_composition_cache.has_element(si)) { - continue; - } - + size_t size = _this->_invert_composition_cache.get_num_entries(); + for (size_t si = 0; si < size; ++si) { PyObject *tuple = PyTuple_New(2); PyObject *a, *b; @@ -149,11 +141,8 @@ get_states() { PyObject *list = PyList_New(num_states); size_t i = 0; - int size = TransformState::_states->get_size(); - for (int si = 0; si < size; ++si) { - if (!TransformState::_states->has_element(si)) { - continue; - } + size_t size = TransformState::_states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { const TransformState *state = TransformState::_states->get_key(si); state->ref(); PyObject *a = @@ -180,11 +169,8 @@ get_unused_states() { LightReMutexHolder holder(*TransformState::_states_lock); PyObject *list = PyList_New(0); - int size = TransformState::_states->get_size(); - for (int si = 0; si < size; ++si) { - if (!TransformState::_states->has_element(si)) { - continue; - } + size_t size = TransformState::_states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { const TransformState *state = TransformState::_states->get_key(si); if (state->get_cache_ref_count() == state->get_ref_count()) { state->ref(); diff --git a/panda/src/pgraph/transparencyAttrib.cxx b/panda/src/pgraph/transparencyAttrib.cxx index 4dd7d0a1c2..ad105f3214 100644 --- a/panda/src/pgraph/transparencyAttrib.cxx +++ b/panda/src/pgraph/transparencyAttrib.cxx @@ -109,14 +109,6 @@ get_hash_impl() const { return hash; } -/** - * - */ -CPT(RenderAttrib) TransparencyAttrib:: -get_auto_shader_attrib_impl(const RenderState *state) const { - return this; -} - /** * Tells the BamReader how to create objects of type TransparencyAttrib. */ diff --git a/panda/src/pgraph/transparencyAttrib.h b/panda/src/pgraph/transparencyAttrib.h index ebd7f4f157..75af58c64e 100644 --- a/panda/src/pgraph/transparencyAttrib.h +++ b/panda/src/pgraph/transparencyAttrib.h @@ -59,7 +59,6 @@ public: protected: virtual int compare_to_impl(const RenderAttrib *other) const; virtual size_t get_hash_impl() const; - virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const; private: Mode _mode; @@ -71,6 +70,7 @@ PUBLISHED: virtual int get_slot() const { return get_class_slot(); } + MAKE_PROPERTY(class_slot, get_class_slot); public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/workingNodePath.I b/panda/src/pgraph/workingNodePath.I index 83010f2009..b74921827b 100644 --- a/panda/src/pgraph/workingNodePath.I +++ b/panda/src/pgraph/workingNodePath.I @@ -43,10 +43,10 @@ WorkingNodePath(const WorkingNodePath ©) : * traversal to the next node. */ INLINE WorkingNodePath:: -WorkingNodePath(const WorkingNodePath &parent, PandaNode *child) { - _next = &parent; - _start = (NodePathComponent *)NULL; - _node = child; +WorkingNodePath(const WorkingNodePath &parent, PandaNode *child) : + _next(&parent), + _start(nullptr), + _node(child) { nassertv(_node != _next->_node); } diff --git a/panda/src/pgraphnodes/ambientLight.h b/panda/src/pgraphnodes/ambientLight.h index e52122bdde..f3582e54fd 100644 --- a/panda/src/pgraphnodes/ambientLight.h +++ b/panda/src/pgraphnodes/ambientLight.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDA_PGRAPHNODES AmbientLight : public LightNode { PUBLISHED: - AmbientLight(const string &name); + explicit AmbientLight(const string &name); protected: AmbientLight(const AmbientLight ©); @@ -33,7 +33,7 @@ protected: public: virtual PandaNode *make_copy() const; virtual void write(ostream &out, int indent_level) const; - virtual bool is_ambient_light() const; + virtual bool is_ambient_light() const FINAL; PUBLISHED: virtual int get_class_priority() const; diff --git a/panda/src/pgraphnodes/callbackNode.h b/panda/src/pgraphnodes/callbackNode.h index 22f1f0f00b..ba2baec389 100644 --- a/panda/src/pgraphnodes/callbackNode.h +++ b/panda/src/pgraphnodes/callbackNode.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDA_PGRAPHNODES CallbackNode : public PandaNode { PUBLISHED: - CallbackNode(const string &name); + explicit CallbackNode(const string &name); INLINE void set_cull_callback(CallbackObject *object); INLINE void clear_cull_callback(); diff --git a/panda/src/pgraphnodes/computeNode.I b/panda/src/pgraphnodes/computeNode.I index 06e022515e..677a0f406f 100644 --- a/panda/src/pgraphnodes/computeNode.I +++ b/panda/src/pgraphnodes/computeNode.I @@ -71,6 +71,21 @@ set_dispatch(size_t n, const LVecBase3i &dispatch) { cdata->_dispatches[n] = dispatch; } +/** + * Inserts a dispatch command with the given number of work groups in the X, + * Y, and Z dimensions at the given position in the list of dispatch commands. + * Any of these values may be set to 1 if the respective dimension should not + * be used. + */ +INLINE void ComputeNode:: +insert_dispatch(size_t n, const LVecBase3i &dispatch) { + Dispatcher::CDWriter cdata(_dispatcher->_cycler); + if (n > cdata->_dispatches.size()) { + n = cdata->_dispatches.size(); + } + cdata->_dispatches.insert(cdata->_dispatches.begin(), dispatch); +} + /** * Erases the given dispatch index from the list. */ diff --git a/panda/src/pgraphnodes/computeNode.h b/panda/src/pgraphnodes/computeNode.h index 3210fba40f..83f7297216 100644 --- a/panda/src/pgraphnodes/computeNode.h +++ b/panda/src/pgraphnodes/computeNode.h @@ -26,7 +26,7 @@ */ class EXPCL_PANDA_PGRAPHNODES ComputeNode : public PandaNode { PUBLISHED: - ComputeNode(const string &name); + explicit ComputeNode(const string &name); INLINE void add_dispatch(const LVecBase3i &num_groups); INLINE void add_dispatch(int num_groups_x, int num_groups_y, int num_groups_z); @@ -34,11 +34,12 @@ PUBLISHED: INLINE size_t get_num_dispatches() const; INLINE const LVecBase3i &get_dispatch(size_t i) const; INLINE void set_dispatch(size_t i, const LVecBase3i &num_groups); + INLINE void insert_dispatch(size_t i, const LVecBase3i &num_groups); INLINE void remove_dispatch(size_t i); INLINE void clear_dispatches(); MAKE_SEQ(get_dispatches, get_num_dispatches, get_dispatch); - MAKE_SEQ_PROPERTY(dispatches, get_num_dispatches, get_dispatch, set_dispatch, remove_dispatch); + MAKE_SEQ_PROPERTY(dispatches, get_num_dispatches, get_dispatch, set_dispatch, remove_dispatch, insert_dispatch); public: ComputeNode(const ComputeNode ©); diff --git a/panda/src/pgraphnodes/directionalLight.cxx b/panda/src/pgraphnodes/directionalLight.cxx index 127f994fbb..9d198fac6f 100644 --- a/panda/src/pgraphnodes/directionalLight.cxx +++ b/panda/src/pgraphnodes/directionalLight.cxx @@ -56,9 +56,8 @@ fillin(DatagramIterator &scan, BamReader *) { */ DirectionalLight:: DirectionalLight(const string &name) : - LightLensNode(name, new OrthographicLens()), - _has_specular_color(false) -{ + LightLensNode(name, new OrthographicLens()) { + _lenses[0]._lens->set_interocular_distance(0); } /** @@ -68,7 +67,6 @@ DirectionalLight(const string &name) : DirectionalLight:: DirectionalLight(const DirectionalLight ©) : LightLensNode(copy), - _has_specular_color(copy._has_specular_color), _cycler(copy._cycler) { } diff --git a/panda/src/pgraphnodes/directionalLight.h b/panda/src/pgraphnodes/directionalLight.h index 5f0404b9a8..df2a198273 100644 --- a/panda/src/pgraphnodes/directionalLight.h +++ b/panda/src/pgraphnodes/directionalLight.h @@ -24,7 +24,7 @@ */ class EXPCL_PANDA_PGRAPHNODES DirectionalLight : public LightLensNode { PUBLISHED: - DirectionalLight(const string &name); + explicit DirectionalLight(const string &name); protected: DirectionalLight(const DirectionalLight ©); @@ -59,8 +59,6 @@ public: int light_id); private: - bool _has_specular_color; - // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA_PGRAPHNODES CData : public CycleData { public: diff --git a/panda/src/pgraphnodes/fadeLodNode.cxx b/panda/src/pgraphnodes/fadeLodNode.cxx index 5f1f8d15ec..de952457e8 100644 --- a/panda/src/pgraphnodes/fadeLodNode.cxx +++ b/panda/src/pgraphnodes/fadeLodNode.cxx @@ -93,7 +93,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { consider_verify_lods(trav, data); Camera *camera = trav->get_scene()->get_camera_node(); - NodePath this_np = data._node_path.get_node_path(); + NodePath this_np = data.get_node_path(); FadeLODNodeData *ldata = DCAST(FadeLODNodeData, camera->get_aux_scene_data(this_np)); diff --git a/panda/src/pgraphnodes/fadeLodNode.h b/panda/src/pgraphnodes/fadeLodNode.h index 79930a39b4..728cc8f8e6 100644 --- a/panda/src/pgraphnodes/fadeLodNode.h +++ b/panda/src/pgraphnodes/fadeLodNode.h @@ -23,7 +23,7 @@ */ class EXPCL_PANDA_PGRAPHNODES FadeLODNode : public LODNode { PUBLISHED: - FadeLODNode(const string &name); + explicit FadeLODNode(const string &name); protected: FadeLODNode(const FadeLODNode ©); diff --git a/panda/src/pgraphnodes/lightLensNode.I b/panda/src/pgraphnodes/lightLensNode.I index ddb2808e86..eb2b192f97 100644 --- a/panda/src/pgraphnodes/lightLensNode.I +++ b/panda/src/pgraphnodes/lightLensNode.I @@ -11,11 +11,20 @@ * @date 2002-03-26 */ +/** + * Returns true if this light defines a specular color, false if the specular + * color is derived automatically from the light color. + */ +INLINE bool LightLensNode:: +has_specular_color() const { + return _has_specular_color; +} + /** * Returns whether this light is configured to cast shadows or not. */ INLINE bool LightLensNode:: -is_shadow_caster() { +is_shadow_caster() const { return _shadow_caster; } diff --git a/panda/src/pgraphnodes/lightLensNode.cxx b/panda/src/pgraphnodes/lightLensNode.cxx index bc3e134116..f68b7084ec 100644 --- a/panda/src/pgraphnodes/lightLensNode.cxx +++ b/panda/src/pgraphnodes/lightLensNode.cxx @@ -27,7 +27,9 @@ TypeHandle LightLensNode::_type_handle; */ LightLensNode:: LightLensNode(const string &name, Lens *lens) : - Camera(name, lens) + Camera(name, lens), + _has_specular_color(false), + _attrib_count(0) { set_active(false); _shadow_caster = false; @@ -46,6 +48,10 @@ LightLensNode:: ~LightLensNode() { set_active(false); clear_shadow_buffers(); + + // If this triggers, the number of attrib_ref() didn't match the number of + // attrib_unref() calls, probably indicating a bug in LightAttrib. + nassertv(AtomicAdjust::get(_attrib_count) == 0); } /** @@ -57,7 +63,9 @@ LightLensNode(const LightLensNode ©) : Camera(copy), _shadow_caster(copy._shadow_caster), _sb_size(copy._sb_size), - _sb_sort(-10) + _sb_sort(-10), + _has_specular_color(copy._has_specular_color), + _attrib_count(0) { } @@ -81,6 +89,28 @@ clear_shadow_buffers() { _sbuffers.clear(); } + +/** + * This is called when the light is added to a LightAttrib. + */ +void LightLensNode:: +attrib_ref() { + AtomicAdjust::inc(_attrib_count); +} + +/** + * This is called when the light is removed from a LightAttrib. + */ +void LightLensNode:: +attrib_unref() { + // When it is removed from the last LightAttrib, destroy the shadow buffers. + // This is necessary to break the circular reference that the buffer holds + // on this node, via the display region's camera. + if (!AtomicAdjust::dec(_attrib_count)) { + clear_shadow_buffers(); + } +} + /** * Returns the Light object upcast to a PandaNode. */ diff --git a/panda/src/pgraphnodes/lightLensNode.h b/panda/src/pgraphnodes/lightLensNode.h index d4e0c7385d..2102acc541 100644 --- a/panda/src/pgraphnodes/lightLensNode.h +++ b/panda/src/pgraphnodes/lightLensNode.h @@ -20,6 +20,7 @@ #include "camera.h" #include "graphicsStateGuardianBase.h" #include "graphicsOutputBase.h" +#include "atomicAdjust.h" class ShaderGenerator; class GraphicsStateGuardian; @@ -31,10 +32,12 @@ class GraphicsStateGuardian; */ class EXPCL_PANDA_PGRAPHNODES LightLensNode : public Light, public Camera { PUBLISHED: - LightLensNode(const string &name, Lens *lens = new PerspectiveLens()); + explicit LightLensNode(const string &name, Lens *lens = new PerspectiveLens()); virtual ~LightLensNode(); - INLINE bool is_shadow_caster(); + INLINE bool has_specular_color() const; + + INLINE bool is_shadow_caster() const; INLINE void set_shadow_caster(bool caster); INLINE void set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int sort = -10); @@ -53,13 +56,21 @@ protected: LVecBase2i _sb_size; bool _shadow_caster; + bool _has_specular_color; int _sb_sort; // This is really a map of GSG -> GraphicsOutput. typedef pmap ShadowBuffers; ShadowBuffers _sbuffers; + // This counts how many LightAttribs in the world are referencing this + // LightLensNode object. + AtomicAdjust::Integer _attrib_count; + public: + virtual void attrib_ref(); + virtual void attrib_unref(); + virtual PandaNode *as_node(); virtual Light *as_light(); diff --git a/panda/src/pgraphnodes/lightNode.h b/panda/src/pgraphnodes/lightNode.h index 14ebbd378a..9e9187ebe0 100644 --- a/panda/src/pgraphnodes/lightNode.h +++ b/panda/src/pgraphnodes/lightNode.h @@ -26,7 +26,7 @@ */ class EXPCL_PANDA_PGRAPHNODES LightNode : public Light, public PandaNode { PUBLISHED: - LightNode(const string &name); + explicit LightNode(const string &name); protected: LightNode(const LightNode ©); diff --git a/panda/src/pgraphnodes/lodNode.cxx b/panda/src/pgraphnodes/lodNode.cxx index 5936b0634b..4c2ca87528 100644 --- a/panda/src/pgraphnodes/lodNode.cxx +++ b/panda/src/pgraphnodes/lodNode.cxx @@ -330,7 +330,7 @@ compute_child(CullTraverser *trav, CullTraverserData &data) { * trav->get_scene()->get_camera_node()->get_lod_scale())) { if (pgraph_cat.is_debug()) { pgraph_cat.debug() - << data._node_path << " at distance " << sqrt(dist2) + << data.get_node_path() << " at distance " << sqrt(dist2) << ", selected child " << index << "\n"; } @@ -340,7 +340,7 @@ compute_child(CullTraverser *trav, CullTraverserData &data) { if (pgraph_cat.is_debug()) { pgraph_cat.debug() - << data._node_path << " at distance " << sqrt(dist2) + << data.get_node_path() << " at distance " << sqrt(dist2) << ", no children in range.\n"; } @@ -393,20 +393,14 @@ show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data) { // And draw the spindle in this color. CullTraverserData next_data2(data, sw.get_spindle_viz()); - next_data2.apply_transform_and_state(trav, viz_transform, - RenderState::make_empty(), - RenderEffects::make_empty(), - ClipPlaneAttrib::make()); + next_data2.apply_transform(viz_transform); trav->traverse(next_data2); } // Draw the rings for this switch level. We do this after we have drawn // the geometry and the spindle. CullTraverserData next_data(data, sw.get_ring_viz()); - next_data.apply_transform_and_state(trav, viz_transform, - RenderState::make_empty(), - RenderEffects::make_empty(), - ClipPlaneAttrib::make()); + next_data.apply_transform(viz_transform); trav->traverse(next_data); } } @@ -650,7 +644,7 @@ do_auto_verify_lods(CullTraverser *trav, CullTraverserData &data) { const Switch &sw = cdata->_switch_vector[index]; ostringstream strm; strm - << "Level " << index << " geometry of " << data._node_path + << "Level " << index << " geometry of " << data.get_node_path() << " is larger than its switch radius; suggest radius of " << suggested_radius << " instead of " << sw.get_in() << " (configure verify-lods 0 to ignore this error)"; diff --git a/panda/src/pgraphnodes/lodNode.h b/panda/src/pgraphnodes/lodNode.h index 6348b4c23a..7258ebe447 100644 --- a/panda/src/pgraphnodes/lodNode.h +++ b/panda/src/pgraphnodes/lodNode.h @@ -27,7 +27,7 @@ */ class EXPCL_PANDA_PGRAPHNODES LODNode : public PandaNode { PUBLISHED: - INLINE LODNode(const string &name); + INLINE explicit LODNode(const string &name); static PT(LODNode) make_default_lod(const string &name); diff --git a/panda/src/pgraphnodes/nodeCullCallbackData.cxx b/panda/src/pgraphnodes/nodeCullCallbackData.cxx index d0bf763e98..b0cbd9d980 100644 --- a/panda/src/pgraphnodes/nodeCullCallbackData.cxx +++ b/panda/src/pgraphnodes/nodeCullCallbackData.cxx @@ -41,7 +41,7 @@ void NodeCullCallbackData:: upcall() { PandaNode *node = _data.node(); if (node->is_of_type(CallbackNode::get_class_type())) { - CallbackNode *cbnode = DCAST(CallbackNode, _data.node()); + CallbackNode *cbnode = (CallbackNode *)node; // OK, render this node. Rendering a CallbackNode means creating a // CullableObject for the draw_callback, if any. We don't need to pass diff --git a/panda/src/pgraphnodes/pointLight.cxx b/panda/src/pgraphnodes/pointLight.cxx index 564116ecac..caa8d86261 100644 --- a/panda/src/pgraphnodes/pointLight.cxx +++ b/panda/src/pgraphnodes/pointLight.cxx @@ -61,26 +61,30 @@ fillin(DatagramIterator &scan, BamReader *manager) { */ PointLight:: PointLight(const string &name) : - LightLensNode(name), - _has_specular_color(false) -{ + LightLensNode(name) { PT(Lens) lens; lens = new PerspectiveLens(90, 90); + lens->set_interocular_distance(0); lens->set_view_vector(1, 0, 0, 0, -1, 0); set_lens(0, lens); lens = new PerspectiveLens(90, 90); + lens->set_interocular_distance(0); lens->set_view_vector(-1, 0, 0, 0, -1, 0); set_lens(1, lens); lens = new PerspectiveLens(90, 90); + lens->set_interocular_distance(0); lens->set_view_vector(0, 1, 0, 0, 0, 1); set_lens(2, lens); lens = new PerspectiveLens(90, 90); + lens->set_interocular_distance(0); lens->set_view_vector(0, -1, 0, 0, 0, -1); set_lens(3, lens); lens = new PerspectiveLens(90, 90); + lens->set_interocular_distance(0); lens->set_view_vector(0, 0, 1, 0, -1, 0); set_lens(4, lens); lens = new PerspectiveLens(90, 90); + lens->set_interocular_distance(0); lens->set_view_vector(0, 0, -1, 0, -1, 0); set_lens(5, lens); } @@ -92,7 +96,6 @@ PointLight(const string &name) : PointLight:: PointLight(const PointLight ©) : LightLensNode(copy), - _has_specular_color(copy._has_specular_color), _cycler(copy._cycler) { } diff --git a/panda/src/pgraphnodes/pointLight.h b/panda/src/pgraphnodes/pointLight.h index c0907dba01..cbfcf9b50d 100644 --- a/panda/src/pgraphnodes/pointLight.h +++ b/panda/src/pgraphnodes/pointLight.h @@ -24,7 +24,7 @@ */ class EXPCL_PANDA_PGRAPHNODES PointLight : public LightLensNode { PUBLISHED: - PointLight(const string &name); + explicit PointLight(const string &name); protected: PointLight(const PointLight ©); @@ -63,8 +63,6 @@ public: int light_id); private: - bool _has_specular_color; - // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA_PGRAPHNODES CData : public CycleData { public: diff --git a/panda/src/pgraphnodes/rectangleLight.h b/panda/src/pgraphnodes/rectangleLight.h index 00d4d586ea..3870b4f8f7 100644 --- a/panda/src/pgraphnodes/rectangleLight.h +++ b/panda/src/pgraphnodes/rectangleLight.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDA_PGRAPHNODES RectangleLight : public LightLensNode { PUBLISHED: - RectangleLight(const string &name); + explicit RectangleLight(const string &name); protected: RectangleLight(const RectangleLight ©); diff --git a/panda/src/pgraphnodes/selectiveChildNode.h b/panda/src/pgraphnodes/selectiveChildNode.h index 2175c5d13f..76720aa933 100644 --- a/panda/src/pgraphnodes/selectiveChildNode.h +++ b/panda/src/pgraphnodes/selectiveChildNode.h @@ -24,7 +24,7 @@ */ class EXPCL_PANDA_PGRAPHNODES SelectiveChildNode : public PandaNode { PUBLISHED: - INLINE SelectiveChildNode(const string &name); + INLINE explicit SelectiveChildNode(const string &name); protected: INLINE SelectiveChildNode(const SelectiveChildNode ©); diff --git a/panda/src/pgraphnodes/sequenceNode.h b/panda/src/pgraphnodes/sequenceNode.h index 13a7a826fa..ce05b792f2 100644 --- a/panda/src/pgraphnodes/sequenceNode.h +++ b/panda/src/pgraphnodes/sequenceNode.h @@ -26,7 +26,7 @@ */ class EXPCL_PANDA_PGRAPHNODES SequenceNode : public SelectiveChildNode, public AnimInterface { PUBLISHED: - INLINE SequenceNode(const string &name); + INLINE explicit SequenceNode(const string &name); protected: SequenceNode(const SequenceNode ©); diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index 2c840e46ee..6b83c3ef14 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -40,24 +40,35 @@ #include "directionalLight.h" #include "rescaleNormalAttrib.h" #include "pointLight.h" +#include "sphereLight.h" #include "spotlight.h" #include "lightLensNode.h" #include "lvector4.h" #include "config_pgraphnodes.h" +#include "pStatTimer.h" TypeHandle ShaderGenerator::_type_handle; #ifdef HAVE_CG +#define PACK_COMBINE(src0, op0, src1, op1, src2, op2) ( \ + ((uint16_t)src0) | ((((uint16_t)op0 - 1u) & 3u) << 3u) | \ + ((uint16_t)src1 << 5u) | ((((uint16_t)op1 - 1u) & 3u) << 8u) | \ + ((uint16_t)src2 << 10u) | ((((uint16_t)op2 - 1u) & 3u) << 13u)) + +#define UNPACK_COMBINE_SRC(from, n) (TextureStage::CombineSource)((from >> ((uint16_t)n * 5u)) & 7u) +#define UNPACK_COMBINE_OP(from, n) (TextureStage::CombineOperand)(((from >> (((uint16_t)n * 5u) + 3u)) & 3u) + 1u) + +static PStatCollector lookup_collector("*:Munge:ShaderGen:Lookup"); +static PStatCollector synthesize_collector("*:Munge:ShaderGen:Synthesize"); + /** * Create a ShaderGenerator. This has no state, except possibly to cache * certain results. The parameter that must be passed is the GSG to which the * shader generator belongs. */ ShaderGenerator:: -ShaderGenerator(GraphicsStateGuardianBase *gsg, GraphicsOutputBase *host) : - _gsg(gsg), _host(host) { - +ShaderGenerator(const GraphicsStateGuardianBase *gsg) { // The ATTR# input semantics seem to map to generic vertex attributes in // both arbvp1 and glslv, which behave more consistently. However, they // don't exist in Direct3D 9. Use this silly little check for now. @@ -66,6 +77,10 @@ ShaderGenerator(GraphicsStateGuardianBase *gsg, GraphicsOutputBase *host) : #else _use_generic_attr = false; #endif + + // Do we want to use the ARB_shadow extension? This also allows us to use + // hardware shadows PCF. + _use_shadow_filter = gsg->get_supports_shadow_filter(); } /** @@ -159,13 +174,8 @@ alloc_freg() { case 6: _ftregs_used += 1; return "TEXCOORD6"; case 7: _ftregs_used += 1; return "TEXCOORD7"; } -/* - * We really shouldn't rely on COLOR fregs, since the clamping can have - * unexpected side-effects. switch (_fcregs_used) { case 0: _fcregs_used += - * 1; return "COLOR0"; case 1: _fcregs_used += 1; return "COLOR1"; } These - * don't exist in arbvp1arbfp1, though they're reportedly supported by other - * profiles. - */ + // NB. We really shouldn't use the COLOR fregs, since the clamping can have + // unexpected side-effects. switch (_ftregs_used) { case 8: _ftregs_used += 1; return "TEXCOORD8"; case 9: _ftregs_used += 1; return "TEXCOORD9"; @@ -184,342 +194,404 @@ alloc_freg() { * analysis are stored in instance variables of the Shader Generator. */ void ShaderGenerator:: -analyze_renderstate(const RenderState *rs) { - clear_analysis(); +analyze_renderstate(ShaderKey &key, const RenderState *rs) { + const ShaderAttrib *shader_attrib; + rs->get_attrib_def(shader_attrib); + nassertv(shader_attrib->auto_shader()); // verify_enforce_attrib_lock(); - _state = rs; const AuxBitplaneAttrib *aux_bitplane; rs->get_attrib_def(aux_bitplane); - int outputs = aux_bitplane->get_outputs(); + key._outputs = aux_bitplane->get_outputs(); // Decide whether or not we need alpha testing or alpha blending. - + bool have_alpha_test = false; + bool have_alpha_blend = false; const AlphaTestAttrib *alpha_test; rs->get_attrib_def(alpha_test); - if ((alpha_test->get_mode() != RenderAttrib::M_none)&& - (alpha_test->get_mode() != RenderAttrib::M_always)) { - _have_alpha_test = true; + if (alpha_test->get_mode() != RenderAttrib::M_none && + alpha_test->get_mode() != RenderAttrib::M_always) { + have_alpha_test = true; } const ColorBlendAttrib *color_blend; rs->get_attrib_def(color_blend); if (color_blend->get_mode() != ColorBlendAttrib::M_none) { - _have_alpha_blend = true; + have_alpha_blend = true; } const TransparencyAttrib *transparency; rs->get_attrib_def(transparency); - if ((transparency->get_mode() == TransparencyAttrib::M_alpha)|| - (transparency->get_mode() == TransparencyAttrib::M_premultiplied_alpha)|| - (transparency->get_mode() == TransparencyAttrib::M_dual)) { - _have_alpha_blend = true; + if (transparency->get_mode() == TransparencyAttrib::M_alpha || + transparency->get_mode() == TransparencyAttrib::M_premultiplied_alpha || + transparency->get_mode() == TransparencyAttrib::M_dual) { + have_alpha_blend = true; } // Decide what to send to the framebuffer alpha, if anything. - - if (outputs & AuxBitplaneAttrib::ABO_glow) { - if (_have_alpha_blend) { - _calc_primary_alpha = true; - _out_primary_glow = false; - _disable_alpha_write = true; - } else if (_have_alpha_test) { - _calc_primary_alpha = true; - _out_primary_glow = true; - _subsume_alpha_test = true; - } else { - _calc_primary_alpha = false; - _out_primary_glow = true; - } - } else { - if (_have_alpha_blend || _have_alpha_test) { - _calc_primary_alpha = true; + if (key._outputs & AuxBitplaneAttrib::ABO_glow) { + if (have_alpha_blend) { + key._outputs &= ~AuxBitplaneAttrib::ABO_glow; + key._disable_alpha_write = true; + } else if (have_alpha_test) { + // Subsume the alpha test in our shader. + key._alpha_test_mode = alpha_test->get_mode(); + key._alpha_test_ref = alpha_test->get_reference_alpha(); } } - // Determine what to put into the aux bitplane. - - _out_aux_normal = (outputs & AuxBitplaneAttrib::ABO_aux_normal) ? true:false; - _out_aux_glow = (outputs & AuxBitplaneAttrib::ABO_aux_glow) ? true:false; - _out_aux_any = (_out_aux_normal || _out_aux_glow); - - if (_out_aux_normal) { - _need_eye_normal = true; + if (have_alpha_blend || have_alpha_test) { + key._calc_primary_alpha = true; } - // Count number of textures. - - const TextureAttrib *texture; - rs->get_attrib_def(texture); - _num_textures = texture->get_num_on_stages(); - // Determine whether or not vertex colors or flat colors are present. - const ColorAttrib *color; rs->get_attrib_def(color); - if (color->get_color_type() == ColorAttrib::T_vertex) { - _vertex_colors = true; - } else if (color->get_color_type() == ColorAttrib::T_flat) { - _flat_colors = true; - } - - // Find the material. + key._color_type = color->get_color_type(); + // Store the material flags (not the material values itself). const MaterialAttrib *material; rs->get_attrib_def(material); - - if (!material->is_off()) { - _material = material->get_material(); - } else { - _material = Material::get_default(); + if (material->get_material() != nullptr) { + key._material_flags = material->get_material()->get_flags(); } // Break out the lights by type. - - _shadows = false; const LightAttrib *la; rs->get_attrib_def(la); + bool have_ambient = false; for (int i = 0; i < la->get_num_on_lights(); ++i) { - NodePath light = la->get_on_light(i); - nassertv(!light.is_empty()); - PandaNode *light_obj = light.node(); - nassertv(light_obj != (PandaNode *)NULL); + NodePath np = la->get_on_light(i); + nassertv(!np.is_empty()); + PandaNode *node = np.node(); + nassertv(node != nullptr); - if (light_obj->get_type() == AmbientLight::get_class_type()) { - if (_material->has_ambient()) { - LColor a = _material->get_ambient(); - if ((a[0]!=0.0)||(a[1]!=0.0)||(a[2]!=0.0)) { - _have_ambient = true; + if (node->is_ambient_light()) { + have_ambient = true; + key._lighting = true; + } else { + ShaderKey::LightInfo info; + info._type = node->get_type(); + info._flags = 0; + + if (node->is_of_type(LightLensNode::get_class_type())) { + const LightLensNode *llnode = (const LightLensNode *)node; + if (shader_attrib->auto_shadow_on() && llnode->is_shadow_caster()) { + info._flags |= ShaderKey::LF_has_shadows; + } + if (llnode->has_specular_color()) { + info._flags |= ShaderKey::LF_has_specular_color; } - } else { - _have_ambient = true; } - _lighting = true; - } else if (light_obj->is_of_type(LightLensNode::get_class_type())) { - _lights_np.push_back(light); - _lights.push_back((LightLensNode *)light_obj); - if (DCAST(LightLensNode, light_obj)->is_shadow_caster()) { - _shadows = true; - } - _lighting = true; - _need_eye_normal = true; + key._lights.push_back(info); + key._lighting = true; } } + bool normal_mapping = key._lighting && shader_attrib->auto_normal_on(); + // See if there is a normal map, height map, gloss map, or glow map. Also // check if anything has TexGen. + const TextureAttrib *texture; + rs->get_attrib_def(texture); const TexGenAttrib *tex_gen; rs->get_attrib_def(tex_gen); - for (int i = 0; i < _num_textures; ++i) { + const TexMatrixAttrib *tex_matrix; + rs->get_attrib_def(tex_matrix); + + size_t num_textures = texture->get_num_on_stages(); + for (size_t i = 0; i < num_textures; ++i) { TextureStage *stage = texture->get_on_stage(i); - TextureStage::Mode mode = stage->get_mode(); - if ((mode == TextureStage::M_normal)|| - (mode == TextureStage::M_normal_height)|| - (mode == TextureStage::M_normal_gloss)) { - _map_index_normal = i; + Texture *tex = texture->get_on_texture(stage); + nassertd(tex != nullptr) continue; + + // Mark this TextureStage as having been used by the shader generator, so + // that the next time its properties change, it will cause the state to be + // rehashed to ensure that the shader is regenerated if needed. + stage->mark_used_by_auto_shader(); + + ShaderKey::TextureInfo info; + info._type = tex->get_texture_type(); + info._mode = stage->get_mode(); + info._flags = 0; + info._combine_rgb = 0u; + info._combine_alpha = 0u; + + // While we look at the mode, determine whether we need to change the mode + // in order to reflect disabled features. + switch (info._mode) { + case TextureStage::M_modulate: + { + Texture::Format format = tex->get_format(); + if (format != Texture::F_alpha) { + info._flags |= ShaderKey::TF_has_rgb; + } + if (Texture::has_alpha(format)) { + info._flags |= ShaderKey::TF_has_alpha; + } + } + break; + + case TextureStage::M_modulate_glow: + if (shader_attrib->auto_glow_on()) { + info._flags = ShaderKey::TF_map_glow; + } else { + info._mode = TextureStage::M_modulate; + info._flags = ShaderKey::TF_has_rgb; + } + break; + + case TextureStage::M_modulate_gloss: + if (shader_attrib->auto_gloss_on()) { + info._flags = ShaderKey::TF_map_glow; + } else { + info._mode = TextureStage::M_modulate; + info._flags = ShaderKey::TF_has_rgb; + } + break; + + case TextureStage::M_normal_height: + if (parallax_mapping_samples == 0) { + info._mode = TextureStage::M_normal; + } else if (!shader_attrib->auto_normal_on() || + (!key._lighting && (key._outputs & AuxBitplaneAttrib::ABO_aux_normal) == 0)) { + info._mode = TextureStage::M_height; + info._flags = ShaderKey::TF_has_alpha; + } else { + info._flags = ShaderKey::TF_map_normal | ShaderKey::TF_map_height; + } + break; + + case TextureStage::M_normal_gloss: + if (!shader_attrib->auto_gloss_on() || !key._lighting) { + info._mode = TextureStage::M_normal; + } else if (!shader_attrib->auto_normal_on()) { + info._mode = TextureStage::M_gloss; + } else { + info._flags = ShaderKey::TF_map_normal | ShaderKey::TF_map_gloss; + } + break; + + case TextureStage::M_combine: + // If we have this rare, special mode, we encode all these extra + // parameters as flags to prevent bloating the shader key. + info._flags |= (uint32_t)stage->get_combine_rgb_mode() << ShaderKey::TF_COMBINE_RGB_MODE_SHIFT; + info._flags |= (uint32_t)stage->get_combine_alpha_mode() << ShaderKey::TF_COMBINE_ALPHA_MODE_SHIFT; + if (stage->get_rgb_scale() == 2) { + info._flags |= ShaderKey::TF_rgb_scale_2; + } + if (stage->get_rgb_scale() == 4) { + info._flags |= ShaderKey::TF_rgb_scale_4; + } + if (stage->get_alpha_scale() == 2) { + info._flags |= ShaderKey::TF_alpha_scale_2; + } + if (stage->get_alpha_scale() == 4) { + info._flags |= ShaderKey::TF_alpha_scale_4; + } + info._combine_rgb = PACK_COMBINE( + stage->get_combine_rgb_source0(), stage->get_combine_rgb_operand0(), + stage->get_combine_rgb_source1(), stage->get_combine_rgb_operand1(), + stage->get_combine_rgb_source2(), stage->get_combine_rgb_operand2()); + info._combine_alpha = PACK_COMBINE( + stage->get_combine_alpha_source0(), stage->get_combine_alpha_operand0(), + stage->get_combine_alpha_source1(), stage->get_combine_alpha_operand1(), + stage->get_combine_alpha_source2(), stage->get_combine_alpha_operand2()); + + if (stage->uses_primary_color()) { + info._flags |= ShaderKey::TF_uses_primary_color; + } + if (stage->uses_last_saved_result()) { + info._flags |= ShaderKey::TF_uses_last_saved_result; + } + break; } - if ((mode == TextureStage::M_height)||(mode == TextureStage::M_normal_height)) { - _map_index_height = i; + + // In fact, perhaps this stage should be disabled altogether? + switch (info._mode) { + case TextureStage::M_normal: + if (!shader_attrib->auto_normal_on() || + (!key._lighting && (key._outputs & AuxBitplaneAttrib::ABO_aux_normal) == 0)) { + continue; + } else { + info._flags = ShaderKey::TF_map_normal; + } + break; + case TextureStage::M_glow: + if (shader_attrib->auto_glow_on()) { + info._flags = ShaderKey::TF_map_glow; + } else { + continue; + } + break; + case TextureStage::M_gloss: + if (key._lighting && shader_attrib->auto_gloss_on()) { + info._flags = ShaderKey::TF_map_gloss; + } else { + continue; + } + break; + case TextureStage::M_height: + if (parallax_mapping_samples > 0) { + info._flags = ShaderKey::TF_map_height; + } else { + continue; + } + break; } - if ((mode == TextureStage::M_glow)||(mode == TextureStage::M_modulate_glow)) { - _map_index_glow = i; - } - if ((mode == TextureStage::M_gloss)|| - (mode == TextureStage::M_modulate_gloss)|| - (mode == TextureStage::M_normal_gloss)) { - _map_index_gloss = i; - } - if (mode == TextureStage::M_height) { - _map_height_in_alpha = false; - } - if (mode == TextureStage::M_normal_height) { - _map_height_in_alpha = true; + + // Check if this state has a texture matrix to transform the texture + // coordinates. + if (tex_matrix->has_stage(stage)) { + CPT(TransformState) transform = tex_matrix->get_transform(stage); + if (!transform->is_identity()) { + // Optimize for common case: if we only have a scale component, we + // can get away with fewer shader inputs and operations. + if (transform->has_components() && !transform->has_nonzero_shear() && + transform->get_pos() == LPoint3::zero() && + transform->get_hpr() == LVecBase3::zero()) { + info._flags |= ShaderKey::TF_has_texscale; + } else { + info._flags |= ShaderKey::TF_has_texmat; + } + } } + if (tex_gen->has_stage(stage)) { - switch (tex_gen->get_mode(stage)) { - case TexGenAttrib::M_world_position: - _need_world_position = true; - break; - case TexGenAttrib::M_world_normal: - _need_world_normal = true; - break; - case TexGenAttrib::M_eye_position: - _need_eye_position = true; - break; - case TexGenAttrib::M_eye_normal: - _need_eye_normal = true; - break; - default: - break; - } - } - } - - // Determine whether we should normalize the normals. - const RescaleNormalAttrib *rescale; - rs->get_attrib_def(rescale); - - _normalize_normals = (rescale->get_mode() != RescaleNormalAttrib::M_none); - - // Decide which material modes need to be calculated. - - if (_lighting) { - if (_material->has_diffuse()) { - LColor d = _material->get_diffuse(); - if ((d[0]!=0.0)||(d[1]!=0.0)||(d[2]!=0.0)) { - _have_diffuse = true; - } + info._texcoord_name = nullptr; + info._gen_mode = tex_gen->get_mode(stage); } else { - _have_diffuse = true; + info._texcoord_name = stage->get_texcoord_name(); + info._gen_mode = TexGenAttrib::M_off; } + + // Does this stage require saving its result? + if (stage->get_saved_result()) { + info._flags |= ShaderKey::TF_saved_result; + } + + // Does this stage need a texcolor_# input? + if (stage->uses_color()) { + info._flags |= ShaderKey::TF_uses_color; + } + + key._textures.push_back(info); + key._texture_flags |= info._flags; } - if (_lighting && (_material->has_emission())) { - LColor e = _material->get_emission(); - if ((e[0]!=0.0)||(e[1]!=0.0)||(e[2]!=0.0)) { - _have_emission = true; - } - } + // Does nothing use the saved result? If so, don't bother saving it. + if ((key._texture_flags & ShaderKey::TF_uses_last_saved_result) == 0 && + (key._texture_flags & ShaderKey::TF_saved_result) != 0) { - if (_lighting) { - if (_material->has_specular()) { - LColor s = _material->get_specular(); - if ((s[0]!=0.0)||(s[1]!=0.0)||(s[2]!=0.0)) { - _have_specular = true; - } - } else if (_map_index_gloss >= 0) { - _have_specular = true; + pvector::iterator it; + for (it = key._textures.begin(); it != key._textures.end(); ++it) { + (*it)._flags &= ~ShaderKey::TF_saved_result; } - - _need_eye_position = true; + key._texture_flags &= ~ShaderKey::TF_saved_result; } // Decide whether to separate ambient and diffuse calculations. - - if (_have_ambient && _have_diffuse) { - if (_material->has_ambient()) { - if (_material->has_diffuse()) { - _separate_ambient_diffuse = _material->get_ambient() != _material->get_diffuse(); - } else { - _separate_ambient_diffuse = true; - } + if (have_ambient) { + if (key._material_flags & Material::F_ambient) { + key._have_separate_ambient = true; } else { - if (_material->has_diffuse()) { - _separate_ambient_diffuse = true; + if (key._material_flags & Material::F_diffuse) { + key._have_separate_ambient = true; } else { - _separate_ambient_diffuse = false; + key._have_separate_ambient = false; } } } - const LightRampAttrib *light_ramp; - if (_lighting && rs->get_attrib(light_ramp) && - (light_ramp->get_mode() != LightRampAttrib::LRT_identity)) { - _separate_ambient_diffuse = true; + if (shader_attrib->auto_ramp_on()) { + const LightRampAttrib *light_ramp; + if (rs->get_attrib(light_ramp)) { + key._light_ramp = light_ramp; + if (key._lighting) { + key._have_separate_ambient = true; + } + } } - // Do we want to use the ARB_shadow extension? This also allows us to use - // hardware shadows PCF. - - _use_shadow_filter = _gsg->get_supports_shadow_filter(); - - // Does the shader need material properties as input? - - _need_material_props = - (_have_ambient && (_material->has_ambient()))|| - (_have_diffuse && (_material->has_diffuse()))|| - (_have_emission && (_material->has_emission()))|| - (_have_specular && (_material->has_specular())); - // Check for clip planes. - const ClipPlaneAttrib *clip_plane; rs->get_attrib_def(clip_plane); - _num_clip_planes = clip_plane->get_num_on_planes(); - if (_num_clip_planes > 0) { - _need_world_position = true; - } - - const ShaderAttrib *shader_attrib; - rs->get_attrib_def(shader_attrib); - if (shader_attrib->auto_shader()) { - _auto_normal_on = shader_attrib->auto_normal_on(); - _auto_glow_on = shader_attrib->auto_glow_on(); - _auto_gloss_on = shader_attrib->auto_gloss_on(); - _auto_ramp_on = shader_attrib->auto_ramp_on(); - _auto_shadow_on = shader_attrib->auto_shadow_on(); - } + key._num_clip_planes = clip_plane->get_num_on_planes(); // Check for fog. const FogAttrib *fog; if (rs->get_attrib(fog) && !fog->is_off()) { - _fog = true; + key._fog_mode = (int)fog->get_fog()->get_mode() + 1; } } /** - * Called after analyze_renderstate to discard all the results of the - * analysis. This is generally done after shader generation is complete. + * Rehashes all the states with generated shaders, removing the ones that are + * no longer fresh. + * + * Call this if certain state has changed in such a way as to require a rerun + * of the shader generator. This should be rare because in most cases, the + * shader generator will automatically regenerate shaders as necessary. */ void ShaderGenerator:: -clear_analysis() { - _vertex_colors = false; - _flat_colors = false; - _lighting = false; - _shadows = false; - _fog = false; - _have_ambient = false; - _have_diffuse = false; - _have_emission = false; - _have_specular = false; - _separate_ambient_diffuse = false; - _map_index_normal = -1; - _map_index_glow = -1; - _map_index_gloss = -1; - _map_index_height = -1; - _map_height_in_alpha = false; - _calc_primary_alpha = false; - _have_alpha_test = false; - _have_alpha_blend = false; - _subsume_alpha_test = false; - _disable_alpha_write = false; - _num_clip_planes = 0; - _use_shadow_filter = false; - _out_primary_glow = false; - _out_aux_normal = false; - _out_aux_glow = false; - _out_aux_any = false; - _material = (Material*)NULL; - _need_material_props = false; - _need_world_position = false; - _need_world_normal = false; - _need_eye_position = false; - _need_eye_normal = false; - _normalize_normals = false; - _auto_normal_on = false; - _auto_glow_on = false; - _auto_gloss_on = false; - _auto_ramp_on = false; - _auto_shadow_on = false; +rehash_generated_shaders() { + LightReMutexHolder holder(*RenderState::_states_lock); - _lights.clear(); - _lights_np.clear(); + // With uniquify-states turned on, we can actually go through all the states + // and check whether their generated shader is still OK. + size_t size = RenderState::_states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { + const RenderState *state = RenderState::_states->get_key(si); + + if (state->_generated_shader != nullptr) { + ShaderKey key; + analyze_renderstate(key, state); + + GeneratedShaders::const_iterator si; + si = _generated_shaders.find(key); + if (si != _generated_shaders.end()) { + if (si->second != state->_generated_shader) { + state->_generated_shader = si->second; + state->_munged_states.clear(); + } + } else { + // We have not yet generated a shader for this modified state. + state->_generated_shader.clear(); + state->_munged_states.clear(); + } + } + } + + // If we don't have uniquify-states, however, the above list won't contain + // all the state. We can change a global seq value to require Panda to + // rehash the states the next time it tries to render an object with it. + if (!uniquify_states) { + GraphicsStateGuardianBase::mark_rehash_generated_shaders(); + } } /** - * Creates a ShaderAttrib given a generated shader's body. Also inserts the - * lights into the shader attrib. + * Removes all previously generated shaders, requiring all shaders to be + * regenerated. Does not clear cache of compiled shaders. */ -CPT(RenderAttrib) ShaderGenerator:: -create_shader_attrib(const string &txt) { - PT(Shader) shader = Shader::make(txt, Shader::SL_Cg); - CPT(RenderAttrib) shattr = ShaderAttrib::make(shader); +void ShaderGenerator:: +clear_generated_shaders() { + LightReMutexHolder holder(*RenderState::_states_lock); - for (size_t i = 0; i < _lights.size(); ++i) { - shattr = DCAST(ShaderAttrib, shattr)->set_shader_input(InternalName::make("light", i), _lights_np[i]); + size_t size = RenderState::_states->get_num_entries(); + for (size_t si = 0; si < size; ++si) { + const RenderState *state = RenderState::_states->get_key(si); + state->_generated_shader.clear(); + } + + _generated_shaders.clear(); + + // If we don't have uniquify-states, we can't clear all the ShaderAttribs + // that are cached on the states, but we can simulate the effect of that. + if (!uniquify_states) { + GraphicsStateGuardianBase::mark_rehash_generated_shaders(); } - return shattr; } /** @@ -527,22 +599,49 @@ create_shader_attrib(const string &txt) { * synthesizing a shader. It also takes care of setting up any buffers needed * to produce the requested effects. * - * Currently supports: - flat colors - vertex colors - lighting - normal maps, - * but not multiple - gloss maps, but not multiple - glow maps, but not - * multiple - materials, but not updates to materials - 2D textures - all - * texture stage modes, including combine modes - color scale attrib - light - * ramps (for cartoon shading) - shadow mapping - most texgen modes - - * texmatrix - 1D/2D/3D textures, cube textures, 2D tex arrays - - * linear/exp/exp2 fog - animation + * Currently supports: + * - flat colors + * - vertex colors + * - lighting + * - normal maps, even multiple + * - gloss maps, but not multiple + * - glow maps, but not multiple + * - materials, but not updates to materials + * - 2D textures + * - all texture stage modes, including combine modes + * - color scale attrib + * - light ramps (for cartoon shading) + * - shadow mapping + * - most texgen modes + * - texmatrix + * - 1D/2D/3D textures, cube textures, 2D tex arrays + * - linear/exp/exp2 fog + * - animation * - * Not yet supported: - dot3_rgb and dot3_rgba combine modes - * - * Potential optimizations - omit attenuation calculations if attenuation off + * Potential optimizations + * - omit attenuation calculations if attenuation off * */ CPT(ShaderAttrib) ShaderGenerator:: synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { - analyze_renderstate(rs); + ShaderKey key; + + // First look up the state key in the table of already generated shaders. + { + PStatTimer timer(lookup_collector); + key._anim_spec = anim; + analyze_renderstate(key, rs); + + GeneratedShaders::const_iterator si; + si = _generated_shaders.find(key); + if (si != _generated_shaders.end()) { + // We've already generated a shader for this state. + return si->second; + } + } + + PStatTimer timer(synthesize_collector); + reset_register_allocator(); if (pgraphnodes_cat.is_debug()) { @@ -581,7 +680,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { normal_vreg = "NORMAL"; } - if (_vertex_colors) { + if (key._color_type == ColorAttrib::T_vertex) { // Reserve COLOR0 color_vreg = _use_generic_attr ? "ATTR3" : "COLOR0"; _vcregs_used = 1; @@ -598,117 +697,152 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { rs->write(text, 2); text << "*/\n"; + int map_index_glow = -1; + int map_index_gloss = -1; + + // Figure out whether we need to calculate any of these variables. + bool need_world_position = (key._num_clip_planes > 0); + bool need_world_normal = false; + bool need_eye_position = key._lighting; + bool need_eye_normal = !key._lights.empty() || ((key._outputs & AuxBitplaneAttrib::ABO_aux_normal) != 0); + + bool have_specular = false; + if (key._lighting) { + if (key._material_flags & Material::F_specular) { + have_specular = true; + } else if ((key._texture_flags & ShaderKey::TF_map_gloss) != 0) { + have_specular = true; + } + } + text << "void vshader(\n"; - const TextureAttrib *texture = DCAST(TextureAttrib, rs->get_attrib_def(TextureAttrib::get_class_slot())); - const TexGenAttrib *tex_gen = DCAST(TexGenAttrib, rs->get_attrib_def(TexGenAttrib::get_class_slot())); - for (int i = 0; i < _num_textures; ++i) { - TextureStage *stage = texture->get_on_stage(i); - if (!tex_gen->has_stage(stage)) { - const InternalName *texcoord_name = stage->get_texcoord_name(); + for (size_t i = 0; i < key._textures.size(); ++i) { + const ShaderKey::TextureInfo &tex = key._textures[i]; - if (texcoord_fregs.count(texcoord_name) == 0) { + switch (tex._gen_mode) { + case TexGenAttrib::M_world_position: + need_world_position = true; + break; + case TexGenAttrib::M_world_normal: + need_world_normal = true; + break; + case TexGenAttrib::M_eye_position: + need_eye_position = true; + break; + case TexGenAttrib::M_eye_normal: + need_eye_normal = true; + break; + default: + break; + } + + if (tex._texcoord_name != nullptr) { + if (texcoord_fregs.count(tex._texcoord_name) == 0) { const char *freg = alloc_freg(); - string tcname = texcoord_name->join("_"); - texcoord_fregs[texcoord_name] = freg; + texcoord_fregs[tex._texcoord_name] = freg; + string tcname = tex._texcoord_name->join("_"); text << "\t in float4 vtx_" << tcname << " : " << alloc_vreg() << ",\n"; text << "\t out float4 l_" << tcname << " : " << freg << ",\n"; } } - if ((_map_index_normal == i && (_lighting || _out_aux_normal) && _auto_normal_on) || _map_index_height == i) { - const InternalName *texcoord_name = stage->get_texcoord_name(); + if (tangent_input.empty() && + (tex._flags & (ShaderKey::TF_map_normal | ShaderKey::TF_map_height)) != 0) { PT(InternalName) tangent_name = InternalName::get_tangent(); PT(InternalName) binormal_name = InternalName::get_binormal(); - if (texcoord_name != InternalName::get_texcoord()) { - tangent_name = tangent_name->append(texcoord_name->get_basename()); - binormal_name = binormal_name->append(texcoord_name->get_basename()); + if (tex._texcoord_name != nullptr && + tex._texcoord_name != InternalName::get_texcoord()) { + tangent_name = tangent_name->append(tex._texcoord_name->get_basename()); + binormal_name = binormal_name->append(tex._texcoord_name->get_basename()); } + tangent_input = tangent_name->join("_"); binormal_input = binormal_name->join("_"); text << "\t in float4 vtx_" << tangent_input << " : " << alloc_vreg() << ",\n"; text << "\t in float4 vtx_" << binormal_input << " : " << alloc_vreg() << ",\n"; + } - if (_map_index_normal == i && (_lighting || _out_aux_normal) && _auto_normal_on) { - tangent_freg = alloc_freg(); - binormal_freg = alloc_freg(); - text << "\t out float4 l_tangent : " << tangent_freg << ",\n"; - text << "\t out float4 l_binormal : " << binormal_freg << ",\n"; - } + if (tex._flags & ShaderKey::TF_map_glow) { + map_index_glow = i; + } + if (tex._flags & ShaderKey::TF_map_gloss) { + map_index_gloss = i; } } - if (_vertex_colors) { + if (key._texture_flags & ShaderKey::TF_map_normal) { + tangent_freg = alloc_freg(); + binormal_freg = alloc_freg(); + text << "\t out float4 l_tangent : " << tangent_freg << ",\n"; + text << "\t out float4 l_binormal : " << binormal_freg << ",\n"; + } + if (key._color_type == ColorAttrib::T_vertex) { text << "\t in float4 vtx_color : " << color_vreg << ",\n"; text << "\t out float4 l_color : COLOR0,\n"; } - if (_need_world_position || _need_world_normal) { + if (need_world_position || need_world_normal) { text << "\t uniform float4x4 trans_model_to_world,\n"; } - if (_need_world_position) { + if (need_world_position) { world_position_freg = alloc_freg(); text << "\t out float4 l_world_position : " << world_position_freg << ",\n"; } - if (_need_world_normal) { + if (need_world_normal) { world_normal_freg = alloc_freg(); text << "\t out float4 l_world_normal : " << world_normal_freg << ",\n"; } - if (_need_eye_position) { + if (need_eye_position) { text << "\t uniform float4x4 trans_model_to_view,\n"; eye_position_freg = alloc_freg(); text << "\t out float4 l_eye_position : " << eye_position_freg << ",\n"; - } else if ((_lighting || _out_aux_normal) && (_map_index_normal >= 0 && _auto_normal_on)) { + } else if (key._texture_flags & ShaderKey::TF_map_normal) { text << "\t uniform float4x4 trans_model_to_view,\n"; } - if (_need_eye_normal) { + if (need_eye_normal) { eye_normal_freg = alloc_freg(); text << "\t uniform float4x4 tpose_view_to_model,\n"; text << "\t out float4 l_eye_normal : " << eye_normal_freg << ",\n"; } - if (_map_index_height >= 0 || _need_world_normal || _need_eye_normal) { + if ((key._texture_flags & ShaderKey::TF_map_height) != 0 || need_world_normal || need_eye_normal) { text << "\t in float3 vtx_normal : " << normal_vreg << ",\n"; } - if (_map_index_height >= 0) { + if (key._texture_flags & ShaderKey::TF_map_height) { text << "\t uniform float4 mspos_view,\n"; text << "\t out float3 l_eyevec,\n"; } - if (_lighting && _shadows && _auto_shadow_on) { - for (size_t i = 0; i < _lights.size(); ++i) { - if (_lights[i]->_shadow_caster) { - lightcoord_fregs.push_back(alloc_freg()); - if (_lights[i]->is_of_type(PointLight::get_class_type())) { - text << "\t uniform float4x4 trans_model_to_light" << i << ",\n"; - } else { - text << "\t uniform float4x4 trans_model_to_clip_of_light" << i << ",\n"; - } - text << "\t out float4 l_lightcoord" << i << " : " << lightcoord_fregs[i] << ",\n"; - } else { - lightcoord_fregs.push_back(NULL); - } + for (size_t i = 0; i < key._lights.size(); ++i) { + const ShaderKey::LightInfo &light = key._lights[i]; + if (light._flags & ShaderKey::LF_has_shadows) { + lightcoord_fregs.push_back(alloc_freg()); + text << "\t uniform float4x4 mat_shadow_" << i << ",\n"; + text << "\t out float4 l_lightcoord" << i << " : " << lightcoord_fregs[i] << ",\n"; + } else { + lightcoord_fregs.push_back(nullptr); } } - if (_fog) { + if (key._fog_mode != 0) { hpos_freg = alloc_freg(); text << "\t out float4 l_hpos : " << hpos_freg << ",\n"; } - if (anim.get_animation_type() == GeomEnums::AT_hardware && - anim.get_num_transforms() > 0) { + if (key._anim_spec.get_animation_type() == GeomEnums::AT_hardware && + key._anim_spec.get_num_transforms() > 0) { int num_transforms; - if (anim.get_indexed_transforms()) { + if (key._anim_spec.get_indexed_transforms()) { num_transforms = 120; } else { - num_transforms = anim.get_num_transforms(); + num_transforms = key._anim_spec.get_num_transforms(); } - if (transform_weight_vreg == NULL) { + if (transform_weight_vreg == nullptr) { transform_weight_vreg = alloc_vreg(); } - if (transform_index_vreg == NULL) { + if (transform_index_vreg == nullptr) { transform_index_vreg = alloc_vreg(); } text << "\t uniform float4x4 tbl_transforms[" << num_transforms << "],\n"; text << "\t in float4 vtx_transform_weight : " << transform_weight_vreg << ",\n"; - if (anim.get_indexed_transforms()) { + if (key._anim_spec.get_indexed_transforms()) { text << "\t in uint4 vtx_transform_index : " << transform_index_vreg << ",\n"; } } @@ -717,50 +851,46 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << "\t uniform float4x4 mat_modelproj\n"; text << ") {\n"; - if (anim.get_animation_type() == GeomEnums::AT_hardware && - anim.get_num_transforms() > 0) { + if (key._anim_spec.get_animation_type() == GeomEnums::AT_hardware && + key._anim_spec.get_num_transforms() > 0) { - if (!anim.get_indexed_transforms()) { + if (!key._anim_spec.get_indexed_transforms()) { text << "\t const uint4 vtx_transform_index = uint4(0, 1, 2, 3);\n"; } text << "\t float4x4 matrix = tbl_transforms[vtx_transform_index.x] * vtx_transform_weight.x"; - if (anim.get_num_transforms() > 1) { + if (key._anim_spec.get_num_transforms() > 1) { text << "\n\t + tbl_transforms[vtx_transform_index.y] * vtx_transform_weight.y"; } - if (anim.get_num_transforms() > 2) { + if (key._anim_spec.get_num_transforms() > 2) { text << "\n\t + tbl_transforms[vtx_transform_index.z] * vtx_transform_weight.z"; } - if (anim.get_num_transforms() > 3) { + if (key._anim_spec.get_num_transforms() > 3) { text << "\n\t + tbl_transforms[vtx_transform_index.w] * vtx_transform_weight.w"; } text << ";\n"; text << "\t vtx_position = mul(matrix, vtx_position);\n"; - if (_need_world_normal || _need_eye_normal) { + if (need_world_normal || need_eye_normal) { text << "\t vtx_normal = mul((float3x3)matrix, vtx_normal);\n"; } } text << "\t l_position = mul(mat_modelproj, vtx_position);\n"; - if (_fog) { + if (key._fog_mode != 0) { text << "\t l_hpos = l_position;\n"; } - if (_need_world_position) { + if (need_world_position) { text << "\t l_world_position = mul(trans_model_to_world, vtx_position);\n"; } - if (_need_world_normal) { + if (need_world_normal) { text << "\t l_world_normal = mul(trans_model_to_world, float4(vtx_normal, 0));\n"; } - if (_need_eye_position) { + if (need_eye_position) { text << "\t l_eye_position = mul(trans_model_to_view, vtx_position);\n"; } - if (_need_eye_normal) { - if (_normalize_normals) { - text << "\t l_eye_normal.xyz = normalize(mul((float3x3)tpose_view_to_model, vtx_normal));\n"; - } else { - text << "\t l_eye_normal.xyz = mul((float3x3)tpose_view_to_model, vtx_normal);\n"; - } + if (need_eye_normal) { + text << "\t l_eye_normal.xyz = normalize(mul((float3x3)tpose_view_to_model, vtx_normal));\n"; text << "\t l_eye_normal.w = 0;\n"; } pmap::const_iterator it; @@ -769,28 +899,21 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { string tcname = it->first->join("_"); text << "\t l_" << tcname << " = vtx_" << tcname << ";\n"; } - if (_vertex_colors) { + if (key._color_type == ColorAttrib::T_vertex) { text << "\t l_color = vtx_color;\n"; } - if ((_lighting || _out_aux_normal) && (_map_index_normal >= 0 && _auto_normal_on)) { + if (key._texture_flags & ShaderKey::TF_map_normal) { text << "\t l_tangent.xyz = normalize(mul((float3x3)trans_model_to_view, vtx_" << tangent_input << ".xyz));\n"; text << "\t l_tangent.w = 0;\n"; text << "\t l_binormal.xyz = normalize(mul((float3x3)trans_model_to_view, -vtx_" << binormal_input << ".xyz));\n"; text << "\t l_binormal.w = 0;\n"; } - if (_shadows && _auto_shadow_on) { - text << "\t float4x4 biasmat = {0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f};\n"; - for (size_t i = 0; i < _lights.size(); ++i) { - if (_lights[i]->_shadow_caster) { - if (_lights[i]->is_of_type(PointLight::get_class_type())) { - text << "\t l_lightcoord" << i << " = mul(trans_model_to_light" << i << ", vtx_position);\n"; - } else { - text << "\t l_lightcoord" << i << " = mul(biasmat, mul(trans_model_to_clip_of_light" << i << ", vtx_position));\n"; - } - } + for (size_t i = 0; i < key._lights.size(); ++i) { + if (key._lights[i]._flags & ShaderKey::LF_has_shadows) { + text << "\t l_lightcoord" << i << " = mul(mat_shadow_" << i << ", l_eye_position);\n"; } } - if (_map_index_height >= 0) { + if (key._texture_flags & ShaderKey::TF_map_height) { text << "\t float3 eyedir = mspos_view.xyz - vtx_position.xyz;\n"; text << "\t l_eyevec.x = dot(vtx_" << tangent_input << ".xyz, eyedir);\n"; text << "\t l_eyevec.y = dot(vtx_" << binormal_input << ".xyz, eyedir);\n"; @@ -802,144 +925,144 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { // Fragment shader text << "void fshader(\n"; - if (_fog) { + if (key._fog_mode != 0) { text << "\t in float4 l_hpos : " << hpos_freg << ",\n"; text << "\t in uniform float4 attr_fog,\n"; text << "\t in uniform float4 attr_fogcolor,\n"; } - if (_need_world_position) { + if (need_world_position) { text << "\t in float4 l_world_position : " << world_position_freg << ",\n"; } - if (_need_world_normal) { + if (need_world_normal) { text << "\t in float4 l_world_normal : " << world_normal_freg << ",\n"; } - if (_need_eye_position) { + if (need_eye_position) { text << "\t in float4 l_eye_position : " << eye_position_freg << ",\n"; } - if (_need_eye_normal) { + if (need_eye_normal) { text << "\t in float4 l_eye_normal : " << eye_normal_freg << ",\n"; } for (it = texcoord_fregs.begin(); it != texcoord_fregs.end(); ++it) { text << "\t in float4 l_" << it->first->join("_") << " : " << it->second << ",\n"; } - const TexMatrixAttrib *tex_matrix = DCAST(TexMatrixAttrib, rs->get_attrib_def(TexMatrixAttrib::get_class_slot())); - for (int i=0; i<_num_textures; i++) { - TextureStage *stage = texture->get_on_stage(i); - Texture *tex = texture->get_on_texture(stage); - nassertr(tex != NULL, NULL); - text << "\t uniform sampler" << texture_type_as_string(tex->get_texture_type()) << " tex_" << i << ",\n"; - if (tex_matrix->has_stage(stage)) { + for (size_t i = 0; i < key._textures.size(); ++i) { + const ShaderKey::TextureInfo &tex = key._textures[i]; + text << "\t uniform sampler" << texture_type_as_string(tex._type) << " tex_" << i << ",\n"; + + if (tex._flags & ShaderKey::TF_has_texscale) { + text << "\t uniform float3 texscale_" << i << ",\n"; + } else if (tex._flags & ShaderKey::TF_has_texmat) { text << "\t uniform float4x4 texmat_" << i << ",\n"; } + + if (tex._flags & ShaderKey::TF_uses_color) { + text << "\t uniform float4 texcolor_" << i << ",\n"; + } } - if ((_lighting || _out_aux_normal) && (_map_index_normal >= 0 && _auto_normal_on)) { + if (key._texture_flags & ShaderKey::TF_map_normal) { text << "\t in float3 l_tangent : " << tangent_freg << ",\n"; text << "\t in float3 l_binormal : " << binormal_freg << ",\n"; } - if (_lighting) { - for (size_t i = 0; i < _lights.size(); ++i) { - if (_lights[i]->is_of_type(DirectionalLight::get_class_type())) { - text << "\t uniform float4x4 dlight_light" << i << "_rel_view,\n"; + for (size_t i = 0; i < key._lights.size(); ++i) { + text << "\t uniform float4x4 attr_light" << i << ",\n"; - } else if (_lights[i]->is_of_type(PointLight::get_class_type())) { - text << "\t uniform float4x4 plight_light" << i << "_rel_view,\n"; - - } else if (_lights[i]->is_of_type(Spotlight::get_class_type())) { - text << "\t uniform float4x4 slight_light" << i << "_rel_view,\n"; - text << "\t uniform float4 satten_light" << i << ",\n"; - } - - if (_shadows && _lights[i]->_shadow_caster && _auto_shadow_on) { - if (_lights[i]->is_of_type(PointLight::get_class_type())) { - text << "\t uniform samplerCUBE shadow_light" << i << ",\n"; - } else if (_use_shadow_filter) { - text << "\t uniform sampler2DShadow shadow_light" << i << ",\n"; - } else { - text << "\t uniform sampler2D shadow_light" << i << ",\n"; - } - text << "\t in float4 l_lightcoord" << i << " : " << lightcoord_fregs[i] << ",\n"; - } - } - if (_need_material_props) { - text << "\t uniform float4x4 attr_material,\n"; - } - if (_have_specular) { - if (_material->get_local()) { - text << "\t uniform float4 mspos_view,\n"; + const ShaderKey::LightInfo &light = key._lights[i]; + if (light._flags & ShaderKey::LF_has_shadows) { + if (light._type.is_derived_from(PointLight::get_class_type())) { + text << "\t uniform samplerCUBE shadow_" << i << ",\n"; + } else if (_use_shadow_filter) { + text << "\t uniform sampler2DShadow shadow_" << i << ",\n"; } else { - text << "\t uniform float4 row1_view_to_model,\n"; + text << "\t uniform sampler2D shadow_" << i << ",\n"; } + text << "\t in float4 l_lightcoord" << i << " : " << lightcoord_fregs[i] << ",\n"; + } + if (light._flags & ShaderKey::LF_has_specular_color) { + text << "\t uniform float4 attr_lspec" << i << ",\n"; } } - if (_map_index_height >= 0) { + + // Does the shader need material properties as input? + if (key._material_flags & (Material::F_ambient | Material::F_diffuse | Material::F_emission | Material::F_specular)) { + text << "\t uniform float4x4 attr_material,\n"; + } + if (key._texture_flags & ShaderKey::TF_map_height) { text << "\t float3 l_eyevec,\n"; } - if (_out_aux_any) { + if (key._outputs & (AuxBitplaneAttrib::ABO_aux_normal | AuxBitplaneAttrib::ABO_aux_glow)) { text << "\t out float4 o_aux : COLOR1,\n"; } text << "\t out float4 o_color : COLOR0,\n"; - if (_vertex_colors) { + + if (key._color_type == ColorAttrib::T_vertex) { text << "\t in float4 l_color : COLOR0,\n"; - } else { + } else if (key._color_type == ColorAttrib::T_flat) { text << "\t uniform float4 attr_color,\n"; } - for (int i=0; i<_num_clip_planes; ++i) { + + for (int i = 0; i < key._num_clip_planes; ++i) { text << "\t uniform float4 clipplane_" << i << ",\n"; } + text << "\t uniform float4 attr_ambient,\n"; text << "\t uniform float4 attr_colorscale\n"; text << ") {\n"; + // Clipping first! - for (int i=0; i<_num_clip_planes; ++i) { + for (int i = 0; i < key._num_clip_planes; ++i) { text << "\t if (l_world_position.x * clipplane_" << i << ".x + l_world_position.y "; text << "* clipplane_" << i << ".y + l_world_position.z * clipplane_" << i << ".z + clipplane_" << i << ".w <= 0) {\n"; text << "\t discard;\n"; text << "\t }\n"; } text << "\t float4 result;\n"; - if (_out_aux_any) { + if (key._outputs & (AuxBitplaneAttrib::ABO_aux_normal | AuxBitplaneAttrib::ABO_aux_glow)) { text << "\t o_aux = float4(0, 0, 0, 0);\n"; } // Now generate any texture coordinates according to TexGenAttrib. If it // has a TexMatrixAttrib, also transform them. - for (int i=0; i<_num_textures; i++) { - TextureStage *stage = texture->get_on_stage(i); - if (tex_gen != NULL && tex_gen->has_stage(stage)) { - switch (tex_gen->get_mode(stage)) { - case TexGenAttrib::M_world_position: - text << "\t float4 texcoord" << i << " = l_world_position;\n"; - break; - case TexGenAttrib::M_world_normal: - text << "\t float4 texcoord" << i << " = l_world_normal;\n"; - break; - case TexGenAttrib::M_eye_position: - text << "\t float4 texcoord" << i << " = l_eye_position;\n"; - break; - case TexGenAttrib::M_eye_normal: - text << "\t float4 texcoord" << i << " = l_eye_normal;\n"; - text << "\t texcoord" << i << ".w = 1.0f;\n"; - break; - default: - pgraphnodes_cat.error() << "Unsupported TexGenAttrib mode\n"; - text << "\t float4 texcoord" << i << " = float4(0, 0, 0, 0);\n"; - } - } else { + for (size_t i = 0; i < key._textures.size(); ++i) { + const ShaderKey::TextureInfo &tex = key._textures[i]; + switch (tex._gen_mode) { + case TexGenAttrib::M_off: // Cg seems to be able to optimize this temporary away when appropriate. - const InternalName *texcoord_name = stage->get_texcoord_name(); - text << "\t float4 texcoord" << i << " = l_" << texcoord_name->join("_") << ";\n"; + text << "\t float4 texcoord" << i << " = l_" << tex._texcoord_name->join("_") << ";\n"; + break; + case TexGenAttrib::M_world_position: + text << "\t float4 texcoord" << i << " = l_world_position;\n"; + break; + case TexGenAttrib::M_world_normal: + text << "\t float4 texcoord" << i << " = l_world_normal;\n"; + break; + case TexGenAttrib::M_eye_position: + text << "\t float4 texcoord" << i << " = l_eye_position;\n"; + break; + case TexGenAttrib::M_eye_normal: + text << "\t float4 texcoord" << i << " = l_eye_normal;\n"; + text << "\t texcoord" << i << ".w = 1.0f;\n"; + break; + default: + text << "\t float4 texcoord" << i << " = float4(0, 0, 0, 0);\n"; + pgraphnodes_cat.error() + << "Unsupported TexGenAttrib mode: " << tex._gen_mode << "\n"; } - if (tex_matrix != NULL && tex_matrix->has_stage(stage)) { + if (tex._flags & ShaderKey::TF_has_texscale) { + text << "\t texcoord" << i << ".xyz *= texscale_" << i << ";\n"; + } else if (tex._flags & ShaderKey::TF_has_texmat) { text << "\t texcoord" << i << " = mul(texmat_" << i << ", texcoord" << i << ");\n"; text << "\t texcoord" << i << ".xyz /= texcoord" << i << ".w;\n"; } } text << "\t // Fetch all textures.\n"; - if (_map_index_height >= 0 && parallax_mapping_samples > 0) { - Texture *tex = texture->get_on_texture(texture->get_on_stage(_map_index_height)); - nassertr(tex != NULL, NULL); - text << "\t float4 tex" << _map_index_height << " = tex" << texture_type_as_string(tex->get_texture_type()); - text << "(tex_" << _map_index_height << ", texcoord" << _map_index_height << "."; - switch (tex->get_texture_type()) { + for (size_t i = 0; i < key._textures.size(); ++i) { + const ShaderKey::TextureInfo &tex = key._textures[i]; + if ((tex._flags & ShaderKey::TF_map_height) == 0) { + continue; + } + + text << "\t float4 tex" << i << " = tex" << texture_type_as_string(tex._type); + text << "(tex_" << i << ", texcoord" << i << "."; + switch (tex._type) { case Texture::TT_cube_map: case Texture::TT_3d_texture: case Texture::TT_2d_texture_array: @@ -954,17 +1077,19 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { default: break; } - text << ");\n\t float3 parallax_offset = l_eyevec.xyz * (tex" << _map_index_height; - if (_map_height_in_alpha) { + text << ");\n\t float3 parallax_offset = l_eyevec.xyz * (tex" << i; + if (tex._mode == TextureStage::M_normal_height || + (tex._flags & ShaderKey::TF_has_alpha) != 0) { text << ".aaa"; } else { text << ".rgb"; } text << " * 2.0 - 1.0) * " << parallax_mapping_scale << ";\n"; // Additional samples - for (int i=0; iget_on_texture(texture->get_on_stage(i)); - nassertr(tex != NULL, NULL); + for (size_t i = 0; i < key._textures.size(); ++i) { + ShaderKey::TextureInfo &tex = key._textures[i]; + if ((tex._flags & ShaderKey::TF_map_height) == 0) { // Parallax mapping pushes the texture coordinates of the other textures // away from the camera. - if (_map_index_height >= 0 && parallax_mapping_samples > 0) { + if (key._texture_flags & ShaderKey::TF_map_height) { text << "\t texcoord" << i << ".xyz -= parallax_offset;\n"; } - text << "\t float4 tex" << i << " = tex" << texture_type_as_string(tex->get_texture_type()); + text << "\t float4 tex" << i << " = tex" << texture_type_as_string(tex._type); text << "(tex_" << i << ", texcoord" << i << "."; - switch(tex->get_texture_type()) { + switch (tex._type) { case Texture::TT_cube_map: case Texture::TT_3d_texture: case Texture::TT_2d_texture_array: @@ -1001,108 +1125,118 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << ");\n"; } } - if (_lighting || _out_aux_normal) { - if (_map_index_normal >= 0 && _auto_normal_on) { - text << "\t // Translate tangent-space normal in map to view-space.\n"; - text << "\t float3 tsnormal = ((float3)tex" << _map_index_normal << " * 2) - 1;\n"; - text << "\t l_eye_normal.xyz *= tsnormal.z;\n"; - text << "\t l_eye_normal.xyz += l_tangent * tsnormal.x;\n"; - text << "\t l_eye_normal.xyz += l_binormal * tsnormal.y;\n"; + if (key._texture_flags & ShaderKey::TF_map_normal) { + text << "\t // Translate tangent-space normal in map to view-space.\n"; + + // Use Reoriented Normal Mapping to blend additional normal maps. + bool is_first = true; + for (size_t i = 0; i < key._textures.size(); ++i) { + const ShaderKey::TextureInfo &tex = key._textures[i]; + if (tex._flags & ShaderKey::TF_map_normal) { + if (is_first) { + text << "\t float3 tsnormal = (tex" << i << ".xyz * 2) - 1;\n"; + is_first = false; + continue; + } + text << "\t tsnormal.z += 1;\n"; + text << "\t float3 tmp" << i << " = tex" << i << ".xyz * float3(-2, -2, 2) + float3(1, 1, -1);\n"; + text << "\t tsnormal = normalize(tsnormal * dot(tsnormal, tmp" << i << ") - tmp" << i << " * tsnormal.z);\n"; + } } + text << "\t l_eye_normal.xyz *= tsnormal.z;\n"; + text << "\t l_eye_normal.xyz += l_tangent * tsnormal.x;\n"; + text << "\t l_eye_normal.xyz += l_binormal * tsnormal.y;\n"; } - if (_need_eye_normal) { + if (need_eye_normal) { text << "\t // Correct the surface normal for interpolation effects\n"; text << "\t l_eye_normal.xyz = normalize(l_eye_normal.xyz);\n"; } - if (_out_aux_normal) { + if (key._outputs & AuxBitplaneAttrib::ABO_aux_normal) { text << "\t // Output the camera-space surface normal\n"; text << "\t o_aux.rgb = (l_eye_normal.xyz*0.5) + float3(0.5,0.5,0.5);\n"; } - if (_lighting) { + if (key._lighting) { text << "\t // Begin view-space light calculations\n"; - text << "\t float ldist,lattenv,langle;\n"; + text << "\t float ldist,lattenv,langle,lshad;\n"; text << "\t float4 lcolor,lspec,lpoint,latten,ldir,leye;\n"; text << "\t float3 lvec,lhalf;\n"; - if (_shadows && _auto_shadow_on) { - text << "\t float lshad;\n"; + if (key._have_separate_ambient) { + text << "\t float4 tot_ambient = float4(0,0,0,0);\n"; } - if (_separate_ambient_diffuse) { - if (_have_ambient) { - text << "\t float4 tot_ambient = float4(0,0,0,0);\n"; - } - if (_have_diffuse) { - text << "\t float4 tot_diffuse = float4(0,0,0,0);\n"; - } - } else { - if (_have_ambient || _have_diffuse) { - text << "\t float4 tot_diffuse = float4(0,0,0,0);\n"; - } - } - if (_have_specular) { + text << "\t float4 tot_diffuse = float4(0,0,0,0);\n"; + if (have_specular) { text << "\t float4 tot_specular = float4(0,0,0,0);\n"; - if (_material->has_specular()) { + if (key._material_flags & Material::F_specular) { text << "\t float shininess = attr_material[3].w;\n"; } else { text << "\t float shininess = 50; // no shininess specified, using default\n"; } } - if (_separate_ambient_diffuse && _have_ambient) { + if (key._have_separate_ambient) { text << "\t tot_ambient += attr_ambient;\n"; - } else if(_have_diffuse) { + } else { text << "\t tot_diffuse += attr_ambient;\n"; } } - for (size_t i = 0; i < _lights.size(); ++i) { - if (_lights[i]->is_of_type(DirectionalLight::get_class_type())) { + for (size_t i = 0; i < key._lights.size(); ++i) { + const ShaderKey::LightInfo &light = key._lights[i]; + if (light._type.is_derived_from(DirectionalLight::get_class_type())) { text << "\t // Directional Light " << i << "\n"; - text << "\t lcolor = dlight_light" << i << "_rel_view[0];\n"; - text << "\t lspec = dlight_light" << i << "_rel_view[1];\n"; - text << "\t lvec = dlight_light" << i << "_rel_view[2].xyz;\n"; + text << "\t lcolor = attr_light" << i << "[0];\n"; + if (light._flags & ShaderKey::LF_has_specular_color) { + text << "\t lspec = attr_lspec" << i << ";\n"; + } else { + text << "\t lspec = lcolor;\n"; + } + text << "\t lvec = attr_light" << i << "[3].xyz;\n"; text << "\t lcolor *= saturate(dot(l_eye_normal.xyz, lvec.xyz));\n"; - if (_shadows && _lights[i]->_shadow_caster && _auto_shadow_on) { + if (light._flags & ShaderKey::LF_has_shadows) { if (_use_shadow_filter) { - text << "\t lshad = shadow2DProj(shadow_light" << i << ", l_lightcoord" << i << ").r;\n"; + text << "\t lshad = shadow2DProj(shadow_" << i << ", l_lightcoord" << i << ").r;\n"; } else { - text << "\t lshad = tex2Dproj(shadow_light" << i << ", l_lightcoord" << i << ").r > l_lightcoord" << i << ".z / l_lightcoord" << i << ".w;\n"; + text << "\t lshad = tex2Dproj(shadow_" << i << ", l_lightcoord" << i << ").r > l_lightcoord" << i << ".z / l_lightcoord" << i << ".w;\n"; } text << "\t lcolor *= lshad;\n"; text << "\t lspec *= lshad;\n"; } - if (_have_diffuse) { - text << "\t tot_diffuse += lcolor;\n"; - } - if (_have_specular) { - if (_material->get_local()) { + text << "\t tot_diffuse += lcolor;\n"; + if (have_specular) { + if (key._material_flags & Material::F_local) { text << "\t lhalf = normalize(lvec - normalize(l_eye_position.xyz));\n"; } else { - text << "\t lhalf = dlight_light" << i << "_rel_view[3].xyz;\n"; + text << "\t lhalf = normalize(lvec - float3(0, 1, 0));\n"; } text << "\t lspec *= pow(saturate(dot(l_eye_normal.xyz, lhalf)), shininess);\n"; text << "\t tot_specular += lspec;\n"; } - } else if (_lights[i]->is_of_type(PointLight::get_class_type())) { + } else if (light._type.is_derived_from(PointLight::get_class_type())) { text << "\t // Point Light " << i << "\n"; - text << "\t lcolor = plight_light" << i << "_rel_view[0];\n"; - text << "\t lspec = plight_light" << i << "_rel_view[1];\n"; - text << "\t lpoint = plight_light" << i << "_rel_view[2];\n"; - text << "\t latten = plight_light" << i << "_rel_view[3];\n"; + text << "\t lcolor = attr_light" << i << "[0];\n"; + if (light._flags & ShaderKey::LF_has_specular_color) { + text << "\t lspec = attr_lspec" << i << ";\n"; + } else { + text << "\t lspec = lcolor;\n"; + } + text << "\t latten = attr_light" << i << "[1];\n"; + text << "\t lpoint = attr_light" << i << "[3];\n"; text << "\t lvec = lpoint.xyz - l_eye_position.xyz;\n"; text << "\t ldist = length(lvec);\n"; text << "\t lvec /= ldist;\n"; + if (light._type.is_derived_from(SphereLight::get_class_type())) { + text << "\t ldist = max(ldist, attr_light" << i << "[2].w);\n"; + } text << "\t lattenv = 1/(latten.x + latten.y*ldist + latten.z*ldist*ldist);\n"; text << "\t lcolor *= lattenv * saturate(dot(l_eye_normal.xyz, lvec));\n"; - if (_shadows && _lights[i]->_shadow_caster && _auto_shadow_on) { + if (light._flags & ShaderKey::LF_has_shadows) { text << "\t ldist = max(abs(l_lightcoord" << i << ".x), max(abs(l_lightcoord" << i << ".y), abs(l_lightcoord" << i << ".z)));\n"; text << "\t ldist = ((latten.w+lpoint.w)/(latten.w-lpoint.w))+((-2*latten.w*lpoint.w)/(ldist * (latten.w-lpoint.w)));\n"; - text << "\t lshad = texCUBE(shadow_light" << i << ", l_lightcoord" << i << ".xyz).r >= ldist * 0.5 + 0.5;\n"; + text << "\t lshad = texCUBE(shadow_" << i << ", l_lightcoord" << i << ".xyz).r >= ldist * 0.5 + 0.5;\n"; text << "\t lcolor *= lshad;\n"; text << "\t lspec *= lshad;\n"; } - if (_have_diffuse) { - text << "\t tot_diffuse += lcolor;\n"; - } - if (_have_specular) { - if (_material->get_local()) { + text << "\t tot_diffuse += lcolor;\n"; + if (have_specular) { + if (key._material_flags & Material::F_local) { text << "\t lhalf = normalize(lvec - normalize(l_eye_position.xyz));\n"; } else { text << "\t lhalf = normalize(lvec - float3(0, 1, 0));\n"; @@ -1111,13 +1245,17 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << "\t lspec *= pow(saturate(dot(l_eye_normal.xyz, lhalf)), shininess);\n"; text << "\t tot_specular += lspec;\n"; } - } else if (_lights[i]->is_of_type(Spotlight::get_class_type())) { + } else if (light._type.is_derived_from(Spotlight::get_class_type())) { text << "\t // Spot Light " << i << "\n"; - text << "\t lcolor = slight_light" << i << "_rel_view[0];\n"; - text << "\t lspec = slight_light" << i << "_rel_view[1];\n"; - text << "\t lpoint = slight_light" << i << "_rel_view[2];\n"; - text << "\t ldir = slight_light" << i << "_rel_view[3];\n"; - text << "\t latten = satten_light" << i << ";\n"; + text << "\t lcolor = attr_light" << i << "[0];\n"; + if (light._flags & ShaderKey::LF_has_specular_color) { + text << "\t lspec = attr_lspec" << i << ";\n"; + } else { + text << "\t lspec = lcolor;\n"; + } + text << "\t latten = attr_light" << i << "[1];\n"; + text << "\t ldir = attr_light" << i << "[2];\n"; + text << "\t lpoint = attr_light" << i << "[3];\n"; text << "\t lvec = lpoint.xyz - l_eye_position.xyz;\n"; text << "\t ldist = length(lvec);\n"; text << "\t lvec /= ldist;\n"; @@ -1126,21 +1264,19 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << "\t lattenv *= pow(langle, latten.w);\n"; text << "\t if (langle < ldir.w) lattenv = 0;\n"; text << "\t lcolor *= lattenv * saturate(dot(l_eye_normal.xyz, lvec));\n"; - if (_shadows && _lights[i]->_shadow_caster && _auto_shadow_on) { + if (light._flags & ShaderKey::LF_has_shadows) { if (_use_shadow_filter) { - text << "\t lshad = shadow2DProj(shadow_light" << i << ", l_lightcoord" << i << ").r;\n"; + text << "\t lshad = shadow2DProj(shadow_" << i << ", l_lightcoord" << i << ").r;\n"; } else { - text << "\t lshad = tex2Dproj(shadow_light" << i << ", l_lightcoord" << i << ").r > l_lightcoord" << i << ".z / l_lightcoord" << i << ".w;\n"; + text << "\t lshad = tex2Dproj(shadow_" << i << ", l_lightcoord" << i << ").r > l_lightcoord" << i << ".z / l_lightcoord" << i << ".w;\n"; } text << "\t lcolor *= lshad;\n"; text << "\t lspec *= lshad;\n"; } - if (_have_diffuse) { - text << "\t tot_diffuse += lcolor;\n"; - } - if (_have_specular) { - if (_material->get_local()) { + text << "\t tot_diffuse += lcolor;\n"; + if (have_specular) { + if (key._material_flags & Material::F_local) { text << "\t lhalf = normalize(lvec - normalize(l_eye_position.xyz));\n"; } else { text << "\t lhalf = normalize(lvec - float3(0,1,0));\n"; @@ -1151,14 +1287,13 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } } } - if (_lighting) { - const LightRampAttrib *light_ramp = DCAST(LightRampAttrib, rs->get_attrib_def(LightRampAttrib::get_class_slot())); - if (_auto_ramp_on && _have_diffuse) { - switch (light_ramp->get_mode()) { + if (key._lighting) { + if (key._light_ramp != nullptr) { + switch (key._light_ramp->get_mode()) { case LightRampAttrib::LRT_single_threshold: { - PN_stdfloat t = light_ramp->get_threshold(0); - PN_stdfloat l0 = light_ramp->get_level(0); + PN_stdfloat t = key._light_ramp->get_threshold(0); + PN_stdfloat l0 = key._light_ramp->get_level(0); text << "\t // Single-threshold light ramp\n"; text << "\t float lr_in = dot(tot_diffuse.rgb, float3(0.33,0.34,0.33));\n"; text << "\t float lr_scale = (lr_in < " << t << ") ? 0.0 : (" << l0 << "/lr_in);\n"; @@ -1167,10 +1302,10 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } case LightRampAttrib::LRT_double_threshold: { - PN_stdfloat t0 = light_ramp->get_threshold(0); - PN_stdfloat t1 = light_ramp->get_threshold(1); - PN_stdfloat l0 = light_ramp->get_level(0); - PN_stdfloat l1 = light_ramp->get_level(1); + PN_stdfloat t0 = key._light_ramp->get_threshold(0); + PN_stdfloat t1 = key._light_ramp->get_threshold(1); + PN_stdfloat l0 = key._light_ramp->get_level(0); + PN_stdfloat l1 = key._light_ramp->get_level(1); text << "\t // Double-threshold light ramp\n"; text << "\t float lr_in = dot(tot_diffuse.rgb, float3(0.33,0.34,0.33));\n"; text << "\t float lr_out = 0.0;\n"; @@ -1184,100 +1319,93 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } } text << "\t // Begin view-space light summation\n"; - if (_have_emission) { - if (_map_index_glow >= 0 && _auto_glow_on) { - text << "\t result = attr_material[2] * saturate(2 * (tex" << _map_index_glow << ".a - 0.5));\n"; + if (key._material_flags & Material::F_emission) { + if (key._texture_flags & ShaderKey::TF_map_glow) { + text << "\t result = attr_material[2] * saturate(2 * (tex" << map_index_glow << ".a - 0.5));\n"; } else { text << "\t result = attr_material[2];\n"; } } else { - if (_map_index_glow >= 0 && _auto_glow_on) { - text << "\t result = saturate(2 * (tex" << _map_index_glow << ".a - 0.5));\n"; + if (key._texture_flags & ShaderKey::TF_map_glow) { + text << "\t result = saturate(2 * (tex" << map_index_glow << ".a - 0.5));\n"; } else { text << "\t result = float4(0,0,0,0);\n"; } } - if ((_have_ambient)&&(_separate_ambient_diffuse)) { - if (_material->has_ambient()) { + if (key._have_separate_ambient) { + if (key._material_flags & Material::F_ambient) { text << "\t result += tot_ambient * attr_material[0];\n"; - } else if (_vertex_colors) { + } else if (key._color_type == ColorAttrib::T_vertex) { text << "\t result += tot_ambient * l_color;\n"; - } else if (_flat_colors) { + } else if (key._color_type == ColorAttrib::T_flat) { text << "\t result += tot_ambient * attr_color;\n"; } else { text << "\t result += tot_ambient;\n"; } } - if (_have_diffuse) { - if (_material->has_diffuse()) { - text << "\t result += tot_diffuse * attr_material[1];\n"; - } else if (_vertex_colors) { - text << "\t result += tot_diffuse * l_color;\n"; - } else if (_flat_colors) { - text << "\t result += tot_diffuse * attr_color;\n"; - } else { - text << "\t result += tot_diffuse;\n"; - } + if (key._material_flags & Material::F_diffuse) { + text << "\t result += tot_diffuse * attr_material[1];\n"; + } else if (key._color_type == ColorAttrib::T_vertex) { + text << "\t result += tot_diffuse * l_color;\n"; + } else if (key._color_type == ColorAttrib::T_flat) { + text << "\t result += tot_diffuse * attr_color;\n"; + } else { + text << "\t result += tot_diffuse;\n"; } - if (light_ramp->get_mode() == LightRampAttrib::LRT_default) { + if (key._light_ramp == nullptr || + key._light_ramp->get_mode() == LightRampAttrib::LRT_default) { text << "\t result = saturate(result);\n"; } text << "\t // End view-space light calculations\n"; // Combine in alpha, which bypasses lighting calculations. Use of lerp // here is a workaround for a radeon driver bug. - if (_calc_primary_alpha) { - if (_vertex_colors) { + if (key._calc_primary_alpha) { + if (key._color_type == ColorAttrib::T_vertex) { text << "\t result.a = l_color.a;\n"; - } else if (_flat_colors) { + } else if (key._color_type == ColorAttrib::T_flat) { text << "\t result.a = attr_color.a;\n"; } else { text << "\t result.a = 1;\n"; } } } else { - if (_vertex_colors) { + if (key._color_type == ColorAttrib::T_vertex) { text << "\t result = l_color;\n"; - } else if (_flat_colors) { + } else if (key._color_type == ColorAttrib::T_flat) { text << "\t result = attr_color;\n"; } else { text << "\t result = float4(1, 1, 1, 1);\n"; } } - // Loop first to see if something is using primary_color or - // last_saved_result. - bool have_saved_result = false; - bool have_primary_color = false; - for (int i=0; i<_num_textures; i++) { - TextureStage *stage = texture->get_on_stage(i); - if (stage->get_mode() != TextureStage::M_combine) continue; - if (stage->uses_primary_color() && !have_primary_color) { - text << "\t float4 primary_color = result;\n"; - have_primary_color = true; - } - if (stage->uses_last_saved_result() && !have_saved_result) { - text << "\t float4 last_saved_result = result;\n"; - have_saved_result = true; - } + // Apply the color scale. + text << "\t result *= attr_colorscale;\n"; + + // Store these if any stages will use it. + if (key._texture_flags & ShaderKey::TF_uses_primary_color) { + text << "\t float4 primary_color = result;\n"; + } + if (key._texture_flags & ShaderKey::TF_uses_last_saved_result) { + text << "\t float4 last_saved_result = result;\n"; } // Now loop through the textures to compose our magic blending formulas. - for (int i=0; i<_num_textures; i++) { - TextureStage *stage = texture->get_on_stage(i); - switch (stage->get_mode()) { - case TextureStage::M_modulate: { - int num_components = texture->get_on_texture(texture->get_on_stage(i))->get_num_components(); + for (size_t i = 0; i < key._textures.size(); ++i) { + const ShaderKey::TextureInfo &tex = key._textures[i]; + TextureStage::CombineMode combine_rgb, combine_alpha; - if (num_components == 1) { - text << "\t result.a *= tex" << i << ".a;\n"; - } else if (num_components == 3) { - text << "\t result.rgb *= tex" << i << ".rgb;\n"; - } else { + switch (tex._mode) { + case TextureStage::M_modulate: + if ((tex._flags & ShaderKey::TF_has_rgb) != 0 && + (tex._flags & ShaderKey::TF_has_alpha) != 0) { text << "\t result.rgba *= tex" << i << ".rgba;\n"; + } else if (tex._flags & ShaderKey::TF_has_alpha) { + text << "\t result.a *= tex" << i << ".a;\n"; + } else if (tex._flags & ShaderKey::TF_has_rgb) { + text << "\t result.rgb *= tex" << i << ".rgb;\n"; } - - break; } + break; case TextureStage::M_modulate_glow: case TextureStage::M_modulate_gloss: // in the case of glow or spec we currently see the specularity evenly @@ -1290,59 +1418,66 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { case TextureStage::M_decal: text << "\t result.rgb = lerp(result, tex" << i << ", tex" << i << ".a).rgb;\n"; break; - case TextureStage::M_blend: { - LVecBase4 c = stage->get_color(); - text << "\t result.rgb = lerp(result, tex" << i << " * float4(" - << c[0] << ", " << c[1] << ", " << c[2] << ", " << c[3] << "), tex" << i << ".r).rgb;\n"; - break; } + case TextureStage::M_blend: + text << "\t result.rgb = lerp(result.rgb, texcolor_" << i << ".rgb, tex" << i << ".rgb);\n"; + if (key._calc_primary_alpha) { + text << "\t result.a *= tex" << i << ".a;\n"; + } + break; case TextureStage::M_replace: text << "\t result = tex" << i << ";\n"; break; case TextureStage::M_add: text << "\t result.rgb += tex" << i << ".rgb;\n"; - if (_calc_primary_alpha) { + if (key._calc_primary_alpha) { text << "\t result.a *= tex" << i << ".a;\n"; } break; case TextureStage::M_combine: - text << "\t result.rgb = "; - if (stage->get_combine_rgb_mode() != TextureStage::CM_undefined) { - text << combine_mode_as_string(stage, stage->get_combine_rgb_mode(), false, i); + combine_rgb = (TextureStage::CombineMode)((tex._flags & ShaderKey::TF_COMBINE_RGB_MODE_MASK) >> ShaderKey::TF_COMBINE_RGB_MODE_SHIFT); + combine_alpha = (TextureStage::CombineMode)((tex._flags & ShaderKey::TF_COMBINE_ALPHA_MODE_MASK) >> ShaderKey::TF_COMBINE_ALPHA_MODE_SHIFT); + if (combine_rgb == TextureStage::CM_dot3_rgba) { + text << "\t result = "; + text << combine_mode_as_string(tex, combine_rgb, false, i); + text << ";\n"; } else { - text << "tex" << i << ".rgb"; + text << "\t result.rgb = "; + text << combine_mode_as_string(tex, combine_rgb, false, i); + text << ";\n\t result.a = "; + text << combine_mode_as_string(tex, combine_alpha, false, i); + text << ";\n"; } - if (stage->get_rgb_scale() != 1) { - text << " * " << stage->get_rgb_scale(); + if (tex._flags & ShaderKey::TF_rgb_scale_2) { + text << "\t result.rgb *= 2;\n"; } - text << ";\n\t result.a = "; - if (stage->get_combine_alpha_mode() != TextureStage::CM_undefined) { - text << combine_mode_as_string(stage, stage->get_combine_alpha_mode(), true, i); - } else { - text << "tex" << i << ".a"; + if (tex._flags & ShaderKey::TF_rgb_scale_4) { + text << "\t result.rgb *= 4;\n"; } - if (stage->get_alpha_scale() != 1) { - text << " * " << stage->get_alpha_scale(); + if (tex._flags & ShaderKey::TF_alpha_scale_2) { + text << "\t result.a *= 2;\n"; + } + if (tex._flags & ShaderKey::TF_alpha_scale_4) { + text << "\t result.a *= 4;\n"; } - text << ";\n"; break; case TextureStage::M_blend_color_scale: - text << "\t result.rgb = lerp(result, tex" << i << " * attr_colorscale, tex" << i << ".r).rgb;\n"; + text << "\t result.rgb = lerp(result.rgb, texcolor_" << i << ".rgb * attr_colorscale.rgb, tex" << i << ".rgb);\n"; + if (key._calc_primary_alpha) { + text << "\t result.a *= texcolor_" << i << ".a * attr_colorscale.a;\n"; + } break; default: break; } - if (stage->get_saved_result() && have_saved_result) { + if (tex._flags & ShaderKey::TF_saved_result) { text << "\t last_saved_result = result;\n"; } } - // Apply the color scale. - text << "\t result *= attr_colorscale;\n"; - if (_subsume_alpha_test) { - const AlphaTestAttrib *alpha_test = DCAST(AlphaTestAttrib, rs->get_attrib_def(AlphaTestAttrib::get_class_slot())); + if (key._alpha_test_mode != RenderAttrib::M_none) { text << "\t // Shader includes alpha test:\n"; - double ref = alpha_test->get_reference_alpha(); - switch (alpha_test->get_mode()) { + double ref = key._alpha_test_ref; + switch (key._alpha_test_mode) { case RenderAttrib::M_never: text << "\t discard;\n"; break; @@ -1366,40 +1501,36 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { break; case RenderAttrib::M_none: case RenderAttrib::M_always: - default: break; } } - if (_out_primary_glow) { - if (_map_index_glow >= 0 && _auto_glow_on) { - text << "\t result.a = tex" << _map_index_glow << ".a;\n"; + if (key._outputs & AuxBitplaneAttrib::ABO_glow) { + if (key._texture_flags & ShaderKey::TF_map_glow) { + text << "\t result.a = tex" << map_index_glow << ".a;\n"; } else { text << "\t result.a = 0.5;\n"; } } - if (_out_aux_glow) { - if (_map_index_glow >= 0 && _auto_glow_on) { - text << "\t o_aux.a = tex" << _map_index_glow << ".a;\n"; + if (key._outputs & AuxBitplaneAttrib::ABO_aux_glow) { + if (key._texture_flags & ShaderKey::TF_map_glow) { + text << "\t o_aux.a = tex" << map_index_glow << ".a;\n"; } else { text << "\t o_aux.a = 0.5;\n"; } } - if (_lighting) { - if (_have_specular) { - if (_material->has_specular()) { - text << "\t tot_specular *= attr_material[3];\n"; - } - if (_map_index_gloss >= 0 && _auto_gloss_on) { - text << "\t tot_specular *= tex" << _map_index_gloss << ".a;\n"; - } - text << "\t result.rgb = result.rgb + tot_specular.rgb;\n"; + if (have_specular) { + if (key._material_flags & Material::F_specular) { + text << "\t tot_specular *= attr_material[3];\n"; } + if (key._texture_flags & ShaderKey::TF_map_gloss) { + text << "\t tot_specular *= tex" << map_index_gloss << ".a;\n"; + } + text << "\t result.rgb = result.rgb + tot_specular.rgb;\n"; } - if (_auto_ramp_on) { - const LightRampAttrib *light_ramp = DCAST(LightRampAttrib, rs->get_attrib_def(LightRampAttrib::get_class_slot())); - switch (light_ramp->get_mode()) { + if (key._light_ramp != nullptr) { + switch (key._light_ramp->get_mode()) { case LightRampAttrib::LRT_hdr0: text << "\t result.rgb = (result*result*result + result*result + result) / (result*result*result + result*result + result + 1);\n"; break; @@ -1414,11 +1545,9 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } // Apply fog. - if (_fog) { - const FogAttrib *fog_attr = DCAST(FogAttrib, rs->get_attrib_def(FogAttrib::get_class_slot())); - Fog *fog = fog_attr->get_fog(); - - switch (fog->get_mode()) { + if (key._fog_mode != 0) { + Fog::Mode fog_mode = (Fog::Mode)(key._fog_mode - 1); + switch (fog_mode) { case Fog::M_linear: text << "\t result.rgb = lerp(attr_fogcolor.rgb, result.rgb, saturate((attr_fog.z - l_hpos.z) * attr_fog.w));\n"; break; @@ -1434,78 +1563,86 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { // The multiply is a workaround for a radeon driver bug. It's annoying as // heck, since it produces an extra instruction. text << "\t o_color = result * 1.000001;\n"; - if (_subsume_alpha_test) { + if (key._alpha_test_mode != RenderAttrib::M_none) { text << "\t // Shader subsumes normal alpha test.\n"; } - if (_disable_alpha_write) { + if (key._disable_alpha_write) { text << "\t // Shader disables alpha write.\n"; } text << "}\n"; // Insert the shader into the shader attrib. - CPT(RenderAttrib) shattr = create_shader_attrib(text.str()); - if (_subsume_alpha_test) { + PT(Shader) shader = Shader::make(text.str(), Shader::SL_Cg); + nassertr(shader != nullptr, nullptr); + + CPT(RenderAttrib) shattr = ShaderAttrib::make(shader); + if (key._alpha_test_mode != RenderAttrib::M_none) { shattr = DCAST(ShaderAttrib, shattr)->set_flag(ShaderAttrib::F_subsume_alpha_test, true); } - if (_disable_alpha_write) { + if (key._disable_alpha_write) { shattr = DCAST(ShaderAttrib, shattr)->set_flag(ShaderAttrib::F_disable_alpha_write, true); } - clear_analysis(); + reset_register_allocator(); - return DCAST(ShaderAttrib, shattr); + + CPT(ShaderAttrib) attr = DCAST(ShaderAttrib, shattr); + _generated_shaders[key] = attr; + return attr; } /** * This 'synthesizes' a combine mode into a string. */ -const string ShaderGenerator:: -combine_mode_as_string(CPT(TextureStage) stage, TextureStage::CombineMode c_mode, bool alpha, short texindex) { +string ShaderGenerator:: +combine_mode_as_string(const ShaderKey::TextureInfo &info, TextureStage::CombineMode c_mode, bool alpha, short texindex) { ostringstream text; switch (c_mode) { - case TextureStage::CM_modulate: - text << combine_source_as_string(stage, 0, alpha, alpha, texindex); - text << " * "; - text << combine_source_as_string(stage, 1, alpha, alpha, texindex); - break; - case TextureStage::CM_add: - text << combine_source_as_string(stage, 0, alpha, alpha, texindex); - text << " + "; - text << combine_source_as_string(stage, 1, alpha, alpha, texindex); - break; - case TextureStage::CM_add_signed: - text << combine_source_as_string(stage, 0, alpha, alpha, texindex); - text << " + "; - text << combine_source_as_string(stage, 1, alpha, alpha, texindex); - if (alpha) { - text << " - 0.5"; - } else { - text << " - float3(0.5, 0.5, 0.5)"; - } - break; - case TextureStage::CM_interpolate: - text << "lerp("; - text << combine_source_as_string(stage, 1, alpha, alpha, texindex); - text << ", "; - text << combine_source_as_string(stage, 0, alpha, alpha, texindex); - text << ", "; - text << combine_source_as_string(stage, 2, alpha, true, texindex); - text << ")"; - break; - case TextureStage::CM_subtract: - text << combine_source_as_string(stage, 0, alpha, alpha, texindex); - text << " + "; - text << combine_source_as_string(stage, 1, alpha, alpha, texindex); - break; - case TextureStage::CM_dot3_rgb: - pgraphnodes_cat.error() << "TextureStage::CombineMode DOT3_RGB not yet supported in per-pixel mode.\n"; - break; - case TextureStage::CM_dot3_rgba: - pgraphnodes_cat.error() << "TextureStage::CombineMode DOT3_RGBA not yet supported in per-pixel mode.\n"; - break; - case TextureStage::CM_replace: - default: // Not sure if this is correct as default value. - text << combine_source_as_string(stage, 0, alpha, alpha, texindex); - break; + case TextureStage::CM_modulate: + text << combine_source_as_string(info, 0, alpha, texindex); + text << " * "; + text << combine_source_as_string(info, 1, alpha, texindex); + break; + case TextureStage::CM_add: + text << combine_source_as_string(info, 0, alpha, texindex); + text << " + "; + text << combine_source_as_string(info, 1, alpha, texindex); + break; + case TextureStage::CM_add_signed: + text << combine_source_as_string(info, 0, alpha, texindex); + text << " + "; + text << combine_source_as_string(info, 1, alpha, texindex); + if (alpha) { + text << " - 0.5"; + } else { + text << " - float3(0.5, 0.5, 0.5)"; + } + break; + case TextureStage::CM_interpolate: + text << "lerp("; + text << combine_source_as_string(info, 1, alpha, texindex); + text << ", "; + text << combine_source_as_string(info, 0, alpha, texindex); + text << ", "; + text << combine_source_as_string(info, 2, alpha, texindex); + text << ")"; + break; + case TextureStage::CM_subtract: + text << combine_source_as_string(info, 0, alpha, texindex); + text << " - "; + text << combine_source_as_string(info, 1, alpha, texindex); + break; + case TextureStage::CM_dot3_rgb: + case TextureStage::CM_dot3_rgba: + text << "4 * dot("; + text << combine_source_as_string(info, 0, alpha, texindex); + text << " - float3(0.5), "; + text << combine_source_as_string(info, 1, alpha, texindex); + text << " - float3(0.5))"; + break; + case TextureStage::CM_replace: + default: // Not sure if this is correct as default value. + text << combine_source_as_string(info, 0, alpha, texindex); + break; } return text.str(); } @@ -1513,54 +1650,29 @@ combine_mode_as_string(CPT(TextureStage) stage, TextureStage::CombineMode c_mode /** * This 'synthesizes' a combine source into a string. */ -const string ShaderGenerator:: -combine_source_as_string(CPT(TextureStage) stage, short num, bool alpha, bool single_value, short texindex) { - TextureStage::CombineSource c_src = TextureStage::CS_undefined; - TextureStage::CombineOperand c_op = TextureStage::CO_undefined; - if (alpha) { - switch (num) { - case 0: - c_src = stage->get_combine_alpha_source0(); - c_op = stage->get_combine_alpha_operand0(); - break; - case 1: - c_src = stage->get_combine_alpha_source1(); - c_op = stage->get_combine_alpha_operand1(); - break; - case 2: - c_src = stage->get_combine_alpha_source2(); - c_op = stage->get_combine_alpha_operand2(); - break; - } +string ShaderGenerator:: +combine_source_as_string(const ShaderKey::TextureInfo &info, short num, bool alpha, short texindex) { + TextureStage::CombineSource c_src; + TextureStage::CombineOperand c_op; + if (!alpha) { + c_src = UNPACK_COMBINE_SRC(info._combine_rgb, num); + c_op = UNPACK_COMBINE_OP(info._combine_rgb, num); } else { - switch (num) { - case 0: - c_src = stage->get_combine_rgb_source0(); - c_op = stage->get_combine_rgb_operand0(); - break; - case 1: - c_src = stage->get_combine_rgb_source1(); - c_op = stage->get_combine_rgb_operand1(); - break; - case 2: - c_src = stage->get_combine_rgb_source2(); - c_op = stage->get_combine_rgb_operand2(); - break; - } + c_src = UNPACK_COMBINE_SRC(info._combine_alpha, num); + c_op = UNPACK_COMBINE_OP(info._combine_alpha, num); } ostringstream csource; if (c_op == TextureStage::CO_one_minus_src_color || c_op == TextureStage::CO_one_minus_src_alpha) { - csource << "1.0f - "; + csource << "saturate(1.0f - "; } switch (c_src) { case TextureStage::CS_texture: csource << "tex" << texindex; break; - case TextureStage::CS_constant: { - LVecBase4 c = stage->get_color(); - csource << "float4(" << c[0] << ", " << c[1] << ", " << c[2] << ", " << c[3] << ")"; - break; } + case TextureStage::CS_constant: + csource << "texcolor_" << texindex; + break; case TextureStage::CS_primary_color: csource << "primary_color"; break; @@ -1576,16 +1688,15 @@ combine_source_as_string(CPT(TextureStage) stage, short num, bool alpha, bool si case TextureStage::CS_undefined: break; } + if (c_op == TextureStage::CO_one_minus_src_color || + c_op == TextureStage::CO_one_minus_src_alpha) { + csource << ")"; + } if (c_op == TextureStage::CO_src_color || c_op == TextureStage::CO_one_minus_src_color) { - if (single_value) { - // Let's take the red channel. - csource << ".r"; - } else { - csource << ".rgb"; - } + csource << ".rgb"; } else { csource << ".a"; - if (!single_value) { + if (!alpha) { // Dunno if it's legal in the FPP at all, but let's just allow it. return "float3(" + csource.str() + ")"; } @@ -1596,7 +1707,7 @@ combine_source_as_string(CPT(TextureStage) stage, short num, bool alpha, bool si /** * Returns 1D, 2D, 3D or CUBE, depending on the given texture type. */ -const string ShaderGenerator:: +const char *ShaderGenerator:: texture_type_as_string(Texture::TextureType ttype) { switch (ttype) { case Texture::TT_1d_texture: @@ -1620,4 +1731,169 @@ texture_type_as_string(Texture::TextureType ttype) { } } +/** + * Initializes the ShaderKey to the empty state. + */ +ShaderGenerator::ShaderKey:: +ShaderKey() : + _color_type(ColorAttrib::T_vertex), + _material_flags(0), + _texture_flags(0), + _lighting(false), + _have_separate_ambient(false), + _fog_mode(0), + _outputs(0), + _calc_primary_alpha(false), + _disable_alpha_write(false), + _alpha_test_mode(RenderAttrib::M_none), + _alpha_test_ref(0.0), + _num_clip_planes(0), + _light_ramp(nullptr) { +} + +/** + * Returns true if this ShaderKey sorts less than the other one. This is an + * arbitrary, but consistent ordering. + */ +bool ShaderGenerator::ShaderKey:: +operator < (const ShaderKey &other) const { + if (_anim_spec != other._anim_spec) { + return _anim_spec < other._anim_spec; + } + if (_color_type != other._color_type) { + return _color_type < other._color_type; + } + if (_material_flags != other._material_flags) { + return _material_flags < other._material_flags; + } + if (_texture_flags != other._texture_flags) { + return _texture_flags < other._texture_flags; + } + if (_textures.size() != other._textures.size()) { + return _textures.size() < other._textures.size(); + } + for (size_t i = 0; i < _textures.size(); ++i) { + const ShaderKey::TextureInfo &tex = _textures[i]; + const ShaderKey::TextureInfo &other_tex = other._textures[i]; + if (tex._texcoord_name != other_tex._texcoord_name) { + return tex._texcoord_name < other_tex._texcoord_name; + } + if (tex._type != other_tex._type) { + return tex._type < other_tex._type; + } + if (tex._mode != other_tex._mode) { + return tex._mode < other_tex._mode; + } + if (tex._gen_mode != other_tex._gen_mode) { + return tex._gen_mode < other_tex._gen_mode; + } + if (tex._flags != other_tex._flags) { + return tex._flags < other_tex._flags; + } + if (tex._combine_rgb != other_tex._combine_rgb) { + return tex._combine_rgb < other_tex._combine_rgb; + } + if (tex._combine_alpha != other_tex._combine_alpha) { + return tex._combine_alpha < other_tex._combine_alpha; + } + } + if (_lights.size() != other._lights.size()) { + return _lights.size() < other._lights.size(); + } + for (size_t i = 0; i < _lights.size(); ++i) { + const ShaderKey::LightInfo &light = _lights[i]; + const ShaderKey::LightInfo &other_light = other._lights[i]; + if (light._type != other_light._type) { + return light._type < other_light._type; + } + if (light._flags != other_light._flags) { + return light._flags < other_light._flags; + } + } + if (_lighting != other._lighting) { + return _lighting < other._lighting; + } + if (_have_separate_ambient != other._have_separate_ambient) { + return _have_separate_ambient < other._have_separate_ambient; + } + if (_fog_mode != other._fog_mode) { + return _fog_mode < other._fog_mode; + } + if (_outputs != other._outputs) { + return _outputs < other._outputs; + } + if (_calc_primary_alpha != other._calc_primary_alpha) { + return _calc_primary_alpha < other._calc_primary_alpha; + } + if (_disable_alpha_write != other._disable_alpha_write) { + return _disable_alpha_write < other._disable_alpha_write; + } + if (_alpha_test_mode != other._alpha_test_mode) { + return _alpha_test_mode < other._alpha_test_mode; + } + if (_alpha_test_ref != other._alpha_test_ref) { + return _alpha_test_ref < other._alpha_test_ref; + } + if (_num_clip_planes != other._num_clip_planes) { + return _num_clip_planes < other._num_clip_planes; + } + return _light_ramp < other._light_ramp; +} + +/** + * Returns true if this ShaderKey is equal to the other one. + */ +bool ShaderGenerator::ShaderKey:: +operator == (const ShaderKey &other) const { + if (_anim_spec != other._anim_spec) { + return false; + } + if (_color_type != other._color_type) { + return false; + } + if (_material_flags != other._material_flags) { + return false; + } + if (_texture_flags != other._texture_flags) { + return false; + } + if (_textures.size() != other._textures.size()) { + return false; + } + for (size_t i = 0; i < _textures.size(); ++i) { + const ShaderKey::TextureInfo &tex = _textures[i]; + const ShaderKey::TextureInfo &other_tex = other._textures[i]; + if (tex._texcoord_name != other_tex._texcoord_name || + tex._type != other_tex._type || + tex._mode != other_tex._mode || + tex._gen_mode != other_tex._gen_mode || + tex._flags != other_tex._flags || + tex._combine_rgb != other_tex._combine_rgb || + tex._combine_alpha != other_tex._combine_alpha) { + return false; + } + } + if (_lights.size() != other._lights.size()) { + return false; + } + for (size_t i = 0; i < _lights.size(); ++i) { + const ShaderKey::LightInfo &light = _lights[i]; + const ShaderKey::LightInfo &other_light = other._lights[i]; + if (light._type != other_light._type || + light._flags != other_light._flags) { + return false; + } + } + return _lighting == other._lighting + && _have_separate_ambient == other._have_separate_ambient + && _fog_mode == other._fog_mode + && _outputs == other._outputs + && _calc_primary_alpha == other._calc_primary_alpha + && _disable_alpha_write == other._disable_alpha_write + && _alpha_test_mode == other._alpha_test_mode + && _alpha_test_ref == other._alpha_test_ref + && _num_clip_planes == other._num_clip_planes + && _light_ramp == other._light_ramp; +} + #endif // HAVE_CG diff --git a/panda/src/pgraphnodes/shaderGenerator.h b/panda/src/pgraphnodes/shaderGenerator.h index 9564bd6120..66077c4af8 100644 --- a/panda/src/pgraphnodes/shaderGenerator.h +++ b/panda/src/pgraphnodes/shaderGenerator.h @@ -28,6 +28,11 @@ #include "renderState.h" #include "renderAttrib.h" +#include "colorAttrib.h" +#include "lightRampAttrib.h" +#include "texGenAttrib.h" +#include "textureAttrib.h" + class AmbientLight; class DirectionalLight; class PointLight; @@ -60,19 +65,15 @@ class GeomVertexAnimationSpec; */ class EXPCL_PANDA_PGRAPHNODES ShaderGenerator : public TypedReferenceCount { PUBLISHED: - ShaderGenerator(GraphicsStateGuardianBase *gsg, GraphicsOutputBase *host); + ShaderGenerator(const GraphicsStateGuardianBase *gsg); virtual ~ShaderGenerator(); virtual CPT(ShaderAttrib) synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim); -protected: - CPT(RenderAttrib) create_shader_attrib(const string &txt); - static const string combine_mode_as_string(CPT(TextureStage) stage, - TextureStage::CombineMode c_mode, bool alpha, short texindex); - static const string combine_source_as_string(CPT(TextureStage) stage, - short num, bool alpha, bool single_value, short texindex); - static const string texture_type_as_string(Texture::TextureType ttype); + void rehash_generated_shaders(); + void clear_generated_shaders(); +protected: // Shader register allocation: bool _use_generic_attr; @@ -84,76 +85,103 @@ protected: const char *alloc_vreg(); const char *alloc_freg(); + bool _use_shadow_filter; + // RenderState analysis information. Created by analyze_renderstate: CPT(RenderState) _state; - Material *_material; - int _num_textures; + struct ShaderKey { + ShaderKey(); + bool operator < (const ShaderKey &other) const; + bool operator == (const ShaderKey &other) const; + bool operator != (const ShaderKey &other) const { return !operator ==(other); } - pvector _lights; - pvector _lights_np; + GeomVertexAnimationSpec _anim_spec; + enum TextureFlags { + TF_has_rgb = 0x001, + TF_has_alpha = 0x002, + TF_has_texscale = 0x004, + TF_has_texmat = 0x008, + TF_saved_result = 0x010, + TF_map_normal = 0x020, + TF_map_height = 0x040, + TF_map_glow = 0x080, + TF_map_gloss = 0x100, + TF_uses_color = 0x200, + TF_uses_primary_color = 0x400, + TF_uses_last_saved_result = 0x800, - bool _vertex_colors; - bool _flat_colors; + TF_rgb_scale_2 = 0x1000, + TF_rgb_scale_4 = 0x2000, + TF_alpha_scale_2 = 0x4000, + TF_alpha_scale_4 = 0x8000, - bool _lighting; - bool _shadows; - bool _fog; + TF_COMBINE_RGB_MODE_SHIFT = 16, + TF_COMBINE_RGB_MODE_MASK = 0x0000f0000, + TF_COMBINE_ALPHA_MODE_SHIFT = 20, + TF_COMBINE_ALPHA_MODE_MASK = 0x000f00000, + }; - bool _have_ambient; - bool _have_diffuse; - bool _have_emission; - bool _have_specular; + ColorAttrib::Type _color_type; + int _material_flags; + int _texture_flags; - bool _separate_ambient_diffuse; + struct TextureInfo { + CPT_InternalName _texcoord_name; + Texture::TextureType _type; + TextureStage::Mode _mode; + TexGenAttrib::Mode _gen_mode; + int _flags; + uint16_t _combine_rgb; + uint16_t _combine_alpha; + }; + pvector _textures; - int _map_index_normal; - int _map_index_height; - int _map_index_glow; - int _map_index_gloss; - bool _map_height_in_alpha; + enum LightFlags { + LF_has_shadows = 1, + LF_has_specular_color = 2, + }; - bool _out_primary_glow; - bool _out_aux_normal; - bool _out_aux_glow; - bool _out_aux_any; + struct LightInfo { + TypeHandle _type; + int _flags; + }; + pvector _lights; + bool _lighting; + bool _have_separate_ambient; - bool _have_alpha_test; - bool _have_alpha_blend; - bool _calc_primary_alpha; - bool _subsume_alpha_test; - bool _disable_alpha_write; + int _fog_mode; - int _num_clip_planes; - bool _use_shadow_filter; + int _outputs; + bool _calc_primary_alpha; + bool _disable_alpha_write; + RenderAttrib::PandaCompareFunc _alpha_test_mode; + PN_stdfloat _alpha_test_ref; - bool _need_material_props; - bool _need_world_position; - bool _need_world_normal; - bool _need_eye_position; - bool _need_eye_normal; - bool _normalize_normals; - bool _auto_normal_on; - bool _auto_glow_on; - bool _auto_gloss_on; - bool _auto_ramp_on; - bool _auto_shadow_on; + int _num_clip_planes; - void analyze_renderstate(const RenderState *rs); - void clear_analysis(); + CPT(LightRampAttrib) _light_ramp; + }; - // This is not a PT() to prevent a circular reference. - GraphicsStateGuardianBase *_gsg; - GraphicsOutputBase *_host; + typedef phash_map GeneratedShaders; + GeneratedShaders _generated_shaders; + + void analyze_renderstate(ShaderKey &key, const RenderState *rs); + + static string combine_mode_as_string(const ShaderKey::TextureInfo &info, + TextureStage::CombineMode c_mode, bool alpha, short texindex); + static string combine_source_as_string(const ShaderKey::TextureInfo &info, + short num, bool alpha, short texindex); + static const char *texture_type_as_string(Texture::TextureType ttype); public: static TypeHandle get_class_type() { return _type_handle; } static void init_type() { - TypedObject::init_type(); + TypedReferenceCount::init_type(); register_type(_type_handle, "ShaderGenerator", - TypedObject::get_class_type()); + TypedReferenceCount::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); @@ -173,9 +201,9 @@ public: return _type_handle; } static void init_type() { - TypedObject::init_type(); + TypedReferenceCount::init_type(); register_type(_type_handle, "ShaderGenerator", - TypedObject::get_class_type()); + TypedReferenceCount::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); diff --git a/panda/src/pgraphnodes/sphereLight.h b/panda/src/pgraphnodes/sphereLight.h index 4e4c885f7c..7efddb9b50 100644 --- a/panda/src/pgraphnodes/sphereLight.h +++ b/panda/src/pgraphnodes/sphereLight.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDA_PGRAPHNODES SphereLight : public PointLight { PUBLISHED: - SphereLight(const string &name); + explicit SphereLight(const string &name); protected: SphereLight(const SphereLight ©); diff --git a/panda/src/pgraphnodes/spotlight.cxx b/panda/src/pgraphnodes/spotlight.cxx index 638f89f93c..99432a904b 100644 --- a/panda/src/pgraphnodes/spotlight.cxx +++ b/panda/src/pgraphnodes/spotlight.cxx @@ -65,9 +65,8 @@ fillin(DatagramIterator &scan, BamReader *manager) { */ Spotlight:: Spotlight(const string &name) : - LightLensNode(name), - _has_specular_color(false) -{ + LightLensNode(name) { + _lenses[0]._lens->set_interocular_distance(0); } /** @@ -77,7 +76,6 @@ Spotlight(const string &name) : Spotlight:: Spotlight(const Spotlight ©) : LightLensNode(copy), - _has_specular_color(copy._has_specular_color), _cycler(copy._cycler) { } diff --git a/panda/src/pgraphnodes/spotlight.h b/panda/src/pgraphnodes/spotlight.h index 6cc175fe61..c9a6962b07 100644 --- a/panda/src/pgraphnodes/spotlight.h +++ b/panda/src/pgraphnodes/spotlight.h @@ -79,8 +79,6 @@ private: CPT(RenderState) get_viz_state(); private: - bool _has_specular_color; - // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA_PGRAPHNODES CData : public CycleData { public: diff --git a/panda/src/pgraphnodes/switchNode.h b/panda/src/pgraphnodes/switchNode.h index eca88dd541..b2fefd7750 100644 --- a/panda/src/pgraphnodes/switchNode.h +++ b/panda/src/pgraphnodes/switchNode.h @@ -24,7 +24,7 @@ */ class EXPCL_PANDA_PGRAPHNODES SwitchNode : public SelectiveChildNode { PUBLISHED: - INLINE SwitchNode(const string &name); + INLINE explicit SwitchNode(const string &name); public: SwitchNode(const SwitchNode ©); diff --git a/panda/src/pgraphnodes/uvScrollNode.h b/panda/src/pgraphnodes/uvScrollNode.h index 8e59f335e6..10dff152b7 100644 --- a/panda/src/pgraphnodes/uvScrollNode.h +++ b/panda/src/pgraphnodes/uvScrollNode.h @@ -25,8 +25,8 @@ */ class EXPCL_PANDA_PGRAPH UvScrollNode : public PandaNode { PUBLISHED: - INLINE UvScrollNode(const string &name, PN_stdfloat u_speed, PN_stdfloat v_speed, PN_stdfloat w_speed, PN_stdfloat r_speed); - INLINE UvScrollNode(const string &name); + INLINE explicit UvScrollNode(const string &name, PN_stdfloat u_speed, PN_stdfloat v_speed, PN_stdfloat w_speed, PN_stdfloat r_speed); + INLINE explicit UvScrollNode(const string &name); protected: INLINE UvScrollNode(const UvScrollNode ©); diff --git a/panda/src/pgui/pgButton.h b/panda/src/pgui/pgButton.h index 494ed45428..ece2359762 100644 --- a/panda/src/pgui/pgButton.h +++ b/panda/src/pgui/pgButton.h @@ -28,7 +28,7 @@ */ class EXPCL_PANDA_PGUI PGButton : public PGItem { PUBLISHED: - PGButton(const string &name); + explicit PGButton(const string &name); virtual ~PGButton(); protected: @@ -73,6 +73,7 @@ PUBLISHED: INLINE static string get_click_prefix(); INLINE string get_click_event(const ButtonHandle &button) const; + MAKE_PROPERTY(click_prefix, get_click_prefix); private: typedef pset Buttons; diff --git a/panda/src/pgui/pgEntry.h b/panda/src/pgui/pgEntry.h index 3d764edf7f..88fd5aaa89 100644 --- a/panda/src/pgui/pgEntry.h +++ b/panda/src/pgui/pgEntry.h @@ -36,7 +36,7 @@ */ class EXPCL_PANDA_PGUI PGEntry : public PGItem { PUBLISHED: - PGEntry(const string &name); + explicit PGEntry(const string &name); virtual ~PGEntry(); protected: diff --git a/panda/src/pgui/pgItem.h b/panda/src/pgui/pgItem.h index 003d1fa0c5..c39103154a 100644 --- a/panda/src/pgui/pgItem.h +++ b/panda/src/pgui/pgItem.h @@ -52,7 +52,7 @@ class ScissorAttrib; */ class EXPCL_PANDA_PGUI PGItem : public PandaNode { PUBLISHED: - PGItem(const string &name); + explicit PGItem(const string &name); virtual ~PGItem(); INLINE void set_name(const string &name); diff --git a/panda/src/pgui/pgScrollFrame.h b/panda/src/pgui/pgScrollFrame.h index 9847852b1f..7dfae33d1b 100644 --- a/panda/src/pgui/pgScrollFrame.h +++ b/panda/src/pgui/pgScrollFrame.h @@ -34,7 +34,7 @@ */ class EXPCL_PANDA_PGUI PGScrollFrame : public PGVirtualFrame, public PGSliderBarNotify { PUBLISHED: - PGScrollFrame(const string &name = ""); + explicit PGScrollFrame(const string &name = ""); virtual ~PGScrollFrame(); protected: diff --git a/panda/src/pgui/pgSliderBar.h b/panda/src/pgui/pgSliderBar.h index c2b3a90b81..c08a98e172 100644 --- a/panda/src/pgui/pgSliderBar.h +++ b/panda/src/pgui/pgSliderBar.h @@ -30,7 +30,7 @@ */ class EXPCL_PANDA_PGUI PGSliderBar : public PGItem, public PGButtonNotify { PUBLISHED: - PGSliderBar(const string &name = ""); + explicit PGSliderBar(const string &name = ""); virtual ~PGSliderBar(); protected: diff --git a/panda/src/pgui/pgTop.h b/panda/src/pgui/pgTop.h index ead4cce12f..45376e99f5 100644 --- a/panda/src/pgui/pgTop.h +++ b/panda/src/pgui/pgTop.h @@ -37,7 +37,7 @@ class PGMouseWatcherGroup; */ class EXPCL_PANDA_PGUI PGTop : public PandaNode { PUBLISHED: - PGTop(const string &name); + explicit PGTop(const string &name); virtual ~PGTop(); protected: diff --git a/panda/src/pgui/pgVirtualFrame.h b/panda/src/pgui/pgVirtualFrame.h index f0f90d85d3..d6505faaaa 100644 --- a/panda/src/pgui/pgVirtualFrame.h +++ b/panda/src/pgui/pgVirtualFrame.h @@ -42,7 +42,7 @@ class TransformState; */ class EXPCL_PANDA_PGUI PGVirtualFrame : public PGItem { PUBLISHED: - PGVirtualFrame(const string &name = ""); + explicit PGVirtualFrame(const string &name = ""); virtual ~PGVirtualFrame(); protected: diff --git a/panda/src/pgui/pgWaitBar.h b/panda/src/pgui/pgWaitBar.h index c4a5359be5..92381870dd 100644 --- a/panda/src/pgui/pgWaitBar.h +++ b/panda/src/pgui/pgWaitBar.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDA_PGUI PGWaitBar : public PGItem { PUBLISHED: - PGWaitBar(const string &name = ""); + explicit PGWaitBar(const string &name = ""); virtual ~PGWaitBar(); protected: diff --git a/panda/src/physics/actorNode.h b/panda/src/physics/actorNode.h index 2f3690543a..aacdec669e 100644 --- a/panda/src/physics/actorNode.h +++ b/panda/src/physics/actorNode.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDAPHYSICS ActorNode : public PhysicalNode { PUBLISHED: - ActorNode(const string &name = ""); + explicit ActorNode(const string &name = ""); ActorNode(const ActorNode ©); virtual ~ActorNode(); diff --git a/panda/src/physics/angularVectorForce.h b/panda/src/physics/angularVectorForce.h index edb2cf8994..aab32eaaae 100644 --- a/panda/src/physics/angularVectorForce.h +++ b/panda/src/physics/angularVectorForce.h @@ -22,8 +22,8 @@ */ class EXPCL_PANDAPHYSICS AngularVectorForce : public AngularForce { PUBLISHED: - AngularVectorForce(const LRotation& quat); - AngularVectorForce(PN_stdfloat h, PN_stdfloat p, PN_stdfloat r); + explicit AngularVectorForce(const LRotation& quat); + explicit AngularVectorForce(PN_stdfloat h, PN_stdfloat p, PN_stdfloat r); AngularVectorForce(const AngularVectorForce ©); virtual ~AngularVectorForce(); diff --git a/panda/src/physics/forceNode.cxx b/panda/src/physics/forceNode.cxx index c5a998a47e..0e186a98b3 100644 --- a/panda/src/physics/forceNode.cxx +++ b/panda/src/physics/forceNode.cxx @@ -69,15 +69,29 @@ add_forces_from(const ForceNode &other) { */ void ForceNode:: set_force(size_t index, BaseForce *force) { - nassertv(index <= _forces.size()); + nassertv(index < _forces.size()); - _forces[index]->_force_node = (ForceNode *)NULL; + _forces[index]->_force_node = nullptr; _forces[index]->_force_node_path.clear(); _forces[index] = force; force->_force_node = this; force->_force_node_path = NodePath(this); } +/** + * insert operation + */ +void ForceNode:: +insert_force(size_t index, BaseForce *force) { + if (index > _forces.size()) { + index = _forces.size(); + } + + _forces.insert(_forces.begin() + index, force); + force->_force_node = this; + force->_force_node_path = NodePath(this); +} + /** * remove operation */ diff --git a/panda/src/physics/forceNode.h b/panda/src/physics/forceNode.h index 574987ff1a..8fab631322 100644 --- a/panda/src/physics/forceNode.h +++ b/panda/src/physics/forceNode.h @@ -26,7 +26,7 @@ */ class EXPCL_PANDAPHYSICS ForceNode : public PandaNode { PUBLISHED: - ForceNode(const string &name); + explicit ForceNode(const string &name); INLINE void clear(); INLINE BaseForce *get_force(size_t index) const; INLINE size_t get_num_forces() const; @@ -35,10 +35,11 @@ PUBLISHED: void add_forces_from(const ForceNode &other); void set_force(size_t index, BaseForce *force); + void insert_force(size_t index, BaseForce *force); void remove_force(BaseForce *force); void remove_force(size_t index); - MAKE_SEQ_PROPERTY(forces, get_num_forces, get_force, set_force, remove_force); + MAKE_SEQ_PROPERTY(forces, get_num_forces, get_force, set_force, remove_force, insert_force); virtual void output(ostream &out) const; virtual void write_forces(ostream &out, unsigned int indent=0) const; diff --git a/panda/src/physics/linearControlForce.h b/panda/src/physics/linearControlForce.h index 3d9e889130..d39a9c2581 100644 --- a/panda/src/physics/linearControlForce.h +++ b/panda/src/physics/linearControlForce.h @@ -24,8 +24,8 @@ */ class EXPCL_PANDAPHYSICS LinearControlForce : public LinearForce { PUBLISHED: - LinearControlForce(const PhysicsObject *po = 0, PN_stdfloat a = 1.0f, - bool mass = false); + explicit LinearControlForce(const PhysicsObject *po = 0, PN_stdfloat a = 1.0f, + bool mass = false); LinearControlForce(const LinearControlForce ©); virtual ~LinearControlForce(); diff --git a/panda/src/physics/linearCylinderVortexForce.h b/panda/src/physics/linearCylinderVortexForce.h index 14fc911811..5698aa9d8d 100644 --- a/panda/src/physics/linearCylinderVortexForce.h +++ b/panda/src/physics/linearCylinderVortexForce.h @@ -25,11 +25,11 @@ */ class EXPCL_PANDAPHYSICS LinearCylinderVortexForce : public LinearForce { PUBLISHED: - LinearCylinderVortexForce(PN_stdfloat radius = 1.0f, - PN_stdfloat length = 0.0f, - PN_stdfloat coef = 1.0f, - PN_stdfloat a = 1.0f, - bool md = false); + explicit LinearCylinderVortexForce(PN_stdfloat radius = 1.0f, + PN_stdfloat length = 0.0f, + PN_stdfloat coef = 1.0f, + PN_stdfloat a = 1.0f, + bool md = false); LinearCylinderVortexForce(const LinearCylinderVortexForce ©); virtual ~LinearCylinderVortexForce(); diff --git a/panda/src/physics/linearFrictionForce.h b/panda/src/physics/linearFrictionForce.h index af11e73b4a..8ba4d19534 100644 --- a/panda/src/physics/linearFrictionForce.h +++ b/panda/src/physics/linearFrictionForce.h @@ -21,7 +21,7 @@ */ class EXPCL_PANDAPHYSICS LinearFrictionForce : public LinearForce { PUBLISHED: - LinearFrictionForce(PN_stdfloat coef = 1.0f, PN_stdfloat a = 1.0f, bool m = false); + explicit LinearFrictionForce(PN_stdfloat coef = 1.0f, PN_stdfloat a = 1.0f, bool m = false); LinearFrictionForce(const LinearFrictionForce ©); virtual ~LinearFrictionForce(); diff --git a/panda/src/physics/linearJitterForce.h b/panda/src/physics/linearJitterForce.h index bfba75315d..076d7d12ca 100644 --- a/panda/src/physics/linearJitterForce.h +++ b/panda/src/physics/linearJitterForce.h @@ -22,7 +22,7 @@ */ class EXPCL_PANDAPHYSICS LinearJitterForce : public LinearRandomForce { PUBLISHED: - LinearJitterForce(PN_stdfloat a = 1.0f, bool m = false); + explicit LinearJitterForce(PN_stdfloat a = 1.0f, bool m = false); LinearJitterForce(const LinearJitterForce ©); virtual ~LinearJitterForce(); diff --git a/panda/src/physics/linearNoiseForce.h b/panda/src/physics/linearNoiseForce.h index b0c47b56ea..b87fec2256 100644 --- a/panda/src/physics/linearNoiseForce.h +++ b/panda/src/physics/linearNoiseForce.h @@ -23,7 +23,7 @@ */ class EXPCL_PANDAPHYSICS LinearNoiseForce : public LinearRandomForce { PUBLISHED: - LinearNoiseForce(PN_stdfloat a = 1.0f, bool m = false); + explicit LinearNoiseForce(PN_stdfloat a = 1.0f, bool m = false); LinearNoiseForce(const LinearNoiseForce ©); virtual ~LinearNoiseForce(); diff --git a/panda/src/physics/linearSinkForce.h b/panda/src/physics/linearSinkForce.h index 00b3d5008c..42f158d184 100644 --- a/panda/src/physics/linearSinkForce.h +++ b/panda/src/physics/linearSinkForce.h @@ -21,8 +21,8 @@ */ class EXPCL_PANDAPHYSICS LinearSinkForce : public LinearDistanceForce { PUBLISHED: - LinearSinkForce(const LPoint3& p, FalloffType f, PN_stdfloat r, PN_stdfloat a = 1.0f, - bool m = true); + explicit LinearSinkForce(const LPoint3& p, FalloffType f, PN_stdfloat r, + PN_stdfloat a = 1.0f, bool m = true); LinearSinkForce(); LinearSinkForce(const LinearSinkForce ©); virtual ~LinearSinkForce(); diff --git a/panda/src/physics/linearSourceForce.h b/panda/src/physics/linearSourceForce.h index 9285a15780..f6b2358c22 100644 --- a/panda/src/physics/linearSourceForce.h +++ b/panda/src/physics/linearSourceForce.h @@ -21,8 +21,8 @@ */ class EXPCL_PANDAPHYSICS LinearSourceForce : public LinearDistanceForce { PUBLISHED: - LinearSourceForce(const LPoint3& p, FalloffType f, PN_stdfloat r, PN_stdfloat a = 1.0f, - bool mass = true); + explicit LinearSourceForce(const LPoint3& p, FalloffType f, PN_stdfloat r, + PN_stdfloat a = 1.0f, bool mass = true); LinearSourceForce(); LinearSourceForce(const LinearSourceForce ©); virtual ~LinearSourceForce(); diff --git a/panda/src/physics/linearUserDefinedForce.h b/panda/src/physics/linearUserDefinedForce.h index 1edc850828..3f05b1ea77 100644 --- a/panda/src/physics/linearUserDefinedForce.h +++ b/panda/src/physics/linearUserDefinedForce.h @@ -17,18 +17,12 @@ #include "linearForce.h" /** - * a programmable force that takes an evaluator fn. - * - * NOTE : AS OF Interrogate => Squeak, this class does NOT get FFI'd due to - * the function pointer bug, and is currently NOT getting interrogated. - * Change this in the makefile when the time is right or this class becomes - * needed... + * A programmable force that takes an evaluator function. */ class EXPCL_PANDAPHYSICS LinearUserDefinedForce : public LinearForce { PUBLISHED: - LinearUserDefinedForce(LVector3 (*proc)(const PhysicsObject *) = NULL, - PN_stdfloat a = 1.0f, - bool md = false); + explicit LinearUserDefinedForce(LVector3 (*proc)(const PhysicsObject *) = NULL, + PN_stdfloat a = 1.0f, bool md = false); LinearUserDefinedForce(const LinearUserDefinedForce ©); virtual ~LinearUserDefinedForce(); diff --git a/panda/src/physics/linearVectorForce.h b/panda/src/physics/linearVectorForce.h index 59adca06cb..ca12ab6bc5 100644 --- a/panda/src/physics/linearVectorForce.h +++ b/panda/src/physics/linearVectorForce.h @@ -22,10 +22,10 @@ */ class EXPCL_PANDAPHYSICS LinearVectorForce : public LinearForce { PUBLISHED: - LinearVectorForce(const LVector3& vec, PN_stdfloat a = 1.0f, bool mass = false); + explicit LinearVectorForce(const LVector3& vec, PN_stdfloat a = 1.0f, bool mass = false); + explicit LinearVectorForce(PN_stdfloat x = 0.0f, PN_stdfloat y = 0.0f, PN_stdfloat z = 0.0f, + PN_stdfloat a = 1.0f, bool mass = false); LinearVectorForce(const LinearVectorForce ©); - LinearVectorForce(PN_stdfloat x = 0.0f, PN_stdfloat y = 0.0f, PN_stdfloat z = 0.0f, - PN_stdfloat a = 1.0f, bool mass = false); virtual ~LinearVectorForce(); INLINE void set_vector(const LVector3& v); diff --git a/panda/src/physics/physical.cxx b/panda/src/physics/physical.cxx index d67b957595..f7e64e0507 100644 --- a/panda/src/physics/physical.cxx +++ b/panda/src/physics/physical.cxx @@ -30,10 +30,10 @@ TypeHandle Physical::_type_handle; * the speed-vs-overhead deal. */ Physical:: -Physical(int total_objects, bool pre_alloc) { - _viscosity=0.0; - _physical_node = (PhysicalNode *) NULL; - _physics_manager = (PhysicsManager *) NULL; +Physical(int total_objects, bool pre_alloc) : + _viscosity(0.0), + _physics_manager(nullptr), + _physical_node(nullptr) { if (total_objects == 1) { _phys_body = new PhysicsObject; @@ -55,8 +55,9 @@ Physical(int total_objects, bool pre_alloc) { * to its template's physicsmanager. */ Physical:: -Physical(const Physical& copy) { - _physics_manager = (PhysicsManager *) NULL; +Physical(const Physical& copy) : + _physics_manager(nullptr), + _physical_node(nullptr) { // copy the forces. LinearForceVector::const_iterator lf_cur; diff --git a/panda/src/physics/physical.h b/panda/src/physics/physical.h index 0a9ca1d07c..64550823af 100644 --- a/panda/src/physics/physical.h +++ b/panda/src/physics/physical.h @@ -41,7 +41,7 @@ public: typedef pvector AngularForceVector; PUBLISHED: - Physical(int total_objects = 1, bool pre_alloc = false); + explicit Physical(int total_objects = 1, bool pre_alloc = false); Physical(const Physical& copy); virtual ~Physical(); diff --git a/panda/src/physics/physicalNode.I b/panda/src/physics/physicalNode.I index 92e5961ae8..700511b1e6 100644 --- a/panda/src/physics/physicalNode.I +++ b/panda/src/physics/physicalNode.I @@ -16,6 +16,11 @@ */ INLINE void PhysicalNode:: clear() { + PhysicalsVector::iterator it; + for (it = _physicals.begin(); it != _physicals.end(); ++it) { + nassertd((*it)->_physical_node == this) continue; + (*it)->_physical_node = nullptr; + } _physicals.erase(_physicals.begin(), _physicals.end()); } diff --git a/panda/src/physics/physicalNode.cxx b/panda/src/physics/physicalNode.cxx index 381ceeff4f..0fdf995720 100644 --- a/panda/src/physics/physicalNode.cxx +++ b/panda/src/physics/physicalNode.cxx @@ -38,6 +38,15 @@ PhysicalNode(const PhysicalNode ©) : */ PhysicalNode:: ~PhysicalNode() { + PhysicalsVector::iterator it; + for (it = _physicals.begin(); it != _physicals.end(); ++it) { + Physical *physical = *it; + nassertd(physical->_physical_node == this) continue; + physical->_physical_node = nullptr; + if (physical->_physics_manager != nullptr) { + physical->_physics_manager->remove_physical(physical); + } + } } /** @@ -68,13 +77,26 @@ add_physicals_from(const PhysicalNode &other) { */ void PhysicalNode:: set_physical(size_t index, Physical *physical) { - nassertv(index <= _physicals.size()); + nassertv(index < _physicals.size()); - _physicals[index]->_physical_node = (PhysicalNode *) NULL; + _physicals[index]->_physical_node = nullptr; _physicals[index] = physical; physical->_physical_node = this; } +/** + * insert operation + */ +void PhysicalNode:: +insert_physical(size_t index, Physical *physical) { + if (index > _physicals.size()) { + index = _physicals.size(); + } + + _physicals.insert(_physicals.begin() + index, physical); + physical->_physical_node = this; +} + /** * remove operation */ @@ -83,9 +105,13 @@ remove_physical(Physical *physical) { pvector< PT(Physical) >::iterator found; PT(Physical) ptp = physical; found = find(_physicals.begin(), _physicals.end(), ptp); - if (found == _physicals.end()) + if (found == _physicals.end()) { return; + } _physicals.erase(found); + + nassertv(ptp->_physical_node == this); + ptp->_physical_node = nullptr; } /** diff --git a/panda/src/physics/physicalNode.h b/panda/src/physics/physicalNode.h index 5ab9f36372..24bd272fdc 100644 --- a/panda/src/physics/physicalNode.h +++ b/panda/src/physics/physicalNode.h @@ -27,7 +27,7 @@ */ class EXPCL_PANDAPHYSICS PhysicalNode : public PandaNode { PUBLISHED: - PhysicalNode(const string &name); + explicit PhysicalNode(const string &name); INLINE void clear(); INLINE Physical *get_physical(size_t index) const; INLINE size_t get_num_physicals() const; @@ -36,10 +36,12 @@ PUBLISHED: void add_physicals_from(const PhysicalNode &other); void set_physical(size_t index, Physical *physical); + void insert_physical(size_t index, Physical *physical); void remove_physical(Physical *physical); void remove_physical(size_t index); - MAKE_SEQ_PROPERTY(physicals, get_num_physicals, get_physical, set_physical, remove_physical); + MAKE_SEQ_PROPERTY(physicals, get_num_physicals, get_physical, set_physical, + remove_physical, insert_physical); virtual void write(ostream &out, unsigned int indent=0) const; diff --git a/panda/src/physx/physxClothNode.h b/panda/src/physx/physxClothNode.h index 7c9c887092..4f2535b949 100644 --- a/panda/src/physx/physxClothNode.h +++ b/panda/src/physx/physxClothNode.h @@ -31,9 +31,8 @@ class PhysxCloth; * Renderable geometry which represents a cloth mesh. */ class EXPCL_PANDAPHYSX PhysxClothNode : public GeomNode { - PUBLISHED: - INLINE PhysxClothNode(const char *name); + INLINE explicit PhysxClothNode(const char *name); INLINE ~PhysxClothNode(); bool set_texcoords(const Filename &filename); diff --git a/panda/src/physx/physxControllerDesc.h b/panda/src/physx/physxControllerDesc.h index 7919b60118..4624938ffd 100644 --- a/panda/src/physx/physxControllerDesc.h +++ b/panda/src/physx/physxControllerDesc.h @@ -23,8 +23,9 @@ * Descriptor class for a character controller. */ class EXPCL_PANDAPHYSX PhysxControllerDesc { - PUBLISHED: + virtual ~PhysxControllerDesc() {}; + virtual void set_to_default() = 0; virtual bool is_valid() const = 0; diff --git a/panda/src/physx/physxSoftBodyNode.h b/panda/src/physx/physxSoftBodyNode.h index df2af11e71..8f6cba7ba1 100644 --- a/panda/src/physx/physxSoftBodyNode.h +++ b/panda/src/physx/physxSoftBodyNode.h @@ -31,9 +31,8 @@ class PhysxSoftBody; * Renderable geometry which represents a soft body mesh. */ class EXPCL_PANDAPHYSX PhysxSoftBodyNode : public GeomNode { - PUBLISHED: - INLINE PhysxSoftBodyNode(const char *name); + INLINE explicit PhysxSoftBodyNode(const char *name); INLINE ~PhysxSoftBodyNode(); void set_from_geom(const Geom *geom); diff --git a/panda/src/pipeline/asyncTaskBase.cxx b/panda/src/pipeline/asyncTaskBase.cxx deleted file mode 100644 index 4d91d83764..0000000000 --- a/panda/src/pipeline/asyncTaskBase.cxx +++ /dev/null @@ -1,77 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file asyncTaskBase.cxx - * @author drose - * @date 2010-02-09 - */ - -#include "asyncTaskBase.h" -#include "thread.h" -#include "atomicAdjust.h" - -TypeHandle AsyncTaskBase::_type_handle; - -/** - * - */ -AsyncTaskBase:: -AsyncTaskBase() { -} - -/** - * - */ -AsyncTaskBase:: -~AsyncTaskBase() { -} - -/** - * Indicates that this task is now the current task running on the indicated - * thread, presumably the current thread. - */ -void AsyncTaskBase:: -record_task(Thread *current_thread) { - nassertv(current_thread->_current_task == NULL); - - void *result = AtomicAdjust::compare_and_exchange_ptr - ((void * TVOLATILE &)current_thread->_current_task, - (void *)NULL, (void *)this); - - // If the return value is other than NULL, someone else must have assigned - // the task first, in another thread. That shouldn't be possible. - - // But different versions of gcc appear to have problems compiling these - // assertions correctly. -#ifndef __GNUC__ - nassertv(result == NULL); - nassertv(current_thread->_current_task == this); -#endif // __GNUC__ -} - -/** - * Indicates that this task is no longer running on the indicated thread. - */ -void AsyncTaskBase:: -clear_task(Thread *current_thread) { - nassertv(current_thread->_current_task == this); - - void *result = AtomicAdjust::compare_and_exchange_ptr - ((void * TVOLATILE &)current_thread->_current_task, - (void *)this, (void *)NULL); - - // If the return value is other than this, someone else must have assigned - // the task first, in another thread. That shouldn't be possible. - - // But different versions of gcc appear to have problems compiling these - // assertions correctly. -#ifndef __GNUC__ - nassertv(result == this); - nassertv(current_thread->_current_task == NULL); -#endif // __GNUC__ -} diff --git a/panda/src/pipeline/asyncTaskBase.h b/panda/src/pipeline/asyncTaskBase.h deleted file mode 100644 index cdf3884b87..0000000000 --- a/panda/src/pipeline/asyncTaskBase.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file asyncTaskBase.h - * @author drose - * @date 2010-02-09 - */ - -#ifndef ASYNCTASKBASE_H -#define ASYNCTASKBASE_H - -#include "pandabase.h" - -#include "typedReferenceCount.h" -#include "namable.h" - -class Thread; - -/** - * The abstract base class for AsyncTask. This is defined here only so we can - * store a pointer to the current task on the Thread. - */ -class EXPCL_PANDA_PIPELINE AsyncTaskBase : public TypedReferenceCount, public Namable { -protected: - AsyncTaskBase(); -public: - ALLOC_DELETED_CHAIN(AsyncTaskBase); - -PUBLISHED: - virtual ~AsyncTaskBase(); - -protected: - void record_task(Thread *current_thread); - void clear_task(Thread *current_thread); - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - TypedReferenceCount::init_type(); - register_type(_type_handle, "AsyncTaskBase", - TypedReferenceCount::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; -}; - -#include "asyncTaskBase.I" - -#endif diff --git a/panda/src/pipeline/conditionVar.h b/panda/src/pipeline/conditionVar.h index ece37ba6f2..3c94e03a34 100644 --- a/panda/src/pipeline/conditionVar.h +++ b/panda/src/pipeline/conditionVar.h @@ -42,7 +42,7 @@ class EXPCL_PANDA_PIPELINE ConditionVar : public ConditionVarDirect #endif // DEBUG_THREADS { PUBLISHED: - INLINE ConditionVar(Mutex &mutex); + INLINE explicit ConditionVar(Mutex &mutex); INLINE ~ConditionVar(); private: INLINE ConditionVar(const ConditionVar ©); diff --git a/panda/src/pipeline/conditionVarDebug.h b/panda/src/pipeline/conditionVarDebug.h index 45754ffdea..7cbacbaf0d 100644 --- a/panda/src/pipeline/conditionVarDebug.h +++ b/panda/src/pipeline/conditionVarDebug.h @@ -31,7 +31,7 @@ */ class EXPCL_PANDA_PIPELINE ConditionVarDebug { public: - ConditionVarDebug(MutexDebug &mutex); + explicit ConditionVarDebug(MutexDebug &mutex); virtual ~ConditionVarDebug(); private: INLINE ConditionVarDebug(const ConditionVarDebug ©); diff --git a/panda/src/pipeline/conditionVarDirect.h b/panda/src/pipeline/conditionVarDirect.h index 508a7c2bdf..116f22b899 100644 --- a/panda/src/pipeline/conditionVarDirect.h +++ b/panda/src/pipeline/conditionVarDirect.h @@ -31,7 +31,7 @@ */ class EXPCL_PANDA_PIPELINE ConditionVarDirect { public: - INLINE ConditionVarDirect(MutexDirect &mutex); + INLINE explicit ConditionVarDirect(MutexDirect &mutex); INLINE ~ConditionVarDirect(); private: INLINE ConditionVarDirect(const ConditionVarDirect ©); diff --git a/panda/src/pipeline/conditionVarFull.h b/panda/src/pipeline/conditionVarFull.h index df25a717a9..5f8b7ea731 100644 --- a/panda/src/pipeline/conditionVarFull.h +++ b/panda/src/pipeline/conditionVarFull.h @@ -45,7 +45,7 @@ class EXPCL_PANDA_PIPELINE ConditionVarFull : public ConditionVarFullDirect #endif // DEBUG_THREADS { PUBLISHED: - INLINE ConditionVarFull(Mutex &mutex); + INLINE explicit ConditionVarFull(Mutex &mutex); INLINE ~ConditionVarFull(); private: INLINE ConditionVarFull(const ConditionVarFull ©); diff --git a/panda/src/pipeline/conditionVarFullDebug.h b/panda/src/pipeline/conditionVarFullDebug.h index 83e36dd809..02c5957f72 100644 --- a/panda/src/pipeline/conditionVarFullDebug.h +++ b/panda/src/pipeline/conditionVarFullDebug.h @@ -31,7 +31,7 @@ */ class EXPCL_PANDA_PIPELINE ConditionVarFullDebug { public: - ConditionVarFullDebug(MutexDebug &mutex); + explicit ConditionVarFullDebug(MutexDebug &mutex); virtual ~ConditionVarFullDebug(); private: INLINE ConditionVarFullDebug(const ConditionVarFullDebug ©); diff --git a/panda/src/pipeline/conditionVarFullDirect.h b/panda/src/pipeline/conditionVarFullDirect.h index 79a069e2de..45cd0ac3f4 100644 --- a/panda/src/pipeline/conditionVarFullDirect.h +++ b/panda/src/pipeline/conditionVarFullDirect.h @@ -31,7 +31,7 @@ */ class EXPCL_PANDA_PIPELINE ConditionVarFullDirect { public: - INLINE ConditionVarFullDirect(MutexDirect &mutex); + INLINE explicit ConditionVarFullDirect(MutexDirect &mutex); INLINE ~ConditionVarFullDirect(); private: INLINE ConditionVarFullDirect(const ConditionVarFullDirect ©); diff --git a/panda/src/pipeline/config_pipeline.cxx b/panda/src/pipeline/config_pipeline.cxx index 24b23da485..6dc24178f0 100644 --- a/panda/src/pipeline/config_pipeline.cxx +++ b/panda/src/pipeline/config_pipeline.cxx @@ -12,7 +12,6 @@ */ #include "config_pipeline.h" -#include "asyncTaskBase.h" #include "mainThread.h" #include "externalThread.h" #include "genericThread.h" @@ -67,7 +66,6 @@ init_libpipeline() { } initialized = true; - AsyncTaskBase::init_type(); MainThread::init_type(); ExternalThread::init_type(); GenericThread::init_type(); diff --git a/panda/src/pipeline/lightMutex.h b/panda/src/pipeline/lightMutex.h index ad24cfb1e8..77ad4b26ae 100644 --- a/panda/src/pipeline/lightMutex.h +++ b/panda/src/pipeline/lightMutex.h @@ -42,9 +42,9 @@ class EXPCL_PANDA_PIPELINE LightMutex : public LightMutexDirect PUBLISHED: INLINE LightMutex(); public: - INLINE LightMutex(const char *name); + INLINE explicit LightMutex(const char *name); PUBLISHED: - INLINE LightMutex(const string &name); + INLINE explicit LightMutex(const string &name); INLINE ~LightMutex(); private: INLINE LightMutex(const LightMutex ©); diff --git a/panda/src/pipeline/lightReMutex.h b/panda/src/pipeline/lightReMutex.h index 5ae93f4faa..6959c4ab17 100644 --- a/panda/src/pipeline/lightReMutex.h +++ b/panda/src/pipeline/lightReMutex.h @@ -33,9 +33,9 @@ class EXPCL_PANDA_PIPELINE LightReMutex : public LightReMutexDirect PUBLISHED: INLINE LightReMutex(); public: - INLINE LightReMutex(const char *name); + INLINE explicit LightReMutex(const char *name); PUBLISHED: - INLINE LightReMutex(const string &name); + INLINE explicit LightReMutex(const string &name); INLINE ~LightReMutex(); private: INLINE LightReMutex(const LightReMutex ©); diff --git a/panda/src/pipeline/p3pipeline_composite1.cxx b/panda/src/pipeline/p3pipeline_composite1.cxx index 40dd5b3647..487c5ca0ad 100644 --- a/panda/src/pipeline/p3pipeline_composite1.cxx +++ b/panda/src/pipeline/p3pipeline_composite1.cxx @@ -1,4 +1,3 @@ -#include "asyncTaskBase.cxx" #include "conditionVar.cxx" #include "conditionVarDebug.cxx" #include "conditionVarDirect.cxx" diff --git a/panda/src/pipeline/pipelineCyclerTrueImpl.I b/panda/src/pipeline/pipelineCyclerTrueImpl.I index 2c393d0546..9b078a6a30 100644 --- a/panda/src/pipeline/pipelineCyclerTrueImpl.I +++ b/panda/src/pipeline/pipelineCyclerTrueImpl.I @@ -231,6 +231,8 @@ read_stage_unlocked(int pipeline_stage) const { TAU_PROFILE("const CycleData *PipelineCyclerTrueImpl::read_stage_unlocked(int)", " ", TAU_USER); #ifdef _DEBUG nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); +#elif defined(__has_builtin) && __has_builtin(__builtin_assume) + __builtin_assume(pipeline_stage >= 0); #endif return _data[pipeline_stage]._cdata; } @@ -248,6 +250,8 @@ read_stage(int pipeline_stage, Thread *current_thread) const { TAU_PROFILE("const CycleData *PipelineCyclerTrueImpl::read_stage(int, Thread *)", " ", TAU_USER); #ifdef _DEBUG nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); +#elif defined(__has_builtin) && __has_builtin(__builtin_assume) + __builtin_assume(pipeline_stage >= 0); #endif _lock.acquire(current_thread); return _data[pipeline_stage]._cdata; @@ -278,6 +282,8 @@ elevate_read_stage(int pipeline_stage, const CycleData *pointer, #ifdef _DEBUG nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); nassertr(_data[pipeline_stage]._cdata == pointer, NULL); +#elif defined(__has_builtin) && __has_builtin(__builtin_assume) + __builtin_assume(pipeline_stage >= 0); #endif CycleData *new_pointer = write_stage(pipeline_stage, current_thread); _lock.release(); @@ -296,6 +302,8 @@ elevate_read_stage_upstream(int pipeline_stage, const CycleData *pointer, #ifdef _DEBUG nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); nassertr(_data[pipeline_stage]._cdata == pointer, NULL); +#elif defined(__has_builtin) && __has_builtin(__builtin_assume) + __builtin_assume(pipeline_stage >= 0); #endif CycleData *new_pointer = write_stage_upstream(pipeline_stage, force_to_0, current_thread); @@ -313,6 +321,8 @@ release_write_stage(int pipeline_stage, CycleData *pointer) { nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages); nassertv(_data[pipeline_stage]._cdata == pointer); nassertv(_data[pipeline_stage]._writes_outstanding > 0); +#elif defined(__has_builtin) && __has_builtin(__builtin_assume) + __builtin_assume(pipeline_stage >= 0); #endif --(_data[pipeline_stage]._writes_outstanding); _lock.release(); diff --git a/panda/src/pipeline/pmutex.h b/panda/src/pipeline/pmutex.h index ef9e653027..c5d471ae0c 100644 --- a/panda/src/pipeline/pmutex.h +++ b/panda/src/pipeline/pmutex.h @@ -43,7 +43,7 @@ PUBLISHED: public: INLINE Mutex(const char *name); PUBLISHED: - INLINE Mutex(const string &name); + INLINE explicit Mutex(const string &name); INLINE ~Mutex(); private: INLINE Mutex(const Mutex ©); diff --git a/panda/src/pipeline/psemaphore.h b/panda/src/pipeline/psemaphore.h index 229320ab84..6fd9162cc2 100644 --- a/panda/src/pipeline/psemaphore.h +++ b/panda/src/pipeline/psemaphore.h @@ -29,7 +29,7 @@ */ class EXPCL_PANDA_PIPELINE Semaphore { PUBLISHED: - INLINE Semaphore(int initial_count = 1); + INLINE explicit Semaphore(int initial_count = 1); INLINE ~Semaphore(); private: INLINE Semaphore(const Semaphore ©); diff --git a/panda/src/pipeline/pythonThread.h b/panda/src/pipeline/pythonThread.h index ea3adc2ac2..66b7e45aa4 100644 --- a/panda/src/pipeline/pythonThread.h +++ b/panda/src/pipeline/pythonThread.h @@ -26,8 +26,8 @@ */ class PythonThread : public Thread { PUBLISHED: - PythonThread(PyObject *function, PyObject *args, - const string &name, const string &sync_name); + explicit PythonThread(PyObject *function, PyObject *args, + const string &name, const string &sync_name); virtual ~PythonThread(); BLOCKING PyObject *join(); diff --git a/panda/src/pipeline/reMutex.h b/panda/src/pipeline/reMutex.h index 7a54847388..fd6a710b55 100644 --- a/panda/src/pipeline/reMutex.h +++ b/panda/src/pipeline/reMutex.h @@ -35,9 +35,9 @@ class EXPCL_PANDA_PIPELINE ReMutex : public ReMutexDirect PUBLISHED: INLINE ReMutex(); public: - INLINE ReMutex(const char *name); + INLINE explicit ReMutex(const char *name); PUBLISHED: - INLINE ReMutex(const string &name); + INLINE explicit ReMutex(const string &name); INLINE ~ReMutex(); private: INLINE ReMutex(const ReMutex ©); diff --git a/panda/src/pipeline/thread.I b/panda/src/pipeline/thread.I index b721486c72..12a8607e63 100644 --- a/panda/src/pipeline/thread.I +++ b/panda/src/pipeline/thread.I @@ -73,7 +73,16 @@ get_unique_id() const { */ INLINE int Thread:: get_pipeline_stage() const { +#if !defined(_DEBUG) && defined(__has_builtin) && __has_builtin(__builtin_assume) + // Because this is a signed int, this results in a sign extend on x86-64. + // However, since we guarantee that this is never less than zero, clang + // offers a nice way to avoid that. + int pipeline_stage = _pipeline_stage; + __builtin_assume(pipeline_stage >= 0); + return pipeline_stage; +#else return _pipeline_stage; +#endif } /** @@ -268,7 +277,7 @@ preempt() { * AsyncTaskManager), if any, or NULL if the thread is not currently servicing * a task. */ -INLINE AsyncTaskBase *Thread:: +INLINE AsyncTask *Thread:: get_current_task() const { return _current_task; } diff --git a/panda/src/pipeline/thread.h b/panda/src/pipeline/thread.h index 2240174f02..4dbebacc8b 100644 --- a/panda/src/pipeline/thread.h +++ b/panda/src/pipeline/thread.h @@ -28,7 +28,7 @@ class ReMutex; class MutexDebug; class ConditionVarDebug; class ConditionVarFullDebug; -class AsyncTaskBase; +class AsyncTask; /** * A thread; that is, a lightweight process. This is an abstract base class; @@ -89,7 +89,7 @@ PUBLISHED: BLOCKING INLINE void join(); INLINE void preempt(); - INLINE AsyncTaskBase *get_current_task() const; + INLINE AsyncTask *get_current_task() const; INLINE void set_python_index(int index); @@ -100,6 +100,16 @@ PUBLISHED: MAKE_PROPERTY(python_index, get_python_index); MAKE_PROPERTY(unique_id, get_unique_id); MAKE_PROPERTY(pipeline_stage, get_pipeline_stage, set_pipeline_stage); + + MAKE_PROPERTY(main_thread, get_main_thread); + MAKE_PROPERTY(external_thread, get_external_thread); + MAKE_PROPERTY(current_thread, get_current_thread); + MAKE_PROPERTY(current_pipeline_stage, get_current_pipeline_stage); + + MAKE_PROPERTY(threading_supported, is_threading_supported); + MAKE_PROPERTY(true_threads, is_true_threads); + MAKE_PROPERTY(simple_threads, is_simple_threads); + MAKE_PROPERTY(started, is_started); MAKE_PROPERTY(joinable, is_joinable); MAKE_PROPERTY(current_task, get_current_task); @@ -132,7 +142,7 @@ private: int _pipeline_stage; PStatsCallback *_pstats_callback; bool _joinable; - AsyncTaskBase *_current_task; + AsyncTask *_current_task; int _python_index; @@ -174,7 +184,7 @@ private: friend class ThreadPosixImpl; friend class ThreadSimpleImpl; friend class MainThread; - friend class AsyncTaskBase; + friend class AsyncTask; }; INLINE ostream &operator << (ostream &out, const Thread &thread); diff --git a/panda/src/pnmimage/pnm-image-filter.cxx b/panda/src/pnmimage/pnm-image-filter.cxx index ac57b20b23..63398ad3c1 100644 --- a/panda/src/pnmimage/pnm-image-filter.cxx +++ b/panda/src/pnmimage/pnm-image-filter.cxx @@ -696,7 +696,7 @@ box_filter_line(const PNMImage &image, // Get the final (partial) xel float x_contrib = x1 - (float)x_last; - if (x_contrib > 0.0001f) { + if (x_contrib > 0.0001f && x < image.get_x_size()) { box_filter_xel(image, x, y, x_contrib, y_contrib, color, pixel_count); } @@ -728,7 +728,7 @@ box_filter_region(const PNMImage &image, // Get the final (partial) row float y_contrib = y1 - (float)y_last; - if (y_contrib > 0.0001f) { + if (y_contrib > 0.0001f && y < image.get_y_size()) { box_filter_line(image, x0, y, x1, y_contrib, color, pixel_count); } diff --git a/panda/src/pnmimage/pnmImage.h b/panda/src/pnmimage/pnmImage.h index 2c45492029..2f7b653185 100644 --- a/panda/src/pnmimage/pnmImage.h +++ b/panda/src/pnmimage/pnmImage.h @@ -124,8 +124,8 @@ PUBLISHED: void make_grayscale(float rc, float gc, float bc); INLINE void make_rgb(); - void premultiply_alpha(); - void unpremultiply_alpha(); + BLOCKING void premultiply_alpha(); + BLOCKING void unpremultiply_alpha(); BLOCKING void reverse_rows(); BLOCKING void flip(bool flip_x, bool flip_y, bool transpose); @@ -244,27 +244,27 @@ PUBLISHED: // The bodies for the non-inline *_filter() functions can be found in the // file pnm-image-filter.cxx. - INLINE void box_filter(float radius = 1.0); - INLINE void gaussian_filter(float radius = 1.0); + BLOCKING INLINE void box_filter(float radius = 1.0); + BLOCKING INLINE void gaussian_filter(float radius = 1.0); - void unfiltered_stretch_from(const PNMImage ©); - void box_filter_from(float radius, const PNMImage ©); - void gaussian_filter_from(float radius, const PNMImage ©); - void quick_filter_from(const PNMImage ©, - int xborder = 0, int yborder = 0); + BLOCKING void unfiltered_stretch_from(const PNMImage ©); + BLOCKING void box_filter_from(float radius, const PNMImage ©); + BLOCKING void gaussian_filter_from(float radius, const PNMImage ©); + BLOCKING void quick_filter_from(const PNMImage ©, + int xborder = 0, int yborder = 0); void make_histogram(Histogram &hist); - void perlin_noise_fill(float sx, float sy, int table_size = 256, - unsigned long seed = 0); + BLOCKING void perlin_noise_fill(float sx, float sy, int table_size = 256, + unsigned long seed = 0); void perlin_noise_fill(StackedPerlinNoise2 &perlin); void remix_channels(const LMatrix4 &conv); - INLINE void gamma_correct(float from_gamma, float to_gamma); - INLINE void gamma_correct_alpha(float from_gamma, float to_gamma); - INLINE void apply_exponent(float gray_exponent); - INLINE void apply_exponent(float gray_exponent, float alpha_exponent); - INLINE void apply_exponent(float red_exponent, float green_exponent, float blue_exponent); - void apply_exponent(float red_exponent, float green_exponent, float blue_exponent, float alpha_exponent); + BLOCKING INLINE void gamma_correct(float from_gamma, float to_gamma); + BLOCKING INLINE void gamma_correct_alpha(float from_gamma, float to_gamma); + BLOCKING INLINE void apply_exponent(float gray_exponent); + BLOCKING INLINE void apply_exponent(float gray_exponent, float alpha_exponent); + BLOCKING INLINE void apply_exponent(float red_exponent, float green_exponent, float blue_exponent); + BLOCKING void apply_exponent(float red_exponent, float green_exponent, float blue_exponent, float alpha_exponent); LRGBColorf get_average_xel() const; LColorf get_average_xel_a() const; diff --git a/panda/src/pnmimage/pnmPainter.h b/panda/src/pnmimage/pnmPainter.h index 6abdb1cefa..a6192f621b 100644 --- a/panda/src/pnmimage/pnmPainter.h +++ b/panda/src/pnmimage/pnmPainter.h @@ -29,7 +29,7 @@ class PNMImage; */ class EXPCL_PANDA_PNMIMAGE PNMPainter { PUBLISHED: - PNMPainter(PNMImage &image, int xo = 0, int yo = 0); + explicit PNMPainter(PNMImage &image, int xo = 0, int yo = 0); INLINE ~PNMPainter(); INLINE void set_pen(PNMBrush *pen); diff --git a/panda/src/pnmimagetypes/config_pnmimagetypes.cxx b/panda/src/pnmimagetypes/config_pnmimagetypes.cxx index c498951f3f..2fc23d574a 100644 --- a/panda/src/pnmimagetypes/config_pnmimagetypes.cxx +++ b/panda/src/pnmimagetypes/config_pnmimagetypes.cxx @@ -81,6 +81,7 @@ ConfigVariableInt img_size ("img-size", 0, PRC_DESC("If an IMG file without a header is loaded (e.g. img-header-type " "is set to 'none', this specifies the fixed x y size of the image.")); + ConfigVariableInt jpeg_quality ("jpeg-quality", 95, PRC_DESC("Set this to the quality percentage for writing JPEG files. 95 is " @@ -88,10 +89,17 @@ ConfigVariableInt jpeg_quality "significantly better quality, but do lead to significantly greater " "size).")); +ConfigVariableInt png_compression_level +("png-compression-level", 6, + PRC_DESC("Set this to the desired compression level for writing PNG images. " + "Valid values are 0 (no compression), or 1 (compression, best " + "speed) to 9 (best compression). Default is 6. PNG compression is " + "lossless.")); + ConfigVariableBool png_palette ("png-palette", true, - PRC_DESC("Set this true to allow writing palette-based PNG images when possible.")); - + PRC_DESC("Set this true to allow writing palette-based PNG images when " + "possible.")); ConfigVariableInt bmp_bpp ("bmp-bpp", 0, diff --git a/panda/src/pnmimagetypes/config_pnmimagetypes.h b/panda/src/pnmimagetypes/config_pnmimagetypes.h index dca563a683..95ed9224a5 100644 --- a/panda/src/pnmimagetypes/config_pnmimagetypes.h +++ b/panda/src/pnmimagetypes/config_pnmimagetypes.h @@ -57,6 +57,7 @@ extern ConfigVariableBool tga_grayscale; extern ConfigVariableInt jpeg_quality; +extern ConfigVariableInt png_compression_level; extern ConfigVariableBool png_palette; extern ConfigVariableInt bmp_bpp; diff --git a/panda/src/pnmimagetypes/pnmFileTypeJPG.h b/panda/src/pnmimagetypes/pnmFileTypeJPG.h index 2dcd506322..b82534189a 100644 --- a/panda/src/pnmimagetypes/pnmFileTypeJPG.h +++ b/panda/src/pnmimagetypes/pnmFileTypeJPG.h @@ -36,6 +36,10 @@ #include #endif +// jconfig.h overrides our INLINE definition. +#ifdef __GNUC__ +#pragma push_macro("INLINE") +#endif extern "C" { #include // jpeglib requires this to be included first. @@ -43,6 +47,11 @@ extern "C" { #include } +// Restore our own INLINE definition. +#ifdef __GNUC__ +#pragma pop_macro("INLINE") +#endif + /** * For reading and writing Jpeg files. */ diff --git a/panda/src/pnmimagetypes/pnmFileTypePNG.cxx b/panda/src/pnmimagetypes/pnmFileTypePNG.cxx index 49cbc2faba..bf4ab4ac31 100644 --- a/panda/src/pnmimagetypes/pnmFileTypePNG.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypePNG.cxx @@ -526,6 +526,10 @@ write_data(xel *array, xelval *alpha_data) { png_set_write_fn(_png, (void *)this, png_write_data, png_flush_data); + // The compression level corresponds directly to the compression levels for + // zlib. + png_set_compression_level(_png, png_compression_level); + // First, write the header. int true_bit_depth = pm_maxvaltobits(_maxval); diff --git a/panda/src/pnmimagetypes/pnmFileTypeStbImage.cxx b/panda/src/pnmimagetypes/pnmFileTypeStbImage.cxx index 9a96d64fc4..a87aae11f6 100644 --- a/panda/src/pnmimagetypes/pnmFileTypeStbImage.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypeStbImage.cxx @@ -240,12 +240,28 @@ StbImageReader(PNMFileType *type, istream *file, bool owns_file, string magic_nu _context.img_buffer_end = _buffer + length; _context.img_buffer_original_end = _context.img_buffer_end; +#ifndef STBI_NO_PNG + stbi__png png; + png.s = &_context; +#endif + // Invoke stbi_info to read the image size and channel count. - if (strncmp(magic_number.c_str(), "#?", 2) == 0 && + if (magic_number[0] == '#' && magic_number[1] == '?' && stbi__hdr_info(&_context, &_x_size, &_y_size, &_num_channels)) { _is_valid = true; _is_float = true; +#ifndef STBI_NO_PNG + } else if (magic_number[0] == '\x89' && magic_number[1] == 'P' && + stbi__png_info_raw(&png, &_x_size, &_y_size, &_num_channels)) { + // Detect the case of using PNGs so that we can determine whether to do a + // 16-bit load instead. + if (png.depth == 16) { + _maxval = 65535; + } + _is_valid = true; +#endif + } else if (stbi__info_main(&_context, &_x_size, &_y_size, &_num_channels)) { _is_valid = true; @@ -254,8 +270,6 @@ StbImageReader(PNMFileType *type, istream *file, bool owns_file, string magic_nu pnmimage_cat.error() << "stb_info failure: " << stbi_failure_reason() << "\n"; } - - _maxval = 255; } /** @@ -307,11 +321,13 @@ read_pfm(PfmFile &pfm) { int len; unsigned char count, value; int i, j, k, c1, c2, z; + const char *headerToken; // Check identifier - if (strcmp(stbi__hdr_gettoken(&_context, buffer), "#?RADIANCE") != 0) { + headerToken = stbi__hdr_gettoken(&_context, buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) { pnmimage_cat.error() - << "Missing #?RADIANCE header.\n"; + << "Missing #?RADIANCE or #?RGBE header.\n"; return false; } @@ -387,27 +403,43 @@ main_decode_loop: len <<= 8; len |= stbi__get8(&_context); if (len != width) { - STBI_FREE(scanline); pnmimage_cat.error() << "Corrupt HDR: invalid decoded scanline length.\n"; + STBI_FREE(scanline); return false; } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc(width * 4); + if (scanline == nullptr) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + pnmimage_cat.error() << "Out of memory while reading HDR file.\n"; + STBI_FREE(hdr_data); + return false; + } } for (k = 0; k < 4; ++k) { + int nleft; i = 0; - while (i < width) { + while ((nleft = width - i) > 0) { count = stbi__get8(&_context); if (count > 128) { // Run value = stbi__get8(&_context); count -= 128; + if (count > nleft) { + pnmimage_cat.error() << "Bad RLE data in HDR file.\n"; + STBI_FREE(scanline); + return false; + } for (z = 0; z < count; ++z) { scanline[i++ * 4 + k] = value; } } else { // Dump + if (count > nleft) { + pnmimage_cat.error() << "Bad RLE data in HDR file.\n"; + STBI_FREE(scanline); + return false; + } for (z = 0; z < count; ++z) { scanline[i++ * 4 + k] = stbi__get8(&_context); } @@ -418,7 +450,9 @@ main_decode_loop: stbi__hdr_convert(hdr_data+(j*width + i)*3, scanline + i*4, 3); } } - STBI_FREE(scanline); + if (scanline) { + STBI_FREE(scanline); + } } pfm.swap_table(table); @@ -460,9 +494,14 @@ read_data(xel *array, xelval *alpha) { int cols = 0; int rows = 0; int comp = _num_channels; - stbi_uc *data = stbi__load_main(&_context, &cols, &rows, &comp, _num_channels); + void *data; + if (_maxval != 65535) { + data = stbi__load_and_postprocess_8bit(&_context, &cols, &rows, &comp, _num_channels); + } else { + data = stbi__load_and_postprocess_16bit(&_context, &cols, &rows, &comp, _num_channels); + } - if (data == NULL) { + if (data == nullptr) { pnmimage_cat.error() << "stbi_load failure: " << stbi_failure_reason() << "\n"; return 0; @@ -472,36 +511,67 @@ read_data(xel *array, xelval *alpha) { nassertr(comp == _num_channels, 0); size_t pixels = (size_t)_x_size * (size_t)rows; - stbi_uc *ptr = data; - switch (_num_channels) { - case 1: - for (size_t i = 0; i < pixels; ++i) { - PPM_ASSIGN(array[i], ptr[i], ptr[i], ptr[i]); - } - break; + if (_maxval != 65535) { + uint8_t *ptr = (uint8_t *)data; + switch (_num_channels) { + case 1: + for (size_t i = 0; i < pixels; ++i) { + PPM_ASSIGN(array[i], ptr[i], ptr[i], ptr[i]); + } + break; - case 2: - for (size_t i = 0; i < pixels; ++i) { - PPM_ASSIGN(array[i], ptr[0], ptr[0], ptr[0]); - alpha[i] = ptr[1]; - ptr += 2; - } - break; + case 2: + for (size_t i = 0; i < pixels; ++i) { + PPM_ASSIGN(array[i], ptr[0], ptr[0], ptr[0]); + alpha[i] = ptr[1]; + ptr += 2; + } + break; - case 3: - for (size_t i = 0; i < pixels; ++i) { - PPM_ASSIGN(array[i], ptr[0], ptr[1], ptr[2]); - ptr += 3; - } - break; + case 3: + for (size_t i = 0; i < pixels; ++i) { + PPM_ASSIGN(array[i], ptr[0], ptr[1], ptr[2]); + ptr += 3; + } + break; - case 4: - for (size_t i = 0; i < pixels; ++i) { - PPM_ASSIGN(array[i], ptr[0], ptr[1], ptr[2]); - alpha[i] = ptr[3]; - ptr += 4; + case 4: + for (size_t i = 0; i < pixels; ++i) { + PPM_ASSIGN(array[i], ptr[0], ptr[1], ptr[2]); + alpha[i] = ptr[3]; + ptr += 4; + } + break; + } + } else { + uint16_t *ptr = (uint16_t *)data; + switch (_num_channels) { + case 1: + for (size_t i = 0; i < pixels; ++i) { + PPM_ASSIGN(array[i], ptr[i], ptr[i], ptr[i]); + } + break; + + case 2: + for (size_t i = 0; i < pixels; ++i) { + PPM_ASSIGN(array[i], ptr[0], ptr[0], ptr[0]); + alpha[i] = ptr[1]; + ptr += 2; + } + break; + + case 3: + memcpy(array, ptr, pixels * sizeof(uint16_t) * 3); + break; + + case 4: + for (size_t i = 0; i < pixels; ++i) { + PPM_ASSIGN(array[i], ptr[0], ptr[1], ptr[2]); + alpha[i] = ptr[3]; + ptr += 4; + } + break; } - break; } stbi_image_free(data); diff --git a/panda/src/pnmimagetypes/stb_image.h b/panda/src/pnmimagetypes/stb_image.h index a3c1129932..8773da408d 100644 --- a/panda/src/pnmimagetypes/stb_image.h +++ b/panda/src/pnmimagetypes/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.12 - public domain image loader - http://nothings.org/stb_image.h +/* stb_image - v2.15 - public domain image loader - http://nothings.org/stb_image.h no warranty implied; use at your own risk Do this: @@ -21,7 +21,7 @@ avoid problematic images and only need the trivial interface JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8-bit-per-channel (16 bpc not supported) + PNG 1/2/4/8/16-bit-per-channel TGA (not sure what subset, if a subset) BMP non-1bpp, non-RLE @@ -42,114 +42,19 @@ Full documentation under "DOCUMENTATION" below. - Revision 2.00 release notes: +LICENSE - - Progressive JPEG is now supported. + See end of file for license information. - - PPM and PGM binary formats are now supported, thanks to Ken Miller. +RECENT REVISION HISTORY: - - x86 platforms now make use of SSE2 SIMD instructions for - JPEG decoding, and ARM platforms can use NEON SIMD if requested. - This work was done by Fabian "ryg" Giesen. SSE2 is used by - default, but NEON must be enabled explicitly; see docs. - - With other JPEG optimizations included in this version, we see - 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup - on a JPEG on an ARM machine, relative to previous versions of this - library. The same results will not obtain for all JPGs and for all - x86/ARM machines. (Note that progressive JPEGs are significantly - slower to decode than regular JPEGs.) This doesn't mean that this - is the fastest JPEG decoder in the land; rather, it brings it - closer to parity with standard libraries. If you want the fastest - decode, look elsewhere. (See "Philosophy" section of docs below.) - - See final bullet items below for more info on SIMD. - - - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing - the memory allocator. Unlike other STBI libraries, these macros don't - support a context parameter, so if you need to pass a context in to - the allocator, you'll have to store it in a global or a thread-local - variable. - - - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and - STBI_NO_LINEAR. - STBI_NO_HDR: suppress implementation of .hdr reader format - STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API - - - You can suppress implementation of any of the decoders to reduce - your code footprint by #defining one or more of the following - symbols before creating the implementation. - - STBI_NO_JPEG - STBI_NO_PNG - STBI_NO_BMP - STBI_NO_PSD - STBI_NO_TGA - STBI_NO_GIF - STBI_NO_HDR - STBI_NO_PIC - STBI_NO_PNM (.ppm and .pgm) - - - You can request *only* certain decoders and suppress all other ones - (this will be more forward-compatible, as addition of new decoders - doesn't require you to disable them explicitly): - - STBI_ONLY_JPEG - STBI_ONLY_PNG - STBI_ONLY_BMP - STBI_ONLY_PSD - STBI_ONLY_TGA - STBI_ONLY_GIF - STBI_ONLY_HDR - STBI_ONLY_PIC - STBI_ONLY_PNM (.ppm and .pgm) - - Note that you can define multiples of these, and you will get all - of them ("only x" and "only y" is interpreted to mean "only x&y"). - - - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still - want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB - - - Compilation of all SIMD code can be suppressed with - #define STBI_NO_SIMD - It should not be necessary to disable SIMD unless you have issues - compiling (e.g. using an x86 compiler which doesn't support SSE - intrinsics or that doesn't support the method used to detect - SSE2 support at run-time), and even those can be reported as - bugs so I can refine the built-in compile-time checking to be - smarter. - - - The old STBI_SIMD system which allowed installing a user-defined - IDCT etc. has been removed. If you need this, don't upgrade. My - assumption is that almost nobody was doing this, and those who - were will find the built-in SIMD more satisfactory anyway. - - - RGB values computed for JPEG images are slightly different from - previous versions of stb_image. (This is due to using less - integer precision in SIMD.) The C code has been adjusted so - that the same RGB values will be computed regardless of whether - SIMD support is available, so your app should always produce - consistent results. But these results are slightly different from - previous versions. (Specifically, about 3% of available YCbCr values - will compute different RGB results from pre-1.49 versions by +-1; - most of the deviating values are one smaller in the G channel.) - - - If you must produce consistent results with previous versions of - stb_image, #define STBI_JPEG_OLD and you will get the same results - you used to; however, you will not get the SIMD speedups for - the YCbCr-to-RGB conversion step (although you should still see - significant JPEG speedup from the other changes). - - Please note that STBI_JPEG_OLD is a temporary feature; it will be - removed in future versions of the library. It is only intended for - near-term back-compatibility use. - - - Latest revision history: + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; + allocate large structures on the stack; correct channel count for PNG & BMP 2.10 (2016-01-22) avoid warning introduced in 2.09 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED @@ -157,21 +62,6 @@ 2.07 (2015-09-13) partial animated GIF support limited 16-bit PSD support minor bugs, code cleanup, and compiler warnings - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) additional corruption checking - stbi_set_flip_vertically_on_load - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD - progressive JPEG - PGM/PPM support - STBI_MALLOC,STBI_REALLOC,STBI_FREE - STBI_NO_*, STBI_ONLY_* - GIF bugfix See end of file for full revision history. @@ -186,33 +76,28 @@ Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - urraka@github (animated gif) Junggon Kim (PNM comments) + github:urraka (animated gif) Junggon Kim (PNM comments) Daniel Gibson (16-bit TGA) - + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) Optimizations & bugfixes Fabian "ryg" Giesen Arseny Kapoulkine Bug & warning fixes Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Martin Golini Jerry Jansson Joseph Thomson - Dave Moore Roy Eltham Hayaki Saito Phil Jordan - Won Chun Luke Graham Johan Duparc Nathan Reed - the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis - Janez Zemva John Bartholomew Michal Cichon svdijk@github - Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson - Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github - Aruelien Pocheville Thibault Reuille Cass Everitt Matthew Gregan - Ryamond Barbiero Paul Du Bois Engin Manap snagar@github - Michaelangel007@github Oriol Ferrer Mesia socks-the-fox - Blazej Dariusz Roszkowski - - -LICENSE - -This software is dual-licensed to the public domain and under the following -license: you are granted a perpetual, irrevocable license to copy, modify, -publish, and distribute this file as you see fit. + Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan + Dave Moore Roy Eltham Hayaki Saito Nathan Reed + Won Chun Luke Graham Johan Duparc Nick Verigakis + the Horde3D community Thomas Ruf Ronny Chevalier Baldur Karlsson + Janez Zemva John Bartholomew Michal Cichon github:rlyeh + Jonathan Blow Ken Hamada Tero Hanninen github:romigrou + Laurent Gomila Cort Stratton Sergio Gonzalez github:svdijk + Aruelien Pocheville Thibault Reuille Cass Everitt github:snagar + Ryamond Barbiero Paul Du Bois Engin Manap github:Zelex + Michaelangel007@github Philipp Wiesemann Dale Weiler github:grim210 + Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:sammyhw + Blazej Dariusz Roszkowski Gregory Mullen github:phprus */ @@ -238,10 +123,10 @@ publish, and distribute this file as you see fit. // stbi_image_free(data) // // Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *comp -- outputs # of image components in image file -// int req_comp -- if non-zero, # of image components requested in result +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result // // The return value from an image loader is an 'unsigned char *' which points // to the pixel data, or NULL on an allocation failure or if the image is @@ -287,13 +172,13 @@ publish, and distribute this file as you see fit. // and for best performance I may provide less-easy-to-use APIs that give higher // performance, in addition to the easy to use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which // make more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") -// - Small footprint ("easy to maintain") +// - Small source code footprint ("easy to maintain") // - No dependencies ("ease of use") // // =========================================================================== @@ -325,13 +210,6 @@ publish, and distribute this file as you see fit. // (at least this is true for iOS and Android). Therefore, the NEON support is // toggled by a build flag: define STBI_NEON to get NEON loops. // -// The output of the JPEG decoder is slightly different from versions where -// SIMD support was introduced (that is, for versions before 1.49). The -// difference is only +-1 in the 8-bit RGB channels, and only on a small -// fraction of pixels. You can force the pre-1.49 behavior by defining -// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path -// and hence cost some performance. -// // If for some reason you do not want to use any of SIMD code, or if // you have issues compiling it, you can disable it entirely by // defining STBI_NO_SIMD. @@ -387,6 +265,41 @@ publish, and distribute this file as you see fit. // says there's premultiplied data (currently only happens in iPhone images, // and only if iPhone convert-to-rgb processing is on). // +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// #ifndef STBI_NO_STDIO @@ -406,6 +319,7 @@ enum }; typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; #ifdef __cplusplus extern "C" { @@ -433,22 +347,42 @@ typedef struct int (*eof) (void *user); // returns nonzero if we are at end of file/data } stbi_io_callbacks; -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); // for stbi_load_from_file, file pointer is left pointing immediately after image #endif +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif +// @TODO the other variants + +//////////////////////////////////// +// +// float-per-channel interface +// #ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif #endif @@ -566,6 +500,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #include // ptrdiff_t on osx #include #include +#include #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) #include // ldexp @@ -649,12 +584,14 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #define STBI__X86_TARGET #endif -#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// NOTE: not clear do we actually need this for the 64-bit path? +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) // gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; -// this is just broken and gcc are jerks for not fixing it properly -// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. #define STBI_NO_SIMD #endif @@ -712,14 +649,10 @@ static int stbi__sse2_available() static int stbi__sse2_available() { -#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later - // GCC 4.8+ has a nice way to do this - return __builtin_cpu_supports("sse2"); -#else - // portable way to do this, preferably without using GCC inline ASM? - // just bail for now. - return 0; -#endif + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; } #endif #endif @@ -827,57 +760,70 @@ static void stbi__rewind(stbi__context *s) s->img_buffer_end = s->img_buffer_original_end; } +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + #ifndef STBI_NO_JPEG static int stbi__jpeg_test(stbi__context *s); -static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNG static int stbi__png_test(stbi__context *s); -static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_BMP static int stbi__bmp_test(stbi__context *s); -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_TGA static int stbi__tga_test(stbi__context *s); -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s); -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_HDR static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PIC static int stbi__pic_test(stbi__context *s); -static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_GIF static int stbi__gif_test(stbi__context *s); -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s); -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif @@ -900,6 +846,77 @@ static void *stbi__malloc(size_t size) return STBI_MALLOC(size); } +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} + +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} + // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char @@ -935,33 +952,38 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) stbi__vertically_flip_on_load = flag_true_if_should_flip; } -static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); #endif #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); } #endif @@ -969,35 +991,117 @@ static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *com #ifndef STBI_NO_TGA // test tga last because it's a crappy test! if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp); + return stbi__tga_load(s,x,y,comp,req_comp, ri); #endif return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } -static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) { - unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); + int i; + int img_len = w * h * channels; + stbi_uc *reduced; - if (stbi__vertically_flip_on_load && result != NULL) { + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 8) { + STBI_ASSERT(ri.bits_per_channel == 16); + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { int w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; + int channels = req_comp ? req_comp : *comp; int row,col,z; - stbi_uc temp; + stbi_uc *image = (stbi_uc *) result; // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once for (row = 0; row < (h>>1); row++) { for (col = 0; col < w; col++) { - for (z = 0; z < depth; z++) { - temp = result[(row * w + col) * depth + z]; - result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; - result[((h - row - 1) * w + col) * depth + z] = temp; + for (z = 0; z < channels; z++) { + stbi_uc temp = image[(row * w + col) * channels + z]; + image[(row * w + col) * channels + z] = image[((h - row - 1) * w + col) * channels + z]; + image[((h - row - 1) * w + col) * channels + z] = temp; } } } } - return result; + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 16) { + STBI_ASSERT(ri.bits_per_channel == 8); + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int w = *x, h = *y; + int channels = req_comp ? req_comp : *comp; + int row,col,z; + stbi__uint16 *image = (stbi__uint16 *) result; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < channels; z++) { + stbi__uint16 temp = image[(row * w + col) * channels + z]; + image[(row * w + col) * channels + z] = image[((h - row - 1) * w + col) * channels + z]; + image[((h - row - 1) * w + col) * channels + z] = temp; + } + } + } + } + + return (stbi__uint16 *) result; } #ifndef STBI_NO_HDR @@ -1053,27 +1157,52 @@ STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req unsigned char *result; stbi__context s; stbi__start_file(&s,f); - result = stbi__load_flip(&s,x,y,comp,req_comp); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + #endif //!STBI_NO_STDIO STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); - return stbi__load_flip(&s,x,y,comp,req_comp); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_flip(&s,x,y,comp,req_comp); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } #ifndef STBI_NO_LINEAR @@ -1082,13 +1211,14 @@ static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int unsigned char *data; #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); if (hdr_data) stbi__float_postprocess(hdr_data,x,y,comp,req_comp); return hdr_data; } #endif - data = stbi__load_flip(s, x, y, comp, req_comp); + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); if (data) return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); @@ -1346,7 +1476,7 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - good = (unsigned char *) stbi__malloc(req_comp * x * y); + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); if (good == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); @@ -1356,26 +1486,75 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r unsigned char *src = data + j * x * img_n ; unsigned char *dest = good + j * x * req_comp; - #define COMBO(a,b) ((a)*8+(b)) - #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros - switch (COMBO(img_n, req_comp)) { - CASE(1,2) dest[0]=src[0], dest[1]=255; break; - CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; - CASE(2,1) dest[0]=src[0]; break; - CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; - CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; - CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; - CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; - CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; default: STBI_ASSERT(0); } - #undef CASE + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE } STBI_FREE(data); @@ -1386,7 +1565,9 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) { int i,k,n; - float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; @@ -1406,7 +1587,9 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) { int i,k,n; - stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; @@ -1471,7 +1654,7 @@ typedef struct stbi__context *s; stbi__huffman huff_dc[4]; stbi__huffman huff_ac[4]; - stbi_uc dequant[4][64]; + stbi__uint16 dequant[4][64]; stbi__int16 fast_ac[4][1 << FAST_BITS]; // sizes for components, interleaved MCUs @@ -1507,6 +1690,8 @@ typedef struct int succ_high; int succ_low; int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag int rgb; int scan_n, order[4]; @@ -1577,7 +1762,7 @@ static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) // magnitude code followed by receive_extend code int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); int m = 1 << (magbits - 1); - if (k < m) k += (-1 << magbits) + 1; + if (k < m) k += (~0U << magbits) + 1; // if the result is small enough, we can fit it in fast_ac table if (k >= -128 && k <= 127) fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); @@ -1592,6 +1777,7 @@ static void stbi__grow_buffer_unsafe(stbi__jpeg *j) int b = j->nomore ? 0 : stbi__get8(j->s); if (b == 0xff) { int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes if (c != 0) { j->marker = (unsigned char) c; j->nomore = 1; @@ -1716,7 +1902,7 @@ static stbi_uc stbi__jpeg_dezigzag[64+15] = }; // decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) { int diff,dc,k; int t; @@ -2425,7 +2611,7 @@ static stbi_uc stbi__get_marker(stbi__jpeg *j) x = stbi__get8(j->s); if (x != 0xff) return STBI__MARKER_none; while (x == 0xff) - x = stbi__get8(j->s); + x = stbi__get8(j->s); // consume repeated 0xff fill bytes return x; } @@ -2440,7 +2626,7 @@ static void stbi__jpeg_reset(stbi__jpeg *j) j->code_bits = 0; j->code_buffer = 0; j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; j->marker = STBI__MARKER_none; j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; j->eob_run = 0; @@ -2572,7 +2758,7 @@ static int stbi__parse_entropy_coded_data(stbi__jpeg *z) } } -static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) { int i; for (i=0; i < 64; ++i) @@ -2614,13 +2800,14 @@ static int stbi__process_marker(stbi__jpeg *z, int m) L = stbi__get16be(z->s)-2; while (L > 0) { int q = stbi__get8(z->s); - int p = q >> 4; + int p = q >> 4, sixteen = (p != 0); int t = q & 15,i; - if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); - L -= 65; + z->dequant[t][stbi__jpeg_dezigzag[i]] = sixteen ? stbi__get16be(z->s) : stbi__get8(z->s); + L -= (sixteen ? 129 : 65); } return L==0; @@ -2653,12 +2840,50 @@ static int stbi__process_marker(stbi__jpeg *z, int m) } return L==0; } + // check for comment block or APP blocks if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - stbi__skip(z->s, stbi__get16be(z->s)-2); + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); return 1; } - return 0; + + return stbi__err("unknown marker","Corrupt JPEG"); } // after we see SOS @@ -2701,6 +2926,28 @@ static int stbi__process_scan_header(stbi__jpeg *z) return 1; } +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + static int stbi__process_frame_header(stbi__jpeg *z, int scan) { stbi__context *s = z->s; @@ -2710,7 +2957,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires c = stbi__get8(s); - if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); s->img_n = c; for (i=0; i < c; ++i) { z->img_comp[i].data = NULL; @@ -2723,13 +2970,8 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) for (i=0; i < s->img_n; ++i) { static unsigned char rgb[3] = { 'R', 'G', 'B' }; z->img_comp[i].id = stbi__get8(s); - if (z->img_comp[i].id != i+1) // JFIF requires - if (z->img_comp[i].id != i) { // some version of jpegtran outputs non-JFIF-compliant files! - // somethings output this (see http://fileformats.archiveteam.org/wiki/JPEG#Color_format) - if (z->img_comp[i].id != rgb[i]) - return stbi__err("bad component ID","Corrupt JPEG"); - ++z->rgb; - } + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; q = stbi__get8(s); z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); @@ -2738,7 +2980,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (scan != STBI__SCAN_load) return 1; - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); for (i=0; i < s->img_n; ++i) { if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; @@ -2750,6 +2992,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) z->img_v_max = v_max; z->img_mcu_w = h_max * 8; z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; @@ -2761,28 +3004,27 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) // the bogus oversized data from using interleaved MCUs and their // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); - - if (z->img_comp[i].raw_data == NULL) { - for(--i; i >= 0; --i) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - } - return stbi__err("outofmem", "Out of memory"); - } + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); // align blocks for idct using mmx/sse z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - z->img_comp[i].linebuf = NULL; if (z->progressive) { - z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; - z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; - z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } else { - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; } } @@ -2801,6 +3043,8 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) { int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 z->marker = STBI__MARKER_none; // initialize cached marker to empty m = stbi__get_marker(z); if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); @@ -2842,12 +3086,15 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) if (x == 255) { j->marker = stbi__get8(j->s); break; - } else if (x != 0) { - return stbi__err("junk before marker", "Corrupt JPEG"); } } // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) stbi__err("bad DNL height", "Corrupt JPEG"); } else { if (!stbi__process_marker(j, m)) return 0; } @@ -3066,38 +3313,9 @@ static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_ return out; } -#ifdef STBI_JPEG_OLD -// this is the same YCbCr-to-RGB calculation that stb_image has used -// historically before the algorithm changes in 1.49 -#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 16) + 32768; // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr*float2fixed(1.40200f); - g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); - b = y_fixed + cb*float2fixed(1.77200f); - r >>= 16; - g >>= 16; - b >>= 16; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#else // this is a reduced-precision calculation of YCbCr-to-RGB introduced // to make sure the code produces the same results in both SIMD and scalar -#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) { int i; @@ -3106,9 +3324,9 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; - r = y_fixed + cr* float2fixed(1.40200f); - g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* float2fixed(1.77200f); + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; @@ -3122,7 +3340,6 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc out += step; } } -#endif #if defined(STBI_SSE2) || defined(STBI_NEON) static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) @@ -3241,9 +3458,9 @@ static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc cons int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; - r = y_fixed + cr* float2fixed(1.40200f); - g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* float2fixed(1.77200f); + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; @@ -3269,18 +3486,14 @@ static void stbi__setup_jpeg(stbi__jpeg *j) #ifdef STBI_SSE2 if (stbi__sse2_available()) { j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; } #endif #ifdef STBI_NEON j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; #endif } @@ -3288,23 +3501,7 @@ static void stbi__setup_jpeg(stbi__jpeg *j) // clean up the temporary component buffers static void stbi__cleanup_jpeg(stbi__jpeg *j) { - int i; - for (i=0; i < j->s->img_n; ++i) { - if (j->img_comp[i].raw_data) { - STBI_FREE(j->img_comp[i].raw_data); - j->img_comp[i].raw_data = NULL; - j->img_comp[i].data = NULL; - } - if (j->img_comp[i].raw_coeff) { - STBI_FREE(j->img_comp[i].raw_coeff); - j->img_comp[i].raw_coeff = 0; - j->img_comp[i].coeff = 0; - } - if (j->img_comp[i].linebuf) { - STBI_FREE(j->img_comp[i].linebuf); - j->img_comp[i].linebuf = NULL; - } - } + stbi__free_jpeg_components(j, j->s->img_n, 0); } typedef struct @@ -3317,9 +3514,16 @@ typedef struct int ypos; // which pre-expansion row we're on } stbi__resample; +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) { - int n, decode_n; + int n, decode_n, is_rgb; z->s->img_n = 0; // make stbi__cleanup_jpeg safe // validate req_comp @@ -3329,9 +3533,11 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n; + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - if (z->s->img_n == 3 && n < 3) + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) decode_n = 1; else decode_n = z->s->img_n; @@ -3368,7 +3574,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp } // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } // now go ahead and resample @@ -3391,7 +3597,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (n >= 3) { stbi_uc *y = coutput[0]; if (z->s->img_n == 3) { - if (z->rgb == 3) { + if (is_rgb) { for (i=0; i < z->s->img_x; ++i) { out[0] = y[i]; out[1] = coutput[1][i]; @@ -3402,6 +3608,28 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp } else { z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc k = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], k); + out[1] = stbi__blinn_8x8(coutput[1][i], k); + out[2] = stbi__blinn_8x8(coutput[2][i], k); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc k = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], k); + out[1] = stbi__blinn_8x8(255 - out[1], k); + out[2] = stbi__blinn_8x8(255 - out[2], k); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } } else for (i=0; i < z->s->img_x; ++i) { out[0] = out[1] = out[2] = y[i]; @@ -3409,25 +3637,54 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp out += n; } } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc k = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], k); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], k); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], k); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } } } stbi__cleanup_jpeg(z); *out_x = z->s->img_x; *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n; // report original components, not output + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output return output; } } -static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); j->s = s; stbi__setup_jpeg(j); result = load_jpeg_image(j, x,y,comp,req_comp); @@ -3438,11 +3695,12 @@ static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *com static int stbi__jpeg_test(stbi__context *s) { int r; - stbi__jpeg j; - j.s = s; - stbi__setup_jpeg(&j); - r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); stbi__rewind(s); + STBI_FREE(j); return r; } @@ -3454,7 +3712,7 @@ static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) } if (x) *x = j->s->img_x; if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; return 1; } @@ -3511,7 +3769,7 @@ stbi_inline static int stbi__bit_reverse(int v, int bits) return stbi__bitreverse16(v) >> (16-bits); } -static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) { int i,k=0; int code, next_code[16], sizes[17]; @@ -3721,6 +3979,7 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) int hlit = stbi__zreceive(a,5) + 257; int hdist = stbi__zreceive(a,5) + 1; int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; memset(codelength_sizes, 0, sizeof(codelength_sizes)); for (i=0; i < hclen; ++i) { @@ -3730,27 +3989,29 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; n = 0; - while (n < hlit + hdist) { + while (n < ntot) { int c = stbi__zhuffman_decode(a, &z_codelength); if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); if (c < 16) lencodes[n++] = (stbi_uc) c; - else if (c == 16) { - c = stbi__zreceive(a,2)+3; - memset(lencodes+n, lencodes[n-1], c); - n += c; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - memset(lencodes+n, 0, c); - n += c; - } else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - memset(lencodes+n, 0, c); + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = stbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); n += c; } } - if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; return 1; @@ -3798,9 +4059,24 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) return 1; } -// @TODO: should statically initialize these for optimal thread safety -static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; -static void stbi__init_zdefaults(void) +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: { int i; // use <= to match clearly with spec for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; @@ -3810,6 +4086,7 @@ static void stbi__init_zdefaults(void) for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; } +*/ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { @@ -3828,7 +4105,6 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) } else { if (type == 1) { // use fixed code lengths - if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; } else { @@ -4016,7 +4292,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r int width = x; STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc(x * y * output_bytes); // extra bytes to write off the end into + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); @@ -4029,7 +4305,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; - stbi_uc *prior = cur - stride; + stbi_uc *prior; int filter = *raw++; if (filter > 4) @@ -4041,6 +4317,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r filter_bytes = 1; width = img_width_bytes; } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; @@ -4081,37 +4358,37 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r // this is a little gross, so that we don't switch per-pixel or per-component if (depth < 8 || img_n == out_n) { int nk = (width - 1)*filter_bytes; - #define CASE(f) \ + #define STBI__CASE(f) \ case f: \ for (k=0; k < nk; ++k) switch (filter) { // "none" filter turns into a memcpy here; make that explicit. case STBI__F_none: memcpy(cur, raw, nk); break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; } - #undef CASE + #undef STBI__CASE raw += nk; } else { STBI_ASSERT(img_n+1 == out_n); - #define CASE(f) \ + #define STBI__CASE(f) \ case f: \ for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ for (k=0; k < filter_bytes; ++k) switch (filter) { - CASE(STBI__F_none) cur[k] = raw[k]; break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); break; + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; } - #undef CASE + #undef STBI__CASE // the loop above sets the high byte of the pixels' alpha, but for // 16 bit png files we also need the low byte set. we'll do that here. @@ -4214,13 +4491,15 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; stbi_uc *final; int p; if (!interlaced) return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); // de-interlacing - final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; @@ -4240,8 +4519,8 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3 for (i=0; i < x; ++i) { int out_y = j*yspc[p]+yorig[p]; int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, - a->out + (j*x+i)*out_n, out_n); + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); } } STBI_FREE(a->out); @@ -4309,7 +4588,7 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; stbi_uc *p, *temp_out, *orig = a->out; - p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); if (p == NULL) return stbi__err("outofmem", "Out of memory"); // between here and free(out) below, exitting would leak @@ -4341,26 +4620,6 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int return 1; } -static int stbi__reduce_png(stbi__png *p) -{ - int i; - int img_len = p->s->img_x * p->s->img_y * p->s->img_out_n; - stbi_uc *reduced; - stbi__uint16 *orig = (stbi__uint16*)p->out; - - if (p->depth != 16) return 1; // don't need to do anything if not 16-bit data - - reduced = (stbi_uc *)stbi__malloc(img_len); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is a decent approx of 16->8 bit scaling - - p->out = reduced; - STBI_FREE(orig); - - return 1; -} - static int stbi__unpremultiply_on_load = 0; static int stbi__de_iphone_flag = 0; @@ -4451,7 +4710,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); @@ -4500,7 +4759,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = stbi__get16be(s); // copy the values as-is + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is } else { for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger } @@ -4587,20 +4846,22 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) } } -static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) { - unsigned char *result=NULL; + void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth == 16) { - if (!stbi__reduce_png(p)) { - return result; - } - } + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { - result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); p->s->img_out_n = req_comp; if (result == NULL) return result; } @@ -4615,11 +4876,11 @@ static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req return result; } -static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi__png p; p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp); + return stbi__do_png(&p, x,y,comp,req_comp, ri); } static int stbi__png_test(stbi__context *s) @@ -4732,7 +4993,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->offset = stbi__get32le(s); info->hsz = hsz = stbi__get32le(s); info->mr = info->mg = info->mb = info->ma = 0; - + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { s->img_x = stbi__get16le(s); @@ -4807,7 +5068,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) } -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; unsigned int mr=0,mg=0,mb=0,ma=0, all_a; @@ -4815,8 +5076,9 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int int psize=0,i,j,width; int flip_vertically, pad, target; stbi__bmp_data info; + STBI_NOTUSED(ri); - info.all_a = 255; + info.all_a = 255; if (stbi__bmp_parse_header(s, &info) == NULL) return NULL; // error code already set @@ -4843,7 +5105,11 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int else target = s->img_n; // if they want monochrome, we'll post-convert - out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); if (info.bpp < 16) { int z=0; @@ -4931,7 +5197,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int stbi__skip(s, pad); } } - + // if alpha channel is all 0s, replace with all 255s if (target == 4 && all_a == 0) for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) @@ -5077,18 +5343,18 @@ errorEnd: } // read 16bit value and convert to 24bit RGB -void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) { - stbi__uint16 px = stbi__get16le(s); + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); stbi__uint16 fiveBitMask = 31; // we have 3 channels with 5bits each int r = (px >> 10) & fiveBitMask; int g = (px >> 5) & fiveBitMask; int b = px & fiveBitMask; // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (r * 255)/31; - out[1] = (g * 255)/31; - out[2] = (b * 255)/31; + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); // some people claim that the most significant bit might be used for alpha // (possibly if an alpha-bit is set in the "image descriptor byte") @@ -5096,7 +5362,7 @@ void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) // so let's treat all 15 and 16bit TGAs as RGB with no alpha. } -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { // read in the TGA header stuff int tga_offset = stbi__get8(s); @@ -5118,10 +5384,11 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int unsigned char *tga_data; unsigned char *tga_palette = NULL; int i, j; - unsigned char raw_data[4]; + unsigned char raw_data[4] = {0}; int RLE_count = 0; int RLE_repeating = 0; int read_next_pixel = 1; + STBI_NOTUSED(ri); // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -5143,7 +5410,10 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int *y = tga_height; if (comp) *comp = tga_comp; - tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); // skip to the data's starting position (offset usually = 0) @@ -5162,7 +5432,7 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette - tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_comp ); + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); if (!tga_palette) { STBI_FREE(tga_data); return stbi__errpuc("outofmem", "Out of memory"); @@ -5298,14 +5568,53 @@ static int stbi__psd_test(stbi__context *s) return r; } -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) { - int pixelCount; + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; int channelCount, compression; - int channel, i, count, len; + int channel, i; int bitdepth; int w,h; stbi_uc *out; + STBI_NOTUSED(ri); // Check identifier if (stbi__get32be(s) != 0x38425053) // "8BPS" @@ -5362,8 +5671,18 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int if (compression > 1) return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + // Create the destination image. - out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); pixelCount = w*h; @@ -5395,82 +5714,86 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int *p = (channel == 3 ? 255 : 0); } else { // Read the RLE data. - count = 0; - while (count < pixelCount) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len ^= 0x0FF; - len += 2; - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); } } } } else { // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit value for each pixel in the image. + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. // Read the data by channel. for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out + channel; if (channel >= channelCount) { // Fill this channel with default data. - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } else { - // Read the data. - if (bitdepth == 16) { - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } } } } } + // remove weird white matte from PSD if (channelCount >= 4) { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - // remove weird white matte from PSD - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } } } } + // convert to desired output format if (req_comp && req_comp != 4) { - out = stbi__convert_format(out, 4, req_comp, w, h); + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); if (out == NULL) return out; // stbi__convert_format frees input on failure } @@ -5654,10 +5977,13 @@ static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *c return result; } -static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) { stbi_uc *result; - int i, x,y; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; for (i=0; i<92; ++i) stbi__get8(s); @@ -5665,14 +5991,14 @@ static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int re x = stbi__get16be(s); y = stbi__get16be(s); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); stbi__get32be(s); //skip `ratio' stbi__get16be(s); //skip `fields' stbi__get16be(s); //skip `pad' // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc(x*y*4); + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { @@ -5931,8 +6257,11 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(g->w, g->h, 4, 0)) + return stbi__errpuc("too large", "GIF too large"); + prev_out = g->out; - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + g->out = (stbi_uc *) stbi__malloc_mad3(4, g->w, g->h, 0); if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); switch ((g->eflags & 0x1C) >> 2) { @@ -6039,11 +6368,12 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i STBI_NOTUSED(req_comp); } -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *u = 0; stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); memset(g, 0, sizeof(*g)); + STBI_NOTUSED(ri); u = stbi__gif_load_next(s, g, comp, req_comp); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker @@ -6069,20 +6399,24 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) // Radiance RGBE HDR loader // originally by Nicolas Schulz #ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s) +static int stbi__hdr_test_core(stbi__context *s, const char *signature) { - const char *signature = "#?RADIANCE\n"; int i; for (i=0; signature[i]; ++i) if (stbi__get8(s) != signature[i]) - return 0; + return 0; + stbi__rewind(s); return 1; } static int stbi__hdr_test(stbi__context* s) { - int r = stbi__hdr_test_core(s); + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } return r; } @@ -6136,7 +6470,7 @@ static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) } } -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { char buffer[STBI__HDR_BUFLEN]; char *token; @@ -6147,10 +6481,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re int len; unsigned char count, value; int i, j, k, c1,c2, z; - + const char *headerToken; + STBI_NOTUSED(ri); // Check identifier - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) return stbi__errpf("not HDR", "Corrupt HDR image"); // Parse header @@ -6179,8 +6515,13 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re if (comp) *comp = 3; if (req_comp == 0) req_comp = 3; + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + // Read data - hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); // Load image data // image data is stored as some number of sca @@ -6219,20 +6560,29 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re len <<= 8; len |= stbi__get8(s); if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } for (k = 0; k < 4; ++k) { + int nleft; i = 0; - while (i < width) { + while ((nleft = width - i) > 0) { count = stbi__get8(s); if (count > 128) { // Run value = stbi__get8(s); count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } @@ -6241,7 +6591,8 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re for (i=0; i < width; ++i) stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); } - STBI_FREE(scanline); + if (scanline) + STBI_FREE(scanline); } return hdr_data; @@ -6252,6 +6603,11 @@ static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) char buffer[STBI__HDR_BUFLEN]; char *token; int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; if (stbi__hdr_test(s) == 0) { stbi__rewind( s ); @@ -6293,14 +6649,14 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) void *p; stbi__bmp_data info; - info.all_a = 255; + info.all_a = 255; p = stbi__bmp_parse_header(s, &info); stbi__rewind( s ); if (p == NULL) return 0; - *x = s->img_x; - *y = s->img_y; - *comp = info.ma ? 4 : 3; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) *comp = info.ma ? 4 : 3; return 1; } #endif @@ -6308,7 +6664,10 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_PSD static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { - int channelCount; + int channelCount, dummy; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; if (stbi__get32be(s) != 0x38425053) { stbi__rewind( s ); return 0; @@ -6341,9 +6700,13 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_PIC static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) { - int act_comp=0,num_packets=0,chained; + int act_comp=0,num_packets=0,chained,dummy; stbi__pic_packet packets[10]; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { stbi__rewind(s); return 0; @@ -6419,16 +6782,22 @@ static int stbi__pnm_test(stbi__context *s) return 1; } -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; + STBI_NOTUSED(ri); + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) return 0; + *x = s->img_x; *y = s->img_y; - *comp = s->img_n; + if (comp) *comp = s->img_n; - out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); stbi__getn(s, out, s->img_n * s->img_x * s->img_y); @@ -6477,16 +6846,20 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c) static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) { - int maxv; + int maxv, dummy; char c, p, t; - stbi__rewind( s ); + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); // Get identifier p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); + stbi__rewind(s); return 0; } @@ -6593,6 +6966,12 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int /* revision history: + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) allocate large structures on the stack remove white matting for transparent PSD diff --git a/panda/src/pnmtext/pnmTextGlyph.h b/panda/src/pnmtext/pnmTextGlyph.h index 8042fddc2a..d6075c4291 100644 --- a/panda/src/pnmtext/pnmTextGlyph.h +++ b/panda/src/pnmtext/pnmTextGlyph.h @@ -24,7 +24,7 @@ */ class EXPCL_PANDA_PNMTEXT PNMTextGlyph { PUBLISHED: - PNMTextGlyph(double advance); + explicit PNMTextGlyph(double advance); ~PNMTextGlyph(); INLINE int get_advance() const; diff --git a/panda/src/pnmtext/pnmTextMaker.h b/panda/src/pnmtext/pnmTextMaker.h index 662341c16c..4ae8f04069 100644 --- a/panda/src/pnmtext/pnmTextMaker.h +++ b/panda/src/pnmtext/pnmTextMaker.h @@ -34,10 +34,10 @@ class PNMTextGlyph; */ class EXPCL_PANDA_PNMTEXT PNMTextMaker : public FreetypeFont { PUBLISHED: - PNMTextMaker(const Filename &font_filename, int face_index); - PNMTextMaker(const char *font_data, int data_length, int face_index); + explicit PNMTextMaker(const Filename &font_filename, int face_index); + explicit PNMTextMaker(const char *font_data, int data_length, int face_index); + explicit PNMTextMaker(const FreetypeFont ©); PNMTextMaker(const PNMTextMaker ©); - PNMTextMaker(const FreetypeFont ©); ~PNMTextMaker(); enum Alignment { diff --git a/panda/src/pstatclient/pStatCollector.h b/panda/src/pstatclient/pStatCollector.h index 4bcd92d55a..6c265f5e0c 100644 --- a/panda/src/pstatclient/pStatCollector.h +++ b/panda/src/pstatclient/pStatCollector.h @@ -50,10 +50,10 @@ public: INLINE PStatCollector(); PUBLISHED: - INLINE PStatCollector(const string &name, - PStatClient *client = NULL); - INLINE PStatCollector(const PStatCollector &parent, - const string &name); + INLINE explicit PStatCollector(const string &name, + PStatClient *client = NULL); + INLINE explicit PStatCollector(const PStatCollector &parent, + const string &name); INLINE PStatCollector(const PStatCollector ©); INLINE void operator = (const PStatCollector ©); diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index 7b14891daa..25d2bfd591 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -119,7 +119,7 @@ static TimeCollectorProperties time_properties[] = { { 1, "Cull:Sort", { 0.3, 0.3, 0.6 } }, { 1, "*", { 0.1, 0.1, 0.5 } }, { 1, "*:Show fps", { 0.5, 0.8, 1.0 } }, - { 0, "*:Munge", { 0.3, 0.3, 0.9 } }, + { 1, "*:Munge", { 0.3, 0.3, 0.9 } }, { 1, "*:Munge:Geom", { 0.4, 0.2, 0.8 } }, { 1, "*:Munge:Sprites", { 0.2, 0.8, 0.4 } }, { 0, "*:Munge:Data", { 0.7, 0.5, 0.2 } }, diff --git a/panda/src/putil/bamReader.h b/panda/src/putil/bamReader.h index 4785a4d4fc..bf88ced2af 100644 --- a/panda/src/putil/bamReader.h +++ b/panda/src/putil/bamReader.h @@ -115,7 +115,7 @@ public: PUBLISHED: // The primary interface for a caller. - BamReader(DatagramGenerator *source = NULL); + explicit BamReader(DatagramGenerator *source = NULL); ~BamReader(); void set_source(DatagramGenerator *source); diff --git a/panda/src/putil/bamWriter.h b/panda/src/putil/bamWriter.h index 3436457e9e..c2b04b3757 100644 --- a/panda/src/putil/bamWriter.h +++ b/panda/src/putil/bamWriter.h @@ -62,7 +62,7 @@ */ class EXPCL_PANDA_PUTIL BamWriter : public BamEnums { PUBLISHED: - BamWriter(DatagramSink *target = NULL); + explicit BamWriter(DatagramSink *target = NULL); ~BamWriter(); void set_target(DatagramSink *target); diff --git a/panda/src/putil/bitArray.I b/panda/src/putil/bitArray.I index 6c16a549d0..f746a9ba77 100644 --- a/panda/src/putil/bitArray.I +++ b/panda/src/putil/bitArray.I @@ -30,26 +30,6 @@ BitArray(WordType init_value) { _highest_bits = 0; } -/** - * - */ -INLINE BitArray:: -BitArray(const BitArray ©) : - _array(copy._array), - _highest_bits(copy._highest_bits) -{ -} - -/** - * - */ -INLINE BitArray &BitArray:: -operator = (const BitArray ©) { - _array = copy._array; - _highest_bits = copy._highest_bits; - return *this; -} - /** * Returns a BitArray with an infinite array of bits, all on. */ @@ -98,13 +78,6 @@ range(int low_bit, int size) { return result; } -/** - * - */ -INLINE BitArray:: -~BitArray() { -} - /** * Returns true if there is a maximum number of bits that may be stored in * this structure, false otherwise. If this returns true, the number may be diff --git a/panda/src/putil/bitArray.h b/panda/src/putil/bitArray.h index 3a4a4b16d2..72ef1f656d 100644 --- a/panda/src/putil/bitArray.h +++ b/panda/src/putil/bitArray.h @@ -46,8 +46,6 @@ PUBLISHED: INLINE BitArray(); INLINE BitArray(WordType init_value); - INLINE BitArray(const BitArray ©); - INLINE BitArray &operator = (const BitArray ©); BitArray(const SparseArray &from); INLINE static BitArray all_on(); @@ -56,8 +54,6 @@ PUBLISHED: INLINE static BitArray bit(int index); INLINE static BitArray range(int low_bit, int size); - INLINE ~BitArray(); - CONSTEXPR static bool has_max_num_bits(); CONSTEXPR static int get_max_num_bits(); diff --git a/panda/src/putil/bitMask.I b/panda/src/putil/bitMask.I index dca29c5b6a..0411d06042 100644 --- a/panda/src/putil/bitMask.I +++ b/panda/src/putil/bitMask.I @@ -34,26 +34,6 @@ BitMask(WordType init_value) : { } -/** - * - */ -template -INLINE BitMask:: -BitMask(const BitMask ©) : - _word(copy._word) -{ -} - -/** - * - */ -template -INLINE BitMask &BitMask:: -operator = (const BitMask ©) { - _word = copy._word; - return *this; -} - /** * Returns a BitMask whose bits are all on. */ @@ -121,14 +101,6 @@ range(int low_bit, int size) { return result; } -/** - * - */ -template -INLINE BitMask:: -~BitMask() { -} - /** * Returns true if there is a maximum number of bits that may be stored in * this structure, false otherwise. If this returns true, the number may be diff --git a/panda/src/putil/bitMask.h b/panda/src/putil/bitMask.h index 17c1c9f69d..ff9b15db97 100644 --- a/panda/src/putil/bitMask.h +++ b/panda/src/putil/bitMask.h @@ -38,8 +38,6 @@ PUBLISHED: INLINE BitMask(); INLINE BitMask(WordType init_value); - INLINE BitMask(const BitMask ©); - INLINE BitMask &operator = (const BitMask ©); INLINE static BitMask all_on(); INLINE static BitMask all_off(); @@ -47,8 +45,6 @@ PUBLISHED: INLINE static BitMask bit(int index); INLINE static BitMask range(int low_bit, int size); - INLINE ~BitMask(); - CONSTEXPR static bool has_max_num_bits(); CONSTEXPR static int get_max_num_bits(); diff --git a/panda/src/putil/buttonHandle.I b/panda/src/putil/buttonHandle.I index 5701ccb395..2353b01689 100644 --- a/panda/src/putil/buttonHandle.I +++ b/panda/src/putil/buttonHandle.I @@ -19,13 +19,6 @@ CONSTEXPR ButtonHandle:: ButtonHandle(int index) : _index(index) { } -/** - * - */ -INLINE ButtonHandle:: -ButtonHandle(const ButtonHandle ©) : _index(copy._index) { -} - /** * */ diff --git a/panda/src/putil/buttonHandle.h b/panda/src/putil/buttonHandle.h index d2b97be36a..429108249f 100644 --- a/panda/src/putil/buttonHandle.h +++ b/panda/src/putil/buttonHandle.h @@ -31,7 +31,6 @@ PUBLISHED: // previously by another static initializer! INLINE ButtonHandle() DEFAULT_CTOR; CONSTEXPR ButtonHandle(int index); - INLINE ButtonHandle(const ButtonHandle ©); ButtonHandle(const string &name); PUBLISHED: diff --git a/panda/src/putil/copyOnWriteObject.h b/panda/src/putil/copyOnWriteObject.h index 12ea5baf24..2f6b5cc9fc 100644 --- a/panda/src/putil/copyOnWriteObject.h +++ b/panda/src/putil/copyOnWriteObject.h @@ -161,6 +161,10 @@ private: static TypeHandle _type_handle; }; +// We can safely redefine this as a no-op. +template<> +INLINE void PointerToBase::update_type(To *ptr) {} + #include "copyOnWriteObject.I" #endif diff --git a/panda/src/putil/copyOnWritePointer.I b/panda/src/putil/copyOnWritePointer.I index a44731bcbc..9e996355fa 100644 --- a/panda/src/putil/copyOnWritePointer.I +++ b/panda/src/putil/copyOnWritePointer.I @@ -166,7 +166,7 @@ operator < (const CopyOnWritePointer &other) const { * This flavor of the method is written for the non-threaded case. */ INLINE const CopyOnWriteObject *CopyOnWritePointer:: -get_read_pointer() const { +get_read_pointer(Thread *current_thread) const { return _cow_object; } #endif // COW_THREADED @@ -362,8 +362,14 @@ operator = (PointerTo &&from) NOEXCEPT { */ template INLINE CPT(TYPENAME CopyOnWritePointerTo::To) CopyOnWritePointerTo:: -get_read_pointer() const { - return (const To *)(CopyOnWritePointer::get_read_pointer().p()); +get_read_pointer(Thread *current_thread) const { + // This is necessary because we don't currently have a way to cast between + // two compatible PointerTo types without losing the reference count. + CPT(TYPENAME CopyOnWritePointerTo::To) to; + CPT(CopyOnWriteObject) from = CopyOnWritePointer::get_read_pointer(current_thread); + to.cheat() = (const To *)from.p(); + from.cheat() = nullptr; + return to; } #else // COW_THREADED /** @@ -371,8 +377,8 @@ get_read_pointer() const { */ template INLINE const TYPENAME CopyOnWritePointerTo::To *CopyOnWritePointerTo:: -get_read_pointer() const { - return (const To *)CopyOnWritePointer::get_read_pointer(); +get_read_pointer(Thread *current_thread) const { + return (const To *)CopyOnWritePointer::get_read_pointer(current_thread); } #endif // COW_THREADED #endif // CPPPARSER @@ -385,7 +391,13 @@ get_read_pointer() const { template INLINE PT(TYPENAME CopyOnWritePointerTo::To) CopyOnWritePointerTo:: get_write_pointer() { - return (To *)(CopyOnWritePointer::get_write_pointer().p()); + // This is necessary because we don't currently have a way to cast between + // two compatible PointerTo types without losing the reference count. + PT(TYPENAME CopyOnWritePointerTo::To) to; + PT(CopyOnWriteObject) from = CopyOnWritePointer::get_write_pointer(); + to.cheat() = (To *)from.p(); + from.cheat() = nullptr; + return to; } #else // COW_THREADED /** diff --git a/panda/src/putil/copyOnWritePointer.cxx b/panda/src/putil/copyOnWritePointer.cxx index e2396e84a9..03ed76e24b 100644 --- a/panda/src/putil/copyOnWritePointer.cxx +++ b/panda/src/putil/copyOnWritePointer.cxx @@ -23,13 +23,11 @@ * This flavor of the method is written for the threaded case. */ CPT(CopyOnWriteObject) CopyOnWritePointer:: -get_read_pointer() const { +get_read_pointer(Thread *current_thread) const { if (_cow_object == (CopyOnWriteObject *)NULL) { return NULL; } - Thread *current_thread = Thread::get_current_thread(); - MutexHolder holder(_cow_object->_lock_mutex); while (_cow_object->_lock_status == CopyOnWriteObject::LS_locked_write) { if (_cow_object->_locking_thread == current_thread) { diff --git a/panda/src/putil/copyOnWritePointer.h b/panda/src/putil/copyOnWritePointer.h index 1a7f0e099d..2178b6b433 100644 --- a/panda/src/putil/copyOnWritePointer.h +++ b/panda/src/putil/copyOnWritePointer.h @@ -48,10 +48,10 @@ public: INLINE bool operator < (const CopyOnWritePointer &other) const; #ifdef COW_THREADED - CPT(CopyOnWriteObject) get_read_pointer() const; + CPT(CopyOnWriteObject) get_read_pointer(Thread *current_thread) const; PT(CopyOnWriteObject) get_write_pointer(); #else - INLINE const CopyOnWriteObject *get_read_pointer() const; + INLINE const CopyOnWriteObject *get_read_pointer(Thread *current_thread) const; INLINE CopyOnWriteObject *get_write_pointer(); #endif // COW_THREADED @@ -93,10 +93,10 @@ public: #endif #ifdef COW_THREADED - INLINE CPT(To) get_read_pointer() const; + INLINE CPT(To) get_read_pointer(Thread *current_thread = Thread::get_current_thread()) const; INLINE PT(To) get_write_pointer(); #else - INLINE const To *get_read_pointer() const; + INLINE const To *get_read_pointer(Thread *current_thread = Thread::get_current_thread()) const; INLINE To *get_write_pointer(); #endif // COW_THREADED diff --git a/panda/src/putil/datagramBuffer.I b/panda/src/putil/datagramBuffer.I new file mode 100644 index 0000000000..123642db9f --- /dev/null +++ b/panda/src/putil/datagramBuffer.I @@ -0,0 +1,68 @@ +/** + * 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 datagramBuffer.I + * @author rdb + * @date 2017-11-07 + */ + +/** + * Initializes an empty datagram buffer. + */ +INLINE DatagramBuffer:: +DatagramBuffer() : + _read_offset(0), + _wrote_first_datagram(false), + _read_first_datagram(false) { +} + +/** + * Initializes the buffer with the given data. + */ +INLINE DatagramBuffer:: +DatagramBuffer(vector_uchar data) : + _data(move(data)), + _read_offset(0), + _wrote_first_datagram(false), + _read_first_datagram(false) { +} + +/** + * Clears the internal buffer. + */ +INLINE void DatagramBuffer:: +clear() { + _data.clear(); + _read_offset = 0; + _wrote_first_datagram = false; + _read_first_datagram = false; +} + +/** + * Returns the internal buffer. + */ +INLINE const vector_uchar &DatagramBuffer:: +get_data() const { + return _data; +} + +/** + * Replaces the data in the internal buffer. + */ +INLINE void DatagramBuffer:: +set_data(vector_uchar data) { + _data = move(data); +} + +/** + * Swaps the data in the internal buffer with that of the other buffer. + */ +INLINE void DatagramBuffer:: +swap_data(vector_uchar &other) { + _data.swap(other); +} diff --git a/panda/src/putil/datagramBuffer.cxx b/panda/src/putil/datagramBuffer.cxx new file mode 100644 index 0000000000..c9831a4f82 --- /dev/null +++ b/panda/src/putil/datagramBuffer.cxx @@ -0,0 +1,152 @@ +/** + * 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 datagramBuffer.cxx + * @author rdb + * @date 2017-11-07 + */ + +#include "datagramBuffer.h" + +/** + * Writes a sequence of bytes to the beginning of the datagram file. This may + * be called any number of times after the file has been opened and before the + * first datagram is written. It may not be called once the first datagram is + * written. + */ +bool DatagramBuffer:: +write_header(const string &header) { + nassertr(!_wrote_first_datagram, false); + + _data.insert(_data.end(), header.begin(), header.end()); + return true; +} + +/** + * Writes the given datagram to the file. Returns true on success, false if + * there is an error. + */ +bool DatagramBuffer:: +put_datagram(const Datagram &data) { + _wrote_first_datagram = true; + + // First, write the size of the upcoming datagram. + size_t num_bytes = data.get_length(); + size_t offset = _data.size(); + + if (num_bytes == (uint32_t)-1 || num_bytes != (uint32_t)num_bytes) { + // Write a large value as a 64-bit size. + _data.resize(offset + num_bytes + 4 + sizeof(uint64_t)); + _data[offset++] = 0xff; + _data[offset++] = 0xff; + _data[offset++] = 0xff; + _data[offset++] = 0xff; + + LittleEndian s(&num_bytes, sizeof(uint64_t)); + memcpy(&_data[offset], s.get_data(), sizeof(uint64_t)); + offset += sizeof(uint64_t); + } else { + // Write a value that fits in 32 bits. + _data.resize(offset + num_bytes + sizeof(uint32_t)); + + LittleEndian s(&num_bytes, sizeof(uint32_t)); + memcpy(&_data[offset], s.get_data(), sizeof(uint32_t)); + offset += sizeof(uint32_t); + } + + // Now, write the datagram itself. + memcpy(&_data[offset], data.get_data(), data.get_length()); + return true; +} + +/** + * This does absolutely nothing. + */ +void DatagramBuffer:: +flush() { +} + +/** + * Reads a sequence of bytes from the beginning of the datagram file. This + * may be called any number of times after the file has been opened and before + * the first datagram is read. It may not be called once the first datagram + * has been read. + */ +bool DatagramBuffer:: +read_header(string &header, size_t num_bytes) { + nassertr(!_read_first_datagram, false); + if (_read_offset + num_bytes > _data.size()) { + return false; + } + + header = string((char *)&_data[_read_offset], num_bytes); + _read_offset += num_bytes; + return true; +} + +/** + * Reads the next datagram from the file. Returns true on success, false if + * there is an error or end of file. + */ +bool DatagramBuffer:: +get_datagram(Datagram &data) { + _read_first_datagram = true; + if (_read_offset + sizeof(uint32_t) > _data.size()) { + // Reached the end of the buffer. + return false; + } + + // First, get the size of the upcoming datagram. + uint32_t num_bytes_32; + LittleEndian s(&_data[_read_offset], 0, sizeof(uint32_t)); + s.store_value(&num_bytes_32, sizeof(uint32_t)); + _read_offset += 4; + + if (num_bytes_32 == 0) { + // A special case for a zero-length datagram: no need to try to read any + // data. + data.clear(); + return true; + } + + size_t num_bytes = (size_t)num_bytes_32; + if (num_bytes_32 == (uint32_t)-1) { + // Another special case for a value larger than 32 bits. + uint64_t num_bytes_64; + LittleEndian s(&_data[_read_offset], 0, sizeof(uint64_t)); + s.store_value(&num_bytes_64, sizeof(uint64_t)); + _read_offset += 8; + + num_bytes = (size_t)num_bytes_64; + nassertr((uint64_t)num_bytes == num_bytes_64, false); + } + + // Make sure we have this much data to read. + nassertr_always(_read_offset + num_bytes <= _data.size(), false); + + data = Datagram(&_data[_read_offset], num_bytes); + _read_offset += num_bytes; + return true; +} + +/** + * Returns true if the buffer has reached the end-of-buffer. This test may + * only be made after a call to read_header() or get_datagram() has failed. + */ +bool DatagramBuffer:: +is_eof() { + return (_read_offset + sizeof(uint32_t)) > _data.size(); +} + +/** + * Returns true if the buffer has reached an error condition. + */ +bool DatagramBuffer:: +is_error() { + return false; +} diff --git a/panda/src/putil/datagramBuffer.h b/panda/src/putil/datagramBuffer.h new file mode 100644 index 0000000000..acb67836f9 --- /dev/null +++ b/panda/src/putil/datagramBuffer.h @@ -0,0 +1,63 @@ +/** + * 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 datagramBuffer.h + * @author rdb + * @date 2017-11-07 + */ + +#ifndef DATAGRAMBUFFER_H +#define DATAGRAMBUFFER_H + +#include "pandabase.h" +#include "datagramSink.h" +#include "vector_uchar.h" + +/** + * This class can be used to write a series of datagrams into a memory buffer. + * It acts as both a datagram sink and generator; you can fill it up with + * datagrams and then read as many datagrams from it. + * + * This uses the same format as DatagramInputFile and DatagramOutputFile, + * meaning that Datagram sizes are always stored little-endian. + */ +class EXPCL_PANDA_PUTIL DatagramBuffer : public DatagramSink, public DatagramGenerator { +PUBLISHED: + INLINE DatagramBuffer(); + INLINE explicit DatagramBuffer(vector_uchar data); + + INLINE void clear(); + +public: + bool write_header(const string &header); + virtual bool put_datagram(const Datagram &data) override; + virtual void flush() override; + + bool read_header(string &header, size_t num_bytes); + virtual bool get_datagram(Datagram &data) override; + virtual bool is_eof() override; + + virtual bool is_error() override; + + INLINE const vector_uchar &get_data() const; + INLINE void set_data(vector_uchar data); + INLINE void swap_data(vector_uchar &other); + +PUBLISHED: + MAKE_PROPERTY(data, get_data, set_data); + +private: + vector_uchar _data; + size_t _read_offset; + bool _wrote_first_datagram; + bool _read_first_datagram; +}; + +#include "datagramBuffer.I" + +#endif diff --git a/panda/src/putil/datagramOutputFile.h b/panda/src/putil/datagramOutputFile.h index d2013c75ee..bd2007219a 100644 --- a/panda/src/putil/datagramOutputFile.h +++ b/panda/src/putil/datagramOutputFile.h @@ -28,14 +28,13 @@ * header followed by a number of datagrams. */ class EXPCL_PANDA_PUTIL DatagramOutputFile : public DatagramSink { -public: +PUBLISHED: INLINE DatagramOutputFile(); INLINE ~DatagramOutputFile(); bool open(const FileReference *file); INLINE bool open(const Filename &filename); bool open(ostream &out, const Filename &filename = Filename()); - INLINE ostream &get_stream(); void close(); @@ -46,10 +45,16 @@ public: virtual bool is_error(); virtual void flush(); +public: virtual const Filename &get_filename(); virtual const FileReference *get_file(); virtual streampos get_file_pos(); + INLINE ostream &get_stream(); + +PUBLISHED: + MAKE_PROPERTY(stream, get_stream); + private: bool _wrote_first_datagram; bool _error; diff --git a/panda/src/putil/iterator_types.h b/panda/src/putil/iterator_types.h index cb405ce18c..2f2966cde7 100644 --- a/panda/src/putil/iterator_types.h +++ b/panda/src/putil/iterator_types.h @@ -14,6 +14,7 @@ #ifndef ITERATOR_TYPES_H #define ITERATOR_TYPES_H +#include "dtoolbase.h" /** * This is an iterator adaptor that converts any iterator that returns a pair @@ -25,9 +26,8 @@ class first_of_pair_iterator : public pair_iterator { public: typedef TYPENAME pair_iterator::value_type::first_type value_type; - first_of_pair_iterator() { } + first_of_pair_iterator() DEFAULT_CTOR; first_of_pair_iterator(const pair_iterator &init) : pair_iterator(init) { } - first_of_pair_iterator(const first_of_pair_iterator ©) : pair_iterator(copy) { } value_type operator *() { return pair_iterator::operator *().first; @@ -44,9 +44,8 @@ class second_of_pair_iterator : public pair_iterator { public: typedef TYPENAME pair_iterator::value_type::second_type value_type; - second_of_pair_iterator() { } + second_of_pair_iterator() DEFAULT_CTOR; second_of_pair_iterator(const pair_iterator &init) : pair_iterator(init) { } - second_of_pair_iterator(const second_of_pair_iterator ©) : pair_iterator(copy) { } value_type operator *() { return pair_iterator::operator *().second; @@ -62,9 +61,8 @@ class typecast_iterator : public base_iterator { public: typedef new_type value_type; - typecast_iterator() { } + typecast_iterator() DEFAULT_CTOR; typecast_iterator(const base_iterator &init) : base_iterator(init) { } - typecast_iterator(const typecast_iterator ©) : base_iterator(copy) { } value_type operator *() { return (new_type)base_iterator::operator *(); diff --git a/panda/src/putil/p3putil_composite1.cxx b/panda/src/putil/p3putil_composite1.cxx index 294f465629..28ff6d6893 100644 --- a/panda/src/putil/p3putil_composite1.cxx +++ b/panda/src/putil/p3putil_composite1.cxx @@ -22,6 +22,7 @@ #include "copyOnWriteObject.cxx" #include "copyOnWritePointer.cxx" #include "cPointerCallbackObject.cxx" +#include "datagramBuffer.cxx" #include "datagramInputFile.cxx" #include "datagramOutputFile.cxx" #include "doubleBitMask.cxx" diff --git a/panda/src/putil/simpleHashMap.I b/panda/src/putil/simpleHashMap.I index d68fae9542..78d4bdf2bd 100644 --- a/panda/src/putil/simpleHashMap.I +++ b/panda/src/putil/simpleHashMap.I @@ -15,16 +15,53 @@ * */ template -INLINE SimpleHashMap:: +CONSTEXPR SimpleHashMap:: SimpleHashMap(const Compare &comp) : - _table(NULL), - _deleted_chain(NULL), + _table(nullptr), + _deleted_chain(nullptr), _table_size(0), _num_entries(0), _comp(comp) { } +/** + * + */ +template +INLINE SimpleHashMap:: +SimpleHashMap(const SimpleHashMap ©) : + _table_size(copy._table_size), + _num_entries(copy._num_entries), + _comp(copy._comp) { + + // We allocate enough bytes for _table_size elements of TableEntry, plus + // _table_size * 4 more ints at the end (for the index array). + size_t alloc_size = _table_size * (sizeof(TableEntry) + sizeof(int) * sparsity); + + _deleted_chain = memory_hook->get_deleted_chain(alloc_size); + _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none()); + memcpy(_table, copy._table, alloc_size); +} + +/** + * + */ +template +INLINE SimpleHashMap:: +SimpleHashMap(SimpleHashMap &&from) NOEXCEPT : + _table(from._table), + _deleted_chain(from._deleted_chain), + _table_size(from._table_size), + _num_entries(from._num_entries), + _comp(move(from._comp)) +{ + from._table = nullptr; + from._deleted_chain = nullptr; + from._table_size = 0; + from._num_entries = 0; +} + /** * */ @@ -34,6 +71,48 @@ INLINE SimpleHashMap:: clear(); } +/** + * + */ +template +INLINE SimpleHashMap &SimpleHashMap:: +operator = (const SimpleHashMap ©) { + if (this != ©) { + _table_size = copy._table_size; + _num_entries = copy._num_entries; + _comp = copy._comp; + + // We allocate enough bytes for _table_size elements of TableEntry, plus + // _table_size * 4 more ints at the end (for the index array). + size_t alloc_size = _table_size * (sizeof(TableEntry) + sizeof(int) * sparsity); + + _deleted_chain = memory_hook->get_deleted_chain(alloc_size); + _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none()); + memcpy(_table, copy._table, alloc_size); + } + return *this; +} + +/** + * + */ +template +INLINE SimpleHashMap &SimpleHashMap:: +operator = (SimpleHashMap &&from) NOEXCEPT { + if (this != &from) { + _table = from._table; + _deleted_chain = from._deleted_chain; + _table_size = from._table_size; + _num_entries = from._num_entries; + _comp = move(from._comp); + + from._table = nullptr; + from._deleted_chain = nullptr; + from._table_size = 0; + from._num_entries = 0; + } +} + /** * Quickly exchanges the contents of this map and the other map. */ @@ -69,29 +148,13 @@ find(const Key &key) const { return -1; } - size_t index = get_hash(key); - if (!has_element(index)) { + int slot = find_slot(key); + if (slot >= 0) { + return get_index_array()[slot]; + } else { + // The key is not in the table. return -1; } - if (is_element(index, key)) { - return index; - } - - // There was some other key at the hashed slot. That's a hash conflict. - // Maybe our entry was recorded at a later slot position; scan the - // subsequent positions until we find the entry or an unused slot, - // indicating the end of the scan. - size_t i = index; - i = (i + 1) & (_table_size - 1); - while (i != index && has_element(i)) { - if (is_element(i, key)) { - return i; - } - i = (i + 1) & (_table_size - 1); - } - - // The key is not in the table. - return -1; } /** @@ -105,23 +168,23 @@ store(const Key &key, const Value &data) { // Special case: the first key in an empty table. nassertr(_num_entries == 0, -1); new_table(); - size_t index = get_hash(key); - store_new_element(index, key, data); - ++_num_entries; + int pos = store_new_element(get_hash(key), key, data); #ifdef _DEBUG - nassertr(validate(), index); + nassertr(validate(), pos); #endif - return index; + return pos; } + consider_expand_table(); - size_t index = get_hash(key); - if (!has_element(index)) { + const int *index_array = get_index_array(); + size_t hash = get_hash(key); + int index = index_array[hash]; + if (index < 0) { // This element is not already in the map; add it. if (consider_expand_table()) { return store(key, data); } - store_new_element(index, key, data); - ++_num_entries; + index = store_new_element(hash, key, data); #ifdef _DEBUG nassertr(validate(), index); #endif @@ -129,7 +192,7 @@ store(const Key &key, const Value &data) { } if (is_element(index, key)) { // This element is already in the map; replace the data at that key. - _table[index]._data = data; + set_data(index, data); #ifdef _DEBUG nassertr(validate(), index); #endif @@ -138,28 +201,27 @@ store(const Key &key, const Value &data) { // There was some other key at the hashed slot. That's a hash conflict. // Record this entry at a later position. - size_t i = index; - i = (i + 1) & (_table_size - 1); - while (i != index) { - if (!has_element(i)) { + size_t slot = next_hash(hash); + while (slot != hash) { + index = index_array[slot]; + if (index < 0) { if (consider_expand_table()) { return store(key, data); } - store_new_element(i, key, data); - ++_num_entries; + index = store_new_element(slot, key, data); #ifdef _DEBUG - nassertr(validate(), i); + nassertr(validate(), index); #endif - return i; + return index; } - if (is_element(i, key)) { - _table[i]._data = data; + if (is_element(index, key)) { + set_data(index, data); #ifdef _DEBUG - nassertr(validate(), i); + nassertr(validate(), index); #endif - return i; + return index; } - i = (i + 1) & (_table_size - 1); + slot = next_hash(slot); } // Shouldn't get here unless _num_entries == _table_size, which shouldn't be @@ -171,15 +233,82 @@ store(const Key &key, const Value &data) { /** * Removes the indicated key and its associated data from the table. Returns * true if the key was removed, false if it was not present. + * + * Iterator safety: To perform removal during iteration, revisit the element + * at the current index if removal succeeds, keeping in mind that the number + * of elements has now shrunk by one. */ template INLINE bool SimpleHashMap:: remove(const Key &key) { - int index = find(key); - if (index == -1) { + if (_num_entries == 0) { + // Special case: the table is empty. return false; } - remove_element(index); + + int *index_array = get_index_array(); + size_t slot = (size_t)find_slot(key); + if (slot == (size_t)-1) { + // It wasn't in the hash map. + return false; + } + + // Now remove this element. + size_t last = _num_entries - 1; + size_t index = (size_t)index_array[slot]; + if (index < _num_entries) { + // Find the last element in the index array. + int other_slot = find_slot(_table[last]._key); + nassertr(other_slot != -1, false); + nassertr(index_array[(size_t)other_slot] == (int)last, false); + + // Swap it with the last one, so that we don't get any gaps in the table + // of entries. + _table[index] = move(_table[last]); + index_array[(size_t)other_slot] = index; + } + + _table[last].~TableEntry(); + _num_entries = last; + + // It's important that we do this after the second find_slot, above, since + // it might otherwise fail due to the unexpected gap, since some indices may + // not be at their ideal positions right now. + index_array[slot] = -1; + + //if (consider_shrink_table()) { + // // No need to worry about that gap; resize_table() will rebuild the index. + // return true; + //} + + // Now we have put a hole in the index array. If there was a hash conflict + // in the slot after this one, we have to move it down to close the hole. + slot = next_hash(slot); + while (has_slot(slot)) { + size_t index = (size_t)index_array[slot]; + size_t wants_slot = get_hash(_table[index]._key); + if (wants_slot != slot) { + // This one was a hash conflict; try to put it where it belongs. We + // can't just put it in n, since maybe it belongs somewhere after n. + while (wants_slot != slot && has_slot(wants_slot)) { + wants_slot = next_hash(wants_slot); + } + if (wants_slot != slot) { + // We just have to flip the slots in the index array; we can keep the + // elements in the table where they are. + index_array[wants_slot] = index; + index_array[slot] = -1; + } + } + + // Continue until we encounter the next unused slot. Until we do, we + // can't be sure we've found all of the potential hash conflicts. + slot = next_hash(slot); + } + +#ifdef _DEBUG + nassertr(validate(), true); +#endif return true; } @@ -190,15 +319,13 @@ template void SimpleHashMap:: clear() { if (_table_size != 0) { - for (size_t i = 0; i < _table_size; ++i) { - if (has_element(i)) { - clear_element(i); - } + for (size_t i = 0; i < _num_entries; ++i) { + _table[i].~TableEntry(); } _deleted_chain->deallocate(_table, TypeHandle::none()); - _table = NULL; - _deleted_chain = NULL; + _table = nullptr; + _deleted_chain = nullptr; _table_size = 0; _num_entries = 0; } @@ -219,131 +346,88 @@ operator [] (const Key &key) { } /** - * Returns the total number of slots in the table. + * Returns the total number of entries in the table. Same as get_num_entries. */ template -INLINE size_t SimpleHashMap:: -get_size() const { - return _table_size; +CONSTEXPR size_t SimpleHashMap:: +size() const { + return _num_entries; } /** - * Returns true if there is an element stored in the nth slot, false - * otherwise. + * Returns the key in the nth entry of the table. * - * n should be in the range 0 <= n < get_size(). - */ -template -INLINE bool SimpleHashMap:: -has_element(int n) const { - nassertr(n >= 0 && n < (int)_table_size, false); - return (get_exists_array()[n] != 0); -} - -/** - * Returns the key in the nth slot of the table. - * - * It is an error to call this if there is nothing stored in the nth slot (use - * has_element() to check this first). n should be in the range 0 <= n < - * get_size(). + * @param n should be in the range 0 <= n < size(). */ template INLINE const Key &SimpleHashMap:: -get_key(int n) const { - nassertr(has_element(n), _table[n]._key); +get_key(size_t n) const { + nassertr(n < _num_entries, _table[n]._key); return _table[n]._key; } /** - * Returns the data in the nth slot of the table. + * Returns the data in the nth entry of the table. * - * It is an error to call this if there is nothing stored in the nth slot (use - * has_element() to check this first). n should be in the range 0 <= n < - * get_size(). + * @param n should be in the range 0 <= n < size(). */ template INLINE const Value &SimpleHashMap:: -get_data(int n) const { - nassertr(has_element(n), _table[n]._data); - return _table[n]._data; +get_data(size_t n) const { + nassertr(n < _num_entries, _table[n].get_data()); + return _table[n].get_data(); } /** - * Returns a modifiable reference to the data in the nth slot of the table. + * Returns a modifiable reference to the data in the nth entry of the table. * - * It is an error to call this if there is nothing stored in the nth slot (use - * has_element() to check this first). n should be in the range 0 <= n < - * get_size(). + * @param n should be in the range 0 <= n < size(). */ template INLINE Value &SimpleHashMap:: -modify_data(int n) { - nassertr(has_element(n), _table[n]._data); - return _table[n]._data; +modify_data(size_t n) { + nassertr(n < _num_entries, _table[n].modify_data()); + return _table[n].modify_data(); } /** - * Changes the data for the nth slot of the table. + * Changes the data for the nth entry of the table. * - * It is an error to call this if there is nothing stored in the nth slot (use - * has_element() to check this first). n should be in the range 0 <= n < - * get_size(). + * @param n should be in the range 0 <= n < size(). */ template INLINE void SimpleHashMap:: -set_data(int n, const Value &data) { - nassertv(has_element(n)); - _table[n]._data = data; +set_data(size_t n, const Value &data) { + nassertv(n < _num_entries); + _table[n].set_data(data); } /** - * Removes the nth slot from the table. + * Changes the data for the nth entry of the table. * - * It is an error to call this if there is nothing stored in the nth slot (use - * has_element() to check this first). n should be in the range 0 <= n < - * get_size(). + * @param n should be in the range 0 <= n < size(). + */ +template +INLINE void SimpleHashMap:: +set_data(size_t n, Value &&data) { + nassertv(n < _num_entries); + _table[n].set_data(move(data)); +} + +/** + * Removes the nth entry from the table. + * + * @param n should be in the range 0 <= n < size(). */ template void SimpleHashMap:: -remove_element(int n) { - nassertv(has_element(n)); - - clear_element(n); - nassertv(_num_entries > 0); - --_num_entries; - - // Now we have put a hole in the table. If there was a hash conflict in the - // slot following this one, we have to move it down to close the hole. - size_t i = (size_t)n; - i = (i + 1) & (_table_size - 1); - while (has_element(i)) { - size_t wants_index = get_hash(_table[i]._key); - if (wants_index != i) { - // This one was a hash conflict; try to put it where it belongs. We - // can't just put it in n, since maybe it belongs somewhere after n. - while (wants_index != i && has_element(wants_index)) { - wants_index = (wants_index + 1) & (_table_size - 1); - } - if (wants_index != i) { - store_new_element(wants_index, _table[i]._key, _table[i]._data); - clear_element(i); - } - } - - // Continue until we encounter the next unused slot. Until we do, we - // can't be sure we've found all of the potential hash conflicts. - i = (i + 1) & (_table_size - 1); - } - -#ifdef _DEBUG - nassertv(validate()); -#endif +remove_element(size_t n) { + nassertv(n < _num_entries); + remove(_table[n]._key); } /** - * Returns the number of active entries in the table. This is not necessarily - * related to the number of slots in the table as reported by get_size(). Use - * get_size() to iterate through all of the slots, not get_num_entries(). + * Returns the number of active entries in the table. Same as size(). */ template INLINE size_t SimpleHashMap:: @@ -352,7 +436,7 @@ get_num_entries() const { } /** - * Returns true if the table is empty; i.e. get_num_entries() == 0. + * Returns true if the table is empty; i.e. get_num_entries() == 0. */ template INLINE bool SimpleHashMap:: @@ -367,17 +451,20 @@ template void SimpleHashMap:: output(ostream &out) const { out << "SimpleHashMap (" << _num_entries << " entries): ["; - for (size_t i = 0; i < _table_size; ++i) { - if (!has_element(i)) { + const int *index_array = get_index_array(); + size_t num_slots = _table_size * sparsity; + for (size_t slot = 0; slot < num_slots; ++slot) { + if (!has_slot(slot)) { out << " *"; } else { - out << " " << _table[i]._key; - size_t index = get_hash(_table[i]._key); - if (index != i) { + size_t index = (size_t)index_array[slot]; + out << " " << index; + size_t ideal_slot = get_hash(_table[index]._key); + if (ideal_slot != slot) { // This was misplaced as the result of a hash conflict. Report how // far off it is. - out << "(" << ((_table_size + i - index) & (_table_size - 1)) << ")"; + out << "(" << ((_table_size + slot - ideal_slot) & (num_slots - 1)) << ")"; } } } @@ -392,6 +479,9 @@ void SimpleHashMap:: write(ostream &out) const { output(out); out << "\n"; + for (size_t i = 0; i < _num_entries; ++i) { + out << " " << _table[i]._key << " (hash " << get_hash(_table[i]._key) << ")\n"; + } } /** @@ -403,19 +493,31 @@ bool SimpleHashMap:: validate() const { size_t count = 0; - for (size_t i = 0; i < _table_size; ++i) { - if (has_element(i)) { + const int *index_array = get_index_array(); + size_t num_slots = _table_size * sparsity; + for (size_t slot = 0; slot < num_slots; ++slot) { + if (has_slot(slot)) { + size_t index = (size_t)index_array[slot]; ++count; - size_t ideal_index = get_hash(_table[i]._key); - size_t wants_index = ideal_index; - while (wants_index != i && has_element(wants_index)) { - wants_index = (wants_index + 1) & (_table_size - 1); - } - if (wants_index != i) { + if (index >= _num_entries) { util_cat.error() - << "SimpleHashMap is invalid: key " << _table[i]._key - << " should be in slot " << wants_index << " instead of " - << i << " (ideal is " << ideal_index << ")\n"; + << "SimpleHashMap " << this << " is invalid: slot " << slot + << " contains index " << index << " which is past the end of the" + " table\n"; + write(util_cat.error(false)); + return false; + } + nassertd(index < _num_entries) continue; + size_t ideal_slot = get_hash(_table[index]._key); + size_t wants_slot = ideal_slot; + while (wants_slot != slot && has_slot(wants_slot)) { + wants_slot = next_hash(wants_slot); + } + if (wants_slot != slot) { + util_cat.error() + << "SimpleHashMap " << this << " is invalid: key " + << _table[index]._key << " should be in slot " << wants_slot + << " instead of " << slot << " (ideal is " << ideal_slot << ")\n"; write(util_cat.error(false)); return false; } @@ -424,7 +526,7 @@ validate() const { if (count != _num_entries) { util_cat.error() - << "SimpleHashMap is invalid: reports " << _num_entries + << "SimpleHashMap " << this << " is invalid: reports " << _num_entries << " entries, actually has " << count << "\n"; write(util_cat.error(false)); return false; @@ -447,7 +549,57 @@ get_hash(const Key &key) const { return (size_t)floor(f * _table_size); */ - return ((_comp(key) * (size_t)9973) >> 8) & (_table_size - 1); + return ((_comp(key) * (size_t)9973) >> 8) & ((_table_size * sparsity) - 1); +} + +/** + * Given a hash value, increments it, looping around the hash space. + */ +template +INLINE size_t SimpleHashMap:: +next_hash(size_t hash) const { + return (hash + 1) & ((_table_size * sparsity) - 1); +} + +/** + * Finds the slot in which the given key should fit. + */ +template +INLINE int SimpleHashMap:: +find_slot(const Key &key) const { + const int *index_array = get_index_array(); + size_t hash = get_hash(key); + int index = index_array[hash]; + if (index < 0) { + return -1; + } + + if (is_element((size_t)index, key)) { + return hash; + } + + // There was some other key at the hashed slot. That's a hash conflict. + // Maybe our entry was recorded at a later slot position; scan the + // subsequent positions until we find the entry or an unused slot, + // indicating the end of the scan. + size_t slot = next_hash(hash); + while (slot != hash && has_slot(slot)) { + if (is_element((size_t)index_array[slot], key)) { + return (int)slot; + } + slot = next_hash(slot); + } + + return -1; +} + +/** + * Returns true if the given slot refers to an element. + */ +template +INLINE bool SimpleHashMap:: +has_slot(size_t slot) const { + return get_index_array()[slot] >= 0; } /** @@ -455,41 +607,34 @@ get_hash(const Key &key) const { */ template INLINE bool SimpleHashMap:: -is_element(int n, const Key &key) const { - nassertr(has_element(n), false); +is_element(size_t n, const Key &key) const { + nassertr(n < _num_entries, false); return _comp.is_equal(_table[n]._key, key); } /** - * Constructs a new TableEntry at position n, storing the indicated key and - * value. + * Constructs a new TableEntry with the given slot, storing the indicated key + * and value. */ template -INLINE void SimpleHashMap:: -store_new_element(int n, const Key &key, const Value &data) { - new(&_table[n]) TableEntry(key, data); - get_exists_array()[n] = true; +INLINE size_t SimpleHashMap:: +store_new_element(size_t slot, const Key &key, const Value &data) { + size_t index = _num_entries++; + new(&_table[index]) TableEntry(key, data); + nassertr(get_index_array()[slot] == -1, index) + get_index_array()[slot] = index; + return index; } /** - * Destructs the TableEntry at position n. - */ -template -INLINE void SimpleHashMap:: -clear_element(int n) { - _table[n].~TableEntry(); - get_exists_array()[n] = false; -} - -/** - * Returns the beginning of the array of _table_size unsigned chars that are - * the boolean flags for whether each element exists (has been constructed) + * Returns the beginning of the array of _table_size ints that are the indices + * pointing to the location within the table where the elements are stored. * within the table. */ template -INLINE unsigned char *SimpleHashMap:: -get_exists_array() const { - return (unsigned char *)(_table + _table_size); +INLINE int *SimpleHashMap:: +get_index_array() const { + return (int *)(_table + _table_size); } /** @@ -502,15 +647,15 @@ new_table() { // Pick a good initial table size. For now, we make it really small. Maybe // that's the right answer. - _table_size = 4; + _table_size = 2; // We allocate enough bytes for _table_size elements of TableEntry, plus - // _table_size more bytes at the end (for the exists array). - size_t alloc_size = _table_size * sizeof(TableEntry) + _table_size; + // _table_size * 4 more ints at the end (for the index array). + size_t alloc_size = _table_size * (sizeof(TableEntry) + sizeof(int) * sparsity); _deleted_chain = memory_hook->get_deleted_chain(alloc_size); _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none()); - memset(get_exists_array(), 0, _table_size); + memset(get_index_array(), -1, _table_size * sizeof(int) * sparsity); } /** @@ -520,62 +665,78 @@ new_table() { template INLINE bool SimpleHashMap:: consider_expand_table() { - if (_num_entries >= (_table_size >> 1)) { - expand_table(); + if (_num_entries < _table_size) { + return false; + } else { + resize_table(_table_size << 1); return true; } - return false; } /** - * Doubles the size of the existing table. + * Shrinks the table if the allocated storage is significantly larger than the + * number of elements in it. Returns true if shrunk, false otherwise. + */ +template +INLINE bool SimpleHashMap:: +consider_shrink_table() { + // If the number of elements gets less than an eighth of the table size, we + // know it's probably time to shrink it down. + if (_table_size <= 16 || _num_entries >= (_table_size >> 3)) { + return false; + } else { + size_t new_size = _table_size; + do { + new_size >>= 1; + } while (new_size >= 16 && _num_entries < (new_size >> 2)); + resize_table(new_size); + return true; + } +} + +/** + * Resizes the existing table. */ template void SimpleHashMap:: -expand_table() { +resize_table(size_t new_size) { nassertv(_table_size != 0); + nassertv(new_size >= _num_entries); - SimpleHashMap old_map(_comp); - swap(old_map); + DeletedBufferChain *old_chain = _deleted_chain; + TableEntry *old_table = _table; - // Double the table size. - size_t old_table_size = old_map._table_size; - _table_size = (old_table_size << 1); - nassertv(_table == NULL); + _table_size = new_size; // We allocate enough bytes for _table_size elements of TableEntry, plus - // _table_size more bytes at the end (for the exists array). - size_t alloc_size = _table_size * sizeof(TableEntry) + _table_size; + // _table_size * sparsity more ints at the end (for the sparse index array). + size_t alloc_size = _table_size * sizeof(TableEntry) + _table_size * sparsity * sizeof(int); _deleted_chain = memory_hook->get_deleted_chain(alloc_size); _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none()); - unsigned char *exists_array = get_exists_array(); - memset(exists_array, 0, _table_size); - nassertv(_num_entries == 0); + int *index_array = get_index_array(); + memset(index_array, -1, _table_size * sizeof(int) * sparsity); - // Now copy the entries from the old table into the new table. - for (size_t i = 0; i < old_table_size; ++i) { - if (old_map.has_element(i)) { - size_t new_index = get_hash(old_map._table[i]._key); + // Now copy the entries from the old table into the new table. We don't + // have to reorder these, fortunately. Hopefully, a smart compiler will + // optimize this to a memcpy. + for (size_t i = 0; i < _num_entries; ++i) { + new(&_table[i]) TableEntry(move(old_table[i])); + old_table[i].~TableEntry(); + } - while (exists_array[new_index] != 0) { - // Hash conflict; look for a better spot. This has to succeed. - new_index = (new_index + 1) & (_table_size - 1); - } + // We don't need this old thing anymore. + old_chain->deallocate(old_table, TypeHandle::none()); -#ifdef USE_MOVE_SEMANTICS - // Use C++11 rvalue references to invoke the move constructor, which may - // be more efficient. - new(&_table[new_index]) TableEntry(move(old_map._table[i])); -#else - new(&_table[new_index]) TableEntry(old_map._table[i]); -#endif - exists_array[new_index] = true; - ++_num_entries; + // Reindex the table. + for (size_t i = 0; i < _num_entries; ++i) { + size_t slot = get_hash(_table[i]._key); + + while (has_slot(slot)) { + // Hash conflict; look for a better spot. This has to succeed. + slot = next_hash(slot); } + index_array[slot] = (int)i; } nassertv(validate()); - nassertv(old_map.validate()); - - nassertv(_num_entries == old_map._num_entries); } diff --git a/panda/src/putil/simpleHashMap.h b/panda/src/putil/simpleHashMap.h index 7954cd943e..50a7e3af12 100644 --- a/panda/src/putil/simpleHashMap.h +++ b/panda/src/putil/simpleHashMap.h @@ -18,20 +18,81 @@ #include "pvector.h" #include "config_util.h" +/** + * Entry in the SimpleHashMap. + */ +template +class SimpleKeyValuePair { +public: + INLINE SimpleKeyValuePair(const Key &key, const Value &data) : + _key(key), + _data(data) {} + + Key _key; + + ALWAYS_INLINE const Value &get_data() const { + return _data; + } + ALWAYS_INLINE Value &modify_data() { + return _data; + } + ALWAYS_INLINE void set_data(const Value &data) { + _data = data; + } + ALWAYS_INLINE void set_data(Value &&data) { + _data = move(data); + } + +private: + Value _data; +}; + +/** + * Specialisation of SimpleKeyValuePair to not waste memory for nullptr_t + * values. This allows effectively using SimpleHashMap as a set. + */ +template +class SimpleKeyValuePair { +public: + INLINE SimpleKeyValuePair(const Key &key, nullptr_t data) : + _key(key) {} + + Key _key; + + ALWAYS_INLINE_CONSTEXPR static nullptr_t get_data() { return nullptr; } + ALWAYS_INLINE_CONSTEXPR static nullptr_t modify_data() { return nullptr; } + ALWAYS_INLINE static void set_data(nullptr_t) {} +}; + /** * This template class implements an unordered map of keys to data, - * implemented as a hashtable. It is similar to STL's hash_map, but (a) it - * has a simpler interface (we don't mess around with iterators), (b) it wants - * an additional method on the Compare object, Compare::is_equal(a, b), and - * (c) it doesn't depend on the system STL providing hash_map. + * implemented as a hashtable. It is similar to STL's hash_map, but + * (a) it has a simpler interface (we don't mess around with iterators), + * (b) it wants an additional method on the Compare object, + Compare::is_equal(a, b), + * (c) it doesn't depend on the system STL providing hash_map, + * (d) it allows for efficient iteration over the entries, + * (e) permits removal and resizing during forward iteration, and + * (f) it has a constexpr constructor. + * + * It can also be used as a set, by using nullptr_t as Value typename. */ template > > class SimpleHashMap { + // Per-entry overhead is determined by sizeof(int) * sparsity. Should be a + // power of two. + static const unsigned int sparsity = 2u; + public: #ifndef CPPPARSER - INLINE SimpleHashMap(const Compare &comp = Compare()); + CONSTEXPR SimpleHashMap(const Compare &comp = Compare()); + INLINE SimpleHashMap(const SimpleHashMap ©); + INLINE SimpleHashMap(SimpleHashMap &&from) NOEXCEPT; INLINE ~SimpleHashMap(); + INLINE SimpleHashMap &operator = (const SimpleHashMap ©); + INLINE SimpleHashMap &operator = (SimpleHashMap &&from) NOEXCEPT; + INLINE void swap(SimpleHashMap &other); int find(const Key &key) const; @@ -40,14 +101,14 @@ public: void clear(); INLINE Value &operator [] (const Key &key); + CONSTEXPR size_t size() const; - INLINE size_t get_size() const; - INLINE bool has_element(int n) const; - INLINE const Key &get_key(int n) const; - INLINE const Value &get_data(int n) const; - INLINE Value &modify_data(int n); - INLINE void set_data(int n, const Value &data); - void remove_element(int n); + INLINE const Key &get_key(size_t n) const; + INLINE const Value &get_data(size_t n) const; + INLINE Value &modify_data(size_t n); + INLINE void set_data(size_t n, const Value &data); + INLINE void set_data(size_t n, Value &&data); + void remove_element(size_t n); INLINE size_t get_num_entries() const; INLINE bool is_empty() const; @@ -56,37 +117,24 @@ public: void write(ostream &out) const; bool validate() const; + INLINE bool consider_shrink_table(); + private: - class TableEntry; - INLINE size_t get_hash(const Key &key) const; + INLINE size_t next_hash(size_t hash) const; - INLINE bool is_element(int n, const Key &key) const; - INLINE void store_new_element(int n, const Key &key, const Value &data); - INLINE void clear_element(int n); - INLINE unsigned char *get_exists_array() const; + INLINE int find_slot(const Key &key) const; + INLINE bool has_slot(size_t slot) const; + INLINE bool is_element(size_t n, const Key &key) const; + INLINE size_t store_new_element(size_t n, const Key &key, const Value &data); + INLINE int *get_index_array() const; void new_table(); INLINE bool consider_expand_table(); - void expand_table(); - - class TableEntry { - public: - INLINE TableEntry(const Key &key, const Value &data) : - _key(key), - _data(data) {} - INLINE TableEntry(const TableEntry ©) : - _key(copy._key), - _data(copy._data) {} -#ifdef USE_MOVE_SEMANTICS - INLINE TableEntry(TableEntry &&from) NOEXCEPT : - _key(move(from._key)), - _data(move(from._data)) {} -#endif - Key _key; - Value _data; - }; + void resize_table(size_t new_size); +public: + typedef SimpleKeyValuePair TableEntry; TableEntry *_table; DeletedBufferChain *_deleted_chain; size_t _table_size; diff --git a/panda/src/putil/sparseArray.I b/panda/src/putil/sparseArray.I index fb459806d0..682d5f93be 100644 --- a/panda/src/putil/sparseArray.I +++ b/panda/src/putil/sparseArray.I @@ -18,26 +18,6 @@ INLINE SparseArray:: SparseArray() : _inverse(false) { } -/** - * - */ -INLINE SparseArray:: -SparseArray(const SparseArray ©) : - _subranges(copy._subranges), - _inverse(copy._inverse) -{ -} - -/** - * - */ -INLINE SparseArray &SparseArray:: -operator = (const SparseArray ©) { - _subranges = copy._subranges; - _inverse = copy._inverse; - return *this; -} - /** * Returns a SparseArray with an infinite array of bits, all on. */ @@ -62,7 +42,9 @@ all_off() { INLINE SparseArray SparseArray:: lower_on(int on_bits) { SparseArray result; - result.set_range(0, on_bits); + if (on_bits > 0) { + result._subranges.push_back(Subrange(0, on_bits)); + } return result; } @@ -86,13 +68,6 @@ range(int low_bit, int size) { return result; } -/** - * - */ -INLINE SparseArray:: -~SparseArray() { -} - /** * Returns true if there is a maximum number of bits that may be stored in * this structure, false otherwise. If this returns true, the number may be diff --git a/panda/src/putil/sparseArray.h b/panda/src/putil/sparseArray.h index 9ebd205bac..a372fc240e 100644 --- a/panda/src/putil/sparseArray.h +++ b/panda/src/putil/sparseArray.h @@ -42,8 +42,6 @@ class DatagramIterator; class EXPCL_PANDA_PUTIL SparseArray { PUBLISHED: INLINE SparseArray(); - INLINE SparseArray(const SparseArray ©); - INLINE SparseArray &operator = (const SparseArray ©); SparseArray(const BitArray &from); INLINE static SparseArray all_on(); @@ -52,8 +50,6 @@ PUBLISHED: INLINE static SparseArray bit(int index); INLINE static SparseArray range(int low_bit, int size); - INLINE ~SparseArray(); - INLINE static bool has_max_num_bits(); INLINE static int get_max_num_bits(); diff --git a/panda/src/putil/typedWritable.I b/panda/src/putil/typedWritable.I index dc40248c4a..21f86f03a4 100644 --- a/panda/src/putil/typedWritable.I +++ b/panda/src/putil/typedWritable.I @@ -53,22 +53,21 @@ get_bam_modified() const { return _bam_modified; } - /** * Converts the TypedWritable object into a single stream of data using a - * BamWriter, and returns that data as a string string. Returns empty string - * on failure. + * BamWriter, and returns that data as a bytes object. Returns an empty bytes + * object on failure. * * This is a convenience method particularly useful for cases when you are * only serializing a single object. If you have many objects to process, it * is more efficient to use the same BamWriter to serialize all of them * together. */ -INLINE string TypedWritable:: +INLINE vector_uchar TypedWritable:: encode_to_bam_stream() const { - string data; + vector_uchar data; if (!encode_to_bam_stream(data)) { - return string(); + data.clear(); } return data; } diff --git a/panda/src/putil/typedWritable.cxx b/panda/src/putil/typedWritable.cxx index ed29f779e4..7cc9d93cbd 100644 --- a/panda/src/putil/typedWritable.cxx +++ b/panda/src/putil/typedWritable.cxx @@ -14,8 +14,7 @@ #include "typedWritable.h" #include "bamWriter.h" #include "bamReader.h" -#include "datagramOutputFile.h" -#include "datagramInputFile.h" +#include "datagramBuffer.h" #include "lightMutexHolder.h" #include "bam.h" @@ -134,52 +133,43 @@ as_reference_count() { * together. */ bool TypedWritable:: -encode_to_bam_stream(string &data, BamWriter *writer) const { +encode_to_bam_stream(vector_uchar &data, BamWriter *writer) const { data.clear(); - ostringstream stream; - // We use nested scoping to ensure the destructors get called in the right - // order. - { - DatagramOutputFile dout; - if (!dout.open(stream)) { + DatagramBuffer buffer; + if (writer == nullptr) { + // Create our own writer. + + if (!buffer.write_header(_bam_header)) { return false; } - if (writer == NULL) { - // Create our own writer. + BamWriter writer(&buffer); + if (!writer.init()) { + return false; + } - if (!dout.write_header(_bam_header)) { - return false; - } - - BamWriter writer(&dout); - if (!writer.init()) { - return false; - } - - if (!writer.write_object(this)) { - return false; - } - } else { - // Use the existing writer. - writer->set_target(&dout); - bool result = writer->write_object(this); - writer->set_target(NULL); - if (!result) { - return false; - } + if (!writer.write_object(this)) { + return false; + } + } else { + // Use the existing writer. + writer->set_target(&buffer); + bool result = writer->write_object(this); + writer->set_target(nullptr); + if (!result) { + return false; } } - data = stream.str(); + buffer.swap_data(data); return true; } /** - * Reads the string created by a previous call to encode_to_bam_stream(), and - * extracts the single object on that string. Returns true on success, false - * on on error. + * Reads the bytes created by a previous call to encode_to_bam_stream(), and + * extracts the single object on those bytes. Returns true on success, false + * on error. * * This variant sets the TypedWritable and ReferenceCount pointers separately; * both are pointers to the same object. The reference count is not @@ -198,18 +188,14 @@ encode_to_bam_stream(string &data, BamWriter *writer) const { */ bool TypedWritable:: decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, - const string &data, BamReader *reader) { - istringstream stream(data); + vector_uchar data, BamReader *reader) { - DatagramInputFile din; - if (!din.open(stream)) { - return false; - } + DatagramBuffer buffer(move(data)); if (reader == NULL) { // Create a local reader. string head; - if (!din.read_header(head, _bam_header.size())) { + if (!buffer.read_header(head, _bam_header.size())) { return false; } @@ -217,7 +203,7 @@ decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, return false; } - BamReader reader(&din); + BamReader reader(&buffer); if (!reader.init()) { return false; } @@ -241,7 +227,7 @@ decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, } else { // Use the existing reader. - reader->set_source(&din); + reader->set_source(&buffer); if (!reader->read_object(ptr, ref_ptr)) { reader->set_source(NULL); return false; diff --git a/panda/src/putil/typedWritable.h b/panda/src/putil/typedWritable.h index 8278efba95..b13a1991fb 100644 --- a/panda/src/putil/typedWritable.h +++ b/panda/src/putil/typedWritable.h @@ -19,6 +19,7 @@ #include "pvector.h" #include "lightMutex.h" #include "updateSeq.h" +#include "vector_uchar.h" class BamReader; class BamWriter; @@ -62,11 +63,11 @@ PUBLISHED: EXTENSION(PyObject *__reduce__(PyObject *self) const); EXTENSION(PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const); - INLINE string encode_to_bam_stream() const; - bool encode_to_bam_stream(string &data, BamWriter *writer = NULL) const; + INLINE vector_uchar encode_to_bam_stream() const; + bool encode_to_bam_stream(vector_uchar &data, BamWriter *writer = NULL) const; static bool decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, - const string &data, + vector_uchar data, BamReader *reader = NULL); private: diff --git a/panda/src/putil/typedWritableReferenceCount.cxx b/panda/src/putil/typedWritableReferenceCount.cxx index 40a1db268b..ba4e2c49e4 100644 --- a/panda/src/putil/typedWritableReferenceCount.cxx +++ b/panda/src/putil/typedWritableReferenceCount.cxx @@ -26,8 +26,8 @@ as_reference_count() { } /** - * Reads the string created by a previous call to encode_to_bam_stream(), and - * extracts and returns the single object on that string. Returns NULL on + * Reads the bytes created by a previous call to encode_to_bam_stream(), and + * extracts and returns the single object on those bytes. Returns NULL on * error. * * This method is intended to replace decode_raw_from_bam_stream() when you @@ -37,13 +37,13 @@ as_reference_count() { * reference count on the return value. */ PT(TypedWritableReferenceCount) TypedWritableReferenceCount:: -decode_from_bam_stream(const string &data, BamReader *reader) { +decode_from_bam_stream(vector_uchar data, BamReader *reader) { TypedWritable *object; ReferenceCount *ref_ptr; - if (!TypedWritable::decode_raw_from_bam_stream(object, ref_ptr, data, reader)) { - return NULL; + if (TypedWritable::decode_raw_from_bam_stream(object, ref_ptr, move(data), reader)) { + return DCAST(TypedWritableReferenceCount, object); + } else { + return nullptr; } - - return DCAST(TypedWritableReferenceCount, object); } diff --git a/panda/src/putil/typedWritableReferenceCount.h b/panda/src/putil/typedWritableReferenceCount.h index 97d7d90029..7211db9e6e 100644 --- a/panda/src/putil/typedWritableReferenceCount.h +++ b/panda/src/putil/typedWritableReferenceCount.h @@ -37,7 +37,7 @@ public: virtual ReferenceCount *as_reference_count(); PUBLISHED: - static PT(TypedWritableReferenceCount) decode_from_bam_stream(const string &data, BamReader *reader = NULL); + static PT(TypedWritableReferenceCount) decode_from_bam_stream(vector_uchar data, BamReader *reader = nullptr); public: virtual TypeHandle get_type() const { @@ -61,6 +61,10 @@ private: static TypeHandle _type_handle; }; +// We can safely redefine this as a no-op. +template<> +INLINE void PointerToBase::update_type(To *ptr) {} + #include "typedWritableReferenceCount.I" #endif diff --git a/panda/src/putil/typedWritable_ext.cxx b/panda/src/putil/typedWritable_ext.cxx index 224686ed3e..efaaac63fb 100644 --- a/panda/src/putil/typedWritable_ext.cxx +++ b/panda/src/putil/typedWritable_ext.cxx @@ -15,6 +15,8 @@ #ifdef HAVE_PYTHON +#include "bamWriter.h" + #ifndef CPPPARSER extern Dtool_PyTypedObject Dtool_BamWriter; #endif // CPPPARSER @@ -65,13 +67,13 @@ __reduce_persist__(PyObject *self, PyObject *pickler) const { // It's OK if there's no bamWriter. PyErr_Clear(); } else { - DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer); + DtoolInstance_GetPointer(py_writer, writer, Dtool_BamWriter); Py_DECREF(py_writer); } } // First, streamify the object, if possible. - string bam_stream; + vector_uchar bam_stream; if (!_this->encode_to_bam_stream(bam_stream, writer)) { ostringstream stream; stream << "Could not bamify object of type " << _this->get_type() << "\n"; @@ -101,7 +103,6 @@ __reduce_persist__(PyObject *self, PyObject *pickler) const { } else { // The traditional pickle support: call the non-persistent version of this // function. - func = find_global_decode(this_class, "py_decode_TypedWritable_from_bam_stream"); if (func == NULL) { PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_TypedWritable_from_bam_stream()"); @@ -110,14 +111,15 @@ __reduce_persist__(PyObject *self, PyObject *pickler) const { } } -#if PY_MAJOR_VERSION >= 3 - PyObject *result = Py_BuildValue("(O(Oy#))", func, this_class, bam_stream.data(), (Py_ssize_t) bam_stream.size()); -#else - PyObject *result = Py_BuildValue("(O(Os#))", func, this_class, bam_stream.data(), (Py_ssize_t) bam_stream.size()); -#endif - Py_DECREF(func); - Py_DECREF(this_class); - return result; + // PyTuple_SET_ITEM conveniently borrows the reference it is passed. + PyObject *args = PyTuple_New(2); + PyTuple_SET_ITEM(args, 0, this_class); + PyTuple_SET_ITEM(args, 1, Dtool_WrapValue(bam_stream)); + + PyObject *tuple = PyTuple_New(2); + PyTuple_SET_ITEM(tuple, 0, func); + PyTuple_SET_ITEM(tuple, 1, args); + return tuple; } /** @@ -131,7 +133,8 @@ __reduce_persist__(PyObject *self, PyObject *pickler) const { */ PyObject *Extension:: find_global_decode(PyObject *this_class, const char *func_name) { - PyObject *module_name = PyObject_GetAttrString(this_class, "__module__"); + // Get the module in which BamWriter is defined. + PyObject *module_name = PyObject_GetAttrString((PyObject *)&Dtool_BamWriter, "__module__"); if (module_name != NULL) { // borrowed reference PyObject *sys_modules = PyImport_GetModuleDict(); @@ -146,8 +149,8 @@ find_global_decode(PyObject *this_class, const char *func_name) { } } } + Py_DECREF(module_name); } - Py_DECREF(module_name); PyObject *bases = PyObject_GetAttrString(this_class, "__bases__"); if (bases != NULL) { @@ -178,8 +181,8 @@ find_global_decode(PyObject *this_class, const char *func_name) { * properly handle self-referential BAM objects. */ PyObject * -py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data) { - return py_decode_TypedWritable_from_bam_stream_persist(NULL, this_class, data); +py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const vector_uchar &data) { + return py_decode_TypedWritable_from_bam_stream_persist(nullptr, this_class, data); } /** @@ -192,7 +195,7 @@ py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data * direct/src/stdpy. */ PyObject * -py_decode_TypedWritable_from_bam_stream_persist(PyObject *pickler, PyObject *this_class, const string &data) { +py_decode_TypedWritable_from_bam_stream_persist(PyObject *pickler, PyObject *this_class, const vector_uchar &data) { PyObject *py_reader = NULL; if (pickler != NULL) { @@ -210,28 +213,30 @@ py_decode_TypedWritable_from_bam_stream_persist(PyObject *pickler, PyObject *thi // decode_from_bam_stream appropriate to this class. PyObject *func = PyObject_GetAttrString(this_class, "decode_from_bam_stream"); - if (func == NULL) { - return NULL; + if (func == nullptr) { + Py_XDECREF(py_reader); + return nullptr; + } + + PyObject *bytes = Dtool_WrapValue(data); + if (bytes == nullptr) { + Py_DECREF(func); + Py_XDECREF(py_reader); + return nullptr; } PyObject *result; - if (py_reader != NULL){ -#if PY_MAJOR_VERSION >= 3 - result = PyObject_CallFunction(func, (char *)"(y#O)", data.data(), (Py_ssize_t) data.size(), py_reader); -#else - result = PyObject_CallFunction(func, (char *)"(s#O)", data.data(), (Py_ssize_t) data.size(), py_reader); -#endif + if (py_reader != nullptr) { + result = PyObject_CallFunctionObjArgs(func, bytes, py_reader, nullptr); Py_DECREF(py_reader); } else { -#if PY_MAJOR_VERSION >= 3 - result = PyObject_CallFunction(func, (char *)"(y#)", data.data(), (Py_ssize_t) data.size()); -#else - result = PyObject_CallFunction(func, (char *)"(s#)", data.data(), (Py_ssize_t) data.size()); -#endif + result = PyObject_CallFunctionObjArgs(func, bytes, nullptr); } + Py_DECREF(bytes); + Py_DECREF(func); - if (result == NULL) { - return NULL; + if (result == nullptr) { + return nullptr; } if (result == Py_None) { diff --git a/panda/src/putil/typedWritable_ext.h b/panda/src/putil/typedWritable_ext.h index 1582021c49..3d745ffb3e 100644 --- a/panda/src/putil/typedWritable_ext.h +++ b/panda/src/putil/typedWritable_ext.h @@ -33,12 +33,11 @@ public: PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const; static PyObject *find_global_decode(PyObject *this_class, const char *func_name); - }; BEGIN_PUBLISH -PyObject *py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data); -PyObject *py_decode_TypedWritable_from_bam_stream_persist(PyObject *unpickler, PyObject *this_class, const string &data); +PyObject *py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const vector_uchar &data); +PyObject *py_decode_TypedWritable_from_bam_stream_persist(PyObject *unpickler, PyObject *this_class, const vector_uchar &data); END_PUBLISH #endif // HAVE_PYTHON diff --git a/panda/src/putil/uniqueIdAllocator.h b/panda/src/putil/uniqueIdAllocator.h index 90efcaee69..54da7c6463 100644 --- a/panda/src/putil/uniqueIdAllocator.h +++ b/panda/src/putil/uniqueIdAllocator.h @@ -37,7 +37,7 @@ */ class EXPCL_PANDA_PUTIL UniqueIdAllocator { PUBLISHED: - UniqueIdAllocator(uint32_t min=0, uint32_t max=20); + explicit UniqueIdAllocator(uint32_t min=0, uint32_t max=20); ~UniqueIdAllocator(); uint32_t allocate(); diff --git a/panda/src/putil/updateSeq.I b/panda/src/putil/updateSeq.I index a3f4877c7f..99703059d4 100644 --- a/panda/src/putil/updateSeq.I +++ b/panda/src/putil/updateSeq.I @@ -11,48 +11,56 @@ * @date 1999-09-30 */ +/** + * Creates an UpdateSeq in the given state. + */ +CONSTEXPR UpdateSeq:: +UpdateSeq(unsigned int seq) : _seq(seq) { +} + /** * Creates an UpdateSeq in the 'initial' state. */ -INLINE UpdateSeq:: -UpdateSeq() { - _seq = (unsigned int)SC_initial; +CONSTEXPR UpdateSeq:: +UpdateSeq() : _seq((unsigned int)SC_initial) { } /** * Returns an UpdateSeq in the 'initial' state. */ -INLINE UpdateSeq UpdateSeq:: +CONSTEXPR UpdateSeq UpdateSeq:: initial() { - return UpdateSeq(); + return UpdateSeq((unsigned int)SC_initial); } /** * Returns an UpdateSeq in the 'old' state. */ -INLINE UpdateSeq UpdateSeq:: +CONSTEXPR UpdateSeq UpdateSeq:: old() { - UpdateSeq result; - result._seq = (unsigned int)SC_old; - return result; + return UpdateSeq((unsigned int)SC_old); } /** * Returns an UpdateSeq in the 'fresh' state. */ -INLINE UpdateSeq UpdateSeq:: +CONSTEXPR UpdateSeq UpdateSeq:: fresh() { - UpdateSeq result; - result._seq = (unsigned int)SC_fresh; - return result; + return UpdateSeq((unsigned int)SC_fresh); } /** * */ INLINE UpdateSeq:: -UpdateSeq(const UpdateSeq ©) { - _seq = AtomicAdjust::get(copy._seq); +UpdateSeq(const UpdateSeq ©) : _seq(AtomicAdjust::get(copy._seq)) { +} + +/** + * + */ +CONSTEXPR UpdateSeq:: +UpdateSeq(const UpdateSeq &&from) NOEXCEPT : _seq(from._seq) { } /** diff --git a/panda/src/putil/updateSeq.h b/panda/src/putil/updateSeq.h index 4899787244..5ba9de908f 100644 --- a/panda/src/putil/updateSeq.h +++ b/panda/src/putil/updateSeq.h @@ -35,13 +35,17 @@ * sequences are numeric and are monotonically increasing. */ class EXPCL_PANDA_PUTIL UpdateSeq { +private: + CONSTEXPR UpdateSeq(unsigned int seq); + PUBLISHED: - INLINE UpdateSeq(); - INLINE static UpdateSeq initial(); - INLINE static UpdateSeq old(); - INLINE static UpdateSeq fresh(); + CONSTEXPR UpdateSeq(); + CONSTEXPR static UpdateSeq initial(); + CONSTEXPR static UpdateSeq old(); + CONSTEXPR static UpdateSeq fresh(); INLINE UpdateSeq(const UpdateSeq ©); + CONSTEXPR UpdateSeq(const UpdateSeq &&from) NOEXCEPT; INLINE UpdateSeq &operator = (const UpdateSeq ©); INLINE void clear(); diff --git a/panda/src/putil/weakKeyHashMap.I b/panda/src/putil/weakKeyHashMap.I index c80e47723d..f894411f24 100644 --- a/panda/src/putil/weakKeyHashMap.I +++ b/panda/src/putil/weakKeyHashMap.I @@ -234,8 +234,8 @@ get_size() const { */ template INLINE bool WeakKeyHashMap:: -has_element(int n) const { - nassertr(n >= 0 && n < (int)_table_size, false); +has_element(size_t n) const { + nassertr(n < _table_size, false); return (get_exists_array()[n] != 0 && !_table[n]._key.was_deleted()); } @@ -248,7 +248,7 @@ has_element(int n) const { */ template INLINE const Key *WeakKeyHashMap:: -get_key(int n) const { +get_key(size_t n) const { nassertr(has_element(n), _table[n]._key); return _table[n]._key; } @@ -262,7 +262,7 @@ get_key(int n) const { */ template INLINE const Value &WeakKeyHashMap:: -get_data(int n) const { +get_data(size_t n) const { nassertr(has_element(n), _table[n]._data); return _table[n]._data; } @@ -276,7 +276,7 @@ get_data(int n) const { */ template INLINE Value &WeakKeyHashMap:: -modify_data(int n) { +modify_data(size_t n) { nassertr(has_element(n), _table[n]._data); return _table[n]._data; } @@ -290,7 +290,7 @@ modify_data(int n) { */ template INLINE void WeakKeyHashMap:: -set_data(int n, const Value &data) { +set_data(size_t n, const Value &data) { nassertv(has_element(n)); _table[n]._data = data; } @@ -305,9 +305,9 @@ set_data(int n, const Value &data) { */ template INLINE void WeakKeyHashMap:: -set_data(int n, Value &&data) { +set_data(size_t n, Value &&data) { nassertv(has_element(n)); - _table[n]._data = std::move(data); + _table[n]._data = move(data); } #endif // USE_MOVE_SEMANTICS @@ -320,7 +320,7 @@ set_data(int n, Value &&data) { */ template void WeakKeyHashMap:: -remove_element(int n) { +remove_element(size_t n) { nassertv(get_exists_array()[n] != 0); clear_element(n); @@ -329,7 +329,7 @@ remove_element(int n) { // Now we have put a hole in the table. If there was a hash conflict in the // slot following this one, we have to move it down to close the hole. - size_t i = (size_t)n; + size_t i = n; i = (i + 1) & (_table_size - 1); while (get_exists_array()[i] != 0) { if (_table[i]._key.was_deleted()) { @@ -430,15 +430,17 @@ bool WeakKeyHashMap:: validate() const { size_t count = 0; + const unsigned char *exists_array = get_exists_array(); + for (size_t i = 0; i < _table_size; ++i) { - if (get_exists_array()[i] != 0) { + if (exists_array[i] != 0) { ++count; if (_table[i]._key.was_deleted()) { continue; } size_t ideal_index = get_hash(_table[i]._key.get_orig()); size_t wants_index = ideal_index; - while (wants_index != i && get_exists_array()[i] != 0) { + while (wants_index != i && exists_array[wants_index] != 0) { wants_index = (wants_index + 1) & (_table_size - 1); } if (wants_index != i) { @@ -485,7 +487,7 @@ get_hash(const Key *key) const { */ template INLINE bool WeakKeyHashMap:: -is_element(int n, const Key *key) const { +is_element(size_t n, const Key *key) const { nassertr(has_element(n), false); return _table[n]._key == key; } @@ -496,7 +498,7 @@ is_element(int n, const Key *key) const { */ template INLINE void WeakKeyHashMap:: -store_new_element(int n, const Key *key, const Value &data) { +store_new_element(size_t n, const Key *key, const Value &data) { if (get_exists_array()[n] != 0) { // There was already an element in this spot. This can happen if it was a // pointer that had already been deleted. @@ -513,7 +515,7 @@ store_new_element(int n, const Key *key, const Value &data) { */ template INLINE void WeakKeyHashMap:: -clear_element(int n) { +clear_element(size_t n) { _table[n].~TableEntry(); get_exists_array()[n] = false; } diff --git a/panda/src/putil/weakKeyHashMap.h b/panda/src/putil/weakKeyHashMap.h index 98a8863ee2..bbb7077998 100644 --- a/panda/src/putil/weakKeyHashMap.h +++ b/panda/src/putil/weakKeyHashMap.h @@ -45,15 +45,15 @@ public: INLINE Value &operator [] (const Key *key); INLINE size_t get_size() const; - INLINE bool has_element(int n) const; - INLINE const Key *get_key(int n) const; - INLINE const Value &get_data(int n) const; - INLINE Value &modify_data(int n); - INLINE void set_data(int n, const Value &data); + INLINE bool has_element(size_t n) const; + INLINE const Key *get_key(size_t n) const; + INLINE const Value &get_data(size_t n) const; + INLINE Value &modify_data(size_t n); + INLINE void set_data(size_t n, const Value &data); #ifdef USE_MOVE_SEMANTICS - INLINE void set_data(int n, Value &&data); + INLINE void set_data(size_t n, Value &&data); #endif - void remove_element(int n); + void remove_element(size_t n); INLINE size_t get_num_entries() const; INLINE bool is_empty() const; @@ -65,9 +65,9 @@ public: private: INLINE size_t get_hash(const Key *key) const; - INLINE bool is_element(int n, const Key *key) const; - INLINE void store_new_element(int n, const Key *key, const Value &data); - INLINE void clear_element(int n); + INLINE bool is_element(size_t n, const Key *key) const; + INLINE void store_new_element(size_t n, const Key *key, const Value &data); + INLINE void clear_element(size_t n); INLINE unsigned char *get_exists_array() const; void new_table(); diff --git a/panda/src/putil/writableParam.I b/panda/src/putil/writableParam.I index 49b75bf0f9..878820379a 100644 --- a/panda/src/putil/writableParam.I +++ b/panda/src/putil/writableParam.I @@ -30,22 +30,6 @@ WritableParam(const WritableParam ©) : { } -/** - * - */ -INLINE WritableParam:: -~WritableParam() { -} - -/** - * - */ -INLINE void WritableParam:: -operator = (const WritableParam &) { - // The assignment operator cannot be used for this class. - nassertv(false); -} - /** * */ diff --git a/panda/src/putil/writableParam.h b/panda/src/putil/writableParam.h index beefff5b8a..7bd49ebd30 100644 --- a/panda/src/putil/writableParam.h +++ b/panda/src/putil/writableParam.h @@ -36,11 +36,10 @@ private: public: INLINE WritableParam(const Datagram &datagram); INLINE WritableParam(const WritableParam &other); - INLINE ~WritableParam(); private: // The assignment operator cannot be used for this class. - INLINE void operator = (const WritableParam &other); + WritableParam &operator = (const WritableParam &other) DELETED_ASSIGN; public: virtual TypeHandle get_type() const { diff --git a/panda/src/recorder/mouseRecorder.h b/panda/src/recorder/mouseRecorder.h index b97df0b7ed..f43150c2b8 100644 --- a/panda/src/recorder/mouseRecorder.h +++ b/panda/src/recorder/mouseRecorder.h @@ -33,7 +33,7 @@ class BamWriter; */ class EXPCL_PANDA_RECORDER MouseRecorder : public DataNode, public RecorderBase { PUBLISHED: - MouseRecorder(const string &name); + explicit MouseRecorder(const string &name); virtual ~MouseRecorder(); public: diff --git a/panda/src/recorder/socketStreamRecorder.h b/panda/src/recorder/socketStreamRecorder.h index 26f39047c4..3b43f5ffcd 100644 --- a/panda/src/recorder/socketStreamRecorder.h +++ b/panda/src/recorder/socketStreamRecorder.h @@ -41,7 +41,7 @@ class EXPCL_PANDA_RECORDER SocketStreamRecorder : public RecorderBase, public ReferenceCount { PUBLISHED: INLINE SocketStreamRecorder(); - INLINE SocketStreamRecorder(SocketStream *stream, bool owns_stream); + INLINE explicit SocketStreamRecorder(SocketStream *stream, bool owns_stream); INLINE ~SocketStreamRecorder(); bool receive_datagram(Datagram &dg); diff --git a/panda/src/speedtree/speedTreeNode.h b/panda/src/speedtree/speedTreeNode.h index 942312a2fb..567cb00af9 100644 --- a/panda/src/speedtree/speedTreeNode.h +++ b/panda/src/speedtree/speedTreeNode.h @@ -85,7 +85,7 @@ PUBLISHED: }; PUBLISHED: - SpeedTreeNode(const string &name); + explicit SpeedTreeNode(const string &name); virtual ~SpeedTreeNode(); INLINE bool is_valid() const; @@ -141,6 +141,9 @@ PUBLISHED: INLINE double get_time_delta() const; INLINE static void set_global_time_delta(double delta); INLINE static double get_global_time_delta(); + MAKE_PROPERTY(time_delta, get_time_delta, set_time_delta); + MAKE_PROPERTY(global_time_delta, get_global_time_delta, + set_global_time_delta); static bool authorize(const string &license = ""); diff --git a/panda/src/testbed/pview.cxx b/panda/src/testbed/pview.cxx index c31537e2c5..6d68f099ab 100644 --- a/panda/src/testbed/pview.cxx +++ b/panda/src/testbed/pview.cxx @@ -25,6 +25,9 @@ #include "panda_getopt.h" #include "preprocess_argv.h" #include "graphicsPipeSelection.h" +#include "asyncTaskManager.h" +#include "asyncTask.h" +#include "boundingSphere.h" // By including checkPandaVersion.h, we guarantee that runtime attempts to run // pview will fail if it inadvertently links with the wrong version of @@ -231,6 +234,111 @@ report_version() { nout << "\n"; } +// Task that dynamically adjusts the camera len's near/far clipping +// planes to ensure the user can zoom in as close as needed to a model. +// +// Code adapted from WindowFramework::center_trackball(), but +// without moving the camera. When the camera is inside the model, +// the near clip is set to near-zero. +// +class AdjustCameraClipPlanesTask : public AsyncTask { +public: + AdjustCameraClipPlanesTask(const string &name, Camera *camera) : + AsyncTask(name), _camera(camera), _lens(camera->get_lens(0)), _sphere(NULL) + { + NodePath np = framework.get_models(); + PT(BoundingVolume) volume = np.get_bounds(); + + // We expect at least a geometric bounding volume around the world. + nassertv(volume != (BoundingVolume *)NULL); + nassertv(volume->is_of_type(GeometricBoundingVolume::get_class_type())); + CPT(GeometricBoundingVolume) gbv = DCAST(GeometricBoundingVolume, volume); + + if (np.has_parent()) { + CPT(TransformState) net_transform = np.get_parent().get_net_transform(); + PT(GeometricBoundingVolume) new_gbv = DCAST(GeometricBoundingVolume, gbv->make_copy()); + new_gbv->xform(net_transform->get_mat()); + gbv = new_gbv; + } + + // Determine the bounding sphere around the object. + if (gbv->is_infinite()) { + framework_cat.warning() + << "Infinite bounding volume for " << np << "\n"; + return; + } + + if (gbv->is_empty()) { + framework_cat.warning() + << "Empty bounding volume for " << np << "\n"; + return; + } + + // The BoundingVolume might be a sphere (it's likely), but since it + // might not, we'll take no chances and make our own sphere. + _sphere = new BoundingSphere(gbv->get_approx_center(), 0.0f); + if (!_sphere->extend_by(gbv)) { + framework_cat.warning() + << "Cannot determine bounding volume of " << np << "\n"; + return; + } + } + ALLOC_DELETED_CHAIN(AdjustCameraClipPlanesTask); + + virtual DoneStatus do_task() { + if (!_sphere) { + return DS_done; + } + + if (framework.get_num_windows() == 0) { + return DS_cont; + } + + WindowFramework *wf = framework.get_window(0); + if (!wf) { + return DS_cont; + } + + // Get current camera position. + NodePath cameraNP = wf->get_camera_group(); + LPoint3 pos = cameraNP.get_pos(); + + // See how far or close the camera is + LPoint3 center = _sphere->get_center(); + PN_stdfloat radius = _sphere->get_radius(); + + PN_stdfloat min_distance = 0.001 * radius; + + // Choose a suitable distance to view the whole volume in our frame. + // This is based on the camera lens in use. + PN_stdfloat distance; + CPT(GeometricBoundingVolume) gbv = DCAST(GeometricBoundingVolume, _sphere); + if (gbv->contains(pos)) { + // See as up-close to the model as possible + distance = min_distance; + } else { + // View from a distance + distance = (center - pos).length(); + } + + // Ensure the far plane is far enough back to see the entire object. + PN_stdfloat ideal_far_plane = distance + radius * 1.5; + _lens->set_far(max(_lens->get_default_far(), ideal_far_plane)); + + // And that the near plane is far enough forward, but if inside + // the sphere, keep above 0. + PN_stdfloat ideal_near_plane = max(min_distance * 10, distance - radius); + _lens->set_near(min(_lens->get_default_near(), ideal_near_plane)); + + return DS_cont; + } + + Camera *_camera; + Lens *_lens; + PT(BoundingSphere) _sphere; +}; + + int main(int argc, char **argv) { preprocess_argv(argc, argv); @@ -383,6 +491,9 @@ main(int argc, char **argv) { window->set_anim_controls(true); } + PT(AdjustCameraClipPlanesTask) task = new AdjustCameraClipPlanesTask("Adjust Camera Bounds", window->get_camera(0)); + framework.get_task_mgr().add(task); + framework.enable_default_keys(); framework.define_key("shift-w", "open a new window", event_W, NULL); framework.define_key("shift-f", "flatten hierarchy", event_F, NULL); diff --git a/panda/src/text/config_text.cxx b/panda/src/text/config_text.cxx index 4790c5ecf7..9475eabf55 100644 --- a/panda/src/text/config_text.cxx +++ b/panda/src/text/config_text.cxx @@ -48,6 +48,18 @@ ConfigVariableBool text_dynamic_merge "operation. Usually it's a performance " "advantage to keep this true. See TextNode::set_flatten_flags().")); +ConfigVariableBool text_kerning +("text-kerning", false, + PRC_DESC("Set this true to enable kerning when the font provides kerning " + "tables. This can result in more aesthetically pleasing spacing " + "between individual glyphs. Has no effect when text-use-harfbuzz " + "is true, since HarfBuzz offers superior kerning support.")); + +ConfigVariableBool text_use_harfbuzz +("text-use-harfbuzz", false, + PRC_DESC("Set this true to enable HarfBuzz support, which offers superior " + "text shaping and better support for non-Latin text.")); + ConfigVariableInt text_anisotropic_degree ("text-anisotropic-degree", 1, PRC_DESC("This is the default anisotropic-degree that is set on dynamic " diff --git a/panda/src/text/config_text.h b/panda/src/text/config_text.h index 5c50af6a52..895072fdf6 100644 --- a/panda/src/text/config_text.h +++ b/panda/src/text/config_text.h @@ -30,6 +30,8 @@ NotifyCategoryDecl(text, EXPCL_PANDA_TEXT, EXPTP_PANDA_TEXT); extern ConfigVariableBool text_flatten; extern ConfigVariableBool text_dynamic_merge; +extern ConfigVariableBool text_kerning; +extern ConfigVariableBool text_use_harfbuzz; extern ConfigVariableInt text_anisotropic_degree; extern ConfigVariableInt text_texture_margin; extern ConfigVariableDouble text_poly_margin; diff --git a/panda/src/text/dynamicTextFont.cxx b/panda/src/text/dynamicTextFont.cxx index 09369a12b9..3a349a6d69 100644 --- a/panda/src/text/dynamicTextFont.cxx +++ b/panda/src/text/dynamicTextFont.cxx @@ -44,6 +44,10 @@ #include "textureAttrib.h" #include "transparencyAttrib.h" +#ifdef HAVE_HARFBUZZ +#include +#endif + TypeHandle DynamicTextFont::_type_handle; @@ -114,7 +118,8 @@ DynamicTextFont(const DynamicTextFont ©) : _has_outline(copy._has_outline), _tex_format(copy._tex_format), _needs_image_processing(copy._needs_image_processing), - _preferred_page(0) + _preferred_page(0), + _hb_font(nullptr) { } @@ -123,6 +128,11 @@ DynamicTextFont(const DynamicTextFont ©) : */ DynamicTextFont:: ~DynamicTextFont() { +#ifdef HAVE_HARFBUZZ + if (_hb_font != nullptr) { + hb_font_destroy(_hb_font); + } +#endif } /** @@ -203,6 +213,13 @@ clear() { _cache.clear(); _pages.clear(); _empty_glyphs.clear(); + +#ifdef HAVE_HARFBUZZ + if (_hb_font != nullptr) { + hb_font_destroy(_hb_font); + _hb_font = nullptr; + } +#endif } /** @@ -278,6 +295,81 @@ get_glyph(int character, CPT(TextGlyph) &glyph) { return (glyph_index != 0); } +/** + * Returns the amount by which to offset the second glyph when it directly + * follows the first glyph. This is an additional offset that is added on top + * of the advance. + */ +PN_stdfloat DynamicTextFont:: +get_kerning(int first, int second) const { + if (!_is_valid) { + return 0; + } + + FT_Face face = acquire_face(); + if (!FT_HAS_KERNING(face)) { + release_face(face); + return 0; + } + + int first_index = FT_Get_Char_Index(face, first); + int second_index = FT_Get_Char_Index(face, second); + + FT_Vector delta; + FT_Get_Kerning(face, first_index, second_index, FT_KERNING_DEFAULT, &delta); + release_face(face); + + return delta.x / (_font_pixels_per_unit * 64); +} + +/** + * Like get_glyph, but uses a glyph index. + */ +bool DynamicTextFont:: +get_glyph_by_index(int character, int glyph_index, CPT(TextGlyph) &glyph) { + if (!_is_valid) { + glyph = nullptr; + return false; + } + + Cache::iterator ci = _cache.find(glyph_index); + if (ci != _cache.end()) { + glyph = (*ci).second; + } else { + FT_Face face = acquire_face(); + glyph = make_glyph(character, face, glyph_index); + _cache.insert(Cache::value_type(glyph_index, glyph.p())); + release_face(face); + } + + if (glyph.is_null()) { + glyph = get_invalid_glyph(); + return false; + } + + return true; +} + +/** + * If Panda was compiled with HarfBuzz enabled, returns a HarfBuzz font for + * this font. + */ +hb_font_t *DynamicTextFont:: +get_hb_font() const { +#ifdef HAVE_HARFBUZZ + if (_hb_font != nullptr) { + return _hb_font; + } + + FT_Face face = acquire_face(); + _hb_font = hb_ft_font_create(face, nullptr); + release_face(face); + + return _hb_font; +#else + return nullptr; +#endif +} /** * Called from both constructors to set up some initial values. @@ -302,6 +394,8 @@ initialize() { _winding_order = WO_default; _preferred_page = 0; + + _hb_font = nullptr; } /** diff --git a/panda/src/text/dynamicTextFont.h b/panda/src/text/dynamicTextFont.h index c418ea34c3..775fa6a450 100644 --- a/panda/src/text/dynamicTextFont.h +++ b/panda/src/text/dynamicTextFont.h @@ -31,6 +31,8 @@ class NurbsCurveResult; +typedef struct hb_font_t hb_font_t; + /** * A DynamicTextFont is a special TextFont object that rasterizes its glyphs * from a standard font file (e.g. a TTF file) on the fly. It requires the @@ -123,6 +125,10 @@ PUBLISHED: public: virtual bool get_glyph(int character, CPT(TextGlyph) &glyph); + virtual PN_stdfloat get_kerning(int first, int second) const; + + bool get_glyph_by_index(int character, int glyph_index, CPT(TextGlyph) &glyph); + hb_font_t *get_hb_font() const; private: void initialize(); @@ -170,6 +176,8 @@ private: typedef pvector< PT(TextGlyph) > EmptyGlyphs; EmptyGlyphs _empty_glyphs; + mutable hb_font_t *_hb_font; + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/text/textAssembler.cxx b/panda/src/text/textAssembler.cxx index 4829576407..a10179db04 100644 --- a/panda/src/text/textAssembler.cxx +++ b/panda/src/text/textAssembler.cxx @@ -31,10 +31,15 @@ #include "geomVertexData.h" #include "geom.h" #include "modelNode.h" +#include "dynamicTextFont.h" #include #include // for sprintf +#ifdef HAVE_HARFBUZZ +#include +#endif + // This is the factor by which CT_small scales the character down. static const PN_stdfloat small_accent_scale = 0.6f; @@ -609,6 +614,8 @@ assemble_text() { * Returns the width of a single character, according to its associated font. * This also correctly calculates the width of cheesy ligatures and accented * characters, which may not exist in the font as such. + * + * This does not take kerning into account, however. */ PN_stdfloat TextAssembler:: calc_width(wchar_t character, const TextProperties &properties) { @@ -1142,7 +1149,6 @@ generate_quads(GeomNode *geom_node, const QuadMap &quad_map) { } else { tris->set_index_type(GeomEnums::NT_uint16); } - PT(GeomVertexArrayData) indices = tris->modify_vertices(); int i = 0; @@ -1150,9 +1156,10 @@ generate_quads(GeomNode *geom_node, const QuadMap &quad_map) { // bottleneck. So, I've written this out the hard way instead. Two // versions of the loop: one for 32-bit indices, one for 16-bit. { - PT(GeomVertexArrayDataHandle) vtx_handle = vdata->modify_array(0)->modify_handle(); + PT(GeomVertexArrayDataHandle) vtx_handle = vdata->modify_array_handle(0); vtx_handle->unclean_set_num_rows(quads.size() * 4); + Thread *current_thread = Thread::get_current_thread(); unsigned char *write_ptr = vtx_handle->get_write_pointer(); size_t stride = format->get_array(0)->get_stride() / sizeof(PN_float32); @@ -1163,7 +1170,7 @@ generate_quads(GeomNode *geom_node, const QuadMap &quad_map) { if (tris->get_index_type() == GeomEnums::NT_uint32) { // 32-bit index case. - PT(GeomVertexArrayDataHandle) idx_handle = indices->modify_handle(); + PT(GeomVertexArrayDataHandle) idx_handle = tris->modify_vertices_handle(current_thread); idx_handle->unclean_set_num_rows(quads.size() * 6); uint32_t *idx_ptr = (uint32_t *)idx_handle->get_write_pointer(); @@ -1219,7 +1226,7 @@ generate_quads(GeomNode *geom_node, const QuadMap &quad_map) { } } else { // 16-bit index case. - PT(GeomVertexArrayDataHandle) idx_handle = indices->modify_handle(); + PT(GeomVertexArrayDataHandle) idx_handle = tris->modify_vertices_handle(current_thread); idx_handle->unclean_set_num_rows(quads.size() * 6); uint16_t *idx_ptr = (uint16_t *)idx_handle->get_write_pointer(); @@ -1399,9 +1406,17 @@ assemble_row(TextAssembler::TextRow &row, PN_stdfloat xpos = 0.0f; align = TextProperties::A_left; + // Remember previous character, for kerning. + int prev_char = -1; + bool underscore = false; PN_stdfloat underscore_start = 0.0f; - const TextProperties *underscore_properties = NULL; + const TextProperties *underscore_properties = nullptr; + const ComputedProperties *prev_cprops = nullptr; + +#ifdef HAVE_HARFBUZZ + hb_buffer_t *harfbuff = nullptr; +#endif TextString::const_iterator si; for (si = row._string.begin(); si != row._string.end(); ++si) { @@ -1443,18 +1458,39 @@ assemble_row(TextAssembler::TextRow &row, LVecBase4 frame = graphic->get_frame(); line_height = max(line_height, frame[3] - frame[2]); } else { - // [fabius] this is not the right place to calc line height (see below) - // line_height = max(line_height, font->get_line_height()); + line_height = max(line_height, font->get_line_height() * properties->get_glyph_scale() * properties->get_text_scale()); } +#ifdef HAVE_HARFBUZZ + if (tch._cprops != prev_cprops || graphic != nullptr) { + if (harfbuff != nullptr && hb_buffer_get_length(harfbuff) > 0) { + // Shape the buffer accumulated so far. + shape_buffer(harfbuff, placed_glyphs, xpos, prev_cprops->_properties); + hb_buffer_reset(harfbuff); + + } else if (harfbuff == nullptr && text_use_harfbuzz && + font->is_of_type(DynamicTextFont::get_class_type())) { + harfbuff = hb_buffer_create(); + } + prev_cprops = tch._cprops; + } + + if (graphic == nullptr && harfbuff != nullptr) { + hb_buffer_add(harfbuff, character, character); + continue; + } +#endif + if (character == ' ') { // A space is a special case. xpos += properties->get_glyph_scale() * properties->get_text_scale() * font->get_space_advance(); + prev_char = -1; } else if (character == '\t') { // So is a tab character. PN_stdfloat tab_width = properties->get_tab_width(); xpos = (floor(xpos / tab_width) + 1.0f) * tab_width; + prev_char = -1; } else if (character == text_soft_hyphen_key) { // And so is the 'soft-hyphen' key character. @@ -1493,6 +1529,7 @@ assemble_row(TextAssembler::TextRow &row, placed_glyphs.push_back(placement); xpos += advance * glyph_scale; + prev_char = -1; } else { // A printable character. @@ -1521,13 +1558,22 @@ assemble_row(TextAssembler::TextRow &row, << "\n"; } + glyph_scale *= properties->get_glyph_scale() * properties->get_text_scale(); + + // Add the kerning delta. + if (text_kerning) { + if (prev_char != -1) { + xpos += font->get_kerning(prev_char, character) * glyph_scale; + } + prev_char = character; + } + // Build up a GlyphPlacement, indicating all of the Geoms that go into // this character. Normally, there is only one Geom per character, but // it may involve multiple Geoms if we need to add cheesy accents or // ligatures. GlyphPlacement placement; - glyph_scale *= properties->get_glyph_scale() * properties->get_text_scale(); placement._glyph = NULL; placement._scale = glyph_scale; placement._xpos = xpos; @@ -1596,10 +1642,16 @@ assemble_row(TextAssembler::TextRow &row, } xpos += advance * glyph_scale; - line_height = max(line_height, font->get_line_height() * glyph_scale); } } +#ifdef HAVE_HARFBUZZ + if (harfbuff != nullptr && hb_buffer_get_length(harfbuff) > 0) { + shape_buffer(harfbuff, placed_glyphs, xpos, prev_cprops->_properties); + } + hb_buffer_destroy(harfbuff); +#endif + if (underscore && underscore_start != xpos) { draw_underscore(placed_glyphs, underscore_start, xpos, underscore_properties); @@ -1623,6 +1675,89 @@ assemble_row(TextAssembler::TextRow &row, } } +/** + * Places the glyphs collected from a HarfBuzz buffer. + */ +void TextAssembler:: +shape_buffer(hb_buffer_t *buf, PlacedGlyphs &placed_glyphs, PN_stdfloat &xpos, + const TextProperties &properties) { + +#ifdef HAVE_HARFBUZZ + // If we did not specify a text direction, harfbuzz will guess it based on + // the script we are using. + hb_direction_t direction = HB_DIRECTION_INVALID; + if (properties.has_direction()) { + switch (properties.get_direction()) { + case TextProperties::D_ltr: + direction = HB_DIRECTION_LTR; + break; + case TextProperties::D_rtl: + direction = HB_DIRECTION_RTL; + break; + } + } + hb_buffer_set_content_type(buf, HB_BUFFER_CONTENT_TYPE_UNICODE); + hb_buffer_set_direction(buf, direction); + hb_buffer_guess_segment_properties(buf); + + DynamicTextFont *font = DCAST(DynamicTextFont, properties.get_font()); + hb_font_t *hb_font = font->get_hb_font(); + hb_shape(hb_font, buf, NULL, 0); + + PN_stdfloat glyph_scale = properties.get_glyph_scale() * properties.get_text_scale(); + PN_stdfloat scale = glyph_scale / (font->get_pixels_per_unit() * font->get_scale_factor() * 64.0); + + unsigned int glyph_count; + hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); + hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); + + for (unsigned int i = 0; i < glyph_count; ++i) { + int character = glyph_info[i].cluster; + int glyph_index = glyph_info[i].codepoint; + + CPT(TextGlyph) glyph; + if (!font->get_glyph_by_index(character, glyph_index, glyph)) { + char buffer[512]; + sprintf(buffer, "U+%04x", character); + text_cat.warning() + << "No definition in " << font->get_name() + << " for character " << buffer; + if (character < 128 && isprint((unsigned int)character)) { + text_cat.warning(false) + << " ('" << (char)character << "')"; + } + text_cat.warning(false) + << "\n"; + } + + PN_stdfloat advance = glyph_pos[i].x_advance * scale; + if (glyph->is_whitespace()) { + // A space is a special case. + xpos += advance; + continue; + } + + PN_stdfloat x_offset = glyph_pos[i].x_offset * scale; + PN_stdfloat y_offset = glyph_pos[i].y_offset * scale; + + // Build up a GlyphPlacement, indicating all of the Geoms that go into + // this character. Normally, there is only one Geom per character, but + // it may involve multiple Geoms if we need to add cheesy accents or + // ligatures. + GlyphPlacement placement; + placement._glyph = move(glyph); + placement._scale = glyph_scale; + placement._xpos = xpos + x_offset; + placement._ypos = properties.get_glyph_shift() + y_offset; + placement._slant = properties.get_slant(); + placement._properties = &properties; + placed_glyphs.push_back(placement); + + xpos += advance; + } +#endif +} + /** * Creates the geometry to render the underscore line for the indicated range * of glyphs in this row. diff --git a/panda/src/text/textAssembler.h b/panda/src/text/textAssembler.h index 1215765bf5..52195f8f69 100644 --- a/panda/src/text/textAssembler.h +++ b/panda/src/text/textAssembler.h @@ -28,6 +28,7 @@ #include "pmap.h" +typedef struct hb_buffer_t hb_buffer_t; class TextEncoder; class TextGraphic; @@ -41,7 +42,7 @@ class TextAssembler; */ class EXPCL_PANDA_TEXT TextAssembler { PUBLISHED: - TextAssembler(TextEncoder *encoder); + explicit TextAssembler(TextEncoder *encoder); TextAssembler(const TextAssembler ©); void operator = (const TextAssembler ©); ~TextAssembler(); @@ -212,21 +213,6 @@ private: typedef pmap GeomCollectorMap; struct QuadDef { - // Copying this class is a performance hotspot, hence we define the move - // constructor. - ALWAYS_INLINE QuadDef() {} - ALWAYS_INLINE QuadDef(const QuadDef ©) : - _dimensions(copy._dimensions), _uvs(copy._uvs), - _slantl(copy._slantl), _slanth(copy._slanth), - _glyph(copy._glyph) {} - -#ifdef USE_MOVE_SEMANTICS - ALWAYS_INLINE QuadDef(QuadDef &&from) NOEXCEPT : - _dimensions(from._dimensions), _uvs(from._uvs), - _slantl(from._slantl), _slanth(from._slanth), - _glyph(move(from._glyph)) {} -#endif - LVecBase4 _dimensions; LVecBase4 _uvs; PN_stdfloat _slantl, _slanth; @@ -262,6 +248,9 @@ private: PN_stdfloat &row_width, PN_stdfloat &line_height, TextProperties::Alignment &align, PN_stdfloat &wordwrap); + void shape_buffer(hb_buffer_t *buf, PlacedGlyphs &glyphs, PN_stdfloat &xpos, + const TextProperties &properties); + // These interfaces are for implementing cheesy accent marks and ligatures // when the font doesn't support them. enum CheesyPosition { diff --git a/panda/src/text/textFont.cxx b/panda/src/text/textFont.cxx index 70ca9a74d5..add86483bd 100644 --- a/panda/src/text/textFont.cxx +++ b/panda/src/text/textFont.cxx @@ -54,6 +54,16 @@ TextFont:: ~TextFont() { } +/** + * Returns the amount by which to offset the second glyph when it directly + * follows the first glyph. This is an additional offset that is added on top + * of the advance. + */ +PN_stdfloat TextFont:: +get_kerning(int first, int second) const { + return 0; +} + /** * */ diff --git a/panda/src/text/textFont.h b/panda/src/text/textFont.h index 1249e7b817..7cea63e391 100644 --- a/panda/src/text/textFont.h +++ b/panda/src/text/textFont.h @@ -74,6 +74,8 @@ PUBLISHED: INLINE CPT(TextGlyph) get_glyph(int character); + virtual PN_stdfloat get_kerning(int first, int second) const; + virtual void write(ostream &out, int indent_level) const; public: diff --git a/panda/src/text/textGraphic.h b/panda/src/text/textGraphic.h index a3d268cd8c..d8a3e1a476 100644 --- a/panda/src/text/textGraphic.h +++ b/panda/src/text/textGraphic.h @@ -37,8 +37,8 @@ class EXPCL_PANDA_TEXT TextGraphic { PUBLISHED: INLINE TextGraphic(); - INLINE TextGraphic(const NodePath &model, const LVecBase4 &frame); - INLINE TextGraphic(const NodePath &model, PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top); + INLINE explicit TextGraphic(const NodePath &model, const LVecBase4 &frame); + INLINE explicit TextGraphic(const NodePath &model, PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top); INLINE NodePath get_model() const; INLINE void set_model(const NodePath &model); diff --git a/panda/src/text/textNode.h b/panda/src/text/textNode.h index 5f10e22da3..50266cbfd7 100644 --- a/panda/src/text/textNode.h +++ b/panda/src/text/textNode.h @@ -45,8 +45,8 @@ */ class EXPCL_PANDA_TEXT TextNode : public PandaNode, public TextEncoder, public TextProperties { PUBLISHED: - TextNode(const string &name); - TextNode(const string &name, const TextProperties ©); + explicit TextNode(const string &name); + explicit TextNode(const string &name, const TextProperties ©); protected: TextNode(const TextNode ©); virtual PandaNode *make_copy() const; diff --git a/panda/src/text/textProperties.I b/panda/src/text/textProperties.I index 4e73399be1..e3e296c95f 100644 --- a/panda/src/text/textProperties.I +++ b/panda/src/text/textProperties.I @@ -797,3 +797,39 @@ INLINE PN_stdfloat TextProperties:: get_text_scale() const { return _text_scale; } + +/** + * Specifies the text direction. If none is specified, it will be guessed + * based on the contents of the string. + */ +INLINE void TextProperties:: +set_direction(Direction direction) { + _direction = direction; + _specified |= F_has_direction; +} + +/** + * Clears the text direction setting. If no text direction is specified, it + * will be guessed based on the contents of the string. + */ +INLINE void TextProperties:: +clear_direction() { + _specified &= ~F_has_direction; + _direction = D_ltr; +} + +/** + * + */ +INLINE bool TextProperties:: +has_direction() const { + return (_specified & F_has_direction) != 0; +} + +/** + * Returns the direction of the text as specified by set_direction(). + */ +INLINE TextProperties::Direction TextProperties:: +get_direction() const { + return _direction; +} diff --git a/panda/src/text/textProperties.cxx b/panda/src/text/textProperties.cxx index 94b02ffc11..341f2385a3 100644 --- a/panda/src/text/textProperties.cxx +++ b/panda/src/text/textProperties.cxx @@ -31,26 +31,27 @@ TypeHandle TextProperties::_type_handle; * */ TextProperties:: -TextProperties() { - _specified = 0; +TextProperties() : + _specified(0), - _small_caps = text_small_caps; - _small_caps_scale = text_small_caps_scale; - _slant = 0.0f; - _underscore = false; - _underscore_height = 0.0f; - _align = A_left; - _indent_width = 0.0f; - _wordwrap_width = 0.0f; - _preserve_trailing_whitespace = false; - _text_color.set(1.0f, 1.0f, 1.0f, 1.0f); - _shadow_color.set(0.0f, 0.0f, 0.0f, 1.0f); - _shadow_offset.set(0.0f, 0.0f); - _draw_order = 1; - _tab_width = text_tab_width; - _glyph_scale = 1.0f; - _glyph_shift = 0.0f; - _text_scale = 1.0f; + _small_caps(text_small_caps), + _small_caps_scale(text_small_caps_scale), + _slant(0.0f), + _underscore(false), + _underscore_height(0.0f), + _align(A_left), + _indent_width(0.0f), + _wordwrap_width(0.0f), + _preserve_trailing_whitespace(false), + _text_color(1.0f, 1.0f, 1.0f, 1.0f), + _shadow_color(0.0f, 0.0f, 0.0f, 1.0f), + _shadow_offset(0.0f, 0.0f), + _draw_order(1), + _tab_width(text_tab_width), + _glyph_scale(1.0f), + _glyph_shift(0.0f), + _text_scale(1.0f), + _direction(D_rtl) { } /** @@ -89,6 +90,7 @@ operator = (const TextProperties ©) { _glyph_scale = copy._glyph_scale; _glyph_shift = copy._glyph_shift; _text_scale = copy._text_scale; + _direction = copy._direction; _text_state.clear(); _shadow_state.clear(); @@ -163,6 +165,9 @@ operator == (const TextProperties &other) const { if ((_specified & F_has_text_scale) && _text_scale != other._text_scale) { return false; } + if ((_specified & F_has_direction) && _direction != other._direction) { + return false; + } return true; } @@ -238,6 +243,9 @@ add_properties(const TextProperties &other) { if (other.has_text_scale()) { set_text_scale(other.get_text_scale()); } + if (other.has_direction()) { + set_direction(other.get_direction()); + } } @@ -361,6 +369,20 @@ write(ostream &out, int indent_level) const { indent(out, indent_level) << "text scale is " << get_text_scale() << "\n"; } + + if (has_direction()) { + indent(out, indent_level) + << "direction is "; + switch (get_direction()) { + case D_ltr: + out << "D_ltr\n"; + break; + + case D_rtl: + out << "D_rtl\n"; + break; + } + } } /** diff --git a/panda/src/text/textProperties.h b/panda/src/text/textProperties.h index c320e9a37b..af899f7b59 100644 --- a/panda/src/text/textProperties.h +++ b/panda/src/text/textProperties.h @@ -49,6 +49,11 @@ PUBLISHED: A_boxed_center }; + enum Direction { + D_ltr, + D_rtl, + }; + TextProperties(); TextProperties(const TextProperties ©); void operator = (const TextProperties ©); @@ -160,6 +165,11 @@ PUBLISHED: INLINE bool has_text_scale() const; INLINE PN_stdfloat get_text_scale() const; + INLINE void set_direction(Direction direction); + INLINE void clear_direction(); + INLINE bool has_direction() const; + INLINE Direction get_direction() const; + void add_properties(const TextProperties &other); void write(ostream &out, int indent_level = 0) const; @@ -197,6 +207,8 @@ PUBLISHED: set_glyph_shift, clear_glyph_shift); MAKE_PROPERTY2(text_scale, has_text_scale, get_text_scale, set_text_scale, clear_text_scale); + MAKE_PROPERTY2(direction, has_direction, get_direction, + set_direction, clear_direction); public: const RenderState *get_text_state() const; @@ -225,6 +237,7 @@ private: F_has_underscore = 0x00010000, F_has_underscore_height = 0x00020000, F_has_text_scale = 0x00040000, + F_has_direction = 0x00080000, }; int _specified; @@ -248,6 +261,7 @@ private: PN_stdfloat _glyph_scale; PN_stdfloat _glyph_shift; PN_stdfloat _text_scale; + Direction _direction; mutable CPT(RenderState) _text_state; mutable CPT(RenderState) _shadow_state; diff --git a/panda/src/tform/buttonThrower.h b/panda/src/tform/buttonThrower.h index 009055fe3b..2c1a8a3982 100644 --- a/panda/src/tform/buttonThrower.h +++ b/panda/src/tform/buttonThrower.h @@ -34,7 +34,7 @@ */ class EXPCL_PANDA_TFORM ButtonThrower : public DataNode { PUBLISHED: - ButtonThrower(const string &name); + explicit ButtonThrower(const string &name); ~ButtonThrower(); INLINE void set_button_down_event(const string &button_down_event); diff --git a/panda/src/tform/driveInterface.h b/panda/src/tform/driveInterface.h index 49c6a521d7..89f6b069b0 100644 --- a/panda/src/tform/driveInterface.h +++ b/panda/src/tform/driveInterface.h @@ -30,7 +30,7 @@ */ class EXPCL_PANDA_TFORM DriveInterface : public MouseInterfaceNode { PUBLISHED: - DriveInterface(const string &name = ""); + explicit DriveInterface(const string &name = ""); ~DriveInterface(); INLINE void set_forward_speed(PN_stdfloat speed); diff --git a/panda/src/tform/mouseInterfaceNode.h b/panda/src/tform/mouseInterfaceNode.h index 02fdf2c200..1555dba852 100644 --- a/panda/src/tform/mouseInterfaceNode.h +++ b/panda/src/tform/mouseInterfaceNode.h @@ -30,7 +30,7 @@ class ButtonEventList; */ class EXPCL_PANDA_TFORM MouseInterfaceNode : public DataNode { public: - MouseInterfaceNode(const string &name); + explicit MouseInterfaceNode(const string &name); virtual ~MouseInterfaceNode(); PUBLISHED: diff --git a/panda/src/tform/mouseSubregion.h b/panda/src/tform/mouseSubregion.h index acfd8a4142..f500992bcb 100644 --- a/panda/src/tform/mouseSubregion.h +++ b/panda/src/tform/mouseSubregion.h @@ -32,7 +32,7 @@ */ class EXPCL_PANDA_TFORM MouseSubregion : public MouseInterfaceNode { PUBLISHED: - MouseSubregion(const string &name); + explicit MouseSubregion(const string &name); ~MouseSubregion(); INLINE PN_stdfloat get_left() const; diff --git a/panda/src/tform/mouseWatcher.h b/panda/src/tform/mouseWatcher.h index aa67b1b657..c3b4988b7f 100644 --- a/panda/src/tform/mouseWatcher.h +++ b/panda/src/tform/mouseWatcher.h @@ -60,7 +60,7 @@ class DisplayRegion; */ class EXPCL_PANDA_TFORM MouseWatcher : public DataNode, public MouseWatcherBase { PUBLISHED: - MouseWatcher(const string &name = ""); + explicit MouseWatcher(const string &name = ""); ~MouseWatcher(); bool remove_region(MouseWatcherRegion *region); diff --git a/panda/src/tform/mouseWatcherRegion.h b/panda/src/tform/mouseWatcherRegion.h index 2874082750..0230c42d96 100644 --- a/panda/src/tform/mouseWatcherRegion.h +++ b/panda/src/tform/mouseWatcherRegion.h @@ -30,9 +30,9 @@ class MouseWatcherParameter; */ class EXPCL_PANDA_TFORM MouseWatcherRegion : public TypedWritableReferenceCount, public Namable { PUBLISHED: - INLINE MouseWatcherRegion(const string &name, PN_stdfloat left, PN_stdfloat right, - PN_stdfloat bottom, PN_stdfloat top); - INLINE MouseWatcherRegion(const string &name, const LVecBase4 &frame); + INLINE explicit MouseWatcherRegion(const string &name, PN_stdfloat left, PN_stdfloat right, + PN_stdfloat bottom, PN_stdfloat top); + INLINE explicit MouseWatcherRegion(const string &name, const LVecBase4 &frame); INLINE void set_frame(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top); INLINE void set_frame(const LVecBase4 &frame); diff --git a/panda/src/tform/trackball.h b/panda/src/tform/trackball.h index 964ad7d8dd..634e81f61a 100644 --- a/panda/src/tform/trackball.h +++ b/panda/src/tform/trackball.h @@ -34,7 +34,7 @@ */ class EXPCL_PANDA_TFORM Trackball : public MouseInterfaceNode { PUBLISHED: - Trackball(const string &name); + explicit Trackball(const string &name); ~Trackball(); void reset(); diff --git a/panda/src/tform/transform2sg.h b/panda/src/tform/transform2sg.h index b5c1fa40ba..66939aa096 100644 --- a/panda/src/tform/transform2sg.h +++ b/panda/src/tform/transform2sg.h @@ -27,7 +27,7 @@ */ class EXPCL_PANDA_TFORM Transform2SG : public DataNode { PUBLISHED: - Transform2SG(const string &name); + explicit Transform2SG(const string &name); void set_node(PandaNode *node); PandaNode *get_node() const; diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx index 6818ea65a0..9da9aa0fea 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx +++ b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx @@ -470,7 +470,6 @@ end_frame(Thread *current_thread) { */ bool TinyGraphicsStateGuardian:: begin_draw_primitives(const GeomPipelineReader *geom_reader, - const GeomMunger *munger, const GeomVertexDataPipelineReader *data_reader, bool force) { #ifndef NDEBUG @@ -479,7 +478,7 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader, } #endif // NDEBUG - if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger, data_reader, force)) { + if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, data_reader, force)) { return false; } nassertr(_data_reader != (GeomVertexDataPipelineReader *)NULL, false); @@ -1734,11 +1733,7 @@ release_texture(TextureContext *tc) { */ void TinyGraphicsStateGuardian:: do_issue_light() { - // Initialize the current ambient light total and newly enabled light list - LColor cur_ambient_light(0.0f, 0.0f, 0.0f, 0.0f); - int num_enabled = 0; - int num_on_lights = 0; const LightAttrib *target_light = DCAST(LightAttrib, _target_rs->get_attrib_def(LightAttrib::get_class_slot())); if (display_cat.is_spam()) { @@ -1750,43 +1745,35 @@ do_issue_light() { clear_light_state(); // Now, assign new lights. - if (target_light != (LightAttrib *)NULL) { - CPT(LightAttrib) new_light = target_light->filter_to_max(_max_lights); - if (display_cat.is_spam()) { - new_light->write(display_cat.spam(false), 2); - } - - num_on_lights = new_light->get_num_on_lights(); - for (int li = 0; li < num_on_lights; li++) { - NodePath light = new_light->get_on_light(li); - nassertv(!light.is_empty()); - Light *light_obj = light.node()->as_light(); - nassertv(light_obj != (Light *)NULL); - + if (target_light != nullptr) { + if (target_light->has_any_on_light()) { _lighting_enabled = true; _c->lighting_enabled = true; + } - if (light_obj->get_type() == AmbientLight::get_class_type()) { - // Accumulate all of the ambient lights together into one. - cur_ambient_light += light_obj->get_color(); + size_t filtered_lights = min((size_t)_max_lights, target_light->get_num_non_ambient_lights()); + for (size_t li = 0; li < filtered_lights; ++li) { + NodePath light = target_light->get_on_light(li); + nassertv(!light.is_empty()); + Light *light_obj = light.node()->as_light(); + nassertv(light_obj != nullptr); - } else { - // Other kinds of lights each get their own GLLight object. - light_obj->bind(this, light, num_enabled); - num_enabled++; + // Other kinds of lights each get their own GLLight object. + light_obj->bind(this, light, num_enabled); + num_enabled++; - // Handle the diffuse color here, since all lights have this property. - GLLight *gl_light = _c->first_light; - nassertv(gl_light != NULL); - const LColor &diffuse = light_obj->get_color(); - gl_light->diffuse.v[0] = diffuse[0]; - gl_light->diffuse.v[1] = diffuse[1]; - gl_light->diffuse.v[2] = diffuse[2]; - gl_light->diffuse.v[3] = diffuse[3]; - } + // Handle the diffuse color here, since all lights have this property. + GLLight *gl_light = _c->first_light; + nassertv(gl_light != NULL); + const LColor &diffuse = light_obj->get_color(); + gl_light->diffuse.v[0] = diffuse[0]; + gl_light->diffuse.v[1] = diffuse[1]; + gl_light->diffuse.v[2] = diffuse[2]; + gl_light->diffuse.v[3] = diffuse[3]; } } + LColor cur_ambient_light = target_light->get_ambient_contribution(); _c->ambient_light_model.v[0] = cur_ambient_light[0]; _c->ambient_light_model.v[1] = cur_ambient_light[1]; _c->ambient_light_model.v[2] = cur_ambient_light[2]; @@ -2612,12 +2599,10 @@ setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) { if (gltex->total_bytecount != total_bytecount) { if (gltex->allocated_buffer != NULL) { - PANDA_FREE_ARRAY(gltex->allocated_buffer); - TinyTextureContext::get_class_type().dec_memory_usage(TypeHandle::MC_array, gltex->total_bytecount); + TinyTextureContext::get_class_type().deallocate_array(gltex->allocated_buffer); } - gltex->allocated_buffer = PANDA_MALLOC_ARRAY(total_bytecount); + gltex->allocated_buffer = TinyTextureContext::get_class_type().allocate_array(total_bytecount); gltex->total_bytecount = total_bytecount; - TinyTextureContext::get_class_type().inc_memory_usage(TypeHandle::MC_array, total_bytecount); } char *next_buffer = (char *)gltex->allocated_buffer; diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.h b/panda/src/tinydisplay/tinyGraphicsStateGuardian.h index 65e01d7fbc..c7aae852cd 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.h +++ b/panda/src/tinydisplay/tinyGraphicsStateGuardian.h @@ -63,7 +63,6 @@ public: virtual void end_frame(Thread *current_thread); virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, - const GeomMunger *munger, const GeomVertexDataPipelineReader *data_reader, bool force); virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, diff --git a/panda/src/tinydisplay/tinyTextureContext.cxx b/panda/src/tinydisplay/tinyTextureContext.cxx index 87427ac365..5a3ae3414d 100644 --- a/panda/src/tinydisplay/tinyTextureContext.cxx +++ b/panda/src/tinydisplay/tinyTextureContext.cxx @@ -24,8 +24,7 @@ TinyTextureContext:: GLTexture *gltex = &_gltex; if (gltex->allocated_buffer != NULL) { nassertv(gltex->num_levels != 0); - TinyTextureContext::get_class_type().dec_memory_usage(TypeHandle::MC_array, gltex->total_bytecount); - PANDA_FREE_ARRAY(gltex->allocated_buffer); + get_class_type().deallocate_array(gltex->allocated_buffer); gltex->allocated_buffer = NULL; gltex->total_bytecount = 0; gltex->num_levels = 0; @@ -51,8 +50,7 @@ evict_lru() { GLTexture *gltex = &_gltex; if (gltex->allocated_buffer != NULL) { nassertv(gltex->num_levels != 0); - TinyTextureContext::get_class_type().dec_memory_usage(TypeHandle::MC_array, gltex->total_bytecount); - PANDA_FREE_ARRAY(gltex->allocated_buffer); + get_class_type().deallocate_array(gltex->allocated_buffer); gltex->allocated_buffer = NULL; gltex->total_bytecount = 0; gltex->num_levels = 0; diff --git a/panda/src/vision/webcamVideo.h b/panda/src/vision/webcamVideo.h index a1a74d3562..cdcf5e393d 100644 --- a/panda/src/vision/webcamVideo.h +++ b/panda/src/vision/webcamVideo.h @@ -28,6 +28,7 @@ PUBLISHED: static int get_num_options(); static PT(WebcamVideo) get_option(int n); MAKE_SEQ(get_options, get_num_options, get_option); + MAKE_SEQ_PROPERTY(options, get_num_options, get_option); INLINE int get_size_x() const; INLINE int get_size_y() const; diff --git a/panda/src/vision/webcamVideoCursorV4L.h b/panda/src/vision/webcamVideoCursorV4L.h index 7eb7b02763..9555096eea 100644 --- a/panda/src/vision/webcamVideoCursorV4L.h +++ b/panda/src/vision/webcamVideoCursorV4L.h @@ -24,9 +24,18 @@ #include #ifdef HAVE_JPEG +// jconfig.h overrides our INLINE definition. +#ifdef __GNUC__ +#pragma push_macro("INLINE") +#endif + extern "C" { #include } + +#ifdef __GNUC__ +#pragma pop_macro("INLINE") +#endif #endif class WebcamVideoV4L; diff --git a/panda/src/vrpn/vrpnClient.h b/panda/src/vrpn/vrpnClient.h index 1fd0267601..501c0e2ae8 100644 --- a/panda/src/vrpn/vrpnClient.h +++ b/panda/src/vrpn/vrpnClient.h @@ -34,7 +34,7 @@ class VrpnDialDevice; */ class EXPCL_VRPN VrpnClient : public ClientBase { PUBLISHED: - VrpnClient(const string &server_name); + explicit VrpnClient(const string &server_name); ~VrpnClient(); INLINE const string &get_server_name() const; diff --git a/panda/src/windisplay/winGraphicsPipe.cxx b/panda/src/windisplay/winGraphicsPipe.cxx index 3d584de226..cf76e06296 100644 --- a/panda/src/windisplay/winGraphicsPipe.cxx +++ b/panda/src/windisplay/winGraphicsPipe.cxx @@ -20,10 +20,7 @@ #include "psapi.h" #include "powrprof.h" - -#ifdef _WIN64 #include -#endif TypeHandle WinGraphicsPipe::_type_handle; @@ -99,443 +96,6 @@ void get_memory_information (DisplayInformation *display_information) { } } -typedef union { - uint64_t long_integer; -} -LONG_INTEGER; - -uint64_t cpu_time_function (void) { -#ifdef _WIN64 - return __rdtsc(); -#else - LONG_INTEGER long_integer; - LONG_INTEGER *long_integer_pointer; - - long_integer_pointer = &long_integer; - - __asm { - mov ebx,[long_integer_pointer] - rdtsc - mov [ebx + 0], eax - mov [ebx + 4], edx - } - - return long_integer.long_integer; -#endif -} - -typedef union { - struct { - union { - struct { - unsigned char al; - unsigned char ah; - }; - unsigned int eax; - }; - unsigned int ebx; - unsigned int ecx; - unsigned int edx; - }; -} CPU_ID_REGISTERS; - -typedef struct { - union { - struct { - int maximum_cpu_id_input; - char cpu_vendor [16]; - }; - - CPU_ID_REGISTERS cpu_id_registers_0; - }; - - union { - CPU_ID_REGISTERS cpu_id_registers_1; - - struct { - // eax - union { - unsigned int eax; - unsigned int version_information; - struct { - unsigned int stepping_id : 4; - unsigned int model : 4; - unsigned int family : 4; - unsigned int processor_type : 2; - unsigned int reserved_0 : 2; - unsigned int extended_model_id : 4; - unsigned int extended_family_id : 8; - unsigned int reserved_1 : 4; - }; - }; - - // ebx - union { - unsigned int ebx; - struct { - unsigned int brand_index : 8; - unsigned int clflush : 8; - unsigned int maximum_logical_processors : 8; - unsigned int initial_apic_id : 8; - }; - }; - - // ecx - union { - unsigned int ecx; - struct { - unsigned int sse3 : 1; - unsigned int reserved_1_to_2 : 2; - unsigned int monitor : 1; - unsigned int ds_cpl : 1; - unsigned int vmx : 1; - unsigned int reserved_6 : 1; - unsigned int est : 1; - unsigned int tm2 : 1; - unsigned int reserved_9 : 1; - unsigned int cnxt_id : 1; - unsigned int reserved_11_to_12 : 2; - unsigned int cmpxchg16b : 1; - unsigned int xtpr_disable : 1; - unsigned int reserved_15_to_31 : 17; - }; - }; - - // edx - union { - unsigned int edx; - struct { - unsigned int fpu : 1; - unsigned int vme : 1; - unsigned int de : 1; - unsigned int pse : 1; - unsigned int tsc : 1; - unsigned int msr : 1; - unsigned int pae : 1; - unsigned int mce : 1; - unsigned int cx8 : 1; - unsigned int apic : 1; - unsigned int reserved_10 : 1; - unsigned int sep : 1; - unsigned int mtrr : 1; - unsigned int pge : 1; - unsigned int mca : 1; - unsigned int cmov : 1; - unsigned int pat : 1; - unsigned int pse_36 : 1; - unsigned int psn : 1; - unsigned int cflush : 1; - unsigned int reserved_20 : 1; - unsigned int ds : 1; - unsigned int acpi : 1; - unsigned int mmx : 1; - unsigned int fxsr : 1; - unsigned int sse : 1; - unsigned int sse2 : 1; - unsigned int ss : 1; - unsigned int htt : 1; - unsigned int tm : 1; - unsigned int reserved_30 : 1; - unsigned int pbe : 1; - }; - }; - }; - }; - - #define MAXIMUM_2 8 - #define MAXIMUM_CHARACTERS (MAXIMUM_2 * sizeof(CPU_ID_REGISTERS)) - - union { - CPU_ID_REGISTERS cpu_id_registers_2; - unsigned char character_array_2 [MAXIMUM_CHARACTERS]; - CPU_ID_REGISTERS cpu_id_registers_2_array [MAXIMUM_2]; - }; - - union { - CPU_ID_REGISTERS cpu_id_registers_0x80000000; - }; - - union { - CPU_ID_REGISTERS cpu_id_registers_0x80000001; - }; - - union { - char cpu_brand_string [sizeof(CPU_ID_REGISTERS) * 3]; - struct { - CPU_ID_REGISTERS cpu_id_registers_0x80000002; - CPU_ID_REGISTERS cpu_id_registers_0x80000003; - CPU_ID_REGISTERS cpu_id_registers_0x80000004; - }; - }; - - union { - struct { - unsigned int eax; - unsigned int ebx; - union { - unsigned int ecx; - struct { - unsigned int l1_data_cache_line_size : 8; - unsigned int l1_data_reserved_8_to_15 : 8; - unsigned int l1_data_associativity : 8; - unsigned int l1_data_cache_size : 8; - }; - }; - union { - unsigned int edx; - struct { - unsigned int l1_code_cache_line_size : 8; - unsigned int l1_code_reserved_8_to_15 : 8; - unsigned int l1_code_associativity : 8; - unsigned int l1_code_cache_size : 8; - }; - }; - }; - CPU_ID_REGISTERS cpu_id_registers_0x80000005; - }; - - union { - struct { - unsigned int eax; - unsigned int ebx; - union { - unsigned int ecx; - struct { - unsigned int l2_cache_line_size : 8; - unsigned int l2_reserved_8_to_11 : 4; - unsigned int l2_associativity : 4; - unsigned int l2_cache_size : 16; - }; - }; - unsigned int edx; - }; - CPU_ID_REGISTERS cpu_id_registers_0x80000006; - }; - - CPU_ID_REGISTERS cpu_id_registers_0x80000008; - - unsigned int cache_line_size; - unsigned int log_base_2_cache_line_size; -} CPU_ID; - -typedef struct { - CPU_ID_REGISTERS cpu_id_registers_0; - CPU_ID_REGISTERS cpu_id_registers_1; - - CPU_ID_REGISTERS cpu_id_registers_0x80000000; - CPU_ID_REGISTERS cpu_id_registers_0x80000001; - CPU_ID_REGISTERS cpu_id_registers_0x80000002; - CPU_ID_REGISTERS cpu_id_registers_0x80000003; - CPU_ID_REGISTERS cpu_id_registers_0x80000004; - - CPU_ID_REGISTERS cpu_id_registers_0x80000006; - - CPU_ID_REGISTERS cpu_id_registers_0x80000008; -} CPU_ID_BINARY_DATA; - -void cpu_id_to_cpu_id_binary_data (CPU_ID *cpu_id, CPU_ID_BINARY_DATA *cpu_id_binary_data) { - cpu_id_binary_data->cpu_id_registers_0 = cpu_id->cpu_id_registers_0; - cpu_id_binary_data->cpu_id_registers_1 = cpu_id->cpu_id_registers_1; - cpu_id_binary_data->cpu_id_registers_0x80000000 = cpu_id->cpu_id_registers_0x80000000; - cpu_id_binary_data->cpu_id_registers_0x80000001 = cpu_id->cpu_id_registers_0x80000001; - cpu_id_binary_data->cpu_id_registers_0x80000002 = cpu_id->cpu_id_registers_0x80000002; - cpu_id_binary_data->cpu_id_registers_0x80000003 = cpu_id->cpu_id_registers_0x80000003; - cpu_id_binary_data->cpu_id_registers_0x80000004 = cpu_id->cpu_id_registers_0x80000004; - cpu_id_binary_data->cpu_id_registers_0x80000006 = cpu_id->cpu_id_registers_0x80000006; - cpu_id_binary_data->cpu_id_registers_0x80000008 = cpu_id->cpu_id_registers_0x80000008; -} - -void cpu_id_binary_data_to_cpu_id (CPU_ID_BINARY_DATA *cpu_id_binary_data, CPU_ID *cpu_id) { - memset (cpu_id, 0, sizeof(CPU_ID)); - - cpu_id->cpu_id_registers_0 = cpu_id_binary_data->cpu_id_registers_0; - cpu_id->cpu_id_registers_1 = cpu_id_binary_data->cpu_id_registers_1; - cpu_id->cpu_id_registers_0x80000000 = cpu_id_binary_data->cpu_id_registers_0x80000000; - cpu_id->cpu_id_registers_0x80000001 = cpu_id_binary_data->cpu_id_registers_0x80000001; - cpu_id->cpu_id_registers_0x80000002 = cpu_id_binary_data->cpu_id_registers_0x80000002; - cpu_id->cpu_id_registers_0x80000003 = cpu_id_binary_data->cpu_id_registers_0x80000003; - cpu_id->cpu_id_registers_0x80000004 = cpu_id_binary_data->cpu_id_registers_0x80000004; - cpu_id->cpu_id_registers_0x80000006 = cpu_id_binary_data->cpu_id_registers_0x80000006; - cpu_id->cpu_id_registers_0x80000008 = cpu_id_binary_data->cpu_id_registers_0x80000008; -} - -int cpuid(int input_eax, CPU_ID_REGISTERS *cpu_id_registers) { - int state; - - state = false; - __try { - if (input_eax == 0) { - // the order of ecx and edx is swapped when saved to make a proper - // vendor string -#ifdef _WIN64 - __cpuid((int*)cpu_id_registers, input_eax); - unsigned int tmp = cpu_id_registers->edx; - cpu_id_registers->edx = cpu_id_registers->ecx; - cpu_id_registers->ecx = tmp; -#else - __asm { - mov eax, [input_eax] - mov edi, [cpu_id_registers] - - cpuid - - mov [edi + 0], eax - mov [edi + 4], ebx - mov [edi + 8], edx - mov [edi + 12], ecx - } -#endif - } else { -#ifdef _WIN64 - __cpuid((int*)cpu_id_registers, input_eax); -#else - __asm { - mov eax, [input_eax] - mov edi, [cpu_id_registers] - - cpuid - - mov [edi + 0], eax - mov [edi + 4], ebx - mov [edi + 8], ecx - mov [edi + 12], edx - } -#endif - } - - state = true; - } - __except (1) { - state = false; - } - - return state; -} - -void parse_cpu_id(CPU_ID *cpu_id) { - printf("CPUID\n"); - printf(" vendor = %s\n", cpu_id->cpu_vendor); - printf(" brand string %s\n", cpu_id->cpu_brand_string); - printf(" maximum_cpu_id_input = %u\n", cpu_id->maximum_cpu_id_input); - printf(" maximum extended information = 0x%X\n", cpu_id->cpu_id_registers_0x80000000.eax); - - printf(" MMX = %u\n", cpu_id->mmx); - printf(" SSE = %u\n", cpu_id->sse); - printf(" SSE2 = %u\n", cpu_id->sse2); - printf(" SSE3 = %u\n", cpu_id->sse3); - - printf(" EST = %u\n", cpu_id->est); - - if (cpu_id->maximum_cpu_id_input >= 1) { - printf(" version_information\n"); - printf(" stepping_id %u\n", cpu_id->stepping_id); - printf(" model %u\n", cpu_id->model); - printf(" family %u\n", cpu_id->family); - printf(" processor_type %u\n", cpu_id->processor_type); - printf(" extended_model_id %u\n", cpu_id->extended_model_id); - printf(" extended_family_id %u\n", cpu_id->extended_family_id); - - printf(" brand_index %u\n", cpu_id->brand_index); - printf(" clflush %u\n", cpu_id->clflush); - printf(" maximum_logical_processors %u\n", cpu_id->maximum_logical_processors); - printf(" initial_apic_id %u\n", cpu_id->initial_apic_id); - -// printf(" cache_line_size %u\n", cpu_id->cache_line_size); printf(" -// log_base_2_cache_line_size %u\n", cpu_id->log_base_2_cache_line_size); - } - - if (cpu_id->cpu_id_registers_0x80000000.eax >= 0x80000005) { - printf(" l1_data_cache_line_size %d\n", cpu_id->l1_data_cache_line_size); - printf(" l1_data_associativity %d\n", cpu_id->l1_data_associativity); - printf(" l1_data_cache_size %dK\n", cpu_id->l1_data_cache_size); - - printf(" l1_code_cache_line_size %d\n", cpu_id->l1_code_cache_line_size); - printf(" l1_code_associativity %d\n", cpu_id->l1_code_associativity); - printf(" l1_code_cache_size %dK\n", cpu_id->l1_code_cache_size); - } - - if (cpu_id->cpu_id_registers_0x80000000.eax >= 0x80000006) { - printf(" l2_cache_line_size %d\n", cpu_id->l2_cache_line_size); - printf(" l2_associativity %d\n", cpu_id->l2_associativity); - printf(" l2_cache_size %dK\n", cpu_id->l2_cache_size); - } -} - -int initialize_cpu_id(CPU_ID *cpu_id) { - int debug = false; - memset(cpu_id, 0, sizeof(CPU_ID)); - - if (cpuid(0, &cpu_id->cpu_id_registers_0)) { - if (cpu_id->maximum_cpu_id_input >= 1) { - cpuid(1, &cpu_id->cpu_id_registers_1); - } - if (cpu_id->maximum_cpu_id_input >= 2) { - unsigned int index; - - cpuid(2, &cpu_id->cpu_id_registers_2); - if (debug) { - printf(" al = %u\n", cpu_id->cpu_id_registers_2.al); - } - - for (index = 1; index < cpu_id->cpu_id_registers_2.al && index < MAXIMUM_2; index++) { - cpuid(2, &cpu_id->cpu_id_registers_2_array [index]); - } - - for (index = 1; index < MAXIMUM_CHARACTERS; index++) { - if (cpu_id->character_array_2 [index]) { - if (debug) { - printf(" cache/TLB byte = %X\n", cpu_id->character_array_2 [index]); - } - switch (cpu_id->character_array_2 [index]) { - case 0x0A: - case 0x0C: - cpu_id->cache_line_size = 32; - cpu_id->log_base_2_cache_line_size = 5; - break; - - case 0x2C: - case 0x60: - case 0x66: - case 0x67: - case 0x68: - cpu_id->cache_line_size = 64; - cpu_id->log_base_2_cache_line_size = 6; - break; - } - } - } - } - - cpuid(0x80000000, &cpu_id->cpu_id_registers_0x80000000); - - if (cpu_id->cpu_id_registers_0x80000000.eax >= 0x80000001) { - cpuid(0x80000001, &cpu_id->cpu_id_registers_0x80000001); - } - - if (cpu_id->cpu_id_registers_0x80000000.eax >= 0x80000004) { - cpuid(0x80000002, &cpu_id->cpu_id_registers_0x80000002); - cpuid(0x80000003, &cpu_id->cpu_id_registers_0x80000003); - cpuid(0x80000004, &cpu_id->cpu_id_registers_0x80000004); - } - - if (cpu_id->cpu_id_registers_0x80000000.eax >= 0x80000005) { - cpuid(0x80000005, &cpu_id->cpu_id_registers_0x80000005); - } - - if (cpu_id->cpu_id_registers_0x80000000.eax >= 0x80000006) { - cpuid(0x80000006, &cpu_id->cpu_id_registers_0x80000006); - } - - if (cpu_id->cpu_id_registers_0x80000000.eax >= 0x80000008) { - cpuid(0x80000008, &cpu_id->cpu_id_registers_0x80000008); - } - - return true; - } - - return false; -} - int update_cpu_frequency_function(int processor_number, DisplayInformation *display_information) { int update; @@ -776,9 +336,6 @@ lookup_cpu_data() { // set callback for memory function _display_information->_get_memory_information_function = get_memory_information; - // set callback for cpu time function - _display_information->_cpu_time_function = cpu_time_function; - // determine CPU frequency uint64_t time; uint64_t end_time; @@ -803,12 +360,12 @@ lookup_cpu_data() { if (QueryPerformanceFrequency(&frequency)) { if (frequency.QuadPart > 0) { if (QueryPerformanceCounter (&counter)) { - time = cpu_time_function(); + time = __rdtsc(); end.QuadPart = counter.QuadPart + frequency.QuadPart; while (QueryPerformanceCounter (&counter) && counter.QuadPart < end.QuadPart) { } - end_time = cpu_time_function(); + end_time = __rdtsc(); _display_information->_cpu_frequency = end_time - time; } @@ -821,54 +378,6 @@ lookup_cpu_data() { sprintf(string, "CPU frequency: %I64d\n", _display_information->_cpu_frequency); windisplay_cat.info() << string; - - // CPUID - CPU_ID cpu_id; - - windisplay_cat.info() << "start CPU ID\n"; - - if (initialize_cpu_id(&cpu_id)) { - CPU_ID_BINARY_DATA *cpu_id_binary_data; - - cpu_id_binary_data = new (CPU_ID_BINARY_DATA); - if (cpu_id_binary_data) { - cpu_id_to_cpu_id_binary_data(&cpu_id, cpu_id_binary_data); - _display_information->_cpu_id_size = sizeof(CPU_ID_BINARY_DATA) / sizeof(unsigned int); - _display_information->_cpu_id_data = (unsigned int *) cpu_id_binary_data; - - _display_information->_cpu_vendor_string = strdup(cpu_id.cpu_vendor); - _display_information->_cpu_brand_string = strdup(cpu_id.cpu_brand_string); - _display_information->_cpu_version_information = cpu_id.version_information; - _display_information->_cpu_brand_index = cpu_id.brand_index; - - if (windisplay_cat.is_debug()) { - windisplay_cat.debug() - << hex << _display_information->_cpu_id_version << dec << "|"; - - int index; - for (index = 0; index < _display_information->_cpu_id_size; ++index) { - unsigned int data; - data = _display_information->_cpu_id_data[index]; - - windisplay_cat.debug(false) - << hex << data << dec; - if (index < _display_information->_cpu_id_size - 1) { - windisplay_cat.debug(false) - << "|"; - } - } - windisplay_cat.debug(false) - << "\n"; - } - } - - if (windisplay_cat.is_debug()) { - parse_cpu_id(&cpu_id); - } - } - - windisplay_cat.info() << "end CPU ID\n"; - // Number of CPU's count_number_of_cpus(_display_information); } diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index fd6a44fcef..f76f1baad9 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -1440,15 +1440,12 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { break; case WM_SIZE: + // Actually, since we don't return in WM_WINDOWPOSCHANGED, WM_SIZE won't + // end up being called at all. This is more efficient according to MSDN. if (windisplay_cat.is_debug()) { windisplay_cat.debug() << "WM_SIZE: " << hwnd << ", " << wparam << "\n"; } - - // Resist calling handle_reshape before the window has opened. - if (_hWnd != NULL) { - handle_reshape(); - } break; case WM_EXITSIZEMOVE: @@ -1456,8 +1453,15 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { break; case WM_WINDOWPOSCHANGED: + if (windisplay_cat.is_debug()) { + windisplay_cat.debug() + << "WM_WINDOWPOSCHANGED: " << hwnd << ", " << wparam << "\n"; + } + if (_hWnd != NULL) { + handle_reshape(); + } adjust_z_order(); - break; + return 0; case WM_PAINT: // In response to WM_PAINT, we check to see if there are any update diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index ccc9523b2f..a0bd1013a4 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -1858,7 +1858,7 @@ get_keyboard_map() const { continue; } - KeySym sym = XKeycodeToKeysym(_display, k, 0); + KeySym sym = XkbKeycodeToKeysym(_display, k, 0, 0); ButtonHandle button = map_button(sym); if (button == ButtonHandle::none()) { continue; @@ -2087,9 +2087,11 @@ read_ico(istream &ico) { if (!ico.good()) goto cleanup; } + int and_stride = ((infoHeader.width >> 3) + 3) & ~0x03; + // Read in the pixel data. xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8; - andBmpSize = (infoHeader.width * (infoHeader.height / 2)) / 8; + andBmpSize = and_stride * (infoHeader.height / 2); curXor = xorBmp = new char[xorBmpSize]; curAnd = andBmp = new char[andBmpSize]; ico.read(xorBmp, xorBmpSize); @@ -2137,21 +2139,15 @@ read_ico(istream &ico) { // Pack each of the three bytes into a single color, BGR -> 0RGB for (i = image->height - 1; i >= 0; i--) { for (j = 0; j < image->width; j++) { - image->pixels[(i * image->width) + j] = (*(curXor + 2) << 16) + - (*(curXor + 1) << 8) + (*curXor); + shift = 7 - (j & 0x7); + uint32_t alpha = (curAnd[j >> 3] & (1 << shift)) ? 0 : 0xff000000U; + image->pixels[(i * image->width) + j] = (uint8_t)curXor[0] + | ((uint8_t)curXor[1] << 8u) + | ((uint8_t)curXor[2] << 16u) + | alpha; curXor += 3; } - - // Set the alpha byte properly according to the andBmp. - for (j = 0; j < image->width; j += 8) { - for (k = 0; k < 8; k++) { - shift = 7 - k; - image->pixels[(i * image->width) + j + k] |= - ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24); - } - - curAnd++; - } + curAnd += and_stride; } break; diff --git a/pandatool/src/eggbase/eggReader.cxx b/pandatool/src/eggbase/eggReader.cxx index 72d0007fb3..dce7b64c51 100644 --- a/pandatool/src/eggbase/eggReader.cxx +++ b/pandatool/src/eggbase/eggReader.cxx @@ -193,7 +193,7 @@ handle_args(ProgramBase::Args &args) { file_path.append_directory(filename.get_dirname()); if (_force_complete) { - if (!file_data.load_externals()) { + if (!file_data.load_externals(file_path)) { exit(1); } } diff --git a/pandatool/src/maxegg/maxEgg.rc b/pandatool/src/maxegg/maxEgg.rc index 161b0ec456..a037734218 100644 --- a/pandatool/src/maxegg/maxEgg.rc +++ b/pandatool/src/maxegg/maxEgg.rc @@ -7,7 +7,8 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "afxres.h" +#include "WinResrc.h" +#define IDC_STATIC -1 ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS diff --git a/pandatool/src/maxprogs/maxImportRes.rc b/pandatool/src/maxprogs/maxImportRes.rc index a8041e52d7..b4f2e778e9 100644 --- a/pandatool/src/maxprogs/maxImportRes.rc +++ b/pandatool/src/maxprogs/maxImportRes.rc @@ -7,8 +7,8 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "afxres.h" - +#include "WinResrc.h" +#define IDC_STATIC -1 //////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS diff --git a/pandatool/src/mayaprogs/mayapath.cxx b/pandatool/src/mayaprogs/mayapath.cxx index aac472759d..428e4ba128 100644 --- a/pandatool/src/mayaprogs/mayapath.cxx +++ b/pandatool/src/mayaprogs/mayapath.cxx @@ -98,6 +98,7 @@ struct MayaVerInfo maya_versions[] = { { "MAYA2015", "2015"}, { "MAYA2016", "2016"}, { "MAYA20165", "2016.5"}, + { "MAYA2017", "2017"}, { 0, 0 }, }; diff --git a/pandatool/src/palettizer/texturePlacement.cxx b/pandatool/src/palettizer/texturePlacement.cxx index 323fe6e368..2cbea3019c 100644 --- a/pandatool/src/palettizer/texturePlacement.cxx +++ b/pandatool/src/palettizer/texturePlacement.cxx @@ -740,25 +740,61 @@ fill_image(PNMImage &image) { for (int y = _placed._y; y < _placed._y + _placed._y_size; y++) { int sy = y - top; - if (_placed._wrap_v == EggTexture::WM_clamp) { + switch (_placed._wrap_v) { + case EggTexture::WM_clamp: // Clamp at [0, y_size). sy = max(min(sy, y_size - 1), 0); + break; - } else { + case EggTexture::WM_mirror: + sy = (sy < 0) ? (y_size * 2) - 1 - ((-sy - 1) % (y_size * 2)) : sy % (y_size * 2); + sy = (sy < y_size) ? sy : 2 * y_size - sy - 1; + break; + + case EggTexture::WM_mirror_once: + sy = (sy < y_size) ? sy : 2 * y_size - sy - 1; + // Fall through + + case EggTexture::WM_border_color: + if (sy < 0 || sy >= y_size) { + continue; + } + break; + + default: // Wrap: sign-independent modulo. sy = (sy < 0) ? y_size - 1 - ((-sy - 1) % y_size) : sy % y_size; + break; } for (int x = _placed._x; x < _placed._x + _placed._x_size; x++) { int sx = x - left; - if (_placed._wrap_u == EggTexture::WM_clamp) { + switch (_placed._wrap_u) { + case EggTexture::WM_clamp: // Clamp at [0, x_size). sx = max(min(sx, x_size - 1), 0); + break; - } else { + case EggTexture::WM_mirror: + sx = (sx < 0) ? (x_size * 2) - 1 - ((-sx - 1) % (x_size * 2)) : sx % (x_size * 2); + sx = (sx < x_size) ? sx : 2 * x_size - sx - 1; + break; + + case EggTexture::WM_mirror_once: + sx = (sx >= 0) ? sx : ~sx; + // Fall through + + case EggTexture::WM_border_color: + if (sx < 0 || sx >= x_size) { + continue; + } + break; + + default: // Wrap: sign-independent modulo. sx = (sx < 0) ? x_size - 1 - ((-sx - 1) % x_size) : sx % x_size; + break; } image.set_xel(x, y, source.get_xel(sx, sy)); diff --git a/pandatool/src/xfile/xParser.cxx.prebuilt b/pandatool/src/xfile/xParser.cxx.prebuilt index 606ead1db3..42c65a72a6 100644 --- a/pandatool/src/xfile/xParser.cxx.prebuilt +++ b/pandatool/src/xfile/xParser.cxx.prebuilt @@ -98,7 +98,7 @@ static XFile *x_file = (XFile *)NULL; static XFileNode *current_node = (XFileNode *)NULL; -static PT(XFileDataDef) current_data_def = (XFileDataDef *)NULL; +static PT(XFileDataDef) current_data_def; //////////////////////////////////////////////////////////////////// // Defining the interface to the parser. diff --git a/pandatool/src/xfile/xParser.yxx b/pandatool/src/xfile/xParser.yxx index 4aa8ffd3b3..a98f39d177 100644 --- a/pandatool/src/xfile/xParser.yxx +++ b/pandatool/src/xfile/xParser.yxx @@ -32,7 +32,7 @@ static XFile *x_file = (XFile *)NULL; static XFileNode *current_node = (XFileNode *)NULL; -static PT(XFileDataDef) current_data_def = (XFileDataDef *)NULL; +static PT(XFileDataDef) current_data_def; //////////////////////////////////////////////////////////////////// // Defining the interface to the parser. diff --git a/samples/chessboard/main.py b/samples/chessboard/main.py index 7da1d97a14..5c8b68302d 100755 --- a/samples/chessboard/main.py +++ b/samples/chessboard/main.py @@ -20,7 +20,7 @@ from direct.showbase.DirectObject import DirectObject from direct.task.Task import Task import sys -# First we define some contants for the colors +# First we define some constants for the colors BLACK = (0, 0, 0, 1) WHITE = (1, 1, 1, 1) HIGHLIGHT = (0, 1, 1, 1) @@ -33,7 +33,7 @@ PIECEBLACK = (.15, .15, .15, 1) # This is how we know where to position an object in 3D space based on a 2D mouse # position. It also assumes that we are dragging in the XY plane. # -# This is derived from the mathmatical of a plane, solved for a given point +# This is derived from the mathematical of a plane, solved for a given point def PointAtZ(z, point, vec): return point + vec * ((z - point.getZ()) / vec.getZ()) @@ -41,7 +41,7 @@ def PointAtZ(z, point, vec): def SquarePos(i): return LPoint3((i % 8) - 3.5, int(i // 8) - 3.5, 0) -# Helper function for determining wheter a square should be white or black +# Helper function for determining whether a square should be white or black # The modulo operations (%) generate the every-other pattern of a chess-board def SquareColor(i): if (i + ((i // 8) % 2)) % 2: @@ -84,7 +84,7 @@ class ChessboardDemo(ShowBase): # relative to it self.pickerNP = camera.attachNewNode(self.pickerNode) # Everything to be picked will use bit 1. This way if we were doing other - # collision we could seperate it + # collision we could separate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() # Make our ray # Add it to the collision node @@ -96,7 +96,7 @@ class ChessboardDemo(ShowBase): # Now we create the chess board and its pieces # We will attach all of the squares to their own root. This way we can do the - # collision pass just on the sqaures and save the time of checking the rest + # collision pass just on the squares and save the time of checking the rest # of the scene self.squareRoot = render.attachNewNode("squareRoot") @@ -240,7 +240,7 @@ class ChessboardDemo(ShowBase): render.setLight(render.attachNewNode(ambientLight)) -# Class for a piece. This just handels loading the model and setting initial +# Class for a piece. This just handles loading the model and setting initial # position and color class Piece(object): def __init__(self, square, color): diff --git a/samples/fireflies/main.py b/samples/fireflies/main.py index 994a7d09ec..41527beb88 100755 --- a/samples/fireflies/main.py +++ b/samples/fireflies/main.py @@ -338,7 +338,7 @@ class FireflyDemo(ShowBase): color_g = random.uniform(0.8, 1.0) color_b = min(color_g, random.uniform(0.5, 1.0)) fly.setColor(color_r, color_g, color_b, 1.0) - fly.setShaderInput("lightcolor", color_r, color_g, color_b, 1.0) + fly.setShaderInput("lightcolor", (color_r, color_g, color_b, 1.0)) int1 = fly.posInterval(random.uniform(7, 12), pos1, pos2) int2 = fly.posInterval(random.uniform(7, 12), pos2, pos1) si1 = fly.scaleInterval(random.uniform(0.8, 1.5), diff --git a/samples/rocket-console/assets/Perfect DOS VGA 437.ttf b/samples/rocket-console/assets/Perfect DOS VGA 437.ttf new file mode 100644 index 0000000000..f5cbfc06fd Binary files /dev/null and b/samples/rocket-console/assets/Perfect DOS VGA 437.ttf differ diff --git a/samples/rocket-console/assets/console.rcss b/samples/rocket-console/assets/console.rcss new file mode 100755 index 0000000000..b1df4879f5 --- /dev/null +++ b/samples/rocket-console/assets/console.rcss @@ -0,0 +1,38 @@ +body +{ + font-family: "Perfect DOS VGA 437"; + font-weight: normal; + font-style: normal; + + // use all the allocated texture space + min-width: 100%; + min-height: 100%; + + background-color: #000; +} + + +text#content +{ + z-index: 2; + font-size: 30px; + + white-space: pre-wrap; + + margin: auto; + + text-align: left; + position: absolute; + + // account for non-proportionality of our 1024x512 + // buffer compared with VGA font proportions and + // wanting to center the screen with 40 columns + top: 16px; + left: 32px; + + width: 100%; + height: 100%; + + color: #888; + +} diff --git a/samples/rocket-console/assets/console.rml b/samples/rocket-console/assets/console.rml new file mode 100755 index 0000000000..7887f4ef7c --- /dev/null +++ b/samples/rocket-console/assets/console.rml @@ -0,0 +1,11 @@ + + + + Administrative Console + + + + + + + diff --git a/samples/rocket-console/assets/dos437.txt b/samples/rocket-console/assets/dos437.txt new file mode 100644 index 0000000000..614d29885e --- /dev/null +++ b/samples/rocket-console/assets/dos437.txt @@ -0,0 +1 @@ +from www.dafont.com/perfect-dos-vga-437.font (info at http://zehfernando.com/2015/revisiting-vga-fonts/) \ No newline at end of file diff --git a/samples/rocket-console/assets/loading.rml b/samples/rocket-console/assets/loading.rml new file mode 100755 index 0000000000..c85a5d3bed --- /dev/null +++ b/samples/rocket-console/assets/loading.rml @@ -0,0 +1,54 @@ + + + Main Menu + + + + + + + +
+ +
+ +
diff --git a/samples/rocket-console/assets/modenine.nfo b/samples/rocket-console/assets/modenine.nfo new file mode 100644 index 0000000000..c6af4f1f8b --- /dev/null +++ b/samples/rocket-console/assets/modenine.nfo @@ -0,0 +1,18 @@ +ModeNine + +Based on Andrew Bulhak's ModeSeven, in turn inspired by the screen +output of the BBC Micro. + +copyright: +(C) 1998 Andrew C. Bulhak +(C) 2001 Graham H Freeman + +Freely Distributable. + +All we ask is that this readme file must be included with the font package. +Impresarios of free font sites and shovelware cd-roms, this means you! + +If you think this font is doovy, let us know at fonts@grudnuk.com, and we +might actually make more fonts. + +Another fine Grudnuk Creations produkt | http://grudnuk.com/ \ No newline at end of file diff --git a/samples/rocket-console/assets/modenine.ttf b/samples/rocket-console/assets/modenine.ttf new file mode 100644 index 0000000000..c0b00462d7 Binary files /dev/null and b/samples/rocket-console/assets/modenine.ttf differ diff --git a/samples/rocket-console/assets/monitor.egg.pz b/samples/rocket-console/assets/monitor.egg.pz new file mode 100644 index 0000000000..9c9f3675fd Binary files /dev/null and b/samples/rocket-console/assets/monitor.egg.pz differ diff --git a/samples/rocket-console/assets/monitor.txt b/samples/rocket-console/assets/monitor.txt new file mode 100644 index 0000000000..f2e1f7265e --- /dev/null +++ b/samples/rocket-console/assets/monitor.txt @@ -0,0 +1,12 @@ + +This is an edited version of this file from blendswap.com (http://www.blendswap.com/blends/view/74468). + +VERY IMPORTANT LICENSE INFORMATION: + +This file has been released by buzo under the following license: + + Creative Commons Zero (Public Domain) + +You can use this model for any purposes according to the following conditions: + + There are no requirements for CC-Zero licensed blends. \ No newline at end of file diff --git a/samples/rocket-console/assets/rkt.rcss b/samples/rocket-console/assets/rkt.rcss new file mode 100755 index 0000000000..ebc49f50fb --- /dev/null +++ b/samples/rocket-console/assets/rkt.rcss @@ -0,0 +1,44 @@ +/* +* Default styles for all the basic elements. +*/ + +div +{ + display: block; +} + +p +{ + display: block; +} + +h1 +{ + display: block; +} + +em +{ + font-style: italic; +} + +strong +{ + font-weight: bold; +} + +datagrid +{ + display: block; +} + +select, dataselect, datacombo +{ + text-align: left; +} + +tabset tabs +{ + display: block; +} + diff --git a/samples/rocket-console/assets/takeyga_kb.egg b/samples/rocket-console/assets/takeyga_kb.egg new file mode 100644 index 0000000000..3085947839 --- /dev/null +++ b/samples/rocket-console/assets/takeyga_kb.egg @@ -0,0 +1,316 @@ + { Z-up } + takeyga_kb { + diffr { 0.800000 } + diffg { 0.800000 } + diffb { 0.800000 } + specr { 0.500000 } + specg { 0.500000 } + specb { 0.500000 } + shininess { 12.5 } + ambr { 1.000000 } + ambg { 1.000000 } + ambb { 1.000000 } + emitr { 0.000000 } + emitg { 0.000000 } + emitb { 0.000000 } +} + + Texture.001 { + "./tex/takeyga_kb_specular.dds" + envtype { MODULATE } + minfilter { LINEAR_MIPMAP_LINEAR } + magfilter { LINEAR_MIPMAP_LINEAR } + wrap { REPEAT } +} + + Tex { + "./tex/takeyga_kb_diffuse.dds" + envtype { MODULATE } + minfilter { LINEAR_MIPMAP_LINEAR } + magfilter { LINEAR_MIPMAP_LINEAR } + wrap { REPEAT } +} + + Texture { + "./tex/takeyga_kb_normal.dds" + envtype { NORMAL } + minfilter { LINEAR_MIPMAP_LINEAR } + magfilter { LINEAR_MIPMAP_LINEAR } + wrap { REPEAT } +} + + Cube.001 { + { + { + 0.08499996364116669 0.0 0.0 0.0 + 0.0 0.23999987542629242 0.0 0.0 + 0.0 0.0 0.02500000037252903 0.0 + 0.0 0.0 0.0 1.0 + } + } + + Cube.001 { + + 0 {0.082953 0.240000 -0.025000 + { + 0.158203125 0.595703125 + } + } + 1 {0.082953 -0.240000 -0.025000 + { + 0.98828125 0.6015625 + } + } + 2 {-0.085000 -0.240000 -0.025000 + { + 0.98828125 0.986328125 + } + } + 3 {-0.085000 0.240000 -0.025000 + { + 0.162109375 0.98828125 + } + } + 4 {0.073544 0.227744 -0.005546 + { + 0.990234375 0.017578125 + } + } + 5 {-0.067932 0.223167 0.018996 + { + 0.98046875 0.46875 + } + } + 6 {-0.067932 -0.223167 0.018996 + { + 0.017578125 0.470703125 + } + } + 7 {0.073544 -0.227744 -0.005546 + { + 0.01953125 0.013671875 + } + } + 8 {0.082372 0.237328 -0.015273 + { + 0.9794921875 0.552734375 + } + } + 9 {0.073544 0.227744 -0.005546 + { + 0.9765625 0.57421875 + } + } + 10 {0.073544 -0.227744 -0.005546 + { + 0.025390625 0.57421875 + } + } + 11 {0.082372 -0.237328 -0.015273 + { + 0.0234375 0.5537109375 + } + } + 12 {0.082372 -0.237328 -0.015273 + { + 0.0703125 0.6123046875 + } + } + 13 {0.073544 -0.227744 -0.005546 + { + 0.095703125 0.615234375 + } + } + 14 {-0.067932 -0.223167 0.018996 + { + 0.13671875 0.97265625 + } + } + 15 {-0.080884 -0.235006 0.001122 + { + 0.07421875 0.982421875 + } + } + 16 {-0.080884 -0.235006 0.001122 + { + 0.0166015625 0.5361328125 + } + } + 17 {-0.067932 -0.223167 0.018996 + { + 0.017578125 0.58203125 + } + } + 18 {-0.067932 0.223167 0.018996 + { + 0.978515625 0.580078125 + } + } + 19 {-0.080884 0.235006 0.001122 + { + 0.984375 0.5341796875 + } + } + 20 {0.082372 0.237328 -0.015273 + { + 0.0703125 0.6123046875 + } + } + 21 {0.082953 0.240000 -0.025000 + { + 0.044921875 0.609375 + } + } + 22 {-0.085000 0.240000 -0.025000 + { + 0.01171875 0.9921875 + } + } + 23 {-0.080884 0.235006 0.001122 + { + 0.07421875 0.982421875 + } + } + 24 {0.082953 0.240000 -0.025000 + { + 0.982421875 0.53125 + } + } + 25 {0.082372 0.237328 -0.015273 + { + 0.9794921875 0.552734375 + } + } + 26 {0.082372 -0.237328 -0.015273 + { + 0.0234375 0.5537109375 + } + } + 27 {0.082953 -0.240000 -0.025000 + { + 0.021484375 0.533203125 + } + } + 28 {0.082953 -0.240000 -0.025000 + { + 0.044921875 0.609375 + } + } + 29 {0.082372 -0.237328 -0.015273 + { + 0.0703125 0.6123046875 + } + } + 30 {-0.080884 -0.235006 0.001122 + { + 0.07421875 0.982421875 + } + } + 31 {-0.085000 -0.240000 -0.025000 + { + 0.01171875 0.9921875 + } + } + 32 {-0.085000 -0.240000 -0.025000 + { + 0.015625 0.490234375 + } + } + 33 {-0.080884 -0.235006 0.001122 + { + 0.0166015625 0.5361328125 + } + } + 34 {-0.080884 0.235006 0.001122 + { + 0.984375 0.5341796875 + } + } + 35 {-0.085000 0.240000 -0.025000 + { + 0.990234375 0.48828125 + } + } + 36 {0.073544 0.227744 -0.005546 + { + 0.095703125 0.615234375 + } + } + 37 {0.082372 0.237328 -0.015273 + { + 0.0703125 0.6123046875 + } + } + 38 {-0.080884 0.235006 0.001122 + { + 0.07421875 0.982421875 + } + } + 39 {-0.067932 0.223167 0.018996 + { + 0.13671875 0.97265625 + } + }} + + + { + { Tex } + { takeyga_kb } + {0.000000 0.000000 -1.000000} + { 0 1 2 3 { Cube.001 }} + } + { + { Tex } + { takeyga_kb } + {0.508027 -0.000000 0.861341} + { 4 5 6 7 { Cube.001 }} + } + { + { Tex } + { takeyga_kb } + {0.966167 -0.000000 0.257917} + { 8 9 10 11 { Cube.001 }} + } + { + { Tex } + { takeyga_kb } + {0.028240 -0.996449 0.079322} + { 12 13 14 15 { Cube.001 }} + } + { + { Tex } + { takeyga_kb } + {-0.978036 0.000000 0.208436} + { 16 17 18 19 { Cube.001 }} + } + { + { Tex } + { takeyga_kb } + {0.001260 0.999752 0.022233} + { 20 21 22 23 { Cube.001 }} + } + { + { Tex } + { takeyga_kb } + {0.999846 -0.000000 0.017550} + { 24 25 26 27 { Cube.001 }} + } + { + { Tex } + { takeyga_kb } + {0.001259 -0.999752 0.022233} + { 28 29 30 31 { Cube.001 }} + } + { + { Tex } + { takeyga_kb } + {-0.998928 0.000000 0.046291} + { 32 33 34 35 { Cube.001 }} + } + { + { Tex } + { takeyga_kb } + {0.028241 0.996449 0.079322} + { 36 37 38 39 { Cube.001 }} + } + } diff --git a/samples/rocket-console/assets/tex/takeyga_kb_diffuse.dds b/samples/rocket-console/assets/tex/takeyga_kb_diffuse.dds new file mode 100644 index 0000000000..406e689545 Binary files /dev/null and b/samples/rocket-console/assets/tex/takeyga_kb_diffuse.dds differ diff --git a/samples/rocket-console/assets/tex/takeyga_kb_normal.dds b/samples/rocket-console/assets/tex/takeyga_kb_normal.dds new file mode 100644 index 0000000000..a949e2be69 Binary files /dev/null and b/samples/rocket-console/assets/tex/takeyga_kb_normal.dds differ diff --git a/samples/rocket-console/assets/tex/takeyga_kb_specular.dds b/samples/rocket-console/assets/tex/takeyga_kb_specular.dds new file mode 100644 index 0000000000..a63868f99c Binary files /dev/null and b/samples/rocket-console/assets/tex/takeyga_kb_specular.dds differ diff --git a/samples/rocket-console/assets/window.rcss b/samples/rocket-console/assets/window.rcss new file mode 100755 index 0000000000..0e7adcedf9 --- /dev/null +++ b/samples/rocket-console/assets/window.rcss @@ -0,0 +1,56 @@ +body +{ + font-family: "MODENINE"; + font-weight: normal; + font-style: normal; + font-size: 15; + +} + +body.window +{ + padding-top: 43px; + padding-bottom: 20px; + + min-width: 250px; + + min-height: 135px; + max-height: 700px; + +} + + + +div#title_bar +{ + z-index: 1; + + position: absolute; + top: 0px; + left: 0px; + + text-align: center; + + color: #fff; + background-color: #22f; +} + + +div#title_bar span +{ + padding-top: 17px; + padding-bottom: 48px; + + font-size: 32; + font-weight: bold; + + outline-font-effect: outline; + outline-width: 1px; + outline-color: black; +} + +div#title_bar_content +{ + text-align: center; + color: #cff; +} diff --git a/samples/rocket-console/assets/window.rml b/samples/rocket-console/assets/window.rml new file mode 100755 index 0000000000..47336e7ab2 --- /dev/null +++ b/samples/rocket-console/assets/window.rml @@ -0,0 +1,42 @@ + diff --git a/samples/rocket-console/console.py b/samples/rocket-console/console.py new file mode 100644 index 0000000000..e32d2408d4 --- /dev/null +++ b/samples/rocket-console/console.py @@ -0,0 +1,153 @@ +""" +Simple console widget for rocket +""" +import sys, os.path + +# workaround: https://www.panda3d.org/forums/viewtopic.php?t=10062&p=99697#p99054 +#from panda3d import rocket +import _rocketcore as rocket + +from panda3d.rocket import RocketRegion, RocketInputHandler + +class Console(object): + def __init__(self, base, context, cols, rows, commandHandler): + self.base = base + + self.context = context + self.loadFonts() + self.cols = cols + self.rows = rows + self.commandHandler = commandHandler + + self.setupConsole() + self.allowEditing(True) + + def getTextContainer(self): + return self.textEl + + def setPrompt(self, prompt): + self.consolePrompt = prompt + + def allowEditing(self, editMode): + self.editMode = editMode + if editMode: + self.input = "" + if not self.lastLine: + self.addLine("") + self.newEditLine() + + def loadFonts(self): + rocket.LoadFontFace("Perfect DOS VGA 437.ttf") + + def setupConsole(self): + self.document = self.context.LoadDocument("console.rml") + if not self.document: + raise AssertionError("did not find console.rml") + + el = self.document.GetElementById('content') + + self.textEl = el + + # roundabout way of accessing the current object through rocket event... + + # add attribute to let Rocket know about the receiver + self.context.console = self + + # then reference through the string format (dunno how else to get the event...) + self.document.AddEventListener( + 'keydown', 'document.context.console.handleKeyDown(event)', True) + self.document.AddEventListener( + 'textinput', 'document.context.console.handleTextInput(event)', True) + + self.consolePrompt = "C:\\>" + + self.input = "" + self.lastLine = None + + self.blinkState = False + self.queueBlinkCursor() + + self.document.Show() + + def queueBlinkCursor(self): + self.base.taskMgr.doMethodLater(0.2, self.blinkCursor, 'blinkCursor') + + def blinkCursor(self, task): + self.blinkState = not self.blinkState + if self.editMode: + self.updateEditLine(self.input) + self.queueBlinkCursor() + + def escape(self, text): + return text. \ + replace('<', '<'). \ + replace('>', '>'). \ + replace('"', '"') + + def addLine(self, text): + curKids = list(self.textEl.child_nodes) + while len(curKids) >= self.rows: + self.textEl.RemoveChild(curKids[0]) + curKids = curKids[1:] + + line = self.document.CreateTextNode(self.escape(text) + '\n') + self.textEl.AppendChild(line) + self.lastLine = line + + def addLines(self, lines): + for line in lines: + self.addLine(line) + + def updateEditLine(self, newInput=''): + newText = self.consolePrompt + newInput + self.lastLine.text = self.escape(newText) + (self.blinkState and '_' or '') + self.input = newInput + + def scroll(self): + self.blinkState = False + self.updateEditLine(self.input + '\n') + + def handleKeyDown(self, event): + """ + Handle control keys + """ + keyId = event.parameters['key_identifier'] + if not self.editMode: + if keyId == rocket.key_identifier.PAUSE: + if event.parameters['ctrl_key']: + self.commandHandler(None) + + return + + if keyId == rocket.key_identifier.RETURN: + # emit line without cursor + self.scroll() + + # handle command + self.commandHandler(self.input) + + if self.editMode: + # start with new "command" + self.addLine(self.consolePrompt) + self.updateEditLine("") + + elif keyId == rocket.key_identifier.BACK: + self.updateEditLine(self.input[0:-1]) + + def handleTextInput(self, event): + if not self.editMode: + return + + # handle normal text character + data = event.parameters['data'] + if 32 <= data < 128: + self.updateEditLine(self.input + chr(data)) + + def newEditLine(self): + self.addLine("") + self.updateEditLine() + + def cls(self): + curKids = list(self.textEl.child_nodes) + for kid in curKids: + self.textEl.RemoveChild(kid) \ No newline at end of file diff --git a/samples/rocket-console/main.py b/samples/rocket-console/main.py new file mode 100644 index 0000000000..7269c4b960 --- /dev/null +++ b/samples/rocket-console/main.py @@ -0,0 +1,410 @@ +""" +Show how to use libRocket in Panda3D. +""" +import sys +from panda3d.core import loadPrcFile, loadPrcFileData, Point3,Vec4, Mat4, LoaderOptions # @UnusedImport +from panda3d.core import DirectionalLight, AmbientLight, PointLight +from panda3d.core import Texture, PNMImage +from panda3d.core import PandaSystem +import random +from direct.interval.LerpInterval import LerpHprInterval, LerpPosInterval, LerpFunc +from direct.showbase.ShowBase import ShowBase + +# workaround: https://www.panda3d.org/forums/viewtopic.php?t=10062&p=99697#p99054 +#from panda3d import rocket +import _rocketcore as rocket + +from panda3d.rocket import RocketRegion, RocketInputHandler + +loadPrcFileData("", "model-path $MAIN_DIR/assets") + +import console + +global globalClock + +class MyApp(ShowBase): + + def __init__(self): + ShowBase.__init__(self) + + self.win.setClearColor(Vec4(0.2, 0.2, 0.2, 1)) + + self.disableMouse() + + self.render.setShaderAuto() + + dlight = DirectionalLight('dlight') + alight = AmbientLight('alight') + dlnp = self.render.attachNewNode(dlight) + alnp = self.render.attachNewNode(alight) + dlight.setColor((0.8, 0.8, 0.5, 1)) + alight.setColor((0.2, 0.2, 0.2, 1)) + dlnp.setHpr(0, -60, 0) + self.render.setLight(dlnp) + self.render.setLight(alnp) + + # Put lighting on the main scene + plight = PointLight('plight') + plnp = self.render.attachNewNode(plight) + plnp.setPos(0, 0, 10) + self.render.setLight(plnp) + self.render.setLight(alnp) + + self.loadRocketFonts() + + self.loadingTask = None + + #self.startModelLoadingAsync() + self.startModelLoading() + + self.inputHandler = RocketInputHandler() + self.mouseWatcher.attachNewNode(self.inputHandler) + + self.openLoadingDialog() + + def loadRocketFonts(self): + """ Load fonts referenced from e.g. 'font-family' RCSS directives. + + Note: the name of the font as used in 'font-family' + is not always the same as the filename; + open the font in your OS to see its display name. + """ + rocket.LoadFontFace("modenine.ttf") + + + def startModelLoading(self): + self.monitorNP = None + self.keyboardNP = None + self.loadingError = False + + self.taskMgr.doMethodLater(1, self.loadModels, 'loadModels') + + def loadModels(self, task): + self.monitorNP = self.loader.loadModel("monitor") + self.keyboardNP = self.loader.loadModel("takeyga_kb") + + def startModelLoadingAsync(self): + """ + NOTE: this seems to invoke a few bugs (crashes, sporadic model + reading errors, etc) so is disabled for now... + """ + self.monitorNP = None + self.keyboardNP = None + self.loadingError = False + + # force the "loading" to take some time after the first run... + options = LoaderOptions() + options.setFlags(options.getFlags() | LoaderOptions.LFNoCache) + + def gotMonitorModel(model): + if not model: + self.loadingError = True + self.monitorNP = model + + self.loader.loadModel("monitor", loaderOptions=options, callback=gotMonitorModel) + + def gotKeyboardModel(model): + if not model: + self.loadingError = True + self.keyboardNP = model + + self.loader.loadModel("takeyga_kb", loaderOptions=options, callback=gotKeyboardModel) + + def openLoadingDialog(self): + self.userConfirmed = False + + self.windowRocketRegion = RocketRegion.make('pandaRocket', self.win) + self.windowRocketRegion.setActive(1) + + self.windowRocketRegion.setInputHandler(self.inputHandler) + + self.windowContext = self.windowRocketRegion.getContext() + + self.loadingDocument = self.windowContext.LoadDocument("loading.rml") + if not self.loadingDocument: + raise AssertionError("did not find loading.rml") + + self.loadingDots = 0 + el = self.loadingDocument.GetElementById('loadingLabel') + self.loadingText = el.first_child + self.stopLoadingTime = globalClock.getFrameTime() + 3 + self.loadingTask = self.taskMgr.add(self.cycleLoading, 'doc changer') + + + # note: you may encounter errors like 'KeyError: 'document'" + # when invoking events using methods from your own scripts with this + # obvious code: + # + # self.loadingDocument.AddEventListener('aboutToClose', + # self.onLoadingDialogDismissed, True) + # + # A workaround is to define callback methods in standalone Python + # files with event, self, and document defined to None. + # + # see https://www.panda3d.org/forums/viewtopic.php?f=4&t=16412 + # + + # Or, use this indirection technique to work around the problem, + # by publishing the app into the context, then accessing it through + # the document's context... + + self.windowContext.app = self + self.loadingDocument.AddEventListener('aboutToClose', + 'document.context.app.handleAboutToClose()', True) + + self.loadingDocument.Show() + + def handleAboutToClose(self): + self.userConfirmed = True + if self.monitorNP and self.keyboardNP: + self.onLoadingDialogDismissed() + + def attachCustomRocketEvent(self, document, rocketEventName, pandaHandler, once=False): + # handle custom event + + # note: you may encounter errors like 'KeyError: 'document'" + # when invoking events using methods from your own scripts with this + # obvious code: + # + # self.loadingDocument.AddEventListener('aboutToClose', + # self.onLoadingDialogDismissed, True) + # + # see https://www.panda3d.org/forums/viewtopic.php?f=4&t=16412 + + + # this technique converts Rocket events to Panda3D events + + pandaEvent = 'panda.' + rocketEventName + + document.AddEventListener( + rocketEventName, + "messenger.send('" + pandaEvent + "', [event])") + + if once: + self.acceptOnce(pandaEvent, pandaHandler) + else: + self.accept(pandaEvent, pandaHandler) + + + def cycleLoading(self, task): + """ + Update the "loading" text in the initial window until + the user presses Space, Enter, or Escape or clicks (see loading.rxml) + or sufficient time has elapsed (self.stopLoadingTime). + """ + text = self.loadingText + + now = globalClock.getFrameTime() + if self.monitorNP and self.keyboardNP: + text.text = "Ready" + if now > self.stopLoadingTime or self.userConfirmed: + self.onLoadingDialogDismissed() + return task.done + elif self.loadingError: + text.text = "Assets not found" + else: + count = 5 + intv = int(now * 4) % count # @UndefinedVariable + text.text = "Loading" + ("." * (1+intv)) + (" " * (2 - intv)) + + return task.cont + + def onLoadingDialogDismissed(self): + """ Once a models are loaded, stop 'loading' and proceed to 'start' """ + if self.loadingDocument: + if self.loadingTask: + self.taskMgr.remove(self.loadingTask) + self.loadingTask = None + + self.showStarting() + + def fadeOut(self, element, time): + """ Example updating RCSS attributes from code + by modifying the 'color' RCSS attribute to slowly + change from solid to transparent. + + element: the Rocket element whose style to modify + time: time in seconds for fadeout + """ + + # get the current color from RCSS effective style + color = element.style.color + # convert to RGBA form + prefix = color[:color.rindex(',')+1].replace('rgb(', 'rgba(') + + def updateAlpha(t): + # another way of setting style on a specific element + attr = 'color: ' + prefix + str(int(t)) +');' + element.SetAttribute('style', attr) + + alphaInterval = LerpFunc(updateAlpha, + duration=time, + fromData=255, + toData=0, + blendType='easeIn') + + return alphaInterval + + def showStarting(self): + """ Models are loaded, so update the dialog, + fade out, then transition to the console. """ + self.loadingText.text = 'Starting...' + + alphaInterval = self.fadeOut(self.loadingText, 0.5) + alphaInterval.setDoneEvent('fadeOutFinished') + + def fadeOutFinished(): + if self.loadingDocument: + self.loadingDocument.Close() + self.loadingDocument = None + self.createConsole() + + self.accept('fadeOutFinished', fadeOutFinished) + + alphaInterval.start() + + def createConsole(self): + """ Create the in-world console, which displays + a RocketRegion in a GraphicsBuffer, which appears + in a Texture on the monitor model. """ + + self.monitorNP.reparentTo(self.render) + self.monitorNP.setScale(1.5) + + self.keyboardNP.reparentTo(self.render) + self.keyboardNP.setHpr(-90, 0, 15) + self.keyboardNP.setScale(20) + + self.placeItems() + + self.setupRocketConsole() + + # re-enable mouse + mat=Mat4(self.camera.getMat()) + mat.invertInPlace() + self.mouseInterfaceNode.setMat(mat) + self.enableMouse() + + def placeItems(self): + self.camera.setPos(0, -20, 0) + self.camera.setHpr(0, 0, 0) + self.monitorNP.setPos(0, 0, 1) + self.keyboardNP.setPos(0, -5, -2.5) + + + def setupRocketConsole(self): + """ + Place a new rocket window onto a texture + bound to the front of the monitor. + """ + self.win.setClearColor(Vec4(0.5, 0.5, 0.8, 1)) + + faceplate = self.monitorNP.find("**/Faceplate") + assert faceplate + + mybuffer = self.win.makeTextureBuffer("Console Buffer", 1024, 512) + tex = mybuffer.getTexture() + tex.setMagfilter(Texture.FTLinear) + tex.setMinfilter(Texture.FTLinear) + + faceplate.setTexture(tex, 1) + + self.rocketConsole = RocketRegion.make('console', mybuffer) + self.rocketConsole.setInputHandler(self.inputHandler) + + self.consoleContext = self.rocketConsole.getContext() + self.console = console.Console(self, self.consoleContext, 40, 13, self.handleCommand) + + self.console.addLine("Panda DOS") + self.console.addLine("type 'help'") + self.console.addLine("") + + self.console.allowEditing(True) + + def handleCommand(self, command): + if command is None: + # hack for Ctrl-Break + self.spewInProgress = False + self.console.addLine("*** break ***") + self.console.allowEditing(True) + return + + command = command.strip() + if not command: + return + + tokens = [x.strip() for x in command.split(' ')] + command = tokens[0].lower() + + if command == 'help': + self.console.addLines([ + "Sorry, this is utter fakery.", + "You won't get much more", + "out of this simulation unless", + "you program it yourself. :)" + ]) + elif command == 'dir': + self.console.addLines([ + "Directory of C:\\:", + "HELP COM 72 05-06-2015 14:07", + "DIR COM 121 05-06-2015 14:11", + "SPEW COM 666 05-06-2015 15:02", + " 2 Files(s) 859 Bytes.", + " 0 Dirs(s) 7333 Bytes free.", + ""]) + elif command == 'cls': + self.console.cls() + elif command == 'echo': + self.console.addLine(' '.join(tokens[1:])) + elif command == 'ver': + self.console.addLine('Panda DOS v0.01 in Panda3D ' + PandaSystem.getVersionString()) + elif command == 'spew': + self.startSpew() + elif command == 'exit': + self.console.setPrompt("System is shutting down NOW!") + self.terminateMonitor() + else: + self.console.addLine("command not found") + + def startSpew(self): + self.console.allowEditing(False) + self.console.addLine("LINE NOISE 1.0") + self.console.addLine("") + + self.spewInProgress = True + + # note: spewage always occurs in 'doMethodLater'; + # time.sleep() would be pointless since the whole + # UI would be frozen during the wait. + self.queueSpew(2) + + def queueSpew(self, delay=0.1): + self.taskMgr.doMethodLater(delay, self.spew, 'spew') + + def spew(self, task): + # generate random spewage, just like on TV! + if not self.spewInProgress: + return + + def randchr(): + return chr(int(random.random() < 0.25 and 32 or random.randint(32, 127))) + + line = ''.join([randchr() for _ in range(40) ]) + + self.console.addLine(line) + self.queueSpew() + + def terminateMonitor(self): + alphaInterval = self.fadeOut(self.console.getTextContainer(), 2) + + alphaInterval.setDoneEvent('fadeOutFinished') + + def fadeOutFinished(): + sys.exit(0) + + self.accept('fadeOutFinished', fadeOutFinished) + + alphaInterval.start() + +app = MyApp() +app.run() diff --git a/samples/shadows/advanced.py b/samples/shadows/advanced.py index 988e78df32..e11ed2645a 100755 --- a/samples/shadows/advanced.py +++ b/samples/shadows/advanced.py @@ -103,7 +103,7 @@ class World(DirectObject): self.pandaModel = Actor.Actor('panda-model', {'walk': 'panda-walk4'}) self.pandaModel.reparentTo(self.pandaAxis) self.pandaModel.setPos(9, 0, 0) - self.pandaModel.setShaderInput("scale", 0.01, 0.01, 0.01, 1.0) + self.pandaModel.setShaderInput("scale", (0.01, 0.01, 0.01, 1.0)) self.pandaWalk = self.pandaModel.actorInterval('walk', playRate=1.8) self.pandaWalk.loop() self.pandaMovement = self.pandaAxis.hprInterval( @@ -113,7 +113,7 @@ class World(DirectObject): self.teapot = loader.loadModel('teapot') self.teapot.reparentTo(render) self.teapot.setPos(0, -20, 10) - self.teapot.setShaderInput("texDisable", 1, 1, 1, 1) + self.teapot.setShaderInput("texDisable", (1, 1, 1, 1)) self.teapotMovement = self.teapot.hprInterval(50, LPoint3(0, 360, 360)) self.teapotMovement.loop() @@ -145,9 +145,9 @@ class World(DirectObject): # setting up shader render.setShaderInput('light', self.LCam) render.setShaderInput('Ldepthmap', Ldepthmap) - render.setShaderInput('ambient', self.ambient, 0, 0, 1.0) - render.setShaderInput('texDisable', 0, 0, 0, 0) - render.setShaderInput('scale', 1, 1, 1, 1) + render.setShaderInput('ambient', (self.ambient, 0, 0, 1.0)) + render.setShaderInput('texDisable', (0, 0, 0, 0)) + render.setShaderInput('scale', (1, 1, 1, 1)) # Put a shader on the Light camera. lci = NodePath(PandaNode("Light Camera Initializer")) diff --git a/samples/shadows/basic.py b/samples/shadows/basic.py index 843b9b9b27..722aeb0643 100755 --- a/samples/shadows/basic.py +++ b/samples/shadows/basic.py @@ -80,7 +80,6 @@ class World(DirectObject): self.teapot = loader.loadModel('teapot') self.teapot.reparentTo(render) self.teapot.setPos(0, -20, 10) - self.teapot.setShaderInput("texDisable", 1, 1, 1, 1) self.teapotMovement = self.teapot.hprInterval(50, LPoint3(0, 360, 360)) self.teapotMovement.loop() diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000000..a2b7a81fa5 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,24 @@ +[metadata] +name = Panda3D +version = 1.10.0 +url = https://www.panda3d.org/ +description = Panda3D is a framework for 3D rendering and game development for Python and C++ programs. +license = Modified BSD License +license_file = LICENSE +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + Intended Audience :: End Users/Desktop + License :: OSI Approved :: BSD License + Operating System :: OS Independent + Programming Language :: C++ + Programming Language :: Python + Topic :: Games/Entertainment + Topic :: Multimedia + Topic :: Multimedia :: Graphics + Topic :: Multimedia :: Graphics :: 3D Rendering +author = Panda3D Team +author_email = etc-panda3d@lists.andrew.cmu.edu + +[tool:pytest] +testpaths = tests diff --git a/tests/bullet/test_bullet_bam.py b/tests/bullet/test_bullet_bam.py new file mode 100644 index 0000000000..c6b9d2c282 --- /dev/null +++ b/tests/bullet/test_bullet_bam.py @@ -0,0 +1,133 @@ +import pytest + +# Skip these tests if we can't import bullet. +bullet = pytest.importorskip("panda3d.bullet") +from panda3d import core + + +def reconstruct(object): + # Create a temporary buffer, which we first write the object into, and + # subsequently read it from again. + buffer = core.DatagramBuffer() + + writer = core.BamWriter(buffer) + writer.init() + writer.write_object(object) + + reader = core.BamReader(buffer) + reader.init() + object = reader.read_object() + reader.resolve() + return object + + +def test_box_shape(): + shape = bullet.BulletBoxShape((1, 2, 3)) + shape.margin = 0.5 + + shape2 = reconstruct(shape) + + assert type(shape) is type(shape2) + assert shape.margin == shape2.margin + assert shape.name == shape2.name + assert shape.half_extents_without_margin == shape2.half_extents_without_margin + assert shape.half_extents_with_margin == shape2.half_extents_with_margin + + +def test_capsule_shape(): + shape = bullet.BulletCapsuleShape(1.4, 3.5, bullet.Y_up) + shape.margin = 0.5 + + shape2 = reconstruct(shape) + + assert type(shape) is type(shape2) + assert shape.margin == shape2.margin + assert shape.name == shape2.name + assert shape.radius == shape2.radius + assert shape.height == shape2.height + + +def test_cone_shape(): + shape = bullet.BulletConeShape(1.4, 3.5, bullet.Y_up) + shape.margin = 0.5 + + shape2 = reconstruct(shape) + + assert type(shape) is type(shape2) + assert shape.margin == shape2.margin + assert shape.name == shape2.name + assert shape.radius == shape2.radius + assert shape.height == shape2.height + + +def test_cylinder_shape(): + shape = bullet.BulletCylinderShape(1.4, 3.5, bullet.Y_up) + shape.margin = 0.5 + + shape2 = reconstruct(shape) + + assert type(shape) is type(shape2) + assert shape.margin == shape2.margin + assert shape.name == shape2.name + assert shape.radius == shape2.radius + assert shape.half_extents_without_margin == shape2.half_extents_without_margin + assert shape.half_extents_with_margin == shape2.half_extents_with_margin + + +def test_minkowski_sum_shape(): + box = bullet.BulletBoxShape((1, 2, 3)) + sphere = bullet.BulletSphereShape(2.0) + + shape = bullet.BulletMinkowskiSumShape(box, sphere) + shape.transform_a = core.TransformState.make_pos((8, 7, 3)) + shape.transform_b = core.TransformState.make_hpr((45, -10, 110)) + shape.margin = 0.5 + + shape2 = reconstruct(shape) + + assert type(shape) is type(shape2) + assert shape.margin == shape2.margin + assert shape.name == shape2.name + assert shape.transform_a.compare_to(shape2.transform_a, True) == 0 + assert shape.transform_b.compare_to(shape2.transform_b, True) == 0 + assert type(shape.shape_a) == type(shape2.shape_a) + assert type(shape.shape_b) == type(shape2.shape_b) + + +def test_multi_sphere_shape(): + shape = bullet.BulletMultiSphereShape([(1, 2, 3), (4, 5, 6)], [1.5, 2.5]) + shape.margin = 0.5 + + shape2 = reconstruct(shape) + + assert type(shape) is type(shape2) + assert shape.margin == shape2.margin + assert shape.name == shape2.name + assert shape.sphere_count == shape2.sphere_count + assert tuple(shape.sphere_pos) == tuple(shape2.sphere_pos) + assert tuple(shape.sphere_radius) == tuple(shape2.sphere_radius) + + +def test_plane_shape(): + shape = bullet.BulletPlaneShape((1.2, 0.2, 0.5), 2.5) + shape.margin = 0.5 + + shape2 = reconstruct(shape) + + assert type(shape) is type(shape2) + assert shape.margin == shape2.margin + assert shape.name == shape2.name + assert shape.plane_normal == shape2.plane_normal + assert shape.plane_constant == shape2.plane_constant + + +def test_sphere_shape(): + shape = bullet.BulletSphereShape(1.4) + shape.margin = 0.5 + + shape2 = reconstruct(shape) + + assert type(shape) is type(shape2) + assert shape.margin == shape2.margin + assert shape.name == shape2.name + assert shape.radius == shape2.radius diff --git a/tests/display/conftest.py b/tests/display/conftest.py new file mode 100644 index 0000000000..821b06af80 --- /dev/null +++ b/tests/display/conftest.py @@ -0,0 +1,42 @@ +import pytest + +@pytest.fixture +def graphics_pipe(scope='session'): + from panda3d.core import GraphicsPipeSelection + + pipe = GraphicsPipeSelection.get_global_ptr().make_default_pipe() + + if not pipe.is_valid(): + pytest.xfail("GraphicsPipe is invalid") + + yield pipe + +@pytest.fixture +def graphics_engine(scope='session'): + from panda3d.core import GraphicsEngine + + engine = GraphicsEngine.get_global_ptr() + yield engine + +@pytest.fixture +def window(graphics_pipe, graphics_engine): + from panda3d.core import GraphicsPipe, FrameBufferProperties, WindowProperties + + fbprops = FrameBufferProperties.get_default() + winprops = WindowProperties.get_default() + + win = graphics_engine.make_output( + graphics_pipe, + 'window', + 0, + fbprops, + winprops, + GraphicsPipe.BF_require_window + ) + graphics_engine.open_windows() + + assert win is not None + yield win + + if win is not None: + graphics_engine.remove_window(win) diff --git a/tests/display/test_fbprops.py b/tests/display/test_fbprops.py new file mode 100644 index 0000000000..a4620efdfc --- /dev/null +++ b/tests/display/test_fbprops.py @@ -0,0 +1,97 @@ +from panda3d.core import FrameBufferProperties + + +def test_fbquality_depth(): + # We check common framebuffer depth configurations to make sure they + # are rated predictably with respect to each other when requesting 1 bit. + # In particular, we make sure that we don't get a 16-bit depth buffer if + # the driver only gives extra depth in combination with a stencil buffer. + req = FrameBufferProperties() + req.depth_bits = 1 + + #NB. we need to set rgb_color=True when testing the quality of framebuffer + # properties, lest it return a quality of 0. + fb_d16s8 = FrameBufferProperties() + fb_d16s8.rgb_color = True + fb_d16s8.depth_bits = 16 + fb_d16s8.stencil_bits = 8 + + fb_d24s8 = FrameBufferProperties() + fb_d24s8.rgb_color = True + fb_d24s8.depth_bits = 24 + fb_d24s8.stencil_bits = 8 + + fb_d32 = FrameBufferProperties() + fb_d32.rgb_color = True + fb_d32.depth_bits = 32 + + fb_d32s8 = FrameBufferProperties() + fb_d32s8.rgb_color = True + fb_d32s8.depth_bits = 32 + fb_d32s8.stencil_bits = 8 + + # 16-bit depth is terrible for most applications and should not be chosen. + assert fb_d16s8.get_quality(req) < fb_d24s8.get_quality(req) + assert fb_d16s8.get_quality(req) < fb_d32.get_quality(req) + assert fb_d16s8.get_quality(req) < fb_d32s8.get_quality(req) + + # Getting extra depth should be better than getting an unwanted bitplane. + assert fb_d32.get_quality(req) > fb_d16s8.get_quality(req) + assert fb_d32.get_quality(req) > fb_d24s8.get_quality(req) + + # If we're getting stencil anyway, we'll prefer to maximize our depth. + assert fb_d32s8.get_quality(req) > fb_d24s8.get_quality(req) + + # However, unnecessary stencil bits are still a waste. + assert fb_d32s8.get_quality(req) < fb_d32.get_quality(req) + + +def test_fbquality_rgba64(): + # Make sure that we don't get a 64-bit configuration if we request + # an unspecific number of color bits. See: + # https://www.panda3d.org/forums/viewtopic.php?t=20192 + # This issue occurs if we are requesting 1 bit, not if we are requesting + # a specific amount. There are several ways to do that, so we want to + # assert that none of them will yield a 64-bit color buffer. + req_color0 = FrameBufferProperties() + req_color0.color_bits = 0 + + req_color1 = FrameBufferProperties() + req_color1.color_bits = 1 + + req_color0_alpha0 = FrameBufferProperties() + req_color0_alpha0.color_bits = 0 + req_color0_alpha0.alpha_bits = 0 + + req_color1_alpha1 = FrameBufferProperties() + req_color1_alpha1.color_bits = 1 + req_color1_alpha1.alpha_bits = 1 + + req_rgb0 = FrameBufferProperties() + req_rgb0.set_rgba_bits(0, 0, 0, 0) + + req_rgb1 = FrameBufferProperties() + req_rgb1.set_rgba_bits(1, 1, 1, 0) + + req_rgb0_alpha0 = FrameBufferProperties() + req_rgb0_alpha0.set_rgba_bits(0, 0, 0, 0) + + req_rgb1_alpha1 = FrameBufferProperties() + req_rgb1_alpha1.set_rgba_bits(1, 1, 1, 1) + + fb_rgba8 = FrameBufferProperties() + fb_rgba8.rgb_color = True + fb_rgba8.set_rgba_bits(8, 8, 8, 8) + + fb_rgba16 = FrameBufferProperties() + fb_rgba16.rgb_color = True + fb_rgba16.set_rgba_bits(16, 16, 16, 16) + + assert fb_rgba8.get_quality(req_color0) > fb_rgba16.get_quality(req_color0) + assert fb_rgba8.get_quality(req_color1) > fb_rgba16.get_quality(req_color1) + assert fb_rgba8.get_quality(req_color0_alpha0) > fb_rgba16.get_quality(req_color0_alpha0) + assert fb_rgba8.get_quality(req_color1_alpha1) > fb_rgba16.get_quality(req_color1_alpha1) + assert fb_rgba8.get_quality(req_rgb0) > fb_rgba16.get_quality(req_rgb0) + assert fb_rgba8.get_quality(req_rgb1) > fb_rgba16.get_quality(req_rgb1) + assert fb_rgba8.get_quality(req_rgb0_alpha0) > fb_rgba16.get_quality(req_rgb0_alpha0) + assert fb_rgba8.get_quality(req_rgb1_alpha1) > fb_rgba16.get_quality(req_rgb1_alpha1) diff --git a/tests/display/test_window.py b/tests/display/test_window.py new file mode 100644 index 0000000000..d193fe92dd --- /dev/null +++ b/tests/display/test_window.py @@ -0,0 +1,15 @@ +def test_window_basic(window): + from panda3d.core import WindowProperties + assert window is not None + + current_props = window.get_properties() + default_props = WindowProperties.get_default() + + # Opening the window changes these from the defaults + default_props.set_size(current_props.get_size()) + default_props.set_origin(current_props.get_origin()) + default_props.set_minimized(False) + default_props.set_foreground(True) + + # The rest should be the same + assert current_props == default_props diff --git a/tests/event/test_futures.py b/tests/event/test_futures.py new file mode 100644 index 0000000000..c353834257 --- /dev/null +++ b/tests/event/test_futures.py @@ -0,0 +1,212 @@ +from panda3d import core +import pytest +import threading +import time +import sys + +if sys.version_info >= (3,): + from concurrent.futures._base import TimeoutError, CancelledError +else: + TimeoutError = Exception + CancelledError = Exception + + +def test_future_cancelled(): + fut = core.AsyncFuture() + + assert not fut.done() + assert not fut.cancelled() + fut.cancel() + assert fut.done() + assert fut.cancelled() + + with pytest.raises(CancelledError): + fut.result() + + # Works more than once + with pytest.raises(CancelledError): + fut.result() + + +def test_future_timeout(): + fut = core.AsyncFuture() + + with pytest.raises(TimeoutError): + fut.result(0.001) + + # Works more than once + with pytest.raises(TimeoutError): + fut.result(0.001) + + +def test_future_wait(): + fut = core.AsyncFuture() + + # Launch a thread to set the result value. + def thread_main(): + time.sleep(0.001) + fut.set_result(None) + + thread = threading.Thread(target=thread_main) + thread.start() + + # Make sure it didn't sneakily already run the thread + assert not fut.done() + + assert fut.result() is None + + assert fut.done() + assert not fut.cancelled() + assert fut.result() is None + + +def test_future_wait_cancel(): + fut = core.AsyncFuture() + + # Launch a thread to cancel the future. + def thread_main(): + time.sleep(0.001) + fut.cancel() + + thread = threading.Thread(target=thread_main) + thread.start() + + # Make sure it didn't sneakily already run the thread + assert not fut.done() + + with pytest.raises(CancelledError): + fut.result() + + assert fut.done() + assert fut.cancelled() + with pytest.raises(CancelledError): + fut.result() + + +def test_task_cancel(): + task_mgr = core.AsyncTaskManager.get_global_ptr() + task = core.PythonTask(lambda task: task.done) + task_mgr.add(task) + + assert not task.done() + task_mgr.remove(task) + assert task.done() + assert task.cancelled() + + with pytest.raises(CancelledError): + task.result() + + +def test_task_cancel_during_run(): + task_mgr = core.AsyncTaskManager.get_global_ptr() + task_chain = task_mgr.make_task_chain("test_task_cancel_during_run") + + def task_main(task): + task.remove() + + # It won't yet be marked done until after it returns. + assert not task.done() + return task.done + + task = core.PythonTask(task_main) + task.set_task_chain(task_chain.name) + task_mgr.add(task) + task_chain.wait_for_tasks() + + assert task.done() + assert task.cancelled() + with pytest.raises(CancelledError): + task.result() + + +def test_task_result(): + task_mgr = core.AsyncTaskManager.get_global_ptr() + task_chain = task_mgr.make_task_chain("test_task_result") + + def task_main(task): + task.set_result(42) + + # It won't yet be marked done until after it returns. + assert not task.done() + return core.PythonTask.done + + task = core.PythonTask(task_main) + task.set_task_chain(task_chain.name) + task_mgr.add(task) + task_chain.wait_for_tasks() + + assert task.done() + assert not task.cancelled() + assert task.result() == 42 + + +def test_coro_exception(): + task_mgr = core.AsyncTaskManager.get_global_ptr() + task_chain = task_mgr.make_task_chain("test_coro_exception") + + def coro_main(): + raise RuntimeError + yield None + + task = core.PythonTask(coro_main()) + task.set_task_chain(task_chain.name) + task_mgr.add(task) + task_chain.wait_for_tasks() + + assert task.done() + assert not task.cancelled() + with pytest.raises(RuntimeError): + task.result() + + +def test_event_future(): + queue = core.EventQueue() + handler = core.EventHandler(queue) + + fut = handler.get_future("test") + + # If we ask again, we should get the same one. + assert handler.get_future("test") == fut + + event = core.Event("test") + handler.dispatch_event(event) + + assert fut.done() + assert not fut.cancelled() + assert fut.result() == event + + +def test_event_future_cancel(): + # This is a very strange thing to do, but it's possible, so let's make + # sure it gives defined behavior. + queue = core.EventQueue() + handler = core.EventHandler(queue) + + fut = handler.get_future("test") + fut.cancel() + + assert fut.done() + assert fut.cancelled() + + event = core.Event("test") + handler.dispatch_event(event) + + assert fut.done() + assert fut.cancelled() + + +def test_event_future_cancel2(): + queue = core.EventQueue() + handler = core.EventHandler(queue) + + # Make sure we get a new future if we cancelled the first one. + fut = handler.get_future("test") + fut.cancel() + fut2 = handler.get_future("test") + + assert fut != fut2 + assert fut.done() + assert fut.cancelled() + assert not fut2.done() + assert not fut2.cancelled() + diff --git a/tests/interrogate/test_property.py b/tests/interrogate/test_property.py new file mode 100755 index 0000000000..e39f45f9dc --- /dev/null +++ b/tests/interrogate/test_property.py @@ -0,0 +1,625 @@ +import sys +import pytest +from panda3d import core +from contextlib import contextmanager +import collections + + +@contextmanager +def constant_refcount(var): + """ with block that checks that the Python refcount remains the same. """ + rc = sys.getrefcount(var) + yield + assert sys.getrefcount(var) == rc + + +def test_property(): + # This is a property defined by MAKE_PROPERTY. + np = core.PandaNode("") + + # Getter + transform = np.get_transform() + assert transform == np.transform + + # Setter + new_transform = transform.set_pos((1, 0, 0)) + np.transform = new_transform + assert np.transform == new_transform + + # Invalid assignments + with pytest.raises(TypeError): + np.transform = None + with pytest.raises(TypeError): + np.transform = "nonsense" + with pytest.raises(TypeError): + del np.transform + + +def test_property2(): + # This is a property defined by MAKE_PROPERTY2, that can be None. + mat = core.Material() + + mat.ambient = (1, 0, 0, 1) + assert mat.ambient == (1, 0, 0, 1) + + mat.ambient = None + assert mat.ambient is None + + with pytest.raises(TypeError): + mat.ambient = "nonsense" + with pytest.raises(TypeError): + del mat.ambient + + +# The next tests are for MAKE_SEQ_PROPERTY. +@pytest.fixture +def seq_property(*items): + """ Returns a sequence property initialized with the given items. """ + + # It doesn't matter which property we use; I just happened to pick + # CollisionNode.solids. + cn = core.CollisionNode("") + append = cn.add_solid + for item in items: + append(item) + assert len(cn.solids) == len(items) + return cn.solids + +# Arbitrary items we can use as items for the above seq property. +item_a = core.CollisionSphere((0, 0, 0), 1) +item_b = core.CollisionSphere((0, 0, 0), 2) +item_c = core.CollisionSphere((0, 0, 0), 3) + + +def test_seq_property_abc(): + prop = seq_property() + assert isinstance(prop, collections.Container) + assert isinstance(prop, collections.Sized) + assert isinstance(prop, collections.Iterable) + assert isinstance(prop, collections.MutableSequence) + assert isinstance(prop, collections.Sequence) + + +def test_seq_property_empty(): + prop = seq_property() + assert not prop + assert len(prop) == 0 + + with pytest.raises(IndexError): + prop[0] + with pytest.raises(IndexError): + prop[-1] + + +def test_seq_property_iter(): + prop = seq_property(item_a, item_b, item_b) + assert prop + assert len(prop) == 3 + + assert tuple(prop) == (item_a, item_b, item_b) + assert item_a in prop + assert item_c not in prop + assert None not in prop + + +def test_seq_property_reversed(): + prop = seq_property(item_a, item_b, item_b) + assert tuple(reversed(prop)) == tuple(reversed(tuple(prop))) + + +def test_seq_property_getitem(): + prop = seq_property(item_a, item_b, item_b) + + assert prop[0] == item_a + assert prop[1] == item_b + assert prop[2] == item_b + + # Reverse index + assert prop[-1] == item_b + assert prop[-2] == item_b + assert prop[-3] == item_a + + # Long index + if sys.version_info[0] < 3: + assert prop[long(1)] == item_b + assert prop[long(-1)] == item_b + + # Out of bounds access + with pytest.raises(IndexError): + prop[-4] + with pytest.raises(IndexError): + prop[3] + with pytest.raises(IndexError): + prop[2**63] + + # Invalid index + with pytest.raises(TypeError): + prop[None] + with pytest.raises(TypeError): + prop[item_a] + with pytest.raises(TypeError): + prop["nonsense"] + + # Reference count check + i = 1 + with constant_refcount(i): + prop[i] + + # Make sure it preserves refcount of invalid indices + i = "nonsense" + with constant_refcount(i): + try: + prop[i] + except TypeError: + pass + + +def test_seq_property_setitem(): + prop = seq_property(item_c, item_c, item_c) + + prop[0] = item_a + prop[1] = item_b + assert tuple(prop) == (item_a, item_b, item_c) + + # Refcount of key and value stays the same? + i = 0 + with constant_refcount(i): + with constant_refcount(item_a): + prop[0] = item_a + + # Reverse index + prop[-1] = item_a + prop[-2] = item_b + prop[-3] = item_c + assert tuple(prop) == (item_c, item_b, item_a) + + # Long index + if sys.version_info[0] < 3: + prop[long(1)] = item_b + assert prop[1] == item_b + prop[long(-1)] = item_b + assert prop[-1] == item_b + + # Out of bounds access + with pytest.raises(IndexError): + prop[-4] = item_c + with pytest.raises(IndexError): + prop[3] = item_c + with pytest.raises(IndexError): + prop[2**63] = item_c + + # Invalid index + with pytest.raises(TypeError): + prop[None] = item_c + with pytest.raises(TypeError): + prop[item_a] = item_c + with pytest.raises(TypeError): + prop["nonsense"] = item_c + + # Invalid type + with pytest.raises(TypeError): + prop[0] = None + with pytest.raises(TypeError): + prop[0] = "nonsense" + + +def test_seq_property_delitem(): + prop = seq_property(item_a, item_b, item_c) + + # Out of bounds + with pytest.raises(IndexError): + prop[3] + with pytest.raises(IndexError): + prop[-4] + + # Positive index + del prop[0] + assert tuple(prop) == (item_b, item_c) + + # Negative index + del prop[-2] + assert tuple(prop) == (item_c,) + + # Invalid index + with pytest.raises(TypeError): + del prop[None] + + +def test_seq_property_index(): + prop = seq_property(item_a, item_b, item_b) + + assert prop.index(item_a) == 0 + assert prop.index(item_b) == 1 + + with pytest.raises(ValueError): + prop.index(item_c) + with pytest.raises(ValueError): + prop.index(None) + with pytest.raises(ValueError): + prop.index("nonsense") + + # Refcount is properly decreased + with constant_refcount(item_b): + prop.index(item_b) + with constant_refcount(item_c): + try: + prop.index(item_c) + except ValueError: + pass + + nonsense = "nonsense" + with constant_refcount(nonsense): + try: + prop.index(nonsense) + except ValueError: + pass + + +def test_seq_property_count(): + prop = seq_property(item_a, item_b, item_b) + + prop.count(item_a) == 1 + prop.count(item_b) == 2 + prop.count(item_c) == 0 + prop.count(None) == 0 + prop.count(("nonsense", -3.5)) == 0 + + # Refcount does not leak + with constant_refcount(item_b): + prop.count(item_b) + + nonsense = "nonsense" + with constant_refcount(nonsense): + prop.count(nonsense) + + +def test_seq_property_clear(): + prop = seq_property(item_a, item_b, item_b) + prop.clear() + + assert not prop + assert len(prop) == 0 + assert tuple(prop) == () + + +def test_seq_property_pop(): + prop = seq_property(item_a, item_b, item_c, item_b) + + # Test out of bounds pop + with pytest.raises(IndexError): + prop.pop(4) + with pytest.raises(IndexError): + prop.pop(-5) + + assert prop.pop(1) == item_b + assert prop.pop(-1) == item_b + assert prop.pop() == item_c + assert prop.pop(0) == item_a + + # Wrong args + with pytest.raises(TypeError): + prop.pop(0, 0) + + # Pop on empty sequence + with pytest.raises(IndexError): + prop.pop() + with pytest.raises(IndexError): + prop.pop(0) + with pytest.raises(IndexError): + prop.pop(-1) + + +def test_seq_property_remove(): + prop = seq_property(item_a, item_b, item_c, item_b) + + with constant_refcount(item_b): + prop.remove(item_b) + + assert tuple(prop) == (item_a, item_c, item_b) + + prop.remove(item_b) + assert tuple(prop) == (item_a, item_c) + + with pytest.raises(ValueError): + prop.remove(item_b) + with pytest.raises(ValueError): + prop.remove(None) + with pytest.raises(ValueError): + prop.remove("nonsense") + + +def test_seq_property_append(): + prop = seq_property(item_a, item_b) + + with constant_refcount(item_c): + prop.append(item_c) + + assert tuple(prop) == (item_a, item_b, item_c) + + with pytest.raises(TypeError): + prop.append(None) + with pytest.raises(TypeError): + prop.append("nonsense") + + +def test_seq_property_insert(): + # Adding at the beginning + prop = seq_property(item_a, item_a, item_a) + with constant_refcount(item_b): + prop.insert(0, item_b) + + assert tuple(prop) == (item_b, item_a, item_a, item_a) + + # Adding in the middle + prop = seq_property(item_a, item_a, item_a) + with constant_refcount(item_b): + prop.insert(2, item_b) + + assert tuple(prop) == (item_a, item_a, item_b, item_a) + + # Adding at the end + prop = seq_property(item_a, item_a, item_a) + with constant_refcount(item_b): + prop.insert(len(prop), item_b) + + assert tuple(prop) == (item_a, item_a, item_a, item_b) + + # Adding with negative index + prop = seq_property(item_a, item_a, item_a) + with constant_refcount(item_b): + prop.insert(-2, item_b) + + assert tuple(prop) == (item_a, item_b, item_a, item_a) + + # Adding at the end with overflowing index + prop = seq_property(item_a, item_a, item_a) + with constant_refcount(item_b): + prop.insert(2345, item_b) + + assert tuple(prop) == (item_a, item_a, item_a, item_b) + + # Adding at the beginning with negative overflowing index + prop = seq_property(item_a, item_a, item_a) + with constant_refcount(item_b): + prop.insert(-2345, item_b) + + assert tuple(prop) == (item_b, item_a, item_a, item_a) + + +def test_seq_property_extend(): + prop = seq_property(item_a) + + with constant_refcount(item_b): + prop.extend((item_b, item_c)) + + assert tuple(prop) == (item_a, item_b, item_c) + + with pytest.raises(TypeError): + prop.extend(None) + with pytest.raises(TypeError): + prop.extend("nonsense") + with pytest.raises(TypeError): + prop.extend(item_a) + with pytest.raises(TypeError): + prop.extend(item_a, item_b) + with pytest.raises(TypeError): + prop.extend() + with pytest.raises(TypeError): + prop.extend((item_a, None)) + with pytest.raises(TypeError): + prop.extend(["nonsense"]) + + +# The next tests are for MAKE_MAP_PROPERTY. +@pytest.fixture +def map_property(**items): + """ Returns a mapping property initialized with the given values. """ + + # It doesn't matter which property we use; I just happened to pick + # NodePath.tags. + np = core.NodePath("") + for k, v in items.items(): + np.set_tag(k, v) + return np.tags + + +def test_map_property_abc(): + prop = map_property() + assert isinstance(prop, collections.Container) + assert isinstance(prop, collections.Sized) + assert isinstance(prop, collections.Iterable) + assert isinstance(prop, collections.MutableMapping) + assert isinstance(prop, collections.Mapping) + + +def test_map_property_empty(): + prop = map_property() + assert not prop + assert len(prop) == 0 + + with pytest.raises(KeyError): + prop.popitem() + + with pytest.raises(KeyError): + prop['nonsense'] + + +def test_map_property_getitem(): + key = 'key' + value = 'value' + prop = map_property(**{key: value}) + + with constant_refcount(key): + with constant_refcount(value): + assert prop[key] == value + + with pytest.raises(KeyError): + prop['nonsense'] + with pytest.raises(TypeError): + prop[None] + + +def test_map_property_setitem(): + key = 'key' + value = 'value' + prop = map_property() + + # Setting new key + with constant_refcount(key): + with constant_refcount(value): + prop[key] = value + + assert prop[key] == value + + # Setting existing key + with constant_refcount(key): + with constant_refcount(value): + prop[key] = value + + assert prop[key] == value + + # Unknown key + with pytest.raises(TypeError): + prop[None] = value + + # Unknown value + with pytest.raises(TypeError): + prop[key] = None + + +def test_map_property_delitem(): + key = 'key' + value = 'value' + prop = map_property(**{key: value}) + + with constant_refcount(key): + with constant_refcount(value): + del prop[key] + + with pytest.raises(KeyError): + assert prop[key] + + # Nonexistent key + with pytest.raises(KeyError): + del prop['nonsense'] + + # Invalid type key + with pytest.raises(TypeError): + del prop[None] + + +def test_map_property_contains(): + prop = map_property(key='value') + + assert 'key' in prop + assert None not in prop + assert 'value' not in prop + + +def test_map_property_get(): + key = 'key' + value = 'value' + prop = map_property(**{key: value}) + + default = 'default' + with constant_refcount(key): + with constant_refcount(default): + assert prop.get(key) == value + + with constant_refcount(key): + with constant_refcount(default): + assert prop.get(key, default) == value + + assert prop.get('unknown') is None + + with constant_refcount(default): + assert prop.get('unknown', default) == default + + +def test_map_property_pop(): + key = 'key' + value = 'value' + prop = map_property(**{key: value}) + + assert prop.pop('nonsense', None) is None + assert prop.pop('nonsense', 'default') == 'default' + + assert prop.pop('key', 'default') == 'value' + assert 'key' not in prop + + +def test_map_property_popitem(): + key = 'key' + value = 'value' + prop = map_property(**{key: value}) + + assert prop.popitem() == (key, value) + + with pytest.raises(KeyError): + assert prop.popitem() + + +def test_map_property_clear(): + prop = map_property(key='value', key2='value2') + + prop.clear() + assert len(prop) == 0 + + +def test_map_property_setdefault(): + prop = map_property(key='value') + + # Don't change value of key that already exists + prop.setdefault('key', 'value2') + assert prop['key'] == 'value' + + # Change values of nonexistent key + prop.setdefault('key2', 'value2') + assert prop['key2'] == 'value2' + + # These should error because you can't set None on this property + with pytest.raises(TypeError): + prop.setdefault('key3', None) + with pytest.raises(TypeError): + prop.setdefault('key3') + + +def test_map_property_update(): + prop = map_property() + + # Empty update + prop.update() + + # Passing in dictionary + prop.update({'key': 'value'}) + + # Passing in keywords + prop.update(key2='value2') + + # Don't pass in both! + with pytest.raises(TypeError): + prop.update({}, k='v') + + assert prop['key'] == 'value' + assert prop['key2'] == 'value2' + + +def test_map_property_keys(): + prop = map_property(key='value', key2='value2') + + assert isinstance(prop.keys(), collections.MappingView) + assert frozenset(prop.keys()) == frozenset(('key', 'key2')) + + +def test_map_property_values(): + prop = map_property(key='value', key2='value2') + + assert isinstance(prop.values(), collections.ValuesView) + assert frozenset(prop.values()) == frozenset(('value', 'value2')) + + +def test_map_property_items(): + prop = map_property(key='value', key2='value2') + + assert isinstance(prop.items(), collections.MappingView) + assert frozenset(prop.items()) == frozenset((('key', 'value'), ('key2', 'value2'))) diff --git a/tests/pgraph/test_nodepath.py b/tests/pgraph/test_nodepath.py new file mode 100644 index 0000000000..a625533634 --- /dev/null +++ b/tests/pgraph/test_nodepath.py @@ -0,0 +1,81 @@ +def test_nodepath_empty(): + """Tests NodePath behavior for empty NodePaths.""" + from panda3d.core import NodePath + + empty = NodePath('np') + + assert empty.get_pos() == (0, 0, 0) + assert empty.get_hpr() == (0, 0, 0) + assert empty.get_scale() == (1, 1, 1) + +def test_nodepath_parent(): + """Tests NodePath.reparentTo().""" + from panda3d.core import NodePath + + np1 = NodePath('np') + np2 = NodePath('np') + + assert np1.parent is None + assert np2.parent is None + + np1.reparentTo(np2) + + assert np1.parent == np2 + assert np2.parent is None + +def test_nodepath_transform_changes(): + """Tests that NodePath applies transform changes to its managed node.""" + from panda3d.core import NodePath + + np = NodePath('np') + assert np.get_pos() == (0, 0, 0) + assert np.get_hpr() == (0, 0, 0) + assert np.get_scale() == (1, 1, 1) + + np.set_pos(1, 2, 3) + assert np.get_pos() == (1, 2, 3) + assert np.node().get_transform().get_pos() == (1, 2, 3) + +def test_nodepath_transform_composition(): + """Tests that NodePath composes transform states according to the path it holds.""" + from panda3d.core import PandaNode, NodePath, LPoint3, LVector3 + + # Create 3 PandaNodes, and give each some interesting transform state: + node1 = PandaNode('node1') + node2 = PandaNode('node2') + node3 = PandaNode('node3') + + node1.set_transform(node1.get_transform().set_pos(LPoint3(0, 0, 1)).set_hpr(LVector3(90, 0, -90))) + node2.set_transform(node2.get_transform().set_pos(LPoint3(0, 1, 0)).set_hpr(LVector3(180, 180, 0))) + node3.set_transform(node3.get_transform().set_pos(LPoint3(1, 0, 0)).set_hpr(LVector3(270, 0, 270))) + + # node3 is going to be attached under both node1 and node2 and we will + # hold a path both ways: + node1.add_child(node3) + node2.add_child(node3) + + assert len(node1.children) == 1 + assert len(node2.children) == 1 + assert len(node3.children) == 0 + assert len(node1.parents) == 0 + assert len(node2.parents) == 0 + assert len(node3.parents) == 2 + + # np1 is the path to node3 via node1: + np1 = NodePath(node1).children[0] + # np2 is the path to node3 via node2: + np2 = NodePath(node2).children[0] + + # Both should point to node3: + assert np1.node() == node3 + assert np2.node() == node3 + + # However if we ask for the net transform to node3, it should compose: + assert np1.get_transform(NodePath()) == node1.get_transform().compose(node3.get_transform()) + assert np2.get_transform(NodePath()) == node2.get_transform().compose(node3.get_transform()) + + # If we ask for np1 RELATIVE to np2, it should compose like so: + leg1 = node2.get_transform().compose(node3.get_transform()) + leg2 = node1.get_transform().compose(node3.get_transform()) + relative_transform = leg1.get_inverse().compose(leg2) + assert np1.get_transform(np2) == relative_transform