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 a6e1a267a5..fde01c38f0 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,25 @@ 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 this command fails, please make sure your version of pip is up-to-date. + +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 +50,11 @@ 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.3/panda3d-1.9.3-tools-win32.zip -https://www.panda3d.org/download/panda3d-1.9.3/panda3d-1.9.3-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 + +(It is also possible to build using MSVC 2015 and 2017, which requires a +different set of thirdparty libraries, but that is not described here.) After acquiring these dependencies, you may simply build Panda3D from the command prompt using the following command: @@ -64,7 +86,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 +115,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. +Alternatively, you can add the `--wheel` option, which will produce a .whl +file that can be installed into a Python installation using `pip`. + 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.3/panda3d-1.9.3-tools-mac.tar.gz). +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: @@ -114,13 +139,46 @@ 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/contrib/src/panda3dtoolsgui/build_exe.bat b/contrib/src/panda3dtoolsgui/build_exe.bat old mode 100755 new mode 100644 diff --git a/contrib/src/rplight/config_rplight.cxx b/contrib/src/rplight/config_rplight.cxx new file mode 100644 index 0000000000..563c4db641 --- /dev/null +++ b/contrib/src/rplight/config_rplight.cxx @@ -0,0 +1,51 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "config_rplight.h" + +#include "rpLight.h" +#include "rpPointLight.h" + +#include "dconfig.h" + +Configure(config_rplight); +NotifyCategoryDef(rplight, ""); + +ConfigureFn(config_rplight) { + init_librplight(); +} + +void +init_librplight() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + // RPLight::init_type(); + // RPPointLight::init_type(); +} diff --git a/contrib/src/rplight/config_rplight.h b/contrib/src/rplight/config_rplight.h new file mode 100644 index 0000000000..10cf63fc56 --- /dev/null +++ b/contrib/src/rplight/config_rplight.h @@ -0,0 +1,40 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef CONFIG_RPLIGHT_H +#define CONFIG_RPLIGHT_H + +#include "pandabase.h" +#include "notifyCategoryProxy.h" +#include "configVariableDouble.h" +#include "configVariableString.h" +#include "configVariableInt.h" + +NotifyCategoryDecl(rplight, EXPORT_CLASS, EXPORT_TEMPL); + +extern void init_librplight(); + +#endif // CONFIG_RPLIGHT_H diff --git a/contrib/src/rplight/gpuCommand.I b/contrib/src/rplight/gpuCommand.I new file mode 100644 index 0000000000..3531eed2ba --- /dev/null +++ b/contrib/src/rplight/gpuCommand.I @@ -0,0 +1,185 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "stdint.h" + +/** + * @brief Appends an integer to the GPUCommand. + * @details This adds an integer to the back of the GPUCommand. Depending on the + * setting in convert_int_to_float, this will either just convert the int to a + * float by casting it, or just do a bitwise copy. + * + * @param v The integer to append. + */ +inline void GPUCommand::push_int(int v) { + push_float(convert_int_to_float(v)); +} + +/** + * @brief Internal method to convert an integer to float + * @details This methods gets called by the GPUCommand::push_int, and manages + * storing an integer in a floating point variable. There are two options, + * which are documented inside of the method. + * + * @param v Integer to convert + * @return Float-representation of that integer, either casted or binary converted.s + */ +inline float GPUCommand::convert_int_to_float(int v) const { + + #if !PACK_INT_AS_FLOAT + // Just round to float, can cause rounding issues tho + return (float)v; + + #else + assert(sizeof(float) == 4); // We really need this for packing! Better + // throw an error if the compiler uses more + // than 4 bytes. + // Simple binary conversion, assuming sizeof(int) == sizeof(float) + union { int32_t _int; float _float; } converter = { (int32_t)v }; + return converter._float; + #endif +} + +/** + * @brief Appends a float to the GPUCommand. + * @details This adds an integer to the back of the GPUCommand. Its used by all + * other push_xxx methods, and simply stores the value, then increments the write + * pointer. When the amount of floats exceeds the capacity of the GPUCommand, + * an error will be printed, and the method returns without doing anything else. + * + * @param v The float to append. + */ +inline void GPUCommand::push_float(float v) { + if (_current_index >= GPU_COMMAND_ENTRIES) { + gpucommand_cat.error() << "Out of bounds! Exceeded command size of " << GPU_COMMAND_ENTRIES << endl; + return; + } + _data[_current_index++] = v; +} + +/** + * @brief Appends a 3-component floating point vector to the GPUCommand. + * @details This appends a 3-component floating point vector to the command. + * It basically just calls push_float() for every component, in the order + * x, y, z, which causes the vector to occupy the space of 3 floats. + * + * @param v Int-Vector to append. + */ +inline void GPUCommand::push_vec3(const LVecBase3 &v) { + push_float(v.get_x()); + push_float(v.get_y()); + push_float(v.get_z()); +} + + +/** + * @brief Appends a 3-component integer vector to the GPUCommand. + * @details This appends a 3-component integer vector to the command. + * It basically just calls push_int() for every component, in the order + * x, y, z, which causes the vector to occupy the space of 3 floats. + * + * @param v Int-Vector to append. + */ +inline void GPUCommand::push_vec3(const LVecBase3i &v) { + push_int(v.get_x()); + push_int(v.get_y()); + push_int(v.get_z()); +} + +/** + * @brief Appends a 4-component floating point vector to the GPUCommand. + * @details This appends a 4-component floating point vector to the command. + * It basically just calls push_float() for every component, in the order + * x, y, z, which causes the vector to occupy the space of 3 floats. + * + * @param v Int-Vector to append. + */ +inline void GPUCommand::push_vec4(const LVecBase4 &v) { + push_float(v.get_x()); + push_float(v.get_y()); + push_float(v.get_z()); + push_float(v.get_w()); +} + +/** + * @brief Appends a 4-component integer vector to the GPUCommand. + * @details This appends a 4-component integer vector to the command. + * It basically just calls push_int() for every component, in the order + * x, y, z, w, which causes the vector to occupy the space of 4 floats. + * + * @param v Int-Vector to append. + */ +inline void GPUCommand::push_vec4(const LVecBase4i &v) { + push_int(v.get_x()); + push_int(v.get_y()); + push_int(v.get_z()); + push_int(v.get_w()); +} + +/** + * @brief Appends a floating point 3x3 matrix to the GPUCommand. + * @details This appends a floating point 3x3 matrix to the GPUCommand, by + * pushing all components in row-order to the command. This occupies a space of + * 9 floats. + * + * @param v Matrix to append + */ +inline void GPUCommand::push_mat3(const LMatrix3 &v) { + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + push_float(v.get_cell(i, j)); + } + } +} + +/** + * @brief Appends a floating point 4x4 matrix to the GPUCommand. + * @details This appends a floating point 4x4 matrix to the GPUCommand, by + * pushing all components in row-order to the command. This occupies a space of + * 16 floats. + * + * @param v Matrix to append + */ +inline void GPUCommand::push_mat4(const LMatrix4 &v) { + for (size_t i = 0; i < 4; ++i) { + for (size_t j = 0; j < 4; ++j) { + push_float(v.get_cell(i, j)); + } + } +} + +/** + * @brief Returns whether integers are packed as floats. + * @details This returns how integer are packed into the data stream. If the + * returned value is true, then integers are packed using their binary + * representation converted to floating point format. If the returned value + * is false, then integers are packed by simply casting them to float, + * e.g. val = (float)i; + * @return The integer representation flag + */ +inline bool GPUCommand::get_uses_integer_packing() { + return PACK_INT_AS_FLOAT; +} diff --git a/contrib/src/rplight/gpuCommand.cxx b/contrib/src/rplight/gpuCommand.cxx new file mode 100644 index 0000000000..2bd468c902 --- /dev/null +++ b/contrib/src/rplight/gpuCommand.cxx @@ -0,0 +1,87 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "gpuCommand.h" + +#include +#include +#include + + +NotifyCategoryDef(gpucommand, ""); + +/** + * @brief Constructs a new GPUCommand with the given command type. + * @details This will construct a new GPUCommand of the given command type. + * The command type should be of GPUCommand::CommandType, and determines + * what data the GPUCommand contains, and how it will be handled. + * + * @param command_type The type of the GPUCommand + */ +GPUCommand::GPUCommand(CommandType command_type) { + _command_type = command_type; + _current_index = 0; + memset(_data, 0x0, sizeof(float) * GPU_COMMAND_ENTRIES); + + // Store the command type as the first entry + push_int(command_type); +} + +/** + * @brief Prints out the GPUCommand to the console + * @details This method prints the type, size, and data of the GPUCommand to the + * console. This helps for debugging the contents of the GPUCommand. Keep + * in mind that integers might be shown in their binary float representation, + * depending on the setting in the GPUCommand::convert_int_to_float method. + */ +void GPUCommand::write(ostream &out) const { + out << "GPUCommand(type=" << _command_type << ", size=" << _current_index << ", data = {" << endl; + for (size_t k = 0; k < GPU_COMMAND_ENTRIES; ++k) { + out << std::setw(12) << std::fixed << std::setprecision(5) << _data[k] << " "; + if (k % 6 == 5 || k == GPU_COMMAND_ENTRIES - 1) out << endl; + } + out << "})" << endl; +} + +/** + * @brief Writes the GPU command to a given target. + * @details This method writes all the data of the GPU command to a given target. + * The target should be a pointer to memory being big enough to hold the + * data. Presumably #dest will be a handle to texture memory. + * The command_index controls the offset where the data will be written + * to. + * + * @param dest Handle to the memory to write the command to + * @param command_index Offset to write the command to. The command will write + * its data to command_index * GPU_COMMAND_ENTRIES. When writing + * the GPUCommand in a GPUCommandList, the command_index will + * most likely be the index of the command in the list. + */ +void GPUCommand::write_to(const PTA_uchar &dest, size_t command_index) { + size_t command_size = GPU_COMMAND_ENTRIES * sizeof(float); + size_t offset = command_index * command_size; + memcpy(dest.p() + offset, &_data, command_size); +} diff --git a/contrib/src/rplight/gpuCommand.h b/contrib/src/rplight/gpuCommand.h new file mode 100644 index 0000000000..7fa784b975 --- /dev/null +++ b/contrib/src/rplight/gpuCommand.h @@ -0,0 +1,89 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef GPUCOMMAND_H +#define GPUCOMMAND_H + +#include "pandabase.h" +#include "luse.h" + +NotifyCategoryDecl(gpucommand, EXPORT_CLASS, EXPORT_TEMPL); + +#define GPU_COMMAND_ENTRIES 32 + +// Packs integers by storing their binary representation in floats +// This only works if the command and light buffer is 32bit floating point. +#define PACK_INT_AS_FLOAT 0 + +/** + * @brief Class for storing data to be transferred to the GPU. + * @details This class can be seen like a packet, to be transferred to the GPU. + * It has a command type, which tells the GPU what to do once it recieved this + * "packet". It stores a limited amount of floating point components. + */ +class GPUCommand { +PUBLISHED: + /** + * The different types of GPUCommands. Each type has a special case in + * the command queue processor. When adding new types, those need to + * be handled in the command target, too. + */ + enum CommandType { + CMD_invalid = 0, + CMD_store_light = 1, + CMD_remove_light = 2, + CMD_store_source = 3, + CMD_remove_sources = 4, + }; + + GPUCommand(CommandType command_type); + + inline void push_int(int v); + inline void push_float(float v); + inline void push_vec3(const LVecBase3 &v); + inline void push_vec3(const LVecBase3i &v); + inline void push_vec4(const LVecBase4 &v); + inline void push_vec4(const LVecBase4i &v); + inline void push_mat3(const LMatrix3 &v); + inline void push_mat4(const LMatrix4 &v); + + inline static bool get_uses_integer_packing(); + + void write_to(const PTA_uchar &dest, size_t command_index); + void write(ostream &out) const; + +private: + + inline float convert_int_to_float(int v) const; + + CommandType _command_type; + size_t _current_index; + float _data[GPU_COMMAND_ENTRIES]; +}; + +#include "gpuCommand.I" + +#endif // GPUCOMMAND_H diff --git a/contrib/src/rplight/gpuCommandList.cxx b/contrib/src/rplight/gpuCommandList.cxx new file mode 100644 index 0000000000..3f92194f64 --- /dev/null +++ b/contrib/src/rplight/gpuCommandList.cxx @@ -0,0 +1,82 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "gpuCommandList.h" + + +/** + * @brief Constructs a new GPUCommandList + * @details This constructs a new GPUCommandList. By default, there are no commands + * in the list. + */ +GPUCommandList::GPUCommandList() { +} + +/** + * @brief Pushes a GPUCommand to the command list. + * @details This adds a new GPUCommand to the list of commands to be processed. + * + * @param cmd The command to add + */ +void GPUCommandList::add_command(const GPUCommand& cmd) { + _commands.push(cmd); +} + +/** + * @brief Returns the number of commands in this list. + * @details This returns the amount of commands which are currently stored in this + * list, and are waiting to get processed. + * @return Amount of commands + */ +size_t GPUCommandList::get_num_commands() { + return _commands.size(); +} + +/** + * @brief Writes the first n-commands to a destination. + * @details This takes the first #limit commands, and writes them to the + * destination using GPUCommand::write_to. See GPUCommand::write_to for + * further information about #dest. The limit controls after how much + * commands the processing will be stopped. All commands which got processed + * will get removed from the list. + * + * @param dest Destination to write to, see GPUCommand::write_to + * @param limit Maximum amount of commands to process + * + * @return Amount of commands processed, between 0 and #limit. + */ +size_t GPUCommandList::write_commands_to(const PTA_uchar &dest, size_t limit) { + size_t num_commands_written = 0; + + while (num_commands_written < limit && !_commands.empty()) { + // Write the first command to the stream, and delete it afterwards + _commands.front().write_to(dest, num_commands_written); + _commands.pop(); + num_commands_written ++; + } + + return num_commands_written; +} diff --git a/contrib/src/rplight/gpuCommandList.h b/contrib/src/rplight/gpuCommandList.h new file mode 100644 index 0000000000..f2ec280116 --- /dev/null +++ b/contrib/src/rplight/gpuCommandList.h @@ -0,0 +1,54 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef GPUCOMMANDLIST_H +#define GPUCOMMANDLIST_H + +#include "pandabase.h" +#include "gpuCommand.h" + +#include + +/** + * @brief Class to store a list of commands. + * @details This is a class to store a list of GPUCommands. It provides + * functionality to only provide the a given amount of commands at one time. + */ +class GPUCommandList { +PUBLISHED: + GPUCommandList(); + + void add_command(const GPUCommand& cmd); + size_t get_num_commands(); + size_t write_commands_to(const PTA_uchar &dest, size_t limit = 32); + + MAKE_PROPERTY(num_commands, get_num_commands); + +protected: + queue _commands; +}; + +#endif // GPUCOMMANDLIST_H diff --git a/contrib/src/rplight/iesDataset.cxx b/contrib/src/rplight/iesDataset.cxx new file mode 100644 index 0000000000..2975268138 --- /dev/null +++ b/contrib/src/rplight/iesDataset.cxx @@ -0,0 +1,233 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "iesDataset.h" + +#define _USE_MATH_DEFINES +#include + +NotifyCategoryDef(iesdataset, "") + +/** + * @brief Constructs a new empty dataset. + * @details This constructs a new IESDataset with no data set. + */ +IESDataset::IESDataset() { +} + +/** + * @brief Sets the vertical angles of the dataset. + * @details This sets the list of vertical angles of the dataset. + * + * @param vertical_angles Vector of all vertical angles. + */ +void IESDataset::set_vertical_angles(const PTA_float &vertical_angles) { + nassertv(vertical_angles.size() > 0); + _vertical_angles = vertical_angles; +} + +/** + * @brief Sets the horizontal angles of the dataset. + * @details This sets the list of horizontal angles of the dataset. + * + * @param horizontal_angles Vector of all horizontal angles. + */ +void IESDataset::set_horizontal_angles(const PTA_float &horizontal_angles) { + nassertv(horizontal_angles.size() > 0); + _horizontal_angles = horizontal_angles; +} + +/** + * @brief Sets the candela values. + * @details This sets the candela values of the dataset. They should be an + * interleaved 2D array with the dimensions vertical_angles x horizontal_angles. + * They also should be normalized by dividing by the maximum entry. + * @param candela_values Interleaved 2D-vector of candela values. + */ +void IESDataset::set_candela_values(const PTA_float &candela_values) { + nassertv(candela_values.size() == _horizontal_angles.size() * _vertical_angles.size()); + _candela_values = candela_values; +} + +/** + * @brief Internal method to access the candela data. + * @details This lookups a candela value in the candela values. It converts a + * two dimensional index to a onedimensional index and then returns the candela + * value at that position. + * + * @param vertical_angle_idx Index of the vertical angle + * @param horizontal_angle_idx Index of the horizontal angle + * + * @return Candela value between 0 .. 1 + */ +float IESDataset::get_candela_value_from_index(size_t vertical_angle_idx, size_t horizontal_angle_idx) const { + size_t index = vertical_angle_idx + horizontal_angle_idx * _vertical_angles.size(); + nassertr(index >= 0 && index < _candela_values.size(), 0.0); + return _candela_values[index]; +} + +/** + * @brief Samples the dataset at the given position + * @details This looks up a value in the dataset, by specifying a horizontal and + * vertical angle. This is used for generating the LUT. The vertical and horizontal + * angle should be inside of the bounds of the vertical and horizontal angle arrays. + * + * @param vertical_angle Vertical angle, from 0 .. 90 or 0 .. 180 depending on the dataset + * @param horizontal_angle Horizontal angle, from 0 .. 180 or 0 .. 360 depending on the dataset. + * + * @return Candela value between 0 .. 1 + */ +float IESDataset::get_candela_value(float vertical_angle, float horizontal_angle) const { + + // Special case for datasets without horizontal angles + if (_horizontal_angles.size() == 1) { + return get_vertical_candela_value(0, vertical_angle); + } + + float max_angle = _horizontal_angles[_horizontal_angles.size() - 1]; + + // Wrap angle to fit from 0 .. 360 degree. Most profiles only distribute + // candela values from 0 .. 180 or even 0 .. 90. We have to mirror the + // values at those borders (so 2 times for 180 degree and 4 times for 90 degree) + horizontal_angle = fmod(horizontal_angle, 2.0f * max_angle); + if (horizontal_angle > max_angle) { + horizontal_angle = 2.0 * max_angle - horizontal_angle; + } + + // Simlar to the vertical step, we now try interpolating a horizontal angle, + // but we need to evaluate the vertical value for each row instead of fetching + // the value directly + for (size_t horizontal_index = 1; horizontal_index < _horizontal_angles.size(); ++horizontal_index) { + float curr_angle = _horizontal_angles[horizontal_index]; + + if (curr_angle >= horizontal_angle) { + + // Get previous angle data + float prev_angle = _horizontal_angles[horizontal_index - 1]; + float prev_value = get_vertical_candela_value(horizontal_index - 1, vertical_angle); + float curr_value = get_vertical_candela_value(horizontal_index, vertical_angle); + + // Interpolate lineary + float lerp = (horizontal_angle - prev_angle) / (curr_angle - prev_angle); + + // Should never occur, but to be safe: + if (lerp < 0.0 || lerp > 1.0) { + iesdataset_cat.error() << "Invalid horizontal lerp: " << lerp + << ", requested angle was " << horizontal_angle + << ", prev = " << prev_angle << ", cur = " << curr_angle + << endl; + } + + return curr_value * lerp + prev_value * (1-lerp); + } + } + + return 0.0; +} + +/** + * @brief Fetches a vertical candela value + * @details Fetches a vertical candela value, using a given horizontal position. + * This does an 1D interpolation in the candela values array. + * + * @param horizontal_angle_idx The index of the horizontal angle in the horizontal + * angle array. + * @param vertical_angle The vertical angle. Interpolation will be done if the + * vertical angle is not in the vertical angles array. + * + * @return Candela value between 0 .. 1 + */ +float IESDataset::get_vertical_candela_value(size_t horizontal_angle_idx, float vertical_angle) const { + nassertr(horizontal_angle_idx >= 0 && horizontal_angle_idx < _horizontal_angles.size(), 0.0); + + // Lower bound + if (vertical_angle < 0.0) return 0.0; + + // Upper bound + if (vertical_angle > _vertical_angles[_vertical_angles.size() - 1] ) return 0.0; + + // Find lowest enclosing angle + for (size_t vertical_index = 1; vertical_index < _vertical_angles.size(); ++vertical_index) { + float curr_angle = _vertical_angles[vertical_index]; + + // Found value + if (curr_angle > vertical_angle) { + + // Get previous angle data + float prev_angle = _vertical_angles[vertical_index - 1]; + float prev_value = get_candela_value_from_index(vertical_index - 1, horizontal_angle_idx); + float curr_value = get_candela_value_from_index(vertical_index, horizontal_angle_idx); + + // Interpolate lineary + float lerp = (vertical_angle - prev_angle) / (curr_angle - prev_angle); + + // Should never occur, but to be safe: + if (lerp < 0.0 || lerp > 1.0) { + iesdataset_cat.error() << "ERROR: Invalid vertical lerp: " << lerp + << ", requested angle was " << vertical_angle + << ", prev = " << prev_angle << ", cur = " << curr_angle + << endl; + } + + return curr_value * lerp + prev_value * (1-lerp); + } + } + return 0.0; +} + +/** + * @brief Generates the IES LUT + * @details This generates the LUT into a given dataset texture. The x-axis + * referes to the vertical_angle, whereas the y-axis refers to the + * horizontal angle. + * + * @param dest_tex Texture to write the LUT into + * @param z Layer to write the LUT into, in case the texture is a 3D Texture or + * 2D Texture Array. + */ +void IESDataset::generate_dataset_texture_into(Texture* dest_tex, size_t z) const { + + size_t resolution_vertical = dest_tex->get_y_size(); + size_t resolution_horizontal = dest_tex->get_x_size(); + + // Candla values are stored flippped - vertical angles in the x - Axis + // and horizontal angles in the y - Axis + PNMImage dest = PNMImage(resolution_vertical, resolution_horizontal, 1, 65535); + + for (size_t vert = 0; vert < resolution_vertical; ++vert) { + for (size_t horiz = 0; horiz < resolution_horizontal; ++horiz) { + float vert_angle = (float)vert / (float)(resolution_vertical-1); + vert_angle = cos(vert_angle * M_PI) * 90.0 + 90.0; + float horiz_angle = (float)horiz / (float)(resolution_horizontal-1) * 360.0; + float candela = get_candela_value(vert_angle, horiz_angle); + dest.set_xel(vert, horiz, candela); + } + } + + + dest_tex->load(dest, z, 0); +} diff --git a/contrib/src/rplight/iesDataset.h b/contrib/src/rplight/iesDataset.h new file mode 100644 index 0000000000..3f7ee09e7e --- /dev/null +++ b/contrib/src/rplight/iesDataset.h @@ -0,0 +1,68 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef IESDATASET_H +#define IESDATASET_H + +#include "pandabase.h" +#include "pta_float.h" +#include "pointerToArray.h" +#include "texture.h" +#include "pnmImage.h" + +NotifyCategoryDecl(iesdataset, EXPORT_CLASS, EXPORT_TEMPL); + + +/** + * @brief This class generates a LUT from IES data. + * @details This class is used by the IESLoader to generate a LUT texture which + * is used in the shaders to perform IES lighting. It takes a set of vertical + * and horizontal angles, as well as a set of candela values, which then are + * lineary interpolated onto a 2D LUT Texture. + */ +class IESDataset { +PUBLISHED: + IESDataset(); + + void set_vertical_angles(const PTA_float &vertical_angles); + void set_horizontal_angles(const PTA_float &horizontal_angles); + void set_candela_values(const PTA_float &candela_values); + + void generate_dataset_texture_into(Texture* dest_tex, size_t z) const; + +public: + + float get_candela_value(float vertical_angle, float horizontal_angle) const; + float get_candela_value_from_index(size_t vertical_angle_idx, size_t horizontal_angle_idx) const; + float get_vertical_candela_value(size_t horizontal_angle_idx, float vertical_angle) const; + +private: + PTA_float _vertical_angles; + PTA_float _horizontal_angles; + PTA_float _candela_values; +}; + +#endif // IESDATASET_H diff --git a/contrib/src/rplight/internalLightManager.I b/contrib/src/rplight/internalLightManager.I new file mode 100644 index 0000000000..25e27e5d44 --- /dev/null +++ b/contrib/src/rplight/internalLightManager.I @@ -0,0 +1,140 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +/** + * @brief Returns the maximum light index + * @details This returns the maximum light index (also called slot). Any lights + * after that slot are guaranteed to be zero-lights. This is useful when + * iterating over the list of lights, because iteration can be stopped when + * the maximum light index is reached. + * + * The maximum light index points to the last slot which is used. If no lights + * are attached, -1 is returned. If one light is attached at slot 0, the index + * is 0, if two are attached at the slots 0 and 1, the index is 1, and so on. + * + * If, for example, two lights are attached at the slots 2 and 5, then the + * index will be 5. Keep in mind that the max-index is not an indicator for + * how many lights are attached. Also, zero lights still may occur when iterating + * over the light lists + * + * @return Maximum light index + */ +inline int InternalLightManager::get_max_light_index() const { + return _lights.get_max_index(); +} + +/** + * @brief Returns the amount of stored lights. + * @details This returns the amount of stored lights. This behaves unlike + * InternalLightManager::get_max_light_index, and instead returns the true + * amount of lights, which is completely unrelated to the amount of used slots. + * + * @return Amount of stored lights + */ +inline size_t InternalLightManager::get_num_lights() const { + return _lights.get_num_entries(); +} + +/** + * @brief Returns the amount of shadow sources. + * @details This returns the total amount of stored shadow sources. This does + * not denote the amount of updated sources, but instead takes into account + * all sources, even those out of frustum. + * @return Amount of shadow sources. + */ +inline size_t InternalLightManager::get_num_shadow_sources() const { + return _shadow_sources.get_num_entries(); +} + +/** + * @brief Sets the handle to the shadow manager + * @details This sets the handle to the global shadow manager. It is usually + * constructed on the python side, so we need to get a handle to it. + * + * The manager should be a handle to a ShadowManager instance, and will be + * stored somewhere on the python side most likely. The light manager does not + * keep a reference to it, so the python side should make sure to keep one. + * + * Be sure to call this before the InternalLightManager::update() method is + * called, otherwise an assertion will get triggered. + * + * @param mgr The ShadowManager instance + */ +inline void InternalLightManager::set_shadow_manager(ShadowManager* mgr) { + _shadow_manager = mgr; +} + +/** + * @brief Sets a handle to the command list + * @details This sets a handle to the global GPUCommandList. This is required to + * emit GPUCommands, which are used for attaching and detaching lights, as well + * as shadow source updates. + * + * The cmd_list should be a handle to a GPUCommandList handle, and will be + * stored somewhere on the python side most likely. The light manager does not + * keep a reference to it, so the python side should make sure to keep one. + * + * Be sure to call this before the InternalLightManager::update() method is + * called, otherwise an assertion will get triggered. + * + * @param cmd_list The GPUCommandList instance + */ +inline void InternalLightManager::set_command_list(GPUCommandList *cmd_list) { + _cmd_list = cmd_list; +} + +/** + * @brief Sets the camera position + * @details This sets the camera position, which will be used to determine which + * shadow sources have to get updated + * + * @param mat View projection mat + */ +inline void InternalLightManager::set_camera_pos(const LPoint3 &pos) { + _camera_pos = pos; +} + +/** + * @brief Sets the maximum shadow update distance + * @details This controls the maximum distance until which shadows are updated. + * If a shadow source is past that distance, it is ignored and no longer recieves + * updates until it is in range again + * + * @param dist Distance in world space units + */ +inline void InternalLightManager::set_shadow_update_distance(PN_stdfloat dist) { + _shadow_update_distance = dist; +} + +/** + * @brief Returns the internal used ShadowManager + * @details This returns a handle to the internally used shadow manager + * @return Shadow manager + */ +inline ShadowManager* InternalLightManager::get_shadow_manager() const { + return _shadow_manager; +} diff --git a/contrib/src/rplight/internalLightManager.cxx b/contrib/src/rplight/internalLightManager.cxx new file mode 100644 index 0000000000..ea45c518fd --- /dev/null +++ b/contrib/src/rplight/internalLightManager.cxx @@ -0,0 +1,441 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "internalLightManager.h" + +#include + +NotifyCategoryDef(lightmgr, ""); + + +/** + * @brief Constructs the light manager + * @details This constructs the light manager, initializing the light and shadow + * storage. You should set a command list and shadow manager before calling + * InternalLightManager::update. s + */ +InternalLightManager::InternalLightManager() { + _shadow_update_distance = 100.0; + _cmd_list = nullptr; + _shadow_manager = nullptr; +} + +/** + * @brief Adds a new light. + * @details This adds a new light to the list of lights. This will throw an + * error and return if the light is already attached. You may only call + * this after the ShadowManager was already set. + * + * While the light is attached, the light manager keeps a reference to it, so + * the light does not get destructed. + * + * This also setups the shadows on the light, in case shadows are enabled. + * While a light is attached, you can not change whether it casts shadows or not. + * To do so, detach the light, change the setting, and re-add the light. + * + * In case no free light slot is available, an error will be printed and no + * action will be performed. + * + * If no shadow manager was set, an assertion will be triggered. + * + * @param light The light to add. + */ +void InternalLightManager::add_light(PT(RPLight) light) { + nassertv(_shadow_manager != nullptr); // Shadow manager not set yet! + + // Don't attach the light in case its already attached + if (light->has_slot()) { + lightmgr_cat.error() << "could not add light because it already is attached! " + << "Detach the light first, then try it again." << endl; + return; + } + + // Find a free slot + size_t slot; + if (!_lights.find_slot(slot)) { + lightmgr_cat.error() << "Light limit of " << MAX_LIGHT_COUNT << " reached, " + << "all light slots used!" << endl; + return; + } + + // Reference the light because we store it, to avoid it getting destructed + // on the python side while we still work with it. The reference will be + // removed when the light gets detached. + light->ref(); + + // Reserve the slot + light->assign_slot(slot); + _lights.reserve_slot(slot, light); + + // Setup the shadows in case the light uses them + if (light->get_casts_shadows()) { + setup_shadows(light); + } + + // Store the light on the gpu, to make sure the GPU directly knows about it. + // We could wait until the next update cycle, but then we might be one frame + // too late already. + gpu_update_light(light); +} + +/** + * @brief Internal method to setup shadows for a light + * @details This method gets called by the InternalLightManager::add_light method + * to setup a lights shadow sources, in case shadows are enabled on that light. + * + * It finds a slot for all shadow sources of the ilhgt, and inits the shadow + * sources as well. If no slot could be found, an error is printed an nothing + * happens. + * + * @param light The light to init the shadow sources for + */ +void InternalLightManager::setup_shadows(RPLight* light) { + + // Init the lights shadow sources, and also call update once to make sure + // the sources are properly initialized + light->init_shadow_sources(); + light->update_shadow_sources(); + + // Find consecutive slots, this is important for PointLights so we can just + // store the first index of the source, and get the other slots by doing + // first_index + 1, +2 and so on. + size_t base_slot; + size_t num_sources = light->get_num_shadow_sources(); + if (!_shadow_sources.find_consecutive_slots(base_slot, num_sources)) { + lightmgr_cat.error() << "Failed to find slot for shadow sources! " + << "Shadow-Source limit of " << MAX_SHADOW_SOURCES + << " reached!" << endl; + return; + } + + // Init all sources + for (int i = 0; i < num_sources; ++i) { + ShadowSource* source = light->get_shadow_source(i); + + // Set the source as dirty, so it gets updated in the beginning + source->set_needs_update(true); + + // Assign the slot to the source. Since we got consecutive slots, we can + // just do base_slot + N. + size_t slot = base_slot + i; + _shadow_sources.reserve_slot(slot, source); + source->set_slot(slot); + } +} + +/** + * @brief Removes a light + * @details This detaches a light. This prevents it from being rendered, and also + * cleans up all resources used by that light. If no reference is kept on the + * python side, the light will also get destructed. + * + * If the light was not previously attached with InternalLightManager::add_light, + * an error will be triggered and nothing happens. + * + * In case the light was set to cast shadows, all shadow sources are cleaned + * up, and their regions in the shadow atlas are freed. + * + * All resources used by the light in the light and shadow storage are also + * cleaned up, by emitting cleanup GPUCommands. + * + * If no shadow manager was set, an assertion will be triggered. + * + * @param light [description] + */ +void InternalLightManager::remove_light(PT(RPLight) light) { + nassertv(_shadow_manager != nullptr); + + if (!light->has_slot()) { + lightmgr_cat.error() << "Could not detach light, light was not attached!" << endl; + return; + } + + // Free the lights slot in the light storage + _lights.free_slot(light->get_slot()); + + // Tell the GPU we no longer need the lights data + gpu_remove_light(light); + + // Mark the light as detached. After this call, we can not call get_slot + // anymore, so its important we do this after we unregistered the light + // from everywhere. + light->remove_slot(); + + // Clear shadow related stuff, in case the light casts shadows + if (light->get_casts_shadows()) { + + // Free the slots of all sources, and also unregister their regions from + // the shadow atlas. + for (size_t i = 0; i < light->get_num_shadow_sources(); ++i) { + ShadowSource* source = light->get_shadow_source(i); + if (source->has_slot()) { + _shadow_sources.free_slot(source->get_slot()); + } + if (source->has_region()) { + _shadow_manager->get_atlas()->free_region(source->get_region()); + source->clear_region(); + } + } + + // Remove all sources of the light by emitting a consecutive remove command + gpu_remove_consecutive_sources(light->get_shadow_source(0), + light->get_num_shadow_sources()); + + // Finally remove all shadow sources. This is important in case the light + // will be re-attached. Otherwise an assertion will get triggered. + light->clear_shadow_sources(); + } + + // Since we referenced the light when we stored it, we have to decrease + // the reference now. In case no reference was kept on the python side, + // the light will get destructed soon. + light->unref(); +} + +/** + * @brief Internal method to remove consecutive sources from the GPU. + * @details This emits a GPUCommand to consecutively remove shadow sources from + * the GPU. This is called when a light gets removed, to free the space its + * shadow sources took. Its not really required, because as long as the light + * is not used, there is no reference to the sources. However, it can't hurt to + * cleanup the memory. + * + * All sources starting at first_source->get_slot() until + * first_source->get_slot() + num_sources will get cleaned up. + * + * @param first_source First source of the light + * @param num_sources Amount of consecutive sources to clear + */ +void InternalLightManager::gpu_remove_consecutive_sources(ShadowSource *first_source, + size_t num_sources) { + nassertv(_cmd_list != nullptr); // No command list set yet + nassertv(first_source->has_slot()); // Source has no slot! + GPUCommand cmd_remove(GPUCommand::CMD_remove_sources); + cmd_remove.push_int(first_source->get_slot()); + cmd_remove.push_int(num_sources); + _cmd_list->add_command(cmd_remove); +} + +/** + * @brief Internal method to remove a light from the GPU. + * @details This emits a GPUCommand to clear a lights data. This sets the data + * to all zeros, marking that no light is stored anymore. + * + * This throws an assertion in case the light is not currently attached. Be + * sure to call this before detaching the light. + * + * @param light The light to remove, must be attached. + */ +void InternalLightManager::gpu_remove_light(RPLight* light) { + nassertv(_cmd_list != nullptr); // No command list set yet + nassertv(light->has_slot()); // Light has no slot! + GPUCommand cmd_remove(GPUCommand::CMD_remove_light); + cmd_remove.push_int(light->get_slot()); + _cmd_list->add_command(cmd_remove); +} + +/** + * @brief Updates a lights data on the GPU + * @details This method emits a GPUCommand to update a lights data. This can + * be used to initially store the lights data, or to update the data whenever + * the light changed. + * + * This throws an assertion in case the light is not currently attached. Be + * sure to call this after attaching the light. + * + * @param light The light to update + */ +void InternalLightManager::gpu_update_light(RPLight* light) { + nassertv(_cmd_list != nullptr); // No command list set yet + nassertv(light->has_slot()); // Light has no slot! + GPUCommand cmd_update(GPUCommand::CMD_store_light); + cmd_update.push_int(light->get_slot()); + light->write_to_command(cmd_update); + light->set_needs_update(false); + _cmd_list->add_command(cmd_update); +} + +/** + * @brief Updates a shadow source data on the GPU + * @details This emits a GPUCommand to update a given shadow source, storing all + * data of the source on the GPU. This can also be used to initially store a + * ShadowSource, since all data will be overridden. + * + * This throws an assertion if the source has no slot yet. + * + * @param source The source to update + */ +void InternalLightManager::gpu_update_source(ShadowSource* source) { + nassertv(_cmd_list != nullptr); // No command list set yet + nassertv(source->has_slot()); // Source has no slot! + GPUCommand cmd_update(GPUCommand::CMD_store_source); + cmd_update.push_int(source->get_slot()); + source->write_to_command(cmd_update); + _cmd_list->add_command(cmd_update); +} + +/** + * @brief Internal method to update all lights + * @details This is called by the main update method, and iterates over the list + * of lights. If a light is marked as dirty, it will recieve an update of its + * data and its shadow sources. + */ +void InternalLightManager::update_lights() { + for (auto iter = _lights.begin(); iter != _lights.end(); ++iter) { + RPLight* light = *iter; + if (light && light->get_needs_update()) { + if (light->get_casts_shadows()) { + light->update_shadow_sources(); + } + gpu_update_light(light); + } + } +} + +/** + * @brief Compares shadow sources by their priority + * @details Returns if a has a greater priority than b. This depends on the + * resolution of the source, and also if the source has a region or not. + * This method can be passed to std::sort. + * + * @param a First source + * @param b Second source + * + * @return true if a is more important than b, else false + */ +bool InternalLightManager::compare_shadow_sources(const ShadowSource* a, const ShadowSource* b) const { + + // Make sure that sources which already have a region (but maybe outdated) + // come after sources which have no region at all. + if (a->has_region() != b->has_region()) { + return b->has_region(); + } + + // Compare sources based on their distance to the camera + PN_stdfloat dist_a = (_camera_pos - a->get_bounds().get_center()).length_squared(); + PN_stdfloat dist_b = (_camera_pos - a->get_bounds().get_center()).length_squared(); + + // XXX: Should also compare based on source size, so that huge sources recieve + // more updates + + return dist_b > dist_a; +} + +/** + * @brief Internal method to update all shadow sources + * @details This updates all shadow sources which are marked dirty. It will sort + * the list of all dirty shadow sources by their resolution, take the first + * n entries, and update them. The amount of sources processed depends on the + * max_updates of the ShadowManager. + */ +void InternalLightManager::update_shadow_sources() { + + // Find all dirty shadow sources and make a list of them + vector sources_to_update; + for (auto iter = _shadow_sources.begin(); iter != _shadow_sources.end(); ++iter) { + ShadowSource* source = *iter; + if (source) { + const BoundingSphere& bounds = source->get_bounds(); + + // Check if source is in range + PN_stdfloat distance_to_camera = (_camera_pos - bounds.get_center()).length() - bounds.get_radius(); + if (distance_to_camera < _shadow_update_distance) { + if (source->get_needs_update()) { + sources_to_update.push_back(source); + } + } else { + + // Free regions of sources which are out of the update radius, + // to make space for other regions + if (source->has_region()) { + _shadow_manager->get_atlas()->free_region(source->get_region()); + source->clear_region(); + } + } + } + + } + + // Sort the sources based on their importance, so that sources with a bigger + // priority come first. This helps to get a better packing on the shadow atlas. + // However, we also need to prioritize sources which have no current region, + // because no shadows are worse than outdated-shadows. + std::sort(sources_to_update.begin(), sources_to_update.end(), [this](const ShadowSource* a, const ShadowSource* b) { + return this->compare_shadow_sources(a, b); + }); + + // Get a handle to the atlas, will be frequently used + ShadowAtlas *atlas = _shadow_manager->get_atlas(); + + // Free the regions of all sources which will get updated. We have to take into + // account that only a limited amount of sources can get updated per frame. + size_t update_slots = min(sources_to_update.size(), + _shadow_manager->get_num_update_slots_left()); + for(size_t i = 0; i < update_slots; ++i) { + if (sources_to_update[i]->has_region()) { + atlas->free_region(sources_to_update[i]->get_region()); + } + } + + // Find an atlas spot for all regions which are supposed to get an update + for (size_t i = 0; i < update_slots; ++i) { + ShadowSource *source = sources_to_update[i]; + + if(!_shadow_manager->add_update(source)) { + // In case the ShadowManager lied about the number of updates left + lightmgr_cat.error() << "ShadowManager ensured update slot, but slot is taken!" << endl; + break; + } + + // We have an update slot, and are guaranteed to get updated as soon + // as possible, so we can start getting a new atlas position. + size_t region_size = atlas->get_required_tiles(source->get_resolution()); + LVecBase4i new_region = atlas->find_and_reserve_region(region_size, region_size); + LVecBase4 new_uv_region = atlas->region_to_uv(new_region); + source->set_region(new_region, new_uv_region); + + // Mark the source as updated + source->set_needs_update(false); + gpu_update_source(source); + } +} + +/** + * @brief Main update method + * @details This is the main update method of the InternalLightManager. It + * processes all lights and shadow sources, updates them, and notifies the + * GPU about it. This should be called on a per-frame basis. + * + * If the InternalLightManager was not initialized yet, an assertion is thrown. + */ +void InternalLightManager::update() { + nassertv(_shadow_manager != nullptr); // Not initialized yet! + nassertv(_cmd_list != nullptr); // Not initialized yet! + + update_lights(); + update_shadow_sources(); +} diff --git a/contrib/src/rplight/internalLightManager.h b/contrib/src/rplight/internalLightManager.h new file mode 100644 index 0000000000..572ce297a0 --- /dev/null +++ b/contrib/src/rplight/internalLightManager.h @@ -0,0 +1,102 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef INTERNALLIGHTMANAGER_H +#define INTERNALLIGHTMANAGER_H + +#include "referenceCount.h" +#include "rpLight.h" +#include "shadowSource.h" +#include "shadowAtlas.h" +#include "shadowManager.h" +#include "pointerSlotStorage.h" +#include "gpuCommandList.h" + +#define MAX_LIGHT_COUNT 65535 +#define MAX_SHADOW_SOURCES 2048 + +NotifyCategoryDecl(lightmgr, EXPORT_CLASS, EXPORT_TEMPL); + +/** + * @brief Internal class used for handling lights and shadows. + * @details This is the internal class used by the pipeline to handle all + * lights and shadows. It stores references to the lights, manages handling + * the light and shadow slots, and also communicates with the GPU with the + * GPUCommandQueue to store light and shadow source data. + */ +class InternalLightManager { +PUBLISHED: + InternalLightManager(); + + void add_light(PT(RPLight) light); + void remove_light(PT(RPLight) light); + + void update(); + inline void set_camera_pos(const LPoint3 &pos); + inline void set_shadow_update_distance(PN_stdfloat dist); + + inline int get_max_light_index() const; + MAKE_PROPERTY(max_light_index, get_max_light_index); + + inline size_t get_num_lights() const; + MAKE_PROPERTY(num_lights, get_num_lights); + + inline size_t get_num_shadow_sources() const; + MAKE_PROPERTY(num_shadow_sources, get_num_shadow_sources); + + inline void set_shadow_manager(ShadowManager* mgr); + inline ShadowManager* get_shadow_manager() const; + MAKE_PROPERTY(shadow_manager, get_shadow_manager, set_shadow_manager); + + inline void set_command_list(GPUCommandList *cmd_list); + +protected: + + void gpu_update_light(RPLight* light); + void gpu_update_source(ShadowSource* source); + void gpu_remove_light(RPLight* light); + void gpu_remove_consecutive_sources(ShadowSource *first_source, size_t num_sources); + + void setup_shadows(RPLight* light); + bool compare_shadow_sources(const ShadowSource* a, const ShadowSource* b) const; + + void update_lights(); + void update_shadow_sources(); + + GPUCommandList* _cmd_list; + ShadowManager* _shadow_manager; + + PointerSlotStorage _lights; + PointerSlotStorage _shadow_sources; + + LPoint3 _camera_pos; + float _shadow_update_distance; + +}; + +#include "internalLightManager.I" + +#endif // INTERNALLIGHTMANAGER_H diff --git a/contrib/src/rplight/p3rplight_composite1.cxx b/contrib/src/rplight/p3rplight_composite1.cxx new file mode 100644 index 0000000000..4bfca47bf7 --- /dev/null +++ b/contrib/src/rplight/p3rplight_composite1.cxx @@ -0,0 +1,13 @@ +#include "config_rplight.cxx" +#include "gpuCommand.cxx" +#include "gpuCommandList.cxx" +#include "iesDataset.cxx" +#include "internalLightManager.cxx" +#include "pssmCameraRig.cxx" +#include "rpLight.cxx" +#include "rpPointLight.cxx" +#include "rpSpotLight.cxx" +#include "shadowAtlas.cxx" +#include "shadowManager.cxx" +#include "shadowSource.cxx" +#include "tagStateManager.cxx" diff --git a/contrib/src/rplight/pointerSlotStorage.h b/contrib/src/rplight/pointerSlotStorage.h new file mode 100644 index 0000000000..2d908c5795 --- /dev/null +++ b/contrib/src/rplight/pointerSlotStorage.h @@ -0,0 +1,239 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef POINTERSLOTSTORAGE_H +#define POINTERSLOTSTORAGE_H + + +#ifdef CPPPARSER + +// Dummy implementation for interrogate +template +class PointerSlotStorage {}; + +#else // CPPPARSER + + +#include "pandabase.h" + +// Apple has an outdated libstdc++, so pull the class from TR1. +#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20070719 +#include +using std::tr1::array; +#else +#include +#endif + +/** + * @brief Class to keep a list of pointers and nullpointers. + * @details This class stores a fixed size list of pointers, whereas pointers + * may be a nullptr as well. It provides functionality to find free slots, + * and also to find free consecutive slots, as well as taking care of reserving slots. + * + * @tparam T* Pointer-Type + * @tparam SIZE Size of the storage + */ +template +class PointerSlotStorage { +public: + /** + * @brief Constructs a new PointerSlotStorage + * @details This constructs a new PointerSlotStorage, with all slots + * initialized to a nullptr. + */ + PointerSlotStorage() { +#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20070719 + _data.assign(nullptr); +#else + _data.fill(nullptr); +#endif + _max_index = 0; + _num_entries = 0; + } + + /** + * @brief Returns the maximum index of the container + * @details This returns the greatest index of any element which is not zero. + * This can be useful for iterating the container, since all elements + * coming after the returned index are guaranteed to be a nullptr. + * + * If no elements are in this container, -1 is returned. + * @return Maximum index of the container + */ + int get_max_index() const { + return _max_index; + } + + /** + * @brief Returns the amount of elements of the container + * @details This returns the amount of elements in the container which are + * no nullptr. + * @return Amount of elements + */ + size_t get_num_entries() const { + return _num_entries; + } + + /** + * @brief Finds a free slot + * @details This finds the first slot which is a nullptr and returns it. + * This is most likely useful in combination with reserve_slot. + * + * When no slot found was found, slot will be undefined, and false will + * be returned. + * + * @param slot Output-Variable, slot will be stored there + * @return true if a slot was found, otherwise false + */ + bool find_slot(size_t &slot) const { + for (size_t i = 0; i < SIZE; ++i) { + if (_data[i] == nullptr) { + slot = i; + return true; + } + } + return false; + } + + /** + * @brief Finds free consecutive slots + * @details This behaves like find_slot, but it tries to find a slot + * after which free slots follow as well. + * + * When no slot found was found, slot will be undefined, and false will + * be returned. + * + * @param slot Output-Variable, index of the first slot of the consecutive + * slots will be stored there. + * @param num_consecutive Amount of consecutive slots to find, including the + * first slot. + * + * @return true if consecutive slots were found, otherwise false. + */ + bool find_consecutive_slots(size_t &slot, size_t num_consecutive) const { + nassertr(num_consecutive > 0, false); + + // Fall back to default search algorithm in case the parameters are equal + if (num_consecutive == 1) { + return find_slot(slot); + } + + // Try to find consecutive slots otherwise + for (size_t i = 0; i < SIZE; ++i) { + bool any_taken = false; + for (size_t k = 0; !any_taken && k < num_consecutive; ++k) { + any_taken = _data[i + k] != nullptr; + } + if (!any_taken) { + slot = i; + return true; + } + } + return false; + } + + /** + * @brief Frees an allocated slot + * @details This frees an allocated slot. If the slot was already freed + * before, this method throws an assertion. + * + * @param slot Slot to free + */ + void free_slot(size_t slot) { + nassertv(slot >= 0 && slot < SIZE); + nassertv(_data[slot] != nullptr); // Slot was already empty! + _data[slot] = nullptr; + _num_entries--; + + // Update maximum index + if (slot == _max_index) { + while (_max_index >= 0 && !_data[_max_index--]); + } + } + + /** + * @brief Frees consecutive allocated slots + * @details This behaves like PointerSlotStorage::free_slot, but deletes + * consecutive slots. + * + * @param slot Start of the consecutive slots to free + * @param num_consecutive Number of consecutive slots + */ + void free_consecutive_slots(size_t slot, size_t num_consecutive) { + for (size_t i = slot; i < slot + num_consecutive; ++i) { + free_slot(i); + } + } + + /** + * @brief Reserves a slot + * @details This reserves a slot by storing a pointer in it. If the slot + * was already taken, throws an assertion. + * If the ptr is a nullptr, also throws an assertion. + * If the slot was out of bounds, also throws an assertion. + * + * @param slot Slot to reserve + * @param ptr Pointer to store + */ + void reserve_slot(size_t slot, T ptr) { + nassertv(slot >= 0 && slot < SIZE); + nassertv(_data[slot] == nullptr); // Slot already taken! + nassertv(ptr != nullptr); // nullptr passed as argument! + _max_index = max(_max_index, (int)slot); + _data[slot] = ptr; + _num_entries++; + } + + typedef array InternalContainer; + + /** + * @brief Returns an iterator to the begin of the container + * @details This returns an iterator to the beginning of the container + * @return Begin-Iterator + */ + typename InternalContainer::iterator begin() { + return _data.begin(); + } + + /** + * @brief Returns an iterator to the end of the container + * @details This returns an iterator to the end of the iterator. This only + * iterates to PointerSlotStorage::get_max_index() + * @return [description] + */ + typename InternalContainer::iterator end() { + return _data.begin() + _max_index + 1; + } + +private: + int _max_index; + size_t _num_entries; + InternalContainer _data; +}; + +#endif // CPPPARSER + +#endif // POINTERSLOTSTORAGE_H diff --git a/contrib/src/rplight/pssmCameraRig.I b/contrib/src/rplight/pssmCameraRig.I new file mode 100644 index 0000000000..cd87718262 --- /dev/null +++ b/contrib/src/rplight/pssmCameraRig.I @@ -0,0 +1,243 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +/** + * @brief Sets the maximum pssm distance. + * @details This sets the maximum distance in world space until which shadows + * are rendered. After this distance, no shadows will be rendered. + * + * If the distance is below zero, an assertion is triggered. + * + * @param distance Maximum distance in world space + */ +inline void PSSMCameraRig::set_pssm_distance(float distance) { + nassertv(distance > 0.0 && distance < 100000.0); + _pssm_distance = distance; +} + +/** + * @brief Sets the suns distance + * @details This sets the distance the cameras will have from the cameras frustum. + * This prevents far objects from having no shadows, which can occur when these + * objects are between the cameras frustum and the sun, but not inside of the + * cameras frustum. Setting the sun distance high enough will move the cameras + * away from the camera frustum, being able to cover those distant objects too. + * + * If the sun distance is set too high, artifacts will occur due to the reduced + * range of depth. If a value below zero is passed, an assertion will get + * triggered. + * + * @param distance The sun distance + */ +inline void PSSMCameraRig::set_sun_distance(float distance) { + nassertv(distance > 0.0 && distance < 100000.0); + _sun_distance = distance; +} + +/** + * @brief Sets the logarithmic factor + * @details This sets the logarithmic factor, which is the core of the algorithm. + * PSSM splits the camera frustum based on a linear and a logarithmic factor. + * While a linear factor provides a good distribution, it often is not applicable + * for wider distances. A logarithmic distribution provides a better distribution + * at distance, but suffers from splitting in the near areas. + * + * The logarithmic factor mixes the logarithmic and linear split distribution, + * to get the best of both. A greater factor will make the distribution more + * logarithmic, while a smaller factor will make it more linear. + * + * If the factor is below zero, an ssertion is triggered. + * + * @param factor The logarithmic factor + */ +inline void PSSMCameraRig::set_logarithmic_factor(float factor) { + nassertv(factor > 0.0); + _logarithmic_factor = factor; +} + +/** + * @brief Sets whether to use a fixed film size + * @details This controls if a fixed film size should be used. This will cause + * the camera rig to cache the current film size, and only change it in case + * it gets too small. This provides less flickering when moving, because the + * film size will stay roughly constant. However, to prevent the cached film + * size getting too big, one should call PSSMCameraRig::reset_film_size + * once in a while, otherwise there might be a lot of wasted space. + * + * @param flag Whether to use a fixed film size + */ +inline void PSSMCameraRig::set_use_fixed_film_size(bool flag) { + _use_fixed_film_size = flag; +} + +/** + * @brief Sets the resolution of each split + * @details This sets the resolution of each split. Currently it is equal for + * each split. This is required when using PSSMCameraRig::set_use_stable_csm, + * to compute how bix a texel is. + * + * It has to match the y-resolution of the pssm shadow map. If an invalid + * resolution is triggered, an assertion is thrown. + * + * @param resolution The resolution of each split. + */ +inline void PSSMCameraRig::set_resolution(size_t resolution) { + nassertv(resolution >= 0 && resolution < 65535); + _resolution = resolution; +} + +/** + * @brief Sets whether to use stable CSM snapping. + * @details This option controls if stable CSM snapping should be used. When the + * option is enabled, all splits will snap to their texels, so that when moving, + * no flickering will occur. However, this only works when the splits do not + * change their film size, rotation and angle. + * + * @param flag Whether to use stable CSM snapping + */ +inline void PSSMCameraRig::set_use_stable_csm(bool flag) { + _use_stable_csm = flag; +} + +/** + * @brief Sets the border bias for each split + * @details This sets the border bias for every split. This increases each + * splits frustum by multiplying it by (1 + bias), and helps reducing artifacts + * at the borders of the splits. Artifacts can occur when the bias is too low, + * because then the filtering will go over the bounds of the split, producing + * invalid results. + * + * If the bias is below zero, an assertion is thrown. + * + * @param bias Border bias + */ +inline void PSSMCameraRig::set_border_bias(float bias) { + nassertv(bias >= 0.0); + _border_bias = bias; +} + +/** + * @brief Resets the film size cache + * @details In case PSSMCameraRig::set_use_fixed_film_size is used, this resets + * the film size cache. This might lead to a small "jump" in the shadows, + * because the film size changes, however it leads to a better shadow distribution. + * + * This is the case because when using a fixed film size, the cache will get + * bigger and bigger, whenever the camera moves to a grazing angle. However, + * when moving back to a normal angle, the film size cache still stores this + * big angle, and thus the splits will have a much bigger film size than actualy + * required. To prevent this, call this method once in a while, so an optimal + * distribution is ensured. + */ +inline void PSSMCameraRig::reset_film_size_cache() { + for (size_t i = 0; i < _max_film_sizes.size(); ++i) { + _max_film_sizes[i].fill(0); + } +} + +/** + * @brief Returns the n-th camera + * @details This returns the n-th camera of the camera rig, which can be used + * for various stuff like showing its frustum, passing it as a shader input, + * and so on. + * + * The first camera is the camera which is the camera of the first split, + * which is the split closest to the camera. All cameras follow in descending + * order until to the last camera, which is the split furthest away from the + * camera. + * + * If an invalid index is passed, an assertion is thrown. + * + * @param index Index of the camera. + * @return [description] + */ +inline NodePath PSSMCameraRig::get_camera(size_t index) { + nassertr(index >= 0 && index < _cam_nodes.size(), NodePath()); + return _cam_nodes[index]; +} + +/** + * @brief Internal method to compute the distance of a split + * @details This is the internal method to perform the weighting of the + * logarithmic and linear distribution. It computes the distance to the + * camera from which a given split starts, by weighting the logarithmic and + * linear factor. + * + * The return value is a value ranging from 0 .. 1. To get the distance in + * world space, the value has to get multiplied with the maximum shadow distance. + * + * @param split_index The index of the split + * @return Distance of the split, ranging from 0 .. 1 + */ +inline float PSSMCameraRig::get_split_start(size_t split_index) { + float x = (float)split_index / (float)_cam_nodes.size(); + return (exp(_logarithmic_factor*x)-1) / (exp(_logarithmic_factor)-1); +} + +/** + * @brief Internal method for interpolating a point along the camera frustum + * @details This method takes a given distance in the 0 .. 1 range, whereas + * 0 denotes the camera near plane, and 1 denotes the camera far plane, + * and lineary interpolates between them. + * + * @param origin Edge of the frustum + * @param depth Distance in the 0 .. 1 range + * + * @return interpolated point in world space + */ +inline LPoint3 PSSMCameraRig::get_interpolated_point(CoordinateOrigin origin, float depth) { + nassertr(depth >= 0.0 && depth <= 1.0, LPoint3()); + return _curr_near_points[origin] * (1.0 - depth) + _curr_far_points[origin] * depth; +} + +/** + * @brief Returns a handle to the MVP array + * @details This returns a handle to the array of view-projection matrices + * of the different splits. This can be used for computing shadows. The array + * is a PTALMatrix4 and thus can be directly bound to a shader. + * + * @return view-projection matrix array + */ +inline const PTA_LMatrix4 &PSSMCameraRig::get_mvp_array() { + return _camera_mvps; +} + +/** + * @brief Returns a handle to the near and far planes array + * @details This returns a handle to the near and far plane array. Each split + * has an entry in the array, whereas the x component of the vecto denotes the + * near plane, and the y component denotes the far plane of the split. + * + * This is required because the near and far planes of the splits change + * constantly. To access them in a shader, the shader needs access to the + * array. + * + * @return Array of near and far planes + */ +inline const PTA_LVecBase2 &PSSMCameraRig::get_nearfar_array() { + return _camera_nearfar; +} diff --git a/contrib/src/rplight/pssmCameraRig.cxx b/contrib/src/rplight/pssmCameraRig.cxx new file mode 100644 index 0000000000..b9560a80d2 --- /dev/null +++ b/contrib/src/rplight/pssmCameraRig.cxx @@ -0,0 +1,396 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "pssmCameraRig.h" + +#define _USE_MATH_DEFINES +#include +#include "orthographicLens.h" + + +PStatCollector PSSMCameraRig::_update_collector("App:Show code:RP_PSSM_update"); + +/** + * @brief Constructs a new PSSM camera rig + * @details This constructs a new camera rig, with a given amount of splits. + * The splits can not be changed later on. Splits are also called Cascades. + * + * An assertion will be triggered if the splits are below zero. + * + * @param num_splits Amount of PSSM splits + */ +PSSMCameraRig::PSSMCameraRig(size_t num_splits) { + nassertv(num_splits > 0); + _num_splits = num_splits; + _pssm_distance = 100.0; + _sun_distance = 500.0; + _use_fixed_film_size = false; + _use_stable_csm = true; + _logarithmic_factor = 1.0; + _resolution = 512; + _border_bias = 0.1; + _camera_mvps = PTA_LMatrix4::empty_array(num_splits); + _camera_nearfar = PTA_LVecBase2::empty_array(num_splits); + init_cam_nodes(); +} + +/** + * @brief Destructs the camera rig + * @details This destructs the camera rig, cleaning up all used resources. + */ +PSSMCameraRig::~PSSMCameraRig() { + // TODO: Detach all cameras and call remove_node. Most likely this is not + // an issue tho, because the camera rig will never get destructed. +} + +/** + * @brief Internal method to init the cameras + * @details This method constructs all cameras and their required lens nodes + * for all splits. It also resets the film size array. + */ +void PSSMCameraRig::init_cam_nodes() { + _cam_nodes.reserve(_num_splits); + _max_film_sizes.resize(_num_splits); + _cameras.resize(_num_splits); + for (size_t i = 0; i < _num_splits; ++i) + { + // Construct a new lens + Lens *lens = new OrthographicLens(); + lens->set_film_size(1, 1); + lens->set_near_far(1, 1000); + + // Construct a new camera + _cameras[i] = new Camera("pssm-cam-" + format_string(i), lens); + _cam_nodes.push_back(NodePath(_cameras[i])); + _max_film_sizes[i].fill(0); + } +} + +/** + * @brief Reparents the camera rig + * @details This reparents all cameras to the given parent. Usually the parent + * will be ShowBase.render. The parent should be the same node where the + * main camera is located in, too. + * + * If an empty parrent is passed, an assertion will get triggered. + * + * @param parent Parent node path + */ +void PSSMCameraRig::reparent_to(NodePath parent) { + nassertv(!parent.is_empty()); + for (size_t i = 0; i < _num_splits; ++i) { + _cam_nodes[i].reparent_to(parent); + } + _parent = parent; +} + +/** + * @brief Internal method to compute the view-projection matrix of a camera + * @details This returns the view-projection matrix of the given split. No bounds + * checking is done. If an invalid index is passed, undefined behaviour occurs. + * + * @param split_index Index of the split + * @return view-projection matrix of the split + */ +LMatrix4 PSSMCameraRig::compute_mvp(size_t split_index) { + LMatrix4 transform = _parent.get_transform(_cam_nodes[split_index])->get_mat(); + return transform * _cameras[split_index]->get_lens()->get_projection_mat(); +} + +/** + * @brief Internal method used for stable CSM + * @details This method is used when stable CSM is enabled. It ensures that each + * source only moves in texel-steps, thus preventing flickering. This works by + * projecting the point (0, 0, 0) to NDC space, making sure that it gets projected + * to a texel center, and then projecting that texel back. + * + * This only works if the camera does not rotate, change its film size, or change + * its angle. + * + * @param mat view-projection matrix of the camera + * @param resolution resolution of the split + * + * @return Offset to add to the camera position to achieve stable snapping + */ +LVecBase3 PSSMCameraRig::get_snap_offset(const LMatrix4& mat, size_t resolution) { + // Transform origin to camera space + LPoint4 base_point = mat.get_row(3) * 0.5 + 0.5; + + // Compute the snap offset + float texel_size = 1.0 / (float)(resolution); + float offset_x = fmod(base_point.get_x(), texel_size); + float offset_y = fmod(base_point.get_y(), texel_size); + + // Reproject the offset back, for that we need the inverse MVP + LMatrix4 inv_mat(mat); + inv_mat.invert_in_place(); + LVecBase3 new_base_point = inv_mat.xform_point(LVecBase3( + (base_point.get_x() - offset_x) * 2.0 - 1.0, + (base_point.get_y() - offset_y) * 2.0 - 1.0, + base_point.get_z() * 2.0 - 1.0 + )); + return -new_base_point; +} + +/** + * @brief Computes the average of a list of points + * @details This computes the average over a given set of points in 3D space. + * It returns the average of those points, namely sum_of_points / num_points. + * + * It is designed to work with a frustum, which is why it takes two arrays + * with a dimension of 4. Usually the first array are the camera near points, + * and the second array are the camera far points. + * + * @param starts First array of points + * @param ends Second array of points + * @return Average of points + */ +LPoint3 get_average_of_points(LVecBase3 const (&starts)[4], LVecBase3 const (&ends)[4]) { + LPoint3 mid_point(0, 0, 0); + for (size_t k = 0; k < 4; ++k) { + mid_point += starts[k]; + mid_point += ends[k]; + } + return mid_point / 8.0; +} + +/** + * @brief Finds the minimum and maximum extends of the given projection + * @details This projects each point of the given array of points using the + * cameras view-projection matrix, and computes the minimum and maximum + * of the projected points. + * + * @param min_extent Will store the minimum extent of the projected points in NDC space + * @param max_extent Will store the maximum extent of the projected points in NDC space + * @param transform The transformation matrix of the camera + * @param proj_points The array of points to project + * @param cam The camera to be used to project the points + */ +void find_min_max_extents(LVecBase3 &min_extent, LVecBase3 &max_extent, const LMatrix4 &transform, LVecBase3 const (&proj_points)[8], Camera *cam) { + + min_extent.fill(1e10); + max_extent.fill(-1e10); + LPoint2 screen_points[8]; + + // Now project all points to the screen space of the current camera and also + // find the minimum and maximum extents + for (size_t k = 0; k < 8; ++k) { + LVecBase4 point(proj_points[k], 1); + LPoint4 proj_point = transform.xform(point); + LPoint3 proj_point_3d(proj_point.get_x(), proj_point.get_y(), proj_point.get_z()); + cam->get_lens()->project(proj_point_3d, screen_points[k]); + + // Find min / max extents + if (screen_points[k].get_x() > max_extent.get_x()) max_extent.set_x(screen_points[k].get_x()); + if (screen_points[k].get_y() > max_extent.get_y()) max_extent.set_y(screen_points[k].get_y()); + + if (screen_points[k].get_x() < min_extent.get_x()) min_extent.set_x(screen_points[k].get_x()); + if (screen_points[k].get_y() < min_extent.get_y()) min_extent.set_y(screen_points[k].get_y()); + + // Find min / max projected depth to adjust far plane + if (proj_point.get_y() > max_extent.get_z()) max_extent.set_z(proj_point.get_y()); + if (proj_point.get_y() < min_extent.get_z()) min_extent.set_z(proj_point.get_y()); + } +} + +/** + * @brief Computes a film size from a given minimum and maximum extend + * @details This takes a minimum and maximum extent in NDC space and computes + * the film size and film offset needed to cover that extent. + * + * @param film_size Output film size, can be used for Lens::set_film_size + * @param film_offset Output film offset, can be used for Lens::set_film_offset + * @param min_extent Minimum extent + * @param max_extent Maximum extent + */ +inline void get_film_properties(LVecBase2 &film_size, LVecBase2 &film_offset, const LVecBase3 &min_extent, const LVecBase3 &max_extent) { + float x_center = (min_extent.get_x() + max_extent.get_x()) * 0.5; + float y_center = (min_extent.get_y() + max_extent.get_y()) * 0.5; + float x_size = max_extent.get_x() - x_center; + float y_size = max_extent.get_y() - y_center; + film_size.set(x_size, y_size); + film_offset.set(x_center * 0.5, y_center * 0.5); +} + +/** + * @brief Merges two arrays + * @details This takes two arrays which each 4 members and produces an array + * with both arrays contained. + * + * @param dest Destination array + * @param array1 First array + * @param array2 Second array + */ +inline void merge_points_interleaved(LVecBase3 (&dest)[8], LVecBase3 const (&array1)[4], LVecBase3 const (&array2)[4]) { + for (size_t k = 0; k < 4; ++k) { + dest[k] = array1[k]; + dest[k+4] = array2[k]; + } +} + + +/** + * @brief Internal method to compute the splits + * @details This is the internal update method to update the PSSM splits. + * It distributes the camera splits over the frustum, and updates the + * MVP array aswell as the nearfar array. + * + * @param transform Main camera transform + * @param max_distance Maximum pssm distance, relative to the camera far plane + * @param light_vector Sun-Vector + */ +void PSSMCameraRig::compute_pssm_splits(const LMatrix4& transform, float max_distance, const LVecBase3& light_vector) { + nassertv(!_parent.is_empty()); + + // PSSM Distance should never be smaller than camera far plane. + nassertv(max_distance <= 1.0); + + float filmsize_bias = 1.0 + _border_bias; + + // Compute the positions of all cameras + for (size_t i = 0; i < _cam_nodes.size(); ++i) { + float split_start = get_split_start(i) * max_distance; + float split_end = get_split_start(i + 1) * max_distance; + + LVecBase3 start_points[4]; + LVecBase3 end_points[4]; + LVecBase3 proj_points[8]; + + // Get split bounding box, and collect all points which define the frustum + for (size_t k = 0; k < 4; ++k) { + start_points[k] = get_interpolated_point((CoordinateOrigin)k, split_start); + end_points[k] = get_interpolated_point((CoordinateOrigin)k, split_end); + proj_points[k] = start_points[k]; + proj_points[k + 4] = end_points[k]; + } + + // Compute approximate split mid point + LPoint3 split_mid = get_average_of_points(start_points, end_points); + LPoint3 cam_start = split_mid + light_vector * _sun_distance; + + // Reset the film size, offset and far-plane + Camera* cam = DCAST(Camera, _cam_nodes[i].node()); + cam->get_lens()->set_film_size(1, 1); + cam->get_lens()->set_film_offset(0, 0); + cam->get_lens()->set_near_far(1, 100); + + // Find a good initial position + _cam_nodes[i].set_pos(cam_start); + _cam_nodes[i].look_at(split_mid); + + LVecBase3 best_min_extent, best_max_extent; + + // Find minimum and maximum extents of the points + LMatrix4 merged_transform = _parent.get_transform(_cam_nodes[i])->get_mat(); + find_min_max_extents(best_min_extent, best_max_extent, merged_transform, proj_points, cam); + + // Find the film size to cover all points + LVecBase2 film_size, film_offset; + get_film_properties(film_size, film_offset, best_min_extent, best_max_extent); + + if (_use_fixed_film_size) { + // In case we use a fixed film size, store the maximum film size, and + // only change the film size if a new maximum is there + if (_max_film_sizes[i].get_x() < film_size.get_x()) _max_film_sizes[i].set_x(film_size.get_x()); + if (_max_film_sizes[i].get_y() < film_size.get_y()) _max_film_sizes[i].set_y(film_size.get_y()); + + cam->get_lens()->set_film_size(_max_film_sizes[i] * filmsize_bias); + } else { + // If we don't use a fixed film size, we can just set the film size + // on the lens. + cam->get_lens()->set_film_size(film_size * filmsize_bias); + } + + // Compute new film offset + cam->get_lens()->set_film_offset(film_offset); + cam->get_lens()->set_near_far(10, best_max_extent.get_z()); + _camera_nearfar[i] = LVecBase2(10, best_max_extent.get_z()); + + // Compute the camera MVP + LMatrix4 mvp = compute_mvp(i); + + // Stable CSM Snapping + if (_use_stable_csm) { + LPoint3 snap_offset = get_snap_offset(mvp, _resolution); + _cam_nodes[i].set_pos(_cam_nodes[i].get_pos() + snap_offset); + + // Compute the new mvp, since we changed the snap offset + mvp = compute_mvp(i); + } + + _camera_mvps.set_element(i, mvp); + } +} + + +/** + * @brief Updates the PSSM camera rig + * @details This updates the rig with an updated camera position, and a given + * light vector. This should be called on a per-frame basis. It will reposition + * all camera sources to fit the frustum based on the pssm distribution. + * + * The light vector should be the vector from the light source, not the + * vector to the light source. + * + * @param cam_node Target camera node + * @param light_vector The vector from the light to any point + */ +void PSSMCameraRig::update(NodePath cam_node, const LVecBase3 &light_vector) { + nassertv(!cam_node.is_empty()); + _update_collector.start(); + + // Get camera node transform + LMatrix4 transform = cam_node.get_transform()->get_mat(); + + // Get Camera and Lens pointers + Camera* cam = DCAST(Camera, cam_node.get_child(0).node()); + nassertv(cam != nullptr); + Lens* lens = cam->get_lens(); + + // Extract near and far points: + lens->extrude(LPoint2(-1, 1), _curr_near_points[UpperLeft], _curr_far_points[UpperLeft]); + lens->extrude(LPoint2(1, 1), _curr_near_points[UpperRight], _curr_far_points[UpperRight]); + lens->extrude(LPoint2(-1, -1), _curr_near_points[LowerLeft], _curr_far_points[LowerLeft]); + lens->extrude(LPoint2(1, -1), _curr_near_points[LowerRight], _curr_far_points[LowerRight]); + + // Construct MVP to project points to world space + LMatrix4 mvp = transform * lens->get_view_mat(); + + // Project all points to world space + for (size_t i = 0; i < 4; ++i) { + LPoint4 ws_near = mvp.xform(_curr_near_points[i]); + LPoint4 ws_far = mvp.xform(_curr_far_points[i]); + _curr_near_points[i].set(ws_near.get_x(), ws_near.get_y(), ws_near.get_z()); + _curr_far_points[i].set(ws_far.get_x(), ws_far.get_y(), ws_far.get_z()); + } + + // Do the actual PSSM + compute_pssm_splits( transform, _pssm_distance / lens->get_far(), light_vector ); + + _update_collector.stop(); +} + diff --git a/contrib/src/rplight/pssmCameraRig.h b/contrib/src/rplight/pssmCameraRig.h new file mode 100644 index 0000000000..ce0567bbbf --- /dev/null +++ b/contrib/src/rplight/pssmCameraRig.h @@ -0,0 +1,125 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef PSSMCAMERARIG_H +#define PSSMCAMERARIG_H + +#include "pandabase.h" +#include "luse.h" +#include "camera.h" +#include "nodePath.h" +#include "pStatCollector.h" + +#include + +/** + * @brief Main class used for handling PSSM + * @details This is the main class for supporting PSSM, it is used by the PSSM + * plugin to compute the position of the splits. + * + * It supports handling a varying amount of cameras, and fitting those cameras + * into the main camera frustum, to render distant shadows. It also supports + * various optimizations for fitting the frustum, e.g. rotating the sources + * to get a better coverage. + * + * It also provides methods to get arrays of data about the used cameras + * view-projection matrices and their near and far plane, which is required for + * processing the data in the shadow sampling shader. + * + * In this class, there is often referred to "Splits" or also called "Cascades". + * These denote the different cameras which are used to split the frustum, + * and are a common term related to the PSSM algorithm. + * + * To understand the functionality of this class, a detailed knowledge of the + * PSSM algorithm is helpful. + */ +class PSSMCameraRig { +PUBLISHED: + PSSMCameraRig(size_t num_splits); + ~PSSMCameraRig(); + + inline void set_pssm_distance(float distance); + inline void set_sun_distance(float distance); + inline void set_use_fixed_film_size(bool flag); + inline void set_resolution(size_t resolution); + inline void set_use_stable_csm(bool flag); + inline void set_logarithmic_factor(float factor); + inline void set_border_bias(float bias); + + void update(NodePath cam_node, const LVecBase3 &light_vector); + inline void reset_film_size_cache(); + + inline NodePath get_camera(size_t index); + + void reparent_to(NodePath parent); + inline const PTA_LMatrix4 &get_mvp_array(); + inline const PTA_LVecBase2 &get_nearfar_array(); + +public: + // Used to access the near and far points in the array + enum CoordinateOrigin { + UpperLeft = 0, + UpperRight, + LowerLeft, + LowerRight + }; + +protected: + void init_cam_nodes(); + void compute_pssm_splits(const LMatrix4& transform, float max_distance, + const LVecBase3 &light_vector); + inline float get_split_start(size_t split_index); + LMatrix4 compute_mvp(size_t cam_index); + inline LPoint3 get_interpolated_point(CoordinateOrigin origin, float depth); + LVecBase3 get_snap_offset(const LMatrix4& mat, size_t resolution); + + vector _cam_nodes; + vector _cameras; + vector _max_film_sizes; + + // Current near and far points + // Order: UL, UR, LL, LR (See CoordinateOrigin) + LPoint3 _curr_near_points[4]; + LPoint3 _curr_far_points[4]; + float _pssm_distance; + float _sun_distance; + float _logarithmic_factor; + float _border_bias; + bool _use_fixed_film_size; + bool _use_stable_csm; + size_t _resolution; + size_t _num_splits; + NodePath _parent; + + PTA_LMatrix4 _camera_mvps; + PTA_LVecBase2 _camera_nearfar; + + static PStatCollector _update_collector; +}; + +#include "pssmCameraRig.I" + +#endif // PSSMCAMERARIG_H diff --git a/contrib/src/rplight/rpLight.I b/contrib/src/rplight/rpLight.I new file mode 100644 index 0000000000..9b21906310 --- /dev/null +++ b/contrib/src/rplight/rpLight.I @@ -0,0 +1,406 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +/** + * @brief Returns the amount of shadow sources + * @details This returns the amount of shadow sources attached to this light. + * In case the light has no shadows enabled, or the light was not attached + * yet, this returns 0. + * + * @return Amount of shadow sources + */ +inline int RPLight::get_num_shadow_sources() const { + return _shadow_sources.size(); +} + +/** + * @brief Returns the n-th shadow source + * @details This returns the n-th attached shadow source. This ranges from + * 0 .. RPLight::get_num_shadow_sources(). If an invalid index is passed, + * an assertion is thrown. + * + * @param index Index of the source + * @return Handle to the shadow source + */ +inline ShadowSource* RPLight::get_shadow_source(size_t index) const { + nassertr(index < _shadow_sources.size(), nullptr); // Invalid shadow source index + return _shadow_sources[index]; +} + +/** + * @brief Clears all shadow source + * @details This removes and destructs all shadow sources attached to this light. + * This usually gets called when the light gets detached or destructed. + * All shadows sources are freed, and then removed from the shadow source list. + */ +inline void RPLight::clear_shadow_sources() { + for (size_t i = 0; i < _shadow_sources.size(); ++i) { + delete _shadow_sources[i]; + } + _shadow_sources.clear(); +} + +/** + * @brief Sets whether the light needs an update + * @details This controls whether the light needs to get an update. This is the + * case when a property of the light changed, e.g. position or color. It does + * not affect the shadows (For that use RPLight::invalidate_shadows()). + * When this flag is set to true, the light will get resubmitted to the GPU + * in the next update cycle. + * + * You should usually never set the flag to false manually. The + * InternalLightManager will do this when the data got sucessfully updated. + * + * @param flag Update-Flag + */ +inline void RPLight::set_needs_update(bool flag) { + _needs_update = flag; +} + +/** + * @brief Returns whether the light needs an update + * @details This returns whether the light needs an update. This might be the + * case when a property of the light was changed, e.g. position or color. + * It does not affect the shadows, you have to query the update flag of each + * individual source for that. + * The return value is the value previously set with RPLight::set_needs_update. + * + * @return Update-flag + */ +inline bool RPLight::get_needs_update() const { + return _needs_update; +} + +/** + * @brief Returns whether the light has a slot + * @details This returns wheter the light currently is attached, and thus has + * a slot in the InternalLightManagers light list. When the light is attached, + * this returns true, otherwise it will return false. + * + * @return true if the light has a slot, false otherwise + */ +inline bool RPLight::has_slot() const { + return _slot >= 0; +} + +/** + * @brief Returns the slot of the light + * @details This returns the slot of the light. This is the space on the GPU + * where the light is stored. If the light is not attached yet, this will + * return -1, otherwise the index of the light. + * + * @return Light-Slot + */ +inline int RPLight::get_slot() const { + return _slot; +} + +/** + * @brief Removes the light slot + * @details This is an internal method to remove the slot of the light. It gets + * called by the InternalLightManager when a light gets detached. It internally + * sets the slot to -1 to indicate the light is no longer attached. + */ +inline void RPLight::remove_slot() { + _slot = -1; +} + +/** + * @brief Assigns a slot to the light + * @details This assigns a slot to the light, marking it as attached. The slot + * relates to the index in the GPU's storage of lights. This is an internal + * method called by the InternalLightManager when the light got attached. + * + * @param slot Slot of the light + */ +inline void RPLight::assign_slot(int slot) { + _slot = slot; +} + +/** + * @brief Invalidates the shadows + * @details This invalidates all shadows of the light, causing them to get + * regenerated. This might be the case when the lights position or similar + * changed. This will cause all shadow sources to be updated, emitting a + * shadow update. Be careful when calling this method if you don't want all + * sources to get updated. If you only have to invalidate a single shadow source, + * use get_shadow_source(n)->set_needs_update(true). + */ +inline void RPLight::invalidate_shadows() { + for (size_t i = 0; i < _shadow_sources.size(); ++i) { + _shadow_sources[i]->set_needs_update(true); + } +} + +/** + * @brief Sets the position of the light + * @details This sets the position of the light in world space. It will cause + * the light to get invalidated, and resubmitted to the GPU. + * + * @param pos Position in world space + */ +inline void RPLight::set_pos(const LVecBase3 &pos) { + set_pos(pos.get_x(), pos.get_y(), pos.get_z()); +} + +/** + * @brief Sets the position of the light + * @details @copydetails RPLight::set_pos(const LVecBase3 &pos) + * + * @param x X-component of the position + * @param y Y-component of the position + * @param z Z-component of the position + */ +inline void RPLight::set_pos(float x, float y, float z) { + _position.set(x, y, z); + set_needs_update(true); + invalidate_shadows(); +} + +/** + * @brief Returns the position of the light + * @details This returns the position of the light previously set with + * RPLight::set_pos(). The returned position is in world space. + * @return Light-position + */ +inline const LVecBase3& RPLight::get_pos() const { + return _position; +} + +/** + * @brief Sets the lights color + * @details This sets the lights color. The color should not include the brightness + * of the light, you should control that with the energy. The color specifies + * the lights "tint" and will get multiplied with its specular and diffuse + * contribution. + * + * The color will be normalized by dividing by the colors luminance. Setting + * higher values than 1.0 will have no effect. + * + * @param color Light color + */ +inline void RPLight::set_color(const LVecBase3 &color) { + _color = color; + _color /= 0.2126 * color.get_x() + 0.7152 * color.get_y() + 0.0722 * color.get_z(); + set_needs_update(true); +} + +/** + * @brief Sets the lights color + * @details @copydetails RPLight::set_color(const LVecBase3 &color) + * + * @param r Red-component of the color + * @param g Green-component of the color + * @param b Blue-component of the color + */ +inline void RPLight::set_color(float r, float g, float b) { + set_color(LVecBase3(r, g, b)); +} + +/** + * @brief Returns the lights color + * @details This returns the light color, previously set with RPLight::set_color. + * This does not include the energy of the light. It might differ from what + * was set with set_color, because the color is normalized by dividing it + * by its luminance. + * @return Light-color + */ +inline const LVecBase3& RPLight::get_color() const { + return _color; +} + +/** + * @brief Sets the energy of the light + * @details This sets the energy of the light, which can be seen as the brightness + * of the light. It will get multiplied with the normalized color. + * + * @param energy energy of the light + */ +inline void RPLight::set_energy(float energy) { + _energy = energy; + set_needs_update(true); +} + +/** + * @brief Returns the energy of the light + * @details This returns the energy of the light, previously set with + * RPLight::set_energy. + * + * @return energy of the light + */ +inline float RPLight::get_energy() const { + return _energy; +} + +/** + * @brief Returns the type of the light + * @details This returns the internal type of the light, which was specified + * in the lights constructor. This can be used to distinguish between light + * types. + * @return Type of the light + */ +inline RPLight::LightType RPLight::get_light_type() const { + return _light_type; +} + +/** + * @brief Controls whether the light casts shadows + * @details This sets whether the light casts shadows. You can not change this + * while the light is attached. When flag is set to true, the light will be + * setup to cast shadows, spawning shadow sources based on the lights type. + * If the flag is set to false, the light will be inddicated to cast no shadows. + * + * @param flag Whether the light casts shadows + */ +inline void RPLight::set_casts_shadows(bool flag) { + if (has_slot()) { + cerr << "Light is already attached, can not call set_casts_shadows!" << endl; + return; + } + _casts_shadows = flag; +} + +/** + * @brief Returns whether the light casts shadows + * @details This returns whether the light casts shadows, the returned value + * is the one previously set with RPLight::set_casts_shadows. + * + * @return true if the light casts shadows, false otherwise + */ +inline bool RPLight::get_casts_shadows() const { + return _casts_shadows; +} + +/** + * @brief Sets the lights shadow map resolution + * @details This sets the lights shadow map resolution. This has no effect + * when the light is not told to cast shadows (Use RPLight::set_casts_shadows). + * + * When calling this on a light with multiple shadow sources (e.g. PointLight), + * this controls the resolution of each source. If the light has 6 shadow sources, + * and you use a resolution of 512x512, the lights shadow map will occur a + * space of 6 * 512x512 maps in the shadow atlas. + * + * @param resolution Resolution of the shadow map in pixels + */ +inline void RPLight::set_shadow_map_resolution(size_t resolution) { + nassertv(resolution >= 32 && resolution <= 16384); + _source_resolution = resolution; + invalidate_shadows(); +} + +/** + * @brief Returns the shadow map resolution + * @details This returns the shadow map resolution of each source of the light. + * If the light is not setup to cast shadows, this value is meaningless. + * The returned value is the one previously set with RPLight::set_shadow_map_resolution. + * + * @return Shadow map resolution in pixels + */ +inline size_t RPLight::get_shadow_map_resolution() const { + return _source_resolution; +} + +/** + * @brief Sets the ies profile + * @details This sets the ies profile of the light. The parameter should be a + * handle previously returned by RenderPipeline.load_ies_profile. Using a + * value of -1 indicates no ies profile. + * + * Notice that for ies profiles which cover a whole range, you should use + * PointLights, whereas for ies profiles which only cover the lower hemisphere + * you should use SpotLights for the best performance. + * + * @param profile IES Profile handle + */ +inline void RPLight::set_ies_profile(int profile) { + _ies_profile = profile; + set_needs_update(true); +} + +/** + * @brief Returns the lights ies profile + * @details This returns the ies profile of a light, previously set with + * RPLight::set_ies_profile. In case no ies profile was set, returns -1. + * + * @return IES Profile handle + */ +inline int RPLight::get_ies_profile() const { + return _ies_profile; +} + +/** + * @brief Returns whether the light has an ies profile assigned + * @details This returns whether the light has an ies profile assigned, + * previously done with RPLight::set_ies_profile. + * + * @return true if the light has an ies profile assigned, false otherwise + */ +inline bool RPLight::has_ies_profile() const { + return _ies_profile >= 0; +} + +/** + * @brief Clears the ies profile + * @details This clears the ies profile of the light, telling it to no longer + * use an ies profile, and instead use the default attenuation. + */ +inline void RPLight::clear_ies_profile() { + set_ies_profile(-1); +} + +/** + * @brief Sets the near plane of the light + * @details This sets the near plane of all shadow sources of the light. It has + * no effects if the light does not cast shadows. This prevents artifacts from + * objects near to the light. It behaves like Lens::set_near_plane. + * + * It can also help increasing shadow map precision, low near planes will + * cause the precision to suffer. Try setting the near plane as big as possible. + * + * If a negative or zero near plane is passed, an assertion is thrown. + * + * @param near_plane Near-plane + */ +inline void RPLight::set_near_plane(float near_plane) { + nassertv(near_plane > 0.00001); + _near_plane = near_plane; + invalidate_shadows(); +} + +/** + * @brief Returns the near plane of the light + * @details This returns the lights near plane, previously set with + * RPLight::set_near_plane. If the light does not cast shadows, this value + * is meaningless. + * + * @return Near-plane + */ +inline float RPLight::get_near_plane() const { + return _near_plane; +} + diff --git a/contrib/src/rplight/rpLight.cxx b/contrib/src/rplight/rpLight.cxx new file mode 100644 index 0000000000..1dcb92a3d0 --- /dev/null +++ b/contrib/src/rplight/rpLight.cxx @@ -0,0 +1,137 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "rpLight.h" + + +/** + * @brief Constructs a new light with the given type + * @details This constructs a new base light with the given light type. + * Sub-Classes should call this to initialize all properties. + * + * @param light_type Type of the light + */ +RPLight::RPLight(LightType light_type) { + _light_type = light_type; + _needs_update = false; + _casts_shadows = false; + _slot = -1; + _position.fill(0); + _color.fill(1); + _ies_profile = -1; + _source_resolution = 512; + _near_plane = 0.5; + _energy = 20.0; +} + +/** + * @brief Writes the light to a GPUCommand + * @details This writes all of the lights data to the given GPUCommand handle. + * Subclasses should first call this method, and then append their own + * data. This makes sure that for unpacking a light, no information about + * the type of the light is required. + * + * @param cmd The GPUCommand to write to + */ +void RPLight::write_to_command(GPUCommand &cmd) { + cmd.push_int(_light_type); + cmd.push_int(_ies_profile); + + if (_casts_shadows) { + // If we casts shadows, write the index of the first source, we expect + // them to be consecutive + nassertv(_shadow_sources.size() >= 0); + nassertv(_shadow_sources[0]->has_slot()); + cmd.push_int(_shadow_sources[0]->get_slot()); + } else { + // If we cast no shadows, just push a negative number + cmd.push_int(-1); + } + + cmd.push_vec3(_position); + + // Get the lights color by multiplying color with energy. Divide by + // 100, since 16bit floating point buffers only go up to 65000.0, which + // prevents very bright lights + cmd.push_vec3(_color * _energy / 100.0); +} + +/** + * @brief Light destructor + * @details This destructs the light, cleaning up all resourced used. The light + * should be detached at this point, because while the Light is attached, + * the InternalLightManager holds a reference to prevent it from being + * destructed. + */ +RPLight::~RPLight() { + nassertv(!has_slot()); // Light still attached - should never happen + clear_shadow_sources(); +} + +/** + * @brief Sets the lights color from a given color temperature + * @details This sets the lights color, given a temperature. This is more + * physically based than setting a user defined color. The color will be + * computed from the given temperature. + * + * @param temperature Light temperature + */ +void RPLight::set_color_from_temperature(float temperature) { + + // Thanks to rdb for this conversion script + float mm = 1000.0 / temperature; + float mm2 = mm * mm; + float mm3 = mm2 * mm; + float x, y; + + if (temperature < 4000) { + x = -0.2661239 * mm3 - 0.2343580 * mm2 + 0.8776956 * mm + 0.179910; + } else { + x = -3.0258469 * mm3 + 2.1070379 * mm2 + 0.2226347 * mm + 0.240390; + } + + float x2 = x * x; + float x3 = x2 * x; + if (temperature < 2222) { + y = -1.1063814 * x3 - 1.34811020 * x2 + 2.18555832 * x - 0.20219683; + } else if (temperature < 4000) { + y = -0.9549476 * x3 - 1.37418593 * x2 + 2.09137015 * x - 0.16748867; + } else { + y = 3.0817580 * x3 - 5.87338670 * x2 + 3.75112997 * x - 0.37001483; + } + + // xyY to XYZ, assuming Y=1. + LVecBase3 xyz(x / y, 1, (1 - x - y) / y); + + // Convert XYZ to linearized sRGB. + const static LMatrix3 xyz_to_rgb( + 3.2406, -0.9689, 0.0557, + -1.5372, 1.8758, -0.2050, + -0.4986, 0.0415, 1.0570); + + set_color(xyz_to_rgb.xform(xyz)); +} diff --git a/contrib/src/rplight/rpLight.h b/contrib/src/rplight/rpLight.h new file mode 100644 index 0000000000..13665c23ad --- /dev/null +++ b/contrib/src/rplight/rpLight.h @@ -0,0 +1,130 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef RPLIGHT_H +#define RPLIGHT_H + +#include "referenceCount.h" +#include "luse.h" +#include "gpuCommand.h" +#include "shadowSource.h" + +/** + * @brief Base class for Lights + * @details This is the base class for all lights in the render pipeline. It + * stores common properties, and provides methods to modify these. + * It also defines some interface functions which subclasses have to implement. + */ +class RPLight : public ReferenceCount { +PUBLISHED: + /** + * Different types of light. + */ + enum LightType { + LT_empty = 0, + LT_point_light = 1, + LT_spot_light = 2, + }; + +public: + RPLight(LightType light_type); + virtual ~RPLight(); + + virtual void init_shadow_sources() = 0; + virtual void update_shadow_sources() = 0; + virtual void write_to_command(GPUCommand &cmd); + + inline int get_num_shadow_sources() const; + inline ShadowSource* get_shadow_source(size_t index) const; + inline void clear_shadow_sources(); + + inline void set_needs_update(bool flag); + inline bool get_needs_update() const; + + inline bool has_slot() const; + inline int get_slot() const; + inline void remove_slot(); + inline void assign_slot(int slot); + +PUBLISHED: + inline void invalidate_shadows(); + + inline void set_pos(const LVecBase3 &pos); + inline void set_pos(float x, float y, float z); + inline const LVecBase3& get_pos() const; + MAKE_PROPERTY(pos, get_pos, set_pos); + + inline void set_color(const LVecBase3 &color); + inline void set_color(float r, float g, float b); + inline const LVecBase3& get_color() const; + MAKE_PROPERTY(color, get_color, set_color); + + void set_color_from_temperature(float temperature); + + inline void set_energy(float energy); + inline float get_energy() const; + MAKE_PROPERTY(energy, get_energy, set_energy); + + inline LightType get_light_type() const; + MAKE_PROPERTY(light_type, get_light_type); + + inline void set_casts_shadows(bool flag = true); + inline bool get_casts_shadows() const; + MAKE_PROPERTY(casts_shadows, get_casts_shadows, set_casts_shadows); + + inline void set_shadow_map_resolution(size_t resolution); + inline size_t get_shadow_map_resolution() const; + MAKE_PROPERTY(shadow_map_resolution, get_shadow_map_resolution, set_shadow_map_resolution); + + inline void set_ies_profile(int profile); + inline int get_ies_profile() const; + inline bool has_ies_profile() const; + inline void clear_ies_profile(); + MAKE_PROPERTY2(ies_profile, has_ies_profile, get_ies_profile, + set_ies_profile, clear_ies_profile); + + inline void set_near_plane(float near_plane); + inline float get_near_plane() const; + MAKE_PROPERTY(near_plane, get_near_plane, set_near_plane); + +protected: + int _slot; + int _ies_profile; + size_t _source_resolution; + bool _needs_update; + bool _casts_shadows; + LVecBase3 _position; + LVecBase3 _color; + float _energy; + LightType _light_type; + float _near_plane; + + vector _shadow_sources; +}; + +#include "rpLight.I" + +#endif // RP_LIGHT_H diff --git a/contrib/src/rplight/rpPointLight.I b/contrib/src/rplight/rpPointLight.I new file mode 100644 index 0000000000..9daf29d598 --- /dev/null +++ b/contrib/src/rplight/rpPointLight.I @@ -0,0 +1,82 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +/** + * @brief Sets the radius of the light + * @details This sets the radius of the light. It controls the lights + * influence. After a distance greater than this radius, the light influence + * is zero. + * + * @param radius Light radius in world space + */ +inline void RPPointLight::set_radius(float radius) { + nassertv(radius > 0); // Invalid light radius + _radius = radius; + set_needs_update(true); + invalidate_shadows(); +} + +/** + * @brief Returns the lights radius + * @details This returns the lights radius previously set with + * RPPointLight::set_radius + * @return Light radius in world space + */ +inline float RPPointLight::get_radius() const { + return _radius; +} + +/** + * @brief Sets the inner radius of the light + * @details This sets the inner radius of the light. Anything greater than + * zero causes the light to get an area light. This has influence on the + * specular highlights of the light aswell as the shadows. + * + * The inner radius controls the size of the lights sphere size in world + * space units. A radius of 0 means the light has no inner radius, and the + * light will be have like an infinite small point light source. + * A radius greater than zero will cause the light to behave like it would be + * an emissive sphere with the given inner radius emitting light. This is + * more physically correct. + * + * @param inner_radius Inner-radius in world space + */ +inline void RPPointLight::set_inner_radius(float inner_radius) { + nassertv(inner_radius >= 0.01); // Invalid inner radius + _inner_radius = inner_radius; + set_needs_update(true); +} + +/** + * @brief Returns the inner radius of the light + * @details This returns the inner radius of the light, previously set with + * RPPointLight::get_inner_radius. + * @return [description] + */ +inline float RPPointLight::get_inner_radius() const { + return _inner_radius; +} diff --git a/contrib/src/rplight/rpPointLight.cxx b/contrib/src/rplight/rpPointLight.cxx new file mode 100644 index 0000000000..f4f0551d8f --- /dev/null +++ b/contrib/src/rplight/rpPointLight.cxx @@ -0,0 +1,90 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "rpPointLight.h" + + +/** + * @brief Constructs a new point light + * @details This contructs a new point light with default settings. By default + * the light is set to be an infinitely small point light source. You can + * change this with RPPointLight::set_inner_radius. + */ +RPPointLight::RPPointLight() : RPLight(RPLight::LT_point_light) { + _radius = 10.0; + _inner_radius = 0.01; +} + +/** + * @brief Writes the light to a GPUCommand + * @details This writes the point light data to a GPUCommand. + * @see RPLight::write_to_command + * + * @param cmd The target GPUCommand + */ +void RPPointLight::write_to_command(GPUCommand &cmd) { + RPLight::write_to_command(cmd); + cmd.push_float(_radius); + cmd.push_float(_inner_radius); +} + +/** + * @brief Inits the shadow sources of the light + * @details This inits all required shadow sources for the point light. + * @see RPLight::init_shadow_sources + */ +void RPPointLight::init_shadow_sources() { + nassertv(_shadow_sources.size() == 0); + // Create 6 shadow sources, one for each direction + for(size_t i = 0; i < 6; ++i) { + _shadow_sources.push_back(new ShadowSource()); + } +} + +/** + * @brief Updates the shadow sources + * @details This updates all shadow sources of the light. + * @see RPLight::update_shadow_sources + */ +void RPPointLight::update_shadow_sources() { + LVecBase3 directions[6] = { + LVecBase3( 1, 0, 0), + LVecBase3(-1, 0, 0), + LVecBase3( 0, 1, 0), + LVecBase3( 0, -1, 0), + LVecBase3( 0, 0, 1), + LVecBase3( 0, 0, -1) + }; + + // Increase fov to prevent artifacts at the shadow map transitions + const float fov = 90.0f + 3.0f; + for (size_t i = 0; i < _shadow_sources.size(); ++i) { + _shadow_sources[i]->set_resolution(get_shadow_map_resolution()); + _shadow_sources[i]->set_perspective_lens(fov, _near_plane, _radius, + _position, directions[i]); + } +} diff --git a/contrib/src/rplight/rpPointLight.h b/contrib/src/rplight/rpPointLight.h new file mode 100644 index 0000000000..3d933ef266 --- /dev/null +++ b/contrib/src/rplight/rpPointLight.h @@ -0,0 +1,63 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef RPPOINTLIGHT_H +#define RPPOINTLIGHT_H + +#include "pandabase.h" +#include "rpLight.h" + +/** + * @brief PointLight class + * @details This represents a point light, a light which has a position and + * radius. Checkout the RenderPipeline documentation for more information + * about this type of light. + */ +class RPPointLight : public RPLight { +PUBLISHED: + RPPointLight(); + + inline void set_radius(float radius); + inline float get_radius() const; + MAKE_PROPERTY(radius, get_radius, set_radius); + + inline void set_inner_radius(float inner_radius); + inline float get_inner_radius() const; + MAKE_PROPERTY(inner_radius, get_inner_radius, set_inner_radius); + +public: + virtual void write_to_command(GPUCommand &cmd); + virtual void update_shadow_sources(); + virtual void init_shadow_sources(); + +protected: + float _radius; + float _inner_radius; +}; + +#include "rpPointLight.I" + +#endif // RPPOINTLIGHT_H diff --git a/contrib/src/rplight/rpSpotLight.I b/contrib/src/rplight/rpSpotLight.I new file mode 100644 index 0000000000..d1edf8d30d --- /dev/null +++ b/contrib/src/rplight/rpSpotLight.I @@ -0,0 +1,74 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + + +inline void RPSpotLight::set_radius(float radius) { + _radius = radius; + set_needs_update(true); + invalidate_shadows(); +} + +inline float RPSpotLight::get_radius() const { + return _radius; +} + + +inline void RPSpotLight::set_fov(float fov) { + _fov = fov; + set_needs_update(true); + invalidate_shadows(); +} + +inline float RPSpotLight::get_fov() const { + return _fov; +} + +inline void RPSpotLight::set_direction(LVecBase3 direction) { + _direction = direction; + _direction.normalize(); + set_needs_update(true); + invalidate_shadows(); +} + +inline void RPSpotLight::set_direction(float dx, float dy, float dz) { + _direction.set(dx, dy, dz); + _direction.normalize(); + set_needs_update(true); + invalidate_shadows(); +} + +inline const LVecBase3& RPSpotLight::get_direction() const { + return _direction; +} + +inline void RPSpotLight::look_at(LVecBase3 point) { + set_direction(point - _position); +} + +inline void RPSpotLight::look_at(float x, float y, float z) { + set_direction(LVecBase3(x, y, z) - _position); +} diff --git a/contrib/src/rplight/rpSpotLight.cxx b/contrib/src/rplight/rpSpotLight.cxx new file mode 100644 index 0000000000..649e0b1beb --- /dev/null +++ b/contrib/src/rplight/rpSpotLight.cxx @@ -0,0 +1,80 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "rpSpotLight.h" + +#define _USE_MATH_DEFINES +#include + + +/** + * @brief Creates a new spot light + * @details This creates a new spot light with default properties set. You should + * set at least a direction, fov, radius and position to make the light useful. + */ +RPSpotLight::RPSpotLight() : RPLight(RPLight::LT_spot_light) { + _radius = 10.0; + _fov = 45.0; + _direction.set(0, 0, -1); +} + +/** + * @brief Writes the light to a GPUCommand + * @details This writes the spot light data to a GPUCommand. + * @see RPLight::write_to_command + * + * @param cmd The target GPUCommand + */ +void RPSpotLight::write_to_command(GPUCommand &cmd) { + RPLight::write_to_command(cmd); + cmd.push_float(_radius); + + // Encode FOV as cos(fov) + cmd.push_float(cos(_fov / 360.0 * M_PI)); + cmd.push_vec3(_direction); +} + +/** + * @brief Inits the shadow sources of the light + * @details This inits all required shadow sources for the spot light. + * @see RPLight::init_shadow_sources + */ +void RPSpotLight::init_shadow_sources() { + nassertv(_shadow_sources.size() == 0); + _shadow_sources.push_back(new ShadowSource()); +} + +/** + * @brief Updates the shadow sources + * @details This updates all shadow sources of the light. + * @see RPLight::update_shadow_sources + */ +void RPSpotLight::update_shadow_sources() { + _shadow_sources[0]->set_resolution(get_shadow_map_resolution()); + _shadow_sources[0]->set_perspective_lens(_fov, _near_plane, _radius, _position, _direction); +} + diff --git a/contrib/src/rplight/rpSpotLight.h b/contrib/src/rplight/rpSpotLight.h new file mode 100644 index 0000000000..d27e226950 --- /dev/null +++ b/contrib/src/rplight/rpSpotLight.h @@ -0,0 +1,71 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef RPSPOTLIGHT_H +#define RPSPOTLIGHT_H + +#include "pandabase.h" +#include "rpLight.h" + +/** + * @brief SpotLight class + * @details This represents a spot light, a light which has a position, radius, + * direction and FoV. Checkout the RenderPipeline documentation for more + * information about this type of light. + */ +class RPSpotLight : public RPLight { +PUBLISHED: + RPSpotLight(); + + inline void set_radius(float radius); + inline float get_radius() const; + MAKE_PROPERTY(radius, get_radius, set_radius); + + inline void set_fov(float fov); + inline float get_fov() const; + MAKE_PROPERTY(fov, get_fov, set_fov); + + inline void set_direction(LVecBase3 direction); + inline void set_direction(float dx, float dy, float dz); + inline const LVecBase3& get_direction() const; + inline void look_at(LVecBase3 point); + inline void look_at(float x, float y, float z); + MAKE_PROPERTY(direction, get_direction, set_direction); + +public: + virtual void write_to_command(GPUCommand &cmd); + virtual void init_shadow_sources(); + virtual void update_shadow_sources(); + +protected: + float _radius; + float _fov; + LVecBase3 _direction; +}; + +#include "rpSpotLight.I" + +#endif // RPSPOTLIGHT_H diff --git a/contrib/src/rplight/shadowAtlas.I b/contrib/src/rplight/shadowAtlas.I new file mode 100644 index 0000000000..f48fc451f1 --- /dev/null +++ b/contrib/src/rplight/shadowAtlas.I @@ -0,0 +1,159 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +/** + * @brief Returns the tile size of the atlas. + * @details This returns the tile size of the atlas, which was set in the + * constructor. This is the smalles unit of the atlas, and every resolution + * has to be a multiple of the tile size. + * @return Tile size in pixels + */ +inline int ShadowAtlas::get_tile_size() const { + return _tile_size; +} + +/** + * @brief Sets a specific tile status. + * @details This marks a tile to either reserved or free, depending on the flag. + * If flag is true, the tile gets marked as reserved. If flag is false, the + * tile gets marked as free. + * + * No bounds checking is done for performance reasons. Passing an invalid tile + * index causes a crash. The coordinates are expected to be in tile space. + * + * @param x x-position of the tile + * @param y y-position of the tile + * @param flag Flag to set the tile to + */ +inline void ShadowAtlas::set_tile(size_t x, size_t y, bool flag) { + _flags[x + y * _num_tiles] = flag; +} + +/** + * @brief Returns the status of a given tile. + * @details This returns the value of a tile. If the tile is true, this means + * the tile is already reserved. If the tile is false, the tile can be + * used, and is not reserved. + * + * No bounds checking is done for performance reasons. Passing an invalid tile + * index causes a crash. The coordinates are expected to be in tile space. + * + * @param x x-position of the tile + * @param y y-position of the tile + * + * @return Tile-Status + */ +inline bool ShadowAtlas::get_tile(size_t x, size_t y) const { + return _flags[x + y * _num_tiles]; +} + +/** + * @brief Checks wheter a given region is free. + * @details This checks whether a given region in the atlas is still free. This + * is true if *all* tiles in that region are false, and thus are not taken yet. + * The coordinates are expected to be in tile space. + * + * Passing an invalid region, causes an assertion, in case those are enabled. + * If assertions are optimized out, this method will crash when passing invalid + * bounds. + * + * @param x x- start position of the region + * @param y y- start position of the region + * @param w width of the region + * @param h height of the region + * @return true if the region is completely free, else false + */ +inline bool ShadowAtlas::region_is_free(size_t x, size_t y, size_t w, size_t h) const { + // Check if we are out of bounds, this should be disabled for performance + // reasons at some point. + nassertr(x >= 0 && y >= 0 && x + w <= _num_tiles && y + h <= _num_tiles, false); + + // Iterate over every tile in that region and check if it is still free. + for (size_t cx = 0; cx < w; ++cx) { + for (size_t cy = 0; cy < h; ++cy) { + if (get_tile(cx + x, cy + y)) return false; + } + } + return true; +} + +/** + * @brief Returns the amount of tiles required to store a resolution. + * @details Returns the amount of tiles which would be required to store a + * given resolution. This basically just returns resolution / tile_size. + * + * When an invalid resolution is passed (not a multiple of the tile size), + * an error is printed and 1 is returned. + * When a negative or zero resolution is passed, undefined behaviour occurs. + * + * @param resolution The resolution to compute the amount of tiles for + * @return Amount of tiles to store the resolution + */ +inline int ShadowAtlas::get_required_tiles(size_t resolution) const { + nassertr(resolution > 0, -1); + + if (resolution % _tile_size != 0) { + shadowatlas_cat.error() << "Resolution " << resolution << " is not a multiple " + << "of the shadow atlas tile size (" << _tile_size << ")!" << endl; + return 1; + } + return resolution / _tile_size; +} + +/** + * @brief Converts a tile-space region to uv space. + * @details This converts a region (presumably from ShadowAtlas::find_and_reserve_region) + * to uv space (0 .. 1 range). This can be used in shaders, since they expect + * floating point coordinates instead of integer coordinates. + * + * @param region tile-space region + * @return uv-space region + */ +inline LVecBase4 ShadowAtlas::region_to_uv(const LVecBase4i& region) { + LVecBase4 flt = LVecBase4(region.get_x(), region.get_y(), region.get_z(), region.get_w()); + return flt * ((float)_tile_size / (float)_size); +} + +/** + * @brief Returns the amount of used tiles + * @details Returns the amount of used tiles in the atlas + * @return Amount of used tiles + */ +inline int ShadowAtlas::get_num_used_tiles() const { + return _num_used_tiles; +} + +/** + * @brief Returns the amount of used tiles in percentage + * @details This returns in percentage from 0 to 1 how much space of the atlas + * is used right now. A value of 1 means the atlas is completely full, whereas + * a value of 0 means the atlas is completely free. + * @return Atlas usage in percentage + */ +inline float ShadowAtlas::get_coverage() const { + return float(_num_used_tiles) / float(_num_tiles * _num_tiles); +} diff --git a/contrib/src/rplight/shadowAtlas.cxx b/contrib/src/rplight/shadowAtlas.cxx new file mode 100644 index 0000000000..15d4ea1bd0 --- /dev/null +++ b/contrib/src/rplight/shadowAtlas.cxx @@ -0,0 +1,186 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "shadowAtlas.h" +#include + +NotifyCategoryDef(shadowatlas, ""); + +/** + * @brief Constructs a new shadow atlas. + * @details This constructs a new shadow atlas with the given size and tile size. + * + * The size determines the total size of the atlas in pixels. It should be a + * power-of-two to favour the GPU. + * + * The tile_size determines the smallest unit of tiles the atlas can store. + * If, for example, a tile_size of 32 is used, then every entry stored must + * have a resolution of 32 or greater, and the resolution must be a multiple + * of 32. This is to optimize the search in the atlas, so the atlas does not + * have to check every pixel, and instead can just check whole tiles. + * + * If you want to disable the use of tiles, set the tile_size to 1, which + * will make the shadow atlas use pixels instead of tiles. + * + * @param size Atlas-size in pixels + * @param tile_size tile-size in pixels, or 1 to use no tiles. + */ +ShadowAtlas::ShadowAtlas(size_t size, size_t tile_size) { + nassertv(size > 1 && tile_size >= 1); + nassertv(tile_size < size && size % tile_size == 0); + _size = size; + _tile_size = tile_size; + _num_used_tiles = 0; + init_tiles(); +} + +/** + * @brief Destructs the shadow atlas. + * @details This destructs the shadow atlas, freeing all used resources. + */ +ShadowAtlas::~ShadowAtlas() { + delete [] _flags; +} + +/** + * @brief Internal method to init the storage. + * @details This method setups the storage used for storing the tile flags. + */ +void ShadowAtlas::init_tiles() { + _num_tiles = _size / _tile_size; + _flags = new bool[_num_tiles * _num_tiles]; + memset(_flags, 0x0, sizeof(bool) * _num_tiles * _num_tiles); +} + +/** + * @brief Internal method to reserve a region in the atlas. + * @details This reserves a given region in the shadow atlas. The region should + * be in tile space.This is called by the ShadowAtlas::find_and_reserve_region. + * It sets all flags in that region to true, indicating that those are used. + * When an invalid region is passed, an assertion is triggered. If assertions + * are optimized out, undefined behaviour occurs. + * + * @param x x- start positition of the region + * @param y y- start position of the region + * @param w width of the region + * @param h height of the region + */ +void ShadowAtlas::reserve_region(size_t x, size_t y, size_t w, size_t h) { + // Check if we are out of bounds, this should be disabled for performance + // reasons at some point. + nassertv(x >= 0 && y >= 0 && x + w <= _num_tiles && y + h <= _num_tiles); + + _num_used_tiles += w * h; + + // Iterate over every tile in the region and mark it as used + for (size_t cx = 0; cx < w; ++cx) { + for (size_t cy = 0; cy < h; ++cy) { + set_tile(cx + x, cy + y, true); + } + } +} + +/** + * @brief Finds space for a map of the given size in the atlas. + * @details This methods searches for a space to store a region of the given + * size in the atlas. tile_width and tile_height should be already in tile + * space. They can be converted using ShadowAtlas::get_required_tiles. + * + * If no region is found, or an invalid size is passed, an integer vector with + * all components set to -1 is returned. + * + * If a region is found, an integer vector with the given layout is returned: + * x: x- Start of the region + * y: y- Start of the region + * z: width of the region + * w: height of the region + * + * The layout is in tile space, and can get converted to uv space using + * ShadowAtlas::region_to_uv. + * + * @param tile_width Width of the region in tile space + * @param tile_height Height of the region in tile space + * + * @return Region, see description, or -1 when no region is found. + */ +LVecBase4i ShadowAtlas::find_and_reserve_region(size_t tile_width, size_t tile_height) { + + // Check for empty region + if (tile_width < 1 || tile_height < 1) { + shadowatlas_cat.error() << "Called find_and_reserve_region with null-region!" << endl; + return LVecBase4i(-1); + } + + // Check for region bigger than the shadow atlas + if (tile_width > _num_tiles || tile_height > _num_tiles) { + shadowatlas_cat.error() << "Requested region exceeds shadow atlas size!" << endl; + return LVecBase4i(-1); + } + + // Iterate over every possible region and check if its still free + for (size_t x = 0; x <= _num_tiles - tile_width; ++x) { + for (size_t y = 0; y <= _num_tiles - tile_height; ++y) { + if (region_is_free(x, y, tile_width, tile_height)) { + // Found free region, now reserve it + reserve_region(x, y, tile_width, tile_height); + return LVecBase4i(x, y, tile_width, tile_height); + } + } + } + + // When we reached this part, we couldn't find a free region, so the atlas + // seems to be full. + shadowatlas_cat.error() << "Failed to find a free region of size " << tile_width + << " x " << tile_height << "!" << endl; + return LVecBase4i(-1); +} + +/** + * @brief Frees a given region + * @details This frees a given region, marking it as free so that other shadow + * maps can use the space again. The region should be the same as returned + * by ShadowAtlas::find_and_reserve_region. + * + * If an invalid region is passed, an assertion is triggered. If assertions + * are compiled out, undefined behaviour will occur. + * + * @param region Region to free + */ +void ShadowAtlas::free_region(const LVecBase4i& region) { + // Out of bounds check, can't hurt + nassertv(region.get_x() >= 0 && region.get_y() >= 0); + nassertv(region.get_x() + region.get_z() <= _num_tiles && region.get_y() + region.get_w() <= _num_tiles); + + _num_used_tiles -= region.get_z() * region.get_w(); + + for (size_t x = 0; x < region.get_z(); ++x) { + for (size_t y = 0; y < region.get_w(); ++y) { + // Could do an assert here, that the tile should have been used (=true) before + set_tile(region.get_x() + x, region.get_y() + y, false); + } + } +} diff --git a/contrib/src/rplight/shadowAtlas.h b/contrib/src/rplight/shadowAtlas.h new file mode 100644 index 0000000000..e3f0958a50 --- /dev/null +++ b/contrib/src/rplight/shadowAtlas.h @@ -0,0 +1,80 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef SHADOWATLAS_H +#define SHADOWATLAS_H + +#include "pandabase.h" +#include "lvecBase4.h" + +NotifyCategoryDecl(shadowatlas, EXPORT_CLASS, EXPORT_TEMPL); + + +/** + * @brief Class which manages distributing shadow maps in an atlas. + * @details This class manages the shadow atlas. It handles finding and reserving + * space for new shadow maps. + */ +class ShadowAtlas { +PUBLISHED: + ShadowAtlas(size_t size, size_t tile_size = 32); + ~ShadowAtlas(); + + inline int get_num_used_tiles() const; + inline float get_coverage() const; + + MAKE_PROPERTY(num_used_tiles, get_num_used_tiles); + MAKE_PROPERTY(coverage, get_coverage); + +public: + + LVecBase4i find_and_reserve_region(size_t tile_width, size_t tile_height); + void free_region(const LVecBase4i& region); + inline LVecBase4 region_to_uv(const LVecBase4i& region); + + inline int get_tile_size() const; + inline int get_required_tiles(size_t resolution) const; + +protected: + + void init_tiles(); + + inline void set_tile(size_t x, size_t y, bool flag); + inline bool get_tile(size_t x, size_t y) const; + + inline bool region_is_free(size_t x, size_t y, size_t w, size_t h) const; + void reserve_region(size_t x, size_t y, size_t w, size_t h); + + size_t _size; + size_t _num_tiles; + size_t _tile_size; + size_t _num_used_tiles; + bool* _flags; +}; + +#include "shadowAtlas.I" + +#endif // SHADOWATLAS_H diff --git a/contrib/src/rplight/shadowManager.I b/contrib/src/rplight/shadowManager.I new file mode 100644 index 0000000000..f5d87c463e --- /dev/null +++ b/contrib/src/rplight/shadowManager.I @@ -0,0 +1,192 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +/** + * @brief Sets the maximum amount of updates per frame. + * @details This controls the maximum amount of updated ShadowSources per frame. + * The ShadowManager will take the first ShadowSources, and + * generate shadow maps for them every frame. If there are more ShadowSources + * waiting to get updated than available updates, the sources are sorted by + * priority, and the update of the less important sources is delayed to the + * next frame. + * + * If the update count is set too low, and there are a lot of ShadowSources + * waiting to get updated, artifacts will occur, and there might be ShadowSources + * which never get updated, due to low priority. + * + * If an update count of 0 is passed, no updates will happen. This also means + * that there are no shadows. This is not recommended. + * + * If an update count < 0 is passed, undefined behaviour occurs. + * + * This method has to get called before ShadowManager::init, otherwise an + * assertion will get triggered. + * + * @param max_updates Maximum amoumt of updates + */ +inline void ShadowManager::set_max_updates(size_t max_updates) { + nassertv(max_updates >= 0); + nassertv(_atlas == nullptr); // ShadowManager was already initialized + if (max_updates == 0) { + shadowmanager_cat.warning() << "max_updates set to 0, no shadows will be updated." << endl; + } + _max_updates = max_updates; +} + +/** + * @brief Sets the shadow atlas size + * @details This sets the desired shadow atlas size. It should be big enough + * to store all important shadow sources, with some buffer, because the shadow + * maps usually won't be fitted perfectly, so gaps can occur. + * + * This has to get called before calling ShadowManager::init. When calling this + * method after initialization, an assertion will get triggered. + * + * @param atlas_size Size of the shadow atlas in pixels + */ +inline void ShadowManager::set_atlas_size(size_t atlas_size) { + nassertv(atlas_size >= 16 && atlas_size <= 16384); + nassertv(_atlas == nullptr); // ShadowManager was already initialized + _atlas_size = atlas_size; +} + +/** + * @brief Returns the shadow atlas size. + * @details This returns the shadow atlas size previously set with + * ShadowManager::set_atlas_size. + * @return Shadow atlas size in pixels + */ +inline size_t ShadowManager::get_atlas_size() const { + return _atlas_size; +} + + +/** + * @brief Returns a handle to the shadow atlas. + * @details This returns a handle to the internal shadow atlas instance. This + * is only valid after calling ShadowManager::init. Calling this earlier will + * trigger an assertion and undefined behaviour. + * @return The internal ShadowAtlas instance + */ +inline ShadowAtlas* ShadowManager::get_atlas() const { + nassertr(_atlas != nullptr, nullptr); // Can't hurt to check + return _atlas; +} + +/** + * @brief Sets the target scene + * @details This sets the target scene for rendering shadows. All shadow cameras + * will be parented to this scene to render shadows. + * + * Usually the scene will be ShowBase.render. If the scene is an empty or + * invalid NodePath, an assertion will be triggered. + * + * This method has to get called before calling ShadowManager::init, or an + * assertion will get triggered. + * + * @param scene_parent The target scene + */ +inline void ShadowManager::set_scene(NodePath scene_parent) { + nassertv(!scene_parent.is_empty()); + nassertv(_atlas == nullptr); // ShadowManager was already initialized + _scene_parent = scene_parent; +} + +/** + * @brief Sets the handle to the TagStageManager. + * @details This sets the handle to the TagStateManager used by the pipeline. + * Usually this is RenderPipeline.get_tag_mgr(). + * + * This has to get called before ShadowManager::init, otherwise an assertion + * will get triggered. + * + * @param tag_mgr [description] + */ +inline void ShadowManager::set_tag_state_manager(TagStateManager* tag_mgr) { + nassertv(tag_mgr != nullptr); + nassertv(_atlas == nullptr); // ShadowManager was already initialized + _tag_state_mgr = tag_mgr; +} + +/** + * @brief Sets the handle to the Shadow targets output + * @details This sets the handle to the GraphicsOutput of the shadow atlas. + * Usually this is RenderTarget.get_internal_buffer(), whereas the RenderTarget + * is the target of the ShadowStage. + * + * This is used for creating display regions and attaching cameras to them, + * for performing shadow updates. + * + * This has to get called before ShadowManager::init, otherwise an assertion + * will be triggered. + * + * @param graphics_output [description] + */ +inline void ShadowManager::set_atlas_graphics_output(GraphicsOutput* graphics_output) { + nassertv(graphics_output != nullptr); + nassertv(_atlas == nullptr); // ShadowManager was already initialized + _atlas_graphics_output = graphics_output; +} + + +/** + * @brief Adds a new shadow update + * @details This adds a new update to the update queue. When the queue is already + * full, this method returns false, otherwise it returns true. The next time + * the manager is updated, the shadow source will recieve an update of its + * shadow map. + * + * @param source The shadow source to update + * + * @return Whether the shadow source udpate was sucessfully queued. + */ +inline bool ShadowManager::add_update(const ShadowSource* source) { + nassertr(_atlas != nullptr, false); // ShadowManager::init not called yet. + nassertr(source != nullptr, false); // nullptr-Pointer passed + + if (_queued_updates.size() >= _max_updates) { + if (shadowmanager_cat.is_debug()) { + shadowmanager_cat.debug() << "cannot update source, out of update slots" << endl; + } + return false; + } + + // Add the update to the queue + _queued_updates.push_back(source); + return true; +} + +/** + * @brief Returns how many update slots are left. + * @details This returns how many update slots are left. You can assume the + * next n calls to add_update will succeed, whereas n is the value returned + * by this function. + * @return Number of update slots left. + */ +inline size_t ShadowManager::get_num_update_slots_left() const { + return _max_updates - _queued_updates.size(); +} diff --git a/contrib/src/rplight/shadowManager.cxx b/contrib/src/rplight/shadowManager.cxx new file mode 100644 index 0000000000..be216c0075 --- /dev/null +++ b/contrib/src/rplight/shadowManager.cxx @@ -0,0 +1,157 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "shadowManager.h" + +NotifyCategoryDef(shadowmanager, ""); + +/** + * @brief Constructs a new shadow atlas + * @details This constructs a new shadow atlas. There are a set of properties + * which should be set before calling ShadowManager::init, see the set-Methods. + * After all properties are set, ShadowManager::init should get called. + * ShadowManager::update should get called on a per frame basis. + */ +ShadowManager::ShadowManager() { + _max_updates = 10; + _atlas = nullptr; + _atlas_size = 4096; + _tag_state_mgr = nullptr; + _atlas_graphics_output = nullptr; +} + +/** + * @brief Destructs the ShadowManager + * @details This destructs the shadow manager, clearing all resources used + */ +ShadowManager::~ShadowManager() { + delete _atlas; + + // Todo: Could eventually unregister all shadow cameras. Since the tag state + // manager does this on cleanup already, and we get destructed at the same + // time (if at all), this is not really necessary +} + + +/** + * @brief Initializes the ShadowManager. + * @details This initializes the ShadowManager. All properties should have + * been set before calling this, otherwise assertions will get triggered. + * + * This setups everything required for rendering shadows, including the + * shadow atlas and the various shadow cameras. After calling this method, + * no properties can be changed anymore. + */ +void ShadowManager::init() { + nassertv(!_scene_parent.is_empty()); // Scene parent not set, call set_scene_parent before init! + nassertv(_tag_state_mgr != nullptr); // TagStateManager not set, call set_tag_state_mgr before init! + nassertv(_atlas_graphics_output != nullptr); // AtlasGraphicsOutput not set, call set_atlas_graphics_output before init! + + _cameras.resize(_max_updates); + _display_regions.resize(_max_updates); + _camera_nps.reserve(_max_updates); + + // Create the cameras and regions + for(size_t i = 0; i < _max_updates; ++i) { + + // Create the camera + PT(Camera) camera = new Camera("ShadowCam-" + format_string(i)); + camera->set_lens(new MatrixLens()); + camera->set_active(false); + camera->set_scene(_scene_parent); + _tag_state_mgr->register_camera("shadow", camera); + _camera_nps.push_back(_scene_parent.attach_new_node(camera)); + _cameras[i] = camera; + + // Create the display region + PT(DisplayRegion) region = _atlas_graphics_output->make_display_region(); + region->set_sort(1000); + region->set_clear_depth_active(true); + region->set_clear_depth(1.0); + region->set_clear_color_active(false); + region->set_camera(_camera_nps[i]); + region->set_active(false); + _display_regions[i] = region; + } + + // Create the atlas + _atlas = new ShadowAtlas(_atlas_size); + + // Reserve enough space for the updates + _queued_updates.reserve(_max_updates); +} + + +/** + * @brief Updates the ShadowManager + * @details This updates the ShadowManager, processing all shadow sources which + * need to get updated. + * + * This first collects all sources which require an update, sorts them by priority, + * and then processes the first ShadowSources. + * + * This may not get called before ShadowManager::init, or an assertion will be + * thrown. + */ +void ShadowManager::update() { + nassertv(_atlas != nullptr); // ShadowManager::init not called yet + nassertv(_queued_updates.size() <= _max_updates); // Internal error, should not happen + + // Disable all cameras and regions which will not be used + for (size_t i = _queued_updates.size(); i < _max_updates; ++i) { + _cameras[i]->set_active(false); + _display_regions[i]->set_active(false); + } + + // Iterate over all queued updates + for (size_t i = 0; i < _queued_updates.size(); ++i) { + const ShadowSource* source = _queued_updates[i]; + + // Enable the camera and display region, so they perform a render + _cameras[i]->set_active(true); + _display_regions[i]->set_active(true); + + // Set the view projection matrix + DCAST(MatrixLens, _cameras[i]->get_lens())->set_user_mat(source->get_mvp()); + + // Optional: Show the camera frustum for debugging + // _cameras[i]->show_frustum(); + + // Set the correct dimensions on the display region + const LVecBase4& uv = source->get_uv_region(); + _display_regions[i]->set_dimensions( + uv.get_x(), // left + uv.get_x() + uv.get_z(), // right + uv.get_y(), // bottom + uv.get_y() + uv.get_w() // top + ); + } + + // Clear the update list + _queued_updates.clear(); + _queued_updates.reserve(_max_updates); +} diff --git a/contrib/src/rplight/shadowManager.h b/contrib/src/rplight/shadowManager.h new file mode 100644 index 0000000000..279936b3bb --- /dev/null +++ b/contrib/src/rplight/shadowManager.h @@ -0,0 +1,91 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef SHADOWMANAGER_H +#define SHADOWMANAGER_H + +#include "pandabase.h" +#include "camera.h" +#include "luse.h" +#include "matrixLens.h" +#include "referenceCount.h" +#include "nodePath.h" +#include "displayRegion.h" +#include "graphicsOutput.h" + +#include "tagStateManager.h" +#include "shadowSource.h" +#include "shadowAtlas.h" + +NotifyCategoryDecl(shadowmanager, EXPORT_CLASS, EXPORT_TEMPL); + + +class ShadowManager : public ReferenceCount { +PUBLISHED: + ShadowManager(); + ~ShadowManager(); + + inline void set_max_updates(size_t max_updates); + inline void set_scene(NodePath scene_parent); + inline void set_tag_state_manager(TagStateManager* tag_mgr); + inline void set_atlas_graphics_output(GraphicsOutput* graphics_output); + + inline void set_atlas_size(size_t atlas_size); + inline size_t get_atlas_size() const; + MAKE_PROPERTY(atlas_size, get_atlas_size, set_atlas_size); + + inline size_t get_num_update_slots_left() const; + MAKE_PROPERTY(num_update_slots_left, get_num_update_slots_left); + + inline ShadowAtlas* get_atlas() const; + MAKE_PROPERTY(atlas, get_atlas); + + void init(); + void update(); + +public: + inline bool add_update(const ShadowSource* source); + +private: + size_t _max_updates; + size_t _atlas_size; + NodePath _scene_parent; + + pvector _cameras; + pvector _camera_nps; + pvector _display_regions; + + ShadowAtlas* _atlas; + TagStateManager* _tag_state_mgr; + GraphicsOutput* _atlas_graphics_output; + + typedef pvector UpdateQueue; + UpdateQueue _queued_updates; +}; + +#include "shadowManager.I" + +#endif // SHADOWMANAGER_H diff --git a/contrib/src/rplight/shadowSource.I b/contrib/src/rplight/shadowSource.I new file mode 100644 index 0000000000..e7ee9441b9 --- /dev/null +++ b/contrib/src/rplight/shadowSource.I @@ -0,0 +1,262 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + + +/** + * @brief Returns whether the shadow source needs an update. + * @details This returns the update flag, which was previously set with + * ShadowSource::set_needs_update. If the value is true, it means that the + * ShadowSource is invalid and should be regenerated. This can either be the + * case because the scene changed and affected the shadow source, or the light + * moved. + * @return Update-Flag + */ +inline bool ShadowSource::get_needs_update() const { + return !has_region() || _needs_update; +} + +/** + * @brief Returns the slot of the shadow source. + * @details This returns the assigned slot of the ShadowSource, or -1 if no slot + * was assigned yet. You can check if a slot exists with ShadowSource::has_slot. + * The slot is the index of the ShadowSource in the global source buffer. + * @return Slot, or -1 to indicate no slot. + */ +inline int ShadowSource::get_slot() const { + return _slot; +} + +/** + * @brief Returns whether the source has a slot. + * @details This returns whether the ShadowSource currently has an assigned slot. + * If the source has a slot assigned, this returns true, otherwise false. Cases + * where the source has no slot might be when the source just got attached, but + * never got rendered yet. + * @return [description] + */ +inline bool ShadowSource::has_slot() const { + return _slot >= 0; +} + +/** + * @brief Assigns the source a slot + * @details This assigns a slot to the ShadowSource. This is called from the + * ShadowManager, when the source gets attached first time. This should not + * get called by the user. + * + * @param slot Slot of the source, or -1 to indicate no slot. + */ +inline void ShadowSource::set_slot(int slot) { + _slot = slot; +} + +/** + * @brief Setups a perspective lens for the source. + * @details This makes the shadow source behave like a perspective lens. The + * parameters are similar to the ones of a PerspectiveLens. + * + * @param fov FoV of the lens + * @param near_plane The near plane of the lens, to avoid artifacts at low distance + * @param far_plane The far plane of the lens + * @param pos Position of the lens, in world space + * @param direction Direction (Orientation) of the lens + */ +inline void ShadowSource:: +set_perspective_lens(PN_stdfloat fov, PN_stdfloat near_plane, + PN_stdfloat far_plane, LVecBase3 pos, + LVecBase3 direction) { + // Construct the transfo*rmation matrix + LMatrix4 transform_mat = LMatrix4::translate_mat(-pos); + + // Construct a temporary lens to generate the lens matrix + PerspectiveLens temp_lens = PerspectiveLens(fov, fov); + temp_lens.set_film_offset(0, 0); + temp_lens.set_near_far(near_plane, far_plane); + temp_lens.set_view_vector(direction, LVector3::up()); + set_matrix_lens(transform_mat * temp_lens.get_projection_mat()); + + // Set new bounds, approximate with sphere + CPT(BoundingHexahedron) hexahedron = DCAST(BoundingHexahedron, temp_lens.make_bounds()); + LPoint3 center = (hexahedron->get_min() + hexahedron->get_max()) * 0.5f; + _bounds = BoundingSphere(pos + center, (hexahedron->get_max() - center).length()); +} + +/** + * @brief Sets a custom matrix for the source. + * @details This tells the source to use a custom matrix for rendering, just like + * the matrix lens. The matrix should include all transformations, rotations and + * scales. No other matrices will be used for rendering this shadow source (not + * even a coordinate system conversion). + * + * @param mvp Custom View-Projection matrix + */ +inline void ShadowSource::set_matrix_lens(const LMatrix4& mvp) { + _mvp = mvp; + set_needs_update(true); +} + +/** + * @brief Sets the update flag of the source. + * @details Sets whether the source is still valid, or needs to get regenerated. + * Usually you only want to flag the shadow source as invalid, by passing + * true as the flag. However, the ShadowManager will set the flag to false + * after updating the source. + * + * @param flag The update flag + */ +inline void ShadowSource::set_needs_update(bool flag) { + _needs_update = flag; +} + +/** + * @brief Returns whether the source has a valid region. + * @details This returns whether the ShadowSource has a valid shadow atlas region + * assigned. This might be not the case when the source never was rendered yet, + * or is about to get updated. + * @return true if the source has a valid region, else false. + */ +inline bool ShadowSource::has_region() const { + return _region.get_x() >= 0 && _region.get_y() >= 0 && _region.get_z() >= 0 && _region.get_w() >= 0; +} + +/** + * @brief Returns the resolution of the source. + * @details Returns the shadow map resolution of source, in pixels. This is the + * space the source takes in the shadow atlas, in pixels. + * @return Resolution in pixels + */ +inline size_t ShadowSource::get_resolution() const { + return _resolution; +} + +/** + * @brief Returns the assigned region of the source in atlas space. + * @details This returns the region of the source, in atlas space. This is the + * region set by ShadowSource::set_region. If no region was set yet, returns + * a 4-component integer vector with all components set to -1. To check this, + * you should call ShadowSource::has_region() first. + * + * @return [description] + */ +inline const LVecBase4i& ShadowSource::get_region() const { + return _region; +} + +/** + * @brief Returns the assigned region of the source in UV space. + * @details This returns the region of the source, in UV space. This is the + * region set by ShadowSource::set_region. If no region was set yet, returns + * a 4-component integer vector with all components set to -1. To check this, + * you should call ShadowSource::has_region() first. + * + * @return [description] + */ +inline const LVecBase4& ShadowSource::get_uv_region() const { + return _region_uv; +} + +/** + * @brief Sets the assigned region of the source in atlas and uv space. + * @details This sets the assigned region of the ShadowSource. The region in + * atlas space should be the region returned from the + * ShadowAtlas::find_and_reserve_region. The uv-region should be the same region, + * but in the 0 .. 1 range (can be converted with ShadowAtlas::region_to_uv). + * This is required for the shaders, because they expect coordinates in the + * 0 .. 1 range for sampling. + * + * @param region Atlas-Space region + * @param region_uv UV-Space region + */ +inline void ShadowSource::set_region(const LVecBase4i& region, const LVecBase4& region_uv) { + _region = region; + _region_uv = region_uv; +} + +/** + * @brief Returns the View-Projection matrix of the source. + * @details This returns the current view-projection matrix of the ShadowSource. + * If no matrix was set yet, returns a matrix with all components zero. + * If a matrix was set with ShadowSource::set_matrix_lens, returns the matrix + * set by that function call. + * + * If a matrix was set with ShadowSource::set_perspective_lens, returns a + * perspective view-projection matrix setup by those parameters. + * + * The matrix returned is the matrix used for rendering the shadow map, and + * includes the camera transform as well as the projection matrix. + * + * @return View-Projection matrix. + */ +inline const LMatrix4& ShadowSource::get_mvp() const { + return _mvp; +} + +/** + * @brief Writes the source to a GPUCommand. + * @details This writes the ShadowSource to a GPUCommand. This stores the + * mvp and the uv-region in the command. + * + * @param cmd GPUCommand to write to. + */ +inline void ShadowSource::write_to_command(GPUCommand &cmd) const { + // When storing on the gpu, we should already have a valid slot + nassertv(_slot >= 0); + cmd.push_mat4(_mvp); + cmd.push_vec4(_region_uv); +} + +/** + * @brief Sets the resolution of the source. + * @details This sets the resolution of the ShadowSource, in pixels. It should be + * a multiple of the tile size of the ShadowAtlas, and greater than zero. + * + * @param resolution [description] + */ +inline void ShadowSource::set_resolution(size_t resolution) { + nassertv(resolution > 0); + _resolution = resolution; + set_needs_update(true); +} + +/** + * @brief Returns the shadow sources bounds + * @details This returns the bounds of the shadow source, approximated as a sphere + * @return Bounds as a BoundingSphere + */ +inline const BoundingSphere& ShadowSource::get_bounds() const { + return _bounds; +} + +/** + * @brief Clears the assigned region of the source + * @details This unassigns any shadow atlas region from the source, previously + * set with set_region + */ +inline void ShadowSource::clear_region() { + _region.fill(-1); + _region_uv.fill(0); +} diff --git a/contrib/src/rplight/shadowSource.cxx b/contrib/src/rplight/shadowSource.cxx new file mode 100644 index 0000000000..e5b76b75b5 --- /dev/null +++ b/contrib/src/rplight/shadowSource.cxx @@ -0,0 +1,41 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "shadowSource.h" + +/** + * @brief Constructs a new shadow source + * @details This constructs a new shadow source, with no projection setup, + * and no slot assigned. + */ +ShadowSource::ShadowSource() { + _slot = -1; + _needs_update = true; + _resolution = 512; + _mvp.fill(0.0); + _region.fill(-1); + _region_uv.fill(0); +} diff --git a/contrib/src/rplight/shadowSource.h b/contrib/src/rplight/shadowSource.h new file mode 100644 index 0000000000..84b00a197b --- /dev/null +++ b/contrib/src/rplight/shadowSource.h @@ -0,0 +1,93 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#ifndef SHADOWSOURCE_H +#define SHADOWSOURCE_H + +#include "pandabase.h" +#include "luse.h" +#include "transformState.h" +#include "look_at.h" +#include "compose_matrix.h" +#include "perspectiveLens.h" +#include "boundingVolume.h" +#include "boundingSphere.h" +#include "boundingHexahedron.h" +#include "geometricBoundingVolume.h" + +#include "gpuCommand.h" + +/** + * @brief This class represents a single shadow source. + * @details The ShadowSource can be seen as a Camera. It is used by the Lights + * to render their shadows. Each ShadowSource has a position in the atlas, + * and a view-projection matrix. The shadow manager regenerates the shadow maps + * using the data from the shadow sources. + */ +class ShadowSource { +public: + ShadowSource(); + + inline void write_to_command(GPUCommand &cmd) const; + + inline void set_needs_update(bool flag); + inline void set_slot(int slot); + inline void set_region(const LVecBase4i& region, const LVecBase4& region_uv); + inline void set_resolution(size_t resolution); + inline void set_perspective_lens(PN_stdfloat fov, PN_stdfloat near_plane, + PN_stdfloat far_plane, LVecBase3 pos, + LVecBase3 direction); + inline void set_matrix_lens(const LMatrix4& mvp); + + inline bool has_region() const; + inline bool has_slot() const; + + inline void clear_region(); + + inline int get_slot() const; + inline bool get_needs_update() const; + inline size_t get_resolution() const; + inline const LMatrix4& get_mvp() const; + inline const LVecBase4i& get_region() const; + inline const LVecBase4& get_uv_region() const; + + inline const BoundingSphere& get_bounds() const; + +private: + int _slot; + bool _needs_update; + size_t _resolution; + LMatrix4 _mvp; + LVecBase4i _region; + LVecBase4 _region_uv; + + BoundingSphere _bounds; +}; + +#include "shadowSource.I" + +#endif // SHADOWSOURCE_H diff --git a/contrib/src/rplight/tagStateManager.I b/contrib/src/rplight/tagStateManager.I new file mode 100644 index 0000000000..058832e60d --- /dev/null +++ b/contrib/src/rplight/tagStateManager.I @@ -0,0 +1,93 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +/** + * @brief Registers a new camera which renders a certain pass + * @details This registers a new camera which will be used to render the given + * pass. The TagStateManager will keep track of the camera and + * applies all registered states onto the camera with Camera::set_tag_state. + * It also applies the appropriate camera mask to the camera, + * and sets an initial state to disable color write depending on the pass. + * + * @param source Camera which will be used to render shadows + */ +inline void TagStateManager:: +register_camera(const string& name, Camera* source) { + ContainerList::iterator entry = _containers.find(name); + nassertv(entry != _containers.end()); + register_camera(entry->second, source); +} + +/** + * @brief Unregisters a camera from the list of shadow cameras + * @details This unregisters a camera from the list of shadows cameras. It also + * resets all tag states of the camera, and also its initial state. + * + * @param source Camera to unregister + */ +inline void TagStateManager:: +unregister_camera(const string& name, Camera* source) { + ContainerList::iterator entry = _containers.find(name); + nassertv(entry != _containers.end()); + unregister_camera(entry->second, source); +} + +/** + * @brief Applies a given state for a pass to a NodePath + * @details This applies a shader to the given NodePath which is used when the + * NodePath is rendered by any registered camera for that pass. + * It also disables color write depending on the pass. + * + * @param np The nodepath to apply the shader to + * @param shader A handle to the shader to apply + * @param name Name of the state, should be a unique identifier + * @param sort Determines the sort with which the shader will be applied. + */ +inline void TagStateManager:: +apply_state(const string& state, NodePath np, Shader* shader, + const string &name, int sort) { + ContainerList::iterator entry = _containers.find(state); + nassertv(entry != _containers.end()); + apply_state(entry->second, np, shader, name, sort); +} + +/** + * @brief Returns the render mask for the given state + * @details This returns the mask of a given render pass, which can be used + * to either show or hide objects from this pass. + * + * @param container_name Name of the render-pass + * @return Bit mask of the render pass + */ +inline BitMask32 TagStateManager:: +get_mask(const string &container_name) { + if (container_name == "gbuffer") { + return BitMask32::bit(1); + } + ContainerList::iterator entry = _containers.find(container_name); + nassertr(entry != _containers.end(), BitMask32()); + return entry->second.mask; +} diff --git a/contrib/src/rplight/tagStateManager.cxx b/contrib/src/rplight/tagStateManager.cxx new file mode 100644 index 0000000000..ddc996f9a4 --- /dev/null +++ b/contrib/src/rplight/tagStateManager.cxx @@ -0,0 +1,202 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "tagStateManager.h" + + +NotifyCategoryDef(tagstatemgr, ""); + +/** + * @brief Constructs a new TagStateManager + * @details This constructs a new TagStateManager. The #main_cam_node should + * refer to the main scene camera, and will most likely be base.cam. + * It is necessary to pass the camera because the C++ code does not have + * access to the showbase. + * + * @param main_cam_node The main scene camera + */ +TagStateManager:: +TagStateManager(NodePath main_cam_node) { + nassertv(!main_cam_node.is_empty()); + nassertv(DCAST(Camera, main_cam_node.node()) != nullptr); + _main_cam_node = main_cam_node; + + // Set default camera mask + DCAST(Camera, _main_cam_node.node())->set_camera_mask(BitMask32::bit(1)); + + // Init containers + _containers["shadow"] = StateContainer("Shadows", 2, false); + _containers["voxelize"] = StateContainer("Voxelize", 3, false); + _containers["envmap"] = StateContainer("Envmap", 4, true); + _containers["forward"] = StateContainer("Forward", 5, true); +} + +/** + * @brief Destructs the TagStateManager + * @details This destructs the TagStateManager, and cleans up all resources used. + */ +TagStateManager:: +~TagStateManager() { + cleanup_states(); +} + +/** + * @brief Applies a given state to a NodePath + * @details This applies a shader to the given NodePath which is used when the + * NodePath is rendered by any registered camera of the container. + * + * @param container The container which is used to store the state + * @param np The nodepath to apply the shader to + * @param shader A handle to the shader to apply + * @param name Name of the state, should be a unique identifier + * @param sort Changes the sort with which the shader will be applied. + */ +void TagStateManager:: +apply_state(StateContainer& container, NodePath np, Shader* shader, + const string &name, int sort) { + if (tagstatemgr_cat.is_spam()) { + tagstatemgr_cat.spam() << "Constructing new state " << name + << " with shader " << shader << endl; + } + + // Construct the render state + CPT(RenderState) state = RenderState::make_empty(); + + // Disable color write for all stages except the environment container + if (!container.write_color) { + state = state->set_attrib(ColorWriteAttrib::make(ColorWriteAttrib::C_off), 10000); + } + state = state->set_attrib(ShaderAttrib::make(shader, sort), sort); + + // Emit a warning if we override an existing state + if (container.tag_states.count(name) != 0) { + tagstatemgr_cat.warning() << "Overriding existing state " << name << endl; + } + + // Store the state, this is required whenever we attach a new camera, so + // it can also track the existing states + container.tag_states[name] = state; + + // Save the tag on the node path + np.set_tag(container.tag_name, name); + + // Apply the state on all cameras which are attached so far + for (size_t i = 0; i < container.cameras.size(); ++i) { + container.cameras[i]->set_tag_state(name, state); + } +} + +/** + * @brief Cleans up all registered states. + * @details This cleans up all states which were registered to the TagStateManager. + * It also calls Camera::clear_tag_states() on the main_cam_node and all attached + * cameras. + */ +void TagStateManager:: +cleanup_states() { + if (tagstatemgr_cat.is_info()) { + tagstatemgr_cat.info() << "cleaning up states" << endl; + } + + // Clear all tag states of the main camera + DCAST(Camera, _main_cam_node.node())->clear_tag_states(); + + // Clear the containers + // XXX: Just iterate over the _container map + cleanup_container_states(_containers["shadow"]); + cleanup_container_states(_containers["voxelize"]); + cleanup_container_states(_containers["envmap"]); + cleanup_container_states(_containers["forward"]); +} + +/** + * @brief Cleans up the states of a given container + * @details This cleans all tag states of the given container, + * and also calls Camera::clear_tag_states on every assigned camera. + * + * @param container Container to clear + */ +void TagStateManager:: +cleanup_container_states(StateContainer& container) { + for (size_t i = 0; i < container.cameras.size(); ++i) { + container.cameras[i]->clear_tag_states(); + } + container.tag_states.clear(); +} + +/** + * @brief Registers a new camera to a given container + * @details This registers a new camera to a container, and sets its initial + * state as well as the camera mask. + * + * @param container The container to add the camera to + * @param source The camera to add + */ +void TagStateManager:: +register_camera(StateContainer& container, Camera* source) { + source->set_tag_state_key(container.tag_name); + source->set_camera_mask(container.mask); + + // Construct an initial state which also disables color write, additionally + // to the ColorWriteAttrib on each unique state. + CPT(RenderState) state = RenderState::make_empty(); + + if (!container.write_color) { + state = state->set_attrib(ColorWriteAttrib::make(ColorWriteAttrib::C_off), 10000); + } + source->set_initial_state(state); + + // Store the camera so we can keep track of it + container.cameras.push_back(source); +} + +/** + * @brief Unregisters a camera from a container + * @details This unregisters a camera from the list of cameras of a given + * container. It also resets all tag states of the camera, and also its initial + * state. + * + * @param source Camera to unregister + */ +void TagStateManager:: +unregister_camera(StateContainer& container, Camera* source) { + CameraList& cameras = container.cameras; + + // Make sure the camera was attached so far + if (std::find(cameras.begin(), cameras.end(), source) == cameras.end()) { + tagstatemgr_cat.error() + << "Called unregister_camera but camera was never registered!" << endl; + return; + } + + // Remove the camera from the list of attached cameras + cameras.erase(std::remove(cameras.begin(), cameras.end(), source), cameras.end()); + + // Reset the camera + source->clear_tag_states(); + source->set_initial_state(RenderState::make_empty()); +} diff --git a/contrib/src/rplight/tagStateManager.h b/contrib/src/rplight/tagStateManager.h new file mode 100644 index 0000000000..6c26e5cbf6 --- /dev/null +++ b/contrib/src/rplight/tagStateManager.h @@ -0,0 +1,93 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef TAGSTATEMANAGER_H +#define TAGSTATEMANAGER_H + +#include "pandabase.h" +#include "bitMask.h" +#include "camera.h" +#include "nodePath.h" +#include "shader.h" +#include "renderState.h" +#include "shaderAttrib.h" +#include "colorWriteAttrib.h" + +NotifyCategoryDecl(tagstatemgr, EXPORT_CLASS, EXPORT_TEMPL); + +/** + * @brief This class handles all different tag states + * @details The TagStateManager stores a list of RenderStates assigned to different + * steps in the pipeline. For example, there are a list of shadow states, which + * are applied whenever objects are rendered from a shadow camera. + * + * The Manager also stores a list of all cameras used in the different stages, + * to keep track of the states used and to be able to attach new states. + */ +class TagStateManager { +PUBLISHED: + TagStateManager(NodePath main_cam_node); + ~TagStateManager(); + + inline void apply_state(const string& state, NodePath np, Shader* shader, const string &name, int sort); + void cleanup_states(); + + inline void register_camera(const string& state, Camera* source); + inline void unregister_camera(const string& state, Camera* source); + inline BitMask32 get_mask(const string &container_name); + +private: + typedef vector CameraList; + typedef pmap TagStateList; + + struct StateContainer { + CameraList cameras; + TagStateList tag_states; + string tag_name; + BitMask32 mask; + bool write_color; + + StateContainer() {}; + StateContainer(const string &tag_name, size_t mask, bool write_color) + : tag_name(tag_name), mask(BitMask32::bit(mask)), write_color(write_color) {}; + }; + + void apply_state(StateContainer& container, NodePath np, Shader* shader, + const string& name, int sort); + void cleanup_container_states(StateContainer& container); + void register_camera(StateContainer &container, Camera* source); + void unregister_camera(StateContainer &container, Camera* source); + + typedef pmap ContainerList; + ContainerList _containers; + + NodePath _main_cam_node; +}; + + +#include "tagStateManager.I" + +#endif // TAGSTATEMANAGER_H 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..b9ba299aaa 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 @@ -1095,8 +1103,8 @@ class Actor(DirectObject, NodePath): # Get a handle to the joint. joint = bundle.findChild(jointName) - if node == None: - node = self.attachNewNode(jointName) + if node is None: + node = partDef.partBundleNP.attachNewNode(jointName) if (joint): if localTransform: @@ -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/dcClass.h b/direct/src/dcparser/dcClass.h index 699d953498..73ad9e43d3 100644 --- a/direct/src/dcparser/dcClass.h +++ b/direct/src/dcparser/dcClass.h @@ -117,7 +117,7 @@ PUBLISHED: Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id, ZONEID_TYPE zone_id, PyObject *optional_fields) const; -#endif +#endif public: virtual void output(ostream &out, bool brief) const; 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/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/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/CRDataCache.py b/direct/src/distributed/CRDataCache.py index aa64fdf356..b28453c0a3 100755 --- a/direct/src/distributed/CRDataCache.py +++ b/direct/src/distributed/CRDataCache.py @@ -2,9 +2,6 @@ from direct.distributed.CachedDOData import CachedDOData from panda3d.core import ConfigVariableInt -# This has to be imported for __builtin__.config -from direct.showbase import ShowBase - __all__ = ["CRDataCache"] class CRDataCache: diff --git a/direct/src/distributed/ClientRepositoryBase.py b/direct/src/distributed/ClientRepositoryBase.py index c4555e94ec..5aa7c3fa94 100644 --- a/direct/src/distributed/ClientRepositoryBase.py +++ b/direct/src/distributed/ClientRepositoryBase.py @@ -175,7 +175,7 @@ class ClientRepositoryBase(ConnectionRepository): "generate" messages when they are replayed(). """ - if msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER: + if msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER: # It's a generate message. doId = extra if doId in self.deferredDoIds: @@ -381,7 +381,7 @@ class ClientRepositoryBase(ConnectionRepository): # The object had been deferred. Great; we don't even have # to generate it now. del self.deferredDoIds[doId] - i = self.deferredGenerates.index((CLIENT_CREATE_OBJECT_REQUIRED_OTHER, doId)) + i = self.deferredGenerates.index((CLIENT_ENTER_OBJECT_REQUIRED_OTHER, doId)) del self.deferredGenerates[i] if len(self.deferredGenerates) == 0: taskMgr.remove('deferredGenerate') diff --git a/direct/src/distributed/DistributedObjectBase.py b/direct/src/distributed/DistributedObjectBase.py index f89939c736..3d9b525067 100755 --- a/direct/src/distributed/DistributedObjectBase.py +++ b/direct/src/distributed/DistributedObjectBase.py @@ -95,7 +95,8 @@ class DistributedObjectBase(DirectObject): def delete(self): """ - Overwrite this to handle cleanup right before this object + Override this to handle cleanup right before this object gets deleted. """ + pass diff --git a/direct/src/distributed/DistributedSmoothNode.py b/direct/src/distributed/DistributedSmoothNode.py index cdc2dc034e..65da34052a 100644 --- a/direct/src/distributed/DistributedSmoothNode.py +++ b/direct/src/distributed/DistributedSmoothNode.py @@ -6,19 +6,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 @@ -26,8 +28,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/MsgTypesCMU.py b/direct/src/distributed/MsgTypesCMU.py index c0d1ccd4ca..e7c7501730 100644 --- a/direct/src/distributed/MsgTypesCMU.py +++ b/direct/src/distributed/MsgTypesCMU.py @@ -19,7 +19,7 @@ MsgName2Id = { 'CLIENT_HEARTBEAT_CMU' : 9011, 'CLIENT_OBJECT_UPDATE_FIELD_TARGETED_CMU' : 9011, - 'CLIENT_OBJECT_UPDATE_FIELD' : 24, # Matches MsgTypes.CLIENT_OBJECT_UPDATE_FIELD + 'CLIENT_OBJECT_UPDATE_FIELD' : 120, # Matches MsgTypes.CLIENT_OBJECT_SET_FIELD } # create id->name table for debugging diff --git a/direct/src/distributed/OldClientRepository.py b/direct/src/distributed/OldClientRepository.py deleted file mode 100644 index daeb4af628..0000000000 --- a/direct/src/distributed/OldClientRepository.py +++ /dev/null @@ -1,208 +0,0 @@ -"""OldClientRepository module: contains the OldClientRepository class""" - -from .ClientRepositoryBase import * - -class OldClientRepository(ClientRepositoryBase): - """ - This is the open-source ClientRepository as provided by CMU. It - communicates with the ServerRepository in this same directory. - - If you are looking for the VR Studio's implementation of the - client repository, look to OTPClientRepository (elsewhere). - """ - notify = DirectNotifyGlobal.directNotify.newCategory("ClientRepository") - - def __init__(self, dcFileNames = None): - ClientRepositoryBase.__init__(self, dcFileNames = dcFileNames) - - # The DOID allocator. The CMU LAN server may choose to - # send us a block of DOIDs. If it chooses to do so, then we - # may create objects, using those DOIDs. - self.DOIDbase = 0 - self.DOIDnext = 0 - self.DOIDlast = 0 - - def handleSetDOIDrange(self, di): - self.DOIDbase = di.getUint32() - self.DOIDlast = self.DOIDbase + di.getUint32() - self.DOIDnext = self.DOIDbase - - def handleRequestGenerates(self, di): - # When new clients join the zone of an object, they need to hear - # about it, so we send out all of our information about objects in - # that particular zone. - - assert self.DOIDnext < self.DOIDlast - zone = di.getUint32() - for obj in self.doId2do.values(): - if obj.zone == zone: - id = obj.doId - if (self.isLocalId(id)): - self.send(obj.dclass.clientFormatGenerate(obj, id, zone, [])) - - def createWithRequired(self, className, zoneId = 0, optionalFields=None): - if self.DOIDnext >= self.DOIDlast: - self.notify.error( - "Cannot allocate a distributed object ID: all IDs used up.") - return None - id = self.DOIDnext - self.DOIDnext = self.DOIDnext + 1 - dclass = self.dclassesByName[className] - classDef = dclass.getClassDef() - if classDef == None: - self.notify.error("Could not create an undefined %s object." % ( - dclass.getName())) - obj = classDef(self) - obj.dclass = dclass - obj.zone = zoneId - obj.doId = id - self.doId2do[id] = obj - obj.generateInit() - obj._retrieveCachedData() - obj.generate() - obj.announceGenerate() - datagram = dclass.clientFormatGenerate(obj, id, zoneId, optionalFields) - self.send(datagram) - return obj - - def sendDisableMsg(self, doId): - datagram = PyDatagram() - datagram.addUint16(CLIENT_OBJECT_DISABLE) - datagram.addUint32(doId) - self.send(datagram) - - def sendDeleteMsg(self, doId): - datagram = PyDatagram() - datagram.addUint16(CLIENT_OBJECT_DELETE) - datagram.addUint32(doId) - self.send(datagram) - - def sendRemoveZoneMsg(self, zoneId, visibleZoneList=None): - datagram = PyDatagram() - datagram.addUint16(CLIENT_REMOVE_ZONE) - datagram.addUint32(zoneId) - - # if we have an explicit list of visible zones, add them - if visibleZoneList is not None: - vzl = list(visibleZoneList) - vzl.sort() - assert PythonUtil.uniqueElements(vzl) - for zone in vzl: - datagram.addUint32(zone) - - # send the message - self.send(datagram) - - def sendUpdateZone(self, obj, zoneId): - id = obj.doId - assert self.isLocalId(id) - self.sendDeleteMsg(id, 1) - obj.zone = zoneId - self.send(obj.dclass.clientFormatGenerate(obj, id, zoneId, [])) - - def sendSetZoneMsg(self, zoneId, visibleZoneList=None): - datagram = PyDatagram() - # Add message type - datagram.addUint16(CLIENT_SET_ZONE_CMU) - # Add zone id - datagram.addUint32(zoneId) - - # if we have an explicit list of visible zones, add them - if visibleZoneList is not None: - vzl = list(visibleZoneList) - vzl.sort() - assert PythonUtil.uniqueElements(vzl) - for zone in vzl: - datagram.addUint32(zone) - - # send the message - self.send(datagram) - - def isLocalId(self, id): - return ((id >= self.DOIDbase) and (id < self.DOIDlast)) - - def haveCreateAuthority(self): - return (self.DOIDlast > self.DOIDnext) - - def handleDatagram(self, di): - if self.notify.getDebug(): - print("ClientRepository received datagram:") - di.getDatagram().dumpHex(ostream) - - msgType = self.getMsgType() - - # These are the sort of messages we may expect from the public - # Panda server. - - if msgType == CLIENT_SET_DOID_RANGE: - self.handleSetDOIDrange(di) - elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_RESP: - self.handleGenerateWithRequired(di) - elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER_RESP: - self.handleGenerateWithRequiredOther(di) - elif msgType == CLIENT_OBJECT_UPDATE_FIELD_RESP: - self.handleUpdateField(di) - elif msgType == CLIENT_OBJECT_DELETE_RESP: - self.handleDelete(di) - elif msgType == CLIENT_OBJECT_DISABLE_RESP: - self.handleDisable(di) - elif msgType == CLIENT_REQUEST_GENERATES: - self.handleRequestGenerates(di) - else: - self.handleMessageType(msgType, di) - - # If we're processing a lot of datagrams within one frame, we - # may forget to send heartbeats. Keep them coming! - self.considerHeartbeat() - - def handleGenerateWithRequired(self, di): - # Get the class Id - classId = di.getUint16() - # Get the DO Id - doId = di.getUint32() - # Look up the dclass - dclass = self.dclassesByNumber[classId] - dclass.startGenerate() - # Create a new distributed object, and put it in the dictionary - distObj = self.generateWithRequiredFields(dclass, doId, di) - dclass.stopGenerate() - - def generateWithRequiredFields(self, dclass, doId, di): - if doId in self.doId2do: - # ...it is in our dictionary. - # Just update it. - distObj = self.doId2do[doId] - assert distObj.dclass == dclass - distObj.generate() - distObj.updateRequiredFields(dclass, di) - # updateRequiredFields calls announceGenerate - elif self.cache.contains(doId): - # ...it is in the cache. - # Pull it out of the cache: - distObj = self.cache.retrieve(doId) - assert distObj.dclass == dclass - # put it in the dictionary: - self.doId2do[doId] = distObj - # and update it. - distObj.generate() - distObj.updateRequiredFields(dclass, di) - # updateRequiredFields calls announceGenerate - else: - # ...it is not in the dictionary or the cache. - # Construct a new one - classDef = dclass.getClassDef() - if classDef == None: - self.notify.error("Could not create an undefined %s object." % ( - dclass.getName())) - distObj = classDef(self) - distObj.dclass = dclass - # Assign it an Id - distObj.doId = doId - # Put the new do in the dictionary - self.doId2do[doId] = distObj - # Update the required fields - distObj.generateInit() # Only called when constructed - distObj.generate() - distObj.updateRequiredFields(dclass, di) - # updateRequiredFields calls announceGenerate - return distObj 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.cxx b/direct/src/distributed/cConnectionRepository.cxx index 16bff97d9b..67d6ec1658 100644 --- a/direct/src/distributed/cConnectionRepository.cxx +++ b/direct/src/distributed/cConnectionRepository.cxx @@ -708,7 +708,7 @@ handle_update_field() { Py_DECREF(dclass_obj); nassertr(dclass_this != NULL, false); - DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this); + DCClass *dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this); Py_DECREF(dclass_this); // If in quiet zone mode, throw update away unless distobj has @@ -799,7 +799,7 @@ handle_update_field_owner() { Py_DECREF(dclass_obj); nassertr(dclass_this != NULL, false); - DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this); + DCClass *dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this); Py_DECREF(dclass_this); // check if we should forward this update to the owner view @@ -841,7 +841,7 @@ handle_update_field_owner() { Py_DECREF(dclass_obj); nassertr(dclass_this != NULL, false); - DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this); + DCClass *dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this); Py_DECREF(dclass_this); // check if we should forward this update to the owner view @@ -974,7 +974,7 @@ describe_message(ostream &out, const string &prefix, Py_DECREF(dclass_obj); nassertv(dclass_this != NULL); - dclass = (DCClass *)PyLong_AsLong(dclass_this); + dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this); Py_DECREF(dclass_this); } } 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..894b57c321 100644 --- a/direct/src/filter/CommonFilters.py +++ b/direct/src/filter/CommonFilters.py @@ -131,6 +131,9 @@ class CommonFilters: if (len(configuration) == 0): return + if not self.manager.win.gsg.getSupportsBasicShaders(): + return False + auxbits = 0 needtex = set(["color"]) needtexcoord = set(["color"]) @@ -338,7 +341,10 @@ class CommonFilters: text += " o_color = float4(1, 1, 1, 1) - o_color;\n" text += "}\n" - self.finalQuad.setShader(Shader.make(text, Shader.SL_Cg)) + shader = Shader.make(text, Shader.SL_Cg) + if not shader: + return False + self.finalQuad.setShader(shader) for tex in self.textures: self.finalQuad.setShaderInput("tx"+tex, self.textures[tex]) @@ -536,3 +542,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 af7b7d650c..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 | -- diff --git a/direct/src/fsm/FourStateAI.py b/direct/src/fsm/FourStateAI.py index eee4b43a76..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 | -- 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..6955e550a2 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'] @@ -58,6 +59,8 @@ class DirectEntry(DirectFrame): # Text used for the PGEntry text node # NOTE: This overrides the DirectFrame text option ('initialText', '', DGG.INITOPT), + # Enable or disable text overflow scrolling + ('overflow', 0, self.setOverflowMode), # Command to be called on hitting Enter ('command', None, None), ('extraArgs', [], None), @@ -158,6 +161,9 @@ class DirectEntry(DirectFrame): def setCursorKeysActive(self): PGEntry.setCursorKeysActive(self.guiItem, self['cursorKeys']) + def setOverflowMode(self): + PGEntry.set_overflow_mode(self.guiItem, self['overflow']) + def setObscureMode(self): PGEntry.setObscureMode(self.guiItem, self['obscured']) 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..3180d0dfce 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,32 @@ Code Overview: are left unused. If so, an error is raised. """ +__all__ = ['DirectGuiBase', 'DirectGuiWidget'] + + +from panda3d.core import * +from direct.showbase import ShowBaseGlobal +from direct.showbase.ShowBase import 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' @@ -634,7 +634,7 @@ class DirectGuiBase(DirectObject.DirectObject): """ # Need to tack on gui item specific id gEvent = event + self.guiId - if get_config_showbase().GetBool('debug-directgui-msgs', False): + if ShowBase.config.GetBool('debug-directgui-msgs', False): from direct.showbase.PythonUtil import StackTrace print(gEvent) print(StackTrace()) @@ -663,7 +663,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath): # Determine the default initial state for inactive (or # unclickable) components. If we are in edit mode, these are # actually clickable by default. - guiEdit = get_config_showbase().GetBool('direct-gui-edit', 0) + guiEdit = ShowBase.config.GetBool('direct-gui-edit', False) if guiEdit: inactiveInitState = DGG.NORMAL else: @@ -724,21 +724,24 @@ class DirectGuiWidget(DirectGuiBase, NodePath): if self['guiId']: self.guiItem.setId(self['guiId']) self.guiId = self.guiItem.getId() - if __dev__: + + if ShowBaseGlobal.__dev__: guiObjectCollector.addLevel(1) guiObjectCollector.flushLevel() # track gui items by guiId for tracking down leaks - if hasattr(base, 'guiItems'): - if self.guiId in base.guiItems: - base.notify.warning('duplicate guiId: %s (%s stomping %s)' % - (self.guiId, self, - base.guiItems[self.guiId])) - base.guiItems[self.guiId] = self - if hasattr(base, 'printGuiCreates'): - printStack() + if ShowBase.config.GetBool('track-gui-items', False): + if not hasattr(ShowBase, 'guiItems'): + ShowBase.guiItems = {} + if self.guiId in ShowBase.guiItems: + ShowBase.notify.warning('duplicate guiId: %s (%s stomping %s)' % + (self.guiId, self, + ShowBase.guiItems[self.guiId])) + ShowBase.guiItems[self.guiId] = self + # Attach button to parent and make that self - if (parent == None): - parent = aspect2d + if parent is None: + parent = ShowBaseGlobal.aspect2d + self.assign(parent.attachNewNode(self.guiItem, self['sortOrder'])) # Update pose to initial values if self['pos']: @@ -1025,17 +1028,12 @@ class DirectGuiWidget(DirectGuiBase, NodePath): def destroy(self): if hasattr(self, "frameStyle"): - if __dev__: + if ShowBaseGlobal.__dev__: guiObjectCollector.subLevel(1) guiObjectCollector.flushLevel() - if hasattr(base, 'guiItems'): - if self.guiId in base.guiItems: - del base.guiItems[self.guiId] - else: - base.notify.warning( - 'DirectGuiWidget.destroy(): ' - 'gui item %s not in base.guiItems' % - self.guiId) + if hasattr(ShowBase, 'guiItems'): + ShowBase.guiItems.pop(self.guiId, None) + # Destroy children for child in self.getChildren(): childGui = self.guiDict.get(child.getName()) 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_installer/FileAssociation.nsh b/direct/src/plugin_installer/FileAssociation.nsh old mode 100755 new mode 100644 diff --git a/direct/src/plugin_installer/VersionInfo.vbs b/direct/src/plugin_installer/VersionInfo.vbs old mode 100755 new mode 100644 diff --git a/direct/src/plugin_installer/p3d_installer.nsi b/direct/src/plugin_installer/p3d_installer.nsi old mode 100755 new mode 100644 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 2d43dba169..42ee315909 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -47,10 +47,6 @@ if __debug__: from . import OnScreenDebug from . import AppRunnerGlobal -def legacyRun(): - assert builtins.base.notify.warning("run() is deprecated, use base.run() instead") - builtins.base.run() - @atexit.register def exitfunc(): if getattr(builtins, 'base', None) is not None: @@ -369,7 +365,6 @@ class ShowBase(DirectObject.DirectObject): builtins.bboard = self.bboard # Config needs to be defined before ShowBase is constructed #builtins.config = self.config - builtins.run = legacyRun builtins.ostream = Notify.out() builtins.directNotify = directNotify builtins.giveNotify = giveNotify @@ -389,6 +384,12 @@ class ShowBase(DirectObject.DirectObject): builtins.aspect2dp = self.aspect2dp builtins.pixel2dp = self.pixel2dp + # Now add this instance to the ShowBaseGlobal module scope. + from . import ShowBaseGlobal + builtins.run = ShowBaseGlobal.run + ShowBaseGlobal.base = self + ShowBaseGlobal.__dev__ = self.__dev__ + if self.__dev__: ShowBase.notify.debug('__dev__ == %s' % self.__dev__) else: @@ -396,10 +397,10 @@ class ShowBase(DirectObject.DirectObject): self.createBaseAudioManagers() - if self.__dev__ or self.config.GetBool('want-e3-hacks', False): - if self.config.GetBool('track-gui-items', True): - # dict of guiId to gui item, for tracking down leaks - self.guiItems = {} + if self.__dev__ and self.config.GetBool('track-gui-items', False): + # dict of guiId to gui item, for tracking down leaks + if not hasattr(ShowBase, 'guiItems'): + ShowBase.guiItems = {} # optionally restore the default gui sounds from 1.7.2 and earlier if ConfigVariableBool('orig-gui-sounds', False).getValue(): @@ -511,9 +512,15 @@ class ShowBase(DirectObject.DirectObject): # Remove the built-in base reference if getattr(builtins, 'base', None) is self: + del builtins.run del builtins.base del builtins.loader del builtins.taskMgr + ShowBaseGlobal = sys.modules.get('direct.showbase.ShowBaseGlobal', None) + if ShowBaseGlobal: + del ShowBaseGlobal.base + + self.aspect2d.node().removeAllChildren() # [gjeon] restore sticky key settings if self.config.GetBool('disable-sticky-keys', 0): @@ -968,7 +975,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. @@ -1095,13 +1104,18 @@ class ShowBase(DirectObject.DirectObject): self.render2d.setMaterialOff(1) self.render2d.setTwoSided(1) + # We've already created aspect2d in ShowBaseGlobal, for the + # benefit of creating DirectGui elements before ShowBase. + from . import ShowBaseGlobal + ## The normal 2-d DisplayRegion has an aspect ratio that ## matches the window, but its coordinate system is square. ## This means anything we parent to render2d gets stretched. ## For things where that makes a difference, we set up ## aspect2d, which scales things back to the right aspect ## ratio along the X axis (Z is still from -1 to 1) - self.aspect2d = self.render2d.attachNewNode(PGTop("aspect2d")) + self.aspect2d = ShowBaseGlobal.aspect2d + self.aspect2d.reparentTo(self.render2d) aspectRatio = self.getAspectRatio() self.aspect2d.setScale(1.0 / aspectRatio, 1.0, 1.0) @@ -1260,7 +1274,6 @@ class ShowBase(DirectObject.DirectObject): if win != None 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() @@ -1295,8 +1308,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, @@ -1560,9 +1572,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] @@ -2723,13 +2737,16 @@ 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.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()) + if self.wantRender2dp: + 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 @@ -2753,11 +2770,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 @@ -2767,41 +2785,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/ShowBaseGlobal.py b/direct/src/showbase/ShowBaseGlobal.py index cf601883cb..459d5f708f 100644 --- a/direct/src/showbase/ShowBaseGlobal.py +++ b/direct/src/showbase/ShowBaseGlobal.py @@ -1,18 +1,39 @@ -"""instantiate global ShowBase object""" +"""This module serves as a container to hold the global ShowBase instance, as +an alternative to using the builtin scope. + +Note that you cannot directly import `base` from this module since ShowBase +may not have been created yet; instead, ShowBase dynamically adds itself to +this module's scope when instantiated.""" __all__ = [] -from .ShowBase import * +from .ShowBase import ShowBase, WindowControls +from direct.directnotify.DirectNotifyGlobal import directNotify, giveNotify +from panda3d.core import VirtualFileSystem, Notify, ClockObject, PandaSystem +from panda3d.core import ConfigPageManager, ConfigVariableManager +from panda3d.core import NodePath, PGTop +from panda3d.direct import get_config_showbase -# Create the showbase instance -# This should be created by the game specific "start" file -#ShowBase() -# Instead of creating a show base, assert that one has already been created -assert base +config = get_config_showbase() +__dev__ = config.GetBool('want-dev', __debug__) + +vfs = VirtualFileSystem.getGlobalPtr() +ostream = Notify.out() +globalClock = ClockObject.getGlobalClock() +cpMgr = ConfigPageManager.getGlobalPtr() +cvMgr = ConfigVariableManager.getGlobalPtr() +pandaSystem = PandaSystem.getGlobalPtr() + +# This is defined here so GUI elements can be instantiated before ShowBase. +aspect2d = NodePath(PGTop("aspect2d")) # Set direct notify categories now that we have config directNotify.setDconfigLevels() +def run(): + assert ShowBase.notify.warning("run() is deprecated, use base.run() instead") + base.run() + def inspect(anObject): # Don't use a regular import, to prevent ModuleFinder from picking # it up as a dependency when building a .p3d package. @@ -29,5 +50,4 @@ builtins.inspect = inspect # this also appears in AIBaseGlobal if (not __debug__) and __dev__: - notify = directNotify.newCategory('ShowBaseGlobal') - notify.error("You must set 'want-dev' to false in non-debug mode.") + ShowBase.notify.error("You must set 'want-dev' to false in non-debug mode.") 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..8645e5bc43 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 @@ -84,6 +86,8 @@ Task.DtoolClassDict['exit'] = exit pause = AsyncTaskPause Task.DtoolClassDict['pause'] = staticmethod(pause) +gather = Task.gather + def sequence(*taskList): seq = AsyncTaskSequence('sequence') for task in taskList: @@ -333,6 +337,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 +390,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/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/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/dmodels/src/gui/radio_button_gui.egg b/dmodels/src/gui/radio_button_gui.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/dot_black.gif b/dmodels/src/icons/dot_black.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/dot_blue.gif b/dmodels/src/icons/dot_blue.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/dot_green.gif b/dmodels/src/icons/dot_green.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/dot_red.gif b/dmodels/src/icons/dot_red.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/dot_white.gif b/dmodels/src/icons/dot_white.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/folder.gif b/dmodels/src/icons/folder.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_arrowDown.egg b/dmodels/src/icons/icon_arrowDown.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_arrowDown_tall.egg b/dmodels/src/icons/icon_arrowDown_tall.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_circle_cycle.egg b/dmodels/src/icons/icon_circle_cycle.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_circle_no.egg b/dmodels/src/icons/icon_circle_no.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_diamond.egg b/dmodels/src/icons/icon_diamond.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_gear1.egg b/dmodels/src/icons/icon_gear1.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_hourglass.egg b/dmodels/src/icons/icon_hourglass.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_lightbulb.egg b/dmodels/src/icons/icon_lightbulb.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_lightning.egg b/dmodels/src/icons/icon_lightning.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_pacman.egg b/dmodels/src/icons/icon_pacman.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_plus.egg b/dmodels/src/icons/icon_plus.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_ring.egg b/dmodels/src/icons/icon_ring.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_star_5.egg b/dmodels/src/icons/icon_star_5.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_star_8.egg b/dmodels/src/icons/icon_star_8.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/minusnode.gif b/dmodels/src/icons/minusnode.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/openfolder.gif b/dmodels/src/icons/openfolder.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/plusnode.gif b/dmodels/src/icons/plusnode.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/python.gif b/dmodels/src/icons/python.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/sphere2.gif b/dmodels/src/icons/sphere2.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/tk.gif b/dmodels/src/icons/tk.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/Dirlight.png b/dmodels/src/maps/Dirlight.png old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/Pointlight.png b/dmodels/src/maps/Pointlight.png old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/Spotlight.png b/dmodels/src/maps/Spotlight.png old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/circle.png b/dmodels/src/maps/circle.png old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/lightbulb.tif b/dmodels/src/maps/lightbulb.tif old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/smiley.rgb b/dmodels/src/maps/smiley.rgb old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/square.tif b/dmodels/src/maps/square.tif old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/square_opening.tif b/dmodels/src/maps/square_opening.tif old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/triangle.tif b/dmodels/src/maps/triangle.tif old mode 100755 new mode 100644 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 f0cefb3526..2167d06578 100644 --- a/dtool/src/cppparser/cppBison.cxx.prebuilt +++ b/dtool/src/cppparser/cppBison.cxx.prebuilt @@ -397,51 +397,52 @@ extern int cppyydebug; KW_IS_TRIVIAL = 353, KW_IS_UNION = 354, KW_LONG = 355, - KW_MAKE_MAP_PROPERTY = 356, - KW_MAKE_PROPERTY = 357, - KW_MAKE_PROPERTY2 = 358, - KW_MAKE_SEQ = 359, - KW_MAKE_SEQ_PROPERTY = 360, - KW_MUTABLE = 361, - KW_NAMESPACE = 362, - KW_NEW = 363, - KW_NOEXCEPT = 364, - KW_NULLPTR = 365, - KW_OPERATOR = 366, - KW_OVERRIDE = 367, - KW_PRIVATE = 368, - KW_PROTECTED = 369, - KW_PUBLIC = 370, - KW_REGISTER = 371, - KW_REINTERPRET_CAST = 372, - KW_RETURN = 373, - KW_SHORT = 374, - KW_SIGNED = 375, - KW_SIZEOF = 376, - KW_STATIC = 377, - KW_STATIC_ASSERT = 378, - KW_STATIC_CAST = 379, - KW_STRUCT = 380, - KW_TEMPLATE = 381, - KW_THREAD_LOCAL = 382, - KW_THROW = 383, - KW_TRUE = 384, - KW_TRY = 385, - KW_TYPEDEF = 386, - KW_TYPEID = 387, - KW_TYPENAME = 388, - KW_UNDERLYING_TYPE = 389, - KW_UNION = 390, - KW_UNSIGNED = 391, - KW_USING = 392, - KW_VIRTUAL = 393, - KW_VOID = 394, - KW_VOLATILE = 395, - KW_WCHAR_T = 396, - KW_WHILE = 397, - START_CPP = 398, - START_CONST_EXPR = 399, - START_TYPE = 400 + 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. */ @@ -543,51 +544,52 @@ extern int cppyydebug; #define KW_IS_TRIVIAL 353 #define KW_IS_UNION 354 #define KW_LONG 355 -#define KW_MAKE_MAP_PROPERTY 356 -#define KW_MAKE_PROPERTY 357 -#define KW_MAKE_PROPERTY2 358 -#define KW_MAKE_SEQ 359 -#define KW_MAKE_SEQ_PROPERTY 360 -#define KW_MUTABLE 361 -#define KW_NAMESPACE 362 -#define KW_NEW 363 -#define KW_NOEXCEPT 364 -#define KW_NULLPTR 365 -#define KW_OPERATOR 366 -#define KW_OVERRIDE 367 -#define KW_PRIVATE 368 -#define KW_PROTECTED 369 -#define KW_PUBLIC 370 -#define KW_REGISTER 371 -#define KW_REINTERPRET_CAST 372 -#define KW_RETURN 373 -#define KW_SHORT 374 -#define KW_SIGNED 375 -#define KW_SIZEOF 376 -#define KW_STATIC 377 -#define KW_STATIC_ASSERT 378 -#define KW_STATIC_CAST 379 -#define KW_STRUCT 380 -#define KW_TEMPLATE 381 -#define KW_THREAD_LOCAL 382 -#define KW_THROW 383 -#define KW_TRUE 384 -#define KW_TRY 385 -#define KW_TYPEDEF 386 -#define KW_TYPEID 387 -#define KW_TYPENAME 388 -#define KW_UNDERLYING_TYPE 389 -#define KW_UNION 390 -#define KW_UNSIGNED 391 -#define KW_USING 392 -#define KW_VIRTUAL 393 -#define KW_VOID 394 -#define KW_VOLATILE 395 -#define KW_WCHAR_T 396 -#define KW_WHILE 397 -#define START_CPP 398 -#define START_CONST_EXPR 399 -#define START_TYPE 400 +#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. */ @@ -613,7 +615,7 @@ int cppyyparse (void); /* Copy the second part of user declarations. */ -#line 617 "built/tmp/cppBison.yxx.c" /* yacc.c:358 */ +#line 619 "built/tmp/cppBison.yxx.c" /* yacc.c:358 */ #ifdef short # undef short @@ -857,21 +859,21 @@ union yyalloc /* YYFINAL -- State number of the termination state. */ #define YYFINAL 104 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 6907 +#define YYLAST 7127 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 170 +#define YYNTOKENS 171 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 108 +#define YYNNTS 110 /* YYNRULES -- Number of rules. */ -#define YYNRULES 755 +#define YYNRULES 763 /* YYNSTATES -- Number of states. */ -#define YYNSTATES 1537 +#define YYNSTATES 1570 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 400 +#define YYMAXUTOK 401 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -883,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, 168, 2, 2, 2, 161, 154, 2, - 164, 166, 159, 157, 147, 158, 163, 160, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 149, 148, - 155, 150, 156, 151, 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, 165, 2, 169, 153, 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, 146, 152, 167, 162, 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, @@ -920,89 +922,90 @@ static const yytype_uint8 yytranslate[] = 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, 144, - 145 + 145, 146 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 448, 448, 449, 453, 460, 461, 462, 466, 467, - 471, 475, 479, 492, 491, 503, 504, 505, 506, 507, - 508, 509, 522, 531, 535, 543, 547, 551, 562, 583, - 613, 630, 658, 695, 717, 750, 772, 783, 797, 796, - 811, 815, 820, 824, 835, 839, 843, 847, 851, 855, - 859, 863, 867, 871, 875, 879, 884, 888, 895, 896, - 900, 901, 902, 907, 906, 922, 931, 939, 947, 955, - 966, 982, 981, 996, 1011, 1020, 1035, 1034, 1059, 1058, - 1086, 1085, 1116, 1115, 1134, 1133, 1154, 1153, 1185, 1184, - 1210, 1223, 1227, 1231, 1235, 1248, 1252, 1256, 1260, 1264, - 1269, 1274, 1278, 1282, 1286, 1293, 1297, 1301, 1305, 1309, - 1313, 1317, 1321, 1325, 1329, 1333, 1337, 1341, 1345, 1349, - 1353, 1357, 1361, 1365, 1369, 1373, 1377, 1381, 1385, 1389, - 1393, 1397, 1401, 1405, 1409, 1413, 1417, 1421, 1425, 1429, - 1433, 1437, 1441, 1445, 1452, 1453, 1454, 1458, 1460, 1459, - 1467, 1468, 1472, 1473, 1477, 1483, 1492, 1493, 1497, 1501, - 1505, 1509, 1515, 1521, 1527, 1534, 1539, 1548, 1552, 1557, - 1565, 1577, 1581, 1595, 1610, 1615, 1620, 1625, 1630, 1635, - 1640, 1645, 1651, 1650, 1681, 1691, 1701, 1705, 1709, 1718, - 1722, 1727, 1731, 1736, 1744, 1749, 1757, 1761, 1766, 1770, - 1775, 1783, 1788, 1796, 1800, 1807, 1811, 1818, 1822, 1826, - 1830, 1834, 1841, 1845, 1849, 1853, 1857, 1861, 1868, 1869, - 1870, 1874, 1877, 1878, 1879, 1883, 1888, 1894, 1900, 1905, - 1911, 1917, 1921, 1932, 1936, 1946, 1950, 1954, 1959, 1964, - 1969, 1974, 1979, 1984, 1992, 1996, 2000, 2005, 2010, 2015, - 2020, 2025, 2030, 2035, 2041, 2049, 2054, 2059, 2064, 2069, - 2074, 2079, 2084, 2089, 2094, 2100, 2108, 2112, 2117, 2122, - 2127, 2132, 2137, 2142, 2147, 2152, 2160, 2164, 2169, 2174, - 2179, 2184, 2189, 2194, 2199, 2204, 2209, 2215, 2222, 2229, - 2239, 2243, 2251, 2255, 2259, 2263, 2267, 2283, 2299, 2308, - 2312, 2322, 2329, 2340, 2344, 2352, 2356, 2360, 2364, 2368, - 2384, 2400, 2418, 2427, 2431, 2441, 2448, 2452, 2460, 2464, - 2480, 2496, 2505, 2515, 2522, 2526, 2534, 2538, 2543, 2547, - 2555, 2556, 2557, 2558, 2563, 2562, 2587, 2586, 2616, 2617, - 2624, 2625, 2629, 2630, 2634, 2638, 2642, 2646, 2650, 2654, - 2658, 2662, 2666, 2670, 2677, 2685, 2689, 2693, 2698, 2706, - 2710, 2717, 2718, 2723, 2730, 2731, 2736, 2744, 2748, 2752, - 2759, 2763, 2767, 2775, 2774, 2797, 2796, 2819, 2820, 2824, - 2830, 2837, 2846, 2847, 2848, 2852, 2856, 2860, 2864, 2868, - 2872, 2877, 2882, 2887, 2892, 2896, 2901, 2910, 2915, 2923, - 2927, 2931, 2939, 2949, 2949, 2959, 2960, 2964, 2965, 2966, - 2967, 2968, 2969, 2970, 2971, 2972, 2973, 2974, 2975, 2975, - 2975, 2976, 2976, 2976, 2976, 2977, 2977, 2977, 2977, 2977, - 2978, 2978, 2978, 2979, 2979, 2979, 2979, 2979, 2980, 2980, - 2980, 2980, 2980, 2981, 2981, 2982, 2982, 2982, 2982, 2982, - 2983, 2983, 2983, 2983, 2983, 2984, 2984, 2984, 2984, 2985, - 2985, 2985, 2985, 2985, 2986, 2986, 2986, 2986, 2986, 2987, - 2987, 2987, 2987, 2987, 2987, 2988, 2988, 2988, 2988, 2988, - 2989, 2989, 2989, 2989, 2990, 2990, 2990, 2990, 2991, 2991, - 2991, 2991, 2991, 2992, 2992, 2992, 2992, 2993, 2993, 2993, - 2993, 2993, 2994, 2994, 2994, 2994, 2995, 2995, 2995, 2995, - 2995, 2996, 2996, 2999, 2999, 2999, 2999, 2999, 2999, 2999, - 2999, 2999, 2999, 2999, 3000, 3000, 3000, 3000, 3000, 3000, - 3000, 3000, 3000, 3000, 3001, 3001, 3005, 3009, 3016, 3020, - 3027, 3031, 3038, 3042, 3046, 3050, 3054, 3058, 3062, 3066, - 3078, 3082, 3086, 3090, 3094, 3098, 3102, 3106, 3110, 3114, - 3118, 3122, 3126, 3130, 3134, 3138, 3142, 3146, 3150, 3154, - 3158, 3162, 3166, 3170, 3174, 3178, 3182, 3186, 3190, 3194, - 3198, 3206, 3210, 3214, 3218, 3222, 3226, 3230, 3240, 3250, - 3256, 3262, 3268, 3274, 3280, 3286, 3293, 3300, 3307, 3314, - 3320, 3326, 3330, 3342, 3346, 3350, 3354, 3358, 3369, 3380, - 3384, 3388, 3392, 3396, 3400, 3404, 3408, 3412, 3416, 3420, - 3424, 3428, 3432, 3436, 3440, 3444, 3448, 3452, 3456, 3460, - 3464, 3468, 3472, 3476, 3480, 3484, 3488, 3492, 3496, 3500, - 3507, 3511, 3515, 3519, 3523, 3527, 3531, 3535, 3539, 3545, - 3551, 3555, 3561, 3568, 3572, 3576, 3580, 3584, 3588, 3592, - 3596, 3600, 3604, 3608, 3612, 3616, 3620, 3624, 3628, 3632, - 3646, 3650, 3654, 3658, 3662, 3666, 3670, 3674, 3686, 3690, - 3694, 3698, 3702, 3713, 3724, 3728, 3732, 3736, 3740, 3744, - 3748, 3752, 3756, 3760, 3764, 3768, 3772, 3776, 3780, 3784, - 3788, 3792, 3796, 3800, 3804, 3808, 3812, 3816, 3820, 3824, - 3828, 3832, 3836, 3840, 3847, 3851, 3855, 3859, 3863, 3867, - 3871, 3875, 3879, 3885, 3891, 3899, 3903, 3907, 3911, 3918, - 3928, 3934, 3940, 3950, 3962, 3970, 3974, 4004, 4008, 4012, - 4016, 4020, 4024, 4030, 4034, 4038, 4042, 4053, 4057, 4061, - 4065, 4073, 4077, 4081, 4087, 4098 + 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 @@ -1032,33 +1035,35 @@ static const char *const yytname[] = "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", "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", "KW_PROTECTED", "KW_PUBLIC", "KW_REGISTER", - "KW_REINTERPRET_CAST", "KW_RETURN", "KW_SHORT", "KW_SIGNED", "KW_SIZEOF", - "KW_STATIC", "KW_STATIC_ASSERT", "KW_STATIC_CAST", "KW_STRUCT", - "KW_TEMPLATE", "KW_THREAD_LOCAL", "KW_THROW", "KW_TRUE", "KW_TRY", - "KW_TYPEDEF", "KW_TYPEID", "KW_TYPENAME", "KW_UNDERLYING_TYPE", - "KW_UNION", "KW_UNSIGNED", "KW_USING", "KW_VIRTUAL", "KW_VOID", - "KW_VOLATILE", "KW_WCHAR_T", "KW_WHILE", "START_CPP", "START_CONST_EXPR", - "START_TYPE", "'{'", "','", "';'", "':'", "'='", "'?'", "'|'", "'^'", - "'&'", "'<'", "'>'", "'+'", "'-'", "'*'", "'/'", "'%'", "'~'", "'.'", - "'('", "'['", "')'", "'}'", "'!'", "']'", "$accept", "grammar", "cpp", + "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", + "KW_PROTECTED", "KW_PUBLIC", "KW_REGISTER", "KW_REINTERPRET_CAST", + "KW_RETURN", "KW_SHORT", "KW_SIGNED", "KW_SIZEOF", "KW_STATIC", + "KW_STATIC_ASSERT", "KW_STATIC_CAST", "KW_STRUCT", "KW_TEMPLATE", + "KW_THREAD_LOCAL", "KW_THROW", "KW_TRUE", "KW_TRY", "KW_TYPEDEF", + "KW_TYPEID", "KW_TYPENAME", "KW_UNDERLYING_TYPE", "KW_UNION", + "KW_UNSIGNED", "KW_USING", "KW_VIRTUAL", "KW_VOID", "KW_VOLATILE", + "KW_WCHAR_T", "KW_WHILE", "START_CPP", "START_CONST_EXPR", "START_TYPE", + "'{'", "','", "';'", "':'", "'='", "'?'", "'|'", "'^'", "'&'", "'<'", + "'>'", "'+'", "'-'", "'*'", "'/'", "'%'", "'~'", "'.'", "'('", "'['", + "')'", "'}'", "'!'", "']'", "$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", @@ -1067,12 +1072,12 @@ static const char *const yytname[] = "parameter_pack_identifier", "not_paren_empty_instance_identifier", "empty_instance_identifier", "type", "type_pack", "type_decl", "predefined_type", "var_type_decl", "full_type", "struct_attributes", - "anonymous_struct", "$@14", "named_struct", "$@15", "maybe_final", + "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", - "$@16", "$@17", "using_declaration", "simple_type", "simple_int_type", - "simple_float_type", "simple_void_type", "code", "$@18", "code_block", + "$@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", @@ -1100,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, 399, 400, 123, 44, 59, 58, - 61, 63, 124, 94, 38, 60, 62, 43, 45, 42, - 47, 37, 126, 46, 40, 91, 41, 125, 33, 93 + 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 -922 +#define YYPACT_NINF -926 #define yypact_value_is_default(Yystate) \ - (!!((Yystate) == (-922))) + (!!((Yystate) == (-926))) -#define YYTABLE_NINF -751 +#define YYTABLE_NINF -759 #define yytable_value_is_error(Yytable_value) \ 0 @@ -1120,160 +1126,163 @@ static const yytype_uint16 yytoknum[] = STATE-NUM. */ static const yytype_int16 yypact[] = { - 135, -922, 3702, 5708, 37, 4828, -922, -922, -922, -922, - -922, -922, -922, -922, -125, -122, -95, -89, -85, -71, - -59, -41, -50, -922, -922, -38, 12, 28, 42, 55, - 75, 78, 130, 156, 185, 194, 203, 217, 224, 252, - 287, 294, 296, 303, 6019, -922, -922, -37, 307, 311, - 14, -20, -922, 313, 326, 332, 3702, 3702, 3702, 3702, - 3702, 1776, 1058, 3702, 4709, -922, 77, -922, -922, -922, - -922, -922, -922, -922, -922, 5818, 336, -922, -23, -922, - -922, 4057, 4449, 4449, -922, 3303, 344, -922, 4449, -922, - -922, 59, 59, -922, -922, -922, -922, 20, 48, -922, - -922, -922, -922, -922, -922, 468, 345, -922, 6767, 6767, - 6767, -922, 6767, 5186, 6767, 39, -922, 6758, 347, 348, - 352, 354, 6767, 1630, 52, 100, 186, 6767, 6767, 359, - 6638, 6767, 6767, 5704, 6767, 6767, -922, -922, -922, -922, - 4196, -922, -922, -922, -922, -922, 3702, 3702, 5708, 3702, - 3702, 3702, 3702, 5708, 3702, 5708, 3702, 5708, 3702, 5708, - 5708, 5708, 5708, 5708, 5708, 5708, 5708, 5708, 5708, 5708, - 5708, 5708, 5708, 5708, 3702, -922, -922, 362, 3303, 370, - 373, 3303, -922, -922, 5708, 3702, 3702, 374, 5304, 5708, - 1776, 3702, 3702, 51, 51, 51, 51, 51, -125, -95, - -89, -85, -71, -41, -38, 28, 4337, 4997, 5357, 5612, - 332, 87, -103, 4709, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, 3303, 3303, -98, 191, -922, - -922, 51, 3702, 3702, 3702, 3702, 3702, 3702, 3702, 3702, - 3702, 3702, 3702, 3702, 3702, 3702, 3702, 3702, 3702, 3702, - 3702, 3702, 3702, 2764, 3702, -922, -922, 59, 59, 2898, - -922, -922, -922, 4449, -922, -922, -922, -922, 5708, -922, - 262, 257, 162, 59, 59, 162, 162, 4941, 188, -922, - 367, -922, -922, -922, -922, -922, -922, 4431, 278, 5129, - -922, 3303, 491, 395, 379, 2486, 5213, 6767, -922, -922, - -922, -922, 6767, -922, -922, -922, -922, 6730, 2901, -922, - 3303, 3303, 3303, 3303, -922, -922, 402, -922, -922, -922, - -922, -922, 3702, -922, 4289, -922, 397, -922, 4375, -922, - 3303, 216, -922, -922, 385, 387, -922, 392, 5904, 3303, - 407, -922, 3303, -21, 324, 425, -922, -922, -922, -922, - 1115, -922, -922, 408, 427, -922, 412, 418, 428, 429, - 432, 433, 423, 434, 437, 441, 443, 444, 449, 456, - 462, -69, 482, 464, 467, 469, 470, 472, 473, 479, - 481, 483, 485, 492, 3702, -922, 5708, 3702, -922, 5260, - 501, 493, 494, 3303, 508, 509, 528, 520, 4060, 522, - 523, 3702, 3702, -922, 633, -922, 1031, 532, 3702, -922, - -922, 4026, 4994, 507, 507, 569, 569, 405, 405, -922, - 3054, 1798, 1271, 1600, 569, 569, 683, 683, 51, 51, - 51, -922, -922, -46, 2253, -922, -922, 533, 4444, 534, - 162, 536, 545, 3303, 162, 162, 162, 162, 162, 541, - -922, 188, -922, 188, -922, 541, 541, -922, 162, 468, - 5794, 5679, 162, 162, 543, 7, -922, 442, 396, -922, - 3702, 3303, 546, -922, -922, -922, -922, 4431, -12, -8, - 10, 468, 548, 71, -922, -922, -922, 561, 6767, 468, - 1917, -125, 549, 4501, -922, -922, -922, 578, 588, 590, - 591, 598, 6134, -922, 3855, 5453, 353, 582, 324, -922, - -922, 577, -922, 5708, -922, 21, 3032, 6043, 1099, -922, - 5708, -922, 583, -922, -922, 3303, 66, -922, -922, -922, - 2625, -922, -922, 413, -922, 599, 5129, -922, -922, -922, - -922, -922, -922, -922, 585, -922, 602, -922, -922, -922, - -922, 5708, -922, 5708, -922, 5708, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, 4522, 611, 612, - -922, 606, -922, -922, 614, -922, -922, 618, -922, -922, - -922, -922, 51, 4709, -922, 3303, 191, 5559, 1592, -922, - 4709, 3702, -922, -922, -922, -922, -922, 541, 162, -922, - 541, 541, 541, 541, 541, 3702, 63, 706, 5818, 442, - 396, -922, 98, 120, -922, -922, 5588, 625, 442, 442, - 442, 442, 442, 442, -70, -922, -922, 627, 3303, 396, - 396, 396, 396, 396, 396, -33, 623, 4709, -922, -58, - -922, 641, 747, 2486, -922, 720, 468, -922, -922, -922, - -922, -922, -922, -922, -922, 635, 647, 650, -922, -922, - 6019, -922, -922, 651, 38, 654, -922, 646, 3702, 3702, - 3702, 3702, 1776, 3702, 645, 25, -922, -922, 4763, -922, - 77, -922, 6767, 6767, 6206, -922, 803, 805, 806, 818, - -922, -922, 376, 682, -922, -922, -922, -922, 5521, -922, - 676, 688, 3169, -922, 1035, -922, -922, 21, -922, 413, - -922, 690, 5559, 680, 413, 5559, 679, 4540, 1099, 692, - 1099, 1099, 1099, 1099, 1099, 167, -922, -922, 686, 6278, - -922, 689, -922, 290, -922, -113, 701, 705, 691, 707, - 711, 3166, 3322, 708, 413, 413, 4129, 413, 413, 413, - 413, -922, 61, 411, -922, 4431, -922, 3702, 3702, 699, - 702, 703, -922, -922, -922, 3702, -922, 3702, -922, 709, - -922, 5933, 468, -922, -922, -922, -922, -922, -922, 712, - -922, -922, 725, -922, 4709, 541, 704, 715, 5679, 442, - 396, -70, -33, 729, 732, 1592, -922, -922, 442, 740, - 740, 740, 740, 740, 298, 3702, -922, 396, -922, 742, - 742, 742, 742, 742, 321, 3702, -922, 743, -922, 3702, - -922, 731, 4558, 6350, -922, 760, -922, -922, 5708, 5708, - 5708, 746, 5708, 749, 5333, 5708, 1776, 51, 51, 51, - 51, 750, -26, 51, -922, -922, 3836, 3702, 3702, 3702, - 3702, 3702, 3702, 3702, 3702, 3702, 3702, 3702, 3702, 3702, - 3702, 3702, 3702, 3702, 3702, 3702, 3702, 3702, 3300, 3702, - -922, -922, -922, -922, -14, 768, 771, 772, 6422, 17, - -922, 1035, 6647, 5453, 3303, 767, 763, 1035, 1035, 1035, - 1035, 1035, 1035, 85, 742, -922, 411, -922, 757, 413, - 209, 758, -922, -922, 329, 1099, 775, 775, 775, 775, - 775, -922, 3702, -922, -922, 5559, -922, 2319, -922, -922, - 3303, 3702, 3702, -922, -922, -922, -922, -922, 3166, 761, - 789, 4709, -922, -922, 413, 341, 341, 927, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, 776, 774, -922, -922, 341, 341, - 341, 267, 937, -922, 3702, -922, 2625, 797, -922, 641, - 58, 89, -922, -922, -922, 93, 94, -922, 6019, 59, - 898, 132, -922, -922, 5559, -922, -70, -33, -922, -922, - 5559, 5559, -922, 740, 783, 802, 742, 808, 804, 3590, - -922, -922, -922, 3919, 828, 829, -922, 812, 826, 831, - 3702, 832, 3303, 823, 825, 836, 827, 4595, 3702, -922, - -922, -922, 4026, 4994, 507, 507, 569, 569, 405, 405, - -922, 4613, 1798, 1271, 1600, 569, 569, 683, 683, 51, - 51, 51, -922, -922, 96, 2505, 6494, 984, 847, 987, - 990, 992, -922, 856, 85, 742, -922, -922, -922, -922, - -922, -922, 5708, 1035, 3975, -922, -922, 860, -922, -922, - 276, 844, -922, -922, 775, 5559, 843, 848, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, -922, -922, -922, -922, -922, -922, 846, -922, - 850, 849, 851, -922, 3434, 341, -922, -922, -922, -922, - -922, 1917, 855, 3322, 413, -922, -922, -922, -922, 1592, - 59, -922, -922, -922, 2, 858, 861, -922, -922, 865, - 866, 5559, -922, 5559, -922, -922, 5778, 6003, 6052, 3303, - 368, -922, -922, 1011, -922, 3919, -922, 869, 872, 871, - 876, 878, -922, -922, 887, -922, -922, 51, 3702, -922, - -922, -922, 99, -922, 104, 895, 145, -922, -922, -922, - 899, 919, 920, 921, 40, 923, 3975, 3975, 3975, 3975, - 3975, 1776, 3975, 4211, -922, 413, 2679, 915, -922, 2679, - 5559, 914, -922, -922, 2094, -922, -922, 1066, -922, 3166, - 4709, 917, -922, -922, 938, -922, 926, -922, -922, -922, - -922, -922, 932, 933, 4191, -922, 4191, -922, 4191, -922, - -922, 4191, 4191, 4191, -922, 6566, -922, 3702, 3702, -922, - 3702, -922, 3702, 4709, 1075, 939, 1076, 941, 953, 1092, - 955, 5708, 5708, 5708, 5708, 940, 5424, 5708, 67, 67, - 67, 67, 67, 947, 149, 67, 3975, 3975, 3975, 3975, - 3975, 3975, 3975, 3975, 3975, 3975, 3975, 3975, 3975, 3975, - 3975, 3975, 3975, 3975, 3975, 3568, 3702, -922, -922, 5559, - 948, -922, 2679, -922, -922, 954, -922, -922, -922, 1592, - 1592, 1592, -922, -922, -922, -922, -922, -922, -922, -922, - -922, 153, 171, 177, 181, 949, -922, 958, -922, -922, - 189, -922, 957, 950, 964, 972, 3303, 970, 971, 982, - 3975, -922, 4820, 5010, 1350, 1350, 604, 604, 662, 662, - -922, 1203, 5026, 5050, 5066, 770, 770, 67, 67, 67, - -922, -922, 193, 2782, 5559, 973, -922, 2679, -922, 2679, - 974, -922, -922, -922, 2679, 2679, -922, -922, -922, -922, - 994, 1130, 1135, 1000, -922, 985, 986, 988, 989, -922, - -922, 993, 67, 3975, -922, -922, 995, -922, 2679, -922, - -922, 996, 998, -922, 3702, 3702, 3702, -922, 3702, 4211, - -922, 1592, 1006, 1008, 205, 210, 214, 263, 1592, -922, - -922, -922, -922, -922, -922, -922, -922 + 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. @@ -1281,192 +1290,195 @@ static const yytype_int16 yypact[] = means the default is an error. */ static const yytype_uint16 yydefact[] = { - 0, 755, 0, 0, 0, 755, 5, 644, 640, 643, - 751, 752, 646, 647, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 642, 648, 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, 650, 649, 0, 0, 0, - 0, 0, 641, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 755, 0, 3, 581, 645, 291, 302, 301, - 385, 386, 388, 389, 370, 0, 0, 400, 367, 399, - 394, 391, 390, 393, 371, 0, 0, 372, 392, 402, - 387, 755, 755, 4, 293, 294, 295, 0, 356, 755, - 290, 382, 383, 384, 1, 0, 0, 21, 755, 755, - 755, 22, 755, 755, 755, 0, 38, 755, 0, 0, - 0, 0, 755, 0, 0, 0, 0, 755, 755, 0, - 755, 755, 755, 0, 755, 755, 6, 17, 7, 19, - 0, 15, 16, 18, 68, 40, 755, 755, 0, 755, - 755, 755, 755, 0, 755, 0, 755, 0, 755, 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, 0, 0, 755, 317, 323, 0, 0, 0, - 605, 0, 755, 316, 0, 755, 755, 0, 0, 0, - 0, 755, 755, 614, 612, 611, 613, 610, 291, 385, - 386, 388, 389, 400, 399, 394, 391, 390, 393, 392, - 387, 0, 0, 540, 737, 738, 739, 740, 743, 741, - 745, 744, 742, 746, 726, 727, 0, 0, 755, 732, - 725, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 753, 754, 755, 755, 0, - 368, 369, 401, 391, 396, 395, 398, 292, 0, 397, - 0, 277, 755, 755, 755, 755, 755, 755, 0, 326, - 276, 328, 755, 747, 748, 749, 750, 0, 358, 0, - 330, 0, 0, 58, 60, 0, 755, 755, 52, 41, - 51, 53, 755, 42, 147, 47, 23, 755, 0, 45, - 0, 0, 0, 0, 50, 755, 0, 26, 25, 24, - 48, 44, 0, 151, 0, 150, 0, 54, 0, 20, - 0, 0, 46, 49, 325, 304, 315, 0, 0, 0, - 0, 13, 0, 0, 0, 324, 63, 306, 307, 308, - 356, 755, 303, 0, 539, 538, 0, 0, 0, 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, 0, 0, 0, 318, 0, 755, 320, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 639, 730, 733, 0, 755, 0, 728, - 205, 623, 624, 625, 626, 627, 628, 631, 632, 638, - 0, 620, 621, 622, 629, 630, 618, 619, 615, 616, - 617, 637, 636, 0, 0, 327, 329, 0, 0, 0, - 755, 278, 0, 267, 755, 755, 755, 755, 755, 283, - 266, 0, 279, 0, 280, 282, 281, 190, 755, 0, - 0, 0, 755, 755, 0, 191, 194, 755, 0, 189, - 755, 364, 0, 361, 360, 355, 359, 0, 737, 738, - 739, 0, 0, 741, 334, 296, 336, 0, 755, 0, - 755, 304, 0, 0, 43, 39, 755, 0, 0, 0, - 0, 0, 755, 373, 0, 755, 325, 304, 0, 324, - 71, 0, 379, 0, 76, 78, 0, 0, 755, 305, - 0, 755, 0, 403, 207, 0, 0, 66, 403, 212, - 0, 67, 65, 0, 311, 358, 0, 588, 587, 604, - 594, 590, 592, 593, 0, 600, 0, 599, 653, 589, - 654, 0, 656, 0, 657, 0, 660, 661, 662, 663, - 664, 665, 666, 667, 668, 669, 596, 0, 0, 0, - 319, 0, 595, 598, 0, 602, 601, 0, 607, 608, - 597, 591, 582, 541, 731, 0, 755, 755, 755, 91, - 206, 0, 635, 634, 299, 298, 300, 284, 755, 268, - 273, 269, 270, 272, 271, 755, 0, 0, 0, 755, - 0, 231, 0, 0, 755, 193, 0, 0, 755, 755, - 755, 755, 755, 755, 755, 245, 244, 0, 255, 0, - 0, 0, 0, 0, 0, 755, 0, 537, 536, 365, - 354, 297, 0, 0, 755, 755, 0, 55, 59, 718, - 714, 717, 720, 721, 197, 0, 0, 0, 716, 722, - 0, 724, 723, 0, 0, 0, 715, 0, 0, 0, - 0, 0, 0, 0, 0, 198, 233, 201, 234, 670, - 719, 196, 755, 755, 755, 375, 0, 0, 0, 0, - 377, 755, 0, 0, 168, 169, 170, 156, 0, 157, - 0, 153, 158, 154, 755, 167, 152, 0, 73, 0, - 381, 0, 755, 0, 0, 755, 0, 0, 755, 0, - 755, 755, 755, 755, 755, 0, 236, 235, 0, 755, - 80, 0, 755, 0, 8, 0, 0, 0, 0, 0, - 0, 755, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 64, 755, 755, 171, 0, 309, 0, 0, 0, - 0, 0, 321, 322, 606, 0, 603, 0, 729, 0, - 98, 0, 0, 92, 100, 95, 99, 94, 96, 0, - 93, 97, 0, 186, 633, 274, 0, 0, 0, 755, - 0, 755, 755, 0, 0, 755, 192, 195, 755, 250, - 246, 247, 249, 248, 0, 755, 225, 0, 256, 261, - 257, 258, 260, 259, 0, 755, 228, 285, 362, 0, - 331, 0, 0, 755, 339, 755, 338, 62, 0, 0, - 0, 680, 0, 0, 0, 0, 0, 688, 687, 686, - 685, 0, 0, 684, 61, 200, 0, 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, - 57, 56, 378, 755, 0, 0, 0, 0, 755, 0, - 37, 755, 755, 0, 161, 159, 0, 755, 755, 755, - 755, 755, 755, 755, 165, 72, 755, 380, 0, 0, - 0, 0, 313, 312, 0, 755, 241, 237, 238, 240, - 239, 86, 755, 314, 14, 755, 208, 404, 405, 403, - 0, 755, 755, 210, 211, 213, 215, 216, 755, 0, - 219, 221, 218, 214, 0, 178, 174, 0, 115, 116, - 117, 118, 119, 120, 123, 124, 139, 127, 128, 129, - 130, 131, 132, 133, 134, 135, 136, 137, 138, 143, - 142, 126, 125, 112, 114, 113, 121, 122, 110, 111, - 107, 108, 109, 106, 0, 0, 105, 172, 175, 177, - 176, 0, 0, 182, 755, 184, 0, 0, 69, 310, - 0, 0, 655, 658, 659, 0, 0, 755, 0, 755, - 0, 0, 403, 275, 755, 232, 755, 755, 226, 229, - 755, 755, 286, 251, 254, 0, 262, 265, 0, 366, - 333, 332, 335, 0, 0, 341, 340, 0, 0, 0, - 755, 0, 0, 0, 0, 0, 0, 0, 0, 713, - 199, 202, 697, 698, 699, 700, 701, 702, 705, 706, - 712, 0, 694, 695, 696, 703, 704, 692, 693, 689, - 690, 691, 711, 710, 0, 0, 755, 0, 0, 0, - 0, 0, 374, 0, 755, 166, 146, 144, 149, 145, - 155, 162, 0, 755, 0, 163, 203, 0, 74, 755, - 0, 0, 755, 88, 242, 755, 0, 0, 407, 408, - 412, 409, 417, 410, 411, 413, 414, 415, 416, 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, - 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, - 459, 460, 461, 462, 463, 464, 465, 485, 466, 467, - 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, - 478, 479, 480, 481, 482, 483, 484, 486, 487, 488, - 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, - 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, - 509, 510, 511, 512, 755, 529, 530, 531, 522, 534, - 518, 519, 517, 524, 525, 513, 514, 515, 516, 523, - 521, 528, 526, 532, 527, 520, 533, 406, 0, 9, - 0, 0, 0, 217, 220, 179, 173, 141, 140, 181, - 185, 755, 0, 206, 0, 585, 584, 586, 583, 755, - 755, 187, 104, 101, 0, 0, 0, 227, 230, 0, - 0, 755, 252, 755, 263, 363, 745, 0, 744, 0, - 0, 342, 344, 734, 755, 0, 679, 0, 0, 0, - 0, 0, 677, 676, 0, 682, 683, 671, 0, 709, - 708, 376, 0, 27, 0, 0, 0, 36, 164, 160, + 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, 0, 0, 204, 542, 0, 77, 0, 82, 79, - 755, 0, 243, 755, 0, 209, 12, 10, 222, 755, - 223, 0, 180, 70, 0, 188, 0, 102, 651, 755, - 755, 755, 0, 0, 0, 347, 0, 346, 0, 345, - 735, 0, 0, 0, 736, 755, 343, 0, 0, 681, - 0, 678, 0, 707, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 557, 555, - 554, 556, 553, 0, 0, 552, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 75, 84, 755, - 0, 755, 81, 535, 11, 0, 755, 403, 103, 755, - 755, 755, 755, 755, 353, 352, 351, 350, 349, 348, - 337, 0, 0, 0, 0, 0, 28, 0, 33, 35, - 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 580, 566, 567, 568, 569, 570, 571, 572, 573, - 579, 0, 563, 564, 565, 561, 562, 558, 559, 560, - 578, 577, 0, 0, 755, 0, 755, 87, 224, 183, - 0, 289, 288, 287, 253, 264, 674, 673, 675, 672, - 0, 0, 0, 0, 551, 0, 0, 0, 0, 549, - 548, 0, 543, 0, 576, 575, 0, 755, 89, 652, - 29, 0, 0, 31, 0, 0, 0, 550, 0, 574, - 755, 755, 0, 0, 0, 0, 0, 0, 755, 83, - 34, 32, 546, 545, 547, 544, 85 + 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[] = { - -922, -922, -284, -922, 238, -922, -922, 853, -127, -922, - 195, -424, 519, -126, -922, -78, -922, -922, -148, -922, - -922, -922, 840, -922, -922, -922, -922, -922, -582, -922, - -922, -111, -922, -922, -922, -922, 286, 474, -641, -922, - -691, -728, -576, -922, -64, -922, 108, -558, -922, -492, - -921, -922, -434, 337, -608, 241, 389, 609, -87, 24, - 86, -287, -647, 859, 230, -162, -130, -922, -128, -922, - -922, -922, -922, -91, -121, -922, -471, -922, -922, -18, - 18, -922, -922, -922, -922, -29, 81, -922, -922, -514, - -922, -16, -922, -567, -104, -60, 288, 716, 90, -922, - -922, -922, 787, -367, 1319, -68, -481, -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, 733, 734, 137, 521, 138, 139, 307, - 140, 292, 293, 141, 533, 751, 329, 709, 895, 343, - 712, 715, 344, 915, 1409, 1474, 1095, 1320, 588, 977, - 1078, 142, 326, 700, 701, 702, 703, 704, 752, 1241, - 753, 782, 464, 465, 674, 675, 1085, 409, 527, 531, - 929, 930, 466, 677, 725, 799, 809, 278, 279, 91, - 92, 345, 180, 346, 93, 289, 94, 644, 95, 645, - 825, 1024, 1025, 1271, 96, 97, 475, 471, 472, 98, - 99, 143, 691, 873, 144, 100, 101, 102, 103, 731, - 732, 917, 1227, 636, 353, 354, 1313, 213, 65, 678, - 679, 227, 228, 1272, 1273, 625, 66, 145 + -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 @@ -1474,1008 +1486,1027 @@ static const yytype_int16 yydefgoto[] = number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int16 yytable[] = { - 6, 212, 304, 323, 325, 281, 641, 1232, 492, 680, - 347, 769, 348, 831, 738, 183, 708, 1336, 896, 349, - 389, 146, 615, 255, 985, 256, 181, 611, 768, 187, - 288, 502, 795, 921, 713, 607, 260, 104, 786, 147, - 845, 509, 148, 356, 402, 358, 359, 360, 361, 406, - 363, 922, 365, 833, 367, 1375, 676, 642, 283, 284, - 285, 230, 182, 403, -747, 648, 806, 1012, -748, 149, - 383, 407, 270, 900, 271, 150, 272, 816, 553, 151, - 408, 391, 392, 255, 240, 256, -749, 399, 400, 818, - 280, 280, 819, 152, 771, 805, 153, 554, 290, 257, - 1394, 402, 261, 935, 936, 155, 978, 979, 980, 981, - 904, 352, 906, 907, 908, 909, 910, 408, 184, 273, - 592, 402, 350, 154, 999, 523, 156, 524, 525, 526, - 736, 737, 815, 1067, -747, 189, 898, -747, -748, 901, - 1039, -748, 214, 215, 216, 355, 355, -750, 355, 355, - 355, 355, 1068, 355, 616, 355, -749, 355, 351, -749, - 286, 258, 264, 265, 266, 347, 282, 348, 1337, 269, - 435, 436, 846, 355, 349, 442, 157, 443, 188, 444, - 714, 290, 797, 1073, 355, 355, 452, 454, 306, 536, - 355, 355, 158, 433, 347, 987, 348, 287, 347, 274, - 348, 317, 834, 349, 1376, 402, 159, 349, 217, 218, - 982, 879, 684, 275, 252, 253, 254, -750, 276, 160, - -750, 486, 445, 277, 1245, 983, 984, 410, 605, 787, - 1404, 1405, 1406, 1008, 1009, 1084, 402, 729, 1015, 161, - 402, 402, 162, 402, 219, 220, 1364, 221, 1018, 318, - 805, 1366, 222, 401, 223, 1246, 280, 280, 1090, 1247, - 1248, 988, 1289, 605, 793, 1365, 352, 214, 215, 216, - 1367, 450, 280, 280, 450, 450, 469, 350, 1, 2, - 3, 473, 535, 569, 989, 605, 794, 264, 265, 266, - 269, 211, 1369, 1235, 163, 352, 402, 1094, 1253, 352, - 402, 467, 446, 298, 299, 300, 350, 301, 303, 305, - 350, 1370, 309, 351, 6, 1451, 447, 314, 402, 1486, - 164, 448, 320, 321, 402, 324, 327, 328, 402, 332, - 333, 911, 912, 217, 218, 319, 1492, 1487, 1087, 1097, - 402, 408, 351, 1488, 264, 1096, 351, 1489, 1000, 165, - 290, 1250, 402, 470, 1005, 1493, 821, 402, 166, 1504, - 823, 402, 518, 468, 512, 680, 513, 167, 476, 219, - 220, 1532, 221, 983, 984, 1091, 1533, 222, 357, 223, - 1534, 168, 10, 362, 11, 364, 355, 366, 169, 368, - 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, - 379, 380, 381, 382, 1088, 1228, 589, 878, 1415, 627, - 402, 628, 676, 629, 390, 1249, 170, 1242, 395, 396, - 397, 440, -267, 283, 284, 285, 743, 477, 1256, 1535, - 744, 983, 984, 1239, 1259, 1260, 919, 920, 240, 450, - 983, 984, 1317, 450, 450, 450, 450, 450, 1257, 1258, - 726, 171, 283, 284, 285, 617, 630, 450, 172, 618, - 173, 450, 450, 805, 1014, 754, 626, 174, 486, 638, - 528, 185, 529, 745, 530, 186, 705, 190, 214, 215, - 216, 1351, 1352, 1353, 609, 467, 815, 1017, 1255, 681, - 191, 303, 309, 1093, 912, 6, 192, 494, 439, -90, - 259, -90, 619, -90, 706, 983, 984, 1316, 268, 295, - 1319, 310, 311, 347, 467, 348, 312, 727, 313, 1321, - 6, 1334, 349, 322, 746, 286, 384, 236, 237, 238, - 239, -90, -266, -90, 386, -90, 631, 387, 393, 488, - 240, 718, 489, 490, 217, 218, 610, 468, 503, 514, - 632, 515, 505, 747, 286, 633, 516, 528, 476, 529, - 634, 986, 247, 248, 249, 250, 251, 748, 252, 253, - 254, 520, 749, 532, 402, 537, 468, 750, 538, 544, - 219, 220, 620, 221, 539, 410, 469, 783, 222, 1066, - 223, 238, 239, 546, 540, 541, 621, 450, 542, 543, - 545, 622, 240, 551, 638, 291, 623, 547, 626, 548, - 549, 467, 842, 589, 352, 550, 568, 626, 626, 626, - 626, 626, 626, 410, 896, 350, 1392, 1393, 552, 555, - 556, 183, 789, 557, 410, 558, 559, 1394, 560, 561, - 467, 754, 181, 6, 826, 562, 754, 563, 584, 564, - 726, 565, 726, 726, 726, 726, 726, 571, 566, 572, - 573, 351, 245, 246, 247, 248, 249, 250, 251, 705, - 252, 253, 254, 468, 575, 576, 754, 754, 182, 754, - 754, 754, 754, 647, 577, 1342, 578, 1343, 580, 581, - 6, 1481, 1482, 1483, 790, 1394, 587, 990, 991, 594, - 596, -268, 468, 626, 598, 995, 605, 996, 624, 614, - 646, 469, 643, 640, 469, 682, 240, 727, 64, 727, - 727, 727, 727, 727, 685, 710, 247, 248, 249, 250, - 251, 918, 252, 253, 254, 686, 467, 687, 688, 467, - 932, 1412, 183, 711, 1410, 689, 707, 730, 755, 757, - 728, 783, 410, 181, 788, 1076, 1077, 1419, 1420, 1421, - 680, 1399, 1400, 1401, 1402, 1403, 758, 1404, 1405, 1406, - 765, 1079, 193, 194, 195, 196, 197, 763, 764, 231, - 766, 759, 767, 760, 798, 761, 807, -357, 626, 182, - 410, 410, 817, 1529, 783, 820, 824, 626, 468, 828, - 1536, 468, 829, 1394, 638, 830, 832, 676, 1064, 835, - 836, 844, 467, 874, 638, 875, 876, 1230, 1231, 1399, - 1400, 1401, 1402, 1403, 1026, 1404, 1405, 1406, 877, 1477, - 880, 754, 882, 1475, 1479, 883, 476, 726, 897, 899, - 1484, 1485, 249, 250, 251, 902, 252, 253, 254, 923, - 791, 905, 913, 924, 705, 926, 916, 635, 925, 927, - 800, 801, 802, 803, 804, 992, 754, 934, 993, 994, - 467, 1002, 6, 1003, 468, 997, 1001, 870, 871, 1004, - 626, 449, 451, 453, 455, 456, 626, 626, 626, 626, - 626, 626, 1086, 1010, 1508, 410, 1011, 1020, 1506, 1345, - 1347, 1349, 841, 1480, 727, 805, 398, 815, -275, 1023, - 1030, 638, 1251, 1032, 469, 1069, 1038, 1082, 1070, 1071, - 355, 355, 1083, 1089, 1092, 1521, 1279, 932, 1233, 1401, - 1402, 1403, 468, 1404, 1405, 1406, 1234, 1236, 1528, 467, - 912, 1240, 1237, 1238, 1244, 893, 1252, 1261, 411, 412, - 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, - 423, 424, 425, 426, 427, 428, 429, 430, 431, 183, - 434, 1262, 1263, 1264, 1274, 438, 1275, 1424, 1276, 1425, - 181, 1426, 1277, 638, 1427, 1428, 1429, 1278, 1280, 1282, - 1355, 1283, 1284, 1285, 1292, 1293, 589, 1294, 280, 792, - 1295, 468, 1296, 469, 1297, 410, 410, 1315, 1318, 469, - 469, 493, 1322, 1325, 1323, 1327, 182, 1326, 1328, 810, - 811, 812, 813, 814, 1332, 1338, 1354, 1339, 467, 355, - 1006, 1340, 1341, 1357, 467, 467, 1358, 1359, 504, 1013, - 1360, 214, 215, 216, 1361, 283, 284, 285, 886, 597, - 628, 1362, 887, 600, 601, 602, 603, 604, 1027, 1028, - 1029, 1368, 1031, 1371, 1034, 1035, 1036, 606, 214, 215, - 216, 612, 613, 1086, 1372, 1373, 1374, 324, 1377, 1408, - 1411, 1414, 626, 1416, 1417, 1435, 1437, 1436, 589, 1438, - 468, 589, 1418, 894, 469, 888, 468, 468, 1422, 1423, - 567, 1439, 1440, 1441, 1446, 1491, 1495, 217, 218, 283, - 284, 285, 719, 1450, 1476, 1490, 720, 582, 583, 467, - 1496, 1478, 1074, 1494, 590, 478, 479, 480, 1497, 800, - 801, 802, 803, 804, 217, 218, 1499, 1500, 1501, 1507, - 1511, 1509, 1510, 219, 220, 1512, 221, 286, 1513, 1514, - 1515, 222, 1516, 223, 1530, 1517, 1531, 1518, 1229, 721, - 495, 1520, 1522, 1335, 1523, 827, 1333, 1407, 508, 1080, - 219, 220, 881, 221, 1314, 889, 754, 1331, 222, 1007, - 223, 468, 1298, 1041, 1356, 585, 637, 510, 1324, 890, - 226, 217, 218, 586, 891, 0, 1016, 0, 0, 892, - 0, 0, 0, 918, 0, 0, 0, 785, 224, 0, - 0, 286, 225, 0, 0, 0, 0, 226, 0, 1386, - 1387, 1388, 1389, 1390, 1391, 1392, 1393, 483, 220, 0, - 221, 0, 717, 0, 0, 222, 1394, 223, 0, 722, - 681, 0, 0, 0, 0, 0, 742, 754, 783, 280, - 0, 1384, 0, 723, 0, 0, 0, 0, 724, 0, - 469, 0, 469, 0, 287, 467, 0, 0, 0, 0, - 1075, 0, 0, 6, 0, 0, 0, 810, 811, 812, - 813, 814, 0, 0, 0, 467, 0, 467, 0, 234, - 235, 236, 237, 238, 239, 0, 0, 1431, 1432, 0, - 1433, 0, 1434, 0, 240, 0, 0, 784, 0, 0, - 0, 0, 1299, 0, 0, 0, 0, 0, 0, 469, - 0, 637, 589, 0, 1013, 0, 0, 468, 932, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 589, 589, - 589, 0, 0, 0, 467, 1472, 0, 468, 0, 468, - 0, 0, 1503, 0, 1395, 1396, 1397, 1398, 0, 822, - 1399, 1400, 1401, 1402, 1403, 0, 1404, 1405, 1406, 0, - 1390, 1391, 1392, 1393, 0, 0, 0, 0, 0, 0, - 0, 229, 0, 1394, 837, 838, 839, 840, 0, 843, - 0, 0, 0, 0, 0, 0, 1314, 1314, 1314, 1314, - 1314, 0, 1314, 0, 267, 0, 468, 0, 469, 0, - 589, 0, 0, 0, 0, 589, 0, 0, 783, 783, - 783, 589, 589, 0, 294, 244, 245, 246, 247, 248, - 249, 250, 251, 467, 252, 253, 254, 0, 0, 0, - 0, 0, 316, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 331, 0, 1524, 1525, 1526, 931, 1527, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1016, 469, 0, 589, 1314, 1314, 1314, 1314, - 1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, - 1314, 1314, 1314, 1314, 1314, 468, 0, 385, 467, 0, - 388, 0, 0, 0, 0, 0, 589, 1399, 1400, 1401, - 1402, 1403, 0, 1404, 1405, 1406, 0, 0, 0, 589, - 783, 637, 0, 0, 0, 0, 0, 783, 0, 0, - 0, 637, 0, 0, 0, 1019, 0, 0, 0, 0, - 1314, 1383, 0, 0, 404, 405, 0, 0, 0, 0, - 0, 0, 1037, 0, 0, 0, 0, 0, 0, 0, - 468, 0, 0, 1042, 1043, 1044, 1045, 1046, 1047, 1048, - 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, - 1059, 1060, 1061, 1062, 0, 1065, 0, 0, 0, 0, - 441, 0, 0, 1314, 1378, 1379, 1380, 1381, 1382, 0, - 1385, 1442, 1443, 1444, 1445, 0, 1448, 1449, 485, 770, - 487, 0, 0, 0, 0, 0, 0, 0, 234, 235, - 236, 237, 238, 239, 0, 771, 0, 497, 637, 498, - 499, 500, 501, 240, 0, 0, 0, 0, 0, 772, - 214, 215, 216, 0, 931, 0, 0, 0, 0, 511, - 0, 0, 773, 774, 0, 0, 0, 0, 519, 0, - 0, 522, 0, 0, 0, 0, 0, 0, 775, 534, - 0, 0, 0, 0, 1452, 1453, 1454, 1455, 1456, 1457, - 1458, 1459, 1460, 1461, 1462, 1463, 1464, 1465, 1466, 1467, - 1468, 1469, 1470, 0, 0, 0, 0, 0, 776, 0, - 637, 777, 1243, 0, 778, 0, 217, 218, 570, 0, - 0, 0, 574, 0, 0, 0, 0, 0, 0, 0, - 779, 0, 0, 0, 0, 229, 0, 0, 0, 0, - 0, 0, 780, 0, 0, 0, 0, 0, 1502, 0, - 0, 0, 219, 220, 0, 221, 781, 0, 0, 0, - 222, 0, 223, 0, 1287, 245, 246, 247, 248, 249, - 250, 251, 599, 252, 253, 254, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 315, 0, 294, 7, - 8, 9, 10, 0, 11, 12, 13, 198, 68, 0, - 639, 1519, 0, 0, 0, 0, 0, 0, 0, 0, - 294, 0, 0, 0, 0, 0, 0, 0, 294, 0, - 0, 0, 0, 0, 0, 0, 234, 235, 236, 237, - 238, 239, 0, 0, 0, 0, 15, 69, 0, 0, - 199, 240, 200, 201, 202, 74, 75, 0, 20, 76, - 0, 0, 203, 22, 735, 0, 78, 0, 0, 0, - 0, 23, 24, 204, 0, 756, 0, 26, 0, 0, - 205, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 206, 0, 0, 0, - 0, 0, 0, 0, 44, 0, 45, 0, 46, 0, - 0, 0, 0, 47, 0, 207, 208, 50, 0, 0, - 51, 84, 0, 0, 404, 52, 0, 0, 53, 85, - 86, 87, 209, 0, 0, 89, 0, 210, 0, 0, - 649, 650, 651, 10, 0, 11, 652, 653, 67, 68, - 56, 0, 654, 57, 58, 59, 0, 0, 60, 0, - 61, 62, 0, 0, 63, 0, 0, 808, 0, 0, - 1330, 243, 244, 245, 246, 247, 248, 249, 250, 251, - 0, 252, 253, 254, 459, 294, 0, 655, 69, 0, - 0, 70, 0, 71, 72, 73, 74, 460, 0, 656, - 76, 0, 0, 77, 657, 0, 0, 78, 0, 0, - 0, 0, 658, 659, 79, 0, 0, 0, 0, 0, - 0, 80, 0, 0, 1363, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, - 0, 885, 0, 0, 0, 660, 0, 661, 0, 662, - 0, 0, 0, 461, 663, 0, 82, 83, 664, 0, - 0, 665, 84, 0, 0, 931, 666, 0, 0, 667, - 85, 86, 87, 88, 0, 0, 89, 0, 90, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 668, 0, 0, 669, 670, 0, 0, 0, 671, - 0, 672, 0, 0, 0, 673, 0, 0, 0, 0, - 0, 294, 0, 0, 0, 0, 0, 1098, 1099, 1100, - 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 0, 1109, - 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, - 0, 0, 1473, 0, 0, 0, 0, 1120, 1121, 1122, - 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, - 1133, 1134, 1135, 1136, 1137, 1138, 0, 0, 1139, 1140, - 1141, 1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, - 1151, 1152, 1153, 0, 1154, 0, 1155, 1156, 1157, 1158, - 1159, 1160, 1161, 1162, 1163, 0, 1164, 1165, 1166, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1167, 0, 0, 0, 0, 0, - 1168, 1169, 1170, 1081, 1171, 1172, 1173, 1174, 1175, 1176, - 1177, 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1186, - 1187, 1188, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196, - 1197, 1198, 1199, 1200, 1201, 1202, 1203, 0, 0, 735, - 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, 1212, 1213, - 1214, 1215, 1216, 1217, 1218, 1219, 1220, 1221, 1222, 1223, - 1224, 1413, 1225, 1226, 0, 0, 0, 0, 0, 232, - 233, 234, 235, 236, 237, 238, 239, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 240, 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, 0, - 1254, 0, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, - 1106, 1107, 1108, 0, 1109, 1110, 1111, 1112, 1113, 1114, - 1115, 1116, 1117, 1118, 1119, 0, 0, 0, 0, 0, - 0, 1281, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, - 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, - 1138, 0, 0, 1139, 1140, 1141, 1142, 1143, 1144, 1145, - 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 0, 1154, - 0, 1155, 1156, 1157, 1158, 1159, 1160, 1161, 1162, 1163, - 0, 1164, 1165, 1166, 241, 242, 243, 244, 245, 246, - 247, 248, 249, 250, 251, 0, 252, 253, 254, 1167, - 0, 0, 593, 0, 0, 1168, 1169, 1170, 0, 1171, - 1172, 1173, 1174, 1175, 1176, 1177, 1178, 1179, 1180, 1181, - 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, 1190, 1191, - 1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, 1200, 1201, - 1202, 1203, 0, 0, 0, 1204, 1205, 1206, 1207, 1208, - 1209, 1210, 1211, 1212, 1213, 1214, 1215, 1216, 1217, 1218, - 1219, 1220, 1221, 1222, 1223, 1224, 0, 1225, 1226, 7, - 8, 9, 10, 0, 11, 12, 13, 491, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 232, 233, 234, 235, 236, 237, 238, 239, 0, - 0, 0, 0, 0, 0, 0, 15, 336, 240, 0, - 199, 0, 200, 201, 202, 74, 0, 0, 20, 337, - 0, 0, 203, 22, 0, 0, 78, 0, 0, 0, - 0, 23, 24, 204, 0, 0, 0, 26, 0, 0, - 205, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 206, 0, 1350, 0, - 0, 0, 0, 0, 44, 0, 45, 0, 46, 0, - 0, 0, 0, 47, 0, 207, 208, 50, 0, 0, - 51, 84, 0, 0, 0, 52, 0, 0, 53, 339, - 340, 87, 209, 0, 0, 89, 0, 210, 7, 8, - 9, 10, 0, 11, 12, 13, 14, 0, 0, 0, - 56, 0, 0, 57, 58, 59, 0, 0, 60, 0, - 61, 62, 0, 0, 63, 0, 241, 242, 243, 244, - 245, 246, 247, 248, 249, 250, 251, 0, 252, 253, - 254, 0, 0, 0, 1290, 15, 0, 0, 0, 16, - 0, 17, 18, 19, 0, 0, 0, 20, 0, 739, - 740, 21, 22, 0, 0, 0, 770, 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, 772, 0, 0, 0, - 0, 0, 0, 44, 0, 45, 0, 46, 0, 773, - 774, 0, 47, 0, 48, 49, 50, 0, 0, 51, - 0, 0, 0, 0, 52, 775, 0, 53, 0, 0, - 0, 54, 0, 0, 0, 1498, 55, 7, 8, 9, - 10, 741, 11, 12, 13, 14, 0, 0, 0, 56, - 0, 0, 57, 58, 59, 776, 0, 60, 777, 61, - 62, 778, 0, 63, 0, 0, 0, 0, 232, 233, - 234, 235, 236, 237, 238, 239, 0, 779, 0, 0, - 0, 0, 0, 0, 15, 240, 0, 0, 16, 780, - 17, 18, 19, 0, 0, 0, 20, 0, 0, 0, - 21, 22, 0, 781, 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, 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, 214, 215, 216, 0, 0, 0, 0, 56, 0, + 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, 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, - 432, 0, 63, 241, 242, 243, 244, 245, 246, 247, - 248, 249, 250, 251, 0, 252, 253, 254, 15, 437, - 0, 1505, 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, 217, 218, 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, 219, 220, 47, 221, 48, 49, 50, - 0, 222, 51, 223, 0, 0, 0, 52, 0, 0, - 53, 0, 0, 0, 54, 7, 8, 9, 10, 55, - 11, 12, 13, 14, 0, 0, 0, 496, 0, 0, - 0, 0, 56, 0, 0, 57, 58, 59, 0, 0, - 60, 0, 61, 62, 0, 0, 63, 0, 0, 0, - 232, 233, 234, 235, 236, 237, 238, 239, 0, 0, - 0, 0, 15, 716, 0, 0, 16, 240, 17, 18, + 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, 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, 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, - 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, 214, - 215, 216, 0, 0, 884, 0, 56, 0, 0, 57, - 58, 59, 0, 0, 60, 0, 61, 62, 0, 0, - 63, 0, 0, 591, 0, 241, 242, 243, 244, 245, - 246, 247, 248, 249, 250, 251, 15, 252, 253, 254, - 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, 217, 218, 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, 219, 220, 47, 221, 48, 49, 50, 0, 222, - 51, 223, 0, 0, 0, 52, 0, 0, 53, 0, - 0, 0, 54, 7, 8, 9, 10, 55, 11, 12, - 13, 14, 928, 214, 215, 216, 0, 0, 0, 0, - 56, 0, 0, 57, 58, 59, 0, 0, 60, 0, - 61, 62, 0, 0, 63, 0, 0, 0, 232, 233, - 234, 235, 236, 237, 238, 239, 0, 0, 0, 0, - 15, 0, 0, 0, 16, 240, 17, 18, 19, 0, - 0, 0, 20, 0, 0, 0, 21, 22, 0, 0, - 0, 0, 0, 0, 0, 23, 24, 25, 0, 217, - 218, 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, 219, 220, 47, 221, 48, - 49, 50, 0, 222, 51, 223, 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, 1063, 0, 63, 0, - 933, 0, 0, 241, 242, 243, 244, 245, 246, 247, - 248, 249, 250, 251, 15, 252, 253, 254, 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, 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, - 1329, 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, 232, 233, 234, 235, - 236, 237, 238, 239, 0, 0, 0, 0, 15, 0, - 0, 0, 16, 240, 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, 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, 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, 1471, 0, 63, 1265, 0, 0, - 0, 241, 242, 243, 244, 245, 246, 247, 248, 249, - 250, 251, 15, 252, 253, 254, 16, 0, 17, 18, + 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, 43, 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, 649, - 650, 651, 10, 55, 11, 652, 653, 67, 68, 0, - 0, 1040, 0, 0, 0, 0, 56, 0, 0, 57, - 58, 59, 0, 0, 60, 0, 61, 62, 0, 0, - 63, 232, 233, 234, 235, 236, 237, 238, 239, 0, - 0, 0, 0, 459, 0, 0, 655, 69, 240, 0, - 70, 0, 71, 72, 73, 74, 460, 0, 656, 76, - 0, 0, 77, 657, 0, 0, 78, 0, 0, 0, - 0, 658, 659, 79, 0, 0, 0, 0, 0, 0, - 80, 0, 0, 0, 0, 0, 0, 0, 0, 214, - 215, 216, 0, 0, 0, 0, 81, 0, 0, 0, - 0, 0, 0, 0, 660, 0, 661, 0, 662, 0, - 0, 0, 461, 663, 0, 82, 83, 664, 0, 0, - 665, 84, 0, 0, 0, 666, 0, 0, 667, 85, - 86, 87, 88, 0, 0, 89, 0, 90, 7, 8, - 9, 10, 0, 11, 12, 13, 0, 0, 0, 0, - 668, 0, 0, 669, 670, 217, 218, 0, 671, 0, - 672, 0, 692, 0, 673, 0, 241, 242, 243, 244, - 245, 246, 247, 248, 249, 250, 251, 0, 252, 253, - 254, 693, 0, 0, 0, 1300, 0, 0, 0, 0, - 0, 219, 1266, 1267, 1268, 0, 0, 1301, 0, 222, - 0, 223, 1302, 233, 234, 235, 236, 237, 238, 239, - 23, 24, 1269, 0, 0, 0, 26, 1270, 0, 240, - 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, - 38, 39, 40, 41, 42, 0, 232, 233, 234, 235, - 236, 237, 238, 239, 0, 45, 0, 46, 0, 0, - 0, 0, 1303, 240, 0, 0, 1304, 0, 0, 1305, - 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, - 0, 70, 0, 71, 72, 73, 0, 0, 0, 0, - 0, 0, 0, 262, 0, 0, 0, 0, 0, 1306, - 0, 0, 1307, 1308, 1309, 937, 0, 1310, 0, 1311, - 62, 80, 0, 1312, 0, 938, 939, 940, 941, 942, - 943, 944, 945, 0, 0, 0, 0, 263, 0, 0, - 0, 0, 946, 0, 947, 948, 949, 950, 951, 952, - 953, 954, 955, 956, 957, 958, 82, 83, 242, 243, - 244, 245, 246, 247, 248, 249, 250, 251, 0, 252, - 253, 254, 0, 88, 959, 0, 0, 0, 90, 0, - 0, 214, 215, 216, 0, 0, 334, 335, 0, 0, - 0, 241, 242, 243, 244, 245, 246, 247, 248, 249, - 250, 251, 0, 252, 253, 254, 579, 1386, 1387, 1388, - 1389, 1390, 1391, 1392, 1393, 0, 0, 960, 0, 0, - 0, 0, 0, 0, 1394, 0, 0, 336, 0, 0, - 70, 0, 71, 72, 73, 74, 0, 0, 0, 337, - 0, 0, 77, 0, 0, 0, 78, 217, 218, 0, - 0, 0, 0, 79, 0, 0, 961, 0, 0, 962, - 80, 963, 964, 965, 966, 967, 968, 969, 970, 971, - 972, 973, 0, 974, 975, 0, 81, 976, 0, 334, - 335, 0, 0, 219, 220, 0, 221, 338, 0, 0, - 0, 222, 0, 223, 0, 82, 83, 0, 0, 0, - 0, 84, 0, 0, 1269, 0, 0, 0, 0, 339, - 340, 87, 88, 0, 0, 89, 0, 90, 0, 0, - 336, 0, 341, 70, 0, 71, 72, 73, 74, 0, - 0, 0, 337, 0, 0, 77, 0, 0, 342, 78, - 0, 0, 1395, 1396, 1397, 1398, 79, 0, 1399, 1400, - 1401, 1402, 1403, 80, 1404, 1405, 1406, 0, 0, 0, - 0, 0, 0, 0, 0, 506, 507, 0, 0, 81, - 0, 70, 0, 71, 72, 73, 0, 0, 0, 0, - 338, 0, 0, 262, 0, 0, 0, 0, 82, 83, - 0, 0, 0, 0, 84, 0, 0, 0, 0, 0, - 0, 80, 339, 340, 87, 88, 336, 0, 89, 70, - 90, 71, 72, 73, 74, 0, 0, 263, 337, 0, - 0, 77, 474, 0, 0, 78, 0, 0, 0, 0, - 0, 342, 79, 0, 0, 0, 82, 83, 0, 80, - 232, 233, 234, 235, 236, 237, 238, 239, 0, 0, - 0, 0, 0, 88, 0, 81, 0, 240, 90, 0, - 0, 0, 0, 0, 0, 70, 338, 71, 72, 73, - 0, 0, 0, 0, 82, 83, 0, 0, 0, 0, - 84, 174, 0, 70, 0, 71, 72, 73, 339, 340, - 87, 88, 0, 0, 89, 80, 90, 232, 233, 234, - 235, 236, 237, 238, 239, 0, 0, 0, 0, 0, - 0, 263, 0, 80, 240, 0, 0, 342, 232, 233, - 234, 235, 236, 237, 238, 239, 0, 0, 0, 263, - 82, 83, 0, 0, 0, 240, 232, 233, 234, 235, - 236, 237, 238, 239, 0, 0, 0, 88, 82, 83, - 0, 0, 90, 240, 232, 233, 234, 235, 236, 237, - 238, 239, 0, 0, 0, 88, 0, 0, 0, 0, - 90, 240, 0, 0, 0, 241, 242, 243, 244, 245, - 246, 247, 248, 249, 250, 251, 0, 252, 253, 254, - 595, 232, 233, 234, 235, 236, 237, 238, 239, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 240, 232, - 233, 234, 235, 236, 237, 238, 239, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, - 0, 0, 241, 242, 243, 244, 245, 246, 247, 248, - 249, 250, 251, 0, 252, 253, 254, 683, 0, 0, - 0, 0, 0, 241, 242, 243, 244, 245, 246, 247, - 248, 249, 250, 251, 0, 252, 253, 254, 762, 0, - 0, 241, 242, 243, 244, 245, 246, 247, 248, 249, - 250, 251, 0, 252, 253, 254, 903, 0, 0, 241, - 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, - 0, 252, 253, 254, 1021, 232, 233, 234, 235, 236, - 237, 238, 239, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 240, 0, 0, 0, 241, 242, 243, 244, - 245, 246, 247, 248, 249, 250, 251, 0, 252, 253, - 254, 1286, 1288, 0, 241, 242, 243, 244, 245, 246, - 247, 248, 249, 250, 251, 0, 252, 253, 254, 847, - 848, 849, 850, 851, 852, 853, 854, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 855, 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, 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, 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, 0, 0, -2, 0, - 0, 0, 0, 0, 0, 0, 0, 1387, 1388, 1389, - 1390, 1391, 1392, 1393, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1394, 0, 0, 0, 0, 0, 0, - 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, - 251, 0, 252, 253, 254, 105, 0, 106, 0, 0, - 107, 108, 0, 0, 0, 0, 0, 0, 109, 110, - 0, 0, 0, 0, 0, 0, 0, 111, 0, 112, - 113, 114, 115, 0, 0, 0, 116, 0, 0, 0, - 0, 117, 0, 0, 856, 857, 858, 859, 860, 861, - 862, 863, 864, 865, 866, 0, 867, 868, 869, 0, - 118, 119, 120, 121, 122, 123, 0, 0, 0, 0, - 0, 124, 125, 126, 127, 0, 0, 0, 0, 0, - 128, 129, 67, 68, 130, 131, 457, 0, 458, 132, - 0, 0, 0, 0, 0, 133, 134, 0, 135, 0, - 0, 0, 1396, 1397, 1398, 0, 136, 1399, 1400, 1401, - 1402, 1403, 0, 1404, 1405, 1406, 0, 0, 459, 0, - 0, 0, 69, 0, 0, 70, 0, 71, 72, 73, - 74, 460, 0, 0, 76, 0, 0, 77, 0, 0, - 0, 78, 234, 235, 236, 237, 238, 239, 79, 0, - 0, 0, 0, 0, 0, 80, 0, 240, 1388, 1389, - 1390, 1391, 1392, 1393, 0, 0, 0, 0, 0, 0, - 0, 81, 0, 1394, 1388, 1389, 1390, 1391, 1392, 1393, - 0, 70, 0, 71, 72, 73, 0, 461, 0, 1394, - 82, 83, 0, 0, 0, 0, 84, 0, 1388, 1389, - 1390, 1391, 1392, 1393, 85, 86, 87, 88, 0, 0, - 89, 80, 90, 1394, 1388, 1389, 1390, 1391, 1392, 1393, - 0, 0, 0, 0, 0, 462, 0, 263, 0, 1394, - 463, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 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, 88, 0, 0, 0, 0, 90, 478, - 479, 480, 0, 0, 0, 0, 242, 243, 244, 245, - 246, 247, 248, 249, 250, 251, 0, 252, 253, 254, - 0, 185, 1396, 1397, 1398, 0, 0, 1399, 1400, 1401, - 1402, 1403, 0, 1404, 1405, 1406, 481, 0, 482, 1397, - 1398, 0, 0, 1399, 1400, 1401, 1402, 1403, 0, 1404, - 1405, 1406, 302, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1398, 217, 218, 1399, 1400, 1401, - 1402, 1403, 0, 1404, 1405, 1406, 0, 0, 0, 302, - 0, 0, 0, 1399, 1400, 1401, 1402, 1403, 0, 1404, - 1405, 1406, 0, 105, 0, 106, 0, 0, 0, 108, - 0, 483, 220, 0, 221, 0, 109, 110, 0, 222, - 0, 223, 0, 0, 0, 0, 0, 112, 113, 114, - 105, 0, 106, 0, 0, 0, 108, 0, 0, 297, - 214, 215, 216, 109, 110, 484, 0, 0, 0, 0, - 0, 0, 0, 0, 112, 296, 114, 0, 0, 0, - 0, 0, 122, 0, 0, 0, 297, 0, 0, 0, - 0, 0, 127, 0, 0, 0, 0, 481, 128, 482, - 0, 0, 130, 131, 394, 67, 68, 0, 0, 122, - 0, 0, 0, 0, 134, 0, 135, 0, 0, 127, - 0, 0, 0, 0, 0, 128, 217, 218, 0, 0, - 131, 0, 0, 1033, 67, 68, 0, 0, 0, 0, - 0, 134, 0, 135, 0, 69, 0, 0, 70, 0, - 71, 72, 73, 74, 75, 0, 0, 76, 0, 0, - 77, 0, 219, 220, 78, 221, 0, 0, 0, 0, - 222, 79, 223, 0, 69, 0, 0, 70, 80, 71, - 72, 73, 74, 75, 0, 0, 76, 0, 0, 77, - 0, 0, 0, 78, 81, 0, 0, 0, 0, 0, - 79, 70, 0, 71, 72, 73, 0, 80, 0, 0, - 0, 0, 0, 82, 83, 0, 0, 0, 0, 84, - 0, 0, 0, 81, 1447, 67, 68, 85, 86, 87, - 88, 80, 0, 89, 0, 90, 0, 0, 0, 0, - 0, 0, 82, 83, 0, 0, 0, 263, 84, 0, - 0, 0, 0, 694, 695, 696, 85, 86, 87, 88, - 0, 0, 89, 0, 90, 69, 82, 83, 70, 0, - 71, 72, 73, 74, 75, 0, 0, 76, 0, 0, - 77, 0, 0, 88, 78, 0, 0, 0, 90, 0, - 0, 79, 0, 0, 0, 0, 0, 70, 80, 71, - 72, 73, 697, 698, 0, 0, 0, 0, 0, 77, - 0, 186, 0, 0, 81, 0, 0, 0, 0, 0, - 79, 694, 695, 696, 0, 0, 0, 80, 0, 0, - 0, 0, 0, 82, 83, 0, 0, 0, 0, 84, - 0, 0, 0, 81, 0, 0, 0, 85, 86, 87, - 88, 0, 0, 89, 0, 90, 0, 0, 0, 0, - 67, 68, 82, 83, 457, 70, 0, 71, 72, 73, - 0, 0, 0, 0, 0, 0, 699, 77, 0, 88, - 0, 0, 89, 0, 90, 0, 0, 0, 79, 67, - 68, 0, 0, 796, 0, 80, 459, 0, 0, 0, - 69, 0, 0, 70, 0, 71, 72, 73, 74, 460, - 0, 81, 76, 0, 0, 77, 0, 0, 0, 78, - 0, 0, 0, 0, 0, 459, 79, 0, 0, 69, - 82, 83, 70, 80, 71, 72, 73, 74, 460, 0, - 0, 76, 0, 0, 77, 0, 0, 88, 78, 81, - 89, 0, 90, 0, 0, 79, 70, 0, 71, 72, - 73, 0, 80, 0, 0, 461, 0, 0, 82, 83, - 0, 0, 0, 0, 84, 0, 0, 0, 81, 0, - 67, 68, 85, 86, 87, 88, 80, 0, 89, 0, - 90, 0, 0, 0, 461, 0, 0, 82, 83, 0, - 0, 0, 263, 84, 214, 215, 216, 0, 0, 67, - 68, 85, 86, 87, 88, 0, 459, 89, 0, 90, - 69, 82, 83, 70, 0, 71, 72, 73, 74, 460, - 0, 0, 76, 0, 0, 77, 0, 0, 88, 78, - 0, 0, 0, 90, 0, 0, 79, 0, 0, 69, - 0, 0, 70, 80, 71, 72, 73, 74, 75, 0, - 0, 76, 0, 0, 77, 0, 191, 0, 78, 81, - 217, 218, 0, 0, 0, 79, 0, 0, 214, 215, - 216, 0, 80, 0, 0, 461, 0, 0, 82, 83, - 0, 0, 0, 0, 84, 67, 68, 0, 81, 0, - 0, 330, 85, 86, 87, 88, 219, 220, 89, 221, - 90, 0, 0, 0, 222, 0, 223, 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, 217, 218, 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, 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, - 219, 220, 0, 221, 81, 79, 0, 0, 222, 0, - 223, 0, 80, 0, 0, 0, 0, 0, 0, 0, - 608, 1269, 0, 82, 83, 67, 1344, 0, 81, 84, + 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, 0, 0, 89, 0, 90, 0, 82, 83, 0, - 0, 0, 0, 84, 175, 0, 0, 0, 0, 0, - 0, 85, 86, 87, 88, 69, 0, 89, 70, 90, - 71, 72, 73, 74, 517, 0, 0, 76, 0, 0, + 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, - 0, 79, 0, 0, 176, 0, 0, 70, 80, 71, - 72, 73, 74, 998, 0, 0, 177, 0, 0, 77, - 0, 0, 0, 78, 81, 0, 0, 0, 0, 0, - 79, 0, 0, 214, 215, 216, 0, 80, 0, 0, - 0, 0, 0, 82, 83, 0, 0, 0, 0, 84, - 175, 0, 0, 81, 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, 214, 215, 216, 0, 178, 179, 87, 88, - 176, 0, 89, 70, 90, 71, 72, 73, 74, 217, - 218, 0, 177, 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, 219, 220, 0, 221, 81, - 79, 0, 0, 222, 0, 223, 0, 80, 217, 218, - 0, 0, 0, 0, 0, 0, 1269, 0, 82, 83, - 0, 1346, 0, 81, 84, 0, 0, 0, 0, 0, - 0, 0, 178, 179, 87, 88, 0, 0, 89, 0, - 90, 0, 82, 83, 219, 220, 0, 221, 84, 0, - 0, 0, 222, 0, 223, 0, 85, 86, 87, 88, - 0, 105, 89, 106, 90, 1269, 107, 108, 0, 0, - 1348, 0, 0, 0, 109, 110, 0, 0, 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, 0, 118, 119, 120, 121, - 122, 123, 0, 0, 0, 0, 0, 124, 125, 126, - 127, 0, 0, 105, 0, 106, 128, 129, 107, 108, - 130, 131, 0, 0, 0, 132, 109, 110, 0, 0, - 0, 133, 134, 0, 135, 111, 0, 112, 113, 114, - 115, 0, 136, 0, 116, 0, 0, 0, 0, 117, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 690, 0, 0, 0, 0, 0, 0, 118, 119, - 120, 121, 122, 123, 0, 0, 0, 0, 0, 124, - 125, 126, 127, 0, 0, 105, 0, 106, 128, 129, - 107, 108, 130, 131, 0, 0, 0, 132, 109, 110, - 0, 0, 0, 133, 134, 0, 135, 111, 0, 112, - 113, 114, 115, 0, 136, 0, 116, 0, 0, 0, - 0, 117, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 872, 0, 0, 0, 0, 0, 0, - 118, 119, 120, 121, 122, 123, 0, 0, 0, 0, - 0, 124, 125, 126, 127, 0, 0, 105, 0, 106, - 128, 129, 107, 108, 130, 131, 0, 0, 0, 132, - 109, 110, 0, 0, 0, 133, 134, 0, 135, 111, - 0, 112, 113, 114, 115, 0, 136, 0, 116, 0, - 0, 0, 0, 117, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 914, 0, 0, 0, 0, - 0, 0, 118, 119, 120, 121, 122, 123, 0, 0, - 0, 0, 0, 124, 125, 126, 127, 0, 0, 105, - 0, 106, 128, 129, 107, 108, 130, 131, 0, 0, - 0, 132, 109, 110, 0, 0, 0, 133, 134, 0, - 135, 111, 0, 112, 113, 114, 115, 0, 136, 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, 1022, 0, 0, - 0, 0, 0, 0, 118, 119, 120, 121, 122, 123, - 0, 0, 0, 0, 0, 124, 125, 126, 127, 0, - 0, 105, 0, 106, 128, 129, 107, 108, 130, 131, - 0, 0, 0, 132, 109, 110, 0, 0, 0, 133, - 134, 0, 135, 111, 0, 112, 113, 114, 115, 0, - 136, 0, 116, 0, 0, 0, 0, 117, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1072, - 0, 0, 0, 0, 0, 0, 118, 119, 120, 121, - 122, 123, 0, 0, 0, 0, 0, 124, 125, 126, - 127, 0, 0, 105, 0, 106, 128, 129, 107, 108, - 130, 131, 0, 0, 0, 132, 109, 110, 0, 0, - 0, 133, 134, 0, 135, 111, 0, 112, 113, 114, - 115, 0, 136, 0, 116, 0, 0, 0, 0, 117, + 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, - 0, 1291, 0, 0, 0, 0, 0, 0, 118, 119, - 120, 121, 122, 123, 0, 0, 0, 0, 0, 124, - 125, 126, 127, 0, 0, 105, 0, 106, 128, 129, - 0, 108, 130, 131, 105, 0, 106, 132, 109, 110, - 108, 0, 0, 133, 134, 0, 135, 109, 110, 112, - 296, 114, 0, 0, 136, 0, 116, 0, 112, 113, - 114, 297, 0, 0, 0, 116, 0, 0, 0, 0, - 297, 0, 0, 1430, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 122, 0, 0, 0, 0, 0, - 0, 0, 0, 122, 127, 0, 0, 0, 0, 0, - 128, 0, 0, 127, 0, 131, 0, 0, 0, 128, - 0, 0, 0, 130, 131, 133, 134, 105, 135, 106, - 0, 0, 107, 108, 133, 134, 0, 135, 0, 0, - 109, 110, 0, -148, 0, 0, 0, 0, 0, 111, - 0, 112, 113, 114, 115, 105, 0, 106, 116, 0, - 0, 108, 0, 117, 105, 0, 106, 0, 109, 110, - 108, 0, 0, 0, 0, 0, 0, 109, 110, 112, - 296, 114, 118, 119, 120, 121, 122, 123, 112, 296, - 114, 297, 0, 124, 125, 126, 127, 0, 0, 0, - 297, 0, 128, 129, 0, 0, 130, 131, 0, 0, - 0, 132, 0, 0, 122, 308, 0, 133, 134, 0, - 135, 0, 0, 122, 127, 0, 0, 0, 0, 0, - 128, 0, 0, 127, 0, 131, 0, 0, 0, 128, - 0, 0, 0, 0, 131, 0, 134, 0, 135, 0, - 0, 0, 0, 0, 0, 134, 0, 135 + 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, 113, 130, 130, 92, 477, 928, 295, 490, - 140, 587, 140, 660, 528, 44, 508, 15, 709, 140, - 182, 146, 15, 6, 752, 8, 44, 461, 586, 15, - 98, 315, 614, 146, 13, 459, 59, 0, 605, 164, - 15, 328, 164, 147, 147, 149, 150, 151, 152, 147, - 154, 164, 156, 15, 158, 15, 490, 481, 10, 11, - 12, 62, 44, 166, 76, 489, 624, 795, 76, 164, - 174, 169, 13, 714, 15, 164, 17, 635, 147, 164, - 150, 185, 186, 6, 33, 8, 76, 191, 192, 147, - 91, 92, 150, 164, 33, 165, 155, 166, 99, 75, - 33, 147, 125, 744, 745, 155, 747, 748, 749, 750, - 718, 140, 720, 721, 722, 723, 724, 150, 155, 60, - 166, 147, 140, 164, 771, 146, 164, 148, 149, 150, - 64, 65, 165, 147, 146, 155, 712, 149, 146, 715, - 166, 149, 10, 11, 12, 146, 147, 76, 149, 150, - 151, 152, 166, 154, 147, 156, 146, 158, 140, 149, - 112, 75, 81, 82, 83, 295, 146, 295, 166, 88, - 257, 258, 147, 174, 295, 13, 164, 15, 164, 17, - 159, 182, 616, 166, 185, 186, 273, 274, 149, 351, - 191, 192, 164, 253, 324, 753, 324, 149, 328, 140, - 328, 149, 164, 324, 164, 147, 164, 328, 76, 77, - 149, 692, 496, 154, 163, 164, 165, 146, 159, 164, - 149, 289, 60, 164, 166, 164, 165, 228, 165, 166, - 163, 164, 165, 791, 792, 150, 147, 521, 805, 164, - 147, 147, 164, 147, 112, 113, 147, 115, 815, 149, - 165, 147, 120, 166, 122, 166, 257, 258, 899, 166, - 166, 753, 166, 165, 166, 166, 295, 10, 11, 12, - 166, 272, 273, 274, 275, 276, 277, 295, 143, 144, - 145, 282, 350, 387, 755, 165, 166, 206, 207, 208, - 209, 61, 147, 934, 164, 324, 147, 905, 166, 328, - 147, 277, 140, 108, 109, 110, 324, 112, 113, 114, - 328, 166, 117, 295, 315, 166, 154, 122, 147, 166, - 164, 159, 127, 128, 147, 130, 131, 132, 147, 134, - 135, 164, 165, 76, 77, 149, 147, 166, 896, 915, - 147, 150, 324, 166, 263, 912, 328, 166, 772, 164, - 351, 998, 147, 165, 788, 166, 643, 147, 164, 166, - 644, 147, 338, 277, 148, 846, 150, 164, 287, 112, - 113, 166, 115, 164, 165, 166, 166, 120, 148, 122, - 166, 164, 6, 153, 8, 155, 387, 157, 164, 159, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, - 170, 171, 172, 173, 896, 919, 407, 691, 1329, 13, - 147, 15, 846, 17, 184, 997, 164, 984, 188, 189, - 190, 159, 165, 10, 11, 12, 13, 149, 1004, 166, - 17, 164, 165, 166, 1010, 1011, 146, 147, 33, 440, - 164, 165, 166, 444, 445, 446, 447, 448, 1006, 1007, - 518, 164, 10, 11, 12, 13, 60, 458, 164, 17, - 164, 462, 463, 165, 166, 533, 467, 164, 536, 470, - 146, 164, 148, 60, 150, 164, 505, 164, 10, 11, - 12, 113, 114, 115, 460, 461, 165, 166, 1002, 490, - 164, 296, 297, 164, 165, 496, 164, 302, 268, 146, - 164, 148, 60, 150, 505, 164, 165, 1089, 164, 164, - 1092, 164, 164, 643, 490, 643, 164, 518, 164, 1095, - 521, 1249, 643, 164, 111, 112, 164, 20, 21, 22, - 23, 146, 165, 148, 164, 150, 140, 164, 164, 48, - 33, 517, 147, 164, 76, 77, 460, 461, 146, 164, - 154, 164, 155, 140, 112, 159, 164, 146, 477, 148, - 164, 150, 157, 158, 159, 160, 161, 154, 163, 164, - 165, 164, 159, 148, 147, 167, 490, 164, 166, 156, - 112, 113, 140, 115, 166, 586, 587, 588, 120, 873, - 122, 22, 23, 156, 166, 166, 154, 598, 166, 166, - 166, 159, 33, 147, 605, 137, 164, 166, 609, 166, - 166, 587, 672, 614, 643, 166, 386, 618, 619, 620, - 621, 622, 623, 624, 1315, 643, 22, 23, 166, 147, - 166, 660, 608, 166, 635, 166, 166, 33, 166, 166, - 616, 709, 660, 644, 645, 166, 714, 166, 15, 166, - 718, 166, 720, 721, 722, 723, 724, 156, 166, 166, - 166, 643, 155, 156, 157, 158, 159, 160, 161, 698, - 163, 164, 165, 587, 166, 166, 744, 745, 660, 747, - 748, 749, 750, 488, 156, 1261, 166, 1263, 166, 166, - 691, 1419, 1420, 1421, 608, 33, 164, 757, 758, 166, - 166, 165, 616, 704, 159, 765, 165, 767, 467, 166, - 149, 712, 164, 167, 715, 166, 33, 718, 2, 720, - 721, 722, 723, 724, 146, 148, 157, 158, 159, 160, - 161, 732, 163, 164, 165, 147, 712, 147, 147, 715, - 741, 1323, 771, 513, 1320, 147, 164, 164, 149, 164, - 520, 752, 753, 771, 48, 882, 882, 1339, 1340, 1341, - 1241, 157, 158, 159, 160, 161, 164, 163, 164, 165, - 164, 882, 56, 57, 58, 59, 60, 166, 166, 63, - 166, 551, 164, 553, 159, 555, 159, 146, 789, 771, - 791, 792, 169, 1521, 795, 48, 76, 798, 712, 164, - 1528, 715, 155, 33, 805, 155, 155, 1241, 868, 155, - 164, 166, 788, 10, 815, 10, 10, 921, 922, 157, - 158, 159, 160, 161, 825, 163, 164, 165, 10, 1411, - 148, 899, 156, 1409, 1416, 147, 755, 905, 148, 159, - 1422, 1423, 159, 160, 161, 166, 163, 164, 165, 148, - 609, 159, 166, 148, 883, 148, 167, 468, 167, 148, - 619, 620, 621, 622, 623, 166, 934, 159, 166, 166, - 846, 146, 873, 169, 788, 166, 164, 682, 683, 164, - 881, 272, 273, 274, 275, 276, 887, 888, 889, 890, - 891, 892, 893, 164, 1476, 896, 164, 166, 1474, 1266, - 1267, 1268, 672, 1417, 905, 165, 190, 165, 165, 149, - 164, 912, 999, 164, 915, 147, 166, 150, 147, 147, - 921, 922, 159, 166, 166, 1507, 1030, 928, 167, 159, - 160, 161, 846, 163, 164, 165, 147, 10, 1520, 915, - 165, 4, 166, 169, 147, 704, 48, 164, 232, 233, - 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, - 244, 245, 246, 247, 248, 249, 250, 251, 252, 998, - 254, 169, 164, 169, 146, 259, 147, 1344, 166, 1346, - 998, 1348, 156, 984, 1351, 1352, 1353, 156, 156, 166, - 1274, 166, 156, 166, 10, 148, 997, 10, 999, 610, - 10, 915, 10, 1004, 148, 1006, 1007, 147, 164, 1010, - 1011, 295, 169, 167, 166, 166, 998, 167, 167, 630, - 631, 632, 633, 634, 169, 167, 15, 166, 1004, 1030, - 789, 166, 166, 164, 1010, 1011, 164, 166, 322, 798, - 164, 10, 11, 12, 166, 10, 11, 12, 13, 440, - 15, 164, 17, 444, 445, 446, 447, 448, 828, 829, - 830, 166, 832, 164, 834, 835, 836, 458, 10, 11, - 12, 462, 463, 1074, 155, 155, 155, 882, 155, 164, - 166, 15, 1083, 166, 146, 10, 10, 148, 1089, 148, - 1004, 1092, 166, 704, 1095, 60, 1010, 1011, 166, 166, - 384, 148, 10, 148, 164, 147, 156, 76, 77, 10, - 11, 12, 13, 166, 166, 166, 17, 401, 402, 1095, - 156, 167, 881, 166, 408, 10, 11, 12, 156, 888, - 889, 890, 891, 892, 76, 77, 166, 166, 156, 166, - 10, 167, 148, 112, 113, 10, 115, 112, 148, 164, - 164, 120, 164, 122, 148, 166, 148, 164, 920, 60, - 307, 166, 166, 1250, 166, 646, 1244, 1315, 328, 883, - 112, 113, 698, 115, 1084, 140, 1244, 1241, 120, 790, - 122, 1095, 1074, 846, 1275, 154, 470, 328, 1204, 154, - 159, 76, 77, 406, 159, -1, 807, -1, -1, 164, - -1, -1, -1, 1204, -1, -1, -1, 598, 150, -1, - -1, 112, 154, -1, -1, -1, -1, 159, -1, 16, - 17, 18, 19, 20, 21, 22, 23, 112, 113, -1, - 115, -1, 516, -1, -1, 120, 33, 122, -1, 140, - 1241, -1, -1, -1, -1, -1, 530, 1315, 1249, 1250, - -1, 1311, -1, 154, -1, -1, -1, -1, 159, -1, - 1261, -1, 1263, -1, 149, 1241, -1, -1, -1, -1, - 881, -1, -1, 1274, -1, -1, -1, 888, 889, 890, - 891, 892, -1, -1, -1, 1261, -1, 1263, -1, 18, - 19, 20, 21, 22, 23, -1, -1, 1357, 1358, -1, - 1360, -1, 1362, -1, 33, -1, -1, 591, -1, -1, - -1, -1, 1082, -1, -1, -1, -1, -1, -1, 1320, - -1, 605, 1323, -1, 1083, -1, -1, 1241, 1329, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 1339, 1340, - 1341, -1, -1, -1, 1320, 1405, -1, 1261, -1, 1263, - -1, -1, 149, -1, 151, 152, 153, 154, -1, 643, - 157, 158, 159, 160, 161, -1, 163, 164, 165, -1, - 20, 21, 22, 23, -1, -1, -1, -1, -1, -1, - -1, 62, -1, 33, 668, 669, 670, 671, -1, 673, - -1, -1, -1, -1, -1, -1, 1306, 1307, 1308, 1309, - 1310, -1, 1312, -1, 85, -1, 1320, -1, 1409, -1, - 1411, -1, -1, -1, -1, 1416, -1, -1, 1419, 1420, - 1421, 1422, 1423, -1, 105, 154, 155, 156, 157, 158, - 159, 160, 161, 1409, 163, 164, 165, -1, -1, -1, - -1, -1, 123, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 133, -1, 1514, 1515, 1516, 741, 1518, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 1083, 1474, -1, 1476, 1386, 1387, 1388, 1389, - 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, - 1400, 1401, 1402, 1403, 1404, 1409, -1, 178, 1474, -1, - 181, -1, -1, -1, -1, -1, 1507, 157, 158, 159, - 160, 161, -1, 163, 164, 165, -1, -1, -1, 1520, - 1521, 805, -1, -1, -1, -1, -1, 1528, -1, -1, - -1, 815, -1, -1, -1, 819, -1, -1, -1, -1, - 1450, 1311, -1, -1, 225, 226, -1, -1, -1, -1, - -1, -1, 836, -1, -1, -1, -1, -1, -1, -1, - 1474, -1, -1, 847, 848, 849, 850, 851, 852, 853, - 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, - 864, 865, 866, 867, -1, 869, -1, -1, -1, -1, - 271, -1, -1, 1503, 1306, 1307, 1308, 1309, 1310, -1, - 1312, 1371, 1372, 1373, 1374, -1, 1376, 1377, 289, 17, - 291, -1, -1, -1, -1, -1, -1, -1, 18, 19, - 20, 21, 22, 23, -1, 33, -1, 308, 912, 310, - 311, 312, 313, 33, -1, -1, -1, -1, -1, 47, - 10, 11, 12, -1, 928, -1, -1, -1, -1, 330, - -1, -1, 60, 61, -1, -1, -1, -1, 339, -1, - -1, 342, -1, -1, -1, -1, -1, -1, 76, 350, - -1, -1, -1, -1, 1386, 1387, 1388, 1389, 1390, 1391, - 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, - 1402, 1403, 1404, -1, -1, -1, -1, -1, 106, -1, - 984, 109, 986, -1, 112, -1, 76, 77, 389, -1, - -1, -1, 393, -1, -1, -1, -1, -1, -1, -1, - 128, -1, -1, -1, -1, 406, -1, -1, -1, -1, - -1, -1, 140, -1, -1, -1, -1, -1, 1450, -1, - -1, -1, 112, 113, -1, 115, 154, -1, -1, -1, - 120, -1, 122, -1, 1038, 155, 156, 157, 158, 159, - 160, 161, 443, 163, 164, 165, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 146, -1, 459, 3, - 4, 5, 6, -1, 8, 9, 10, 11, 12, -1, - 471, 1503, -1, -1, -1, -1, -1, -1, -1, -1, - 481, -1, -1, -1, -1, -1, -1, -1, 489, -1, - -1, -1, -1, -1, -1, -1, 18, 19, 20, 21, + 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, 60, -1, 62, 63, - -1, -1, 66, 67, 525, -1, 70, -1, -1, -1, - -1, 75, 76, 77, -1, 536, -1, 81, -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, 108, -1, 110, -1, 112, -1, - -1, -1, -1, 117, -1, 119, 120, 121, -1, -1, - 124, 125, -1, -1, 585, 129, -1, -1, 132, 133, - 134, 135, 136, -1, -1, 139, -1, 141, -1, -1, - 3, 4, 5, 6, -1, 8, 9, 10, 11, 12, - 154, -1, 15, 157, 158, 159, -1, -1, 162, -1, - 164, 165, -1, -1, 168, -1, -1, 628, -1, -1, - 1234, 153, 154, 155, 156, 157, 158, 159, 160, 161, - -1, 163, 164, 165, 47, 646, -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, -1, -1, -1, - -1, 84, -1, -1, 1288, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 100, -1, -1, - -1, 702, -1, -1, -1, 108, -1, 110, -1, 112, - -1, -1, -1, 116, 117, -1, 119, 120, 121, -1, - -1, 124, 125, -1, -1, 1329, 129, -1, -1, 132, - 133, 134, 135, 136, -1, -1, 139, -1, 141, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 154, -1, -1, 157, 158, -1, -1, -1, 162, - -1, 164, -1, -1, -1, 168, -1, -1, -1, -1, - -1, 772, -1, -1, -1, -1, -1, 3, 4, 5, + -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, -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, 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, 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, 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, 1406, -1, -1, -1, -1, 33, 34, 35, + -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, 100, -1, -1, -1, -1, -1, - 106, 107, 108, 884, 110, 111, 112, 113, 114, 115, + -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, -1, -1, 920, - 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 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, 168, 169, -1, -1, -1, -1, -1, 16, - 17, 18, 19, 20, 21, 22, 23, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 33, -1, -1, -1, + 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, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 1001, -1, 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, 1032, 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, 151, 152, 153, 154, 155, 156, - 157, 158, 159, 160, 161, -1, 163, 164, 165, 100, - -1, -1, 169, -1, -1, 106, 107, 108, -1, 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, -1, -1, -1, 146, 147, 148, 149, 150, - 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, - 161, 162, 163, 164, 165, 166, -1, 168, 169, 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, -1, - -1, 16, 17, 18, 19, 20, 21, 22, 23, -1, - -1, -1, -1, -1, -1, -1, 50, 51, 33, -1, - 54, -1, 56, 57, 58, 59, -1, -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, 1269, -1, - -1, -1, -1, -1, 108, -1, 110, -1, 112, -1, - -1, -1, -1, 117, -1, 119, 120, 121, -1, -1, - 124, 125, -1, -1, -1, 129, -1, -1, 132, 133, - 134, 135, 136, -1, -1, 139, -1, 141, 3, 4, - 5, 6, -1, 8, 9, 10, 11, -1, -1, -1, - 154, -1, -1, 157, 158, 159, -1, -1, 162, -1, - 164, 165, -1, -1, 168, -1, 151, 152, 153, 154, - 155, 156, 157, 158, 159, 160, 161, -1, 163, 164, - 165, -1, -1, -1, 169, 50, -1, -1, -1, 54, - -1, 56, 57, 58, -1, -1, -1, 62, -1, 64, - 65, 66, 67, -1, -1, -1, 17, -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, 47, -1, -1, -1, - -1, -1, -1, 108, -1, 110, -1, 112, -1, 60, - 61, -1, 117, -1, 119, 120, 121, -1, -1, 124, - -1, -1, -1, -1, 129, 76, -1, 132, -1, -1, - -1, 136, -1, -1, -1, 1446, 141, 3, 4, 5, - 6, 146, 8, 9, 10, 11, -1, -1, -1, 154, - -1, -1, 157, 158, 159, 106, -1, 162, 109, 164, - 165, 112, -1, 168, -1, -1, -1, -1, 16, 17, - 18, 19, 20, 21, 22, 23, -1, 128, -1, -1, - -1, -1, -1, -1, 50, 33, -1, -1, 54, 140, - 56, 57, 58, -1, -1, -1, 62, -1, -1, -1, - 66, 67, -1, 154, -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, 108, -1, 110, -1, 112, -1, -1, -1, - -1, 117, -1, 119, 120, 121, -1, -1, 124, -1, - -1, -1, -1, 129, -1, -1, 132, -1, -1, -1, - 136, 3, 4, 5, 6, 141, 8, 9, 10, 11, - -1, 10, 11, 12, -1, -1, -1, -1, 154, -1, - -1, 157, 158, 159, -1, -1, 162, -1, 164, 165, - 166, -1, 168, 151, 152, 153, 154, 155, 156, 157, - 158, 159, 160, 161, -1, 163, 164, 165, 50, 51, - -1, 169, 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, 76, 77, 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, 108, -1, 110, -1, - 112, -1, -1, 112, 113, 117, 115, 119, 120, 121, - -1, 120, 124, 122, -1, -1, -1, 129, -1, -1, - 132, -1, -1, -1, 136, 3, 4, 5, 6, 141, - 8, 9, 10, 11, -1, -1, -1, 146, -1, -1, - -1, -1, 154, -1, -1, 157, 158, 159, -1, -1, - 162, -1, 164, 165, -1, -1, 168, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 16, 17, 18, 19, + 20, 21, 22, 23, -1, -1, -1, -1, -1, -1, + -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, @@ -2483,624 +2514,653 @@ static const yytype_int16 yycheck[] = -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, - 108, -1, 110, -1, 112, -1, -1, -1, -1, 117, - -1, 119, 120, 121, -1, -1, 124, -1, -1, -1, - -1, 129, -1, -1, 132, -1, -1, -1, 136, 3, - 4, 5, 6, 141, 8, 9, 10, 11, -1, 10, - 11, 12, -1, -1, 15, -1, 154, -1, -1, 157, - 158, 159, -1, -1, 162, -1, 164, 165, -1, -1, - 168, -1, -1, 149, -1, 151, 152, 153, 154, 155, - 156, 157, 158, 159, 160, 161, 50, 163, 164, 165, - 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, 76, 77, 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, 108, -1, 110, -1, 112, -1, - -1, 112, 113, 117, 115, 119, 120, 121, -1, 120, - 124, 122, -1, -1, -1, 129, -1, -1, 132, -1, - -1, -1, 136, 3, 4, 5, 6, 141, 8, 9, - 10, 11, 146, 10, 11, 12, -1, -1, -1, -1, - 154, -1, -1, 157, 158, 159, -1, -1, 162, -1, - 164, 165, -1, -1, 168, -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, 76, - 77, 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, 108, -1, - 110, -1, 112, -1, -1, 112, 113, 117, 115, 119, - 120, 121, -1, 120, 124, 122, -1, -1, -1, 129, - -1, -1, 132, -1, -1, -1, 136, 3, 4, 5, - 6, 141, 8, 9, 10, 11, -1, -1, -1, -1, - -1, -1, -1, -1, 154, -1, -1, 157, 158, 159, - -1, -1, 162, -1, 164, 165, 166, -1, 168, -1, - 148, -1, -1, 151, 152, 153, 154, 155, 156, 157, - 158, 159, 160, 161, 50, 163, 164, 165, 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, 108, -1, 110, -1, 112, -1, -1, -1, - -1, 117, -1, 119, 120, 121, -1, -1, 124, -1, - -1, -1, -1, 129, -1, -1, 132, -1, -1, -1, - 136, 3, 4, 5, 6, 141, 8, 9, 10, 11, - 146, -1, -1, -1, -1, -1, -1, -1, 154, -1, - -1, 157, 158, 159, -1, -1, 162, -1, 164, 165, - -1, -1, 168, -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, 108, -1, 110, -1, - 112, -1, -1, -1, -1, 117, -1, 119, 120, 121, - -1, -1, 124, -1, -1, -1, -1, 129, -1, -1, - 132, -1, -1, -1, 136, 3, 4, 5, 6, 141, - 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, - -1, -1, 154, -1, -1, 157, 158, 159, -1, -1, - 162, -1, 164, 165, 166, -1, 168, 147, -1, -1, - -1, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 50, 163, 164, 165, 54, -1, 56, 57, + -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, - 108, -1, 110, -1, 112, -1, -1, -1, -1, 117, - -1, 119, 120, 121, -1, -1, 124, -1, -1, -1, - -1, 129, -1, -1, 132, -1, -1, -1, 136, 3, - 4, 5, 6, 141, 8, 9, 10, 11, 12, -1, - -1, 15, -1, -1, -1, -1, 154, -1, -1, 157, - 158, 159, -1, -1, 162, -1, 164, 165, -1, -1, - 168, 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, -1, -1, -1, 10, - 11, 12, -1, -1, -1, -1, 100, -1, -1, -1, - -1, -1, -1, -1, 108, -1, 110, -1, 112, -1, - -1, -1, 116, 117, -1, 119, 120, 121, -1, -1, - 124, 125, -1, -1, -1, 129, -1, -1, 132, 133, - 134, 135, 136, -1, -1, 139, -1, 141, 3, 4, - 5, 6, -1, 8, 9, 10, -1, -1, -1, -1, - 154, -1, -1, 157, 158, 76, 77, -1, 162, -1, - 164, -1, 147, -1, 168, -1, 151, 152, 153, 154, - 155, 156, 157, 158, 159, 160, 161, -1, 163, 164, - 165, 166, -1, -1, -1, 50, -1, -1, -1, -1, - -1, 112, 113, 114, 115, -1, -1, 62, -1, 120, - -1, 122, 67, 17, 18, 19, 20, 21, 22, 23, - 75, 76, 133, -1, -1, -1, 81, 138, -1, 33, - 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, - 95, 96, 97, 98, 99, -1, 16, 17, 18, 19, - 20, 21, 22, 23, -1, 110, -1, 112, -1, -1, - -1, -1, 117, 33, -1, -1, 121, -1, -1, 124, - -1, -1, -1, -1, 129, -1, -1, -1, -1, -1, - -1, 54, -1, 56, 57, 58, -1, -1, -1, -1, - -1, -1, -1, 66, -1, -1, -1, -1, -1, 154, - -1, -1, 157, 158, 159, 6, -1, 162, -1, 164, - 165, 84, -1, 168, -1, 16, 17, 18, 19, 20, - 21, 22, 23, -1, -1, -1, -1, 100, -1, -1, - -1, -1, 33, -1, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 119, 120, 152, 153, - 154, 155, 156, 157, 158, 159, 160, 161, -1, 163, - 164, 165, -1, 136, 65, -1, -1, -1, 141, -1, - -1, 10, 11, 12, -1, -1, 10, 11, -1, -1, - -1, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, -1, 163, 164, 165, 166, 16, 17, 18, - 19, 20, 21, 22, 23, -1, -1, 108, -1, -1, - -1, -1, -1, -1, 33, -1, -1, 51, -1, -1, - 54, -1, 56, 57, 58, 59, -1, -1, -1, 63, - -1, -1, 66, -1, -1, -1, 70, 76, 77, -1, - -1, -1, -1, 77, -1, -1, 147, -1, -1, 150, - 84, 152, 153, 154, 155, 156, 157, 158, 159, 160, - 161, 162, -1, 164, 165, -1, 100, 168, -1, 10, - 11, -1, -1, 112, 113, -1, 115, 111, -1, -1, - -1, 120, -1, 122, -1, 119, 120, -1, -1, -1, - -1, 125, -1, -1, 133, -1, -1, -1, -1, 133, - 134, 135, 136, -1, -1, 139, -1, 141, -1, -1, - 51, -1, 146, 54, -1, 56, 57, 58, 59, -1, - -1, -1, 63, -1, -1, 66, -1, -1, 162, 70, - -1, -1, 151, 152, 153, 154, 77, -1, 157, 158, - 159, 160, 161, 84, 163, 164, 165, -1, -1, -1, - -1, -1, -1, -1, -1, 10, 11, -1, -1, 100, - -1, 54, -1, 56, 57, 58, -1, -1, -1, -1, - 111, -1, -1, 66, -1, -1, -1, -1, 119, 120, - -1, -1, -1, -1, 125, -1, -1, -1, -1, -1, - -1, 84, 133, 134, 135, 136, 51, -1, 139, 54, - 141, 56, 57, 58, 59, -1, -1, 100, 63, -1, - -1, 66, 11, -1, -1, 70, -1, -1, -1, -1, - -1, 162, 77, -1, -1, -1, 119, 120, -1, 84, + -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, -1, 136, -1, 100, -1, 33, 141, -1, - -1, -1, -1, -1, -1, 54, 111, 56, 57, 58, - -1, -1, -1, -1, 119, 120, -1, -1, -1, -1, - 125, 164, -1, 54, -1, 56, 57, 58, 133, 134, - 135, 136, -1, -1, 139, 84, 141, 16, 17, 18, - 19, 20, 21, 22, 23, -1, -1, -1, -1, -1, - -1, 100, -1, 84, 33, -1, -1, 162, 16, 17, - 18, 19, 20, 21, 22, 23, -1, -1, -1, 100, - 119, 120, -1, -1, -1, 33, 16, 17, 18, 19, - 20, 21, 22, 23, -1, -1, -1, 136, 119, 120, - -1, -1, 141, 33, 16, 17, 18, 19, 20, 21, - 22, 23, -1, -1, -1, 136, -1, -1, -1, -1, - 141, 33, -1, -1, -1, 151, 152, 153, 154, 155, - 156, 157, 158, 159, 160, 161, -1, 163, 164, 165, - 166, 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, 151, 152, 153, 154, 155, 156, 157, 158, - 159, 160, 161, -1, 163, 164, 165, 166, -1, -1, - -1, -1, -1, 151, 152, 153, 154, 155, 156, 157, - 158, 159, 160, 161, -1, 163, 164, 165, 166, -1, - -1, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, -1, 163, 164, 165, 166, -1, -1, 151, - 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, - -1, 163, 164, 165, 166, 16, 17, 18, 19, 20, + -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, 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, 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, 151, 152, 153, 154, - 155, 156, 157, 158, 159, 160, 161, -1, 163, 164, - 165, 166, 149, -1, 151, 152, 153, 154, 155, 156, - 157, 158, 159, 160, 161, -1, 163, 164, 165, 16, - 17, 18, 19, 20, 21, 22, 23, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 33, -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, -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, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, - -1, -1, -1, -1, -1, -1, -1, 17, 18, 19, - 20, 21, 22, 23, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 33, -1, -1, -1, -1, -1, -1, - 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, - 161, -1, 163, 164, 165, 47, -1, 49, -1, -1, - 52, 53, -1, -1, -1, -1, -1, -1, 60, 61, - -1, -1, -1, -1, -1, -1, -1, 69, -1, 71, - 72, 73, 74, -1, -1, -1, 78, -1, -1, -1, - -1, 83, -1, -1, 151, 152, 153, 154, 155, 156, - 157, 158, 159, 160, 161, -1, 163, 164, 165, -1, - 102, 103, 104, 105, 106, 107, -1, -1, -1, -1, - -1, 113, 114, 115, 116, -1, -1, -1, -1, -1, - 122, 123, 11, 12, 126, 127, 15, -1, 17, 131, - -1, -1, -1, -1, -1, 137, 138, -1, 140, -1, - -1, -1, 152, 153, 154, -1, 148, 157, 158, 159, - 160, 161, -1, 163, 164, 165, -1, -1, 47, -1, - -1, -1, 51, -1, -1, 54, -1, 56, 57, 58, - 59, 60, -1, -1, 63, -1, -1, 66, -1, -1, - -1, 70, 18, 19, 20, 21, 22, 23, 77, -1, - -1, -1, -1, -1, -1, 84, -1, 33, 18, 19, - 20, 21, 22, 23, -1, -1, -1, -1, -1, -1, - -1, 100, -1, 33, 18, 19, 20, 21, 22, 23, - -1, 54, -1, 56, 57, 58, -1, 116, -1, 33, - 119, 120, -1, -1, -1, -1, 125, -1, 18, 19, - 20, 21, 22, 23, 133, 134, 135, 136, -1, -1, - 139, 84, 141, 33, 18, 19, 20, 21, 22, 23, - -1, -1, -1, -1, -1, 154, -1, 100, -1, 33, - 159, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 119, 120, -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, 136, -1, -1, -1, -1, 141, 10, - 11, 12, -1, -1, -1, -1, 152, 153, 154, 155, - 156, 157, 158, 159, 160, 161, -1, 163, 164, 165, - -1, 164, 152, 153, 154, -1, -1, 157, 158, 159, - 160, 161, -1, 163, 164, 165, 47, -1, 49, 153, - 154, -1, -1, 157, 158, 159, 160, 161, -1, 163, - 164, 165, 6, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 154, 76, 77, 157, 158, 159, - 160, 161, -1, 163, 164, 165, -1, -1, -1, 6, - -1, -1, -1, 157, 158, 159, 160, 161, -1, 163, - 164, 165, -1, 47, -1, 49, -1, -1, -1, 53, - -1, 112, 113, -1, 115, -1, 60, 61, -1, 120, - -1, 122, -1, -1, -1, -1, -1, 71, 72, 73, - 47, -1, 49, -1, -1, -1, 53, -1, -1, 83, - 10, 11, 12, 60, 61, 146, -1, -1, -1, -1, - -1, -1, -1, -1, 71, 72, 73, -1, -1, -1, - -1, -1, 106, -1, -1, -1, 83, -1, -1, -1, - -1, -1, 116, -1, -1, -1, -1, 47, 122, 49, - -1, -1, 126, 127, 10, 11, 12, -1, -1, 106, - -1, -1, -1, -1, 138, -1, 140, -1, -1, 116, - -1, -1, -1, -1, -1, 122, 76, 77, -1, -1, - 127, -1, -1, 10, 11, 12, -1, -1, -1, -1, - -1, 138, -1, 140, -1, 51, -1, -1, 54, -1, - 56, 57, 58, 59, 60, -1, -1, 63, -1, -1, - 66, -1, 112, 113, 70, 115, -1, -1, -1, -1, - 120, 77, 122, -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, 54, -1, 56, 57, 58, -1, 84, -1, -1, - -1, -1, -1, 119, 120, -1, -1, -1, -1, 125, - -1, -1, -1, 100, 10, 11, 12, 133, 134, 135, - 136, 84, -1, 139, -1, 141, -1, -1, -1, -1, - -1, -1, 119, 120, -1, -1, -1, 100, 125, -1, - -1, -1, -1, 10, 11, 12, 133, 134, 135, 136, - -1, -1, 139, -1, 141, 51, 119, 120, 54, -1, - 56, 57, 58, 59, 60, -1, -1, 63, -1, -1, - 66, -1, -1, 136, 70, -1, -1, -1, 141, -1, - -1, 77, -1, -1, -1, -1, -1, 54, 84, 56, - 57, 58, 59, 60, -1, -1, -1, -1, -1, 66, - -1, 164, -1, -1, 100, -1, -1, -1, -1, -1, - 77, 10, 11, 12, -1, -1, -1, 84, -1, -1, - -1, -1, -1, 119, 120, -1, -1, -1, -1, 125, - -1, -1, -1, 100, -1, -1, -1, 133, 134, 135, - 136, -1, -1, 139, -1, 141, -1, -1, -1, -1, - 11, 12, 119, 120, 15, 54, -1, 56, 57, 58, - -1, -1, -1, -1, -1, -1, 133, 66, -1, 136, - -1, -1, 139, -1, 141, -1, -1, -1, 77, 11, - 12, -1, -1, 15, -1, 84, 47, -1, -1, -1, - 51, -1, -1, 54, -1, 56, 57, 58, 59, 60, - -1, 100, 63, -1, -1, 66, -1, -1, -1, 70, - -1, -1, -1, -1, -1, 47, 77, -1, -1, 51, - 119, 120, 54, 84, 56, 57, 58, 59, 60, -1, - -1, 63, -1, -1, 66, -1, -1, 136, 70, 100, - 139, -1, 141, -1, -1, 77, 54, -1, 56, 57, - 58, -1, 84, -1, -1, 116, -1, -1, 119, 120, - -1, -1, -1, -1, 125, -1, -1, -1, 100, -1, - 11, 12, 133, 134, 135, 136, 84, -1, 139, -1, - 141, -1, -1, -1, 116, -1, -1, 119, 120, -1, - -1, -1, 100, 125, 10, 11, 12, -1, -1, 11, - 12, 133, 134, 135, 136, -1, 47, 139, -1, 141, - 51, 119, 120, 54, -1, 56, 57, 58, 59, 60, - -1, -1, 63, -1, -1, 66, -1, -1, 136, 70, - -1, -1, -1, 141, -1, -1, 77, -1, -1, 51, - -1, -1, 54, 84, 56, 57, 58, 59, 60, -1, - -1, 63, -1, -1, 66, -1, 164, -1, 70, 100, - 76, 77, -1, -1, -1, 77, -1, -1, 10, 11, - 12, -1, 84, -1, -1, 116, -1, -1, 119, 120, - -1, -1, -1, -1, 125, 11, 12, -1, 100, -1, - -1, 107, 133, 134, 135, 136, 112, 113, 139, 115, - 141, -1, -1, -1, 120, -1, 122, 119, 120, 11, - 12, -1, -1, 125, -1, -1, -1, -1, -1, -1, - -1, 133, 134, 135, 136, 51, -1, 139, 54, 141, - 56, 57, 58, 59, 76, 77, -1, 63, -1, -1, - 66, -1, -1, -1, 70, -1, -1, -1, -1, 51, - -1, 77, 54, -1, 56, 57, 58, 59, 84, -1, + 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, - 112, 113, -1, 115, 100, 77, -1, -1, 120, -1, - 122, -1, 84, -1, -1, -1, -1, -1, -1, -1, - 116, 133, -1, 119, 120, 11, 138, -1, 100, 125, - -1, -1, -1, -1, -1, -1, -1, 133, 134, 135, - 136, -1, -1, 139, -1, 141, -1, 119, 120, -1, - -1, -1, -1, 125, 11, -1, -1, -1, -1, -1, - -1, 133, 134, 135, 136, 51, -1, 139, 54, 141, - 56, 57, 58, 59, 60, -1, -1, 63, -1, -1, - 66, -1, -1, -1, 70, -1, -1, -1, -1, -1, - -1, 77, -1, -1, 51, -1, -1, 54, 84, 56, + -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, -1, -1, -1, -1, -1, - 77, -1, -1, 10, 11, 12, -1, 84, -1, -1, - -1, -1, -1, 119, 120, -1, -1, -1, -1, 125, - 11, -1, -1, 100, -1, -1, -1, 133, 134, 135, - 136, -1, -1, 139, -1, 141, -1, -1, -1, -1, - -1, -1, 119, 120, 11, -1, -1, -1, 125, -1, - -1, -1, 10, 11, 12, -1, 133, 134, 135, 136, - 51, -1, 139, 54, 141, 56, 57, 58, 59, 76, - 77, -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, 112, 113, -1, 115, 100, - 77, -1, -1, 120, -1, 122, -1, 84, 76, 77, - -1, -1, -1, -1, -1, -1, 133, -1, 119, 120, - -1, 138, -1, 100, 125, -1, -1, -1, -1, -1, - -1, -1, 133, 134, 135, 136, -1, -1, 139, -1, - 141, -1, 119, 120, 112, 113, -1, 115, 125, -1, - -1, -1, 120, -1, 122, -1, 133, 134, 135, 136, - -1, 47, 139, 49, 141, 133, 52, 53, -1, -1, - 138, -1, -1, -1, 60, 61, -1, -1, -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, -1, 102, 103, 104, 105, - 106, 107, -1, -1, -1, -1, -1, 113, 114, 115, - 116, -1, -1, 47, -1, 49, 122, 123, 52, 53, - 126, 127, -1, -1, -1, 131, 60, 61, -1, -1, - -1, 137, 138, -1, 140, 69, -1, 71, 72, 73, - 74, -1, 148, -1, 78, -1, -1, -1, -1, 83, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 167, -1, -1, -1, -1, -1, -1, 102, 103, - 104, 105, 106, 107, -1, -1, -1, -1, -1, 113, - 114, 115, 116, -1, -1, 47, -1, 49, 122, 123, - 52, 53, 126, 127, -1, -1, -1, 131, 60, 61, - -1, -1, -1, 137, 138, -1, 140, 69, -1, 71, - 72, 73, 74, -1, 148, -1, 78, -1, -1, -1, - -1, 83, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 167, -1, -1, -1, -1, -1, -1, - 102, 103, 104, 105, 106, 107, -1, -1, -1, -1, - -1, 113, 114, 115, 116, -1, -1, 47, -1, 49, - 122, 123, 52, 53, 126, 127, -1, -1, -1, 131, - 60, 61, -1, -1, -1, 137, 138, -1, 140, 69, - -1, 71, 72, 73, 74, -1, 148, -1, 78, -1, - -1, -1, -1, 83, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 167, -1, -1, -1, -1, - -1, -1, 102, 103, 104, 105, 106, 107, -1, -1, - -1, -1, -1, 113, 114, 115, 116, -1, -1, 47, - -1, 49, 122, 123, 52, 53, 126, 127, -1, -1, - -1, 131, 60, 61, -1, -1, -1, 137, 138, -1, - 140, 69, -1, 71, 72, 73, 74, -1, 148, -1, + -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, 167, -1, -1, - -1, -1, -1, -1, 102, 103, 104, 105, 106, 107, - -1, -1, -1, -1, -1, 113, 114, 115, 116, -1, - -1, 47, -1, 49, 122, 123, 52, 53, 126, 127, - -1, -1, -1, 131, 60, 61, -1, -1, -1, 137, - 138, -1, 140, 69, -1, 71, 72, 73, 74, -1, - 148, -1, 78, -1, -1, -1, -1, 83, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 167, - -1, -1, -1, -1, -1, -1, 102, 103, 104, 105, - 106, 107, -1, -1, -1, -1, -1, 113, 114, 115, - 116, -1, -1, 47, -1, 49, 122, 123, 52, 53, - 126, 127, -1, -1, -1, 131, 60, 61, -1, -1, - -1, 137, 138, -1, 140, 69, -1, 71, 72, 73, - 74, -1, 148, -1, 78, -1, -1, -1, -1, 83, + -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, - -1, 167, -1, -1, -1, -1, -1, -1, 102, 103, - 104, 105, 106, 107, -1, -1, -1, -1, -1, 113, - 114, 115, 116, -1, -1, 47, -1, 49, 122, 123, - -1, 53, 126, 127, 47, -1, 49, 131, 60, 61, - 53, -1, -1, 137, 138, -1, 140, 60, 61, 71, - 72, 73, -1, -1, 148, -1, 78, -1, 71, 72, - 73, 83, -1, -1, -1, 78, -1, -1, -1, -1, - 83, -1, -1, 167, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 106, -1, -1, -1, -1, -1, - -1, -1, -1, 106, 116, -1, -1, -1, -1, -1, - 122, -1, -1, 116, -1, 127, -1, -1, -1, 122, - -1, -1, -1, 126, 127, 137, 138, 47, 140, 49, - -1, -1, 52, 53, 137, 138, -1, 140, -1, -1, - 60, 61, -1, 155, -1, -1, -1, -1, -1, 69, - -1, 71, 72, 73, 74, 47, -1, 49, 78, -1, - -1, 53, -1, 83, 47, -1, 49, -1, 60, 61, - 53, -1, -1, -1, -1, -1, -1, 60, 61, 71, - 72, 73, 102, 103, 104, 105, 106, 107, 71, 72, - 73, 83, -1, 113, 114, 115, 116, -1, -1, -1, - 83, -1, 122, 123, -1, -1, 126, 127, -1, -1, - -1, 131, -1, -1, 106, 107, -1, 137, 138, -1, - 140, -1, -1, 106, 116, -1, -1, -1, -1, -1, - 122, -1, -1, 116, -1, 127, -1, -1, -1, 122, - -1, -1, -1, -1, 127, -1, 138, -1, 140, -1, - -1, -1, -1, -1, -1, 138, -1, 140 + 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, 143, 144, 145, 171, 172, 277, 3, 4, 5, + 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, 108, 110, 112, 117, 119, 120, - 121, 124, 129, 132, 136, 141, 154, 157, 158, 159, - 162, 164, 165, 168, 267, 268, 276, 11, 12, 51, + 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, 119, 120, 125, 133, 134, 135, 136, 139, - 141, 229, 230, 234, 236, 238, 244, 245, 249, 250, - 255, 256, 257, 258, 0, 47, 49, 52, 53, 60, - 61, 69, 71, 72, 73, 74, 78, 83, 102, 103, - 104, 105, 106, 107, 113, 114, 115, 116, 122, 123, - 126, 127, 131, 137, 138, 140, 148, 175, 177, 178, - 180, 183, 201, 251, 254, 277, 146, 164, 164, 164, - 164, 164, 164, 155, 164, 155, 164, 164, 164, 164, - 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, - 164, 164, 164, 164, 164, 11, 51, 63, 133, 134, - 232, 249, 250, 255, 155, 164, 164, 15, 164, 155, - 164, 164, 164, 267, 267, 267, 267, 267, 11, 54, - 56, 57, 58, 66, 77, 84, 100, 119, 120, 136, - 141, 234, 265, 267, 10, 11, 12, 76, 77, 112, - 113, 115, 120, 122, 150, 154, 159, 271, 272, 274, - 277, 267, 16, 17, 18, 19, 20, 21, 22, 23, - 33, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 163, 164, 165, 6, 8, 229, 230, 164, - 59, 125, 66, 100, 256, 256, 256, 274, 164, 256, - 13, 15, 17, 60, 140, 154, 159, 164, 227, 228, - 277, 228, 146, 10, 11, 12, 112, 149, 275, 235, - 277, 137, 181, 182, 274, 164, 72, 83, 180, 180, - 180, 180, 6, 180, 201, 180, 149, 179, 107, 180, - 164, 164, 164, 164, 180, 146, 274, 149, 149, 149, - 180, 180, 164, 178, 180, 183, 202, 180, 180, 186, - 107, 274, 180, 180, 10, 11, 51, 63, 111, 133, - 134, 146, 162, 189, 192, 231, 233, 236, 238, 244, - 249, 250, 255, 264, 265, 277, 264, 234, 264, 264, - 264, 264, 234, 264, 234, 264, 234, 264, 234, 234, - 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, - 234, 234, 234, 264, 164, 274, 164, 164, 274, 235, - 234, 264, 264, 164, 10, 234, 234, 234, 267, 264, - 264, 166, 147, 166, 274, 274, 147, 169, 150, 217, - 277, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 166, 265, 267, 228, 228, 51, 267, 234, - 159, 274, 13, 15, 17, 60, 140, 154, 159, 227, - 277, 227, 228, 227, 228, 227, 227, 15, 17, 47, - 60, 116, 154, 159, 212, 213, 222, 229, 230, 277, - 165, 247, 248, 277, 11, 246, 256, 149, 10, 11, - 12, 47, 49, 112, 146, 274, 275, 274, 48, 147, - 164, 11, 231, 267, 180, 177, 146, 274, 274, 274, - 274, 274, 172, 146, 267, 155, 10, 11, 192, 231, - 233, 274, 148, 150, 164, 164, 164, 60, 229, 274, - 164, 176, 274, 146, 148, 149, 150, 218, 146, 148, - 150, 219, 148, 184, 274, 275, 235, 167, 166, 166, - 166, 166, 166, 166, 156, 166, 156, 166, 166, 166, - 166, 147, 166, 147, 166, 147, 166, 166, 166, 166, - 166, 166, 166, 166, 166, 166, 166, 267, 234, 264, - 274, 156, 166, 166, 274, 166, 166, 156, 166, 166, - 166, 166, 267, 267, 15, 154, 272, 164, 198, 277, - 267, 149, 166, 169, 166, 166, 166, 227, 159, 274, - 227, 227, 227, 227, 227, 165, 227, 181, 116, 229, - 230, 222, 227, 227, 166, 15, 147, 13, 17, 60, - 140, 154, 159, 164, 225, 275, 277, 13, 15, 17, - 60, 140, 154, 159, 164, 226, 263, 267, 277, 274, - 167, 246, 181, 164, 237, 239, 149, 180, 181, 3, - 4, 5, 9, 10, 15, 50, 62, 67, 75, 76, - 108, 110, 112, 117, 121, 124, 129, 132, 154, 157, - 158, 162, 164, 168, 214, 215, 222, 223, 269, 270, - 276, 277, 166, 166, 172, 146, 147, 147, 147, 147, - 167, 252, 147, 166, 10, 11, 12, 59, 60, 133, - 203, 204, 205, 206, 207, 255, 277, 164, 219, 187, - 148, 234, 190, 13, 159, 191, 51, 267, 229, 13, - 17, 60, 140, 154, 159, 224, 275, 277, 234, 172, - 164, 259, 260, 173, 174, 274, 64, 65, 259, 64, - 65, 146, 267, 13, 17, 60, 111, 140, 154, 159, - 164, 185, 208, 210, 275, 149, 274, 164, 164, 234, - 234, 234, 166, 166, 166, 164, 166, 164, 217, 212, - 17, 33, 47, 60, 61, 76, 106, 109, 112, 128, - 140, 154, 211, 277, 267, 227, 263, 166, 48, 229, - 230, 225, 226, 166, 166, 198, 15, 222, 159, 225, - 225, 225, 225, 225, 225, 165, 217, 159, 274, 226, - 226, 226, 226, 226, 226, 165, 217, 169, 147, 150, - 48, 231, 267, 172, 76, 240, 277, 182, 164, 155, - 155, 232, 155, 15, 164, 155, 164, 267, 267, 267, - 267, 234, 265, 267, 166, 15, 147, 16, 17, 18, - 19, 20, 21, 22, 23, 33, 151, 152, 153, 154, - 155, 156, 157, 158, 159, 160, 161, 163, 164, 165, - 180, 180, 167, 253, 10, 10, 10, 10, 172, 276, - 148, 207, 156, 147, 15, 274, 13, 17, 60, 140, - 154, 159, 164, 225, 226, 188, 210, 148, 212, 159, - 208, 212, 166, 166, 224, 159, 224, 224, 224, 224, - 224, 164, 165, 166, 167, 193, 167, 261, 277, 146, - 147, 146, 164, 148, 148, 167, 148, 148, 146, 220, - 221, 267, 277, 148, 159, 208, 208, 6, 16, 17, - 18, 19, 20, 21, 22, 23, 33, 35, 36, 37, - 38, 39, 40, 41, 42, 43, 44, 45, 46, 65, - 108, 147, 150, 152, 153, 154, 155, 156, 157, 158, - 159, 160, 161, 162, 164, 165, 168, 199, 208, 208, - 208, 208, 149, 164, 165, 211, 150, 217, 219, 246, - 265, 265, 166, 166, 166, 265, 265, 166, 60, 232, - 181, 164, 146, 169, 164, 222, 225, 226, 217, 217, - 164, 164, 211, 225, 166, 263, 226, 166, 263, 267, - 166, 166, 167, 149, 241, 242, 277, 234, 234, 234, - 164, 234, 164, 10, 234, 234, 234, 267, 166, 166, - 15, 223, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 166, 265, 267, 172, 147, 166, 147, - 147, 147, 167, 166, 225, 226, 178, 183, 200, 201, - 206, 274, 150, 159, 150, 216, 277, 217, 219, 166, - 208, 166, 166, 164, 224, 196, 263, 212, 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, 106, 107, - 108, 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, 146, 147, 148, 149, 150, 151, + 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, 163, 164, 165, 166, 168, 169, 262, 259, 174, - 264, 264, 220, 167, 147, 208, 10, 166, 169, 166, - 4, 209, 263, 267, 147, 166, 166, 166, 166, 198, - 232, 228, 48, 166, 274, 259, 212, 217, 217, 212, - 212, 164, 169, 164, 169, 147, 113, 114, 115, 133, - 138, 243, 273, 274, 146, 147, 166, 156, 156, 264, - 156, 274, 166, 166, 156, 166, 166, 267, 149, 166, - 169, 167, 10, 148, 10, 10, 10, 148, 216, 234, - 50, 62, 67, 117, 121, 124, 154, 157, 158, 159, - 162, 164, 168, 266, 268, 147, 198, 166, 164, 198, - 197, 212, 169, 166, 261, 167, 167, 166, 167, 146, - 267, 214, 169, 185, 211, 228, 15, 166, 167, 166, - 166, 166, 212, 212, 138, 273, 138, 273, 138, 273, - 274, 113, 114, 115, 15, 172, 243, 164, 164, 166, - 164, 166, 164, 267, 147, 166, 147, 166, 166, 147, - 166, 164, 155, 155, 155, 15, 164, 155, 266, 266, - 266, 266, 266, 234, 265, 266, 16, 17, 18, 19, - 20, 21, 22, 23, 33, 151, 152, 153, 154, 157, - 158, 159, 160, 161, 163, 164, 165, 188, 164, 194, - 212, 166, 198, 167, 15, 220, 166, 146, 166, 198, - 198, 198, 166, 166, 273, 273, 273, 273, 273, 273, - 167, 265, 265, 265, 265, 10, 148, 10, 148, 148, - 10, 148, 234, 234, 234, 234, 164, 10, 234, 234, - 166, 166, 266, 266, 266, 266, 266, 266, 266, 266, - 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, - 266, 166, 265, 267, 195, 212, 166, 198, 167, 198, - 259, 211, 211, 211, 198, 198, 166, 166, 166, 166, - 166, 147, 147, 166, 166, 156, 156, 156, 274, 166, - 166, 156, 266, 149, 166, 169, 212, 166, 198, 167, - 148, 10, 10, 148, 164, 164, 164, 166, 164, 266, - 166, 198, 166, 166, 265, 265, 265, 265, 198, 211, - 148, 148, 166, 166, 166, 166, 211 + 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, 170, 171, 171, 171, 172, 172, 172, 173, 173, - 174, 174, 174, 176, 175, 177, 177, 177, 177, 177, - 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, - 177, 177, 177, 177, 177, 177, 177, 177, 179, 178, - 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, - 180, 180, 180, 180, 180, 180, 180, 180, 181, 181, - 182, 182, 182, 184, 183, 183, 183, 183, 183, 185, - 185, 187, 186, 186, 188, 188, 190, 189, 191, 189, - 193, 192, 194, 192, 195, 192, 196, 192, 197, 192, - 192, 198, 198, 198, 198, 198, 198, 198, 198, 198, - 198, 198, 198, 198, 198, 199, 199, 199, 199, 199, - 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, - 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, - 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, - 199, 199, 199, 199, 200, 200, 200, 201, 202, 201, - 201, 201, 203, 203, 204, 204, 205, 205, 206, 206, - 206, 206, 206, 206, 206, 206, 206, 207, 207, 207, - 207, 208, 208, 208, 208, 208, 208, 208, 208, 208, - 208, 208, 209, 208, 210, 210, 211, 211, 211, 212, - 212, 212, 212, 212, 213, 213, 214, 214, 214, 214, - 214, 215, 215, 216, 216, 217, 217, 218, 218, 218, - 218, 218, 219, 219, 219, 219, 219, 219, 220, 220, - 220, 221, 221, 221, 221, 222, 222, 222, 222, 222, - 222, 222, 222, 223, 223, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 225, 225, 225, 225, 225, 225, - 225, 225, 225, 225, 225, 226, 226, 226, 226, 226, - 226, 226, 226, 226, 226, 226, 227, 227, 227, 227, - 227, 227, 227, 227, 227, 227, 228, 228, 228, 228, - 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, - 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, - 229, 229, 230, 231, 231, 231, 231, 231, 231, 231, - 231, 231, 231, 231, 231, 231, 232, 232, 232, 232, - 232, 232, 232, 232, 233, 233, 234, 234, 234, 234, - 235, 235, 235, 235, 237, 236, 239, 238, 240, 240, - 241, 241, 242, 242, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 244, 245, 245, 245, 245, 246, - 246, 247, 247, 247, 248, 248, 248, 249, 249, 249, - 250, 250, 250, 252, 251, 253, 251, 251, 251, 254, - 254, 254, 255, 255, 255, 256, 256, 256, 256, 256, - 256, 256, 256, 256, 256, 256, 256, 256, 256, 257, - 257, 257, 258, 260, 259, 261, 261, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 263, 263, 264, 264, - 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, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, - 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, - 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 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, 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, 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, + 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, 269, 269, - 269, 269, 269, 269, 270, 270, 270, 270, 270, 270, - 270, 270, 270, 270, 270, 271, 271, 271, 271, 271, - 272, 272, 272, 272, 273, 273, 273, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, - 275, 276, 276, 276, 276, 277 + 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. */ @@ -3108,45 +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, 4, 5, 5, 1, 3, - 1, 4, 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, - 2, 4, 5, 6, 4, 1, 1, 1, 1, 1, + 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, - 2, 2, 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, - 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, 1, 1, 2, 2, 2, 2, 1, - 1, 2, 1, 0, 2, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 2, 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, + 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, @@ -3159,29 +3220,29 @@ 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, 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, 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, 5, 4, 2, 5, 4, 4, 2, + 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, 4, 5, 4, - 2, 5, 4, 4, 2, 2, 2, 2, 2, 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, 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, + 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, 1, 1, 2, 2, 0 + 1, 2, 2, 0 }; @@ -3957,64 +4018,64 @@ yyreduce: switch (yyn) { case 3: -#line 450 "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 3965 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4026 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 4: -#line 454 "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 3973 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4034 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 10: -#line 472 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 474 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { delete (yyvsp[-1].u.expr); } -#line 3981 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4042 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 11: -#line 476 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 478 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { delete (yyvsp[-2].u.expr); } -#line 3989 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4050 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 12: -#line 480 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 482 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { delete (yyvsp[-1].u.expr); } -#line 3997 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4058 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 13: -#line 492 "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 4006 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4067 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 14: -#line 497 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 499 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_storage_class(); } -#line 4014 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4075 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 21: -#line 510 "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); @@ -4027,11 +4088,11 @@ yyreduce: publish_nest_level++; current_scope->set_current_vis(V_published); } -#line 4031 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4092 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 22: -#line 523 "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])); @@ -4040,19 +4101,19 @@ yyreduce: } publish_nest_level = 0; } -#line 4044 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4105 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 23: -#line 532 "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 4052 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4113 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 24: -#line 536 "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); @@ -4060,258 +4121,413 @@ yyreduce: current_scope->set_current_vis(V_public); } } -#line 4064 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4125 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 25: -#line 544 "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 4072 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4133 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 26: -#line 548 "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 4080 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4141 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 27: -#line 552 "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 4095 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4166 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 28: -#line 563 "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 4120 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 29: -#line 584 "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 4154 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4197 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 30: -#line 614 "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 4175 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4222 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 31: -#line 631 "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 4207 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4255 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 32: -#line 659 "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 4248 "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 696 "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 4274 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4358 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 34: -#line 718 "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 4311 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4383 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 35: -#line 751 "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) { @@ -4333,11 +4549,11 @@ yyreduce: current_scope->add_declaration(make_seq, global_scope, current_lexer, (yylsp[-8])); } } -#line 4337 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4553 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 36: -#line 773 "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) { @@ -4348,11 +4564,11 @@ yyreduce: yywarning("static_assert failed: " + str.str(), (yylsp[-4])); } } -#line 4352 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4568 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 37: -#line 784 "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(); @@ -4362,55 +4578,55 @@ yyreduce: yywarning("static_assert failed", (yylsp[-2])); } } -#line 4366 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4582 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 38: -#line 797 "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 4376 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4592 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 39: -#line 803 "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 4385 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4601 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 40: -#line 812 "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 4393 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4609 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 41: -#line 816 "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 4402 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4618 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 42: -#line 821 "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 4410 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4626 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 43: -#line 825 "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") { @@ -4421,124 +4637,124 @@ yyreduce: yywarning("Ignoring unknown linkage type \"" + (yyvsp[-1].str) + "\"", (yylsp[-1])); } } -#line 4425 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 44: -#line 836 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_static; -} -#line 4433 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 45: -#line 840 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_inline; -} -#line 4441 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 46: -#line 844 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_virtual; -} -#line 4449 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 47: -#line 848 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[0].u.integer) | (int)CPPInstance::SC_explicit; -} -#line 4457 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4641 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 48: -#line 852 "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 4465 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4649 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 49: -#line 856 "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 4473 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4657 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 50: -#line 860 "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 4481 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4665 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 51: -#line 864 "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 4489 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4673 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 52: -#line 868 "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 4497 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4681 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 53: -#line 872 "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 4505 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4689 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 54: -#line 876 "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 4513 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4697 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 55: -#line 880 "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 4522 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4738 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 56: -#line 885 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 60: +#line 1026 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.integer) = (yyvsp[0].u.integer); } -#line 4530 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4746 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 57: -#line 889 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 61: +#line 1030 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.integer) = (yyvsp[0].u.integer); } -#line 4538 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4754 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 63: -#line 907 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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. @@ -4549,19 +4765,19 @@ yyreduce: } push_storage_class((yyvsp[-1].u.integer)); } -#line 4553 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4769 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 64: -#line 918 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 68: +#line 1059 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_storage_class(); } -#line 4561 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4777 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 65: -#line 923 "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 @@ -4570,11 +4786,36 @@ yyreduce: current_scope->add_declaration((yyvsp[-1].u.decl), global_scope, current_lexer, (yylsp[-1])); } -#line 4574 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4790 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 66: -#line 932 "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)); @@ -4582,23 +4823,11 @@ yyreduce: (yyvsp[-1].u.instance)->set_initializer((yyvsp[0].u.expr)); } } -#line 4586 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4827 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 67: -#line 940 "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 4598 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 69: -#line 956 "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); @@ -4609,11 +4838,11 @@ yyreduce: inst->set_initializer((yyvsp[0].u.expr)); current_scope->add_declaration(inst, global_scope, current_lexer, (yylsp[-1])); } -#line 4613 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4842 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 70: -#line 967 "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); @@ -4624,11 +4853,11 @@ yyreduce: inst->set_initializer((yyvsp[-2].u.expr)); current_scope->add_declaration(inst, global_scope, current_lexer, (yylsp[-3])); } -#line 4628 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4857 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 71: -#line 982 "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. @@ -4639,19 +4868,19 @@ yyreduce: } push_storage_class((yyvsp[-1].u.integer)); } -#line 4643 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4872 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 72: -#line 993 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 77: +#line 1143 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_storage_class(); } -#line 4651 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4880 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 73: -#line 997 "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(); @@ -4663,11 +4892,11 @@ yyreduce: } } } -#line 4667 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4896 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 74: -#line 1012 "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); @@ -4676,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 4680 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4909 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 75: -#line 1021 "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); @@ -4689,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 4693 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4922 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 76: -#line 1035 "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 4701 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4930 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 77: -#line 1039 "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() || @@ -4721,19 +4950,19 @@ yyreduce: (yyval.u.instance) = new CPPInstance(type, ii, 0, (yylsp[-5]).file); } -#line 4725 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4954 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 78: -#line 1059 "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 4733 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4962 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 79: -#line 1063 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 84: +#line 1213 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_scope(); CPPType *type; @@ -4751,19 +4980,19 @@ yyreduce: (yyval.u.instance) = new CPPInstance(type, ii, 0, (yylsp[-5]).file); } -#line 4755 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4984 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 80: -#line 1086 "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 4763 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 4992 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 81: -#line 1090 "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()) { @@ -4782,19 +5011,19 @@ yyreduce: (yyval.u.instance) = new CPPInstance(type, ii, 0, (yylsp[-5]).file); } } -#line 4786 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5015 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 82: -#line 1116 "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 4794 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5023 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 83: -#line 1120 "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); @@ -4808,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 4812 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5041 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 84: -#line 1134 "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 4820 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5049 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 85: -#line 1138 "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); @@ -4834,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 4838 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5067 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 86: -#line 1154 "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 4848 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5077 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 87: -#line 1160 "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(); @@ -4873,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 4877 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5106 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 88: -#line 1185 "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 4887 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5116 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 89: -#line 1191 "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(); @@ -4903,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 4907 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5136 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 90: -#line 1211 "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); @@ -4917,574 +5146,574 @@ yyreduce: (yyval.u.instance) = (CPPInstance *)NULL; } } -#line 4921 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 91: -#line 1224 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = 0; -} -#line 4929 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 92: -#line 1228 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_const_method; -} -#line 4937 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 93: -#line 1232 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_volatile_method; -} -#line 4945 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 94: -#line 1236 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_noexcept; -} -#line 4953 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 95: -#line 1249 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_final; -} -#line 4961 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5150 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 96: -#line 1253 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1374 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_override; + (yyval.u.integer) = 0; } -#line 4969 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5158 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 97: -#line 1257 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1378 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_lvalue_method; + (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_const_method; } -#line 4977 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5166 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 98: -#line 1261 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1382 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_rvalue_method; + (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_volatile_method; } -#line 4985 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5174 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 99: -#line 1265 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1386 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_noexcept; +} +#line 5182 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 100: +#line 1399 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_final; +} +#line 5190 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 101: +#line 1403 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.integer) = (yyvsp[-1].u.integer) | (int)CPPFunctionType::F_override; +} +#line 5198 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 102: +#line 1407 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (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 4994 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5223 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 100: -#line 1270 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 5003 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 101: -#line 1275 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-3].u.integer); -} -#line 5011 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 102: -#line 1279 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-4].u.integer); -} -#line 5019 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 103: -#line 1283 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-5].u.integer); -} -#line 5027 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 104: -#line 1287 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.integer) = (yyvsp[-3].u.integer); -} -#line 5035 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 105: -#line 1294 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.str) = "!"; -} -#line 5043 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5232 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 106: -#line 1298 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1425 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "~"; + (yyval.u.integer) = (yyvsp[-3].u.integer); } -#line 5051 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5240 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 107: -#line 1302 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1429 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "*"; + (yyval.u.integer) = (yyvsp[-4].u.integer); } -#line 5059 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5248 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 108: -#line 1306 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1433 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "/"; + (yyval.u.integer) = (yyvsp[-5].u.integer); } -#line 5067 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5256 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 109: -#line 1310 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1437 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "%"; + (yyval.u.integer) = (yyvsp[-3].u.integer); } -#line 5075 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5264 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 110: -#line 1314 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1444 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "+"; + (yyval.str) = "!"; } -#line 5083 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5272 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 111: -#line 1318 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1448 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "-"; + (yyval.str) = "~"; } -#line 5091 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5280 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 112: -#line 1322 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1452 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "|"; + (yyval.str) = "*"; } -#line 5099 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5288 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 113: -#line 1326 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1456 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "&"; + (yyval.str) = "/"; } -#line 5107 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5296 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 114: -#line 1330 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1460 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "^"; + (yyval.str) = "%"; } -#line 5115 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5304 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 115: -#line 1334 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1464 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "||"; + (yyval.str) = "+"; } -#line 5123 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5312 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 116: -#line 1338 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1468 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "&&"; + (yyval.str) = "-"; } -#line 5131 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5320 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 117: -#line 1342 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1472 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "=="; + (yyval.str) = "|"; } -#line 5139 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5328 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 118: -#line 1346 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1476 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "!="; + (yyval.str) = "&"; } -#line 5147 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5336 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 119: -#line 1350 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1480 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "<="; + (yyval.str) = "^"; } -#line 5155 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5344 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 120: -#line 1354 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1484 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = ">="; + (yyval.str) = "||"; } -#line 5163 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5352 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 121: -#line 1358 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1488 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "<"; + (yyval.str) = "&&"; } -#line 5171 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5360 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 122: -#line 1362 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1492 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = ">"; + (yyval.str) = "=="; } -#line 5179 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5368 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 123: -#line 1366 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1496 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "<<"; + (yyval.str) = "!="; } -#line 5187 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5376 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 124: -#line 1370 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1500 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = ">>"; + (yyval.str) = "<="; } -#line 5195 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5384 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 125: -#line 1374 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1504 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "="; + (yyval.str) = ">="; } -#line 5203 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5392 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 126: -#line 1378 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1508 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = ","; + (yyval.str) = "<"; } -#line 5211 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5400 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 127: -#line 1382 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1512 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "++"; + (yyval.str) = ">"; } -#line 5219 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5408 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 128: -#line 1386 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1516 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "--"; + (yyval.str) = "<<"; } -#line 5227 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5416 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 129: -#line 1390 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1520 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "*="; + (yyval.str) = ">>"; } -#line 5235 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5424 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 130: -#line 1394 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1524 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "/="; + (yyval.str) = "="; } -#line 5243 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5432 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 131: -#line 1398 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1528 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "%="; + (yyval.str) = ","; } -#line 5251 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5440 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 132: -#line 1402 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1532 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "+="; + (yyval.str) = "++"; } -#line 5259 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5448 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 133: -#line 1406 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1536 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "-="; + (yyval.str) = "--"; } -#line 5267 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5456 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 134: -#line 1410 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1540 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "|="; + (yyval.str) = "*="; } -#line 5275 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5464 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 135: -#line 1414 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1544 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "&="; + (yyval.str) = "/="; } -#line 5283 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5472 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 136: -#line 1418 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1548 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "^="; + (yyval.str) = "%="; } -#line 5291 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5480 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 137: -#line 1422 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1552 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "<<="; + (yyval.str) = "+="; } -#line 5299 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5488 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 138: -#line 1426 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1556 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = ">>="; + (yyval.str) = "-="; } -#line 5307 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5496 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 139: -#line 1430 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1560 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "->"; + (yyval.str) = "|="; } -#line 5315 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5504 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 140: -#line 1434 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1564 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "[]"; + (yyval.str) = "&="; } -#line 5323 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5512 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 141: -#line 1438 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1568 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "()"; + (yyval.str) = "^="; } -#line 5331 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5520 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 142: -#line 1442 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1572 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "new"; + (yyval.str) = "<<="; } -#line 5339 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5528 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 143: -#line 1446 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1576 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.str) = "delete"; + (yyval.str) = ">>="; } -#line 5347 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#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 1460 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#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 1610 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { push_scope(new CPPTemplateScope(current_scope)); } -#line 5355 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 149: -#line 1464 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - pop_scope(); -} -#line 5363 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5584 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 154: -#line 1478 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1614 "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)); + pop_scope(); } -#line 5373 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 155: -#line 1484 "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 5383 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 158: -#line 1498 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.decl) = CPPType::new_type(new CPPClassTemplateParameter((CPPIdentifier *)NULL)); -} -#line 5391 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5592 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 159: -#line 1502 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1628 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.decl) = CPPType::new_type(new CPPClassTemplateParameter((yyvsp[0].u.identifier))); + CPPTemplateScope *ts = current_scope->as_template_scope(); + assert(ts != NULL); + ts->add_template_parameter((yyvsp[0].u.decl)); } -#line 5399 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5602 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 160: -#line 1506 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#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 5407 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5636 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 161: -#line 1510 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 5417 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5646 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 162: -#line 1516 "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 5427 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5656 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 163: -#line 1522 "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 5437 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5666 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 164: -#line 1528 "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 5448 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5677 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 165: -#line 1535 "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 5457 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5686 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 166: -#line 1540 "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 5467 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5696 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 167: -#line 1549 "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 5475 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5704 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 168: -#line 1553 "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 5484 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5713 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 169: -#line 1558 "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) { @@ -5492,11 +5721,11 @@ yyreduce: } assert((yyval.u.type) != NULL); } -#line 5496 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5725 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 170: -#line 1566 "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) { @@ -5504,19 +5733,19 @@ yyreduce: } assert((yyval.u.type) != NULL); } -#line 5508 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5737 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 171: -#line 1578 "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 5516 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5745 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 172: -#line 1582 "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 @@ -5530,11 +5759,11 @@ yyreduce: (yyval.u.inst_ident) = new CPPInstanceIdentifier(ident); } -#line 5534 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5763 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 173: -#line 1596 "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()) { @@ -5549,83 +5778,83 @@ yyreduce: (yyval.u.inst_ident) = new CPPInstanceIdentifier(ident); } -#line 5553 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5782 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 174: -#line 1611 "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 5562 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5791 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 175: -#line 1616 "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 5571 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5800 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 176: -#line 1621 "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 5580 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5809 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 177: -#line 1626 "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 5589 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5818 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 178: -#line 1631 "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 5598 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5827 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 179: -#line 1636 "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 5607 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5836 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 180: -#line 1641 "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 5616 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5845 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 181: -#line 1646 "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 5625 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5854 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 182: -#line 1651 "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), @@ -5638,11 +5867,11 @@ yyreduce: push_scope(scope); } -#line 5642 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5871 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 183: -#line 1664 "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); @@ -5656,11 +5885,11 @@ yyreduce: (yyval.u.inst_ident)->add_func_modifier((yyvsp[-2].u.param_list), (yyvsp[0].u.integer)); } } -#line 5660 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5889 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 184: -#line 1682 "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 @@ -5670,894 +5899,910 @@ yyreduce: } (yyval.u.inst_ident) = (yyvsp[-1].u.inst_ident); } -#line 5674 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5903 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 185: -#line 1692 "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 5684 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5913 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 186: -#line 1702 "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 5692 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5921 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 187: -#line 1706 "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 5700 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5929 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 188: -#line 1710 "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 5709 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 189: -#line 1719 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = new CPPParameterList; -} -#line 5717 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 190: -#line 1723 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = new CPPParameterList; - (yyval.u.param_list)->_includes_ellipsis = true; -} -#line 5726 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 191: -#line 1728 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.param_list) = (yyvsp[0].u.param_list); -} -#line 5734 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 192: -#line 1732 "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 5743 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 193: -#line 1737 "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 5752 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5938 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 194: -#line 1745 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1869 "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)); + (yyval.u.identifier) = NULL; } -#line 5761 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5946 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 195: -#line 1750 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1873 "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)); + (yyval.u.identifier) = (yyvsp[0].u.identifier); } -#line 5770 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5954 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 196: -#line 1758 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1881 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.param_list) = new CPPParameterList; } -#line 5778 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5962 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 197: -#line 1762 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1885 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.param_list) = new CPPParameterList; (yyval.u.param_list)->_includes_ellipsis = true; } -#line 5787 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5971 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 198: -#line 1767 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 1890 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.param_list) = (yyvsp[0].u.param_list); } -#line 5795 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5979 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 199: -#line 1771 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#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 5804 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5988 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 200: -#line 1776 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#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 5813 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 5997 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 201: -#line 1784 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#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 5822 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 202: -#line 1789 "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 5831 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 203: -#line 1797 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5839 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 204: -#line 1801 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 5847 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 205: -#line 1808 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5855 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 206: -#line 1812 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 5863 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 207: -#line 1819 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5871 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 208: -#line 1823 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5879 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 209: -#line 1827 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5887 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 210: -#line 1831 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::get_default()); -} -#line 5895 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 211: -#line 1835 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::get_delete()); -} -#line 5903 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 212: -#line 1842 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5911 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 213: -#line 1846 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5919 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 214: -#line 1850 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[-1].u.expr); -} -#line 5927 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 215: -#line 1854 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::get_default()); -} -#line 5935 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 216: -#line 1858 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::get_delete()); -} -#line 5943 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 217: -#line 1862 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 5951 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 221: -#line 1875 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { -} -#line 5958 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 225: -#line 1884 "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 5967 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 226: -#line 1889 "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 5977 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 227: -#line 1895 "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 5987 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 228: -#line 1901 "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 5996 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 229: -#line 1906 "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 6006 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 230: + 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 6015 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 203: +#line 1920 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.param_list) = new CPPParameterList; +} +#line 6023 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 204: +#line 1924 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.param_list) = new CPPParameterList; + (yyval.u.param_list)->_includes_ellipsis = true; +} +#line 6032 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 205: +#line 1929 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.param_list) = (yyvsp[0].u.param_list); +} +#line 6040 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 206: +#line 1933 "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 6049 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 207: +#line 1938 "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 6058 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 208: +#line 1946 "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 6067 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 209: +#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 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 1974 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[0].u.expr); +} +#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 1993 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::get_default()); +} +#line 6140 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 218: +#line 1997 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(CPPExpression::get_delete()); +} +#line 6148 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 219: +#line 2004 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (CPPExpression *)NULL; +} +#line 6156 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 220: +#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 6212 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + 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 6222 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + 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 6016 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6232 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 231: -#line 1918 "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 6024 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6269 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 232: -#line 1922 "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 6032 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6277 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 233: -#line 1933 "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 6040 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6285 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 234: -#line 1937 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 6051 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 235: -#line 1947 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); -} -#line 6059 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 236: -#line 1951 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); -} -#line 6067 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 237: -#line 1955 "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 6076 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 238: -#line 1960 "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 6085 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 239: -#line 1965 "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 6094 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 240: -#line 1970 "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 6103 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 241: -#line 1975 "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 6112 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6296 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 242: -#line 1980 "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 6121 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 243: -#line 1985 "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 6130 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 244: -#line 1993 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); -} -#line 6138 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 245: -#line 1997 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); -} -#line 6146 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 246: -#line 2001 "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 6155 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 247: -#line 2006 "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 6164 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 248: -#line 2011 "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 6173 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 249: -#line 2016 "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 6182 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 250: -#line 2021 "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 6191 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 251: -#line 2026 "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 6200 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 252: -#line 2031 "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 6209 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 253: -#line 2036 "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 6219 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 254: -#line 2042 "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 6228 "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) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); - (yyval.u.inst_ident)->_packed = true; -} -#line 6237 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 256: -#line 2055 "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 6246 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 257: -#line 2060 "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 6255 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 258: -#line 2065 "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 6264 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 259: -#line 2070 "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 6273 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 260: -#line 2075 "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 6282 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 261: -#line 2080 "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 6291 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 262: -#line 2085 "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 6300 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 263: -#line 2090 "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 6309 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 264: -#line 2095 "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 6319 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 265: -#line 2101 "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 6328 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 266: #line 2109 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); } -#line 6336 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6304 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 243: +#line 2113 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.inst_ident) = new CPPInstanceIdentifier((yyvsp[0].u.identifier)); +} +#line 6312 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 244: +#line 2117 "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 6321 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 245: +#line 2122 "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 6330 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 246: +#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 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 2113 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); - (yyval.u.inst_ident)->_packed = true; -} -#line 6345 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 268: -#line 2118 "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 6354 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 269: -#line 2123 "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 6363 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 270: -#line 2128 "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 6372 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 271: -#line 2133 "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 6381 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 272: -#line 2138 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#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 6390 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 273: -#line 2143 "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 6399 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 274: -#line 2148 "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 6408 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 275: -#line 2153 "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 6417 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 276: -#line 2161 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.inst_ident) = new CPPInstanceIdentifier((CPPIdentifier *)NULL); -} -#line 6425 "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)->_packed = true; -} -#line 6434 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 278: -#line 2170 "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 6443 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 279: -#line 2175 "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 6452 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 280: -#line 2180 "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 6461 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 281: -#line 2185 "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 6470 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 282: -#line 2190 "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 6479 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 283: -#line 2195 "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 6488 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 284: -#line 2200 "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 6497 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 285: -#line 2205 "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 6506 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 286: -#line 2210 "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 6516 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 287: -#line 2216 "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 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 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 2223 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#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 6538 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6783 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 289: -#line 2230 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 6549 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6794 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 290: -#line 2240 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 297: +#line 2402 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.type) = CPPType::new_type((yyvsp[0].u.simple_type)); } -#line 6557 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6802 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 291: -#line 2244 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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) { @@ -6565,43 +6810,43 @@ yyreduce: } assert((yyval.u.type) != NULL); } -#line 6569 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6814 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 292: -#line 2252 "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 6577 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6822 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 293: -#line 2256 "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 6585 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6830 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 294: -#line 2260 "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 6593 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6838 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 295: -#line 2264 "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 6601 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6846 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 296: -#line 2268 "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) { @@ -6617,11 +6862,11 @@ yyreduce: (yyval.u.type) = et; } } -#line 6621 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6866 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 297: -#line 2284 "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) { @@ -6637,11 +6882,11 @@ yyreduce: (yyval.u.type) = et; } } -#line 6641 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6886 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 298: -#line 2300 "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) { @@ -6650,19 +6895,19 @@ yyreduce: yyerror("could not determine type of " + str.str(), (yylsp[-1])); } } -#line 6654 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6899 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 299: -#line 2309 "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 6662 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6907 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 300: -#line 2313 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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) { @@ -6672,19 +6917,19 @@ yyreduce: (yyval.u.type) = enum_type->get_underlying_type(); } } -#line 6676 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6921 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 301: -#line 2323 "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 6684 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6929 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 302: -#line 2330 "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) { @@ -6692,19 +6937,19 @@ yyreduce: } assert((yyval.u.type) != NULL); } -#line 6696 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6941 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 303: -#line 2341 "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 6704 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6949 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 304: -#line 2345 "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) { @@ -6712,43 +6957,43 @@ yyreduce: } assert((yyval.u.decl) != NULL); } -#line 6716 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6961 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 305: -#line 2353 "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 6724 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6969 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 306: -#line 2357 "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 6732 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6977 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 307: -#line 2361 "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 6740 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6985 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 308: -#line 2365 "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 6748 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 6993 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 309: -#line 2369 "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) { @@ -6764,11 +7009,11 @@ yyreduce: (yyval.u.decl) = et; } } -#line 6768 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7013 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 310: -#line 2385 "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) { @@ -6784,11 +7029,11 @@ yyreduce: (yyval.u.decl) = et; } } -#line 6788 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7033 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 311: -#line 2401 "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])); @@ -6806,11 +7051,11 @@ yyreduce: (yyval.u.decl) = et; } } -#line 6810 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7055 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 312: -#line 2419 "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) { @@ -6819,19 +7064,19 @@ yyreduce: yyerror("could not determine type of " + str.str(), (yylsp[-1])); } } -#line 6823 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7068 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 313: -#line 2428 "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 6831 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7076 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 314: -#line 2432 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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) { @@ -6841,27 +7086,27 @@ yyreduce: (yyval.u.decl) = enum_type->get_underlying_type(); } } -#line 6845 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7090 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 315: -#line 2442 "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 6853 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7098 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 316: -#line 2449 "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 6861 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7106 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 317: -#line 2453 "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) { @@ -6869,19 +7114,19 @@ yyreduce: } assert((yyval.u.type) != NULL); } -#line 6873 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7118 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 318: -#line 2461 "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 6881 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7126 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 319: -#line 2465 "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) { @@ -6897,11 +7142,11 @@ yyreduce: (yyval.u.type) = et; } } -#line 6901 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7146 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 320: -#line 2481 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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) { @@ -6917,11 +7162,11 @@ yyreduce: (yyval.u.type) = et; } } -#line 6921 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7166 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 321: -#line 2497 "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) { @@ -6930,11 +7175,11 @@ yyreduce: yyerror("could not determine type of " + str.str(), (yylsp[-1])); } } -#line 6934 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7179 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 322: -#line 2506 "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) { @@ -6944,71 +7189,71 @@ yyreduce: (yyval.u.type) = enum_type->get_underlying_type(); } } -#line 6948 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7193 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 323: -#line 2516 "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 6956 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7201 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 324: -#line 2523 "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 6964 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7209 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 325: -#line 2527 "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 6974 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7219 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 326: -#line 2535 "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 6982 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 327: -#line 2539 "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 6991 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 328: -#line 2544 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.type) = (yyvsp[0].u.inst_ident)->unroll_type((yyvsp[-1].u.type)); -} -#line 6999 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 329: -#line 2548 "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 7008 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7227 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 334: -#line 2563 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#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 7236 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + 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 7244 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + 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 7253 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 341: +#line 2725 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPVisibility starting_vis = ((yyvsp[-2].u.extension_enum) == CPPExtensionType::T_class) ? V_private : V_public; @@ -7022,22 +7267,22 @@ yyreduce: push_scope(new_scope); push_struct(st); } -#line 7026 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7271 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 335: -#line 2577 "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 7037 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7282 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 336: -#line 2587 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 343: +#line 2749 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPVisibility starting_vis = ((yyvsp[-2].u.extension_enum) == CPPExtensionType::T_class) ? V_private : V_public; @@ -7057,253 +7302,253 @@ yyreduce: push_scope(new_scope); push_struct(st); } -#line 7061 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7306 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 337: -#line 2607 "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 7072 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 339: -#line 2618 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - current_struct->_final = true; -} -#line 7080 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 344: -#line 2635 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - current_struct->append_derivation((yyvsp[0].u.type), V_unknown, false); -} -#line 7088 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 345: -#line 2639 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - current_struct->append_derivation((yyvsp[0].u.type), V_public, false); -} -#line 7096 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7317 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 346: -#line 2643 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2780 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - current_struct->append_derivation((yyvsp[0].u.type), V_protected, false); + current_struct->_final = true; } -#line 7104 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 347: -#line 2647 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - current_struct->append_derivation((yyvsp[0].u.type), V_private, false); -} -#line 7112 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 348: -#line 2651 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - current_struct->append_derivation((yyvsp[0].u.type), V_public, true); -} -#line 7120 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 349: -#line 2655 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - current_struct->append_derivation((yyvsp[0].u.type), V_protected, true); -} -#line 7128 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 350: -#line 2659 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - current_struct->append_derivation((yyvsp[0].u.type), V_private, true); -} -#line 7136 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7325 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 351: -#line 2663 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2797 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - current_struct->append_derivation((yyvsp[0].u.type), V_public, true); + current_struct->append_derivation((yyvsp[0].u.type), V_unknown, false); } -#line 7144 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7333 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 352: -#line 2667 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2801 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - current_struct->append_derivation((yyvsp[0].u.type), V_protected, true); + current_struct->append_derivation((yyvsp[0].u.type), V_public, false); } -#line 7152 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7341 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 353: -#line 2671 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2805 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - current_struct->append_derivation((yyvsp[0].u.type), V_private, true); + current_struct->append_derivation((yyvsp[0].u.type), V_protected, false); } -#line 7160 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7349 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 354: -#line 2678 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 2809 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + current_struct->append_derivation((yyvsp[0].u.type), V_private, false); +} +#line 7357 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 355: +#line 2813 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + current_struct->append_derivation((yyvsp[0].u.type), V_public, true); +} +#line 7365 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 356: +#line 2817 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + current_struct->append_derivation((yyvsp[0].u.type), V_protected, true); +} +#line 7373 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 357: +#line 2821 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + current_struct->append_derivation((yyvsp[0].u.type), V_private, true); +} +#line 7381 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 358: +#line 2825 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + current_struct->append_derivation((yyvsp[0].u.type), V_public, true); +} +#line 7389 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 359: +#line 2829 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + current_struct->append_derivation((yyvsp[0].u.type), V_protected, true); +} +#line 7397 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 360: +#line 2833 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + current_struct->append_derivation((yyvsp[0].u.type), V_private, true); +} +#line 7405 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 361: +#line 2840 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.enum_type) = current_enum; current_enum = NULL; } -#line 7169 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7414 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 355: -#line 2686 "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 7177 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7422 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 356: -#line 2690 "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 7185 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7430 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 357: -#line 2694 "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 7194 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7439 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 358: -#line 2699 "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 7203 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7448 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 359: -#line 2707 "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 7211 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7456 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 360: -#line 2711 "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 7219 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7464 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 362: -#line 2719 "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 7228 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7473 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 363: -#line 2724 "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 7237 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7482 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 365: -#line 2732 "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 7246 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7491 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 366: -#line 2737 "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 7255 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7500 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 367: -#line 2745 "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 7263 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7508 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 368: -#line 2749 "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 7271 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7516 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 369: -#line 2753 "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 7279 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7524 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 370: -#line 2760 "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 7287 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7532 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 371: -#line 2764 "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 7295 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7540 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 372: -#line 2768 "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 7303 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7548 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 373: -#line 2775 "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) { @@ -7321,19 +7566,19 @@ yyreduce: current_scope->define_namespace(nspace); push_scope(scope); } -#line 7325 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7570 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 374: -#line 2793 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 381: +#line 2955 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_scope(); } -#line 7333 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7578 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 375: -#line 2797 "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) { @@ -7352,143 +7597,143 @@ yyreduce: current_scope->define_namespace(nspace); push_scope(scope); } -#line 7356 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7601 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 376: -#line 2816 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 383: +#line 2978 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { pop_scope(); } -#line 7364 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7609 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 379: -#line 2825 "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 7374 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7619 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 380: -#line 2831 "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 7385 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7630 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 381: -#line 2838 "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 7395 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7640 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 385: -#line 2853 "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 7403 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7648 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 386: -#line 2857 "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 7411 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7656 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 387: -#line 2861 "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 7419 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7664 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 388: -#line 2865 "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 7427 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7672 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 389: -#line 2869 "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 7435 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7680 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 390: -#line 2873 "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 7444 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7689 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 391: -#line 2878 "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 7453 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7698 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 392: -#line 2883 "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 7462 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7707 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 393: -#line 2888 "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 7471 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7716 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 394: -#line 2893 "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 7479 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7724 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 395: -#line 2897 "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 7488 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7733 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 396: -#line 2902 "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) { @@ -7497,189 +7742,189 @@ yyreduce: (yyval.u.simple_type)->_flags |= CPPSimpleType::F_long; } } -#line 7501 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7746 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 397: -#line 2911 "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 7510 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7755 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 398: -#line 2916 "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 7519 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7764 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 399: -#line 2924 "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 7527 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7772 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 400: -#line 2928 "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 7535 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7780 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 401: -#line 2932 "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 7544 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7789 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 402: -#line 2940 "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 7552 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7797 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 403: -#line 2949 "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 7560 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7805 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 404: -#line 2953 "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 7568 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7813 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 512: -#line 2997 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 519: +#line 3159 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { } -#line 7575 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 536: -#line 3006 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 7583 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 537: -#line 3010 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 7591 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 538: -#line 3017 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (CPPExpression *)NULL; -} -#line 7599 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 539: -#line 3021 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 7607 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 540: -#line 3028 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 7615 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 541: -#line 3032 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(',', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 7623 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 542: -#line 3039 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = (yyvsp[0].u.expr); -} -#line 7631 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7820 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 543: -#line 3043 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3168 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-2].u.type), (yyvsp[0].u.expr))); + (yyval.u.expr) = (CPPExpression *)NULL; } -#line 7639 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7828 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 544: -#line 3047 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3172 "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)); + (yyval.u.expr) = (yyvsp[0].u.expr); } -#line 7647 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7836 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 545: -#line 3051 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3179 "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)); + (yyval.u.expr) = (CPPExpression *)NULL; } -#line 7655 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7844 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 546: -#line 3055 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3183 "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)); + (yyval.u.expr) = (yyvsp[0].u.expr); } -#line 7663 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7852 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 547: -#line 3059 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3190 "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)); + (yyval.u.expr) = (yyvsp[0].u.expr); } -#line 7671 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7860 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 548: -#line 3063 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3194 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func((yyvsp[-1].u.type))); + (yyval.u.expr) = new CPPExpression(',', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7679 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7868 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 549: -#line 3067 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#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 7884 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + 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 7892 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + 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 7900 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + 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 7908 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + 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 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) { @@ -7691,307 +7936,307 @@ yyreduce: (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func(arg->as_type())); } } -#line 7695 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 550: -#line 3079 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_ellipsis_func((yyvsp[-1].u.identifier))); -} -#line 7703 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 551: -#line 3083 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::alignof_func((yyvsp[-1].u.type))); -} -#line 7711 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 552: -#line 3087 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_NOT, (yyvsp[0].u.expr)); -} -#line 7719 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 553: -#line 3091 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_NEGATE, (yyvsp[0].u.expr)); -} -#line 7727 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 554: -#line 3095 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_MINUS, (yyvsp[0].u.expr)); -} -#line 7735 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 555: -#line 3099 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_PLUS, (yyvsp[0].u.expr)); -} -#line 7743 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 556: -#line 3103 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_STAR, (yyvsp[0].u.expr)); -} -#line 7751 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7940 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 557: -#line 3107 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3241 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(UNARY_REF, (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_ellipsis_func((yyvsp[-1].u.identifier))); } -#line 7759 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7948 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 558: -#line 3111 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3245 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('*', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(CPPExpression::alignof_func((yyvsp[-1].u.type))); } -#line 7767 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7956 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 559: -#line 3115 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3249 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('/', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_NOT, (yyvsp[0].u.expr)); } -#line 7775 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7964 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 560: -#line 3119 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3253 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('%', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_NEGATE, (yyvsp[0].u.expr)); } -#line 7783 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7972 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 561: -#line 3123 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3257 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('+', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_MINUS, (yyvsp[0].u.expr)); } -#line 7791 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7980 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 562: -#line 3127 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3261 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('-', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_PLUS, (yyvsp[0].u.expr)); } -#line 7799 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7988 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 563: -#line 3131 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3265 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('|', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_STAR, (yyvsp[0].u.expr)); } -#line 7807 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 7996 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 564: -#line 3135 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3269 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('^', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_REF, (yyvsp[0].u.expr)); } -#line 7815 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8004 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 565: -#line 3139 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3273 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('&', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('*', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7823 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8012 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 566: -#line 3143 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3277 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(OROR, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('/', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7831 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8020 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 567: -#line 3147 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3281 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(ANDAND, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('%', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7839 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8028 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 568: -#line 3151 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3285 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(EQCOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('+', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7847 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8036 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 569: -#line 3155 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3289 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(NECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('-', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7855 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8044 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 570: -#line 3159 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3293 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(LECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('|', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7863 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8052 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 571: -#line 3163 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3297 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(GECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('^', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7871 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8060 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 572: -#line 3167 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3301 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(LSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('&', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7879 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8068 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 573: -#line 3171 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3305 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(RSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(OROR, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7887 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8076 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 574: -#line 3175 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3309 "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)); + (yyval.u.expr) = new CPPExpression(ANDAND, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7895 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8084 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 575: -#line 3179 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3313 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('[', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); + (yyval.u.expr) = new CPPExpression(EQCOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7903 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8092 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 576: -#line 3183 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3317 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('f', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); + (yyval.u.expr) = new CPPExpression(NECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7911 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8100 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 577: -#line 3187 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3321 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('f', (yyvsp[-2].u.expr)); + (yyval.u.expr) = new CPPExpression(LECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7919 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8108 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 578: -#line 3191 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3325 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('.', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(GECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7927 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8116 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 579: -#line 3195 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3329 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(POINTSAT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(LSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7935 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8124 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 580: -#line 3199 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3333 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (yyvsp[-1].u.expr); + (yyval.u.expr) = new CPPExpression(RSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7943 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8132 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 581: -#line 3207 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3337 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (yyvsp[0].u.expr); + (yyval.u.expr) = new CPPExpression('?', (yyvsp[-4].u.expr), (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7951 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8140 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 582: -#line 3211 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3341 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-2].u.type), (yyvsp[0].u.expr))); + (yyval.u.expr) = new CPPExpression('[', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); } -#line 7959 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8148 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 583: -#line 3215 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3345 "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)); + (yyval.u.expr) = new CPPExpression('f', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); } -#line 7967 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8156 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 584: -#line 3219 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3349 "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)); + (yyval.u.expr) = new CPPExpression('f', (yyvsp[-2].u.expr)); } -#line 7975 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8164 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 585: -#line 3223 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3353 "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)); + (yyval.u.expr) = new CPPExpression('.', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7983 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8172 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 586: -#line 3227 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3357 "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)); + (yyval.u.expr) = new CPPExpression(POINTSAT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 7991 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8180 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 587: -#line 3231 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#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); @@ -8001,11 +8246,11 @@ yyreduce: assert(type != NULL); (yyval.u.expr) = new CPPExpression(CPPExpression::construct_op(type, (yyvsp[-1].u.expr))); } -#line 8005 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8250 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 588: -#line 3241 "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); @@ -8015,143 +8260,143 @@ yyreduce: assert(type != NULL); (yyval.u.expr) = new CPPExpression(CPPExpression::aggregate_init_op(type, (yyvsp[-1].u.expr))); } -#line 8019 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8264 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 589: -#line 3251 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 8029 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8274 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 590: -#line 3257 "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 8039 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8284 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 591: -#line 3263 "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 8049 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8294 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 592: -#line 3269 "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 8059 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8304 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 593: -#line 3275 "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 8069 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8314 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 594: -#line 3281 "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 8079 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8324 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 595: -#line 3287 "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 8090 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8335 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 596: -#line 3294 "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 8101 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8346 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 597: -#line 3301 "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 8112 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8357 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 598: -#line 3308 "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 8123 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8368 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 599: -#line 3315 "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 8133 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8378 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 600: -#line 3321 "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 8143 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8388 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 601: -#line 3327 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 608: +#line 3489 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func((yyvsp[-1].u.type))); } -#line 8151 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8396 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 602: -#line 3331 "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) { @@ -8163,43 +8408,43 @@ yyreduce: (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func(arg->as_type())); } } -#line 8167 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8412 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 603: -#line 3343 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 8175 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8420 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 604: -#line 3347 "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 8183 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8428 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 605: -#line 3351 "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 8191 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8436 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 606: -#line 3355 "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 8199 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8444 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 607: -#line 3359 "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"); @@ -8210,11 +8455,11 @@ yyreduce: } (yyval.u.expr) = new CPPExpression(CPPExpression::typeid_op((yyvsp[-1].u.type), std_type_info)); } -#line 8214 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8459 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 608: -#line 3370 "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"); @@ -8225,564 +8470,564 @@ yyreduce: } (yyval.u.expr) = new CPPExpression(CPPExpression::typeid_op((yyvsp[-1].u.expr), std_type_info)); } -#line 8229 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 609: -#line 3381 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_NOT, (yyvsp[0].u.expr)); -} -#line 8237 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 610: -#line 3385 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_NEGATE, (yyvsp[0].u.expr)); -} -#line 8245 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 611: -#line 3389 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_MINUS, (yyvsp[0].u.expr)); -} -#line 8253 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 612: -#line 3393 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_PLUS, (yyvsp[0].u.expr)); -} -#line 8261 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 613: -#line 3397 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_STAR, (yyvsp[0].u.expr)); -} -#line 8269 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 614: -#line 3401 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_REF, (yyvsp[0].u.expr)); -} -#line 8277 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 615: -#line 3405 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('*', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8285 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8474 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 616: -#line 3409 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3543 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('/', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_NOT, (yyvsp[0].u.expr)); } -#line 8293 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8482 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 617: -#line 3413 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3547 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('%', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_NEGATE, (yyvsp[0].u.expr)); } -#line 8301 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8490 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 618: -#line 3417 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3551 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('+', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_MINUS, (yyvsp[0].u.expr)); } -#line 8309 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8498 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 619: -#line 3421 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3555 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('-', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_PLUS, (yyvsp[0].u.expr)); } -#line 8317 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8506 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 620: -#line 3425 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3559 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('|', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_STAR, (yyvsp[0].u.expr)); } -#line 8325 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8514 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 621: -#line 3429 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3563 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('^', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_REF, (yyvsp[0].u.expr)); } -#line 8333 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8522 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 622: -#line 3433 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3567 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('&', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('*', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8341 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8530 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 623: -#line 3437 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3571 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(OROR, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('/', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8349 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8538 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 624: -#line 3441 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3575 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(ANDAND, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('%', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8357 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8546 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 625: -#line 3445 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3579 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(EQCOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('+', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8365 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8554 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 626: -#line 3449 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3583 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(NECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('-', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8373 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8562 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 627: -#line 3453 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3587 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(LECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('|', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8381 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8570 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 628: -#line 3457 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3591 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(GECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('^', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8389 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8578 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 629: -#line 3461 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3595 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('<', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('&', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8397 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8586 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 630: -#line 3465 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3599 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('>', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(OROR, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8405 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8594 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 631: -#line 3469 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3603 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(LSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(ANDAND, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8413 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8602 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 632: -#line 3473 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3607 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(RSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(EQCOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8421 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8610 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 633: -#line 3477 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3611 "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)); + (yyval.u.expr) = new CPPExpression(NECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8429 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8618 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 634: -#line 3481 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3615 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('[', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); + (yyval.u.expr) = new CPPExpression(LECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8437 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8626 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 635: -#line 3485 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3619 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('f', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); + (yyval.u.expr) = new CPPExpression(GECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8445 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8634 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 636: -#line 3489 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3623 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('f', (yyvsp[-2].u.expr)); + (yyval.u.expr) = new CPPExpression('<', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8453 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8642 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 637: -#line 3493 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3627 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('.', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('>', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8461 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8650 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 638: -#line 3497 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3631 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(POINTSAT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(LSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8469 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8658 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 639: -#line 3501 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3635 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (yyvsp[-1].u.expr); + (yyval.u.expr) = new CPPExpression(RSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8477 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8666 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 640: -#line 3508 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3639 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); + (yyval.u.expr) = new CPPExpression('?', (yyvsp[-4].u.expr), (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8485 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8674 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 641: -#line 3512 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3643 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(true); + (yyval.u.expr) = new CPPExpression('[', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); } -#line 8493 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8682 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 642: -#line 3516 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3647 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(false); + (yyval.u.expr) = new CPPExpression('f', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); } -#line 8501 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8690 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 643: -#line 3520 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3651 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); + (yyval.u.expr) = new CPPExpression('f', (yyvsp[-2].u.expr)); } -#line 8509 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8698 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 644: -#line 3524 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3655 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression((yyvsp[0].u.real)); + (yyval.u.expr) = new CPPExpression('.', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8517 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8706 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 645: -#line 3528 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3659 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (yyvsp[0].u.expr); + (yyval.u.expr) = new CPPExpression(POINTSAT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8525 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8714 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 646: -#line 3532 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3663 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (yyvsp[0].u.expr); + (yyval.u.expr) = (yyvsp[-1].u.expr); } -#line 8533 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8722 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 647: -#line 3536 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3670 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression((yyvsp[0].u.identifier), current_scope, global_scope, current_lexer); + (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); } -#line 8541 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8730 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 648: -#line 3540 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3674 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(true); +} +#line 8738 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 649: +#line 3678 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression(false); +} +#line 8746 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 650: +#line 3682 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); +} +#line 8754 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 651: +#line 3686 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression((yyvsp[0].u.real)); +} +#line 8762 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 652: +#line 3690 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[0].u.expr); +} +#line 8770 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 653: +#line 3694 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[0].u.expr); +} +#line 8778 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + 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 8786 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + 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 8551 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8796 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 649: -#line 3546 "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 8561 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8806 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 650: -#line 3552 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 657: +#line 3714 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(CPPExpression::get_nullptr()); } -#line 8569 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8814 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 651: -#line 3556 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 8579 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8824 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 652: -#line 3562 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 8590 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 653: -#line 3569 "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 8598 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 654: -#line 3573 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_ABSTRACT, (yyvsp[-1].u.type))); -} -#line 8606 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 655: -#line 3577 "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 8614 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 656: -#line 3581 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CLASS, (yyvsp[-1].u.type))); -} -#line 8622 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 657: -#line 3585 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CONSTRUCTIBLE, (yyvsp[-1].u.type))); -} -#line 8630 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 658: -#line 3589 "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 8638 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 659: -#line 3593 "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 8646 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8835 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 660: -#line 3597 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3731 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_DESTRUCTIBLE, (yyvsp[-1].u.type))); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_HAS_VIRTUAL_DESTRUCTOR, (yyvsp[-1].u.type))); } -#line 8654 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8843 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 661: -#line 3601 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3735 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_EMPTY, (yyvsp[-1].u.type))); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_ABSTRACT, (yyvsp[-1].u.type))); } -#line 8662 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8851 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 662: -#line 3605 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3739 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_ENUM, (yyvsp[-1].u.type))); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CLASS, (yyvsp[-3].u.type), (yyvsp[-1].u.type))); } -#line 8670 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8859 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 663: -#line 3609 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3743 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_FINAL, (yyvsp[-1].u.type))); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CLASS, (yyvsp[-1].u.type))); } -#line 8678 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8867 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 664: -#line 3613 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3747 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_FUNDAMENTAL, (yyvsp[-1].u.type))); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CONSTRUCTIBLE, (yyvsp[-1].u.type))); } -#line 8686 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8875 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 665: -#line 3617 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3751 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_POD, (yyvsp[-1].u.type))); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CONSTRUCTIBLE, (yyvsp[-3].u.type), (yyvsp[-1].u.type))); } -#line 8694 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8883 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 666: -#line 3621 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3755 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_POLYMORPHIC, (yyvsp[-1].u.type))); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_CONVERTIBLE_TO, (yyvsp[-3].u.type), (yyvsp[-1].u.type))); } -#line 8702 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8891 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 667: -#line 3625 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3759 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_STANDARD_LAYOUT, (yyvsp[-1].u.type))); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_DESTRUCTIBLE, (yyvsp[-1].u.type))); } -#line 8710 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8899 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 668: -#line 3629 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3763 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_TRIVIAL, (yyvsp[-1].u.type))); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_EMPTY, (yyvsp[-1].u.type))); } -#line 8718 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8907 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 669: -#line 3633 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3767 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_UNION, (yyvsp[-1].u.type))); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_ENUM, (yyvsp[-1].u.type))); } -#line 8726 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8915 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 670: -#line 3647 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3771 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (yyvsp[0].u.expr); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_FINAL, (yyvsp[-1].u.type))); } -#line 8734 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8923 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 671: -#line 3651 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3775 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::typecast_op((yyvsp[-2].u.type), (yyvsp[0].u.expr))); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_FUNDAMENTAL, (yyvsp[-1].u.type))); } -#line 8742 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8931 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 672: -#line 3655 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3779 "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)); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_POD, (yyvsp[-1].u.type))); } -#line 8750 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8939 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 673: -#line 3659 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3783 "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)); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_POLYMORPHIC, (yyvsp[-1].u.type))); } -#line 8758 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8947 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 674: -#line 3663 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3787 "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)); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_STANDARD_LAYOUT, (yyvsp[-1].u.type))); } -#line 8766 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8955 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 675: -#line 3667 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3791 "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)); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_TRIVIAL, (yyvsp[-1].u.type))); } -#line 8774 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8963 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 676: -#line 3671 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3795 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func((yyvsp[-1].u.type))); + (yyval.u.expr) = new CPPExpression(CPPExpression::type_trait(KW_IS_UNION, (yyvsp[-1].u.type))); } -#line 8782 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 8971 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 677: -#line 3675 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#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) { @@ -8794,43 +9039,43 @@ yyreduce: (yyval.u.expr) = new CPPExpression(CPPExpression::sizeof_func(arg->as_type())); } } -#line 8798 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9043 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 678: -#line 3687 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 8806 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9051 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 679: -#line 3691 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 8814 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9059 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 680: -#line 3695 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 8822 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9067 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 681: -#line 3699 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 8830 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9075 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 682: -#line 3703 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 689: +#line 3865 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPIdentifier ident(""); ident.add_name("std"); @@ -8841,11 +9086,11 @@ yyreduce: } (yyval.u.expr) = new CPPExpression(CPPExpression::typeid_op((yyvsp[-1].u.type), std_type_info)); } -#line 8845 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9090 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 683: -#line 3714 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 690: +#line 3876 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { CPPIdentifier ident(""); ident.add_name("std"); @@ -8856,409 +9101,409 @@ yyreduce: } (yyval.u.expr) = new CPPExpression(CPPExpression::typeid_op((yyvsp[-1].u.expr), std_type_info)); } -#line 8860 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 684: -#line 3725 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_NOT, (yyvsp[0].u.expr)); -} -#line 8868 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 685: -#line 3729 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_NEGATE, (yyvsp[0].u.expr)); -} -#line 8876 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 686: -#line 3733 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_MINUS, (yyvsp[0].u.expr)); -} -#line 8884 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 687: -#line 3737 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_PLUS, (yyvsp[0].u.expr)); -} -#line 8892 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 688: -#line 3741 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression(UNARY_REF, (yyvsp[0].u.expr)); -} -#line 8900 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 689: -#line 3745 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('*', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8908 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 690: -#line 3749 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.expr) = new CPPExpression('/', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); -} -#line 8916 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9105 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 691: -#line 3753 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3887 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('%', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_NOT, (yyvsp[0].u.expr)); } -#line 8924 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9113 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 692: -#line 3757 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3891 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('+', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_NEGATE, (yyvsp[0].u.expr)); } -#line 8932 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9121 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 693: -#line 3761 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3895 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('-', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_MINUS, (yyvsp[0].u.expr)); } -#line 8940 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9129 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 694: -#line 3765 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3899 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('|', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_PLUS, (yyvsp[0].u.expr)); } -#line 8948 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9137 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 695: -#line 3769 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3903 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('^', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(UNARY_REF, (yyvsp[0].u.expr)); } -#line 8956 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9145 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 696: -#line 3773 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3907 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('&', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('*', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8964 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9153 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 697: -#line 3777 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3911 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(OROR, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('/', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8972 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9161 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 698: -#line 3781 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3915 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(ANDAND, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('%', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8980 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9169 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 699: -#line 3785 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3919 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(EQCOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('+', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8988 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9177 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 700: -#line 3789 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3923 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(NECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('-', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 8996 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9185 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 701: -#line 3793 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3927 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(LECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('|', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9004 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9193 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 702: -#line 3797 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3931 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(GECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('^', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9012 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9201 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 703: -#line 3801 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3935 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('<', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression('&', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9020 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9209 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 704: -#line 3805 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3939 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('>', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(OROR, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9028 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9217 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 705: -#line 3809 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3943 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(LSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(ANDAND, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9036 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9225 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 706: -#line 3813 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3947 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(RSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(EQCOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9044 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9233 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 707: -#line 3817 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3951 "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)); + (yyval.u.expr) = new CPPExpression(NECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9052 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9241 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 708: -#line 3821 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3955 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('[', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); + (yyval.u.expr) = new CPPExpression(LECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9060 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9249 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 709: -#line 3825 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3959 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('f', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); + (yyval.u.expr) = new CPPExpression(GECOMPARE, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9068 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9257 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 710: -#line 3829 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3963 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression('f', (yyvsp[-2].u.expr)); + (yyval.u.expr) = new CPPExpression('<', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9076 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9265 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 711: -#line 3833 "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)); + (yyval.u.expr) = new CPPExpression('>', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9084 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9273 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 712: -#line 3837 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3971 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(POINTSAT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); + (yyval.u.expr) = new CPPExpression(LSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9092 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9281 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 713: -#line 3841 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3975 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (yyvsp[-1].u.expr); + (yyval.u.expr) = new CPPExpression(RSHIFT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9100 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9289 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 714: -#line 3848 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3979 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); + (yyval.u.expr) = new CPPExpression('?', (yyvsp[-4].u.expr), (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9108 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9297 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 715: -#line 3852 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3983 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(true); + (yyval.u.expr) = new CPPExpression('[', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); } -#line 9116 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9305 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 716: -#line 3856 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3987 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression(false); + (yyval.u.expr) = new CPPExpression('f', (yyvsp[-3].u.expr), (yyvsp[-1].u.expr)); } -#line 9124 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9313 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 717: -#line 3860 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3991 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); + (yyval.u.expr) = new CPPExpression('f', (yyvsp[-2].u.expr)); } -#line 9132 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9321 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 718: -#line 3864 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3995 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression((yyvsp[0].u.real)); + (yyval.u.expr) = new CPPExpression('.', (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9140 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9329 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 719: -#line 3868 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 3999 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (yyvsp[0].u.expr); + (yyval.u.expr) = new CPPExpression(POINTSAT, (yyvsp[-2].u.expr), (yyvsp[0].u.expr)); } -#line 9148 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9337 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 720: -#line 3872 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 4003 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (yyvsp[0].u.expr); + (yyval.u.expr) = (yyvsp[-1].u.expr); } -#line 9156 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9345 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 721: -#line 3876 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 4010 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression((yyvsp[0].u.identifier), current_scope, global_scope, current_lexer); + (yyval.u.expr) = new CPPExpression((yyvsp[0].u.integer)); } -#line 9164 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9353 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 722: -#line 3880 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#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 9174 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9419 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 723: -#line 3886 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 9184 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9429 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 724: -#line 3892 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 731: +#line 4054 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.expr) = new CPPExpression(CPPExpression::get_nullptr()); } -#line 9192 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9437 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 725: -#line 3900 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 732: +#line 4062 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.closure_type) = new CPPClosureType(); } -#line 9200 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9445 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 726: -#line 3904 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 733: +#line 4066 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.closure_type) = new CPPClosureType(CPPClosureType::CT_by_value); } -#line 9208 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9453 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 727: -#line 3908 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + case 734: +#line 4070 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { (yyval.u.closure_type) = new CPPClosureType(CPPClosureType::CT_by_reference); } -#line 9216 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9461 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 728: -#line 3912 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 9227 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9472 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 729: -#line 3919 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 9238 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9483 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 730: -#line 3929 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 9248 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9493 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 731: -#line 3935 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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 9258 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9503 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 732: -#line 3941 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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(); @@ -9268,11 +9513,11 @@ yyreduce: (yyval.u.capture)->_type = CPPClosureType::CT_by_value; } } -#line 9272 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9517 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 733: -#line 3951 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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(); @@ -9281,11 +9526,11 @@ yyreduce: yywarning("only capture name 'this' may be preceded by an asterisk", (yylsp[0])); } } -#line 9285 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9530 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 734: -#line 3963 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + 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) { @@ -9293,169 +9538,177 @@ yyreduce: } (yyval.u.type) = type; } -#line 9297 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9542 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 735: -#line 3971 "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 9305 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9550 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 736: -#line 3975 "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 9315 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9560 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 737: -#line 4005 "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 9323 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9568 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 738: -#line 4009 "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 9331 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9576 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 739: -#line 4013 "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 9339 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9584 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 740: -#line 4017 "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 9347 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9592 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 741: -#line 4021 "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 9355 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9600 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 742: -#line 4025 "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 9365 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 743: -#line 4031 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.identifier) = new CPPIdentifier("float", (yylsp[0])); -} -#line 9373 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 744: -#line 4035 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.identifier) = new CPPIdentifier("public", (yylsp[0])); -} -#line 9381 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 745: -#line 4039 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.identifier) = new CPPIdentifier("private", (yylsp[0])); -} -#line 9389 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 746: -#line 4043 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.identifier) = new CPPIdentifier("static", (yylsp[0])); -} -#line 9397 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 747: -#line 4054 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.identifier) = (yyvsp[0].u.identifier); -} -#line 9405 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 748: -#line 4058 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.identifier) = (yyvsp[0].u.identifier); -} -#line 9413 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ - break; - - case 749: -#line 4062 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ - { - (yyval.u.identifier) = (yyvsp[0].u.identifier); -} -#line 9421 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9610 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 750: -#line 4066 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 4193 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.identifier) = new CPPIdentifier("override", (yylsp[0])); + (yyval.u.identifier) = new CPPIdentifier("float", (yylsp[0])); } -#line 9429 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9618 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 751: -#line 4074 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 4197 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = new CPPExpression((yyvsp[0].str)); + (yyval.u.identifier) = new CPPIdentifier("public", (yylsp[0])); } -#line 9437 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9626 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 752: -#line 4078 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 4201 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ { - (yyval.u.expr) = (yyvsp[0].u.expr); + (yyval.u.identifier) = new CPPIdentifier("private", (yylsp[0])); } -#line 9445 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9634 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; case 753: -#line 4082 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ +#line 4205 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.identifier) = new CPPIdentifier("static", (yylsp[0])); +} +#line 9642 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + 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 9658 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 756: +#line 4224 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.identifier) = (yyvsp[0].u.identifier); +} +#line 9666 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 757: +#line 4228 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.identifier) = (yyvsp[0].u.identifier); +} +#line 9674 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 758: +#line 4232 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.identifier) = new CPPIdentifier("override", (yylsp[0])); +} +#line 9682 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 759: +#line 4240 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = new CPPExpression((yyvsp[0].str)); +} +#line 9690 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + case 760: +#line 4244 "dtool/src/cppparser/cppBison.yxx" /* yacc.c:1646 */ + { + (yyval.u.expr) = (yyvsp[0].u.expr); +} +#line 9698 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ + break; + + 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 9455 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9708 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; - case 754: -#line 4088 "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); @@ -9464,11 +9717,11 @@ yyreduce: } (yyval.u.expr)->_str += (yyvsp[0].u.expr)->_str; } -#line 9468 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ +#line 9721 "built/tmp/cppBison.yxx.c" /* yacc.c:1646 */ break; -#line 9472 "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 cc8062241a..a2ec1fca8c 100644 --- a/dtool/src/cppparser/cppBison.h.prebuilt +++ b/dtool/src/cppparser/cppBison.h.prebuilt @@ -143,51 +143,52 @@ extern int cppyydebug; KW_IS_TRIVIAL = 353, KW_IS_UNION = 354, KW_LONG = 355, - KW_MAKE_MAP_PROPERTY = 356, - KW_MAKE_PROPERTY = 357, - KW_MAKE_PROPERTY2 = 358, - KW_MAKE_SEQ = 359, - KW_MAKE_SEQ_PROPERTY = 360, - KW_MUTABLE = 361, - KW_NAMESPACE = 362, - KW_NEW = 363, - KW_NOEXCEPT = 364, - KW_NULLPTR = 365, - KW_OPERATOR = 366, - KW_OVERRIDE = 367, - KW_PRIVATE = 368, - KW_PROTECTED = 369, - KW_PUBLIC = 370, - KW_REGISTER = 371, - KW_REINTERPRET_CAST = 372, - KW_RETURN = 373, - KW_SHORT = 374, - KW_SIGNED = 375, - KW_SIZEOF = 376, - KW_STATIC = 377, - KW_STATIC_ASSERT = 378, - KW_STATIC_CAST = 379, - KW_STRUCT = 380, - KW_TEMPLATE = 381, - KW_THREAD_LOCAL = 382, - KW_THROW = 383, - KW_TRUE = 384, - KW_TRY = 385, - KW_TYPEDEF = 386, - KW_TYPEID = 387, - KW_TYPENAME = 388, - KW_UNDERLYING_TYPE = 389, - KW_UNION = 390, - KW_UNSIGNED = 391, - KW_USING = 392, - KW_VIRTUAL = 393, - KW_VOID = 394, - KW_VOLATILE = 395, - KW_WCHAR_T = 396, - KW_WHILE = 397, - START_CPP = 398, - START_CONST_EXPR = 399, - START_TYPE = 400 + 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. */ @@ -289,51 +290,52 @@ extern int cppyydebug; #define KW_IS_TRIVIAL 353 #define KW_IS_UNION 354 #define KW_LONG 355 -#define KW_MAKE_MAP_PROPERTY 356 -#define KW_MAKE_PROPERTY 357 -#define KW_MAKE_PROPERTY2 358 -#define KW_MAKE_SEQ 359 -#define KW_MAKE_SEQ_PROPERTY 360 -#define KW_MUTABLE 361 -#define KW_NAMESPACE 362 -#define KW_NEW 363 -#define KW_NOEXCEPT 364 -#define KW_NULLPTR 365 -#define KW_OPERATOR 366 -#define KW_OVERRIDE 367 -#define KW_PRIVATE 368 -#define KW_PROTECTED 369 -#define KW_PUBLIC 370 -#define KW_REGISTER 371 -#define KW_REINTERPRET_CAST 372 -#define KW_RETURN 373 -#define KW_SHORT 374 -#define KW_SIGNED 375 -#define KW_SIZEOF 376 -#define KW_STATIC 377 -#define KW_STATIC_ASSERT 378 -#define KW_STATIC_CAST 379 -#define KW_STRUCT 380 -#define KW_TEMPLATE 381 -#define KW_THREAD_LOCAL 382 -#define KW_THROW 383 -#define KW_TRUE 384 -#define KW_TRY 385 -#define KW_TYPEDEF 386 -#define KW_TYPEID 387 -#define KW_TYPENAME 388 -#define KW_UNDERLYING_TYPE 389 -#define KW_UNION 390 -#define KW_UNSIGNED 391 -#define KW_USING 392 -#define KW_VIRTUAL 393 -#define KW_VOID 394 -#define KW_VOLATILE 395 -#define KW_WCHAR_T 396 -#define KW_WHILE 397 -#define START_CPP 398 -#define START_CONST_EXPR 399 -#define START_TYPE 400 +#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 752bbbfecd..68f4f96706 100644 --- a/dtool/src/cppparser/cppBison.yxx +++ b/dtool/src/cppparser/cppBison.yxx @@ -304,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 @@ -397,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 @@ -548,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); } } @@ -928,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 @@ -1714,6 +1864,18 @@ maybe_trailing_return_type: ; +maybe_comma_identifier: + empty +{ + $$ = NULL; +} + | ',' IDENTIFIER +{ + $$ = $2; +} + ; + + function_parameter_list: empty { @@ -4042,6 +4204,10 @@ name: | KW_STATIC { $$ = new CPPIdentifier("static", @1); +} + | KW_DEFAULT +{ + $$ = new CPPIdentifier("default", @1); } ; diff --git a/dtool/src/cppparser/cppClosureType.cxx b/dtool/src/cppparser/cppClosureType.cxx old mode 100755 new mode 100644 diff --git a/dtool/src/cppparser/cppClosureType.h b/dtool/src/cppparser/cppClosureType.h old mode 100755 new mode 100644 diff --git a/dtool/src/cppparser/cppExpression.cxx b/dtool/src/cppparser/cppExpression.cxx index 0dff4f64b8..adbbaea14e 100644 --- a/dtool/src/cppparser/cppExpression.cxx +++ b/dtool/src/cppparser/cppExpression.cxx @@ -1658,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: 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.h b/dtool/src/cppparser/cppFunctionType.h index a5a66976b0..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, 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 6a24d9eb69..ecaecb98b6 100644 --- a/dtool/src/cppparser/cppPreprocessor.cxx +++ b/dtool/src/cppparser/cppPreprocessor.cxx @@ -2641,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; 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/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/dtoolbase/deletedBufferChain.cxx b/dtool/src/dtoolbase/deletedBufferChain.cxx index 0451ada7d3..27d7214d79 100644 --- a/dtool/src/dtoolbase/deletedBufferChain.cxx +++ b/dtool/src/dtoolbase/deletedBufferChain.cxx @@ -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/dtool_platform.h b/dtool/src/dtoolbase/dtool_platform.h index 7955c7a941..2a6ed12a9b 100644 --- a/dtool/src/dtoolbase/dtool_platform.h +++ b/dtool/src/dtoolbase/dtool_platform.h @@ -51,10 +51,14 @@ #elif defined(__ANDROID__) #if defined(__ARM_ARCH_7A__) #define DTOOL_PLATFORM "android_armv7a" +#elif defined(__aarch64__) +#define DTOOL_PLATFORM "android_aarch64" #elif defined(__arm__) #define DTOOL_PLATFORM "android_arm" #elif defined(__mips__) #define DTOOL_PLATFORM "android_mips" +#elif defined(__x86_64) +#define DTOOL_PLATFORM "android_amd64" #elif defined(__i386__) #define DTOOL_PLATFORM "android_i386" #endif @@ -72,10 +76,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 965069614e..3f8e07f998 100644 --- a/dtool/src/dtoolbase/dtoolbase.h +++ b/dtool/src/dtoolbase/dtoolbase.h @@ -58,18 +58,6 @@ #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 @@ -106,6 +94,14 @@ #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 I'm smart enough to use GetProcAddress for backward compat on @@ -344,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 @@ -468,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 @@ -478,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 88d4244af6..eb84f1fccd 100644 --- a/dtool/src/dtoolbase/dtoolbase_cc.h +++ b/dtool/src/dtoolbase/dtoolbase_cc.h @@ -34,9 +34,9 @@ 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 @@ -166,7 +166,6 @@ template typename remove_reference::type &&move(T &&t) { # 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 @@ -176,13 +175,7 @@ template typename remove_reference::type &&move(T &&t) { # if __has_extension(cxx_deleted_functions) # define DELETED = delete # endif -#elif defined(__GNUC__) && (__cplusplus >= 201103L) // GCC - -// GCC defines several macros which we can query. List of all supported -// builtin macros: https://gcc.gnu.org/projects/cxx-status.html -# if __cpp_constexpr >= 200704 -# define CONSTEXPR constexpr -# endif +#elif defined(__GNUC__) // GCC // Starting at GCC 4.4 # if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) @@ -194,15 +187,21 @@ template typename remove_reference::type &&move(T &&t) { // 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 @@ -210,11 +209,9 @@ template typename remove_reference::type &&move(T &&t) { # define NOEXCEPT noexcept # define USE_MOVE_SEMANTICS # define FINAL final -# define OVERRIDE override # define MOVE(x) move(x) #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) @@ -230,6 +227,9 @@ template typename remove_reference::type &&move(T &&t) { // 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 @@ -240,9 +240,6 @@ template typename remove_reference::type &&move(T &&t) { #ifndef FINAL # define FINAL #endif -#ifndef OVERRIDE -# define OVERRIDE -#endif #ifndef DEFAULT_CTOR # define DEFAULT_CTOR {} #endif diff --git a/dtool/src/dtoolbase/memoryHook.cxx b/dtool/src/dtoolbase/memoryHook.cxx index 224fd3107b..510b5b2688 100644 --- a/dtool/src/dtoolbase/memoryHook.cxx +++ b/dtool/src/dtoolbase/memoryHook.cxx @@ -47,7 +47,9 @@ static_assert(MEMORY_HOOK_ALIGNMENT * 8 >= 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 @@ -202,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 } /** @@ -216,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(); } /** @@ -631,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, @@ -642,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 687e348aef..18ce70c0bb 100644 --- a/dtool/src/dtoolbase/memoryHook.h +++ b/dtool/src/dtoolbase/memoryHook.h @@ -67,7 +67,6 @@ public: 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; @@ -79,7 +78,6 @@ protected: size_t _max_heap_size; virtual void overflow_heap_size(); -#endif // DO_MEMORY_USAGE private: size_t _page_size; @@ -87,7 +85,7 @@ private: typedef map DeletedChains; DeletedChains _deleted_chains; - MutexImpl _lock; + mutable MutexImpl _lock; }; #include "memoryHook.I" 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 4bbdbcbbb1..ed85da8d03 100644 --- a/dtool/src/dtoolbase/typeHandle.cxx +++ b/dtool/src/dtoolbase/typeHandle.cxx @@ -45,7 +45,9 @@ get_memory_usage(MemoryClass memory_class) const { 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); @@ -67,7 +69,9 @@ inc_memory_usage(MemoryClass memory_class, size_t size) { 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); 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/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.h b/dtool/src/dtoolbase/typedObject.h index 789e47110c..cc963b0fac 100644 --- a/dtool/src/dtoolbase/typedObject.h +++ b/dtool/src/dtoolbase/typedObject.h @@ -97,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 cf7ecac50e..c4f2e5093e 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(); @@ -524,7 +540,6 @@ read_environment_variables() { */ void ExecutionEnvironment:: read_args() { -#ifndef ANDROID // First, we need to fill in _dtool_name. This contains the full path to // the p3dtool library. @@ -562,7 +577,7 @@ read_args() { } #endif -#if defined(IS_FREEBSD) || defined(IS_LINUX) +#if defined(IS_FREEBSD) || (defined(IS_LINUX) && !defined(__ANDROID__)) // FreeBSD and Linux have a function to get the origin of a loaded library. char origin[PATH_MAX + 1]; @@ -817,8 +832,6 @@ read_args() { } #endif // _WIN32 -#endif // ANDROID - if (_dtool_name.empty()) { _dtool_name = _binary_name; } 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..d2c566b557 100644 --- a/dtool/src/dtoolutil/filename.cxx +++ b/dtool/src/dtoolutil/filename.cxx @@ -48,6 +48,11 @@ #include #endif +#if defined(__ANDROID__) && !defined(HAVE_LOCKF) +// Needed for flock. +#include +#endif + TextEncoder::Encoding Filename::_filesystem_encoding = TextEncoder::E_utf8; TVOLATILE AtomicAdjust::Pointer Filename::_home_directory; @@ -600,8 +605,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 +660,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/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_composite2.cxx b/dtool/src/dtoolutil/p3dtoolutil_composite2.cxx index 2adf532173..10067c84fd 100644 --- a/dtool/src/dtoolutil/p3dtoolutil_composite2.cxx +++ b/dtool/src/dtoolutil/p3dtoolutil_composite2.cxx @@ -8,6 +8,9 @@ #include "stringDecoder.cxx" #include "textEncoder.cxx" #include "unicodeLatinMap.cxx" +#include "vector_double.cxx" +#include "vector_float.cxx" #include "vector_int.cxx" #include "vector_string.cxx" +#include "vector_uchar.cxx" #include "win32ArgParser.cxx" 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.h b/dtool/src/dtoolutil/pandaSystem.h index bed5f2c073..85c1a0ea38 100644 --- a/dtool/src/dtoolutil/pandaSystem.h +++ b/dtool/src/dtoolutil/pandaSystem.h @@ -48,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/panda/src/express/vector_double.cxx b/dtool/src/dtoolutil/vector_double.cxx similarity index 89% rename from panda/src/express/vector_double.cxx rename to dtool/src/dtoolutil/vector_double.cxx index 165600e2a7..a6def549af 100644 --- a/panda/src/express/vector_double.cxx +++ b/dtool/src/dtoolutil/vector_double.cxx @@ -13,8 +13,8 @@ #include "vector_double.h" -#define EXPCL EXPCL_PANDAEXPRESS -#define EXPTP EXPTP_PANDAEXPRESS +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE double #define NAME vector_double diff --git a/panda/src/express/vector_double.h b/dtool/src/dtoolutil/vector_double.h similarity index 82% rename from panda/src/express/vector_double.h rename to dtool/src/dtoolutil/vector_double.h index 1a36c1e425..6757bddc7a 100644 --- a/panda/src/express/vector_double.h +++ b/dtool/src/dtoolutil/vector_double.h @@ -14,19 +14,17 @@ #ifndef VECTOR_DOUBLE_H #define VECTOR_DOUBLE_H -#include "pandabase.h" - -#include "pvector.h" +#include "dtoolbase.h" /** * A vector of doubles. This class is defined once here, and exported to - * PANDA.DLL; other packages that want to use a vector of this type (whether + * DTOOL.DLL; other packages that want to use a vector of this type (whether * they need to export it or not) should include this header file, rather than * defining the vector again. */ -#define EXPCL EXPCL_PANDAEXPRESS -#define EXPTP EXPTP_PANDAEXPRESS +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE double #define NAME vector_double diff --git a/panda/src/express/vector_float.cxx b/dtool/src/dtoolutil/vector_float.cxx similarity index 89% rename from panda/src/express/vector_float.cxx rename to dtool/src/dtoolutil/vector_float.cxx index cc0df9289c..a84b030a56 100644 --- a/panda/src/express/vector_float.cxx +++ b/dtool/src/dtoolutil/vector_float.cxx @@ -13,8 +13,8 @@ #include "vector_float.h" -#define EXPCL EXPCL_PANDAEXPRESS -#define EXPTP EXPTP_PANDAEXPRESS +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE float #define NAME vector_float diff --git a/panda/src/express/vector_float.h b/dtool/src/dtoolutil/vector_float.h similarity index 81% rename from panda/src/express/vector_float.h rename to dtool/src/dtoolutil/vector_float.h index 862980b15a..36d9f200f9 100644 --- a/panda/src/express/vector_float.h +++ b/dtool/src/dtoolutil/vector_float.h @@ -14,19 +14,17 @@ #ifndef VECTOR_FLOAT_H #define VECTOR_FLOAT_H -#include "pandabase.h" - -#include "pvector.h" +#include "dtoolbase.h" /** * A vector of floats. This class is defined once here, and exported to - * PANDA.DLL; other packages that want to use a vector of this type (whether + * DTOOL.DLL; other packages that want to use a vector of this type (whether * they need to export it or not) should include this header file, rather than * defining the vector again. */ -#define EXPCL EXPCL_PANDAEXPRESS -#define EXPTP EXPTP_PANDAEXPRESS +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE float #define NAME vector_float diff --git a/panda/src/express/vector_stdfloat.h b/dtool/src/dtoolutil/vector_stdfloat.h similarity index 96% rename from panda/src/express/vector_stdfloat.h rename to dtool/src/dtoolutil/vector_stdfloat.h index cecf6c18d8..e0100ae6fd 100644 --- a/panda/src/express/vector_stdfloat.h +++ b/dtool/src/dtoolutil/vector_stdfloat.h @@ -14,7 +14,7 @@ #ifndef VECTOR_STDFLOAT_H #define VECTOR_STDFLOAT_H -#include "pandabase.h" +#include "dtoolbase.h" #include "vector_double.h" #include "vector_float.h" diff --git a/panda/src/express/vector_uchar.cxx b/dtool/src/dtoolutil/vector_uchar.cxx similarity index 89% rename from panda/src/express/vector_uchar.cxx rename to dtool/src/dtoolutil/vector_uchar.cxx index 5463ef0724..07434032dd 100644 --- a/panda/src/express/vector_uchar.cxx +++ b/dtool/src/dtoolutil/vector_uchar.cxx @@ -13,8 +13,8 @@ #include "vector_uchar.h" -#define EXPCL EXPCL_PANDAEXPRESS -#define EXPTP EXPTP_PANDAEXPRESS +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE unsigned char #define NAME vector_uchar diff --git a/panda/src/express/vector_uchar.h b/dtool/src/dtoolutil/vector_uchar.h similarity index 81% rename from panda/src/express/vector_uchar.h rename to dtool/src/dtoolutil/vector_uchar.h index ce6a1997bb..608472ae7e 100644 --- a/panda/src/express/vector_uchar.h +++ b/dtool/src/dtoolutil/vector_uchar.h @@ -14,19 +14,17 @@ #ifndef VECTOR_UCHAR_H #define VECTOR_UCHAR_H -#include "pandabase.h" - -#include "pvector.h" +#include "dtoolbase.h" /** * A vector of uchars. This class is defined once here, and exported to - * PANDAEXPRESS.DLL; other packages that want to use a vector of this type + * DTOOL.DLL; other packages that want to use a vector of this type * (whether they need to export it or not) should include this header file, * rather than defining the vector again. */ -#define EXPCL EXPCL_PANDAEXPRESS -#define EXPTP EXPTP_PANDAEXPRESS +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE unsigned char #define NAME vector_uchar 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 085d9421d1..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) { @@ -799,6 +797,7 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak (_parameters[first_param + 1]._name == "kwargs" || _parameters[first_param + 1]._name == "kwds")) { _flags |= F_explicit_args; + _args_type = InterfaceMaker::AT_keyword_args; } } @@ -848,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; @@ -869,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 /") { @@ -898,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; @@ -941,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/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 e4dbfd4084..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,6 +3530,19 @@ 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); } @@ -3460,6 +3554,7 @@ write_function_for_name(ostream &out, Object *obj, 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"; @@ -3497,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); } @@ -3573,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: @@ -3606,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)", @@ -3762,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"; @@ -3777,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"; @@ -3791,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"; @@ -4066,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; @@ -4084,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; @@ -4230,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; } @@ -4246,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; + } } } } @@ -4262,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; } @@ -4288,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()) { @@ -4357,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"; @@ -4373,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"; } @@ -4392,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"; @@ -4406,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. @@ -4447,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"; } @@ -4516,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; @@ -4611,7 +4644,7 @@ write_function_instance(ostream &out, FunctionRemap *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. @@ -4666,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()) { @@ -4781,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"; @@ -4868,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"; @@ -4906,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. @@ -5393,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"; @@ -5417,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) { @@ -5487,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 << " *)" @@ -5554,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; @@ -5571,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; @@ -5623,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. @@ -5689,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) { @@ -5872,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. @@ -5904,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. @@ -5993,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. @@ -6273,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) { @@ -6299,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"; @@ -6374,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"; } @@ -6384,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); } } @@ -6409,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"; } @@ -6457,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"; } @@ -6486,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(); @@ -6600,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. @@ -6679,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 @@ -6764,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; @@ -6873,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/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/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 100644 index 0000000000..f0dd42cf73 --- /dev/null +++ b/dtool/src/interrogatedb/py_compat.cxx @@ -0,0 +1,55 @@ +/** + * 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" +#include "py_panda.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 100644 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 d66ee88c21..f4edc8f4ad 100644 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -20,121 +20,16 @@ #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" +#include "register_type.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 +128,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 +140,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 +150,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 +164,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,22 +202,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(); -// _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 - #ifdef NDEBUG -#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(); @@ -330,13 +228,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) @@ -456,18 +357,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. @@ -491,8 +393,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); @@ -508,6 +411,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 100644 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 100644 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/MainHelix.h b/dtool/src/parser-inc/MainHelix.h deleted file mode 100644 index deeefca994..0000000000 --- a/dtool/src/parser-inc/MainHelix.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef MAINHELIX_H -#define MAINHELIX_H - -// This file is a stub header file. -class DLLAccess {}; -class IHXClientEngine {}; -class IHXPlayer {}; - -#endif 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/rfftw.h b/dtool/src/parser-inc/fftw3.h similarity index 75% rename from dtool/src/parser-inc/rfftw.h rename to dtool/src/parser-inc/fftw3.h index 47bb2102d1..c5b3b8131d 100644 --- a/dtool/src/parser-inc/rfftw.h +++ b/dtool/src/parser-inc/fftw3.h @@ -6,11 +6,9 @@ * license. You should have received a copy of this license along * with this source code in a file named "LICENSE." * - * @file rfftw.h - * @author drose - * @date 2007-06-27 + * @file fftw.h + * @author cfsworks + * @date 2018-02-17 */ -typedef struct _rfftw_plan rfftw_plan; - - +typedef struct _fftw_plan fftw_plan; 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/glew/glew.h b/dtool/src/parser-inc/glew/glew.h old mode 100755 new mode 100644 diff --git a/dtool/src/parser-inc/hxcom.h b/dtool/src/parser-inc/hxcom.h deleted file mode 100644 index 8c733fddd3..0000000000 --- a/dtool/src/parser-inc/hxcom.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXCOM_H -#define HXCOM_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxcomm.h b/dtool/src/parser-inc/hxcomm.h deleted file mode 100644 index 5e1137c6d7..0000000000 --- a/dtool/src/parser-inc/hxcomm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXCOMM_H -#define HXCOMM_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxcore.h b/dtool/src/parser-inc/hxcore.h deleted file mode 100644 index 4848d39d34..0000000000 --- a/dtool/src/parser-inc/hxcore.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXCORE_H -#define HXCORE_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxengin.h b/dtool/src/parser-inc/hxengin.h deleted file mode 100644 index 0b6467baae..0000000000 --- a/dtool/src/parser-inc/hxengin.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXENGIN_H -#define HXENGIN_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxerror.h b/dtool/src/parser-inc/hxerror.h deleted file mode 100644 index b3cf207ade..0000000000 --- a/dtool/src/parser-inc/hxerror.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXERROR_H -#define HXERROR_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxfiles.h b/dtool/src/parser-inc/hxfiles.h deleted file mode 100644 index 4616b8bd11..0000000000 --- a/dtool/src/parser-inc/hxfiles.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXFILES_H -#define HXFILES_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxtbuf.h b/dtool/src/parser-inc/hxtbuf.h deleted file mode 100644 index 12210512a1..0000000000 --- a/dtool/src/parser-inc/hxtbuf.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXTBUF_H -#define HXTBUF_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxtbuff.h b/dtool/src/parser-inc/hxtbuff.h deleted file mode 100644 index 5ba15806f5..0000000000 --- a/dtool/src/parser-inc/hxtbuff.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXTBUFF_H -#define HXTBUFF_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxwin.h b/dtool/src/parser-inc/hxwin.h deleted file mode 100644 index b8601c2a63..0000000000 --- a/dtool/src/parser-inc/hxwin.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXWIN_H -#define HXWIN_H - -// This file is a stub header file. - -#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/ode/ode.h b/dtool/src/parser-inc/ode/ode.h old mode 100755 new mode 100644 diff --git a/dtool/src/parser-inc/ogg/os_types.h b/dtool/src/parser-inc/ogg/os_types.h old mode 100755 new mode 100644 diff --git a/dtool/src/parser-inc/openssl/x509v3.h b/dtool/src/parser-inc/openssl/x509v3.h old mode 100755 new mode 100644 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/androidLogStream.cxx b/dtool/src/prc/androidLogStream.cxx index b5512f9f52..6427b9163a 100644 --- a/dtool/src/prc/androidLogStream.cxx +++ b/dtool/src/prc/androidLogStream.cxx @@ -92,6 +92,7 @@ overflow(int ch) { */ void AndroidLogStream::AndroidLogStreamBuf:: write_char(char c) { + nout.put(c); if (c == '\n') { // Write a line to the log file. __android_log_write(_priority, _tag.c_str(), _data.c_str()); 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 3d428852d2..21a33f93c0 100644 --- a/dtool/src/prc/encryptStreamBuf.cxx +++ b/dtool/src/prc/encryptStreamBuf.cxx @@ -236,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/notify.cxx b/dtool/src/prc/notify.cxx index f97b7d78ac..f74f55e596 100644 --- a/dtool/src/prc/notify.cxx +++ b/dtool/src/prc/notify.cxx @@ -335,9 +335,8 @@ assert_failure(const char *expression, int line, #ifdef ANDROID __android_log_assert("assert", "Panda3D", "Assertion failed: %s", message.c_str()); -#else - nout << "Assertion failed: " << message << "\n"; #endif + nout << "Assertion failed: " << message << "\n"; // This is redefined here, shadowing the defining in config_prc.h, so we can // guarantee it has already been constructed. 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/notifyCategory.cxx b/dtool/src/prc/notifyCategory.cxx index a6e0a0181c..432dfca159 100644 --- a/dtool/src/prc/notifyCategory.cxx +++ b/dtool/src/prc/notifyCategory.cxx @@ -64,7 +64,11 @@ out(NotifySeverity severity, bool prefix) const { // logging system. We use a special type of stream that redirects it to // Android's log system. if (prefix) { - return AndroidLogStream::out(severity) << *this << ": "; + if (severity == NS_info) { + return AndroidLogStream::out(severity) << *this << ": "; + } else { + return AndroidLogStream::out(severity) << *this << "(" << severity << "): "; + } } else { return AndroidLogStream::out(severity); } 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/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 13a564f226..856cf6f855 100644 --- a/dtool/src/prc/streamWrapper.h +++ b/dtool/src/prc/streamWrapper.h @@ -52,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; @@ -79,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; @@ -115,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 f1d9046108..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; } diff --git a/dtool/src/pystub/pystub.cxx b/dtool/src/pystub/pystub.cxx index 1ebad938bd..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(...); @@ -91,6 +92,8 @@ extern "C" { 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(...); @@ -105,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(...); @@ -141,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(...); @@ -180,12 +190,16 @@ extern "C" { 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(...); @@ -198,6 +212,7 @@ extern "C" { 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; @@ -207,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; @@ -241,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; } @@ -275,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; } @@ -300,6 +321,8 @@ 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; } @@ -314,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; } @@ -350,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; } @@ -389,12 +419,16 @@ 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; }; @@ -411,6 +445,8 @@ void Py_Initialize() { int Py_IsInitialized() { return 0; } +void PyEval_InitThreads() { +} void *PyExc_AssertionError = (void *)NULL; @@ -421,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/makepanda/confauto.in b/makepanda/confauto.in old mode 100755 new mode 100644 diff --git a/makepanda/config.in b/makepanda/config.in old mode 100755 new mode 100644 index e61f73f04b..1317517339 --- 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 old mode 100755 new mode 100644 index c5e76fc766..bd139f7083 --- a/makepanda/installer.nsi +++ b/makepanda/installer.nsi @@ -588,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 @@ -603,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 diff --git a/makepanda/installpanda.py b/makepanda/installpanda.py index c8a2cdf2fe..63a4601ee6 100644 --- a/makepanda/installpanda.py +++ b/makepanda/installpanda.py @@ -229,6 +229,14 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir( DeleteBuildFiles(destdir+prefix+"/include/panda3d") DeleteEmptyDirs(destdir+prefix+"/include/panda3d") + # Change permissions on include directory. + os.chmod(destdir + prefix + "/include", 0o755) + for root, dirs, files in os.walk(destdir + prefix + "/include"): + for basename in dirs: + os.chmod(os.path.join(root, basename), 0o755) + for basename in files: + os.chmod(os.path.join(root, basename), 0o644) + # rpmlint doesn't like this file, for some reason. if (os.path.isfile(destdir+prefix+"/share/panda3d/direct/leveleditor/copyfiles.pl")): os.remove(destdir+prefix+"/share/panda3d/direct/leveleditor/copyfiles.pl") @@ -276,6 +284,7 @@ if (__name__ == "__main__"): parser.add_option('', '--destdir', dest = 'destdir', help = 'Destination directory [default=%s]' % destdir, default = destdir) parser.add_option('', '--prefix', dest = 'prefix', help = 'Prefix [default=/usr/local]', default = '/usr/local') parser.add_option('', '--runtime', dest = 'runtime', help = 'Specify if runtime build [default=no]', action = 'store_true', default = False) + parser.add_option('', '--verbose', dest = 'verbose', help = 'Print commands that are executed [default=no]', action = 'store_true', default = False) (options, args) = parser.parse_args() destdir = options.destdir @@ -286,6 +295,9 @@ if (__name__ == "__main__"): if (destdir != "" and not os.path.isdir(destdir)): exit("Directory '%s' does not exist!" % destdir) + if options.verbose: + SetVerbose(True) + if (options.runtime): print("Installing Panda3D Runtime into " + destdir + options.prefix) InstallRuntime(destdir = destdir, prefix = options.prefix, outputdir = options.outputdir) diff --git a/makepanda/makechm.bat b/makepanda/makechm.bat old mode 100755 new mode 100644 diff --git a/makepanda/makepanda.bat b/makepanda/makepanda.bat old mode 100755 new mode 100644 diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index f782898df5..21c0181159 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 @@ -77,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 + "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 @@ -125,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)") @@ -162,12 +165,12 @@ 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,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=", @@ -191,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 @@ -313,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'.") @@ -366,7 +372,8 @@ 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 @@ -571,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") @@ -634,10 +645,11 @@ 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("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/rfftw.lib") - if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/fftw.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/fftw3.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") @@ -652,6 +664,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") @@ -689,9 +704,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"') @@ -794,9 +815,10 @@ if (COMPILER=="GCC"): SmartPkgEnable("FFMPEG", ffmpeg_libs, ffmpeg_libs, ("libavformat/avformat.h", "libavcodec/avcodec.h", "libavutil/avutil.h")) SmartPkgEnable("SWSCALE", "libswscale", "libswscale", ("libswscale/swscale.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg") SmartPkgEnable("SWRESAMPLE","libswresample", "libswresample", ("libswresample/swresample.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg") - SmartPkgEnable("FFTW", "", ("rfftw", "fftw"), ("fftw.h", "rfftw.h")) + SmartPkgEnable("FFTW", "", ("fftw3"), ("fftw.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"? @@ -810,6 +832,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", "opus")) SmartPkgEnable("JPEG", "", ("jpeg"), "jpeglib.h") SmartPkgEnable("PNG", "libpng", ("png"), "png.h", tool = "libpng-config") @@ -821,13 +844,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): @@ -837,7 +863,7 @@ if (COMPILER=="GCC"): if not PkgSkip("PYTHON"): python_lib = SDK["PYTHONVERSION"] - if not RTDIST: + if not RTDIST and GetTarget() != 'android': # We don't link anything in the SDK with libpython. python_lib = "" SmartPkgEnable("PYTHON", "", python_lib, (SDK["PYTHONVERSION"], SDK["PYTHONVERSION"] + "/Python.h")) @@ -855,7 +881,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 @@ -916,7 +942,7 @@ if (COMPILER=="GCC"): if GetTarget() == 'android': LibName("ALWAYS", '-llog') - LibName("ALWAYS", '-landroid') + LibName("ANDROID", '-landroid') LibName("JNIGRAPHICS", '-ljnigraphics') for pkg in MAYAVERSIONS: @@ -1066,7 +1092,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" @@ -1107,7 +1133,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_" @@ -1200,7 +1226,7 @@ def CompileCxx(obj,src,opts): if (COMPILER=="GCC"): if (src.endswith(".c")): cmd = GetCC() +' -fPIC -c -o ' + obj - else: cmd = GetCXX()+' -std=gnu++0x -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: @@ -1224,7 +1250,13 @@ def CompileCxx(obj,src,opts): cmd += " -arch %s" % arch if "SYSROOT" in SDK: - cmd += ' --sysroot=%s -no-canonical-prefixes' % (SDK["SYSROOT"]) + if GetTarget() != "android": + cmd += ' --sysroot=%s' % (SDK["SYSROOT"]) + else: + ndk_dir = SDK["ANDROID_NDK"].replace('\\', '/') + cmd += ' -isystem %s/sysroot/usr/include' % (ndk_dir) + cmd += ' -isystem %s/sysroot/usr/include/%s' % (ndk_dir, SDK["ANDROID_TRIPLE"]) + cmd += ' -no-canonical-prefixes' # Android-specific flags. arch = GetTargetArch() @@ -1232,32 +1264,42 @@ def CompileCxx(obj,src,opts): if GetTarget() == "android": # Most of the specific optimization flags here were # just copied from the default Android Makefiles. - cmd += ' -I%s/include' % (SDK["ANDROID_STL"]) - cmd += ' -I%s/libs/%s/include' % (SDK["ANDROID_STL"], SDK["ANDROID_ABI"]) + if "ANDROID_API" in SDK: + cmd += ' -D__ANDROID_API__=' + str(SDK["ANDROID_API"]) + if "ANDROID_GCC_TOOLCHAIN" in SDK: + cmd += ' -gcc-toolchain ' + SDK["ANDROID_GCC_TOOLCHAIN"].replace('\\', '/') cmd += ' -ffunction-sections -funwind-tables' if arch == 'armv7a': - cmd += ' -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__' - cmd += ' -fstack-protector -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16' + cmd += ' -target armv7-none-linux-androideabi' + cmd += ' -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16' + cmd += ' -fno-integrated-as' elif arch == 'arm': - cmd += ' -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__' - cmd += ' -fstack-protector -march=armv5te -mtune=xscale -msoft-float' + cmd += ' -target armv5te-none-linux-androideabi' + cmd += ' -march=armv5te -mtune=xscale -msoft-float' + cmd += ' -fno-integrated-as' + elif arch == 'aarch64': + cmd += ' -target aarch64-none-linux-android' elif arch == 'mips': - cmd += ' -finline-functions -fmessage-length=0' - cmd += ' -fno-inline-functions-called-once -fgcse-after-reload' - cmd += ' -frerun-cse-after-loop -frename-registers' + cmd += ' -target mipsel-none-linux-android' + cmd += ' -mips32' + elif arch == 'mips64': + cmd += ' -target mips64el-none-linux-android' + cmd += ' -fintegrated-as' + elif arch == 'x86': + cmd += ' -target i686-none-linux-android' + cmd += ' -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32' + cmd += ' -mstackrealign' + elif arch == 'x86_64': + cmd += ' -target x86_64-none-linux-android' + cmd += ' -march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel' cmd += " -Wa,--noexecstack" - # Now add specific release/debug flags. - if optlevel >= 3: - cmd += " -fomit-frame-pointer" - if arch.startswith('arm'): - cmd += ' -finline-limit=64 -mthumb' - elif arch == 'mips': - cmd += ' -funswitch-loops -finline-limit=300' - else: - cmd += ' -fno-omit-frame-pointer' - if arch.startswith('arm'): + # Do we want thumb or arm instructions? + if arch.startswith('arm'): + if optlevel >= 3: + cmd += ' -mthumb' + else: cmd += ' -marm' # Enable SIMD instructions if requested @@ -1283,7 +1325,7 @@ def CompileCxx(obj,src,opts): if optlevel >= 4 or GetTarget() == "android": cmd += " -fno-rtti" - if ('SSE2' in opts or not PkgSkip("SSE2")) and not arch.startswith("arm"): + if ('SSE2' in opts or not PkgSkip("SSE2")) and not arch.startswith("arm") and arch != 'aarch64': cmd += " -msse2" # Needed by both Python, Panda, Eigen, all of which break aliasing rules. @@ -1410,12 +1452,19 @@ def CompileIgate(woutd,wsrc,opts): cmd += ' -D_MSC_VER=1600 -D"__declspec(param)=" -D__cdecl -D_near -D_far -D__near -D__far -D__stdcall' if (COMPILER=="GCC"): cmd += ' -D__attribute__\(x\)=' - if GetTargetArch() in ("x86_64", "amd64"): + target_arch = GetTargetArch() + if target_arch in ("x86_64", "amd64"): cmd += ' -D_LP64' + elif target_arch == 'aarch64': + cmd += ' -D_LP64 -D__LP64__ -D__aarch64__' else: cmd += ' -D__i386__' - if GetTarget() == 'darwin': + + target = GetTarget() + if target == 'darwin': cmd += ' -D__APPLE__' + elif target == 'android': + cmd += ' -D__ANDROID__' optlevel = GetOptimizeOption(opts) if (optlevel==1): cmd += ' -D_DEBUG' @@ -1498,7 +1547,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 @@ -1551,6 +1602,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 @@ -1650,8 +1716,11 @@ def CompileLink(dll, obj, opts): if COMPILER == "GCC": cxx = GetCXX() - if GetOrigExt(dll) == ".exe" and GetTarget() != 'android': + if GetOrigExt(dll) == ".exe": cmd = cxx + ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp' + if GetTarget() == "android": + # Necessary to work around an issue with libandroid depending on vendor libraries + cmd += ' -Wl,--allow-shlib-undefined' else: if (GetTarget() == "darwin"): cmd = cxx + ' -undefined dynamic_lookup' @@ -1664,6 +1733,7 @@ def CompileLink(dll, obj, opts): cmd += ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp' else: cmd = cxx + ' -shared' + # Always set soname on Android to avoid a linker warning when loading the library. if "MODULE" not in opts or GetTarget() == 'android': cmd += " -Wl,-soname=" + os.path.basename(dll) cmd += ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp' @@ -1687,9 +1757,26 @@ def CompileLink(dll, obj, opts): cmd += " -arch %s" % arch elif GetTarget() == 'android': + arch = GetTargetArch() + if "ANDROID_GCC_TOOLCHAIN" in SDK: + cmd += ' -gcc-toolchain ' + SDK["ANDROID_GCC_TOOLCHAIN"].replace('\\', '/') cmd += " -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now" - if GetTargetArch() == 'armv7a': + if arch == 'armv7a': + cmd += ' -target armv7-none-linux-androideabi' cmd += " -march=armv7-a -Wl,--fix-cortex-a8" + elif arch == 'arm': + cmd += ' -target armv5te-none-linux-androideabi' + elif arch == 'aarch64': + cmd += ' -target aarch64-none-linux-android' + elif arch == 'mips': + cmd += ' -target mipsel-none-linux-android' + cmd += ' -mips32' + elif arch == 'mips64': + cmd += ' -target mips64el-none-linux-android' + elif arch == 'x86': + cmd += ' -target i686-none-linux-android' + elif arch == 'x86_64': + cmd += ' -target x86_64-none-linux-android' cmd += ' -lc -lm' else: cmd += " -pthread" @@ -1700,8 +1787,8 @@ def CompileLink(dll, obj, opts): if LDFLAGS != "": cmd += " " + LDFLAGS - # Don't link libraries with Python. - if "PYTHON" in opts and GetOrigExt(dll) != ".exe" and not RTDIST: + # Don't link libraries with Python, except on Android. + if "PYTHON" in opts and GetOrigExt(dll) != ".exe" and not RTDIST and GetTarget() != 'android': opts = opts[:] opts.remove("PYTHON") @@ -1720,14 +1807,7 @@ def CompileLink(dll, obj, opts): oscmd(cmd) - if GetTarget() == 'android': - # Copy the library to built/libs/$ANDROID_ABI and strip it. - # This is the format that Android NDK projects should use. - new_path = '%s/libs/%s/%s' % (GetOutputDir(), SDK["ANDROID_ABI"], os.path.basename(dll)) - CopyFile(new_path, dll) - oscmd('%s --strip-unneeded %s' % (GetStrip(), BracketNameWithQuotes(new_path))) - - elif (GetOptimizeOption(opts)==4 and GetTarget() == 'linux'): + if GetOptimizeOption(opts) == 4 and GetTarget() in ('linux', 'android'): oscmd(GetStrip() + " --strip-unneeded " + BracketNameWithQuotes(dll)) os.system("chmod +x " + BracketNameWithQuotes(dll)) @@ -1836,6 +1916,25 @@ def CompileRsrc(target, src, opts): cmd += " " + BracketNameWithQuotes(src) oscmd(cmd) +########################################################################################## +# +# CompileJava (Android only) +# +########################################################################################## + +def CompileJava(target, src, opts): + """Compiles a .java file into a .class file.""" + cmd = "ecj " + + optlevel = GetOptimizeOption(opts) + if optlevel >= 4: + cmd += "-debug:none " + + cmd += "-cp " + GetOutputDir() + "/classes " + cmd += "-d " + GetOutputDir() + "/classes " + cmd += BracketNameWithQuotes(src) + oscmd(cmd) + ########################################################################################## # # FreezePy @@ -2089,6 +2188,9 @@ def CompileAnything(target, inputs, opts, progress = None): elif (origsuffix==".rsrc"): ProgressOutput(progress, "Building resource object", target) return CompileRsrc(target, infile, opts) + elif (origsuffix==".class"): + ProgressOutput(progress, "Building Java class", target) + return CompileJava(target, infile, opts) elif (origsuffix==".obj"): if (infile.endswith(".cxx")): ProgressOutput(progress, "Building C++ object", target) @@ -2227,6 +2329,7 @@ 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'), @@ -2298,7 +2401,7 @@ def WriteConfigSettings(): dtool_config["HAVE_CGGL"] = '1' dtool_config["HAVE_CGDX9"] = '1' - if (GetTarget() != "linux"): + if GetTarget() not in ("linux", "android"): dtool_config["HAVE_PROC_SELF_EXE"] = 'UNDEF' dtool_config["HAVE_PROC_SELF_MAPS"] = 'UNDEF' dtool_config["HAVE_PROC_SELF_CMDLINE"] = 'UNDEF' @@ -2666,14 +2769,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: @@ -2762,12 +2867,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") @@ -2788,17 +2893,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): @@ -2853,7 +2958,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]) @@ -2874,7 +2980,8 @@ 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: # We need to copy the Python DLL to the bin directory for now. @@ -2936,9 +3043,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")) @@ -3111,7 +3222,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): @@ -3171,15 +3281,6 @@ if (PkgSkip("CONTRIB")==0): CopyAllHeaders('contrib/src/contribbase') CopyAllHeaders('contrib/src/ai') -######################################################################## -# -# Copy Java files, if applicable -# -######################################################################## - -if GetTarget() == 'android': - CopyAllJavaSources('panda/src/android') - ######################################################################## # # These definitions are syntactic shorthand. They make it easy @@ -3396,6 +3497,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/ # @@ -3446,7 +3600,7 @@ TargetAdd('libpandaexpress.dll', input='p3express_composite1.obj') TargetAdd('libpandaexpress.dll', input='p3express_composite2.obj') TargetAdd('libpandaexpress.dll', input='p3pandabase_pandabase.obj') TargetAdd('libpandaexpress.dll', input=COMMON_DTOOL_LIBS) -TargetAdd('libpandaexpress.dll', opts=['ADVAPI', 'WINSOCK2', 'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER']) +TargetAdd('libpandaexpress.dll', opts=['ADVAPI', 'WINSOCK2', 'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER', 'ANDROID']) # # DIRECTORY: panda/src/pipeline/ @@ -3527,6 +3681,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) @@ -3798,7 +3953,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') @@ -3813,10 +3971,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']) @@ -3948,9 +4106,9 @@ 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'] + 'SQUISH', 'NVIDIACG', 'VORBIS', 'OPUS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI'] TargetAdd('panda_panda.obj', opts=OPTS, input='panda.cxx') @@ -4027,6 +4185,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') @@ -4066,6 +4228,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') @@ -4106,6 +4275,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') @@ -4782,7 +4952,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'] @@ -4802,7 +4972,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') @@ -4927,8 +5097,10 @@ if (not RTDIST and not RUNTIME and PkgSkip("PVIEW")==0 and GetTarget() != 'andro # if (not RUNTIME and GetTarget() == 'android'): - native_app_glue = os.path.join(SDK['ANDROID_NDK'], 'sources', 'android', 'native_app_glue') - OPTS=['DIR:panda/src/android', 'DIR:' + native_app_glue] + OPTS=['DIR:panda/src/android'] + TargetAdd('org/panda3d/android/NativeIStream.class', opts=OPTS, input='NativeIStream.java') + TargetAdd('org/panda3d/android/NativeOStream.class', opts=OPTS, input='NativeOStream.java') + TargetAdd('org/panda3d/android/PandaActivity.class', opts=OPTS, input='PandaActivity.java') TargetAdd('p3android_composite1.obj', opts=OPTS, input='p3android_composite1.cxx') TargetAdd('libp3android.dll', input='p3android_composite1.obj') @@ -4940,15 +5112,15 @@ if (not RUNTIME and GetTarget() == 'android'): if (not RTDIST and PkgSkip("PVIEW")==0): TargetAdd('pview_pview.obj', opts=OPTS, input='pview.cxx') - TargetAdd('pview.exe', input='android_native_app_glue.obj') - TargetAdd('pview.exe', input='android_main.obj') - TargetAdd('pview.exe', input='pview_pview.obj') - TargetAdd('pview.exe', input='libp3framework.dll') + TargetAdd('libpview.dll', input='android_native_app_glue.obj') + TargetAdd('libpview.dll', input='android_main.obj') + TargetAdd('libpview.dll', input='pview_pview.obj') + TargetAdd('libpview.dll', input='libp3framework.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') + TargetAdd('libpview.dll', input='libpandaegg.dll') + TargetAdd('libpview.dll', input='libp3android.dll') + TargetAdd('libpview.dll', input=COMMON_PANDA_LIBS) + TargetAdd('libpview.dll', opts=['MODULE', 'ANDROID']) # # DIRECTORY: panda/src/androiddisplay/ @@ -4956,7 +5128,7 @@ if (not RUNTIME and GetTarget() == 'android'): if (GetTarget() == 'android' and PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and not RUNTIME): DefSymbol('GLES', 'OPENGLES_1', '') - OPTS=['DIR:panda/src/androiddisplay', 'DIR:panda/src/glstuff', 'DIR:' + native_app_glue, 'BUILDING:PANDAGLES', 'GLES', 'EGL'] + OPTS=['DIR:panda/src/androiddisplay', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGLES', 'GLES', 'EGL'] TargetAdd('pandagles_androiddisplay_composite1.obj', opts=OPTS, input='p3androiddisplay_composite1.cxx') OPTS=['DIR:panda/metalibs/pandagles', 'BUILDING:PANDAGLES', 'GLES', 'EGL'] TargetAdd('pandagles_pandagles.obj', opts=OPTS, input='pandagles.cxx') @@ -5109,10 +5281,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': @@ -5124,7 +5292,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') @@ -5574,7 +5742,7 @@ if not PkgSkip("PANDATOOL") and not PkgSkip("ASSIMP"): 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/ @@ -6253,6 +6421,8 @@ for VER in MAYAVERSIONS: continue elif GetTarget() == 'darwin' and int(VNUM) >= 2009: ARCH_OPTS = ['NOARCH:PPC'] + elif GetTarget() == 'darwin': + ARCH_OPTS = ['NOARCH:X86_64'] else: ARCH_OPTS = [] @@ -6378,6 +6548,29 @@ if (PkgSkip("CONTRIB")==0 and not RUNTIME): TargetAdd('ai.pyd', input=COMMON_PANDA_LIBS) TargetAdd('ai.pyd', opts=['PYTHON']) +# +# DIRECTORY: contrib/src/rplight/ +# +if not PkgSkip("CONTRIB") and not PkgSkip("PYTHON") and not RUNTIME: + OPTS=['DIR:contrib/src/rplight', 'BUILDING:RPLIGHT', 'PYTHON'] + TargetAdd('p3rplight_composite1.obj', opts=OPTS, input='p3rplight_composite1.cxx') + + IGATEFILES=GetDirectoryContents('contrib/src/rplight', ["*.h", "*_composite*.cxx"]) + TargetAdd('libp3rplight.in', opts=OPTS, input=IGATEFILES) + TargetAdd('libp3rplight.in', opts=['IMOD:panda3d._rplight', 'ILIB:libp3rplight', 'SRCDIR:contrib/src/rplight']) + TargetAdd('libp3rplight_igate.obj', input='libp3rplight.in', opts=["DEPENDENCYONLY"]) + + TargetAdd('rplight_module.obj', input='libp3rplight.in') + TargetAdd('rplight_module.obj', opts=OPTS) + TargetAdd('rplight_module.obj', opts=['IMOD:panda3d._rplight', 'ILIB:_rplight', 'IMPORT:panda3d.core']) + + TargetAdd('_rplight.pyd', input='rplight_module.obj') + TargetAdd('_rplight.pyd', input='libp3rplight_igate.obj') + TargetAdd('_rplight.pyd', input='p3rplight_composite1.obj') + TargetAdd('_rplight.pyd', input='libp3interrogatedb.dll') + TargetAdd('_rplight.pyd', input=COMMON_PANDA_LIBS) + TargetAdd('_rplight.pyd', opts=['PYTHON']) + # # Generate the models directory and samples directory # @@ -6580,6 +6773,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 @@ -6795,7 +6998,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 @@ -6804,7 +7007,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 @@ -6813,8 +7016,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 @@ -6850,8 +7053,8 @@ def MakeInstallerLinux(): else: InstallPanda(destdir="targetroot", prefix="/usr", outputdir=GetOutputDir(), libdir=lib_dir) oscmd("chmod -R 755 targetroot/usr/share/panda3d") - oscmd("mkdir -p targetroot/usr/share/man/man1") - oscmd("cp doc/man/*.1 targetroot/usr/share/man/man1/") + oscmd("mkdir -m 0755 -p targetroot/usr/share/man/man1") + oscmd("install -m 0644 doc/man/*.1 targetroot/usr/share/man/man1/") oscmd("dpkg --print-architecture > "+GetOutputDir()+"/tmp/architecture.txt") pkg_arch = ReadFile(GetOutputDir()+"/tmp/architecture.txt").strip() @@ -7016,8 +7219,8 @@ def MakeInstallerOSX(): # Trailing newline is important, works around a bug in OSX WriteFile("dstroot/tools/etc/paths.d/Panda3D", "/Developer/Panda3D/bin\n") - oscmd("mkdir -p dstroot/tools/usr/local/share/man/man1") - oscmd("cp doc/man/*.1 dstroot/tools/usr/local/share/man/man1/") + oscmd("mkdir -m 0755 -p dstroot/tools/usr/local/share/man/man1") + oscmd("install -m 0644 doc/man/*.1 dstroot/tools/usr/local/share/man/man1/") for base in os.listdir(GetOutputDir()+"/bin"): binname = "dstroot/tools/Developer/Panda3D/bin/" + base @@ -7148,10 +7351,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') @@ -7202,8 +7409,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): @@ -7224,7 +7431,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') @@ -7236,6 +7443,118 @@ def MakeInstallerFreeBSD(): WriteFile("+MANIFEST", manifest_txt) oscmd("pkg create -p pkg-plist -r %s -m . -o . %s" % (os.path.abspath("targetroot"), "--verbose" if GetVerbose() else "--quiet")) +def MakeInstallerAndroid(): + oscmd("rm -rf apkroot") + oscmd("mkdir apkroot") + + # Also remove the temporary apks. + apk_unaligned = os.path.join(GetOutputDir(), "tmp", "panda3d-unaligned.apk") + apk_unsigned = os.path.join(GetOutputDir(), "tmp", "panda3d-unsigned.apk") + if os.path.exists(apk_unaligned): + os.unlink(apk_unaligned) + if os.path.exists(apk_unsigned): + os.unlink(apk_unsigned) + + # Compile the Java classes into a Dalvik executable. + dx_cmd = "dx --dex --output=apkroot/classes.dex " + if GetOptimize() <= 2: + dx_cmd += "--debug " + if GetVerbose(): + dx_cmd += "--verbose " + if "ANDROID_API" in SDK: + dx_cmd += "--min-sdk-version=%d " % (SDK["ANDROID_API"]) + dx_cmd += os.path.join(GetOutputDir(), "classes") + oscmd(dx_cmd) + + # Copy the libraries one by one. In case of library dependencies, strip + # off any suffix (eg. libfile.so.1.0), as Android does not support them. + source_dir = os.path.join(GetOutputDir(), "lib") + target_dir = os.path.join("apkroot", "lib", SDK["ANDROID_ABI"]) + oscmd("mkdir -p %s" % (target_dir)) + + # Determine the library directories we should look in. + libpath = [source_dir] + for dir in os.environ.get("LD_LIBRARY_PATH", "").split(':'): + dir = os.path.expandvars(dir) + dir = os.path.expanduser(dir) + if os.path.isdir(dir): + dir = os.path.realpath(dir) + if not dir.startswith("/system") and not dir.startswith("/vendor"): + libpath.append(dir) + + def copy_library(source, base): + # Copy file to destination, stripping version suffix. + target = os.path.join(target_dir, base) + if not target.endswith('.so'): + target = target.rpartition('.so.')[0] + '.so' + + if os.path.isfile(target): + # Already processed. + return + + oscmd("cp %s %s" % (source, target)) + + # Walk through the library dependencies. + oscmd("ldd %s | grep .so > %s/tmp/otool-libs.txt" % (target, GetOutputDir()), True) + for line in open(GetOutputDir() + "/tmp/otool-libs.txt", "r"): + line = line.strip() + if not line: + continue + if '.so.' in line: + dep = line.rpartition('.so.')[0] + '.so' + oscmd("patchelf --replace-needed %s %s %s" % (line, dep, target)) + else: + dep = line + + # Find it on the LD_LIBRARY_PATH. + for dir in libpath: + fulldep = os.path.join(dir, dep) + if os.path.isfile(fulldep): + copy_library(os.path.realpath(fulldep), dep) + break + + for base in os.listdir(source_dir): + if not base.startswith('lib'): + continue + if not base.endswith('.so') and '.so.' not in base: + continue + + source = os.path.join(source_dir, base) + if os.path.islink(source): + continue + copy_library(source, base) + + # Copy the models and config files to the virtual assets filesystem. + oscmd("mkdir apkroot/assets") + oscmd("cp -R %s apkroot/assets/models" % (os.path.join(GetOutputDir(), "models"))) + oscmd("cp -R %s apkroot/assets/etc" % (os.path.join(GetOutputDir(), "etc"))) + + # Make an empty res folder. It's needed for the apk to be installable, apparently. + oscmd("mkdir apkroot/res") + + # Now package up the application + oscmd("cp panda/src/android/pview_manifest.xml apkroot/AndroidManifest.xml") + aapt_cmd = "aapt package" + aapt_cmd += " -F %s" % (apk_unaligned) + aapt_cmd += " -M apkroot/AndroidManifest.xml" + aapt_cmd += " -A apkroot/assets -S apkroot/res" + aapt_cmd += " -I $PREFIX/share/aapt/android.jar" + oscmd(aapt_cmd) + + # And add all the libraries to it. + oscmd("cd apkroot && aapt add ../%s classes.dex lib/%s/lib*.so" % (apk_unaligned, SDK["ANDROID_ABI"])) + + # Now align the .apk, which is necessary for Android to load it. + oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned)) + + # Finally, sign it using a debug key. This is generated if it doesn't exist. + oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned)) + + # Clean up. + oscmd("rm -rf apkroot") + os.unlink(apk_unaligned) + os.unlink(apk_unsigned) + try: if INSTALLER: ProgressOutput(100.0, "Building installer") @@ -7270,6 +7589,8 @@ try: MakeInstallerOSX() elif (target == 'freebsd'): MakeInstallerFreeBSD() + elif (target == 'android'): + MakeInstallerAndroid() else: exit("Do not know how to make an installer for this platform") diff --git a/makepanda/makepanda.sln b/makepanda/makepanda.sln old mode 100755 new mode 100644 diff --git a/makepanda/makepanda.vcproj b/makepanda/makepanda.vcproj old mode 100755 new mode 100644 index f533c17448..c21322f94a --- a/makepanda/makepanda.vcproj +++ b/makepanda/makepanda.vcproj @@ -2371,12 +2371,9 @@ - - - @@ -3739,18 +3736,6 @@ - - - - - - - - - - - - diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 2f6482c68f..0bce3f4450 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"] @@ -35,6 +37,8 @@ TARGET_ARCH = None HAS_TARGET_ARCH = False TOOLCHAIN_PREFIX = "" ANDROID_ABI = None +ANDROID_TRIPLE = None +ANDROID_API = None SYS_LIB_DIRS = [] SYS_INC_DIRS = [] DEBUG_DEPENDENCIES = False @@ -55,6 +59,27 @@ else: # case. host_64 = (platform.architecture()[0] == '64bit') +# On Android, get a list of all the public system libraries. +ANDROID_SYS_LIBS = [] +if os.path.exists("/etc/public.libraries.txt"): + for line in open("/etc/public.libraries.txt", "r"): + line = line.strip() + ANDROID_SYS_LIBS.append(line) + +######################################################################## +## +## 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 +101,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"), @@ -273,7 +299,15 @@ def GetHost(): elif sys.platform == 'darwin': return 'darwin' elif sys.platform.startswith('linux'): - return 'linux' + try: + # Python seems to offer no built-in way to check this. + osname = subprocess.check_output(["uname", "-o"]) + if osname.strip().lower() == b'android': + return 'android' + else: + return 'linux' + except: + return 'linux' elif sys.platform.startswith('freebsd'): return 'freebsd' else: @@ -327,26 +361,53 @@ def SetTarget(target, arch=None): if arch not in choices: exit('Mac OS X architecture must be one of %s' % (', '.join(choices))) - elif target == 'android': + elif target == 'android' or target.startswith('android-'): if arch is None: - arch = 'arm' + # If compiling on Android, default to same architecture. Otherwise, arm. + if host == 'android': + arch = host_arch + else: + arch = 'armv7a' + + # Did we specify an API level? + global ANDROID_API + target, _, api = target.partition('-') + if api: + ANDROID_API = int(api) + elif arch in ('mips64', 'aarch64', 'x86_64'): + # 64-bit platforms were introduced in Android 21. + ANDROID_API = 21 + else: + # Default to the lowest API level supported by NDK r16. + ANDROID_API = 14 # Determine the prefix for our gcc tools, eg. arm-linux-androideabi-gcc - global ANDROID_ABI + global ANDROID_ABI, ANDROID_TRIPLE if arch == 'armv7a': ANDROID_ABI = 'armeabi-v7a' - TOOLCHAIN_PREFIX = 'arm-linux-androideabi-' + ANDROID_TRIPLE = 'arm-linux-androideabi' elif arch == 'arm': ANDROID_ABI = 'armeabi' - TOOLCHAIN_PREFIX = 'arm-linux-androideabi-' - elif arch == 'x86': - ANDROID_ABI = 'x86' - TOOLCHAIN_PREFIX = 'i686-linux-android-' + ANDROID_TRIPLE = 'arm-linux-androideabi' + elif arch == 'aarch64': + ANDROID_ABI = 'arm64-v8a' + ANDROID_TRIPLE = 'aarch64-linux-android' elif arch == 'mips': ANDROID_ABI = 'mips' - TOOLCHAIN_PREFIX = 'mipsel-linux-android-' + ANDROID_TRIPLE = 'mipsel-linux-android' + elif arch == 'mips64': + ANDROID_ABI = 'mips64' + ANDROID_TRIPLE = 'mips64el-linux-android' + elif arch == 'x86': + ANDROID_ABI = 'x86' + ANDROID_TRIPLE = 'i686-linux-android' + elif arch == 'x86_64': + ANDROID_ABI = 'x86_64' + ANDROID_TRIPLE = 'x86_64-linux-android' else: - exit('Android architecture must be arm, armv7a, x86 or mips') + exit('Android architecture must be arm, armv7a, aarch64, mips, mips64, x86 or x86_64') + + TOOLCHAIN_PREFIX = ANDROID_TRIPLE + '-' elif target == 'linux': if arch is not None: @@ -396,13 +457,13 @@ def CrossCompiling(): return GetTarget() != GetHost() def GetCC(): - if TARGET == 'darwin': + if TARGET in ('darwin', 'freebsd', 'android'): return os.environ.get('CC', TOOLCHAIN_PREFIX + 'clang') else: return os.environ.get('CC', TOOLCHAIN_PREFIX + 'gcc') def GetCXX(): - if TARGET == 'darwin': + if TARGET in ('darwin', 'freebsd', 'android'): return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'clang++') else: return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'g++') @@ -710,6 +771,34 @@ def CxxGetIncludes(path): CXXINCLUDECACHE[path] = [date, include] return include +JAVAIMPORTCACHE = {} + +global JavaImportRegex +JavaImportRegex = re.compile('[ \t\r\n;]import[ \t]+([a-zA-Z][^;]+)[ \t\r\n]*;') + +def JavaGetImports(path): + date = GetTimestamp(path) + if path in JAVAIMPORTCACHE: + cached = JAVAIMPORTCACHE[path] + if cached[0] == date: + return cached[1] + try: + source = open(path, 'r').read() + except: + exit("Cannot open source file \"" + path + "\" for reading.") + + imports = [] + try: + for match in JavaImportRegex.finditer(source, 0): + impname = match.group(1) + imports.append(impname.strip()) + except: + print("Failed to determine dependencies of \"" + path +"\".") + raise + + JAVAIMPORTCACHE[path] = [date, imports] + return imports + ######################################################################## ## ## SaveDependencyCache / LoadDependencyCache @@ -808,6 +897,13 @@ def CxxFindHeader(srcfile, incfile, ipath): if GetTimestamp(full) > 0: return full return 0 +def JavaFindClasses(impspec, clspath): + path = clspath + '/' + impspec.replace('.', '/') + '.class' + if '*' in path: + return glob.glob(path) + else: + return [path] + ######################################################################## ## ## CxxCalcDependencies(srcfile, ipath, ignore) @@ -840,6 +936,22 @@ def CxxCalcDependencies(srcfile, ipath, ignore): CxxDependencyCache[srcfile] = result return result +global JavaDependencyCache +JavaDependencyCache = {} + +def JavaCalcDependencies(srcfile, clspath): + if srcfile in JavaDependencyCache: + return JavaDependencyCache[srcfile] + + deps = set((srcfile,)) + JavaDependencyCache[srcfile] = deps + + imports = JavaGetImports(srcfile) + for impspec in imports: + for cls in JavaFindClasses(impspec, clspath): + deps.add(cls) + return deps + ######################################################################## ## ## Registry Key Handling @@ -918,6 +1030,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 @@ -1089,12 +1212,7 @@ def MakeBuildTree(): MakeDirectory(OUTPUTDIR + "/Frameworks") elif GetTarget() == 'android': - MakeDirectory(OUTPUTDIR + "/libs") - MakeDirectory(OUTPUTDIR + "/libs/" + ANDROID_ABI) - MakeDirectory(OUTPUTDIR + "/src") - MakeDirectory(OUTPUTDIR + "/src/org") - MakeDirectory(OUTPUTDIR + "/src/org/panda3d") - MakeDirectory(OUTPUTDIR + "/src/org/panda3d/android") + MakeDirectory(OUTPUTDIR + "/classes") ######################################################################## # @@ -1144,7 +1262,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/" @@ -1457,7 +1575,14 @@ def LocateLibrary(lib, lpath=[], prefer_static=False): return None def SystemLibraryExists(lib): - return LocateLibrary(lib, SYS_LIB_DIRS) is not None + result = LocateLibrary(lib, SYS_LIB_DIRS) + if result is not None: + return True + + if GetHost() == "android" and GetTarget() == "android": + return ('lib%s.so' % lib) in ANDROID_SYS_LIBS + + return False def ChooseLib(libs, thirdparty=None): """ Chooses a library from the parameters, in order of preference. Returns the first if none of them were found. """ @@ -2039,21 +2164,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"] @@ -2065,14 +2233,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": @@ -2262,9 +2433,22 @@ def SdkLocateAndroid(): """This actually locates the Android NDK, not the Android SDK. NDK_ROOT must be set to its root directory.""" + global TOOLCHAIN_PREFIX + if GetTarget() != 'android': return + # Allow ANDROID_API/ANDROID_ABI to be used in makepanda.py. + api = ANDROID_API + SDK["ANDROID_API"] = api + + abi = ANDROID_ABI + SDK["ANDROID_ABI"] = abi + SDK["ANDROID_TRIPLE"] = ANDROID_TRIPLE + + if GetHost() == 'android': + return + # Determine the NDK installation directory. if 'NDK_ROOT' not in os.environ: exit('NDK_ROOT must be set when compiling for Android!') @@ -2276,34 +2460,58 @@ def SdkLocateAndroid(): SDK["ANDROID_NDK"] = ndk_root # Determine the toolchain location. - gcc_ver = '4.8' - arch = GetTargetArch() - if arch == 'armv7a' or arch == 'arm': - arch = 'arm' - toolchain = 'arm-linux-androideabi-' + gcc_ver - elif arch == 'x86': - toolchain = 'x86-' + gcc_ver - elif arch == 'mips': - toolchain = 'mipsel-linux-android-' + gcc_ver - SDK["ANDROID_TOOLCHAIN"] = os.path.join(ndk_root, 'toolchains', toolchain) + prebuilt_dir = os.path.join(ndk_root, 'toolchains', 'llvm', 'prebuilt') + if not os.path.isdir(prebuilt_dir): + exit('Not found: %s' % (prebuilt_dir)) - # Allow ANDROID_ABI to be used in makepanda.py. - abi = ANDROID_ABI - SDK["ANDROID_ABI"] = abi + host_tag = GetHost() + '-x86' + if host_64: + host_tag += '_64' + elif host_tag == 'windows-x86': + host_tag = 'windows' + + prebuilt_dir = os.path.join(prebuilt_dir, host_tag) + if host_tag == 'windows-x86_64' and not os.path.isdir(prebuilt_dir): + # Try the 32-bits toolchain instead. + host_tag = 'windows' + prebuilt_dir = os.path.join(prebuilt_dir, host_tag) + + SDK["ANDROID_TOOLCHAIN"] = prebuilt_dir + + # And locate the GCC toolchain, which is needed for some tools (eg. as/ld) + arch = GetTargetArch() + for opt in (TOOLCHAIN_PREFIX + '4.9', arch + '-4.9', TOOLCHAIN_PREFIX + '4.8', arch + '-4.8'): + if os.path.isdir(os.path.join(ndk_root, 'toolchains', opt)): + SDK["ANDROID_GCC_TOOLCHAIN"] = os.path.join(ndk_root, 'toolchains', opt, 'prebuilt', host_tag) + break + + # The prebuilt binaries have no toolchain prefix. + TOOLCHAIN_PREFIX = '' # Determine the sysroot directory. - SDK["SYSROOT"] = os.path.join(ndk_root, 'platforms', 'android-9', 'arch-%s' % (arch)) + if arch == 'armv7a': + arch_dir = 'arch-arm' + elif arch == 'aarch64': + arch_dir = 'arch-arm64' + else: + arch_dir = 'arch-' + arch + SDK["SYSROOT"] = os.path.join(ndk_root, 'platforms', 'android-%s' % (api), arch_dir).replace('\\', '/') #IncDirectory("ALWAYS", os.path.join(SDK["SYSROOT"], 'usr', 'include')) - stdlibc = os.path.join(ndk_root, 'sources', 'cxx-stl', 'gnu-libstdc++', gcc_ver) - SDK["ANDROID_STL"] = stdlibc + # Starting with NDK r16, libc++ is the recommended STL to use. + stdlibc = os.path.join(ndk_root, 'sources', 'cxx-stl', 'llvm-libc++') + IncDirectory("ALWAYS", os.path.join(stdlibc, 'include').replace('\\', '/')) + LibDirectory("ALWAYS", os.path.join(stdlibc, 'libs', abi).replace('\\', '/')) - #IncDirectory("ALWAYS", os.path.join(stdlibc, 'include')) - #IncDirectory("ALWAYS", os.path.join(stdlibc, 'libs', abi, 'include')) + stl_lib = os.path.join(stdlibc, 'libs', abi, 'libc++_shared.so') + LibName("ALWAYS", stl_lib.replace('\\', '/')) + CopyFile(os.path.join(GetOutputDir(), 'lib', 'libc++_shared.so'), stl_lib) - stl_lib = os.path.join(stdlibc, 'libs', abi, 'libgnustl_shared.so') - LibName("ALWAYS", stl_lib) - CopyFile(os.path.join(GetOutputDir(), 'libs', abi, 'libgnustl_shared.so'), stl_lib) + # The Android support library polyfills C++ features not available in the + # STL that ships with Android. + support = os.path.join(ndk_root, 'sources', 'android', 'support', 'include') + IncDirectory("ALWAYS", support.replace('\\', '/')) + LibName("ALWAYS", "-landroid_support") ######################################################################## ## @@ -2380,38 +2588,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 + "\\" @@ -2421,6 +2655,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") @@ -2443,7 +2687,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,13 +2760,23 @@ def SetupBuildEnvironment(compiler): print("Using compiler: %s" % compiler) print("Host OS: %s" % GetHost()) print("Host arch: %s" % GetHostArch()) + + target = GetTarget() + if target != 'android': print("Target OS: %s" % GetTarget()) + else: + print("Target OS: %s (API level %d)" % (GetTarget(), ANDROID_API)) print("Target arch: %s" % GetTargetArch()) # Set to English so we can safely parse the result of gcc commands. # 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" + + # In the case of Android, we have to put the toolchain on the PATH in order to use it. + if GetTarget() == 'android' and GetHost() != 'android': + AddToPathEnv("PATH", os.path.join(SDK["ANDROID_TOOLCHAIN"], "bin")) if compiler == "MSVC": # Add the visual studio tools to PATH et al. @@ -2558,11 +2812,15 @@ def SetupBuildEnvironment(compiler): continue line = line[12:].strip() - for libdir in line.split(':'): - libdir = os.path.normpath(libdir) + libdirs = line.split(':') + while libdirs: + libdir = os.path.normpath(libdirs.pop(0)) if os.path.isdir(libdir): if libdir not in SYS_LIB_DIRS: SYS_LIB_DIRS.append(libdir) + elif len(libdir) == 1: + # Oops, is this a drive letter? Prepend it to the next. + libdirs[0] = libdir + ':' + libdirs[0] elif GetVerbose(): print("Ignoring non-existent library directory %s" % (libdir)) @@ -2608,6 +2866,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:") @@ -2618,33 +2878,6 @@ def SetupBuildEnvironment(compiler): for dir in SYS_INC_DIRS: print(" " + dir) - # In the case of Android, we have to put the toolchain on the PATH in order to use it. - if GetTarget() == 'android': - # Locate the directory where the toolchain binaries reside. - prebuilt_dir = os.path.join(SDK['ANDROID_TOOLCHAIN'], 'prebuilt') - if not os.path.isdir(prebuilt_dir): - exit('Not found: %s' % (prebuilt_dir)) - - host_tag = GetHost() + '-x86' - if host_64: - host_tag += '_64' - elif host_tag == 'windows-x86': - host_tag = 'windows' - - prebuilt_dir = os.path.join(prebuilt_dir, host_tag) - if host_64 and not os.path.isdir(prebuilt_dir): - # Try the 32-bits toolchain instead. - prebuilt_dir = os.path.join(prebuilt_dir, host_tag) - - if not os.path.isdir(prebuilt_dir): - if host_64: - exit('Not found: %s or %s' % (prebuilt_dir, host_tag)) - else: - exit('Not found: %s' % (prebuilt_dir)) - - # Then, add it to the PATH. - AddToPathEnv("PATH", os.path.join(prebuilt_dir, 'bin')) - # If we're cross-compiling, no point in putting our output dirs on the path. if CrossCompiling(): return @@ -2734,14 +2967,6 @@ def CopyAllHeaders(dir, skip=[]): WriteBinaryFile(dstfile, ReadBinaryFile(srcfile)) JustBuilt([dstfile], [srcfile]) -def CopyAllJavaSources(dir, skip=[]): - for filename in GetDirectoryContents(dir, ["*.java"], skip): - srcfile = dir + "/" + filename - dstfile = OUTPUTDIR + "/src/org/panda3d/android/" + filename - if (NeedsBuild([dstfile], [srcfile])): - WriteBinaryFile(dstfile, ReadBinaryFile(srcfile)) - JustBuilt([dstfile], [srcfile]) - def CopyTree(dstdir, srcdir, omitVCS=True): if os.path.isdir(dstdir): source_entries = os.listdir(srcdir) @@ -2835,6 +3060,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") @@ -2973,6 +3214,7 @@ def CalcLocation(fn, ipath): if fn.startswith("panda3d/") and fn.endswith(".py"): return OUTPUTDIR + "/" + fn + if (fn.endswith(".class")):return OUTPUTDIR+"/classes/"+fn if (fn.count("/")): return fn dllext = "" target = GetTarget() @@ -2988,6 +3230,7 @@ def CalcLocation(fn, ipath): if (fn.endswith(".lxx")): return CxxFindSource(fn, ipath) if (fn.endswith(".pdef")):return CxxFindSource(fn, ipath) if (fn.endswith(".xml")): return CxxFindSource(fn, ipath) + if (fn.endswith(".java")):return CxxFindSource(fn, ipath) if (fn.endswith(".egg")): return OUTPUTDIR+"/models/"+fn if (fn.endswith(".egg.pz")):return OUTPUTDIR+"/models/"+fn if (fn.endswith(".pyd")): return OUTPUTDIR+"/panda3d/"+fn[:-4]+GetExtensionSuffix() @@ -3023,15 +3266,6 @@ def CalcLocation(fn, ipath): if (fn.endswith(".rsrc")): return OUTPUTDIR+"/tmp/"+fn if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn if (fn.endswith(".app")): return OUTPUTDIR+"/bin/"+fn - elif (target == 'android'): - # On Android, we build the libraries into built/tmp, then copy them. - if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o" - if (fn.endswith(".dll")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".so" - if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn - if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".so" - if (fn.endswith(".exe")): return OUTPUTDIR+"/tmp/lib"+fn[:-4]+".so" - if (fn.endswith(".lib")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a" - if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a" else: if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o" if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".so" @@ -3145,6 +3379,20 @@ def TargetAdd(target, dummy=0, opts=[], input=[], dep=[], ipath=None, winrc=None if (SUFFIX_INC.count(suffix)): for d in CxxCalcDependencies(fullinput, ipath, []): t.deps[d] = 1 + elif suffix == '.java': + for d in JavaCalcDependencies(fullinput, OUTPUTDIR + "/classes"): + 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. @@ -3152,6 +3400,13 @@ def TargetAdd(target, dummy=0, opts=[], input=[], dep=[], ipath=None, winrc=None 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 56fb43133e..f727f10e73 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 @@ -356,7 +343,6 @@ class WheelFile(object): # Otherwise, just copy it over. temp.write(open(source_path, 'rb').read()) - temp.write(open(source_path, 'rb').read()) os.fchmod(temp.fileno(), os.fstat(temp.fileno()).st_mode | 0o111) temp.close() @@ -401,7 +387,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 +403,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,6 +537,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 += '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/panda-install.bmp b/makepanda/panda-install.bmp old mode 100755 new mode 100644 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/environment.egg b/models/environment.egg old mode 100755 new mode 100644 diff --git a/models/panda-model.egg b/models/panda-model.egg old mode 100755 new mode 100644 index df9ca085a0..5a3b6ec7d1 --- 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/models/panda-walk4.egg b/models/panda-walk4.egg old mode 100755 new mode 100644 diff --git a/models/plugin_images/installer.bmp b/models/plugin_images/installer.bmp old mode 100755 new mode 100644 diff --git a/panda/src/android/NativeOStream.java b/panda/src/android/NativeOStream.java new file mode 100644 index 0000000000..ddd76e485a --- /dev/null +++ b/panda/src/android/NativeOStream.java @@ -0,0 +1,52 @@ +/** + * 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 NativeOStream.java + * @author rdb + * @date 2018-02-10 + */ + +package org.panda3d.android; + +import java.io.OutputStream; + +/** + * An implementation of OutputStream that puts its data into a C++ ostream + * pointer, passed as long. + */ +public class NativeOStream extends OutputStream { + private long streamPtr = 0; + + public NativeOStream(long ptr) { + streamPtr = ptr; + } + + @Override + public void flush() { + nativeFlush(streamPtr); + } + + @Override + public void write(int b) { + nativePut(streamPtr, b); + } + + @Override + public void write(byte[] buffer) { + nativeWrite(streamPtr, buffer, 0, buffer.length); + } + + @Override + public void write(byte[] buffer, int offset, int length) { + nativeWrite(streamPtr, buffer, offset, length); + } + + private static native void nativeFlush(long ptr); + private static native void nativePut(long ptr, int b); + private static native void nativeWrite(long ptr, byte[] buffer, int offset, int length); +} diff --git a/panda/src/android/PandaActivity.java b/panda/src/android/PandaActivity.java index feba4baa4e..a4413a2816 100644 --- a/panda/src/android/PandaActivity.java +++ b/panda/src/android/PandaActivity.java @@ -14,15 +14,35 @@ package org.panda3d.android; import android.app.NativeActivity; +import android.content.Intent; +import android.net.Uri; +import android.widget.Toast; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import org.panda3d.android.NativeIStream; +import org.panda3d.android.NativeOStream; /** * The entry point for a Panda-based activity. Loads the Panda libraries and * also provides some utility functions. */ public class PandaActivity extends NativeActivity { + private static final Bitmap.Config sConfigs[] = { + null, + Bitmap.Config.ALPHA_8, + null, + Bitmap.Config.RGB_565, + Bitmap.Config.ARGB_4444, + Bitmap.Config.ARGB_8888, + null, //Bitmap.Config.RGBA_F16, + null, //Bitmap.Config.HARDWARE, + }; + private static final Bitmap.CompressFormat sFormats[] = { + Bitmap.CompressFormat.JPEG, + Bitmap.CompressFormat.PNG, + Bitmap.CompressFormat.WEBP, + }; + protected static BitmapFactory.Options readBitmapSize(long istreamPtr) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; @@ -41,14 +61,59 @@ public class PandaActivity extends NativeActivity { return BitmapFactory.decodeStream(stream, null, options); } + protected static Bitmap createBitmap(int width, int height, int config, boolean hasAlpha) { + return Bitmap.createBitmap(width, height, sConfigs[config]); + } + + protected static boolean compressBitmap(Bitmap bitmap, int format, int quality, long ostreamPtr) { + NativeOStream stream = new NativeOStream(ostreamPtr); + return bitmap.compress(sFormats[format], quality, stream); + } + + protected static String getCurrentThreadName() { + return Thread.currentThread().getName(); + } + + public String getIntentDataPath() { + Intent intent = getIntent(); + Uri data = intent.getData(); + if (data == null) { + return null; + } + String path = data.getPath(); + if (path.startsWith("//")) { + path = path.substring(1); + } + return path; + } + + public String getIntentOutputPath() { + Intent intent = getIntent(); + return intent.getStringExtra("org.panda3d.OUTPUT_PATH"); + } + + public String getCacheDirString() { + return getCacheDir().toString(); + } + + public void showToast(final String text, final int duration) { + final PandaActivity activity = this; + runOnUiThread(new Runnable() { + public void run() { + Toast toast = Toast.makeText(activity, text, duration); + toast.show(); + } + }); + } + static { - System.loadLibrary("gnustl_shared"); - System.loadLibrary("p3dtool"); - System.loadLibrary("p3dtoolconfig"); - System.loadLibrary("pandaexpress"); - System.loadLibrary("panda"); - System.loadLibrary("p3android"); - System.loadLibrary("p3framework"); + //System.loadLibrary("gnustl_shared"); + //System.loadLibrary("p3dtool"); + //System.loadLibrary("p3dtoolconfig"); + //System.loadLibrary("pandaexpress"); + //System.loadLibrary("panda"); + //System.loadLibrary("p3android"); + //System.loadLibrary("p3framework"); System.loadLibrary("pandaegg"); System.loadLibrary("pandagles"); } diff --git a/panda/src/android/android_main.cxx b/panda/src/android/android_main.cxx index ebc777ffa3..7d4e75e4b2 100644 --- a/panda/src/android/android_main.cxx +++ b/panda/src/android/android_main.cxx @@ -16,6 +16,7 @@ #include "virtualFileMountAndroidAsset.h" #include "virtualFileSystem.h" #include "filename.h" +#include "thread.h" #include "config_display.h" // #define OPENGLES_1 #include "config_androiddisplay.h" @@ -24,26 +25,49 @@ // struct android_app* panda_android_app = NULL; -extern int main(int argc, char **argv); +extern int main(int argc, const char **argv); /** * This function is called by native_app_glue to initialize the program. It * simply stores the android_app object and calls main() normally. + * + * Note that this does not run in the main thread, but in a thread created + * specifically for this activity by android_native_app_glue. */ void android_main(struct android_app* app) { panda_android_app = app; - // Attach the current thread to the JVM. + // Attach the app thread to the Java VM. JNIEnv *env; ANativeActivity* activity = app->activity; - int status = activity->vm->AttachCurrentThread(&env, NULL); - if (status < 0 || env == NULL) { + int status = activity->vm->AttachCurrentThread(&env, nullptr); + if (status < 0 || env == nullptr) { android_cat.error() << "Failed to attach thread to JVM!\n"; return; } - // Fetch the data directory. jclass activity_class = env->GetObjectClass(activity->clazz); + + // Get the current Java thread name. This just helps with debugging. + jmethodID methodID = env->GetStaticMethodID(activity_class, "getCurrentThreadName", "()Ljava/lang/String;"); + jstring jthread_name = (jstring) env->CallStaticObjectMethod(activity_class, methodID); + + string thread_name; + if (jthread_name != nullptr) { + const char *c_str = env->GetStringUTFChars(jthread_name, nullptr); + thread_name.assign(c_str); + env->ReleaseStringUTFChars(jthread_name, c_str); + } + + // Before we make any Panda calls, we must make the thread known to Panda. + // This will also cause the JNIEnv pointer to be stored on the thread. + // Note that we must keep a reference to this thread around. + PT(Thread) current_thread = Thread::bind_thread(thread_name, "android_app"); + + android_cat.info() + << "New native activity started on " << *current_thread << "\n"; + + // Fetch the data directory. jmethodID get_appinfo = env->GetMethodID(activity_class, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;"); jobject appinfo = env->CallObjectMethod(activity->clazz, get_appinfo); @@ -52,48 +76,195 @@ void android_main(struct android_app* app) { // Fetch the path to the data directory. jfieldID datadir_field = env->GetFieldID(appinfo_class, "dataDir", "Ljava/lang/String;"); jstring datadir = (jstring) env->GetObjectField(appinfo, datadir_field); - const char *data_path = env->GetStringUTFChars(datadir, NULL); + const char *data_path = env->GetStringUTFChars(datadir, nullptr); - Filename::_internal_data_dir = data_path; - android_cat.info() << "Path to data: " << data_path << "\n"; + if (data_path != nullptr) { + Filename::_internal_data_dir = data_path; + android_cat.info() << "Path to data: " << data_path << "\n"; - env->ReleaseStringUTFChars(datadir, data_path); + env->ReleaseStringUTFChars(datadir, data_path); + } // Fetch the path to the library directory. - jfieldID libdir_field = env->GetFieldID(appinfo_class, "nativeLibraryDir", "Ljava/lang/String;"); - jstring libdir = (jstring) env->GetObjectField(appinfo, libdir_field); - const char *lib_path = env->GetStringUTFChars(libdir, NULL); + if (ExecutionEnvironment::get_dtool_name().empty()) { + jfieldID libdir_field = env->GetFieldID(appinfo_class, "nativeLibraryDir", "Ljava/lang/String;"); + jstring libdir = (jstring) env->GetObjectField(appinfo, libdir_field); + const char *lib_path = env->GetStringUTFChars(libdir, nullptr); - string dtool_name = string(lib_path) + "/libp3dtool.so"; - ExecutionEnvironment::set_dtool_name(dtool_name); - android_cat.info() << "Path to dtool: " << dtool_name << "\n"; + if (lib_path != nullptr) { + string dtool_name = string(lib_path) + "/libp3dtool.so"; + ExecutionEnvironment::set_dtool_name(dtool_name); + android_cat.info() << "Path to dtool: " << dtool_name << "\n"; - env->ReleaseStringUTFChars(libdir, lib_path); + env->ReleaseStringUTFChars(libdir, lib_path); + } + } + + // Get the cache directory. Set the model-path to this location. + methodID = env->GetMethodID(activity_class, "getCacheDirString", "()Ljava/lang/String;"); + jstring jcache_dir = (jstring) env->CallObjectMethod(activity->clazz, methodID); + + if (jcache_dir != nullptr) { + const char *cache_dir; + cache_dir = env->GetStringUTFChars(jcache_dir, nullptr); + android_cat.info() << "Path to cache: " << cache_dir << "\n"; + + ConfigVariableFilename model_cache_dir("model-cache-dir", Filename()); + model_cache_dir.set_value(cache_dir); + env->ReleaseStringUTFChars(jcache_dir, cache_dir); + } // Get the path to the APK. - jmethodID methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;"); + methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;"); jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID); const char* apk_path; - apk_path = env->GetStringUTFChars(code_path, NULL); + apk_path = env->GetStringUTFChars(code_path, nullptr); + + // We're going to set this as binary name, which is better than the + // default (which refers to the zygote). Or should we set it to the + // native library? How do we get the path to that? android_cat.info() << "Path to APK: " << apk_path << "\n"; + ExecutionEnvironment::set_binary_name(apk_path); // Mount the assets directory. + Filename apk_fn(apk_path); PT(VirtualFileMountAndroidAsset) asset_mount; - asset_mount = new VirtualFileMountAndroidAsset(app->activity->assetManager, apk_path); + asset_mount = new VirtualFileMountAndroidAsset(app->activity->assetManager, apk_fn); VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - vfs->mount(asset_mount, "/android_asset", 0); + + //Filename asset_dir(apk_fn.get_dirname(), "assets"); + Filename asset_dir("/android_asset"); + vfs->mount(asset_mount, asset_dir, 0); // Release the apk_path. env->ReleaseStringUTFChars(code_path, apk_path); // Now add the asset directory to the model-path. - get_model_path().append_directory("/android_asset"); + //TODO: prevent it from adding the directory multiple times. + get_model_path().append_directory(asset_dir); - // Create bogus argc and argv, then call our main function. - char *argv[] = {NULL}; - int argc = 0; - main(argc, argv); + // Now load the configuration files. + vector pages; + ConfigPageManager *cp_mgr; + AAssetDir *etc = AAssetManager_openDir(app->activity->assetManager, "etc"); + if (etc != nullptr) { + cp_mgr = ConfigPageManager::get_global_ptr(); + const char *filename = AAssetDir_getNextFileName(etc); + while (filename != nullptr) { + // Does it match any of the configured prc patterns? + for (size_t i = 0; i < cp_mgr->get_num_prc_patterns(); ++i) { + GlobPattern pattern = cp_mgr->get_prc_pattern(i); + if (pattern.matches(filename)) { + Filename prc_fn("etc", filename); + istream *in = asset_mount->open_read_file(prc_fn); + if (in != nullptr) { + ConfigPage *page = cp_mgr->make_explicit_page(Filename("/android_asset", prc_fn)); + page->read_prc(*in); + pages.push_back(page); + } + break; + } + } + filename = AAssetDir_getNextFileName(etc); + } + AAssetDir_close(etc); + } + + // Also read the intent filename. + methodID = env->GetMethodID(activity_class, "getIntentDataPath", "()Ljava/lang/String;"); + jstring filename = (jstring) env->CallObjectMethod(activity->clazz, methodID); + const char *filename_str = nullptr; + if (filename != nullptr) { + filename_str = env->GetStringUTFChars(filename, nullptr); + android_cat.info() << "Got intent filename: " << filename_str << "\n"; + + Filename fn(filename_str); + if (!fn.exists()) { + // Show a toast with the failure message. + android_show_toast(activity, string("Unable to access ") + filename_str, 1); + } + } + + // Were we given an optional location to write the stdout/stderr streams? + methodID = env->GetMethodID(activity_class, "getIntentOutputPath", "()Ljava/lang/String;"); + jstring joutput_path = (jstring) env->CallObjectMethod(activity->clazz, methodID); + if (joutput_path != nullptr) { + const char *output_path = env->GetStringUTFChars(joutput_path, nullptr); + + if (output_path != nullptr && output_path[0] != 0) { + int fd = open(output_path, O_CREAT | O_TRUNC | O_WRONLY); + if (fd != -1) { + android_cat.info() + << "Writing standard output to file " << output_path << "\n"; + + dup2(fd, 1); + dup2(fd, 2); + } else { + android_cat.error() + << "Failed to open output path " << output_path << "\n"; + } + env->ReleaseStringUTFChars(joutput_path, output_path); + } + } + + // Create bogus argc and argv for calling the main function. + const char *argv[] = {"pview", nullptr, nullptr}; + int argc = 1; + + if (filename_str != nullptr) { + argv[1] = filename_str; + ++argc; + } + + while (!app->destroyRequested) { + // Call the main function. This will not return until the app is done. + android_cat.info() << "Calling main()\n"; + main(argc, argv); + + if (app->destroyRequested) { + // The app closed responding to a destroy request. + break; + } + + // Ask Android to clean up the activity. + android_cat.info() << "Exited from main(), finishing activity\n"; + ANativeActivity_finish(activity); + + // We still need to keep an event loop going until Android gives us leave + // to end the process. + int looper_id; + int events; + struct android_poll_source *source; + while ((looper_id = ALooper_pollAll(-1, nullptr, &events, (void**)&source)) >= 0) { + // Process this event, but intercept application command events. + if (looper_id == LOOPER_ID_MAIN) { + int8_t cmd = android_app_read_cmd(app); + android_app_pre_exec_cmd(app, cmd); + android_app_post_exec_cmd(app, cmd); + + // I don't think we can get a resume command after we call finish(), + // but let's handle it just in case. + if (cmd == APP_CMD_RESUME || + cmd == APP_CMD_DESTROY) { + break; + } + } else if (source != nullptr) { + source->process(app, source); + } + } + } + + android_cat.info() << "Destroy requested, exiting from android_main\n"; + + for (ConfigPage *page : pages) { + cp_mgr->delete_explicit_page(page); + } + vfs->unmount(asset_mount); + + if (filename_str != nullptr) { + env->ReleaseStringUTFChars(filename, filename_str); + } // Detach the thread before exiting. activity->vm->DetachCurrentThread(); diff --git a/panda/src/android/android_native_app_glue.c b/panda/src/android/android_native_app_glue.c new file mode 100644 index 0000000000..7eada08666 --- /dev/null +++ b/panda/src/android/android_native_app_glue.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include + +#include "android_native_app_glue.h" +#include + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__)) + +/* For debug builds, always enable the debug traces in this library */ +#ifndef NDEBUG +# define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__)) +#else +# define LOGV(...) ((void)0) +#endif + +static void free_saved_state(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->savedState != NULL) { + free(android_app->savedState); + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + pthread_mutex_unlock(&android_app->mutex); +} + +int8_t android_app_read_cmd(struct android_app* android_app) { + int8_t cmd; + if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) { + switch (cmd) { + case APP_CMD_SAVE_STATE: + free_saved_state(android_app); + break; + } + return cmd; + } else { + LOGE("No data on command pipe!"); + } + return -1; +} + +static void print_cur_config(struct android_app* android_app) { + char lang[2], country[2]; + AConfiguration_getLanguage(android_app->config, lang); + AConfiguration_getCountry(android_app->config, country); + + LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " + "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " + "modetype=%d modenight=%d", + AConfiguration_getMcc(android_app->config), + AConfiguration_getMnc(android_app->config), + lang[0], lang[1], country[0], country[1], + AConfiguration_getOrientation(android_app->config), + AConfiguration_getTouchscreen(android_app->config), + AConfiguration_getDensity(android_app->config), + AConfiguration_getKeyboard(android_app->config), + AConfiguration_getNavigation(android_app->config), + AConfiguration_getKeysHidden(android_app->config), + AConfiguration_getNavHidden(android_app->config), + AConfiguration_getSdkVersion(android_app->config), + AConfiguration_getScreenSize(android_app->config), + AConfiguration_getScreenLong(android_app->config), + AConfiguration_getUiModeType(android_app->config), + AConfiguration_getUiModeNight(android_app->config)); +} + +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_INPUT_CHANGED: + LOGV("APP_CMD_INPUT_CHANGED\n"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->inputQueue = android_app->pendingInputQueue; + if (android_app->inputQueue != NULL) { + LOGV("Attaching input queue to looper"); + AInputQueue_attachLooper(android_app->inputQueue, + android_app->looper, LOOPER_ID_INPUT, NULL, + &android_app->inputPollSource); + } + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_INIT_WINDOW: + LOGV("APP_CMD_INIT_WINDOW\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = android_app->pendingWindow; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_TERM_WINDOW: + LOGV("APP_CMD_TERM_WINDOW\n"); + pthread_cond_broadcast(&android_app->cond); + break; + + case APP_CMD_RESUME: + case APP_CMD_START: + case APP_CMD_PAUSE: + case APP_CMD_STOP: + LOGV("activityState=%d\n", cmd); + pthread_mutex_lock(&android_app->mutex); + android_app->activityState = cmd; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_CONFIG_CHANGED: + LOGV("APP_CMD_CONFIG_CHANGED\n"); + AConfiguration_fromAssetManager(android_app->config, + android_app->activity->assetManager); + print_cur_config(android_app); + break; + + case APP_CMD_DESTROY: + LOGV("APP_CMD_DESTROY\n"); + android_app->destroyRequested = 1; + break; + } +} + +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_TERM_WINDOW: + LOGV("APP_CMD_TERM_WINDOW\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = NULL; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_SAVE_STATE: + LOGV("APP_CMD_SAVE_STATE\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_RESUME: + free_saved_state(android_app); + break; + } +} + +void app_dummy() { + +} + +static void android_app_destroy(struct android_app* android_app) { + LOGV("android_app_destroy!"); + free_saved_state(android_app); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + AConfiguration_delete(android_app->config); + android_app->destroyed = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + // Can't touch android_app object after this. +} + +static void process_input(struct android_app* app, struct android_poll_source* source) { + AInputEvent* event = NULL; + while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { + LOGV("New input event: type=%d\n", AInputEvent_getType(event)); + if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { + continue; + } + int32_t handled = 0; + if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); + AInputQueue_finishEvent(app->inputQueue, event, handled); + } +} + +static void process_cmd(struct android_app* app, struct android_poll_source* source) { + int8_t cmd = android_app_read_cmd(app); + android_app_pre_exec_cmd(app, cmd); + if (app->onAppCmd != NULL) app->onAppCmd(app, cmd); + android_app_post_exec_cmd(app, cmd); +} + +static void* android_app_entry(void* param) { + struct android_app* android_app = (struct android_app*)param; + + android_app->config = AConfiguration_new(); + AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager); + + print_cur_config(android_app); + + android_app->cmdPollSource.id = LOOPER_ID_MAIN; + android_app->cmdPollSource.app = android_app; + android_app->cmdPollSource.process = process_cmd; + android_app->inputPollSource.id = LOOPER_ID_INPUT; + android_app->inputPollSource.app = android_app; + android_app->inputPollSource.process = process_input; + + ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL, + &android_app->cmdPollSource); + android_app->looper = looper; + + pthread_mutex_lock(&android_app->mutex); + android_app->running = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + + android_main(android_app); + + android_app_destroy(android_app); + return NULL; +} + +// -------------------------------------------------------------------- +// Native activity interaction (called from main thread) +// -------------------------------------------------------------------- + +static struct android_app* android_app_create(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app)); + memset(android_app, 0, sizeof(struct android_app)); + android_app->activity = activity; + + pthread_mutex_init(&android_app->mutex, NULL); + pthread_cond_init(&android_app->cond, NULL); + + if (savedState != NULL) { + android_app->savedState = malloc(savedStateSize); + android_app->savedStateSize = savedStateSize; + memcpy(android_app->savedState, savedState, savedStateSize); + } + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGE("could not create pipe: %s", strerror(errno)); + return NULL; + } + android_app->msgread = msgpipe[0]; + android_app->msgwrite = msgpipe[1]; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&android_app->thread, &attr, android_app_entry, android_app); + + // Wait for thread to start. + pthread_mutex_lock(&android_app->mutex); + while (!android_app->running) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + return android_app; +} + +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { + if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { + LOGE("Failure writing android_app cmd: %s\n", strerror(errno)); + } +} + +static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingInputQueue = inputQueue; + android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); + while (android_app->inputQueue != android_app->pendingInputQueue) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->pendingWindow != NULL) { + android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); + } + android_app->pendingWindow = window; + if (window != NULL) { + android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); + } + while (android_app->window != android_app->pendingWindow) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, cmd); + while (android_app->activityState != cmd) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_free(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, APP_CMD_DESTROY); + while (!android_app->destroyed) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + close(android_app->msgread); + close(android_app->msgwrite); + pthread_cond_destroy(&android_app->cond); + pthread_mutex_destroy(&android_app->mutex); + free(android_app); +} + +static void onDestroy(ANativeActivity* activity) { + LOGV("Destroy: %p\n", activity); + android_app_free((struct android_app*)activity->instance); +} + +static void onStart(ANativeActivity* activity) { + LOGV("Start: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START); +} + +static void onResume(ANativeActivity* activity) { + LOGV("Resume: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME); +} + +static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { + struct android_app* android_app = (struct android_app*)activity->instance; + void* savedState = NULL; + + LOGV("SaveInstanceState: %p\n", activity); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 0; + android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); + while (!android_app->stateSaved) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + + if (android_app->savedState != NULL) { + savedState = android_app->savedState; + *outLen = android_app->savedStateSize; + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + + pthread_mutex_unlock(&android_app->mutex); + + return savedState; +} + +static void onPause(ANativeActivity* activity) { + LOGV("Pause: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE); +} + +static void onStop(ANativeActivity* activity) { + LOGV("Stop: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP); +} + +static void onConfigurationChanged(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGV("ConfigurationChanged: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED); +} + +static void onLowMemory(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGV("LowMemory: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY); +} + +static void onWindowFocusChanged(ANativeActivity* activity, int focused) { + LOGV("WindowFocusChanged: %p -- %d\n", activity, focused); + android_app_write_cmd((struct android_app*)activity->instance, + focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); +} + +static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowCreated: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, window); +} + +static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, NULL); +} + +static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { + LOGV("InputQueueCreated: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, queue); +} + +static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { + LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, NULL); +} + +JNIEXPORT +void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, + size_t savedStateSize) { + LOGV("Creating: %p\n", activity); + activity->callbacks->onDestroy = onDestroy; + activity->callbacks->onStart = onStart; + activity->callbacks->onResume = onResume; + activity->callbacks->onSaveInstanceState = onSaveInstanceState; + activity->callbacks->onPause = onPause; + activity->callbacks->onStop = onStop; + activity->callbacks->onConfigurationChanged = onConfigurationChanged; + activity->callbacks->onLowMemory = onLowMemory; + activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; + activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; + activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; + activity->callbacks->onInputQueueCreated = onInputQueueCreated; + activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; + + activity->instance = android_app_create(activity, savedState, savedStateSize); +} diff --git a/panda/src/android/android_native_app_glue.h b/panda/src/android/android_native_app_glue.h new file mode 100644 index 0000000000..c99d6e12af --- /dev/null +++ b/panda/src/android/android_native_app_glue.h @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _ANDROID_NATIVE_APP_GLUE_H +#define _ANDROID_NATIVE_APP_GLUE_H + +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The native activity interface provided by + * is based on a set of application-provided callbacks that will be called + * by the Activity's main thread when certain events occur. + * + * This means that each one of this callbacks _should_ _not_ block, or they + * risk having the system force-close the application. This programming + * model is direct, lightweight, but constraining. + * + * The 'android_native_app_glue' static library is used to provide a different + * execution model where the application can implement its own main event + * loop in a different thread instead. Here's how it works: + * + * 1/ The application must provide a function named "android_main()" that + * will be called when the activity is created, in a new thread that is + * distinct from the activity's main thread. + * + * 2/ android_main() receives a pointer to a valid "android_app" structure + * that contains references to other important objects, e.g. the + * ANativeActivity obejct instance the application is running in. + * + * 3/ the "android_app" object holds an ALooper instance that already + * listens to two important things: + * + * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX + * declarations below. + * + * - input events coming from the AInputQueue attached to the activity. + * + * Each of these correspond to an ALooper identifier returned by + * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT, + * respectively. + * + * Your application can use the same ALooper to listen to additional + * file-descriptors. They can either be callback based, or with return + * identifiers starting with LOOPER_ID_USER. + * + * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, + * the returned data will point to an android_poll_source structure. You + * can call the process() function on it, and fill in android_app->onAppCmd + * and android_app->onInputEvent to be called for your own processing + * of the event. + * + * Alternatively, you can call the low-level functions to read and process + * the data directly... look at the process_cmd() and process_input() + * implementations in the glue to see how to do this. + * + * See the sample named "native-activity" that comes with the NDK with a + * full usage example. Also look at the JavaDoc of NativeActivity. + */ + +struct android_app; + +/** + * Data associated with an ALooper fd that will be returned as the "outData" + * when that source has data ready. + */ +struct android_poll_source { + // The identifier of this source. May be LOOPER_ID_MAIN or + // LOOPER_ID_INPUT. + int32_t id; + + // The android_app this ident is associated with. + struct android_app* app; + + // Function to call to perform the standard processing of data from + // this source. + void (*process)(struct android_app* app, struct android_poll_source* source); +}; + +/** + * This is the interface for the standard glue code of a threaded + * application. In this model, the application's code is running + * in its own thread separate from the main thread of the process. + * It is not required that this thread be associated with the Java + * VM, although it will need to be in order to make JNI calls any + * Java objects. + */ +struct android_app { + // The application can place a pointer to its own state object + // here if it likes. + void* userData; + + // Fill this in with the function to process main app commands (APP_CMD_*) + void (*onAppCmd)(struct android_app* app, int32_t cmd); + + // Fill this in with the function to process input events. At this point + // the event has already been pre-dispatched, and it will be finished upon + // return. Return 1 if you have handled the event, 0 for any default + // dispatching. + int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); + + // The ANativeActivity object instance that this app is running in. + ANativeActivity* activity; + + // The current configuration the app is running in. + AConfiguration* config; + + // This is the last instance's saved state, as provided at creation time. + // It is NULL if there was no state. You can use this as you need; the + // memory will remain around until you call android_app_exec_cmd() for + // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. + // These variables should only be changed when processing a APP_CMD_SAVE_STATE, + // at which point they will be initialized to NULL and you can malloc your + // state and place the information here. In that case the memory will be + // freed for you later. + void* savedState; + size_t savedStateSize; + + // The ALooper associated with the app's thread. + ALooper* looper; + + // When non-NULL, this is the input queue from which the app will + // receive user input events. + AInputQueue* inputQueue; + + // When non-NULL, this is the window surface that the app can draw in. + ANativeWindow* window; + + // Current content rectangle of the window; this is the area where the + // window's content should be placed to be seen by the user. + ARect contentRect; + + // Current state of the app's activity. May be either APP_CMD_START, + // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. + int activityState; + + // This is non-zero when the application's NativeActivity is being + // destroyed and waiting for the app thread to complete. + int destroyRequested; + + // ------------------------------------------------- + // Below are "private" implementation of the glue code. + + pthread_mutex_t mutex; + pthread_cond_t cond; + + int msgread; + int msgwrite; + + pthread_t thread; + + struct android_poll_source cmdPollSource; + struct android_poll_source inputPollSource; + + int running; + int stateSaved; + int destroyed; + int redrawNeeded; + AInputQueue* pendingInputQueue; + ANativeWindow* pendingWindow; + ARect pendingContentRect; +}; + +enum { + /** + * Looper data ID of commands coming from the app's main thread, which + * is returned as an identifier from ALooper_pollOnce(). The data for this + * identifier is a pointer to an android_poll_source structure. + * These can be retrieved and processed with android_app_read_cmd() + * and android_app_exec_cmd(). + */ + LOOPER_ID_MAIN = 1, + + /** + * Looper data ID of events coming from the AInputQueue of the + * application's window, which is returned as an identifier from + * ALooper_pollOnce(). The data for this identifier is a pointer to an + * android_poll_source structure. These can be read via the inputQueue + * object of android_app. + */ + LOOPER_ID_INPUT = 2, + + /** + * Start of user-defined ALooper identifiers. + */ + LOOPER_ID_USER = 3, +}; + +enum { + /** + * Command from main thread: the AInputQueue has changed. Upon processing + * this command, android_app->inputQueue will be updated to the new queue + * (or NULL). + */ + APP_CMD_INPUT_CHANGED, + + /** + * Command from main thread: a new ANativeWindow is ready for use. Upon + * receiving this command, android_app->window will contain the new window + * surface. + */ + APP_CMD_INIT_WINDOW, + + /** + * Command from main thread: the existing ANativeWindow needs to be + * terminated. Upon receiving this command, android_app->window still + * contains the existing window; after calling android_app_exec_cmd + * it will be set to NULL. + */ + APP_CMD_TERM_WINDOW, + + /** + * Command from main thread: the current ANativeWindow has been resized. + * Please redraw with its new size. + */ + APP_CMD_WINDOW_RESIZED, + + /** + * Command from main thread: the system needs that the current ANativeWindow + * be redrawn. You should redraw the window before handing this to + * android_app_exec_cmd() in order to avoid transient drawing glitches. + */ + APP_CMD_WINDOW_REDRAW_NEEDED, + + /** + * Command from main thread: the content area of the window has changed, + * such as from the soft input window being shown or hidden. You can + * find the new content rect in android_app::contentRect. + */ + APP_CMD_CONTENT_RECT_CHANGED, + + /** + * Command from main thread: the app's activity window has gained + * input focus. + */ + APP_CMD_GAINED_FOCUS, + + /** + * Command from main thread: the app's activity window has lost + * input focus. + */ + APP_CMD_LOST_FOCUS, + + /** + * Command from main thread: the current device configuration has changed. + */ + APP_CMD_CONFIG_CHANGED, + + /** + * Command from main thread: the system is running low on memory. + * Try to reduce your memory use. + */ + APP_CMD_LOW_MEMORY, + + /** + * Command from main thread: the app's activity has been started. + */ + APP_CMD_START, + + /** + * Command from main thread: the app's activity has been resumed. + */ + APP_CMD_RESUME, + + /** + * Command from main thread: the app should generate a new saved state + * for itself, to restore from later if needed. If you have saved state, + * allocate it with malloc and place it in android_app.savedState with + * the size in android_app.savedStateSize. The will be freed for you + * later. + */ + APP_CMD_SAVE_STATE, + + /** + * Command from main thread: the app's activity has been paused. + */ + APP_CMD_PAUSE, + + /** + * Command from main thread: the app's activity has been stopped. + */ + APP_CMD_STOP, + + /** + * Command from main thread: the app's activity is being destroyed, + * and waiting for the app thread to clean up and exit before proceeding. + */ + APP_CMD_DESTROY, +}; + +/** + * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next + * app command message. + */ +int8_t android_app_read_cmd(struct android_app* android_app); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * initial pre-processing of the given command. You can perform your own + * actions for the command after calling this function. + */ +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * final post-processing of the given command. You must have done your own + * actions for the command before calling this function. + */ +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Dummy function that used to be used to prevent the linker from stripping app + * glue code. No longer necessary, since __attribute__((visibility("default"))) + * does this for us. + */ +__attribute__(( + deprecated("Calls to app_dummy are no longer necessary. See " + "https://github.com/android-ndk/ndk/issues/381."))) void +app_dummy(); + +/** + * This is the function that application code must implement, representing + * the main entry to the app. + */ +extern void android_main(struct android_app* app); + +#ifdef __cplusplus +} +#endif + +#endif /* _ANDROID_NATIVE_APP_GLUE_H */ diff --git a/panda/src/android/config_android.cxx b/panda/src/android/config_android.cxx index bf9056c562..afbaf243ff 100644 --- a/panda/src/android/config_android.cxx +++ b/panda/src/android/config_android.cxx @@ -24,11 +24,24 @@ struct android_app *panda_android_app = NULL; jclass jni_PandaActivity; jmethodID jni_PandaActivity_readBitmapSize; jmethodID jni_PandaActivity_readBitmap; +jmethodID jni_PandaActivity_createBitmap; +jmethodID jni_PandaActivity_compressBitmap; +jmethodID jni_PandaActivity_showToast; jclass jni_BitmapFactory_Options; jfieldID jni_BitmapFactory_Options_outWidth; jfieldID jni_BitmapFactory_Options_outHeight; +#ifndef HAVE_JPEG +static PNMFileTypeAndroid file_type_jpeg(PNMFileTypeAndroid::CF_jpeg); +#endif +#ifndef HAVE_PNG +static PNMFileTypeAndroid file_type_png(PNMFileTypeAndroid::CF_png); +#endif +#if __ANDROID_API__ >= 14 +static PNMFileTypeAndroid file_type_webp(PNMFileTypeAndroid::CF_webp); +#endif + /** * Initializes the library. This must be called at least once before any of * the functions or classes in this library can be used. Normally, this is @@ -36,10 +49,6 @@ jfieldID jni_BitmapFactory_Options_outHeight; */ void init_libandroid() { - PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_global_ptr(); - PNMFileTypeAndroid::init_type(); - PNMFileTypeAndroid::register_with_read_factory(); - tr->register_type(new PNMFileTypeAndroid); } /** @@ -47,10 +56,11 @@ init_libandroid() { * references and the method IDs. */ jint JNI_OnLoad(JavaVM *jvm, void *reserved) { - init_libandroid(); + //init_libandroid(); - JNIEnv *env = get_jni_env(); - assert(env != NULL); + Thread *thread = Thread::get_current_thread(); + JNIEnv *env = thread->get_jni_env(); + nassertr(env != nullptr, -1); jni_PandaActivity = env->FindClass("org/panda3d/android/PandaActivity"); jni_PandaActivity = (jclass) env->NewGlobalRef(jni_PandaActivity); @@ -61,12 +71,40 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved) { jni_PandaActivity_readBitmap = env->GetStaticMethodID(jni_PandaActivity, "readBitmap", "(JI)Landroid/graphics/Bitmap;"); + jni_PandaActivity_createBitmap = env->GetStaticMethodID(jni_PandaActivity, + "createBitmap", "(IIIZ)Landroid/graphics/Bitmap;"); + + jni_PandaActivity_compressBitmap = env->GetStaticMethodID(jni_PandaActivity, + "compressBitmap", "(Landroid/graphics/Bitmap;IIJ)Z"); + + jni_PandaActivity_showToast = env->GetMethodID(jni_PandaActivity, + "showToast", "(Ljava/lang/String;I)V"); + jni_BitmapFactory_Options = env->FindClass("android/graphics/BitmapFactory$Options"); jni_BitmapFactory_Options = (jclass) env->NewGlobalRef(jni_BitmapFactory_Options); jni_BitmapFactory_Options_outWidth = env->GetFieldID(jni_BitmapFactory_Options, "outWidth", "I"); jni_BitmapFactory_Options_outHeight = env->GetFieldID(jni_BitmapFactory_Options, "outHeight", "I"); + nassertr(jni_PandaActivity_readBitmapSize, -1); + nassertr(jni_PandaActivity_readBitmap, -1); + nassertr(jni_PandaActivity_createBitmap, -1); + nassertr(jni_PandaActivity_compressBitmap, -1); + nassertr(jni_PandaActivity_showToast, -1); + + // We put this in JNI_OnLoad because it relies on Java classes, which + // are only available when launched from the Java VM. + PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_global_ptr(); +#ifndef HAVE_JPEG + tr->register_type(&file_type_jpeg); +#endif +#ifndef HAVE_PNG + tr->register_type(&file_type_png); +#endif +#if __ANDROID_API__ >= 14 + tr->register_type(&file_type_webp); +#endif + return JNI_VERSION_1_4; } @@ -75,8 +113,38 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved) { * references. */ void JNI_OnUnload(JavaVM *jvm, void *reserved) { - JNIEnv *env = get_jni_env(); + Thread *thread = Thread::get_current_thread(); + JNIEnv *env = thread->get_jni_env(); + nassertv(env != nullptr); env->DeleteGlobalRef(jni_PandaActivity); env->DeleteGlobalRef(jni_BitmapFactory_Options); + + // These will no longer work without JNI, so unregister them. + PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_global_ptr(); + if (tr != nullptr) { +#ifndef HAVE_JPEG + tr->unregister_type(&file_type_jpeg); +#endif +#ifndef HAVE_PNG + tr->unregister_type(&file_type_png); +#endif +#if __ANDROID_API__ >= 14 + tr->unregister_type(&file_type_webp); +#endif + } +} + +/** + * Shows a toast notification at the bottom of the activity. The duration + * should be 0 for short and 1 for long. + */ +void android_show_toast(ANativeActivity *activity, const string &message, int duration) { + Thread *thread = Thread::get_current_thread(); + JNIEnv *env = thread->get_jni_env(); + nassertv(env != nullptr); + + jstring jmsg = env->NewStringUTF(message.c_str()); + env->CallVoidMethod(activity->clazz, jni_PandaActivity_showToast, jmsg, (jint)duration); + env->DeleteLocalRef(jmsg); } diff --git a/panda/src/android/config_android.h b/panda/src/android/config_android.h index a67ac2423f..2224dbd065 100644 --- a/panda/src/android/config_android.h +++ b/panda/src/android/config_android.h @@ -20,6 +20,7 @@ #include "configVariableBool.h" #include "configVariableInt.h" +#include #include NotifyCategoryDecl(android, EXPORT_CLASS, EXPORT_TEMPL); @@ -30,9 +31,14 @@ extern EXPORT_CLASS struct android_app* panda_android_app; extern jclass jni_PandaActivity; extern jmethodID jni_PandaActivity_readBitmapHeader; extern jmethodID jni_PandaActivity_readBitmap; +extern jmethodID jni_PandaActivity_createBitmap; +extern jmethodID jni_PandaActivity_compressBitmap; +extern jmethodID jni_PandaActivity_showToast; extern jclass jni_BitmapFactory_Options; extern jfieldID jni_BitmapFactory_Options_outWidth; extern jfieldID jni_BitmapFactory_Options_outHeight; +EXPORT_CLASS void android_show_toast(ANativeActivity *activity, const string &message, int duration); + #endif diff --git a/panda/src/android/jni_NativeOStream.cxx b/panda/src/android/jni_NativeOStream.cxx new file mode 100644 index 0000000000..c2779d0174 --- /dev/null +++ b/panda/src/android/jni_NativeOStream.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 jni_NativeOStream.cxx + * @author rdb + * @date 2018-02-10 + */ + +#include + +#include + +#if __GNUC__ >= 4 +#define EXPORT_JNI extern "C" __attribute__((visibility("default"))) +#else +#define EXPORT_JNI extern "C" +#endif + +/** + * Flushes the stream. + */ +EXPORT_JNI void +Java_org_panda3d_android_NativeOStream_nativeFlush(JNIEnv *env, jclass clazz, jlong ptr) { + std::ostream *stream = (std::ostream *)ptr; + + stream->flush(); +} + +/** + * Writes a single character to the ostream. + */ +EXPORT_JNI void +Java_org_panda3d_android_NativeOStream_nativePut(JNIEnv *env, jclass clazz, jlong ptr, int b) { + std::ostream *stream = (std::ostream *)ptr; + + stream->put((char)(b & 0xff)); +} + +/** + * Writes an array of bytes to the ostream. + */ +EXPORT_JNI void +Java_org_panda3d_android_NativeOStream_nativeWrite(JNIEnv *env, jclass clazz, jlong ptr, jbyteArray byte_array, jint offset, jint length) { + std::ostream *stream = (std::ostream *)ptr; + + jbyte *buffer = (jbyte *)alloca(length); + env->GetByteArrayRegion(byte_array, offset, length, buffer); + stream->write((char *)buffer, length); +} diff --git a/panda/src/android/p3android_composite1.cxx b/panda/src/android/p3android_composite1.cxx index 14b48ab99f..ea14d2b311 100644 --- a/panda/src/android/p3android_composite1.cxx +++ b/panda/src/android/p3android_composite1.cxx @@ -1,4 +1,6 @@ #include "config_android.cxx" #include "jni_NativeIStream.cxx" +#include "jni_NativeOStream.cxx" #include "pnmFileTypeAndroid.cxx" -#include "pnmFileTypeAndroidReader.cxx" \ No newline at end of file +#include "pnmFileTypeAndroidReader.cxx" +#include "pnmFileTypeAndroidWriter.cxx" diff --git a/panda/src/android/pnmFileTypeAndroid.cxx b/panda/src/android/pnmFileTypeAndroid.cxx index fae5d147c4..aa25b2c777 100644 --- a/panda/src/android/pnmFileTypeAndroid.cxx +++ b/panda/src/android/pnmFileTypeAndroid.cxx @@ -17,21 +17,11 @@ #include "config_pnmimagetypes.h" -#include "pnmFileTypeRegistry.h" -#include "bamReader.h" - -static const char * const extensions_android[] = { - "jpg", "jpeg", "gif", "png",//"webp" (android 4.0+) -}; -static const int num_extensions_android = sizeof(extensions_android) / sizeof(const char *); - -TypeHandle PNMFileTypeAndroid::_type_handle; - /** * */ PNMFileTypeAndroid:: -PNMFileTypeAndroid() { +PNMFileTypeAndroid(CompressFormat format) : _format(format) { } /** @@ -48,7 +38,16 @@ get_name() const { */ int PNMFileTypeAndroid:: get_num_extensions() const { - return num_extensions_android; + switch (_format) { + case CF_jpeg: + return 3; + case CF_png: + return 1; + case CF_webp: + return 1; + default: + return 0; + } } /** @@ -57,8 +56,17 @@ get_num_extensions() const { */ string PNMFileTypeAndroid:: get_extension(int n) const { - nassertr(n >= 0 && n < num_extensions_android, string()); - return extensions_android[n]; + static const char *const jpeg_extensions[] = {"jpg", "jpeg", "jpe"}; + switch (_format) { + case CF_jpeg: + return jpeg_extensions[n]; + case CF_png: + return "png"; + case CF_webp: + return "webp"; + default: + return 0; + } } /** @@ -77,30 +85,17 @@ has_magic_number() const { */ PNMReader *PNMFileTypeAndroid:: make_reader(istream *file, bool owns_file, const string &magic_number) { - init_pnm(); return new Reader(this, file, owns_file, magic_number); } /** - * Registers the current object as something that can be read from a Bam file. + * Allocates and returns a new PNMWriter suitable for reading from this file + * type, if possible. If writing files of this type is not supported, returns + * NULL. */ -void PNMFileTypeAndroid:: -register_with_read_factory() { - BamReader::get_factory()-> - register_factory(get_class_type(), make_PNMFileTypeAndroid); -} - -/** - * This method is called by the BamReader when an object of this type is - * encountered in a Bam file; it should allocate and return a new object with - * all the data read. - * - * In the case of the PNMFileType objects, since these objects are all shared, - * we just pull the object from the registry. - */ -TypedWritable *PNMFileTypeAndroid:: -make_PNMFileTypeAndroid(const FactoryParams ¶ms) { - return PNMFileTypeRegistry::get_global_ptr()->get_type_by_handle(get_class_type()); +PNMWriter *PNMFileTypeAndroid:: +make_writer(ostream *file, bool owns_file) { + return new Writer(this, file, owns_file, _format); } #endif // ANDROID diff --git a/panda/src/android/pnmFileTypeAndroid.h b/panda/src/android/pnmFileTypeAndroid.h index cc49fd8f4f..f27cc4f23d 100644 --- a/panda/src/android/pnmFileTypeAndroid.h +++ b/panda/src/android/pnmFileTypeAndroid.h @@ -30,7 +30,13 @@ */ class EXPCL_PANDA_PNMIMAGETYPES PNMFileTypeAndroid : public PNMFileType { public: - PNMFileTypeAndroid(); + enum CompressFormat : jint { + CF_jpeg = 0, + CF_png = 1, + CF_webp = 2, + }; + + PNMFileTypeAndroid(CompressFormat format); virtual string get_name() const; @@ -41,6 +47,7 @@ public: virtual PNMReader *make_reader(istream *file, bool owns_file = true, const string &magic_number = string()); + virtual PNMWriter *make_writer(ostream *file, bool owns_file = true); public: class Reader : public PNMReader { @@ -60,29 +67,20 @@ public: int32_t _format; }; - // The TypedWritable interface follows. -public: - static void register_with_read_factory(); + class Writer : public PNMWriter { + public: + Writer(PNMFileType *type, ostream *file, bool owns_file, + CompressFormat format); -protected: - static TypedWritable *make_PNMFileTypeAndroid(const FactoryParams ¶ms); + virtual int write_data(xel *array, xelval *alpha); + virtual bool supports_grayscale() const; -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - PNMFileType::init_type(); - register_type(_type_handle, "PNMFileTypeAndroid", - PNMFileType::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + private: + CompressFormat _format; + }; private: - static TypeHandle _type_handle; + CompressFormat _format; }; #endif // ANDROID diff --git a/panda/src/android/pnmFileTypeAndroidReader.cxx b/panda/src/android/pnmFileTypeAndroidReader.cxx index 50c69cb59c..e18ebcbaea 100644 --- a/panda/src/android/pnmFileTypeAndroidReader.cxx +++ b/panda/src/android/pnmFileTypeAndroidReader.cxx @@ -76,10 +76,18 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : } streampos pos = _file->tellg(); - _env = get_jni_env(); + + Thread *current_thread = Thread::get_current_thread(); + _env = current_thread->get_jni_env(); + nassertd(_env != nullptr) { + _is_valid = false; + return; + } + jobject opts = _env->CallStaticObjectMethod(jni_PandaActivity, jni_PandaActivity_readBitmapSize, (jlong) _file); + _file->clear(); _file->seekg(pos); if (_file->tellg() != pos) { android_cat.error() diff --git a/panda/src/android/pnmFileTypeAndroidWriter.cxx b/panda/src/android/pnmFileTypeAndroidWriter.cxx new file mode 100644 index 0000000000..4677a25cbd --- /dev/null +++ b/panda/src/android/pnmFileTypeAndroidWriter.cxx @@ -0,0 +1,146 @@ +/** + * 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 pnmFileTypeAndroidWriter.cxx + * @author rdb + * @date 2018-02-10 + */ + +#include "pnmFileTypeAndroid.h" + +#ifdef ANDROID + +#include "config_pnmimagetypes.h" + +#include +#include + +// See android/graphics/Bitmap.java +enum class BitmapConfig : jint { + ALPHA_8 = 1, + RGB_565 = 3, + ARGB_4444 = 4, + ARGB_8888 = 5, + RGBA_F16 = 6, + HARDWARE = 7, +}; + +/** + * + */ +PNMFileTypeAndroid::Writer:: +Writer(PNMFileType *type, ostream *file, bool owns_file, + CompressFormat format) : + PNMWriter(type, file, owns_file), + _format(format) +{ +} + +/** + * Writes out an entire image all at once, including the header, based on the + * image data stored in the given _x_size * _y_size array and alpha pointers. + * (If the image type has no alpha channel, alpha is ignored.) Returns the + * number of rows correctly written. + * + * It is the user's responsibility to fill in the header data via calls to + * set_x_size(), set_num_channels(), etc., or copy_header_from(), before + * calling write_data(). + * + * It is important to delete the PNMWriter class after successfully writing + * the data. Failing to do this may result in some data not getting flushed! + * + * Derived classes need not override this if they instead provide + * supports_streaming() and write_row(), below. + */ +int PNMFileTypeAndroid::Writer:: +write_data(xel *array, xelval *alpha) { + size_t num_pixels = (size_t)_x_size * (size_t)_y_size; + + Thread *current_thread = Thread::get_current_thread(); + JNIEnv *env = current_thread->get_jni_env(); + nassertr(env != nullptr, 0); + + // Create a Bitmap object. + jobject bitmap = + env->CallStaticObjectMethod(jni_PandaActivity, + jni_PandaActivity_createBitmap, + (jint)_x_size, (jint)_y_size, + BitmapConfig::ARGB_8888, + (jboolean)has_alpha()); + nassertr(bitmap != nullptr, 0); + + // Get a writable pointer to write our pixel data to. + uint32_t *out; + int rc = AndroidBitmap_lockPixels(env, bitmap, (void **)&out); + if (rc != 0) { + android_cat.error() + << "Could not lock bitmap pixels (result code " << rc << ")\n"; + return 0; + } + + if (_maxval == 255) { + if (has_alpha() && alpha != nullptr) { + for (size_t i = 0; i < num_pixels; ++i) { + out[i] = (array[i].r) + | (array[i].g << 8u) + | (array[i].b << 16u) + | (alpha[i] << 24u); + } + } else { + for (size_t i = 0; i < num_pixels; ++i) { + out[i] = (array[i].r) + | (array[i].g << 8u) + | (array[i].b << 16u) + | 0xff000000u; + } + } + } else { + double ratio = 255.0 / _maxval; + if (has_alpha() && alpha != nullptr) { + for (size_t i = 0; i < num_pixels; ++i) { + out[i] = ((uint32_t)(array[i].r * ratio)) + | ((uint32_t)(array[i].g * ratio) << 8u) + | ((uint32_t)(array[i].b * ratio) << 16u) + | ((uint32_t)(alpha[i] * ratio) << 24u); + } + } else { + for (size_t i = 0; i < num_pixels; ++i) { + out[i] = ((uint32_t)(array[i].r * ratio)) + | ((uint32_t)(array[i].g * ratio) << 8u) + | ((uint32_t)(array[i].b * ratio) << 16u) + | 0xff000000u; + } + } + } + + // Finally, unlock the pixel data and compress it to the ostream. + AndroidBitmap_unlockPixels(env, bitmap); + jboolean res = + env->CallStaticBooleanMethod(jni_PandaActivity, + jni_PandaActivity_compressBitmap, + bitmap, _format, 85, (jlong)_file); + if (!res) { + android_cat.error() + << "Failed to compress bitmap.\n"; + return 0; + } + return _y_size; +} + +/** + * Returns true if this particular PNMWriter understands grayscale images. If + * this is false, then the rgb values of the xel array will be pre-filled with + * the same value across all three channels, to allow the writer to simply + * write out RGB data for a grayscale image. + */ +bool PNMFileTypeAndroid::Writer:: +supports_grayscale() const { + return false; +} + +#endif // ANDROID diff --git a/panda/src/android/pview.cxx b/panda/src/android/pview.cxx index 9a29b94574..2b31bb7577 100644 --- a/panda/src/android/pview.cxx +++ b/panda/src/android/pview.cxx @@ -63,12 +63,16 @@ int main(int argc, char **argv) { window->enable_keyboard(); window->setup_trackball(); framework.get_models().instance_to(window->get_render()); - // if (argc < 2) { If we have no arguments, get that trusty old triangle - // out. window->load_default_model(framework.get_models()); } else { - // window->load_models(framework.get_models(), argc, argv); } - - window->load_model(framework.get_models(), "panda-model.egg"); - window->load_model(framework.get_models(), "panda-walk4.egg"); + if (argc < 2) { + // If we have no arguments, get that trusty old triangle + // out. + window->load_default_model(framework.get_models()); + } else { + if (!window->load_models(framework.get_models(), argc, argv)) { + framework.close_framework(); + return 1; + } + } window->loop_animations(hierarchy_match_flags); diff --git a/panda/src/android/pview_manifest.xml b/panda/src/android/pview_manifest.xml index 133fa523aa..30125c673f 100644 --- a/panda/src/android/pview_manifest.xml +++ b/panda/src/android/pview_manifest.xml @@ -5,11 +5,16 @@ android:versionCode="1" android:versionName="1.0"> - - + + + + + + + android:configChanges="orientation|keyboardHidden" + android:launchMode="singleInstance"> @@ -17,6 +22,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/panda/src/androiddisplay/androidGraphicsWindow.cxx b/panda/src/androiddisplay/androidGraphicsWindow.cxx index 946e602aeb..31cd4e23d6 100644 --- a/panda/src/androiddisplay/androidGraphicsWindow.cxx +++ b/panda/src/androiddisplay/androidGraphicsWindow.cxx @@ -44,7 +44,8 @@ AndroidGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, int flags, GraphicsStateGuardian *gsg, GraphicsOutput *host) : - GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) + GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host), + _mouse_button_state(0) { AndroidGraphicsPipe *android_pipe; DCAST_INTO_V(android_pipe, _pipe); @@ -246,6 +247,13 @@ close_window() { } GraphicsWindow::close_window(); + + nassertv(_app != nullptr); + if (_app->userData == this) { + _app->userData = nullptr; + _app->onAppCmd = nullptr; + _app->onInputEvent = nullptr; + } } /** @@ -388,8 +396,10 @@ create_surface() { */ void AndroidGraphicsWindow:: handle_command(struct android_app *app, int32_t command) { - AndroidGraphicsWindow* window = (AndroidGraphicsWindow*) app->userData; - window->ns_handle_command(command); + AndroidGraphicsWindow *window = (AndroidGraphicsWindow *)app->userData; + if (window != nullptr) { + window->ns_handle_command(command); + } } /** @@ -509,11 +519,15 @@ handle_key_event(const AInputEvent *event) { // Is it an up or down event? int32_t action = AKeyEvent_getAction(event); if (action == AKEY_EVENT_ACTION_DOWN) { - _input_devices[0].button_down(button); + if (AKeyEvent_getRepeatCount(event) > 0) { + _input_devices[0].button_resume_down(button); + } else { + _input_devices[0].button_down(button); + } } else if (action == AKEY_EVENT_ACTION_UP) { _input_devices[0].button_up(button); } - // TODO getRepeatCount, ACTION_MULTIPLE + // TODO AKEY_EVENT_ACTION_MULTIPLE return 1; } @@ -526,10 +540,32 @@ handle_motion_event(const AInputEvent *event) { int32_t action = AMotionEvent_getAction(event); action &= AMOTION_EVENT_ACTION_MASK; - if (action == AMOTION_EVENT_ACTION_DOWN) { - _input_devices[0].button_down(MouseButton::one()); - } else if (action == AMOTION_EVENT_ACTION_UP) { - _input_devices[0].button_up(MouseButton::one()); + if (action == AMOTION_EVENT_ACTION_DOWN || + action == AMOTION_EVENT_ACTION_UP) { + // The up event doesn't let us know which button is up, so we need to + // keep track of the button state ourselves. + int32_t button_state = AMotionEvent_getButtonState(event); + if (button_state == 0 && action == AMOTION_EVENT_ACTION_DOWN) { + button_state = AMOTION_EVENT_BUTTON_PRIMARY; + } + int32_t changed = _mouse_button_state ^ button_state; + if (changed != 0) { + if (changed & AMOTION_EVENT_BUTTON_PRIMARY) { + if (button_state & AMOTION_EVENT_BUTTON_PRIMARY) { + _input_devices[0].button_down(MouseButton::one()); + } else { + _input_devices[0].button_up(MouseButton::one()); + } + } + if (changed & AMOTION_EVENT_BUTTON_SECONDARY) { + if (button_state & AMOTION_EVENT_BUTTON_SECONDARY) { + _input_devices[0].button_down(MouseButton::three()); + } else { + _input_devices[0].button_up(MouseButton::three()); + } + } + _mouse_button_state = button_state; + } } float x = AMotionEvent_getX(event, 0) - _app->contentRect.left; @@ -668,7 +704,7 @@ map_button(int32_t keycode) { case AKEYCODE_ENTER: return KeyboardButton::enter(); case AKEYCODE_DEL: - return KeyboardButton::del(); + return KeyboardButton::backspace(); case AKEYCODE_GRAVE: return KeyboardButton::ascii_key('`'); case AKEYCODE_MINUS: @@ -696,6 +732,7 @@ map_button(int32_t keycode) { case AKEYCODE_PLUS: return KeyboardButton::ascii_key('+'); case AKEYCODE_MENU: + return KeyboardButton::menu(); case AKEYCODE_NOTIFICATION: case AKEYCODE_SEARCH: case AKEYCODE_MEDIA_PLAY_PAUSE: @@ -727,6 +764,61 @@ map_button(int32_t keycode) { case AKEYCODE_BUTTON_START: case AKEYCODE_BUTTON_SELECT: case AKEYCODE_BUTTON_MODE: + break; + case AKEYCODE_ESCAPE: + return KeyboardButton::escape(); + case AKEYCODE_FORWARD_DEL: + return KeyboardButton::del(); + case AKEYCODE_CTRL_LEFT: + return KeyboardButton::lcontrol(); + case AKEYCODE_CTRL_RIGHT: + return KeyboardButton::rcontrol(); + case AKEYCODE_CAPS_LOCK: + return KeyboardButton::caps_lock(); + case AKEYCODE_SCROLL_LOCK: + return KeyboardButton::scroll_lock(); + case AKEYCODE_META_LEFT: + return KeyboardButton::lmeta(); + case AKEYCODE_META_RIGHT: + return KeyboardButton::rmeta(); + case AKEYCODE_FUNCTION: + break; + case AKEYCODE_SYSRQ: + return KeyboardButton::print_screen(); + case AKEYCODE_BREAK: + return KeyboardButton::pause(); + case AKEYCODE_MOVE_HOME: + return KeyboardButton::home(); + case AKEYCODE_MOVE_END: + return KeyboardButton::end(); + case AKEYCODE_INSERT: + return KeyboardButton::insert(); + case AKEYCODE_F1: + return KeyboardButton::f1(); + case AKEYCODE_F2: + return KeyboardButton::f2(); + case AKEYCODE_F3: + return KeyboardButton::f3(); + case AKEYCODE_F4: + return KeyboardButton::f4(); + case AKEYCODE_F5: + return KeyboardButton::f5(); + case AKEYCODE_F6: + return KeyboardButton::f6(); + case AKEYCODE_F7: + return KeyboardButton::f7(); + case AKEYCODE_F8: + return KeyboardButton::f8(); + case AKEYCODE_F9: + return KeyboardButton::f9(); + case AKEYCODE_F10: + return KeyboardButton::f10(); + case AKEYCODE_F11: + return KeyboardButton::f11(); + case AKEYCODE_F12: + return KeyboardButton::f12(); + case AKEYCODE_NUM_LOCK: + return KeyboardButton::num_lock(); default: break; } diff --git a/panda/src/androiddisplay/androidGraphicsWindow.h b/panda/src/androiddisplay/androidGraphicsWindow.h index 10ce6de983..3d1ca79946 100644 --- a/panda/src/androiddisplay/androidGraphicsWindow.h +++ b/panda/src/androiddisplay/androidGraphicsWindow.h @@ -71,6 +71,8 @@ private: EGLDisplay _egl_display; EGLSurface _egl_surface; + int32_t _mouse_button_state; + const ARect *rect; public: 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/config_fmodAudio.cxx b/panda/src/audiotraits/config_fmodAudio.cxx index 0726118121..affc593636 100644 --- a/panda/src/audiotraits/config_fmodAudio.cxx +++ b/panda/src/audiotraits/config_fmodAudio.cxx @@ -51,6 +51,8 @@ init_libFmodAudio() { FmodAudioManager::init_type(); FmodAudioSound::init_type(); + AudioManager::register_AudioManager_creator(&Create_FmodAudioManager); + PandaSystem *ps = PandaSystem::get_global_ptr(); ps->add_system("FMOD"); ps->add_system("audio"); diff --git a/panda/src/audiotraits/config_fmodAudio.h b/panda/src/audiotraits/config_fmodAudio.h index 79e221ff31..336f9257d4 100644 --- a/panda/src/audiotraits/config_fmodAudio.h +++ b/panda/src/audiotraits/config_fmodAudio.h @@ -24,7 +24,7 @@ NotifyCategoryDecl(fmodAudio, EXPCL_FMOD_AUDIO, EXPTP_FMOD_AUDIO); extern ConfigVariableInt fmod_audio_preload_threshold; -extern EXPCL_FMOD_AUDIO void init_libFmodAudio(); +extern "C" EXPCL_FMOD_AUDIO void init_libFmodAudio(); extern "C" EXPCL_FMOD_AUDIO Create_AudioManager_proc *get_audio_manager_func_fmod_audio(); #endif // CONFIG_FMODAUDIO_H diff --git a/panda/src/audiotraits/fmodAudioManager.cxx b/panda/src/audiotraits/fmodAudioManager.cxx index dc98e76287..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) { @@ -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 b6d68a0ec0..cbdaadbfa1 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; @@ -443,6 +443,7 @@ get_sound_data(MovieAudio *movie, int mode) { alBufferData(sd->_sample, (channels>1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16, data, samples * channels * 2, stream->audio_rate()); + delete[] data; int err = alGetError(); if (err != AL_NO_ERROR) { audio_error("could not fill OpenAL buffer object with data"); @@ -470,6 +471,12 @@ get_sound(MovieAudio *sound, bool positional, int mode) { PT(OpenALAudioSound) oas = new OpenALAudioSound(this, sound, positional, mode); + if(!oas->_manager) { + // The sound cleaned itself up immediately. It pretty clearly didn't like + // something, so we should just return a null sound instead. + return get_null_sound(); + } + _all_sounds.insert(oas); PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas; return res; @@ -499,6 +506,12 @@ get_sound(const string &file_name, bool positional, int mode) { PT(OpenALAudioSound) oas = new OpenALAudioSound(this, mva, positional, mode); + if(!oas->_manager) { + // The sound cleaned itself up immediately. It pretty clearly didn't like + // something, so we should just return a null sound instead. + return get_null_sound(); + } + _all_sounds.insert(oas); PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas; return res; @@ -715,12 +728,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 +744,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 +764,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 { 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.I b/panda/src/audiotraits/openalAudioSound.I index a68d36ee12..1b7e56a82c 100644 --- a/panda/src/audiotraits/openalAudioSound.I +++ b/panda/src/audiotraits/openalAudioSound.I @@ -37,16 +37,19 @@ get_calibrated_clock(double rtc) const { /** * Makes sure the sound data record is present, and if not, obtains it. + * + * Returns true on success, false on failure. */ -void OpenALAudioSound:: +bool OpenALAudioSound:: require_sound_data() { if (_sd==0) { _sd = _manager->get_sound_data(_movie, _desired_mode); if (_sd==0) { audio_error("Could not open audio " << _movie->get_filename()); - cleanup(); + return false; } } + return true; } /** diff --git a/panda/src/audiotraits/openalAudioSound.cxx b/panda/src/audiotraits/openalAudioSound.cxx index 43ebd149cb..d3c5ff7291 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), @@ -69,8 +69,8 @@ OpenALAudioSound(OpenALAudioManager* manager, ReMutexHolder holder(OpenALAudioManager::_lock); - require_sound_data(); - if (_manager == NULL) { + if (!require_sound_data()) { + cleanup(); return; } @@ -130,10 +130,12 @@ play() { stop(); - require_sound_data(); - if (_manager == 0) return; - _manager->starting_sound(this); + if (!require_sound_data()) { + cleanup(); + return; + } + _manager->starting_sound(this); if (!_source) { return; } @@ -435,7 +437,11 @@ pull_used_buffers() { ReMutexHolder holder(OpenALAudioManager::_lock); while (_stream_queued.size()) { ALuint buffer = 0; - alGetError(); + ALint num_buffers = 0; + alGetSourcei(_source, AL_BUFFERS_PROCESSED, &num_buffers); + if (num_buffers <= 0) { + break; + } alSourceUnqueueBuffers(_source, 1, &buffer); int err = alGetError(); if (err == AL_NO_ERROR) { @@ -673,7 +679,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 +704,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/audiotraits/openalAudioSound.h b/panda/src/audiotraits/openalAudioSound.h index 3442acb4f2..f5b02667e5 100644 --- a/panda/src/audiotraits/openalAudioSound.h +++ b/panda/src/audiotraits/openalAudioSound.h @@ -116,7 +116,7 @@ private: int read_stream_data(int bytelen, unsigned char *data); void pull_used_buffers(); void push_fresh_buffers(); - INLINE void require_sound_data(); + INLINE bool require_sound_data(); INLINE void release_sound_data(); private: diff --git a/panda/src/bullet/bulletBaseCharacterControllerNode.h b/panda/src/bullet/bulletBaseCharacterControllerNode.h index b09b26db74..eafc561c67 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; @@ -44,8 +43,8 @@ public: virtual btPairCachingGhostObject *get_ghost() const = 0; virtual btCharacterControllerInterface *get_character() const = 0; - virtual void sync_p2b(PN_stdfloat dt, int num_substeps) = 0; - virtual void sync_b2p() = 0; + virtual void do_sync_p2b(PN_stdfloat dt, int num_substeps) = 0; + virtual void do_sync_b2p() = 0; public: static TypeHandle get_class_type() { diff --git a/panda/src/bullet/bulletBodyNode.I b/panda/src/bullet/bulletBodyNode.I index 01e62e8bd6..6f9aac9f45 100644 --- a/panda/src/bullet/bulletBodyNode.I +++ b/panda/src/bullet/bulletBodyNode.I @@ -84,51 +84,6 @@ get_collision_response() const { return !get_collision_flag(btCollisionObject::CF_NO_CONTACT_RESPONSE); } -/** - * - */ -INLINE void BulletBodyNode:: -set_collision_flag(int flag, bool value) { - - int flags = get_object()->getCollisionFlags(); - - if (value == true) { - flags |= flag; - } - else { - flags &= ~(flag); - } - - get_object()->setCollisionFlags(flags); -} - -/** - * - */ -INLINE bool BulletBodyNode:: -get_collision_flag(int flag) const { - - return (get_object()->getCollisionFlags() & flag) ? true : false; -} - -/** - * - */ -INLINE bool BulletBodyNode:: -is_static() const { - - return get_object()->isStaticObject(); -} - -/** - * - */ -INLINE bool BulletBodyNode:: -is_kinematic() const { - - return get_object()->isKinematicObject(); -} - /** * */ @@ -147,90 +102,6 @@ set_kinematic(bool value) { set_collision_flag(btCollisionObject::CF_KINEMATIC_OBJECT, value); } -/** - * - */ -INLINE PN_stdfloat BulletBodyNode:: -get_restitution() const { - - return get_object()->getRestitution(); -} - -/** - * - */ -INLINE void BulletBodyNode:: -set_restitution(PN_stdfloat restitution) { - - return get_object()->setRestitution(restitution); -} - -/** - * - */ -INLINE PN_stdfloat BulletBodyNode:: -get_friction() const { - - return get_object()->getFriction(); -} - -/** - * - */ -INLINE void BulletBodyNode:: -set_friction(PN_stdfloat friction) { - - return get_object()->setFriction(friction); -} - -#if BT_BULLET_VERSION >= 281 -/** - * - */ -INLINE PN_stdfloat BulletBodyNode:: -get_rolling_friction() const { - - return get_object()->getRollingFriction(); -} - -/** - * - */ -INLINE void BulletBodyNode:: -set_rolling_friction(PN_stdfloat friction) { - - return get_object()->setRollingFriction(friction); -} -#endif - -/** - * - */ -INLINE bool BulletBodyNode:: -has_anisotropic_friction() const { - - return get_object()->hasAnisotropicFriction(); -} - -/** - * - */ -INLINE int BulletBodyNode:: -get_num_shapes() const { - - return _shapes.size(); -} - -/** - * - */ -INLINE BulletShape *BulletBodyNode:: -get_shape(int idx) const { - - nassertr(idx >= 0 && idx < (int)_shapes.size(), NULL); - return _shapes[idx]; -} - /** * Enables or disables the debug visualisation for this collision object. By * default the debug visualisation is enabled. diff --git a/panda/src/bullet/bulletBodyNode.cxx b/panda/src/bullet/bulletBodyNode.cxx index b078864ccc..f02641cd96 100644 --- a/panda/src/bullet/bulletBodyNode.cxx +++ b/panda/src/bullet/bulletBodyNode.cxx @@ -41,9 +41,11 @@ BulletBodyNode(const char *name) : PandaNode(name) { */ BulletBodyNode:: BulletBodyNode(const BulletBodyNode ©) : - PandaNode(copy), - _shapes(copy._shapes) + PandaNode(copy) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shapes = copy._shapes; if (copy._shape && copy._shape->getShapeType() == COMPOUND_SHAPE_PROXYTYPE) { // btCompoundShape does not define a copy constructor. Manually copy. btCompoundShape *shape = new btCompoundShape; @@ -148,16 +150,168 @@ safe_to_flatten_below() const { * */ void BulletBodyNode:: -output(ostream &out) const { +do_output(ostream &out) const { PandaNode::output(out); - out << " (" << get_num_shapes() << " shapes)"; + out << " (" << _shapes.size() << " shapes)"; - out << (is_active() ? " active" : " inactive"); + out << (get_object()->isActive() ? " active" : " inactive"); - if (is_static()) out << " static"; - if (is_kinematic()) out << " kinematic"; + if (get_object()->isStaticObject()) out << " static"; + if (get_object()->isKinematicObject()) out << " kinematic"; +} + +/** + * + */ +void BulletBodyNode:: +output(ostream &out) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_output(out); +} + +/** + * + */ +void BulletBodyNode:: +set_collision_flag(int flag, bool value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + int flags = get_object()->getCollisionFlags(); + + if (value == true) { + flags |= flag; + } + else { + flags &= ~(flag); + } + + get_object()->setCollisionFlags(flags); +} + +/** + * + */ +bool BulletBodyNode:: +get_collision_flag(int flag) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (get_object()->getCollisionFlags() & flag) ? true : false; +} + +/** + * + */ +bool BulletBodyNode:: +is_static() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->isStaticObject(); +} + +/** + * + */ +bool BulletBodyNode:: +is_kinematic() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->isKinematicObject(); +} + +/** + * + */ +PN_stdfloat BulletBodyNode:: +get_restitution() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->getRestitution(); +} + +/** + * + */ +void BulletBodyNode:: +set_restitution(PN_stdfloat restitution) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->setRestitution(restitution); +} + +/** + * + */ +PN_stdfloat BulletBodyNode:: +get_friction() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->getFriction(); +} + +/** + * + */ +void BulletBodyNode:: +set_friction(PN_stdfloat friction) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->setFriction(friction); +} + +#if BT_BULLET_VERSION >= 281 +/** + * + */ +PN_stdfloat BulletBodyNode:: +get_rolling_friction() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->getRollingFriction(); +} + +/** + * + */ +void BulletBodyNode:: +set_rolling_friction(PN_stdfloat friction) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->setRollingFriction(friction); +} +#endif + +/** + * + */ +bool BulletBodyNode:: +has_anisotropic_friction() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->hasAnisotropicFriction(); +} + +/** + * + */ +int BulletBodyNode:: +get_num_shapes() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _shapes.size(); +} + +/** + * + */ +BulletShape *BulletBodyNode:: +get_shape(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_shapes.size(), NULL); + return _shapes[idx]; } /** @@ -165,6 +319,16 @@ output(ostream &out) const { */ void BulletBodyNode:: add_shape(BulletShape *bullet_shape, const TransformState *ts) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_add_shape(bullet_shape, ts); +} + +/** + * Assumes the lock(bullet global lock) is held by the caller + */ +void BulletBodyNode:: +do_add_shape(BulletShape *bullet_shape, const TransformState *ts) { nassertv(get_object()); nassertv(ts); @@ -246,7 +410,7 @@ add_shape(BulletShape *bullet_shape, const TransformState *ts) { // Restore the local scaling again np.set_scale(scale); - shape_changed(); + do_shape_changed(); } /** @@ -254,6 +418,7 @@ add_shape(BulletShape *bullet_shape, const TransformState *ts) { */ void BulletBodyNode:: remove_shape(BulletShape *shape) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(get_object()); @@ -312,7 +477,7 @@ remove_shape(BulletShape *shape) { compound->removeChildShape(shape->ptr()); } - shape_changed(); + do_shape_changed(); } } @@ -333,6 +498,7 @@ is_identity(btTransform &trans) { */ LPoint3 BulletBodyNode:: get_shape_pos(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertr(idx >= 0 && idx < (int)_shapes.size(), LPoint3::zero()); @@ -352,14 +518,17 @@ get_shape_pos(int idx) const { */ LMatrix4 BulletBodyNode:: get_shape_mat(int idx) const { - return get_shape_transform(idx)->get_mat(); + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_shape_transform(idx)->get_mat(); } /** * */ CPT(TransformState) BulletBodyNode:: -get_shape_transform(int idx) const { +do_get_shape_transform(int idx) const { + nassertr(idx >= 0 && idx < (int)_shapes.size(), TransformState::make_identity()); btCollisionShape *root = get_object()->getCollisionShape(); @@ -386,13 +555,25 @@ get_shape_transform(int idx) const { return TransformState::make_identity(); } +/** + * + */ +CPT(TransformState) BulletBodyNode:: +get_shape_transform(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_shape_transform(idx); +} + + /** * Hook which will be called whenever the total shape of a body changed. Used * for example to update the mass properties (inertia) of a rigid body. The * default implementation does nothing. + * Assumes the lock(bullet global lock) is held */ void BulletBodyNode:: -shape_changed() { +do_shape_changed() { } @@ -401,6 +582,7 @@ shape_changed() { */ void BulletBodyNode:: set_deactivation_time(PN_stdfloat dt) { + LightMutexHolder holder(BulletWorld::get_global_lock()); get_object()->setDeactivationTime(dt); } @@ -410,6 +592,7 @@ set_deactivation_time(PN_stdfloat dt) { */ PN_stdfloat BulletBodyNode:: get_deactivation_time() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->getDeactivationTime(); } @@ -419,6 +602,7 @@ get_deactivation_time() const { */ bool BulletBodyNode:: is_active() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->isActive(); } @@ -428,6 +612,7 @@ is_active() const { */ void BulletBodyNode:: set_active(bool active, bool force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); if (active) { get_object()->activate(force); @@ -457,10 +642,12 @@ force_active(bool active) { */ void BulletBodyNode:: set_deactivation_enabled(bool enabled) { + LightMutexHolder holder(BulletWorld::get_global_lock()); // Don't change the state if it's currently active and we enable // deactivation. - if (enabled != is_deactivation_enabled()) { + bool is_enabled = get_object()->getActivationState() != DISABLE_DEACTIVATION; + if (enabled != is_enabled) { // It's OK to set to ACTIVE_TAG even if we don't mean to activate it; it // will be disabled right away if the deactivation timer has run out. @@ -474,6 +661,7 @@ set_deactivation_enabled(bool enabled) { */ bool BulletBodyNode:: is_deactivation_enabled() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (get_object()->getActivationState() != DISABLE_DEACTIVATION); } @@ -483,6 +671,7 @@ is_deactivation_enabled() const { */ bool BulletBodyNode:: check_collision_with(PandaNode *node) { + LightMutexHolder holder(BulletWorld::get_global_lock()); btCollisionObject *obj = BulletWorld::get_collision_object(node); @@ -499,6 +688,7 @@ check_collision_with(PandaNode *node) { */ LVecBase3 BulletBodyNode:: get_anisotropic_friction() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVecBase3(get_object()->getAnisotropicFriction()); } @@ -508,6 +698,7 @@ get_anisotropic_friction() const { */ void BulletBodyNode:: set_anisotropic_friction(const LVecBase3 &friction) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!friction.is_nan()); get_object()->setAnisotropicFriction(LVecBase3_to_btVector3(friction)); @@ -518,6 +709,7 @@ set_anisotropic_friction(const LVecBase3 &friction) { */ bool BulletBodyNode:: has_contact_response() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->hasContactResponse(); } @@ -527,7 +719,8 @@ has_contact_response() const { */ PN_stdfloat BulletBodyNode:: get_contact_processing_threshold() const { - + LightMutexHolder holder(BulletWorld::get_global_lock()); + return get_object()->getContactProcessingThreshold(); } @@ -537,6 +730,7 @@ get_contact_processing_threshold() const { */ void BulletBodyNode:: set_contact_processing_threshold(PN_stdfloat threshold) { + LightMutexHolder holder(BulletWorld::get_global_lock()); get_object()->setContactProcessingThreshold(threshold); } @@ -546,6 +740,7 @@ set_contact_processing_threshold(PN_stdfloat threshold) { */ PN_stdfloat BulletBodyNode:: get_ccd_swept_sphere_radius() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->getCcdSweptSphereRadius(); } @@ -555,6 +750,7 @@ get_ccd_swept_sphere_radius() const { */ void BulletBodyNode:: set_ccd_swept_sphere_radius(PN_stdfloat radius) { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->setCcdSweptSphereRadius(radius); } @@ -564,6 +760,7 @@ set_ccd_swept_sphere_radius(PN_stdfloat radius) { */ PN_stdfloat BulletBodyNode:: get_ccd_motion_threshold() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->getCcdMotionThreshold(); } @@ -573,6 +770,7 @@ get_ccd_motion_threshold() const { */ void BulletBodyNode:: set_ccd_motion_threshold(PN_stdfloat threshold) { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->setCcdMotionThreshold(threshold); } @@ -582,6 +780,7 @@ set_ccd_motion_threshold(PN_stdfloat threshold) { */ void BulletBodyNode:: add_shapes_from_collision_solids(CollisionNode *cnode) { + LightMutexHolder holder(BulletWorld::get_global_lock()); PT(BulletTriangleMesh) mesh = NULL; @@ -594,7 +793,7 @@ add_shapes_from_collision_solids(CollisionNode *cnode) { CPT(CollisionSphere) sphere = DCAST(CollisionSphere, solid); CPT(TransformState) ts = TransformState::make_pos(sphere->get_center()); - add_shape(BulletSphereShape::make_from_solid(sphere), ts); + do_add_shape(BulletSphereShape::make_from_solid(sphere), ts); } // CollisionBox @@ -602,14 +801,14 @@ add_shapes_from_collision_solids(CollisionNode *cnode) { CPT(CollisionBox) box = DCAST(CollisionBox, solid); CPT(TransformState) ts = TransformState::make_pos(box->get_center()); - add_shape(BulletBoxShape::make_from_solid(box), ts); + do_add_shape(BulletBoxShape::make_from_solid(box), ts); } // CollisionPlane else if (CollisionPlane::get_class_type() == type) { CPT(CollisionPlane) plane = DCAST(CollisionPlane, solid); - add_shape(BulletPlaneShape::make_from_solid(plane)); + do_add_shape(BulletPlaneShape::make_from_solid(plane)); } // CollisionGeom @@ -625,13 +824,13 @@ add_shapes_from_collision_solids(CollisionNode *cnode) { LPoint3 p2 = polygon->get_point(i-1); LPoint3 p3 = polygon->get_point(i); - mesh->add_triangle(p1, p2, p3, true); + mesh->do_add_triangle(p1, p2, p3, true); } } } - if (mesh && mesh->get_num_triangles() > 0) { - add_shape(new BulletTriangleMeshShape(mesh, true)); + if (mesh && mesh->do_get_num_triangles() > 0) { + do_add_shape(new BulletTriangleMeshShape(mesh, true)); } } @@ -651,6 +850,7 @@ set_transform_dirty() { */ BoundingSphere BulletBodyNode:: get_shape_bounds() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); /* btTransform tr; diff --git a/panda/src/bullet/bulletBodyNode.h b/panda/src/bullet/bulletBodyNode.h index 562e88c7fb..55f8583938 100644 --- a/panda/src/bullet/bulletBodyNode.h +++ b/panda/src/bullet/bulletBodyNode.h @@ -42,8 +42,8 @@ PUBLISHED: void add_shape(BulletShape *shape, const TransformState *xform=TransformState::make_identity()); void remove_shape(BulletShape *shape); - INLINE int get_num_shapes() const; - INLINE BulletShape *get_shape(int idx) const; + int get_num_shapes() const; + BulletShape *get_shape(int idx) const; MAKE_SEQ(get_shapes, get_num_shapes, get_shape); LPoint3 get_shape_pos(int idx) const; @@ -54,8 +54,8 @@ PUBLISHED: void add_shapes_from_collision_solids(CollisionNode *cnode); // Static and kinematic - INLINE bool is_static() const; - INLINE bool is_kinematic() const; + bool is_static() const; + bool is_kinematic() const; INLINE void set_static(bool value); INLINE void set_kinematic(bool value); @@ -92,19 +92,19 @@ PUBLISHED: INLINE bool is_debug_enabled() const; // Friction and Restitution - INLINE PN_stdfloat get_restitution() const; - INLINE void set_restitution(PN_stdfloat restitution); + PN_stdfloat get_restitution() const; + void set_restitution(PN_stdfloat restitution); - INLINE PN_stdfloat get_friction() const; - INLINE void set_friction(PN_stdfloat friction); + PN_stdfloat get_friction() const; + void set_friction(PN_stdfloat friction); #if BT_BULLET_VERSION >= 281 - INLINE PN_stdfloat get_rolling_friction() const; - INLINE void set_rolling_friction(PN_stdfloat friction); + PN_stdfloat get_rolling_friction() const; + void set_rolling_friction(PN_stdfloat friction); MAKE_PROPERTY(rolling_friction, get_rolling_friction, set_rolling_friction); #endif - INLINE bool has_anisotropic_friction() const; + bool has_anisotropic_friction() const; void set_anisotropic_friction(const LVecBase3 &friction); LVecBase3 get_anisotropic_friction() const; @@ -151,10 +151,11 @@ public: virtual bool safe_to_flatten_below() const; virtual void output(ostream &out) const; + virtual void do_output(ostream &out) const; protected: - INLINE void set_collision_flag(int flag, bool value); - INLINE bool get_collision_flag(int flag) const; + void set_collision_flag(int flag, bool value); + bool get_collision_flag(int flag) const; btCollisionShape *_shape; @@ -162,7 +163,9 @@ protected: BulletShapes _shapes; private: - virtual void shape_changed(); + virtual void do_shape_changed(); + void do_add_shape(BulletShape *shape, const TransformState *xform=TransformState::make_identity()); + CPT(TransformState) do_get_shape_transform(int idx) const; static bool is_identity(btTransform &trans); diff --git a/panda/src/bullet/bulletBoxShape.I b/panda/src/bullet/bulletBoxShape.I index 1051a4018f..f432db4a69 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()) { +} + /** * */ @@ -19,19 +28,3 @@ INLINE BulletBoxShape:: delete _shape; } - -/** - * - */ -INLINE BulletBoxShape:: -BulletBoxShape(const BulletBoxShape ©) : - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletBoxShape:: -operator = (const BulletBoxShape ©) { - _shape = copy._shape; -} diff --git a/panda/src/bullet/bulletBoxShape.cxx b/panda/src/bullet/bulletBoxShape.cxx index 7d52cf2513..9ae2e16a11 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); @@ -28,6 +28,28 @@ BulletBoxShape(const LVecBase3 &halfExtents) { _shape->setUserPointer(this); } +/** + * + */ +BulletBoxShape:: +BulletBoxShape(const BulletBoxShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _half_extents = copy._half_extents; +} + +/** + * + */ +void BulletBoxShape:: +operator = (const BulletBoxShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _half_extents = copy._half_extents; +} + /** * */ @@ -42,6 +64,7 @@ ptr() const { */ LVecBase3 BulletBoxShape:: get_half_extents_without_margin() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVecBase3(_shape->getHalfExtentsWithoutMargin()); } @@ -51,6 +74,7 @@ get_half_extents_without_margin() const { */ LVecBase3 BulletBoxShape:: get_half_extents_with_margin() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVecBase3(_shape->getHalfExtentsWithMargin()); } @@ -64,9 +88,9 @@ make_from_solid(const CollisionBox *solid) { LPoint3 p0 = solid->get_min(); LPoint3 p1 = solid->get_max(); - LVecBase3 extents(p1.get_x() - p0.get_x() / 2.0, - p1.get_y() - p0.get_y() / 2.0, - p1.get_z() - p0.get_z() / 2.0); + LVecBase3 extents((p1.get_x() - p0.get_x()) / 2.0, + (p1.get_y() - p0.get_y()) / 2.0, + (p1.get_z() - p0.get_z()) / 2.0); return new BulletBoxShape(extents); } @@ -85,8 +109,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 +137,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 72a897d799..bfb9207e61 100644 --- a/panda/src/bullet/bulletBoxShape.h +++ b/panda/src/bullet/bulletBoxShape.h @@ -29,12 +29,12 @@ class EXPCL_PANDABULLET BulletBoxShape : public BulletShape { private: // Only used by make_from_bam - INLINE BulletBoxShape() : _shape(NULL) {}; + INLINE BulletBoxShape(); PUBLISHED: - BulletBoxShape(const LVecBase3 &halfExtents); - INLINE BulletBoxShape(const BulletBoxShape ©); - INLINE void operator = (const BulletBoxShape ©); + explicit BulletBoxShape(const LVecBase3 &halfExtents); + BulletBoxShape(const BulletBoxShape ©); + void operator = (const BulletBoxShape ©); INLINE ~BulletBoxShape(); LVecBase3 get_half_extents_without_margin() const; @@ -50,6 +50,7 @@ public: 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..2491d55d0d 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) { +} + /** * */ @@ -21,35 +31,26 @@ INLINE BulletCapsuleShape:: } /** - * - */ -INLINE BulletCapsuleShape:: -BulletCapsuleShape(const BulletCapsuleShape ©) : - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletCapsuleShape:: -operator = (const BulletCapsuleShape ©) { - _shape = copy._shape; -} - -/** - * + * 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..1c396bbdfb 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: @@ -36,9 +38,35 @@ BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) { break; } + nassertv(_shape); _shape->setUserPointer(this); } +/** + * + */ +BulletCapsuleShape:: +BulletCapsuleShape(const BulletCapsuleShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _radius = copy._radius; + _height = copy._height; +} + +/** + * + */ +void BulletCapsuleShape:: +operator = (const BulletCapsuleShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _radius = copy._radius; + _height = copy._height; +} + + /** * */ @@ -47,3 +75,80 @@ 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; + } + + nassertv(_shape); + _shape->setUserPointer(this); + _shape->setMargin(margin); +} diff --git a/panda/src/bullet/bulletCapsuleShape.h b/panda/src/bullet/bulletCapsuleShape.h index b89001064f..e376674976 100644 --- a/panda/src/bullet/bulletCapsuleShape.h +++ b/panda/src/bullet/bulletCapsuleShape.h @@ -24,24 +24,40 @@ * */ 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); - INLINE BulletCapsuleShape(const BulletCapsuleShape ©); - INLINE void operator = (const BulletCapsuleShape ©); + explicit BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); + BulletCapsuleShape(const BulletCapsuleShape ©); + void operator = (const BulletCapsuleShape ©); INLINE ~BulletCapsuleShape(); INLINE PN_stdfloat get_radius() const; INLINE PN_stdfloat get_half_height() const; - MAKE_PROPERTY(radius, get_radius); - MAKE_PROPERTY(half_height, get_half_height); - 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.I b/panda/src/bullet/bulletCharacterControllerNode.I index 391e387e67..8017857312 100644 --- a/panda/src/bullet/bulletCharacterControllerNode.I +++ b/panda/src/bullet/bulletCharacterControllerNode.I @@ -17,6 +17,8 @@ INLINE BulletCharacterControllerNode:: ~BulletCharacterControllerNode() { + delete _character; + delete _ghost; } /** diff --git a/panda/src/bullet/bulletCharacterControllerNode.cxx b/panda/src/bullet/bulletCharacterControllerNode.cxx index c6af3842e9..d65948a824 100644 --- a/panda/src/bullet/bulletCharacterControllerNode.cxx +++ b/panda/src/bullet/bulletCharacterControllerNode.cxx @@ -77,6 +77,7 @@ BulletCharacterControllerNode(BulletShape *shape, PN_stdfloat step_height, const */ void BulletCharacterControllerNode:: set_linear_movement(const LVector3 &movement, bool is_local) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!movement.is_nan()); @@ -89,18 +90,19 @@ set_linear_movement(const LVector3 &movement, bool is_local) { */ void BulletCharacterControllerNode:: set_angular_movement(PN_stdfloat omega) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _angular_movement = omega; } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletCharacterControllerNode:: -sync_p2b(PN_stdfloat dt, int num_substeps) { +do_sync_p2b(PN_stdfloat dt, int num_substeps) { // Synchronise global transform - transform_changed(); + do_transform_changed(); // Angular rotation btScalar angle = dt * deg_2_rad(_angular_movement); @@ -131,10 +133,10 @@ sync_p2b(PN_stdfloat dt, int num_substeps) { } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletCharacterControllerNode:: -sync_b2p() { +do_sync_b2p() { NodePath np = NodePath::any_path((PandaNode *)this); LVecBase3 scale = np.get_net_transform()->get_scale(); @@ -154,10 +156,10 @@ sync_b2p() { } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletCharacterControllerNode:: -transform_changed() { +do_transform_changed() { if (_sync_disable) return; @@ -187,10 +189,23 @@ transform_changed() { _ghost->getWorldTransform().setBasis(m); // Set scale - _shape->set_local_scale(scale); + _shape->do_set_local_scale(scale); } } +/** + * + */ +void BulletCharacterControllerNode:: +transform_changed() { + + if (_sync_disable) return; + + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_transform_changed(); +} + /** * */ @@ -205,6 +220,7 @@ get_shape() const { */ bool BulletCharacterControllerNode:: is_on_ground() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _character->onGround(); } @@ -214,6 +230,7 @@ is_on_ground() const { */ bool BulletCharacterControllerNode:: can_jump() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _character->canJump(); } @@ -223,6 +240,7 @@ can_jump() const { */ void BulletCharacterControllerNode:: do_jump() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _character->jump(); } @@ -232,6 +250,7 @@ do_jump() { */ void BulletCharacterControllerNode:: set_fall_speed(PN_stdfloat fall_speed) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _character->setFallSpeed((btScalar)fall_speed); } @@ -241,6 +260,7 @@ set_fall_speed(PN_stdfloat fall_speed) { */ void BulletCharacterControllerNode:: set_jump_speed(PN_stdfloat jump_speed) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _character->setJumpSpeed((btScalar)jump_speed); } @@ -250,6 +270,7 @@ set_jump_speed(PN_stdfloat jump_speed) { */ void BulletCharacterControllerNode:: set_max_jump_height(PN_stdfloat max_jump_height) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _character->setMaxJumpHeight((btScalar)max_jump_height); } @@ -259,6 +280,7 @@ set_max_jump_height(PN_stdfloat max_jump_height) { */ void BulletCharacterControllerNode:: set_max_slope(PN_stdfloat max_slope) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _character->setMaxSlope((btScalar)max_slope); } @@ -268,6 +290,7 @@ set_max_slope(PN_stdfloat max_slope) { */ PN_stdfloat BulletCharacterControllerNode:: get_max_slope() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_character->getMaxSlope(); } @@ -277,6 +300,8 @@ get_max_slope() const { */ PN_stdfloat BulletCharacterControllerNode:: get_gravity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + #if BT_BULLET_VERSION >= 285 return -(PN_stdfloat)_character->getGravity()[_up]; #else @@ -289,6 +314,8 @@ get_gravity() const { */ void BulletCharacterControllerNode:: set_gravity(PN_stdfloat gravity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + #if BT_BULLET_VERSION >= 285 _character->setGravity(up_vectors[_up] * -(btScalar)gravity); #else @@ -301,6 +328,7 @@ set_gravity(PN_stdfloat gravity) { */ void BulletCharacterControllerNode:: set_use_ghost_sweep_test(bool value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); 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 1ced97ab9d..b2c43d8bb4 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); @@ -64,8 +64,8 @@ public: INLINE virtual btPairCachingGhostObject *get_ghost() const; INLINE virtual btCharacterControllerInterface *get_character() const; - virtual void sync_p2b(PN_stdfloat dt, int num_substeps); - virtual void sync_b2p(); + virtual void do_sync_p2b(PN_stdfloat dt, int num_substeps); + virtual void do_sync_b2p(); protected: virtual void transform_changed(); @@ -85,6 +85,8 @@ private: bool _linear_movement_is_local; PN_stdfloat _angular_movement; + void do_transform_changed(); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/bullet/bulletConeShape.I b/panda/src/bullet/bulletConeShape.I index 89198b8cf3..b9c05664b7 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) { +} + /** * */ @@ -21,35 +31,17 @@ INLINE BulletConeShape:: } /** - * - */ -INLINE BulletConeShape:: -BulletConeShape(const BulletConeShape ©) : - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletConeShape:: -operator = (const BulletConeShape ©) { - _shape = copy._shape; -} - -/** - * + * 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..30b24a5a18 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: @@ -36,9 +38,34 @@ BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) { break; } + nassertv(_shape); _shape->setUserPointer(this); } +/** + * + */ +BulletConeShape:: +BulletConeShape(const BulletConeShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _radius = copy._radius; + _height = copy._height; +} + +/** + * + */ +void BulletConeShape:: +operator = (const BulletConeShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _radius = copy._radius; + _height = copy._height; +} + /** * */ @@ -47,3 +74,80 @@ 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; + } + + nassertv(_shape); + _shape->setUserPointer(this); + _shape->setMargin(margin); +} diff --git a/panda/src/bullet/bulletConeShape.h b/panda/src/bullet/bulletConeShape.h index 97b99b01fe..4d40c61da9 100644 --- a/panda/src/bullet/bulletConeShape.h +++ b/panda/src/bullet/bulletConeShape.h @@ -24,11 +24,14 @@ * */ 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); - INLINE BulletConeShape(const BulletConeShape ©); - INLINE void operator = (const BulletConeShape ©); + explicit BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); + BulletConeShape(const BulletConeShape ©); + void operator = (const BulletConeShape ©); INLINE ~BulletConeShape(); INLINE PN_stdfloat get_radius() const; @@ -42,6 +45,16 @@ public: 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.I b/panda/src/bullet/bulletConeTwistConstraint.I index a29277462d..9f50b20783 100644 --- a/panda/src/bullet/bulletConeTwistConstraint.I +++ b/panda/src/bullet/bulletConeTwistConstraint.I @@ -19,21 +19,3 @@ INLINE BulletConeTwistConstraint:: delete _constraint; } - -/** - * - */ -INLINE CPT(TransformState) BulletConeTwistConstraint:: -get_frame_a() const { - - return btTrans_to_TransformState(_constraint->getAFrame()); -} - -/** - * - */ -INLINE CPT(TransformState) BulletConeTwistConstraint:: -get_frame_b() const { - - return btTrans_to_TransformState(_constraint->getBFrame()); -} diff --git a/panda/src/bullet/bulletConeTwistConstraint.cxx b/panda/src/bullet/bulletConeTwistConstraint.cxx index 93505b33fc..9b60ab31ce 100644 --- a/panda/src/bullet/bulletConeTwistConstraint.cxx +++ b/panda/src/bullet/bulletConeTwistConstraint.cxx @@ -63,6 +63,7 @@ ptr() const { */ void BulletConeTwistConstraint:: set_limit(int index, PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); value = deg_2_rad(value); @@ -74,6 +75,7 @@ set_limit(int index, PN_stdfloat value) { */ void BulletConeTwistConstraint:: set_limit(PN_stdfloat swing1, PN_stdfloat swing2, PN_stdfloat twist, PN_stdfloat softness, PN_stdfloat bias, PN_stdfloat relaxation) { + LightMutexHolder holder(BulletWorld::get_global_lock()); swing1 = deg_2_rad(swing1); swing2 = deg_2_rad(swing2); @@ -87,6 +89,7 @@ set_limit(PN_stdfloat swing1, PN_stdfloat swing2, PN_stdfloat twist, PN_stdfloat */ void BulletConeTwistConstraint:: set_damping(PN_stdfloat damping) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setDamping(damping); } @@ -96,6 +99,7 @@ set_damping(PN_stdfloat damping) { */ PN_stdfloat BulletConeTwistConstraint:: get_fix_threshold() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _constraint->getFixThresh(); } @@ -105,6 +109,7 @@ get_fix_threshold() const { */ void BulletConeTwistConstraint:: set_fix_threshold(PN_stdfloat threshold) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setFixThresh(threshold); } @@ -114,6 +119,7 @@ set_fix_threshold(PN_stdfloat threshold) { */ void BulletConeTwistConstraint:: enable_motor(bool enable) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->enableMotor(enable); } @@ -123,6 +129,7 @@ enable_motor(bool enable) { */ void BulletConeTwistConstraint:: set_max_motor_impulse(PN_stdfloat max_impulse) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMaxMotorImpulse(max_impulse); } @@ -132,6 +139,7 @@ set_max_motor_impulse(PN_stdfloat max_impulse) { */ void BulletConeTwistConstraint:: set_max_motor_impulse_normalized(PN_stdfloat max_impulse) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMaxMotorImpulseNormalized(max_impulse); } @@ -141,6 +149,7 @@ set_max_motor_impulse_normalized(PN_stdfloat max_impulse) { */ void BulletConeTwistConstraint:: set_motor_target(const LQuaternion &quat) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMotorTarget(LQuaternion_to_btQuat(quat)); } @@ -150,6 +159,7 @@ set_motor_target(const LQuaternion &quat) { */ void BulletConeTwistConstraint:: set_motor_target_in_constraint_space(const LQuaternion &quat) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMotorTargetInConstraintSpace(LQuaternion_to_btQuat(quat)); } @@ -159,9 +169,30 @@ set_motor_target_in_constraint_space(const LQuaternion &quat) { */ void BulletConeTwistConstraint:: set_frames(const TransformState *ts_a, const TransformState *ts_b) { + LightMutexHolder holder(BulletWorld::get_global_lock()); btTransform frame_a = TransformState_to_btTrans(ts_a); btTransform frame_b = TransformState_to_btTrans(ts_b); _constraint->setFrames(frame_a, frame_b); } + +/** + * + */ +CPT(TransformState) BulletConeTwistConstraint:: +get_frame_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getAFrame()); +} + +/** + * + */ +CPT(TransformState) BulletConeTwistConstraint:: +get_frame_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getBFrame()); +} diff --git a/panda/src/bullet/bulletConeTwistConstraint.h b/panda/src/bullet/bulletConeTwistConstraint.h index 7e5c782915..d7fabee508 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); @@ -53,8 +53,8 @@ PUBLISHED: void set_motor_target_in_constraint_space(const LQuaternion &quat); void set_frames(const TransformState *ts_a, const TransformState *ts_b); - INLINE CPT(TransformState) get_frame_a() const; - INLINE CPT(TransformState) get_frame_b() const; + CPT(TransformState) get_frame_a() const; + CPT(TransformState) get_frame_b() const; MAKE_PROPERTY(fix_threshold, get_fix_threshold, set_fix_threshold); MAKE_PROPERTY(frame_a, get_frame_a); diff --git a/panda/src/bullet/bulletConvexHullShape.I b/panda/src/bullet/bulletConvexHullShape.I index 71fa3cd190..2016b38e4d 100644 --- a/panda/src/bullet/bulletConvexHullShape.I +++ b/panda/src/bullet/bulletConvexHullShape.I @@ -19,19 +19,3 @@ INLINE BulletConvexHullShape:: delete _shape; } - -/** - * - */ -INLINE BulletConvexHullShape:: -BulletConvexHullShape(const BulletConvexHullShape ©) : - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletConvexHullShape:: -operator = (const BulletConvexHullShape ©) { - _shape = copy._shape; -} diff --git a/panda/src/bullet/bulletConvexHullShape.cxx b/panda/src/bullet/bulletConvexHullShape.cxx index 5b4494d330..c796072b4a 100644 --- a/panda/src/bullet/bulletConvexHullShape.cxx +++ b/panda/src/bullet/bulletConvexHullShape.cxx @@ -29,6 +29,26 @@ BulletConvexHullShape() { _shape->setUserPointer(this); } +/** + * + */ +BulletConvexHullShape:: +BulletConvexHullShape(const BulletConvexHullShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; +} + +/** + * + */ +void BulletConvexHullShape:: +operator = (const BulletConvexHullShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; +} + /** * */ @@ -43,6 +63,7 @@ ptr() const { */ void BulletConvexHullShape:: add_point(const LPoint3 &p) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _shape->addPoint(LVecBase3_to_btVector3(p)); } @@ -52,6 +73,10 @@ add_point(const LPoint3 &p) { */ void BulletConvexHullShape:: add_array(const PTA_LVecBase3 &points) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + if (_shape) + delete _shape; _shape = new btConvexHullShape(NULL, 0); _shape->setUserPointer(this); @@ -75,6 +100,7 @@ add_array(const PTA_LVecBase3 &points) { */ void BulletConvexHullShape:: add_geom(const Geom *geom, const TransformState *ts) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(geom); nassertv(ts); @@ -125,6 +151,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 +188,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/bulletConvexHullShape.h b/panda/src/bullet/bulletConvexHullShape.h index f74489bd0e..653c044952 100644 --- a/panda/src/bullet/bulletConvexHullShape.h +++ b/panda/src/bullet/bulletConvexHullShape.h @@ -29,8 +29,8 @@ class EXPCL_PANDABULLET BulletConvexHullShape : public BulletShape { PUBLISHED: BulletConvexHullShape(); - INLINE BulletConvexHullShape(const BulletConvexHullShape ©); - INLINE void operator = (const BulletConvexHullShape ©); + BulletConvexHullShape(const BulletConvexHullShape ©); + void operator = (const BulletConvexHullShape ©); INLINE ~BulletConvexHullShape(); void add_point(const LPoint3 &p); diff --git a/panda/src/bullet/bulletConvexPointCloudShape.I b/panda/src/bullet/bulletConvexPointCloudShape.I index aff49d4698..47e0b62704 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) { +} + /** * */ @@ -19,28 +28,3 @@ INLINE BulletConvexPointCloudShape:: delete _shape; } - -/** - * - */ -INLINE BulletConvexPointCloudShape:: -BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©) : - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletConvexPointCloudShape:: -operator = (const BulletConvexPointCloudShape ©) { - _shape = copy._shape; -} - -/** - * - */ -INLINE int BulletConvexPointCloudShape:: -get_num_points() const { - - return _shape->getNumPoints(); -} diff --git a/panda/src/bullet/bulletConvexPointCloudShape.cxx b/panda/src/bullet/bulletConvexPointCloudShape.cxx index 256357df8c..fb7f62287b 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,103 @@ BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale) { _shape = new btConvexPointCloudShape(btPoints, points.size(), btScale); _shape->setUserPointer(this); } + +/** + * + */ +BulletConvexPointCloudShape:: +BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _scale = copy._scale; + _shape = copy._shape; +} + +/** + * + */ +void BulletConvexPointCloudShape:: +operator = (const BulletConvexPointCloudShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _scale = copy._scale; + _shape = copy._shape; +} + +/** + * + */ +int BulletConvexPointCloudShape:: +get_num_points() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _shape->getNumPoints(); +} + +/** + * 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 bb5b5d2029..da403d8794 100644 --- a/panda/src/bullet/bulletConvexPointCloudShape.h +++ b/panda/src/bullet/bulletConvexPointCloudShape.h @@ -26,15 +26,18 @@ * */ 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.)); - INLINE BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©); - INLINE void operator = (const BulletConvexPointCloudShape ©); + explicit BulletConvexPointCloudShape(const PTA_LVecBase3 &points, LVecBase3 scale=LVecBase3(1.)); + explicit BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale=LVecBase3(1.)); + BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©); + void operator = (const BulletConvexPointCloudShape ©); INLINE ~BulletConvexPointCloudShape(); - INLINE int get_num_points() const; + int get_num_points() const; MAKE_PROPERTY(num_points, get_num_points); @@ -43,6 +46,15 @@ public: 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..54616905f9 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) { +} + /** * */ @@ -19,46 +28,3 @@ INLINE BulletCylinderShape:: delete _shape; } - -/** - * - */ -INLINE BulletCylinderShape:: -BulletCylinderShape(const BulletCylinderShape ©) : - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletCylinderShape:: -operator = (const BulletCylinderShape ©) { - _shape = copy._shape; -} - -/** - * - */ -INLINE PN_stdfloat BulletCylinderShape:: -get_radius() const { - - return (PN_stdfloat)_shape->getRadius(); -} - -/** - * - */ -INLINE LVecBase3 BulletCylinderShape:: -get_half_extents_without_margin() const { - - return btVector3_to_LVecBase3(_shape->getHalfExtentsWithoutMargin()); -} - -/** - * - */ -INLINE LVecBase3 BulletCylinderShape:: -get_half_extents_with_margin() const { - - return btVector3_to_LVecBase3(_shape->getHalfExtentsWithMargin()); -} diff --git a/panda/src/bullet/bulletCylinderShape.cxx b/panda/src/bullet/bulletCylinderShape.cxx index 1de1e5d5f0..8f95bc5591 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); @@ -38,6 +39,7 @@ BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up) { break; } + nassertv(_shape); _shape->setUserPointer(this); } @@ -50,21 +52,47 @@ 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; break; } + nassertv(_shape); _shape->setUserPointer(this); } +/** + * + */ +BulletCylinderShape:: +BulletCylinderShape(const BulletCylinderShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _half_extents = copy._half_extents; +} + +/** + * + */ +void BulletCylinderShape:: +operator = (const BulletCylinderShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _half_extents = copy._half_extents; +} + /** * */ @@ -73,3 +101,110 @@ ptr() const { return _shape; } + +/** + * + */ +PN_stdfloat BulletCylinderShape:: +get_radius() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_shape->getRadius(); +} + +/** + * + */ +LVecBase3 BulletCylinderShape:: +get_half_extents_without_margin() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVecBase3(_shape->getHalfExtentsWithoutMargin()); +} + +/** + * + */ +LVecBase3 BulletCylinderShape:: +get_half_extents_with_margin() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVecBase3(_shape->getHalfExtentsWithMargin()); +} + +/** + * 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; + } + + nassertv(_shape); + _shape->setUserPointer(this); + _shape->setMargin(margin); +} diff --git a/panda/src/bullet/bulletCylinderShape.h b/panda/src/bullet/bulletCylinderShape.h index 23c8781756..f53962ec6f 100644 --- a/panda/src/bullet/bulletCylinderShape.h +++ b/panda/src/bullet/bulletCylinderShape.h @@ -24,17 +24,20 @@ * */ 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); - INLINE BulletCylinderShape(const BulletCylinderShape ©); - INLINE void operator = (const BulletCylinderShape ©); + explicit BulletCylinderShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); + explicit BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up=Z_up); + BulletCylinderShape(const BulletCylinderShape ©); + void operator = (const BulletCylinderShape ©); INLINE ~BulletCylinderShape(); - INLINE PN_stdfloat get_radius() const; - INLINE LVecBase3 get_half_extents_without_margin() const; - INLINE LVecBase3 get_half_extents_with_margin() const; + PN_stdfloat get_radius() const; + LVecBase3 get_half_extents_without_margin() const; + LVecBase3 get_half_extents_with_margin() const; MAKE_PROPERTY(radius, get_radius); MAKE_PROPERTY(half_extents_without_margin, get_half_extents_without_margin); @@ -44,8 +47,17 @@ 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.cxx b/panda/src/bullet/bulletDebugNode.cxx index 196a9af09f..743b5163ff 100644 --- a/panda/src/bullet/bulletDebugNode.cxx +++ b/panda/src/bullet/bulletDebugNode.cxx @@ -13,21 +13,28 @@ #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 = false; + _debug_world = nullptr; _wireframe = true; _constraints = true; _bounds = false; @@ -37,40 +44,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 +147,133 @@ 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(BulletWorld::get_global_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) { +do_sync_b2p(btDynamicsWorld *world) { - 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 +436,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 +467,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 987e1a7450..c780429a79 100644 --- a/panda/src/bullet/bulletDebugNode.h +++ b/panda/src/bullet/bulletDebugNode.h @@ -18,15 +18,12 @@ #include "bullet_includes.h" -#include "geomNode.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(); @@ -53,8 +50,11 @@ 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); + void do_sync_b2p(btDynamicsWorld *world); struct Line { LVecBase3 _p0; @@ -102,12 +102,19 @@ private: 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); @@ -121,9 +128,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/bulletGenericConstraint.I b/panda/src/bullet/bulletGenericConstraint.I index 08ee24ccba..da9a6fbc49 100644 --- a/panda/src/bullet/bulletGenericConstraint.I +++ b/panda/src/bullet/bulletGenericConstraint.I @@ -19,21 +19,3 @@ INLINE BulletGenericConstraint:: delete _constraint; } - -/** - * - */ -INLINE CPT(TransformState) BulletGenericConstraint:: -get_frame_a() const { - - return btTrans_to_TransformState(_constraint->getFrameOffsetA()); -} - -/** - * - */ -INLINE CPT(TransformState) BulletGenericConstraint:: -get_frame_b() const { - - return btTrans_to_TransformState(_constraint->getFrameOffsetB()); -} diff --git a/panda/src/bullet/bulletGenericConstraint.cxx b/panda/src/bullet/bulletGenericConstraint.cxx index a68a345ce6..de98b5d40b 100644 --- a/panda/src/bullet/bulletGenericConstraint.cxx +++ b/panda/src/bullet/bulletGenericConstraint.cxx @@ -63,6 +63,7 @@ ptr() const { */ LVector3 BulletGenericConstraint:: get_axis(int axis) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertr(axis >= 0, LVector3::zero()); nassertr(axis <= 3, LVector3::zero()); @@ -76,6 +77,7 @@ get_axis(int axis) const { */ PN_stdfloat BulletGenericConstraint:: get_pivot(int axis) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertr(axis >= 0, 0.0f); nassertr(axis <= 3, 0.0f); @@ -89,6 +91,7 @@ get_pivot(int axis) const { */ PN_stdfloat BulletGenericConstraint:: get_angle(int axis) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertr(axis >= 0, 0.0f); nassertr(axis <= 3, 0.0f); @@ -102,6 +105,7 @@ get_angle(int axis) const { */ void BulletGenericConstraint:: set_linear_limit(int axis, PN_stdfloat low, PN_stdfloat high) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(axis >= 0); nassertv(axis <= 3); @@ -115,6 +119,7 @@ set_linear_limit(int axis, PN_stdfloat low, PN_stdfloat high) { */ void BulletGenericConstraint:: set_angular_limit(int axis, PN_stdfloat low, PN_stdfloat high) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(axis >= 0); nassertv(axis <= 3); @@ -126,11 +131,32 @@ set_angular_limit(int axis, PN_stdfloat low, PN_stdfloat high) { _constraint->setLimit(axis + 3, low, high); } +/** + * + */ +CPT(TransformState) BulletGenericConstraint:: +get_frame_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getFrameOffsetA()); +} + +/** + * + */ +CPT(TransformState) BulletGenericConstraint:: +get_frame_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getFrameOffsetB()); +} + /** * */ BulletRotationalLimitMotor BulletGenericConstraint:: get_rotational_limit_motor(int axis) { + LightMutexHolder holder(BulletWorld::get_global_lock()); return BulletRotationalLimitMotor(*_constraint->getRotationalLimitMotor(axis)); } @@ -140,6 +166,7 @@ get_rotational_limit_motor(int axis) { */ BulletTranslationalLimitMotor BulletGenericConstraint:: get_translational_limit_motor() { + LightMutexHolder holder(BulletWorld::get_global_lock()); return BulletTranslationalLimitMotor(*_constraint->getTranslationalLimitMotor()); } @@ -149,6 +176,7 @@ get_translational_limit_motor() { */ void BulletGenericConstraint:: set_frames(const TransformState *ts_a, const TransformState *ts_b) { + LightMutexHolder holder(BulletWorld::get_global_lock()); btTransform frame_a = TransformState_to_btTrans(ts_a); btTransform frame_b = TransformState_to_btTrans(ts_b); diff --git a/panda/src/bullet/bulletGenericConstraint.h b/panda/src/bullet/bulletGenericConstraint.h index 523330ca0f..f571df1ad6 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 @@ -58,8 +57,8 @@ PUBLISHED: // Frames void set_frames(const TransformState *ts_a, const TransformState *ts_b); - INLINE CPT(TransformState) get_frame_a() const; - INLINE CPT(TransformState) get_frame_b() const; + CPT(TransformState) get_frame_a() const; + CPT(TransformState) get_frame_b() const; MAKE_PROPERTY(translational_limit_motor, get_translational_limit_motor); MAKE_PROPERTY(frame_a, get_frame_a); diff --git a/panda/src/bullet/bulletGhostNode.I b/panda/src/bullet/bulletGhostNode.I index c4620097a8..d9b24a0950 100644 --- a/panda/src/bullet/bulletGhostNode.I +++ b/panda/src/bullet/bulletGhostNode.I @@ -20,23 +20,3 @@ INLINE BulletGhostNode:: delete _ghost; } -/** - * - */ -INLINE int BulletGhostNode:: -get_num_overlapping_nodes() const { - - return _ghost->getNumOverlappingObjects(); -} - -/** - * - */ -INLINE PandaNode *BulletGhostNode:: -get_overlapping_node(int idx) const { - - nassertr(idx >=0 && idx < _ghost->getNumOverlappingObjects(), NULL); - - btCollisionObject *object = _ghost->getOverlappingObject(idx); - return (object) ? (PandaNode *)object->getUserPointer() : NULL; -} diff --git a/panda/src/bullet/bulletGhostNode.cxx b/panda/src/bullet/bulletGhostNode.cxx index ddead41067..39740ff5a1 100644 --- a/panda/src/bullet/bulletGhostNode.cxx +++ b/panda/src/bullet/bulletGhostNode.cxx @@ -53,6 +53,7 @@ get_object() const { */ void BulletGhostNode:: parents_changed() { + LightMutexHolder holder(BulletWorld::get_global_lock()); Parents parents = get_parents(); for (size_t i = 0; i < parents.get_num_parents(); ++i) { @@ -73,10 +74,10 @@ parents_changed() { } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletGhostNode:: -transform_changed() { +do_transform_changed() { if (_sync_disable) return; @@ -98,27 +99,60 @@ transform_changed() { if (!scale.almost_equal(LVecBase3(1.0f, 1.0f, 1.0f))) { for (int i=0; iset_local_scale(scale); + shape->do_set_local_scale(scale); } } } } } -/** - * - */ void BulletGhostNode:: -sync_p2b() { +transform_changed() { - transform_changed(); + if (_sync_disable) return; + + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_transform_changed(); } /** * */ +int BulletGhostNode:: +get_num_overlapping_nodes() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _ghost->getNumOverlappingObjects(); +} + +/** + * + */ +PandaNode *BulletGhostNode:: +get_overlapping_node(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr(idx >=0 && idx < _ghost->getNumOverlappingObjects(), NULL); + + btCollisionObject *object = _ghost->getOverlappingObject(idx); + return (object) ? (PandaNode *)object->getUserPointer() : NULL; +} + +/** + * Assumes the lock(bullet global lock) is held by the caller + */ void BulletGhostNode:: -sync_b2p() { +do_sync_p2b() { + + do_transform_changed(); +} + +/** + * Assumes the lock(bullet global lock) is held by the caller + */ +void BulletGhostNode:: +do_sync_b2p() { NodePath np = NodePath::any_path((PandaNode *)this); LVecBase3 scale = np.get_net_transform()->get_scale(); diff --git a/panda/src/bullet/bulletGhostNode.h b/panda/src/bullet/bulletGhostNode.h index 2cf5f73f21..a76c0ce8a1 100644 --- a/panda/src/bullet/bulletGhostNode.h +++ b/panda/src/bullet/bulletGhostNode.h @@ -29,14 +29,13 @@ 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; + int get_num_overlapping_nodes() const; + 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); @@ -44,8 +43,8 @@ PUBLISHED: public: virtual btCollisionObject *get_object() const; - void sync_p2b(); - void sync_b2p(); + void do_sync_p2b(); + void do_sync_b2p(); protected: virtual void parents_changed(); @@ -58,6 +57,8 @@ private: btPairCachingGhostObject *_ghost; + void do_transform_changed(); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/bullet/bulletHeightfieldShape.I b/panda/src/bullet/bulletHeightfieldShape.I index 6607443110..5a2702be9a 100644 --- a/panda/src/bullet/bulletHeightfieldShape.I +++ b/panda/src/bullet/bulletHeightfieldShape.I @@ -11,39 +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; } - -/** - * - */ -INLINE BulletHeightfieldShape:: -BulletHeightfieldShape(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)); -} - -/** - * - */ -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)); -} diff --git a/panda/src/bullet/bulletHeightfieldShape.cxx b/panda/src/bullet/bulletHeightfieldShape.cxx index f6843d199b..1a01876401 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++) { @@ -60,6 +61,7 @@ ptr() const { */ void BulletHeightfieldShape:: set_use_diamond_subdivision(bool flag) { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _shape->setUseDiamondSubdivision(flag); } @@ -71,14 +73,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 +103,122 @@ BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up) { up, true, false); _shape->setUserPointer(this); -} \ No newline at end of file +} + +/** + * + */ +BulletHeightfieldShape:: +BulletHeightfieldShape(const BulletHeightfieldShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _num_rows = copy._num_rows; + _num_cols = copy._num_cols; + _max_height = copy._max_height; + _up = copy._up; + + size_t size = (size_t)_num_rows * (size_t)_num_cols; + _data = new btScalar[size]; + memcpy(_data, copy._data, size * sizeof(btScalar)); +} + +/** + * + */ +void BulletHeightfieldShape:: +operator = (const BulletHeightfieldShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _num_rows = copy._num_rows; + _num_cols = copy._num_cols; + _max_height = copy._max_height; + _up = copy._up; + + size_t size = (size_t)_num_rows * (size_t)_num_cols; + _data = new btScalar[size]; + memcpy(_data, copy._data, size * sizeof(btScalar)); +} + +/** + * 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..1bfc6c41c3 100644 --- a/panda/src/bullet/bulletHeightfieldShape.h +++ b/panda/src/bullet/bulletHeightfieldShape.h @@ -28,12 +28,14 @@ * */ 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); - INLINE BulletHeightfieldShape(const BulletHeightfieldShape ©); - INLINE void operator = (const BulletHeightfieldShape ©); + 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); + BulletHeightfieldShape(const BulletHeightfieldShape ©); + void operator = (const BulletHeightfieldShape ©); INLINE ~BulletHeightfieldShape(); void set_use_diamond_subdivision(bool flag=true); @@ -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/bulletHingeConstraint.I b/panda/src/bullet/bulletHingeConstraint.I index 01614f603a..e5753a5947 100644 --- a/panda/src/bullet/bulletHingeConstraint.I +++ b/panda/src/bullet/bulletHingeConstraint.I @@ -19,21 +19,3 @@ INLINE BulletHingeConstraint:: delete _constraint; } - -/** - * - */ -INLINE CPT(TransformState) BulletHingeConstraint:: -get_frame_a() const { - - return btTrans_to_TransformState(_constraint->getAFrame()); -} - -/** - * - */ -INLINE CPT(TransformState) BulletHingeConstraint:: -get_frame_b() const { - - return btTrans_to_TransformState(_constraint->getBFrame()); -} diff --git a/panda/src/bullet/bulletHingeConstraint.cxx b/panda/src/bullet/bulletHingeConstraint.cxx index a262be88e8..5cbfa44d94 100644 --- a/panda/src/bullet/bulletHingeConstraint.cxx +++ b/panda/src/bullet/bulletHingeConstraint.cxx @@ -111,6 +111,7 @@ ptr() const { */ void BulletHingeConstraint:: set_angular_only(bool value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _constraint->setAngularOnly(value); } @@ -120,6 +121,7 @@ set_angular_only(bool value) { */ bool BulletHingeConstraint:: get_angular_only() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _constraint->getAngularOnly(); } @@ -129,6 +131,7 @@ get_angular_only() const { */ void BulletHingeConstraint:: set_limit(PN_stdfloat low, PN_stdfloat high, PN_stdfloat softness, PN_stdfloat bias, PN_stdfloat relaxation) { + LightMutexHolder holder(BulletWorld::get_global_lock()); low = deg_2_rad(low); high = deg_2_rad(high); @@ -141,6 +144,7 @@ set_limit(PN_stdfloat low, PN_stdfloat high, PN_stdfloat softness, PN_stdfloat b */ void BulletHingeConstraint:: set_axis(const LVector3 &axis) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!axis.is_nan()); @@ -153,6 +157,7 @@ set_axis(const LVector3 &axis) { */ PN_stdfloat BulletHingeConstraint:: get_lower_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return rad_2_deg(_constraint->getLowerLimit()); } @@ -162,6 +167,7 @@ get_lower_limit() const { */ PN_stdfloat BulletHingeConstraint:: get_upper_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return rad_2_deg(_constraint->getUpperLimit()); } @@ -171,6 +177,7 @@ get_upper_limit() const { */ PN_stdfloat BulletHingeConstraint:: get_hinge_angle() { + LightMutexHolder holder(BulletWorld::get_global_lock()); return rad_2_deg(_constraint->getHingeAngle()); } @@ -184,6 +191,7 @@ get_hinge_angle() { */ void BulletHingeConstraint:: enable_angular_motor(bool enable, PN_stdfloat target_velocity, PN_stdfloat max_impulse) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->enableAngularMotor(enable, target_velocity, max_impulse); } @@ -193,6 +201,7 @@ enable_angular_motor(bool enable, PN_stdfloat target_velocity, PN_stdfloat max_i */ void BulletHingeConstraint:: enable_motor(bool enable) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->enableMotor(enable); } @@ -203,6 +212,7 @@ enable_motor(bool enable) { */ void BulletHingeConstraint:: set_max_motor_impulse(PN_stdfloat max_impulse) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMaxMotorImpulse(max_impulse); } @@ -212,6 +222,7 @@ set_max_motor_impulse(PN_stdfloat max_impulse) { */ void BulletHingeConstraint:: set_motor_target(const LQuaternion &quat, PN_stdfloat dt) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMotorTarget(LQuaternion_to_btQuat(quat), dt); } @@ -221,6 +232,7 @@ set_motor_target(const LQuaternion &quat, PN_stdfloat dt) { */ void BulletHingeConstraint:: set_motor_target(PN_stdfloat target_angle, PN_stdfloat dt) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMotorTarget(target_angle, dt); } @@ -230,9 +242,30 @@ set_motor_target(PN_stdfloat target_angle, PN_stdfloat dt) { */ void BulletHingeConstraint:: set_frames(const TransformState *ts_a, const TransformState *ts_b) { + LightMutexHolder holder(BulletWorld::get_global_lock()); btTransform frame_a = TransformState_to_btTrans(ts_a); btTransform frame_b = TransformState_to_btTrans(ts_b); _constraint->setFrames(frame_a, frame_b); } + +/** + * + */ +CPT(TransformState) BulletHingeConstraint:: +get_frame_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getAFrame()); +} + +/** + * + */ +CPT(TransformState) BulletHingeConstraint:: +get_frame_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getBFrame()); +} diff --git a/panda/src/bullet/bulletHingeConstraint.h b/panda/src/bullet/bulletHingeConstraint.h index 81cf0b2674..dc6768490b 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(); @@ -70,8 +69,8 @@ PUBLISHED: void set_motor_target(PN_stdfloat target_angle, PN_stdfloat dt); void set_frames(const TransformState *ts_a, const TransformState *ts_b); - INLINE CPT(TransformState) get_frame_a() const; - INLINE CPT(TransformState) get_frame_b() const; + CPT(TransformState) get_frame_a() const; + CPT(TransformState) get_frame_b() const; MAKE_PROPERTY(hinge_angle, get_hinge_angle); MAKE_PROPERTY(lower_limit, get_lower_limit); diff --git a/panda/src/bullet/bulletManifoldPoint.I b/panda/src/bullet/bulletManifoldPoint.I index 6730d2f349..4347bad4f4 100644 --- a/panda/src/bullet/bulletManifoldPoint.I +++ b/panda/src/bullet/bulletManifoldPoint.I @@ -18,228 +18,3 @@ INLINE BulletManifoldPoint:: ~BulletManifoldPoint() { } - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_lateral_friction_initialized(bool value) { -#if BT_BULLET_VERSION >= 285 - if (value) { - _pt.m_contactPointFlags |= BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED; - } else { - _pt.m_contactPointFlags &= ~BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED; - } -#else - _pt.m_lateralFrictionInitialized = value; -#endif -} - -/** - * - */ -INLINE bool BulletManifoldPoint:: -get_lateral_friction_initialized() const { -#if BT_BULLET_VERSION >= 285 - return (_pt.m_contactPointFlags & BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED) != 0; -#else - return _pt.m_lateralFrictionInitialized; -#endif -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_lateral_friction_dir1(const LVecBase3 &dir) { - - _pt.m_lateralFrictionDir1 = LVecBase3_to_btVector3(dir); -} - -/** - * - */ -INLINE LVector3 BulletManifoldPoint:: -get_lateral_friction_dir1() const { - - return btVector3_to_LVector3(_pt.m_lateralFrictionDir1); -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_lateral_friction_dir2(const LVecBase3 &dir) { - - _pt.m_lateralFrictionDir2 = LVecBase3_to_btVector3(dir); -} - -/** - * - */ -INLINE LVector3 BulletManifoldPoint:: -get_lateral_friction_dir2() const { - - return btVector3_to_LVector3(_pt.m_lateralFrictionDir2); -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_contact_motion1(PN_stdfloat value) { - - _pt.m_contactMotion1 = (btScalar)value; -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_contact_motion1() const { - - return (PN_stdfloat)_pt.m_contactMotion1; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_contact_motion2(PN_stdfloat value) { - - _pt.m_contactMotion2 = (btScalar)value; -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_contact_motion2() const { - - return (PN_stdfloat)_pt.m_contactMotion2; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_combined_friction(PN_stdfloat value) { - - _pt.m_combinedFriction = (btScalar)value; -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_combined_friction() const { - - return (PN_stdfloat)_pt.m_combinedFriction; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_combined_restitution(PN_stdfloat value) { - - _pt.m_combinedRestitution = (btScalar)value; -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_combined_restitution() const { - - return (PN_stdfloat)_pt.m_combinedRestitution; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_applied_impulse(PN_stdfloat value) { - - _pt.m_appliedImpulse = (btScalar)value; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_applied_impulse_lateral1(PN_stdfloat value) { - - _pt.m_appliedImpulseLateral1 = (btScalar)value; -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_applied_impulse_lateral1() const { - - return (PN_stdfloat)_pt.m_appliedImpulseLateral1; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_applied_impulse_lateral2(PN_stdfloat value) { - - _pt.m_appliedImpulseLateral2 = (btScalar)value; -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_applied_impulse_lateral2() const { - - return (PN_stdfloat)_pt.m_appliedImpulseLateral2; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_contact_cfm1(PN_stdfloat value) { -#if BT_BULLET_VERSION < 285 - _pt.m_contactCFM1 = (btScalar)value; -#endif -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_contact_cfm1() const { -#if BT_BULLET_VERSION < 285 - return (PN_stdfloat)_pt.m_contactCFM1; -#else - return 0; -#endif -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_contact_cfm2(PN_stdfloat value) { -#if BT_BULLET_VERSION < 285 - _pt.m_contactCFM2 = (btScalar)value; -#endif -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_contact_cfm2() const { -#if BT_BULLET_VERSION < 285 - return (PN_stdfloat)_pt.m_contactCFM2; -#else - return 0; -#endif -} diff --git a/panda/src/bullet/bulletManifoldPoint.cxx b/panda/src/bullet/bulletManifoldPoint.cxx index 00651186f0..b56244ed29 100644 --- a/panda/src/bullet/bulletManifoldPoint.cxx +++ b/panda/src/bullet/bulletManifoldPoint.cxx @@ -46,6 +46,7 @@ operator=(const BulletManifoldPoint& other) { */ int BulletManifoldPoint:: get_life_time() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _pt.getLifeTime(); } @@ -55,6 +56,7 @@ get_life_time() const { */ PN_stdfloat BulletManifoldPoint:: get_distance() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_pt.getDistance(); } @@ -64,6 +66,7 @@ get_distance() const { */ PN_stdfloat BulletManifoldPoint:: get_applied_impulse() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_pt.getAppliedImpulse(); } @@ -73,6 +76,7 @@ get_applied_impulse() const { */ LPoint3 BulletManifoldPoint:: get_position_world_on_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_pt.getPositionWorldOnA()); } @@ -82,6 +86,7 @@ get_position_world_on_a() const { */ LPoint3 BulletManifoldPoint:: get_position_world_on_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_pt.getPositionWorldOnB()); } @@ -91,6 +96,7 @@ get_position_world_on_b() const { */ LVector3 BulletManifoldPoint:: get_normal_world_on_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_pt.m_normalWorldOnB); } @@ -100,6 +106,7 @@ get_normal_world_on_b() const { */ LPoint3 BulletManifoldPoint:: get_local_point_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_pt.m_localPointA); } @@ -109,6 +116,7 @@ get_local_point_a() const { */ LPoint3 BulletManifoldPoint:: get_local_point_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_pt.m_localPointB); } @@ -118,6 +126,7 @@ get_local_point_b() const { */ int BulletManifoldPoint:: get_part_id0() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _pt.m_partId0; } @@ -127,6 +136,7 @@ get_part_id0() const { */ int BulletManifoldPoint:: get_part_id1() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _pt.m_partId1; } @@ -136,6 +146,7 @@ get_part_id1() const { */ int BulletManifoldPoint:: get_index0() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _pt.m_index0; } @@ -145,6 +156,261 @@ get_index0() const { */ int BulletManifoldPoint:: get_index1() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _pt.m_index1; } + +/** + * + */ +void BulletManifoldPoint:: +set_lateral_friction_initialized(bool value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + +#if BT_BULLET_VERSION >= 285 + if (value) { + _pt.m_contactPointFlags |= BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED; + } else { + _pt.m_contactPointFlags &= ~BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED; + } +#else + _pt.m_lateralFrictionInitialized = value; +#endif +} + +/** + * + */ +bool BulletManifoldPoint:: +get_lateral_friction_initialized() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + +#if BT_BULLET_VERSION >= 285 + return (_pt.m_contactPointFlags & BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED) != 0; +#else + return _pt.m_lateralFrictionInitialized; +#endif +} + +/** + * + */ +void BulletManifoldPoint:: +set_lateral_friction_dir1(const LVecBase3 &dir) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_lateralFrictionDir1 = LVecBase3_to_btVector3(dir); +} + +/** + * + */ +LVector3 BulletManifoldPoint:: +get_lateral_friction_dir1() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_pt.m_lateralFrictionDir1); +} + +/** + * + */ +void BulletManifoldPoint:: +set_lateral_friction_dir2(const LVecBase3 &dir) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_lateralFrictionDir2 = LVecBase3_to_btVector3(dir); +} + +/** + * + */ +LVector3 BulletManifoldPoint:: +get_lateral_friction_dir2() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_pt.m_lateralFrictionDir2); +} + +/** + * + */ +void BulletManifoldPoint:: +set_contact_motion1(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_contactMotion1 = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_contact_motion1() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_pt.m_contactMotion1; +} + +/** + * + */ +void BulletManifoldPoint:: +set_contact_motion2(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_contactMotion2 = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_contact_motion2() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_pt.m_contactMotion2; +} + +/** + * + */ +void BulletManifoldPoint:: +set_combined_friction(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_combinedFriction = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_combined_friction() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_pt.m_combinedFriction; +} + +/** + * + */ +void BulletManifoldPoint:: +set_combined_restitution(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_combinedRestitution = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_combined_restitution() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_pt.m_combinedRestitution; +} + +/** + * + */ +void BulletManifoldPoint:: +set_applied_impulse(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_appliedImpulse = (btScalar)value; +} + +/** + * + */ +void BulletManifoldPoint:: +set_applied_impulse_lateral1(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_appliedImpulseLateral1 = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_applied_impulse_lateral1() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_pt.m_appliedImpulseLateral1; +} + +/** + * + */ +void BulletManifoldPoint:: +set_applied_impulse_lateral2(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_appliedImpulseLateral2 = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_applied_impulse_lateral2() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_pt.m_appliedImpulseLateral2; +} + +/** + * + */ +void BulletManifoldPoint:: +set_contact_cfm1(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + +#if BT_BULLET_VERSION < 285 + _pt.m_contactCFM1 = (btScalar)value; +#endif +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_contact_cfm1() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + +#if BT_BULLET_VERSION < 285 + return (PN_stdfloat)_pt.m_contactCFM1; +#else + return 0; +#endif +} + +/** + * + */ +void BulletManifoldPoint:: +set_contact_cfm2(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + +#if BT_BULLET_VERSION < 285 + _pt.m_contactCFM2 = (btScalar)value; +#endif +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_contact_cfm2() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + +#if BT_BULLET_VERSION < 285 + return (PN_stdfloat)_pt.m_contactCFM2; +#else + return 0; +#endif +} diff --git a/panda/src/bullet/bulletManifoldPoint.h b/panda/src/bullet/bulletManifoldPoint.h index a7bc271599..bc4c0f9486 100644 --- a/panda/src/bullet/bulletManifoldPoint.h +++ b/panda/src/bullet/bulletManifoldPoint.h @@ -43,30 +43,30 @@ PUBLISHED: int get_index0() const; int get_index1() const; - INLINE void set_lateral_friction_initialized(bool value); - INLINE void set_lateral_friction_dir1(const LVecBase3 &dir); - INLINE void set_lateral_friction_dir2(const LVecBase3 &dir); - INLINE void set_contact_motion1(PN_stdfloat value); - INLINE void set_contact_motion2(PN_stdfloat value); - INLINE void set_combined_friction(PN_stdfloat value); - INLINE void set_combined_restitution(PN_stdfloat value); - INLINE void set_applied_impulse(PN_stdfloat value); - INLINE void set_applied_impulse_lateral1(PN_stdfloat value); - INLINE void set_applied_impulse_lateral2(PN_stdfloat value); - INLINE void set_contact_cfm1(PN_stdfloat value); - INLINE void set_contact_cfm2(PN_stdfloat value); + void set_lateral_friction_initialized(bool value); + void set_lateral_friction_dir1(const LVecBase3 &dir); + void set_lateral_friction_dir2(const LVecBase3 &dir); + void set_contact_motion1(PN_stdfloat value); + void set_contact_motion2(PN_stdfloat value); + void set_combined_friction(PN_stdfloat value); + void set_combined_restitution(PN_stdfloat value); + void set_applied_impulse(PN_stdfloat value); + void set_applied_impulse_lateral1(PN_stdfloat value); + void set_applied_impulse_lateral2(PN_stdfloat value); + void set_contact_cfm1(PN_stdfloat value); + void set_contact_cfm2(PN_stdfloat value); - INLINE bool get_lateral_friction_initialized() const; - INLINE LVector3 get_lateral_friction_dir1() const; - INLINE LVector3 get_lateral_friction_dir2() const; - INLINE PN_stdfloat get_contact_motion1() const; - INLINE PN_stdfloat get_contact_motion2() const; - INLINE PN_stdfloat get_combined_friction() const; - INLINE PN_stdfloat get_combined_restitution() const; - INLINE PN_stdfloat get_applied_impulse_lateral1() const; - INLINE PN_stdfloat get_applied_impulse_lateral2() const; - INLINE PN_stdfloat get_contact_cfm1() const; - INLINE PN_stdfloat get_contact_cfm2() const; + bool get_lateral_friction_initialized() const; + LVector3 get_lateral_friction_dir1() const; + LVector3 get_lateral_friction_dir2() const; + PN_stdfloat get_contact_motion1() const; + PN_stdfloat get_contact_motion2() const; + PN_stdfloat get_combined_friction() const; + PN_stdfloat get_combined_restitution() const; + PN_stdfloat get_applied_impulse_lateral1() const; + PN_stdfloat get_applied_impulse_lateral2() const; + PN_stdfloat get_contact_cfm1() const; + PN_stdfloat get_contact_cfm2() const; MAKE_PROPERTY(life_time, get_life_time); MAKE_PROPERTY(distance, get_distance); diff --git a/panda/src/bullet/bulletMinkowskiSumShape.I b/panda/src/bullet/bulletMinkowskiSumShape.I index 07f96a74fb..b99d0c7561 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) { +} + /** * */ @@ -20,64 +30,6 @@ INLINE BulletMinkowskiSumShape:: delete _shape; } -/** - * - */ -INLINE BulletMinkowskiSumShape:: -BulletMinkowskiSumShape(const BulletMinkowskiSumShape ©) : - _shape(copy._shape), - _shape_a(copy._shape_a), - _shape_b(copy._shape_b) { -} - -/** - * - */ -INLINE void BulletMinkowskiSumShape:: -operator = (const BulletMinkowskiSumShape ©) { - _shape = copy._shape; - _shape_a = copy._shape_a; - _shape_b = copy._shape_b; -} - -/** - * - */ -INLINE void BulletMinkowskiSumShape:: -set_transform_a(const TransformState *ts) { - - nassertv(ts); - _shape->setTransformA(TransformState_to_btTrans(ts)); -} - -/** - * - */ -INLINE void BulletMinkowskiSumShape:: -set_transform_b(const TransformState *ts) { - - nassertv(ts); - _shape->setTransformB(TransformState_to_btTrans(ts)); -} - -/** - * - */ -INLINE CPT(TransformState) BulletMinkowskiSumShape:: -get_transform_a() const { - - return btTrans_to_TransformState(_shape->getTransformA()); -} - -/** - * - */ -INLINE CPT(TransformState) BulletMinkowskiSumShape:: -get_transform_b() const { - - return btTrans_to_TransformState(_shape->GetTransformB()); -} - /** * */ @@ -95,12 +47,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..cc56d39884 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,30 @@ 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; +/** + * + */ +BulletMinkowskiSumShape:: +BulletMinkowskiSumShape(const BulletMinkowskiSumShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _shape_a = copy._shape_a; + _shape_b = copy._shape_b; +} + +/** + * + */ +void BulletMinkowskiSumShape:: +operator = (const BulletMinkowskiSumShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _shape_a = copy._shape_a; + _shape_b = copy._shape_b; } /** @@ -42,3 +65,144 @@ ptr() const { return _shape; } + +/** + * + */ +void BulletMinkowskiSumShape:: +set_transform_a(const TransformState *ts) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(ts); + _shape->setTransformA(TransformState_to_btTrans(ts)); +} + +/** + * + */ +void BulletMinkowskiSumShape:: +set_transform_b(const TransformState *ts) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(ts); + _shape->setTransformB(TransformState_to_btTrans(ts)); +} + +/** + * + */ +CPT(TransformState) BulletMinkowskiSumShape:: +get_transform_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_shape->getTransformA()); +} + +/** + * + */ +CPT(TransformState) BulletMinkowskiSumShape:: +get_transform_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_shape->GetTransformB()); +} + +/** + * 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 dd148ce335..c8629f2e33 100644 --- a/panda/src/bullet/bulletMinkowskiSumShape.h +++ b/panda/src/bullet/bulletMinkowskiSumShape.h @@ -26,28 +26,28 @@ * */ 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); - INLINE BulletMinkowskiSumShape(const BulletMinkowskiSumShape ©); - INLINE void operator = (const BulletMinkowskiSumShape ©); + explicit BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b); + BulletMinkowskiSumShape(const BulletMinkowskiSumShape ©); + void operator = (const BulletMinkowskiSumShape ©); INLINE ~BulletMinkowskiSumShape(); - INLINE void set_transform_a(const TransformState *ts); - INLINE void set_transform_b(const TransformState *ts); - INLINE CPT(TransformState) get_transform_a() const; - INLINE CPT(TransformState) get_transform_b() const; + void set_transform_a(const TransformState *ts); + void set_transform_b(const TransformState *ts); + CPT(TransformState) get_transform_a() const; + CPT(TransformState) get_transform_b() const; 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); - MAKE_PROPERTY(margin, get_margin); public: virtual btCollisionShape *ptr() const; @@ -58,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.I b/panda/src/bullet/bulletMultiSphereShape.I index 1024dce7dc..e4f95c7bf1 100644 --- a/panda/src/bullet/bulletMultiSphereShape.I +++ b/panda/src/bullet/bulletMultiSphereShape.I @@ -19,48 +19,3 @@ INLINE BulletMultiSphereShape:: delete _shape; } - -/** - * - */ -INLINE BulletMultiSphereShape:: -BulletMultiSphereShape(const BulletMultiSphereShape ©) : - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletMultiSphereShape:: -operator = (const BulletMultiSphereShape ©) { - _shape = copy._shape; -} - -/** - * - */ -INLINE int BulletMultiSphereShape:: -get_sphere_count() const { - - return _shape->getSphereCount(); -} - -/** - * - */ -INLINE LPoint3 BulletMultiSphereShape:: -get_sphere_pos(int index) const { - - nassertr(index >=0 && index <_shape->getSphereCount(), LPoint3::zero()); - return btVector3_to_LPoint3(_shape->getSpherePosition(index)); -} - -/** - * - */ -INLINE PN_stdfloat BulletMultiSphereShape:: -get_sphere_radius(int index) const { - - nassertr(index >=0 && index <_shape->getSphereCount(), 0.0); - return (PN_stdfloat)_shape->getSphereRadius(index); -} diff --git a/panda/src/bullet/bulletMultiSphereShape.cxx b/panda/src/bullet/bulletMultiSphereShape.cxx index 52e2f16c7d..ffaed8bafb 100644 --- a/panda/src/bullet/bulletMultiSphereShape.cxx +++ b/panda/src/bullet/bulletMultiSphereShape.cxx @@ -42,6 +42,26 @@ BulletMultiSphereShape(const PTA_LVecBase3 &points, const PTA_stdfloat &radii) { _shape->setUserPointer(this); } +/** + * + */ +BulletMultiSphereShape:: +BulletMultiSphereShape(const BulletMultiSphereShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; +} + +/** + * + */ +void BulletMultiSphereShape:: +operator = (const BulletMultiSphereShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; +} + /** * */ @@ -50,3 +70,111 @@ ptr() const { return _shape; } + +/** + * + */ +int BulletMultiSphereShape:: +get_sphere_count() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _shape->getSphereCount(); +} + +/** + * + */ +LPoint3 BulletMultiSphereShape:: +get_sphere_pos(int index) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr(index >=0 && index <_shape->getSphereCount(), LPoint3::zero()); + return btVector3_to_LPoint3(_shape->getSpherePosition(index)); +} + +/** + * + */ +PN_stdfloat BulletMultiSphereShape:: +get_sphere_radius(int index) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr(index >=0 && index <_shape->getSphereCount(), 0.0); + return (PN_stdfloat)_shape->getSphereRadius(index); +} + +/** + * 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 2fb9c6c3a0..d6fd7af7b7 100644 --- a/panda/src/bullet/bulletMultiSphereShape.h +++ b/panda/src/bullet/bulletMultiSphereShape.h @@ -26,16 +26,18 @@ * */ class EXPCL_PANDABULLET BulletMultiSphereShape : public BulletShape { +private: + BulletMultiSphereShape() : _shape(nullptr) {} PUBLISHED: - BulletMultiSphereShape(const PTA_LVecBase3 &points, const PTA_stdfloat &radii); - INLINE BulletMultiSphereShape(const BulletMultiSphereShape ©); - INLINE void operator = (const BulletMultiSphereShape ©); + explicit BulletMultiSphereShape(const PTA_LVecBase3 &points, const PTA_stdfloat &radii); + BulletMultiSphereShape(const BulletMultiSphereShape ©); + void operator = (const BulletMultiSphereShape ©); INLINE ~BulletMultiSphereShape(); - INLINE int get_sphere_count() const; - INLINE LPoint3 get_sphere_pos(int index) const; - INLINE PN_stdfloat get_sphere_radius(int index) const; + int get_sphere_count() const; + LPoint3 get_sphere_pos(int index) const; + 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); @@ -47,6 +49,14 @@ public: 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.cxx b/panda/src/bullet/bulletPersistentManifold.cxx index 3a4024f8d2..e42ea3c6f8 100644 --- a/panda/src/bullet/bulletPersistentManifold.cxx +++ b/panda/src/bullet/bulletPersistentManifold.cxx @@ -27,6 +27,7 @@ BulletPersistentManifold(btPersistentManifold *manifold) : _manifold(manifold) { */ PN_stdfloat BulletPersistentManifold:: get_contact_breaking_threshold() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_manifold->getContactBreakingThreshold(); } @@ -36,6 +37,7 @@ get_contact_breaking_threshold() const { */ PN_stdfloat BulletPersistentManifold:: get_contact_processing_threshold() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_manifold->getContactProcessingThreshold(); } @@ -45,6 +47,7 @@ get_contact_processing_threshold() const { */ void BulletPersistentManifold:: clear_manifold() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _manifold->clearManifold(); } @@ -54,6 +57,7 @@ clear_manifold() { */ PandaNode *BulletPersistentManifold:: get_node0() { + LightMutexHolder holder(BulletWorld::get_global_lock()); #if BT_BULLET_VERSION >= 281 const btCollisionObject *obj = _manifold->getBody0(); @@ -69,6 +73,7 @@ get_node0() { */ PandaNode *BulletPersistentManifold:: get_node1() { + LightMutexHolder holder(BulletWorld::get_global_lock()); #if BT_BULLET_VERSION >= 281 const btCollisionObject *obj = _manifold->getBody1(); @@ -84,6 +89,7 @@ get_node1() { */ int BulletPersistentManifold:: get_num_manifold_points() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _manifold->getNumContacts(); } @@ -93,6 +99,7 @@ get_num_manifold_points() const { */ BulletManifoldPoint *BulletPersistentManifold:: get_manifold_point(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertr(idx < _manifold->getNumContacts(), NULL) diff --git a/panda/src/bullet/bulletPlaneShape.I b/panda/src/bullet/bulletPlaneShape.I index f6a4c1e752..0b080ab986 100644 --- a/panda/src/bullet/bulletPlaneShape.I +++ b/panda/src/bullet/bulletPlaneShape.I @@ -19,37 +19,3 @@ INLINE BulletPlaneShape:: delete _shape; } - -/** - * - */ -INLINE BulletPlaneShape:: -BulletPlaneShape(const BulletPlaneShape ©) : - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletPlaneShape:: -operator = (const BulletPlaneShape ©) { - _shape = copy._shape; -} - -/** - * - */ -INLINE PN_stdfloat BulletPlaneShape:: -get_plane_constant() const { - - return (PN_stdfloat)_shape->getPlaneConstant(); -} - -/** - * - */ -INLINE LVector3 BulletPlaneShape:: -get_plane_normal() const { - - return btVector3_to_LVector3(_shape->getPlaneNormal()); -} diff --git a/panda/src/bullet/bulletPlaneShape.cxx b/panda/src/bullet/bulletPlaneShape.cxx index 5b74f0caae..a7804290cd 100644 --- a/panda/src/bullet/bulletPlaneShape.cxx +++ b/panda/src/bullet/bulletPlaneShape.cxx @@ -27,6 +27,26 @@ BulletPlaneShape(const LVector3 &normal, PN_stdfloat constant) { _shape->setUserPointer(this); } +/** + * + */ +BulletPlaneShape:: +BulletPlaneShape(const BulletPlaneShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; +} + +/** + * + */ +void BulletPlaneShape:: +operator = (const BulletPlaneShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; +} + /** * */ @@ -36,6 +56,26 @@ ptr() const { return _shape; } +/** + * + */ +PN_stdfloat BulletPlaneShape:: +get_plane_constant() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_shape->getPlaneConstant(); +} + +/** + * + */ +LVector3 BulletPlaneShape:: +get_plane_normal() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_shape->getPlaneNormal()); +} + /** * */ @@ -62,6 +102,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 +132,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 4e75bfc2e6..610c5d2412 100644 --- a/panda/src/bullet/bulletPlaneShape.h +++ b/panda/src/bullet/bulletPlaneShape.h @@ -32,13 +32,13 @@ private: INLINE BulletPlaneShape() : _shape(NULL) {}; PUBLISHED: - BulletPlaneShape(const LVector3 &normal, PN_stdfloat constant); - INLINE BulletPlaneShape(const BulletPlaneShape ©); - INLINE void operator = (const BulletPlaneShape ©); + explicit BulletPlaneShape(const LVector3 &normal, PN_stdfloat constant); + BulletPlaneShape(const BulletPlaneShape ©); + void operator = (const BulletPlaneShape ©); INLINE ~BulletPlaneShape(); - INLINE LVector3 get_plane_normal() const; - INLINE PN_stdfloat get_plane_constant() const; + LVector3 get_plane_normal() const; + PN_stdfloat get_plane_constant() const; static BulletPlaneShape *make_from_solid(const CollisionPlane *solid); diff --git a/panda/src/bullet/bulletRigidBodyNode.I b/panda/src/bullet/bulletRigidBodyNode.I index de1d5eea05..02c9b5dc6d 100644 --- a/panda/src/bullet/bulletRigidBodyNode.I +++ b/panda/src/bullet/bulletRigidBodyNode.I @@ -20,38 +20,3 @@ INLINE BulletRigidBodyNode:: delete _rigid; } -/** - * - */ -INLINE void BulletRigidBodyNode:: -set_linear_damping(PN_stdfloat value) { - - _rigid->setDamping(value, _rigid->getAngularDamping()); -} - -/** - * - */ -INLINE void BulletRigidBodyNode:: -set_angular_damping(PN_stdfloat value) { - - _rigid->setDamping(_rigid->getLinearDamping(), value); -} - -/** - * - */ -INLINE PN_stdfloat BulletRigidBodyNode:: -get_linear_damping() const { - - return (PN_stdfloat)_rigid->getLinearDamping(); -} - -/** - * - */ -INLINE PN_stdfloat BulletRigidBodyNode:: -get_angular_damping() const { - - return (PN_stdfloat)_rigid->getAngularDamping(); -} diff --git a/panda/src/bullet/bulletRigidBodyNode.cxx b/panda/src/bullet/bulletRigidBodyNode.cxx index 5c5e722785..ab8c4327f9 100644 --- a/panda/src/bullet/bulletRigidBodyNode.cxx +++ b/panda/src/bullet/bulletRigidBodyNode.cxx @@ -48,9 +48,11 @@ BulletRigidBodyNode(const char *name) : BulletBodyNode(name) { */ BulletRigidBodyNode:: BulletRigidBodyNode(const BulletRigidBodyNode ©) : - BulletBodyNode(copy), - _motion(copy._motion) + BulletBodyNode(copy) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motion = copy._motion; _rigid = new btRigidBody(*copy._rigid); _rigid->setUserPointer(this); _rigid->setCollisionShape(_shape); @@ -64,6 +66,7 @@ BulletRigidBodyNode(const BulletRigidBodyNode ©) : */ PandaNode *BulletRigidBodyNode:: make_copy() const { + return new BulletRigidBodyNode(*this); } @@ -72,10 +75,11 @@ make_copy() const { */ void BulletRigidBodyNode:: output(ostream &out) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); - BulletBodyNode::output(out); + BulletBodyNode::do_output(out); - out << " mass=" << get_mass(); + out << " mass=" << do_get_mass(); } /** @@ -93,10 +97,10 @@ get_object() const { * The default implementation does nothing. */ void BulletRigidBodyNode:: -shape_changed() { +do_shape_changed() { - set_mass(get_mass()); - transform_changed(); + do_set_mass(do_get_mass()); + do_transform_changed(); } /** @@ -104,9 +108,10 @@ shape_changed() { * automatically computed from the shape of the body. Setting a value of zero * for mass will make the body static. A value of zero can be considered an * infinite mass. + * Assumes the lock(bullet global lock) is held by the caller */ void BulletRigidBodyNode:: -set_mass(PN_stdfloat mass) { +do_set_mass(PN_stdfloat mass) { btScalar bt_mass = mass; btVector3 bt_inertia(0.0, 0.0, 0.0); @@ -119,12 +124,26 @@ set_mass(PN_stdfloat mass) { _rigid->updateInertiaTensor(); } +/** + * Sets the mass of a rigid body. This also modifies the inertia, which is + * automatically computed from the shape of the body. Setting a value of zero + * for mass will make the body static. A value of zero can be considered an + * infinite mass. + */ +void BulletRigidBodyNode:: +set_mass(PN_stdfloat mass) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_set_mass(mass); +} + /** * Returns the total mass of a rigid body. A value of zero means that the * body is staic, i.e. has an infinite mass. + * Assumes the lock(bullet global lock) is held by the caller */ PN_stdfloat BulletRigidBodyNode:: -get_mass() const { +do_get_mass() const { btScalar inv_mass = _rigid->getInvMass(); btScalar mass = (inv_mass == btScalar(0.0)) ? btScalar(0.0) : btScalar(1.0) / inv_mass; @@ -132,11 +151,24 @@ get_mass() const { return mass; } +/** + * Returns the total mass of a rigid body. A value of zero means that the + * body is staic, i.e. has an infinite mass. + */ +PN_stdfloat BulletRigidBodyNode:: +get_mass() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_mass(); +} + + /** * Returns the inverse mass of a rigid body. */ PN_stdfloat BulletRigidBodyNode:: get_inv_mass() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_rigid->getInvMass(); } @@ -153,6 +185,7 @@ get_inv_mass() const { */ void BulletRigidBodyNode:: set_inertia(const LVecBase3 &inertia) { + LightMutexHolder holder(BulletWorld::get_global_lock()); btVector3 inv_inertia( inertia.get_x() == 0.0 ? btScalar(0.0) : btScalar(1.0 / inertia.get_x()), @@ -171,6 +204,7 @@ set_inertia(const LVecBase3 &inertia) { */ LVector3 BulletRigidBodyNode:: get_inertia() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); btVector3 inv_inertia = _rigid->getInvInertiaDiagLocal(); LVector3 inertia( @@ -187,6 +221,7 @@ get_inertia() const { */ LVector3 BulletRigidBodyNode:: get_inv_inertia_diag_local() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getInvInertiaDiagLocal()); } @@ -196,6 +231,7 @@ get_inv_inertia_diag_local() const { */ LMatrix3 BulletRigidBodyNode:: get_inv_inertia_tensor_world() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btMatrix3x3_to_LMatrix3(_rigid->getInvInertiaTensorWorld()); } @@ -205,6 +241,7 @@ get_inv_inertia_tensor_world() const { */ void BulletRigidBodyNode:: apply_force(const LVector3 &force, const LPoint3 &pos) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!force.is_nan()); nassertv_always(!pos.is_nan()); @@ -218,6 +255,7 @@ apply_force(const LVector3 &force, const LPoint3 &pos) { */ void BulletRigidBodyNode:: apply_central_force(const LVector3 &force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!force.is_nan()); @@ -229,6 +267,7 @@ apply_central_force(const LVector3 &force) { */ void BulletRigidBodyNode:: apply_torque(const LVector3 &torque) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!torque.is_nan()); @@ -240,6 +279,7 @@ apply_torque(const LVector3 &torque) { */ void BulletRigidBodyNode:: apply_torque_impulse(const LVector3 &torque) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!torque.is_nan()); @@ -251,6 +291,7 @@ apply_torque_impulse(const LVector3 &torque) { */ void BulletRigidBodyNode:: apply_impulse(const LVector3 &impulse, const LPoint3 &pos) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!impulse.is_nan()); nassertv_always(!pos.is_nan()); @@ -264,6 +305,7 @@ apply_impulse(const LVector3 &impulse, const LPoint3 &pos) { */ void BulletRigidBodyNode:: apply_central_impulse(const LVector3 &impulse) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!impulse.is_nan()); @@ -271,10 +313,10 @@ apply_central_impulse(const LVector3 &impulse) { } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletRigidBodyNode:: -transform_changed() { +do_transform_changed() { if (_motion.sync_disabled()) return; @@ -289,7 +331,7 @@ transform_changed() { _motion.set_net_transform(ts); // For dynamic or static bodies we directly apply the new transform. - if (!is_kinematic()) { + if (!(get_object()->isKinematicObject())) { btTransform trans = TransformState_to_btTrans(ts); _rigid->setCenterOfMassTransform(trans); } @@ -317,18 +359,31 @@ transform_changed() { * */ void BulletRigidBodyNode:: -sync_p2b() { +transform_changed() { - if (is_kinematic()) { - transform_changed(); + if (_motion.sync_disabled()) return; + + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_transform_changed(); +} + +/** + * Assumes the lock(bullet global lock) is held by the caller + */ +void BulletRigidBodyNode:: +do_sync_p2b() { + + if (get_object()->isKinematicObject()) { + do_transform_changed(); } } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletRigidBodyNode:: -sync_b2p() { +do_sync_b2p() { _motion.sync_b2p((PandaNode *)this); } @@ -338,6 +393,7 @@ sync_b2p() { */ LVector3 BulletRigidBodyNode:: get_linear_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getLinearVelocity()); } @@ -347,6 +403,7 @@ get_linear_velocity() const { */ LVector3 BulletRigidBodyNode:: get_angular_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getAngularVelocity()); } @@ -356,6 +413,7 @@ get_angular_velocity() const { */ void BulletRigidBodyNode:: set_linear_velocity(const LVector3 &velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!velocity.is_nan()); @@ -367,17 +425,59 @@ set_linear_velocity(const LVector3 &velocity) { */ void BulletRigidBodyNode:: set_angular_velocity(const LVector3 &velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!velocity.is_nan()); _rigid->setAngularVelocity(LVecBase3_to_btVector3(velocity)); } +/** + * + */ +void BulletRigidBodyNode:: +set_linear_damping(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _rigid->setDamping(value, _rigid->getAngularDamping()); +} + +/** + * + */ +void BulletRigidBodyNode:: +set_angular_damping(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _rigid->setDamping(_rigid->getLinearDamping(), value); +} + +/** + * + */ +PN_stdfloat BulletRigidBodyNode:: +get_linear_damping() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_rigid->getLinearDamping(); +} + +/** + * + */ +PN_stdfloat BulletRigidBodyNode:: +get_angular_damping() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_rigid->getAngularDamping(); +} + /** * */ void BulletRigidBodyNode:: clear_forces() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _rigid->clearForces(); } @@ -387,6 +487,7 @@ clear_forces() { */ PN_stdfloat BulletRigidBodyNode:: get_linear_sleep_threshold() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _rigid->getLinearSleepingThreshold(); } @@ -396,6 +497,7 @@ get_linear_sleep_threshold() const { */ PN_stdfloat BulletRigidBodyNode:: get_angular_sleep_threshold() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _rigid->getAngularSleepingThreshold(); } @@ -405,6 +507,7 @@ get_angular_sleep_threshold() const { */ void BulletRigidBodyNode:: set_linear_sleep_threshold(PN_stdfloat threshold) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _rigid->setSleepingThresholds(threshold, _rigid->getAngularSleepingThreshold()); } @@ -414,6 +517,7 @@ set_linear_sleep_threshold(PN_stdfloat threshold) { */ void BulletRigidBodyNode:: set_angular_sleep_threshold(PN_stdfloat threshold) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _rigid->setSleepingThresholds(_rigid->getLinearSleepingThreshold(), threshold); } @@ -423,6 +527,7 @@ set_angular_sleep_threshold(PN_stdfloat threshold) { */ void BulletRigidBodyNode:: set_gravity(const LVector3 &gravity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!gravity.is_nan()); @@ -434,6 +539,7 @@ set_gravity(const LVector3 &gravity) { */ LVector3 BulletRigidBodyNode:: get_gravity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getGravity()); } @@ -443,6 +549,7 @@ get_gravity() const { */ LVector3 BulletRigidBodyNode:: get_linear_factor() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getLinearFactor()); } @@ -452,6 +559,7 @@ get_linear_factor() const { */ LVector3 BulletRigidBodyNode:: get_angular_factor() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getAngularFactor()); } @@ -461,6 +569,7 @@ get_angular_factor() const { */ void BulletRigidBodyNode:: set_linear_factor(const LVector3 &factor) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _rigid->setLinearFactor(LVecBase3_to_btVector3(factor)); } @@ -470,6 +579,7 @@ set_linear_factor(const LVector3 &factor) { */ void BulletRigidBodyNode:: set_angular_factor(const LVector3 &factor) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _rigid->setAngularFactor(LVecBase3_to_btVector3(factor)); } @@ -479,6 +589,7 @@ set_angular_factor(const LVector3 &factor) { */ LVector3 BulletRigidBodyNode:: get_total_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getTotalForce()); } @@ -488,6 +599,7 @@ get_total_force() const { */ LVector3 BulletRigidBodyNode:: get_total_torque() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getTotalTorque()); } @@ -612,6 +724,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 c8afaeb8bb..641fdc9fd9 100644 --- a/panda/src/bullet/bulletRigidBodyNode.h +++ b/panda/src/bullet/bulletRigidBodyNode.h @@ -50,10 +50,10 @@ PUBLISHED: void set_angular_velocity(const LVector3 &velocity); // Damping - INLINE PN_stdfloat get_linear_damping() const; - INLINE PN_stdfloat get_angular_damping() const; - INLINE void set_linear_damping(PN_stdfloat value); - INLINE void set_angular_damping(PN_stdfloat value); + PN_stdfloat get_linear_damping() const; + PN_stdfloat get_angular_damping() const; + void set_linear_damping(PN_stdfloat value); + void set_angular_damping(PN_stdfloat value); // Forces void clear_forces(); @@ -108,14 +108,18 @@ public: virtual void output(ostream &out) const; - void sync_p2b(); - void sync_b2p(); + void do_sync_p2b(); + void do_sync_b2p(); protected: virtual void transform_changed(); private: - virtual void shape_changed(); + virtual void do_shape_changed(); + void do_transform_changed(); + + void do_set_mass(PN_stdfloat mass); + PN_stdfloat do_get_mass() const; // The motion state is used for synchronisation between Bullet and the // Panda3D scene graph. diff --git a/panda/src/bullet/bulletRotationalLimitMotor.I b/panda/src/bullet/bulletRotationalLimitMotor.I index 10ed68435c..35b923a9d3 100644 --- a/panda/src/bullet/bulletRotationalLimitMotor.I +++ b/panda/src/bullet/bulletRotationalLimitMotor.I @@ -14,162 +14,7 @@ /** * */ -INLINE bool BulletRotationalLimitMotor:: -is_limited() const { +INLINE BulletRotationalLimitMotor:: +~BulletRotationalLimitMotor() { - return _motor.isLimited(); -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_motor_enabled(bool enabled) { - - _motor.m_enableMotor = enabled; -} - -/** - * - */ -INLINE bool BulletRotationalLimitMotor:: -get_motor_enabled() const { - - return _motor.m_enableMotor; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_low_limit(PN_stdfloat limit) { - - _motor.m_loLimit = (btScalar)limit; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_high_limit(PN_stdfloat limit) { - - _motor.m_hiLimit = (btScalar)limit; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_target_velocity(PN_stdfloat velocity) { - - _motor.m_targetVelocity = (btScalar)velocity; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_max_motor_force(PN_stdfloat force) { - - _motor.m_maxMotorForce = (btScalar)force; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_max_limit_force(PN_stdfloat force) { - - _motor.m_maxLimitForce = (btScalar)force; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_damping(PN_stdfloat damping) { - - _motor.m_damping = (btScalar)damping; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_softness(PN_stdfloat softness) { - - _motor.m_limitSoftness = (btScalar)softness; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_bounce(PN_stdfloat bounce) { - - _motor.m_bounce = (btScalar)bounce; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_normal_cfm(PN_stdfloat cfm) { - - _motor.m_normalCFM = (btScalar)cfm; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_stop_cfm(PN_stdfloat cfm) { - - _motor.m_stopCFM = (btScalar)cfm; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_stop_erp(PN_stdfloat erp) { - - _motor.m_stopERP = (btScalar)erp; -} - -/** - * Retrieves the current value of angle: 0 = free, 1 = at low limit, 2 = at - * high limit. - */ -INLINE int BulletRotationalLimitMotor:: -get_current_limit() const { - - return _motor.m_currentLimit; -} - -/** - * - */ -INLINE PN_stdfloat BulletRotationalLimitMotor:: -get_current_error() const { - - return (PN_stdfloat)_motor.m_currentLimitError; -} - -/** - * - */ -INLINE PN_stdfloat BulletRotationalLimitMotor:: -get_current_position() const { - - return (PN_stdfloat)_motor.m_currentPosition; -} - -/** - * - */ -INLINE PN_stdfloat BulletRotationalLimitMotor:: -get_accumulated_impulse() const { - - return (PN_stdfloat)_motor.m_accumulatedImpulse; } diff --git a/panda/src/bullet/bulletRotationalLimitMotor.cxx b/panda/src/bullet/bulletRotationalLimitMotor.cxx index ce95a1f8d7..d521b7ad94 100644 --- a/panda/src/bullet/bulletRotationalLimitMotor.cxx +++ b/panda/src/bullet/bulletRotationalLimitMotor.cxx @@ -34,7 +34,179 @@ BulletRotationalLimitMotor(const BulletRotationalLimitMotor ©) /** * */ -BulletRotationalLimitMotor:: -~BulletRotationalLimitMotor() { +bool BulletRotationalLimitMotor:: +is_limited() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + return _motor.isLimited(); +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_motor_enabled(bool enabled) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_enableMotor = enabled; +} + +/** + * + */ +bool BulletRotationalLimitMotor:: +get_motor_enabled() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _motor.m_enableMotor; +} +/** + * + */ +void BulletRotationalLimitMotor:: +set_low_limit(PN_stdfloat limit) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_loLimit = (btScalar)limit; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_high_limit(PN_stdfloat limit) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_hiLimit = (btScalar)limit; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_target_velocity(PN_stdfloat velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_targetVelocity = (btScalar)velocity; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_max_motor_force(PN_stdfloat force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_maxMotorForce = (btScalar)force; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_max_limit_force(PN_stdfloat force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_maxLimitForce = (btScalar)force; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_damping(PN_stdfloat damping) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_damping = (btScalar)damping; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_softness(PN_stdfloat softness) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_limitSoftness = (btScalar)softness; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_bounce(PN_stdfloat bounce) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_bounce = (btScalar)bounce; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_normal_cfm(PN_stdfloat cfm) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_normalCFM = (btScalar)cfm; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_stop_cfm(PN_stdfloat cfm) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_stopCFM = (btScalar)cfm; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_stop_erp(PN_stdfloat erp) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_stopERP = (btScalar)erp; +} + +/** + * Retrieves the current value of angle: 0 = free, 1 = at low limit, 2 = at + * high limit. + */ +int BulletRotationalLimitMotor:: +get_current_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _motor.m_currentLimit; +} + +/** + * + */ +PN_stdfloat BulletRotationalLimitMotor:: +get_current_error() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_motor.m_currentLimitError; +} + +/** + * + */ +PN_stdfloat BulletRotationalLimitMotor:: +get_current_position() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_motor.m_currentPosition; +} + +/** + * + */ +PN_stdfloat BulletRotationalLimitMotor:: +get_accumulated_impulse() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_motor.m_accumulatedImpulse; } diff --git a/panda/src/bullet/bulletRotationalLimitMotor.h b/panda/src/bullet/bulletRotationalLimitMotor.h index a1cce18ff4..d046cd9171 100644 --- a/panda/src/bullet/bulletRotationalLimitMotor.h +++ b/panda/src/bullet/bulletRotationalLimitMotor.h @@ -28,27 +28,27 @@ class EXPCL_PANDABULLET BulletRotationalLimitMotor { PUBLISHED: BulletRotationalLimitMotor(const BulletRotationalLimitMotor ©); - ~BulletRotationalLimitMotor(); + INLINE ~BulletRotationalLimitMotor(); - INLINE void set_motor_enabled(bool enable); - INLINE void set_low_limit(PN_stdfloat limit); - INLINE void set_high_limit(PN_stdfloat limit); - INLINE void set_target_velocity(PN_stdfloat velocity); - INLINE void set_max_motor_force(PN_stdfloat force); - INLINE void set_max_limit_force(PN_stdfloat force); - INLINE void set_damping(PN_stdfloat damping); - INLINE void set_softness(PN_stdfloat softness); - INLINE void set_bounce(PN_stdfloat bounce); - INLINE void set_normal_cfm(PN_stdfloat cfm); - INLINE void set_stop_cfm(PN_stdfloat cfm); - INLINE void set_stop_erp(PN_stdfloat erp); + void set_motor_enabled(bool enable); + void set_low_limit(PN_stdfloat limit); + void set_high_limit(PN_stdfloat limit); + void set_target_velocity(PN_stdfloat velocity); + void set_max_motor_force(PN_stdfloat force); + void set_max_limit_force(PN_stdfloat force); + void set_damping(PN_stdfloat damping); + void set_softness(PN_stdfloat softness); + void set_bounce(PN_stdfloat bounce); + void set_normal_cfm(PN_stdfloat cfm); + void set_stop_cfm(PN_stdfloat cfm); + void set_stop_erp(PN_stdfloat erp); - INLINE bool is_limited() const; - INLINE bool get_motor_enabled() const; - INLINE int get_current_limit() const; - INLINE PN_stdfloat get_current_error() const; - INLINE PN_stdfloat get_current_position() const; - INLINE PN_stdfloat get_accumulated_impulse() const; + bool is_limited() const; + bool get_motor_enabled() const; + int get_current_limit() const; + PN_stdfloat get_current_error() const; + PN_stdfloat get_current_position() const; + PN_stdfloat get_accumulated_impulse() const; MAKE_PROPERTY(limited, is_limited); MAKE_PROPERTY(motor_enabled, get_motor_enabled, set_motor_enabled); diff --git a/panda/src/bullet/bulletShape.I b/panda/src/bullet/bulletShape.I index 35123a868f..c408ccb95b 100644 --- a/panda/src/bullet/bulletShape.I +++ b/panda/src/bullet/bulletShape.I @@ -18,66 +18,3 @@ INLINE BulletShape:: ~BulletShape() { } - -/** - * - */ -INLINE bool BulletShape:: -is_polyhedral() const { - - return ptr()->isPolyhedral(); -} - -/** - * - */ -INLINE bool BulletShape:: -is_convex() const { - - return ptr()->isConvex(); -} - -/** - * - */ -INLINE bool BulletShape:: -is_convex_2d() const { - - return ptr()->isConvex2d(); -} - -/** - * - */ -INLINE bool BulletShape:: -is_concave() const { - - return ptr()->isConcave(); -} - -/** - * - */ -INLINE bool BulletShape:: -is_infinite() const { - - return ptr()->isInfinite(); -} - -/** - * - */ -INLINE bool BulletShape:: -is_non_moving() const { - - return ptr()->isNonMoving(); -} - -/** - * - */ -INLINE bool BulletShape:: -is_soft_body() const { - - return ptr()->isSoftBody(); -} diff --git a/panda/src/bullet/bulletShape.cxx b/panda/src/bullet/bulletShape.cxx index daf8c1e623..49bd6e4f92 100644 --- a/panda/src/bullet/bulletShape.cxx +++ b/panda/src/bullet/bulletShape.cxx @@ -21,6 +21,7 @@ TypeHandle BulletShape::_type_handle; */ const char *BulletShape:: get_name() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return ptr()->getName(); } @@ -30,6 +31,7 @@ get_name() const { */ PN_stdfloat BulletShape:: get_margin() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return ptr()->getMargin(); } @@ -39,6 +41,7 @@ get_margin() const { */ void BulletShape:: set_margin(PN_stdfloat margin) { + LightMutexHolder holder(BulletWorld::get_global_lock()); ptr()->setMargin(margin); } @@ -48,18 +51,29 @@ set_margin(PN_stdfloat margin) { */ LVecBase3 BulletShape:: get_local_scale() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVecBase3(ptr()->getLocalScaling()); } +/** + * Assumes the lock(bullet global lock) is held by the caller + */ +void BulletShape:: +do_set_local_scale(const LVecBase3 &scale) { + + nassertv(!scale.is_nan()); + ptr()->setLocalScaling(LVecBase3_to_btVector3(scale)); +} + /** * */ void BulletShape:: set_local_scale(const LVecBase3 &scale) { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertv(!scale.is_nan()); - ptr()->setLocalScaling(LVecBase3_to_btVector3(scale)); + do_set_local_scale(scale); } /** @@ -67,6 +81,7 @@ set_local_scale(const LVecBase3 &scale) { */ BoundingSphere BulletShape:: get_shape_bounds() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); /* btTransform tr; @@ -87,3 +102,73 @@ cout << "origin " << aabbMin.x() << " " << aabbMin.y() << " " << aabbMin.z() << return bounds; } + +/** + * + */ +bool BulletShape:: +is_polyhedral() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isPolyhedral(); +} + +/** + * + */ +bool BulletShape:: +is_convex() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isConvex(); +} + +/** + * + */ +bool BulletShape:: +is_convex_2d() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isConvex2d(); +} + +/** + * + */ +bool BulletShape:: +is_concave() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isConcave(); +} + +/** + * + */ +bool BulletShape:: +is_infinite() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isInfinite(); +} + +/** + * + */ +bool BulletShape:: +is_non_moving() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isNonMoving(); +} + +/** + * + */ +bool BulletShape:: +is_soft_body() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isSoftBody(); +} diff --git a/panda/src/bullet/bulletShape.h b/panda/src/bullet/bulletShape.h index 8ebeaf9290..e9dd08bf14 100644 --- a/panda/src/bullet/bulletShape.h +++ b/panda/src/bullet/bulletShape.h @@ -31,13 +31,13 @@ protected: PUBLISHED: INLINE virtual ~BulletShape(); - INLINE bool is_polyhedral() const; - INLINE bool is_convex() const; - INLINE bool is_convex_2d() const; - INLINE bool is_concave() const; - INLINE bool is_infinite() const; - INLINE bool is_non_moving() const; - INLINE bool is_soft_body() const; + bool is_polyhedral() const; + bool is_convex() const; + bool is_convex_2d() const; + bool is_concave() const; + bool is_infinite() const; + bool is_non_moving() const; + bool is_soft_body() const; void set_margin(PN_stdfloat margin); const char *get_name() const; @@ -61,6 +61,7 @@ public: virtual btCollisionShape *ptr() const = 0; LVecBase3 get_local_scale() const; void set_local_scale(const LVecBase3 &scale); + void do_set_local_scale(const LVecBase3 &scale); public: static TypeHandle get_class_type() { diff --git a/panda/src/bullet/bulletSliderConstraint.I b/panda/src/bullet/bulletSliderConstraint.I index 4035f1399f..3eca50b143 100644 --- a/panda/src/bullet/bulletSliderConstraint.I +++ b/panda/src/bullet/bulletSliderConstraint.I @@ -19,21 +19,3 @@ INLINE BulletSliderConstraint:: delete _constraint; } - -/** - * - */ -INLINE CPT(TransformState) BulletSliderConstraint:: -get_frame_a() const { - - return btTrans_to_TransformState(_constraint->getFrameOffsetA()); -} - -/** - * - */ -INLINE CPT(TransformState) BulletSliderConstraint:: -get_frame_b() const { - - return btTrans_to_TransformState(_constraint->getFrameOffsetB()); -} diff --git a/panda/src/bullet/bulletSliderConstraint.cxx b/panda/src/bullet/bulletSliderConstraint.cxx index cda213b8c2..c803f509cd 100644 --- a/panda/src/bullet/bulletSliderConstraint.cxx +++ b/panda/src/bullet/bulletSliderConstraint.cxx @@ -65,6 +65,7 @@ ptr() const { */ PN_stdfloat BulletSliderConstraint:: get_lower_linear_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getLowerLinLimit(); } @@ -74,6 +75,7 @@ get_lower_linear_limit() const { */ PN_stdfloat BulletSliderConstraint:: get_upper_linear_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getUpperLinLimit(); } @@ -83,6 +85,7 @@ get_upper_linear_limit() const { */ PN_stdfloat BulletSliderConstraint:: get_lower_angular_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return rad_2_deg(_constraint->getLowerAngLimit()); } @@ -92,6 +95,7 @@ get_lower_angular_limit() const { */ PN_stdfloat BulletSliderConstraint:: get_upper_angular_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return rad_2_deg(_constraint->getUpperAngLimit()); } @@ -101,6 +105,7 @@ get_upper_angular_limit() const { */ void BulletSliderConstraint:: set_lower_linear_limit(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setLowerLinLimit((btScalar)value); } @@ -110,6 +115,7 @@ set_lower_linear_limit(PN_stdfloat value) { */ void BulletSliderConstraint:: set_upper_linear_limit(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setUpperLinLimit((btScalar)value); } @@ -119,6 +125,7 @@ set_upper_linear_limit(PN_stdfloat value) { */ void BulletSliderConstraint:: set_lower_angular_limit(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setLowerAngLimit((btScalar)deg_2_rad(value)); } @@ -128,6 +135,7 @@ set_lower_angular_limit(PN_stdfloat value) { */ void BulletSliderConstraint:: set_upper_angular_limit(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setUpperAngLimit((btScalar)deg_2_rad(value)); } @@ -137,6 +145,7 @@ set_upper_angular_limit(PN_stdfloat value) { */ PN_stdfloat BulletSliderConstraint:: get_linear_pos() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getLinearPos(); } @@ -146,6 +155,7 @@ get_linear_pos() const { */ PN_stdfloat BulletSliderConstraint:: get_angular_pos() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getAngularPos(); } @@ -155,6 +165,7 @@ get_angular_pos() const { */ void BulletSliderConstraint:: set_powered_linear_motor(bool on) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setPoweredLinMotor(on); } @@ -164,6 +175,7 @@ set_powered_linear_motor(bool on) { */ void BulletSliderConstraint:: set_target_linear_motor_velocity(PN_stdfloat target_velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setTargetLinMotorVelocity((btScalar)target_velocity); } @@ -173,6 +185,7 @@ set_target_linear_motor_velocity(PN_stdfloat target_velocity) { */ void BulletSliderConstraint:: set_max_linear_motor_force(PN_stdfloat max_force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMaxLinMotorForce((btScalar)max_force); } @@ -182,6 +195,7 @@ set_max_linear_motor_force(PN_stdfloat max_force) { */ bool BulletSliderConstraint:: get_powered_linear_motor() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _constraint->getPoweredLinMotor(); } @@ -191,6 +205,7 @@ get_powered_linear_motor() const { */ PN_stdfloat BulletSliderConstraint:: get_target_linear_motor_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getTargetLinMotorVelocity(); } @@ -200,6 +215,7 @@ get_target_linear_motor_velocity() const { */ PN_stdfloat BulletSliderConstraint:: get_max_linear_motor_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getMaxLinMotorForce(); } @@ -209,6 +225,7 @@ get_max_linear_motor_force() const { */ void BulletSliderConstraint:: set_powered_angular_motor(bool on) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setPoweredAngMotor(on); } @@ -218,6 +235,7 @@ set_powered_angular_motor(bool on) { */ void BulletSliderConstraint:: set_target_angular_motor_velocity(PN_stdfloat target_velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setTargetAngMotorVelocity((btScalar)target_velocity); } @@ -227,6 +245,7 @@ set_target_angular_motor_velocity(PN_stdfloat target_velocity) { */ void BulletSliderConstraint:: set_max_angular_motor_force(PN_stdfloat max_force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMaxAngMotorForce((btScalar)max_force); } @@ -236,6 +255,7 @@ set_max_angular_motor_force(PN_stdfloat max_force) { */ bool BulletSliderConstraint:: get_powered_angular_motor() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _constraint->getPoweredAngMotor(); } @@ -245,6 +265,7 @@ get_powered_angular_motor() const { */ PN_stdfloat BulletSliderConstraint:: get_target_angular_motor_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getTargetAngMotorVelocity(); } @@ -254,6 +275,7 @@ get_target_angular_motor_velocity() const { */ PN_stdfloat BulletSliderConstraint:: get_max_angular_motor_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getMaxAngMotorForce(); } @@ -263,9 +285,30 @@ get_max_angular_motor_force() const { */ void BulletSliderConstraint:: set_frames(const TransformState *ts_a, const TransformState *ts_b) { + LightMutexHolder holder(BulletWorld::get_global_lock()); btTransform frame_a = TransformState_to_btTrans(ts_a); btTransform frame_b = TransformState_to_btTrans(ts_b); _constraint->setFrames(frame_a, frame_b); } + +/** + * + */ +CPT(TransformState) BulletSliderConstraint:: +get_frame_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getFrameOffsetA()); +} + +/** + * + */ +CPT(TransformState) BulletSliderConstraint:: +get_frame_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getFrameOffsetB()); +} diff --git a/panda/src/bullet/bulletSliderConstraint.h b/panda/src/bullet/bulletSliderConstraint.h index 181cb3a94a..6a670867c5 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; @@ -71,8 +70,8 @@ PUBLISHED: // Frames void set_frames(const TransformState *ts_a, const TransformState *ts_b); - INLINE CPT(TransformState) get_frame_a() const; - INLINE CPT(TransformState) get_frame_b() const; + CPT(TransformState) get_frame_a() const; + CPT(TransformState) get_frame_b() const; MAKE_PROPERTY(linear_pos, get_linear_pos); MAKE_PROPERTY(angular_pos, get_angular_pos); diff --git a/panda/src/bullet/bulletSoftBodyConfig.I b/panda/src/bullet/bulletSoftBodyConfig.I index eed9499bbb..dfd2b36514 100644 --- a/panda/src/bullet/bulletSoftBodyConfig.I +++ b/panda/src/bullet/bulletSoftBodyConfig.I @@ -18,439 +18,3 @@ INLINE BulletSoftBodyConfig:: ~BulletSoftBodyConfig() { } - -/** - * Getter for property kVCF. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_velocities_correction_factor() const { - - return (PN_stdfloat)_cfg.kVCF; -} - -/** - * Setter for property kVCF. - */ -INLINE void BulletSoftBodyConfig:: -set_velocities_correction_factor(PN_stdfloat value) { - - _cfg.kVCF = (btScalar)value; -} - -/** - * Getter for property kDP. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_damping_coefficient() const { - - return (PN_stdfloat)_cfg.kDP; -} - -/** - * Setter for property kDP. - */ -INLINE void BulletSoftBodyConfig:: -set_damping_coefficient(PN_stdfloat value) { - - _cfg.kDP = (btScalar)value; -} - -/** - * Getter for property kDG. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_drag_coefficient() const { - - return (PN_stdfloat)_cfg.kDG; -} - -/** - * Setter for property kDG. - */ -INLINE void BulletSoftBodyConfig:: -set_drag_coefficient(PN_stdfloat value) { - - _cfg.kDG = (btScalar)value; -} - -/** - * Getter for property kLF. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_lift_coefficient() const { - - return (PN_stdfloat)_cfg.kLF; -} - -/** - * Setter for property kLF. - */ -INLINE void BulletSoftBodyConfig:: -set_lift_coefficient(PN_stdfloat value) { - - _cfg.kLF = (btScalar)value; -} - -/** - * Getter for property kPR. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_pressure_coefficient() const { - - return (PN_stdfloat)_cfg.kPR; -} - -/** - * Setter for property kPR. - */ -INLINE void BulletSoftBodyConfig:: -set_pressure_coefficient(PN_stdfloat value) { - - _cfg.kPR = (btScalar)value; -} - -/** - * Getter for property kVC. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_volume_conservation_coefficient() const { - - return (PN_stdfloat)_cfg.kVC; -} - -/** - * Setter for property kVC. - */ -INLINE void BulletSoftBodyConfig:: -set_volume_conservation_coefficient(PN_stdfloat value) { - - _cfg.kVC = (btScalar)value; -} - -/** - * Getter for property kDF. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_dynamic_friction_coefficient() const { - - return (PN_stdfloat)_cfg.kDF; -} - -/** - * Setter for property kDF. - */ -INLINE void BulletSoftBodyConfig:: -set_dynamic_friction_coefficient(PN_stdfloat value) { - - _cfg.kDF = (btScalar)value; -} - -/** - * Getter for property kMT. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_pose_matching_coefficient() const { - - return (PN_stdfloat)_cfg.kMT; -} - -/** - * Setter for property kMT. - */ -INLINE void BulletSoftBodyConfig:: -set_pose_matching_coefficient(PN_stdfloat value) { - - _cfg.kMT = (btScalar)value; -} - -/** - * Getter for property kCHR. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_rigid_contacts_hardness() const { - - return (PN_stdfloat)_cfg.kCHR; -} - -/** - * Setter for property kCHR. - */ -INLINE void BulletSoftBodyConfig:: -set_rigid_contacts_hardness(PN_stdfloat value) { - - _cfg.kCHR = (btScalar)value; -} - -/** - * Getter for property kKHR. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_kinetic_contacts_hardness() const { - - return (PN_stdfloat)_cfg.kKHR; -} - -/** - * Setter for property kKHR. - */ -INLINE void BulletSoftBodyConfig:: -set_kinetic_contacts_hardness(PN_stdfloat value) { - - _cfg.kKHR = (btScalar)value; -} - -/** - * Getter for property kSHR. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_contacts_hardness() const { - - return (PN_stdfloat)_cfg.kSHR; -} - -/** - * Setter for property kSHR. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_contacts_hardness(PN_stdfloat value) { - - _cfg.kSHR = (btScalar)value; -} - -/** - * Getter for property kAHR. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_anchors_hardness() const { - - return (PN_stdfloat)_cfg.kAHR; -} - -/** - * Setter for property kAHR. - */ -INLINE void BulletSoftBodyConfig:: -set_anchors_hardness(PN_stdfloat value) { - - _cfg.kAHR = (btScalar)value; -} - -/** - * Getter for property kSRHR_CL. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_vs_rigid_hardness() const { - - return (PN_stdfloat)_cfg.kSRHR_CL; -} - -/** - * Setter for property kSRHR_CL. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_vs_rigid_hardness(PN_stdfloat value) { - - _cfg.kSRHR_CL = (btScalar)value; -} - -/** - * Getter for property kSKHR_CL. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_vs_kinetic_hardness() const { - - return (PN_stdfloat)_cfg.kSKHR_CL; -} - -/** - * Setter for property kSKHR_CL. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_vs_kinetic_hardness(PN_stdfloat value) { - - _cfg.kSKHR_CL = (btScalar)value; -} - -/** - * Getter for property kSSHR_CL. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_vs_soft_hardness() const { - - return (PN_stdfloat)_cfg.kSSHR_CL; -} - -/** - * Setter for property kSSHR_CL. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_vs_soft_hardness(PN_stdfloat value) { - - _cfg.kSSHR_CL = (btScalar)value; -} - -/** - * Getter for property kSR_SPLT_CL. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_vs_rigid_impulse_split() const { - - return (PN_stdfloat)_cfg.kSR_SPLT_CL; -} - -/** - * Setter for property kSR_SPLT_CL. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_vs_rigid_impulse_split(PN_stdfloat value) { - - _cfg.kSR_SPLT_CL = (btScalar)value; -} - -/** - * Getter for property kSK_SPLT_CL. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_vs_kinetic_impulse_split() const { - - return (PN_stdfloat)_cfg.kSK_SPLT_CL; -} - -/** - * Setter for property kSK_SPLT_CL. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_vs_kinetic_impulse_split(PN_stdfloat value) { - - _cfg.kSK_SPLT_CL = (btScalar)value; -} - -/** - * Getter for property kSS_SPLT_CL. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_vs_soft_impulse_split() const { - - return (PN_stdfloat)_cfg.kSS_SPLT_CL; -} - -/** - * Setter for property kSS_SPLT_CL. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_vs_soft_impulse_split(PN_stdfloat value) { - - _cfg.kSS_SPLT_CL = (btScalar)value; -} - -/** - * Getter for property maxvolume. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_maxvolume() const { - - return (PN_stdfloat)_cfg.maxvolume; -} - -/** - * Setter for property maxvolume. - */ -INLINE void BulletSoftBodyConfig:: -set_maxvolume(PN_stdfloat value) { - - _cfg.maxvolume = (btScalar)value; -} - -/** - * Getter for property timescale. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_timescale() const { - - return (PN_stdfloat)_cfg.timescale; -} - -/** - * Setter for property timescale. - */ -INLINE void BulletSoftBodyConfig:: -set_timescale(PN_stdfloat value) { - - _cfg.timescale = (btScalar)value; -} - -/** - * Getter for property piterations. - */ -INLINE int BulletSoftBodyConfig:: -get_positions_solver_iterations() const { - - return _cfg.piterations; -} - -/** - * Setter for property piterations. - */ -INLINE void BulletSoftBodyConfig:: -set_positions_solver_iterations(int value) { - - nassertv(value > 0); - _cfg.piterations = value; -} - -/** - * Getter for property viterations. - */ -INLINE int BulletSoftBodyConfig:: -get_velocities_solver_iterations() const { - - return _cfg.viterations; -} - -/** - * Setter for property viterations. - */ -INLINE void BulletSoftBodyConfig:: -set_velocities_solver_iterations(int value) { - - nassertv(value > 0); - _cfg.viterations = value; -} - -/** - * Getter for property diterations. - */ -INLINE int BulletSoftBodyConfig:: -get_drift_solver_iterations() const { - - return _cfg.diterations; -} - -/** - * Setter for property diterations. - */ -INLINE void BulletSoftBodyConfig:: -set_drift_solver_iterations(int value) { - - nassertv(value > 0); - _cfg.diterations = value; -} - -/** - * Getter for property citerations. - */ -INLINE int BulletSoftBodyConfig:: -get_cluster_solver_iterations() const { - - return _cfg.citerations; -} - -/** - * Setter for property citerations. - */ -INLINE void BulletSoftBodyConfig:: -set_cluster_solver_iterations(int value) { - - nassertv(value > 0); - _cfg.citerations = value; -} diff --git a/panda/src/bullet/bulletSoftBodyConfig.cxx b/panda/src/bullet/bulletSoftBodyConfig.cxx index dc12fde309..a6db77cb96 100644 --- a/panda/src/bullet/bulletSoftBodyConfig.cxx +++ b/panda/src/bullet/bulletSoftBodyConfig.cxx @@ -26,6 +26,7 @@ BulletSoftBodyConfig(btSoftBody::Config &cfg) : _cfg(cfg) { */ void BulletSoftBodyConfig:: clear_all_collision_flags() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _cfg.collisions = 0; } @@ -35,6 +36,7 @@ clear_all_collision_flags() { */ void BulletSoftBodyConfig:: set_collision_flag(CollisionFlag flag, bool value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); if (value == true) { _cfg.collisions |= flag; @@ -49,6 +51,7 @@ set_collision_flag(CollisionFlag flag, bool value) { */ bool BulletSoftBodyConfig:: get_collision_flag(CollisionFlag flag) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (_cfg.collisions & flag) ? true : false; } @@ -58,6 +61,7 @@ get_collision_flag(CollisionFlag flag) const { */ void BulletSoftBodyConfig:: set_aero_model(AeroModel value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _cfg.aeromodel = (btSoftBody::eAeroModel::_)value; } @@ -67,6 +71,491 @@ set_aero_model(AeroModel value) { */ BulletSoftBodyConfig::AeroModel BulletSoftBodyConfig:: get_aero_model() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (AeroModel)_cfg.aeromodel; } + +/** + * Getter for property kVCF. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_velocities_correction_factor() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kVCF; +} + +/** + * Setter for property kVCF. + */ +void BulletSoftBodyConfig:: +set_velocities_correction_factor(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kVCF = (btScalar)value; +} + +/** + * Getter for property kDP. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_damping_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kDP; +} + +/** + * Setter for property kDP. + */ +void BulletSoftBodyConfig:: +set_damping_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kDP = (btScalar)value; +} + +/** + * Getter for property kDG. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_drag_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kDG; +} + +/** + * Setter for property kDG. + */ +void BulletSoftBodyConfig:: +set_drag_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kDG = (btScalar)value; +} + +/** + * Getter for property kLF. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_lift_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kLF; +} + +/** + * Setter for property kLF. + */ +void BulletSoftBodyConfig:: +set_lift_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kLF = (btScalar)value; +} + +/** + * Getter for property kPR. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_pressure_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kPR; +} + +/** + * Setter for property kPR. + */ +void BulletSoftBodyConfig:: +set_pressure_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kPR = (btScalar)value; +} + +/** + * Getter for property kVC. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_volume_conservation_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kVC; +} + +/** + * Setter for property kVC. + */ +void BulletSoftBodyConfig:: +set_volume_conservation_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kVC = (btScalar)value; +} + +/** + * Getter for property kDF. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_dynamic_friction_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kDF; +} + +/** + * Setter for property kDF. + */ +void BulletSoftBodyConfig:: +set_dynamic_friction_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kDF = (btScalar)value; +} + +/** + * Getter for property kMT. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_pose_matching_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kMT; +} + +/** + * Setter for property kMT. + */ +void BulletSoftBodyConfig:: +set_pose_matching_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kMT = (btScalar)value; +} + +/** + * Getter for property kCHR. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_rigid_contacts_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kCHR; +} + +/** + * Setter for property kCHR. + */ +void BulletSoftBodyConfig:: +set_rigid_contacts_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kCHR = (btScalar)value; +} + +/** + * Getter for property kKHR. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_kinetic_contacts_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kKHR; +} + +/** + * Setter for property kKHR. + */ +void BulletSoftBodyConfig:: +set_kinetic_contacts_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kKHR = (btScalar)value; +} + +/** + * Getter for property kSHR. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_contacts_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSHR; +} + +/** + * Setter for property kSHR. + */ +void BulletSoftBodyConfig:: +set_soft_contacts_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSHR = (btScalar)value; +} + +/** + * Getter for property kAHR. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_anchors_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kAHR; +} + +/** + * Setter for property kAHR. + */ +void BulletSoftBodyConfig:: +set_anchors_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kAHR = (btScalar)value; +} + +/** + * Getter for property kSRHR_CL. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_vs_rigid_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSRHR_CL; +} + +/** + * Setter for property kSRHR_CL. + */ +void BulletSoftBodyConfig:: +set_soft_vs_rigid_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSRHR_CL = (btScalar)value; +} + +/** + * Getter for property kSKHR_CL. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_vs_kinetic_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSKHR_CL; +} + +/** + * Setter for property kSKHR_CL. + */ +void BulletSoftBodyConfig:: +set_soft_vs_kinetic_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSKHR_CL = (btScalar)value; +} + +/** + * Getter for property kSSHR_CL. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_vs_soft_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSSHR_CL; +} + +/** + * Setter for property kSSHR_CL. + */ +void BulletSoftBodyConfig:: +set_soft_vs_soft_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSSHR_CL = (btScalar)value; +} + +/** + * Getter for property kSR_SPLT_CL. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_vs_rigid_impulse_split() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSR_SPLT_CL; +} + +/** + * Setter for property kSR_SPLT_CL. + */ +void BulletSoftBodyConfig:: +set_soft_vs_rigid_impulse_split(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSR_SPLT_CL = (btScalar)value; +} + +/** + * Getter for property kSK_SPLT_CL. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_vs_kinetic_impulse_split() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSK_SPLT_CL; +} + +/** + * Setter for property kSK_SPLT_CL. + */ +void BulletSoftBodyConfig:: +set_soft_vs_kinetic_impulse_split(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSK_SPLT_CL = (btScalar)value; +} + +/** + * Getter for property kSS_SPLT_CL. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_vs_soft_impulse_split() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSS_SPLT_CL; +} + +/** + * Setter for property kSS_SPLT_CL. + */ +void BulletSoftBodyConfig:: +set_soft_vs_soft_impulse_split(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSS_SPLT_CL = (btScalar)value; +} + +/** + * Getter for property maxvolume. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_maxvolume() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.maxvolume; +} + +/** + * Setter for property maxvolume. + */ +void BulletSoftBodyConfig:: +set_maxvolume(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.maxvolume = (btScalar)value; +} + +/** + * Getter for property timescale. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_timescale() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.timescale; +} + +/** + * Setter for property timescale. + */ +void BulletSoftBodyConfig:: +set_timescale(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.timescale = (btScalar)value; +} + +/** + * Getter for property piterations. + */ +int BulletSoftBodyConfig:: +get_positions_solver_iterations() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _cfg.piterations; +} + +/** + * Setter for property piterations. + */ +void BulletSoftBodyConfig:: +set_positions_solver_iterations(int value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(value > 0); + _cfg.piterations = value; +} + +/** + * Getter for property viterations. + */ +int BulletSoftBodyConfig:: +get_velocities_solver_iterations() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _cfg.viterations; +} + +/** + * Setter for property viterations. + */ +void BulletSoftBodyConfig:: +set_velocities_solver_iterations(int value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(value > 0); + _cfg.viterations = value; +} + +/** + * Getter for property diterations. + */ +int BulletSoftBodyConfig:: +get_drift_solver_iterations() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _cfg.diterations; +} + +/** + * Setter for property diterations. + */ +void BulletSoftBodyConfig:: +set_drift_solver_iterations(int value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(value > 0); + _cfg.diterations = value; +} + +/** + * Getter for property citerations. + */ +int BulletSoftBodyConfig:: +get_cluster_solver_iterations() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _cfg.citerations; +} + +/** + * Setter for property citerations. + */ +void BulletSoftBodyConfig:: +set_cluster_solver_iterations(int value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(value > 0); + _cfg.citerations = value; +} diff --git a/panda/src/bullet/bulletSoftBodyConfig.h b/panda/src/bullet/bulletSoftBodyConfig.h index 2411797146..18bbd49dfa 100644 --- a/panda/src/bullet/bulletSoftBodyConfig.h +++ b/panda/src/bullet/bulletSoftBodyConfig.h @@ -51,55 +51,55 @@ PUBLISHED: void set_aero_model(AeroModel value); AeroModel get_aero_model() const; - INLINE void set_velocities_correction_factor(PN_stdfloat value); - INLINE void set_damping_coefficient(PN_stdfloat value); - 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_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); - INLINE void set_kinetic_contacts_hardness(PN_stdfloat value); - INLINE void set_soft_contacts_hardness(PN_stdfloat value); - INLINE void set_anchors_hardness(PN_stdfloat value); - INLINE void set_soft_vs_rigid_hardness(PN_stdfloat value); - INLINE void set_soft_vs_kinetic_hardness(PN_stdfloat value); - INLINE void set_soft_vs_soft_hardness(PN_stdfloat value); - INLINE void set_soft_vs_rigid_impulse_split(PN_stdfloat value); - INLINE void set_soft_vs_kinetic_impulse_split(PN_stdfloat value); - INLINE void set_soft_vs_soft_impulse_split(PN_stdfloat value); - INLINE void set_maxvolume(PN_stdfloat value); - INLINE void set_timescale(PN_stdfloat value); - INLINE void set_positions_solver_iterations(int value); - INLINE void set_velocities_solver_iterations(int value); - INLINE void set_drift_solver_iterations( int value); - INLINE void set_cluster_solver_iterations(int value); + void set_velocities_correction_factor(PN_stdfloat value); + void set_damping_coefficient(PN_stdfloat value); + void set_drag_coefficient(PN_stdfloat value); + void set_lift_coefficient(PN_stdfloat value); + void set_pressure_coefficient(PN_stdfloat value); + void set_volume_conservation_coefficient(PN_stdfloat value); + void set_dynamic_friction_coefficient(PN_stdfloat value); + void set_pose_matching_coefficient(PN_stdfloat value); + void set_rigid_contacts_hardness(PN_stdfloat value); + void set_kinetic_contacts_hardness(PN_stdfloat value); + void set_soft_contacts_hardness(PN_stdfloat value); + void set_anchors_hardness(PN_stdfloat value); + void set_soft_vs_rigid_hardness(PN_stdfloat value); + void set_soft_vs_kinetic_hardness(PN_stdfloat value); + void set_soft_vs_soft_hardness(PN_stdfloat value); + void set_soft_vs_rigid_impulse_split(PN_stdfloat value); + void set_soft_vs_kinetic_impulse_split(PN_stdfloat value); + void set_soft_vs_soft_impulse_split(PN_stdfloat value); + void set_maxvolume(PN_stdfloat value); + void set_timescale(PN_stdfloat value); + void set_positions_solver_iterations(int value); + void set_velocities_solver_iterations(int value); + void set_drift_solver_iterations( int value); + void set_cluster_solver_iterations(int value); - INLINE PN_stdfloat get_velocities_correction_factor() const; - INLINE PN_stdfloat get_damping_coefficient() const; - 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_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; - INLINE PN_stdfloat get_kinetic_contacts_hardness() const; - INLINE PN_stdfloat get_soft_contacts_hardness() const; - INLINE PN_stdfloat get_anchors_hardness() const; - INLINE PN_stdfloat get_soft_vs_rigid_hardness() const; - INLINE PN_stdfloat get_soft_vs_kinetic_hardness() const; - INLINE PN_stdfloat get_soft_vs_soft_hardness() const; - INLINE PN_stdfloat get_soft_vs_rigid_impulse_split() const; - INLINE PN_stdfloat get_soft_vs_kinetic_impulse_split() const; - INLINE PN_stdfloat get_soft_vs_soft_impulse_split() const; - INLINE PN_stdfloat get_maxvolume() const; - INLINE PN_stdfloat get_timescale() const; - INLINE int get_positions_solver_iterations() const; - INLINE int get_velocities_solver_iterations() const; - INLINE int get_drift_solver_iterations() const; - INLINE int get_cluster_solver_iterations() const; + PN_stdfloat get_velocities_correction_factor() const; + PN_stdfloat get_damping_coefficient() const; + PN_stdfloat get_drag_coefficient() const; + PN_stdfloat get_lift_coefficient() const; + PN_stdfloat get_pressure_coefficient() const; + PN_stdfloat get_volume_conservation_coefficient() const; + PN_stdfloat get_dynamic_friction_coefficient() const; + PN_stdfloat get_pose_matching_coefficient() const; + PN_stdfloat get_rigid_contacts_hardness() const; + PN_stdfloat get_kinetic_contacts_hardness() const; + PN_stdfloat get_soft_contacts_hardness() const; + PN_stdfloat get_anchors_hardness() const; + PN_stdfloat get_soft_vs_rigid_hardness() const; + PN_stdfloat get_soft_vs_kinetic_hardness() const; + PN_stdfloat get_soft_vs_soft_hardness() const; + PN_stdfloat get_soft_vs_rigid_impulse_split() const; + PN_stdfloat get_soft_vs_kinetic_impulse_split() const; + PN_stdfloat get_soft_vs_soft_impulse_split() const; + PN_stdfloat get_maxvolume() const; + PN_stdfloat get_timescale() const; + int get_positions_solver_iterations() const; + int get_velocities_solver_iterations() const; + int get_drift_solver_iterations() const; + 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); diff --git a/panda/src/bullet/bulletSoftBodyMaterial.I b/panda/src/bullet/bulletSoftBodyMaterial.I index ff8182f6ed..7514fc7cbf 100644 --- a/panda/src/bullet/bulletSoftBodyMaterial.I +++ b/panda/src/bullet/bulletSoftBodyMaterial.I @@ -30,66 +30,3 @@ empty() { return BulletSoftBodyMaterial(material); } - -/** - * - */ -INLINE btSoftBody::Material &BulletSoftBodyMaterial:: -get_material() const { - - return _material; -} - -/** - * Getter for the property m_kLST. - */ -INLINE PN_stdfloat BulletSoftBodyMaterial:: -get_linear_stiffness() const { - - return (PN_stdfloat)_material.m_kLST; -} - -/** - * Setter for the property m_kLST. - */ -INLINE void BulletSoftBodyMaterial:: -set_linear_stiffness(PN_stdfloat value) { - - _material.m_kLST = (btScalar)value; -} - -/** - * Getter for the property m_kAST. - */ -INLINE PN_stdfloat BulletSoftBodyMaterial:: -get_angular_stiffness() const { - - return (PN_stdfloat)_material.m_kAST; -} - -/** - * Setter for the property m_kAST. - */ -INLINE void BulletSoftBodyMaterial:: -set_angular_stiffness(PN_stdfloat value) { - - _material.m_kAST = (btScalar)value; -} - -/** - * Getter for the property m_kVST. - */ -INLINE PN_stdfloat BulletSoftBodyMaterial:: -get_volume_preservation() const { - - return (PN_stdfloat)_material.m_kVST; -} - -/** - * Setter for the property m_kVST. - */ -INLINE void BulletSoftBodyMaterial:: -set_volume_preservation(PN_stdfloat value) { - - _material.m_kVST = (btScalar)value; -} diff --git a/panda/src/bullet/bulletSoftBodyMaterial.cxx b/panda/src/bullet/bulletSoftBodyMaterial.cxx index 1767513a27..d40a7bd984 100644 --- a/panda/src/bullet/bulletSoftBodyMaterial.cxx +++ b/panda/src/bullet/bulletSoftBodyMaterial.cxx @@ -20,3 +20,72 @@ BulletSoftBodyMaterial:: BulletSoftBodyMaterial(btSoftBody::Material &material) : _material(material) { } + +/** + * + */ +btSoftBody::Material &BulletSoftBodyMaterial:: +get_material() const { + + return _material; +} + +/** + * Getter for the property m_kLST. + */ +PN_stdfloat BulletSoftBodyMaterial:: +get_linear_stiffness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_material.m_kLST; +} + +/** + * Setter for the property m_kLST. + */ +void BulletSoftBodyMaterial:: +set_linear_stiffness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _material.m_kLST = (btScalar)value; +} + +/** + * Getter for the property m_kAST. + */ +PN_stdfloat BulletSoftBodyMaterial:: +get_angular_stiffness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_material.m_kAST; +} + +/** + * Setter for the property m_kAST. + */ +void BulletSoftBodyMaterial:: +set_angular_stiffness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _material.m_kAST = (btScalar)value; +} + +/** + * Getter for the property m_kVST. + */ +PN_stdfloat BulletSoftBodyMaterial:: +get_volume_preservation() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_material.m_kVST; +} + +/** + * Setter for the property m_kVST. + */ +void BulletSoftBodyMaterial:: +set_volume_preservation(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _material.m_kVST = (btScalar)value; +} diff --git a/panda/src/bullet/bulletSoftBodyMaterial.h b/panda/src/bullet/bulletSoftBodyMaterial.h index 3f1c07a7aa..bf8c8589a9 100644 --- a/panda/src/bullet/bulletSoftBodyMaterial.h +++ b/panda/src/bullet/bulletSoftBodyMaterial.h @@ -27,14 +27,14 @@ PUBLISHED: INLINE ~BulletSoftBodyMaterial(); INLINE static BulletSoftBodyMaterial empty(); - INLINE PN_stdfloat get_linear_stiffness() const; - INLINE void set_linear_stiffness(PN_stdfloat value); + PN_stdfloat get_linear_stiffness() const; + void set_linear_stiffness(PN_stdfloat value); - INLINE PN_stdfloat get_angular_stiffness() const; - INLINE void set_angular_stiffness(PN_stdfloat value); - - INLINE PN_stdfloat get_volume_preservation() const; - INLINE void set_volume_preservation(PN_stdfloat value); + PN_stdfloat get_angular_stiffness() const; + void set_angular_stiffness(PN_stdfloat value); + + PN_stdfloat get_volume_preservation() const; + 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); diff --git a/panda/src/bullet/bulletSoftBodyNode.I b/panda/src/bullet/bulletSoftBodyNode.I index 086c3396e7..6a60de43f1 100644 --- a/panda/src/bullet/bulletSoftBodyNode.I +++ b/panda/src/bullet/bulletSoftBodyNode.I @@ -40,56 +40,3 @@ empty() { return BulletSoftBodyNodeElement(node); } -/** - * - */ -INLINE LPoint3 BulletSoftBodyNodeElement:: -get_pos() const { - - return btVector3_to_LPoint3(_node.m_x); -} - -/** - * - */ -INLINE LVector3 BulletSoftBodyNodeElement:: -get_normal() const { - - return btVector3_to_LVector3(_node.m_n); -} - -/** - * - */ -INLINE LVector3 BulletSoftBodyNodeElement:: -get_velocity() const { - - return btVector3_to_LVector3(_node.m_v); -} - -/** - * - */ -INLINE PN_stdfloat BulletSoftBodyNodeElement:: -get_inv_mass() const { - - return (PN_stdfloat)_node.m_im; -} - -/** - * - */ -INLINE PN_stdfloat BulletSoftBodyNodeElement:: -get_area() const { - - return (PN_stdfloat)_node.m_area; -} - -/** - * - */ -INLINE int BulletSoftBodyNodeElement:: -is_attached() const { - - return (PN_stdfloat)_node.m_battach; -} diff --git a/panda/src/bullet/bulletSoftBodyNode.cxx b/panda/src/bullet/bulletSoftBodyNode.cxx index 349dbe4389..c4094af652 100644 --- a/panda/src/bullet/bulletSoftBodyNode.cxx +++ b/panda/src/bullet/bulletSoftBodyNode.cxx @@ -66,6 +66,7 @@ get_object() const { */ BulletSoftBodyConfig BulletSoftBodyNode:: get_cfg() { + LightMutexHolder holder(BulletWorld::get_global_lock()); return BulletSoftBodyConfig(_soft->m_cfg); } @@ -75,6 +76,7 @@ get_cfg() { */ BulletSoftBodyWorldInfo BulletSoftBodyNode:: get_world_info() { + LightMutexHolder holder(BulletWorld::get_global_lock()); return BulletSoftBodyWorldInfo(*(_soft->m_worldInfo)); } @@ -84,6 +86,7 @@ get_world_info() { */ int BulletSoftBodyNode:: get_num_materials() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _soft->m_materials.size(); } @@ -93,8 +96,9 @@ get_num_materials() const { */ BulletSoftBodyMaterial BulletSoftBodyNode:: get_material(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertr(idx >= 0 && idx < get_num_materials(), BulletSoftBodyMaterial::empty()); + nassertr(idx >= 0 && idx < _soft->m_materials.size(), BulletSoftBodyMaterial::empty()); btSoftBody::Material *material = _soft->m_materials[idx]; return BulletSoftBodyMaterial(*material); @@ -105,6 +109,7 @@ get_material(int idx) const { */ BulletSoftBodyMaterial BulletSoftBodyNode:: append_material() { + LightMutexHolder holder(BulletWorld::get_global_lock()); btSoftBody::Material *material = _soft->appendMaterial(); nassertr(material, BulletSoftBodyMaterial::empty()); @@ -117,6 +122,7 @@ append_material() { */ int BulletSoftBodyNode:: get_num_nodes() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _soft->m_nodes.size(); } @@ -126,6 +132,7 @@ get_num_nodes() const { */ BulletSoftBodyNodeElement BulletSoftBodyNode:: get_node(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertr(idx >=0 && idx < get_num_nodes(), BulletSoftBodyNodeElement::empty()); return BulletSoftBodyNodeElement(_soft->m_nodes[idx]); @@ -136,6 +143,7 @@ get_node(int idx) const { */ void BulletSoftBodyNode:: generate_bending_constraints(int distance, BulletSoftBodyMaterial *material) { + LightMutexHolder holder(BulletWorld::get_global_lock()); if (material) { _soft->generateBendingConstraints(distance, &(material->get_material())); @@ -150,6 +158,7 @@ generate_bending_constraints(int distance, BulletSoftBodyMaterial *material) { */ void BulletSoftBodyNode:: randomize_constraints() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->randomizeConstraints(); } @@ -159,9 +168,10 @@ randomize_constraints() { */ void BulletSoftBodyNode:: transform_changed() { - if (_sync_disable) return; + LightMutexHolder holder(BulletWorld::get_global_lock()); + NodePath np = NodePath::any_path((PandaNode *)this); CPT(TransformState) ts = np.get_net_transform(); @@ -174,7 +184,7 @@ transform_changed() { btTransform trans = TransformState_to_btTrans(ts); // Offset between current approx center and current initial transform - btVector3 pos = LVecBase3_to_btVector3(this->get_aabb().get_approx_center()); + btVector3 pos = LVecBase3_to_btVector3(this->do_get_aabb().get_approx_center()); btVector3 origin = _soft->m_initialWorldTransform.getOrigin(); btVector3 offset = pos - origin; @@ -197,7 +207,7 @@ transform_changed() { _soft->scale(new_scale); } - _sync = ts; + _sync = move(ts); } } @@ -205,16 +215,16 @@ transform_changed() { * */ void BulletSoftBodyNode:: -sync_p2b() { +do_sync_p2b() { // transform_changed(); Disabled for now... } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletSoftBodyNode:: -sync_b2p() { +do_sync_b2p() { // Render softbody if (_geom) { @@ -266,7 +276,7 @@ sync_b2p() { // Update the synchronized transform with the current approximate center of // the soft body - LVecBase3 pos = this->get_aabb().get_approx_center(); + LVecBase3 pos = this->do_get_aabb().get_approx_center(); CPT(TransformState) ts = TransformState::make_pos(pos); NodePath np = NodePath::any_path((PandaNode *)this); @@ -291,6 +301,19 @@ sync_b2p() { */ int BulletSoftBodyNode:: get_closest_node_index(LVecBase3 point, bool local) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_closest_node_index(point, local); +} + +/** + * Returns the index of the node which is closest to the given point. The + * distance between each node and the given point is computed in world space + * if local=false, and in local space if local=true. + * Assumes the lock(bullet global lock) is held by the caller + */ +int BulletSoftBodyNode:: +do_get_closest_node_index(LVecBase3 point, bool local) { btScalar max_dist_sqr = 1e30; btVector3 point_x = LVecBase3_to_btVector3(point); @@ -322,11 +345,12 @@ get_closest_node_index(LVecBase3 point, bool local) { */ void BulletSoftBodyNode:: link_geom(Geom *geom) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(geom->get_vertex_data()->has_column(InternalName::get_vertex())); nassertv(geom->get_vertex_data()->has_column(InternalName::get_normal())); - sync_p2b(); + do_sync_p2b(); _geom = geom; @@ -349,7 +373,7 @@ link_geom(Geom *geom) { while (!vertices.is_at_end()) { LVecBase3 point = vertices.get_data3(); - int node_idx = get_closest_node_index(point, true); + int node_idx = do_get_closest_node_index(point, true); indices.set_data1i(node_idx); } } @@ -359,6 +383,7 @@ link_geom(Geom *geom) { */ void BulletSoftBodyNode:: unlink_geom() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _geom = NULL; } @@ -368,6 +393,7 @@ unlink_geom() { */ void BulletSoftBodyNode:: link_curve(NurbsCurveEvaluator *curve) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(curve->get_num_vertices() == _soft->m_nodes.size()); @@ -379,6 +405,7 @@ link_curve(NurbsCurveEvaluator *curve) { */ void BulletSoftBodyNode:: unlink_curve() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _curve = NULL; } @@ -388,6 +415,7 @@ unlink_curve() { */ void BulletSoftBodyNode:: link_surface(NurbsSurfaceEvaluator *surface) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(surface->get_num_u_vertices() * surface->get_num_v_vertices() == _soft->m_nodes.size()); @@ -399,6 +427,7 @@ link_surface(NurbsSurfaceEvaluator *surface) { */ void BulletSoftBodyNode:: unlink_surface() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _surface = NULL; } @@ -408,6 +437,16 @@ unlink_surface() { */ BoundingBox BulletSoftBodyNode:: get_aabb() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_aabb(); +} + +/** + * + */ +BoundingBox BulletSoftBodyNode:: +do_get_aabb() const { btVector3 pMin; btVector3 pMax; @@ -425,6 +464,7 @@ get_aabb() const { */ void BulletSoftBodyNode:: set_volume_mass(PN_stdfloat mass) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->setVolumeMass(mass); } @@ -434,6 +474,7 @@ set_volume_mass(PN_stdfloat mass) { */ void BulletSoftBodyNode:: set_total_mass(PN_stdfloat mass, bool fromfaces) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->setTotalMass(mass, fromfaces); } @@ -443,6 +484,7 @@ set_total_mass(PN_stdfloat mass, bool fromfaces) { */ void BulletSoftBodyNode:: set_volume_density(PN_stdfloat density) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->setVolumeDensity(density); } @@ -452,6 +494,7 @@ set_volume_density(PN_stdfloat density) { */ void BulletSoftBodyNode:: set_total_density(PN_stdfloat density) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->setTotalDensity(density); } @@ -461,6 +504,7 @@ set_total_density(PN_stdfloat density) { */ void BulletSoftBodyNode:: set_mass(int node, PN_stdfloat mass) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->setMass(node, mass); } @@ -470,6 +514,7 @@ set_mass(int node, PN_stdfloat mass) { */ PN_stdfloat BulletSoftBodyNode:: get_mass(int node) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _soft->getMass(node); } @@ -479,6 +524,7 @@ get_mass(int node) const { */ PN_stdfloat BulletSoftBodyNode:: get_total_mass() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _soft->getTotalMass(); } @@ -488,6 +534,7 @@ get_total_mass() const { */ PN_stdfloat BulletSoftBodyNode:: get_volume() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _soft->getVolume(); } @@ -497,6 +544,7 @@ get_volume() const { */ void BulletSoftBodyNode:: add_force(const LVector3 &force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!force.is_nan()); _soft->addForce(LVecBase3_to_btVector3(force)); @@ -507,6 +555,7 @@ add_force(const LVector3 &force) { */ void BulletSoftBodyNode:: add_force(const LVector3 &force, int node) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!force.is_nan()); _soft->addForce(LVecBase3_to_btVector3(force), node); @@ -517,6 +566,7 @@ add_force(const LVector3 &force, int node) { */ void BulletSoftBodyNode:: set_velocity(const LVector3 &velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!velocity.is_nan()); _soft->setVelocity(LVecBase3_to_btVector3(velocity)); @@ -527,6 +577,7 @@ set_velocity(const LVector3 &velocity) { */ void BulletSoftBodyNode:: add_velocity(const LVector3 &velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!velocity.is_nan()); _soft->addVelocity(LVecBase3_to_btVector3(velocity)); @@ -537,6 +588,7 @@ add_velocity(const LVector3 &velocity) { */ void BulletSoftBodyNode:: add_velocity(const LVector3 &velocity, int node) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!velocity.is_nan()); _soft->addVelocity(LVecBase3_to_btVector3(velocity), node); @@ -547,6 +599,7 @@ add_velocity(const LVector3 &velocity, int node) { */ void BulletSoftBodyNode:: generate_clusters(int k, int maxiterations) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->generateClusters(k, maxiterations); } @@ -556,6 +609,7 @@ generate_clusters(int k, int maxiterations) { */ void BulletSoftBodyNode:: release_clusters() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->releaseClusters(); } @@ -565,6 +619,7 @@ release_clusters() { */ void BulletSoftBodyNode:: release_cluster(int index) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->releaseCluster(index); } @@ -574,6 +629,7 @@ release_cluster(int index) { */ int BulletSoftBodyNode:: get_num_clusters() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _soft->clusterCount(); } @@ -583,6 +639,7 @@ get_num_clusters() const { */ LVecBase3 BulletSoftBodyNode:: cluster_com(int cluster) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVecBase3(_soft->clusterCom(cluster)); } @@ -592,6 +649,7 @@ cluster_com(int cluster) const { */ void BulletSoftBodyNode:: set_pose(bool bvolume, bool bframe) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->setPose(bvolume, bframe); } @@ -601,11 +659,12 @@ set_pose(bool bvolume, bool bframe) { */ void BulletSoftBodyNode:: append_anchor(int node, BulletRigidBodyNode *body, bool disable) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(node < _soft->m_nodes.size()) nassertv(body); - body->sync_p2b(); + body->do_sync_p2b(); btRigidBody *ptr = (btRigidBody *)body->get_object(); _soft->appendAnchor(node, ptr, disable); @@ -616,12 +675,13 @@ append_anchor(int node, BulletRigidBodyNode *body, bool disable) { */ void BulletSoftBodyNode:: append_anchor(int node, BulletRigidBodyNode *body, const LVector3 &pivot, bool disable) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(node < _soft->m_nodes.size()) nassertv(body); nassertv(!pivot.is_nan()); - body->sync_p2b(); + body->do_sync_p2b(); btRigidBody *ptr = (btRigidBody *)body->get_object(); _soft->appendAnchor(node, ptr, LVecBase3_to_btVector3(pivot), disable); @@ -642,6 +702,7 @@ BulletSoftBodyNodeElement(btSoftBody::Node &node) : _node(node) { */ int BulletSoftBodyNode:: get_point_index(LVecBase3 p, PTA_LVecBase3 points) { + LightMutexHolder holder(BulletWorld::get_global_lock()); PN_stdfloat eps = 1.0e-6f; // TODO make this a config option @@ -979,6 +1040,7 @@ make_tet_mesh(BulletSoftBodyWorldInfo &info, const char *ele, const char *face, */ void BulletSoftBodyNode:: append_linear_joint(BulletBodyNode *body, int cluster, PN_stdfloat erp, PN_stdfloat cfm, PN_stdfloat split) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(body); @@ -998,6 +1060,7 @@ append_linear_joint(BulletBodyNode *body, int cluster, PN_stdfloat erp, PN_stdfl */ void BulletSoftBodyNode:: append_linear_joint(BulletBodyNode *body, const LPoint3 &pos, PN_stdfloat erp, PN_stdfloat cfm, PN_stdfloat split) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(body); @@ -1017,6 +1080,7 @@ append_linear_joint(BulletBodyNode *body, const LPoint3 &pos, PN_stdfloat erp, P */ void BulletSoftBodyNode:: append_angular_joint(BulletBodyNode *body, const LVector3 &axis, PN_stdfloat erp, PN_stdfloat cfm, PN_stdfloat split, BulletSoftBodyControl *control) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(body); @@ -1037,6 +1101,7 @@ append_angular_joint(BulletBodyNode *body, const LVector3 &axis, PN_stdfloat erp */ void BulletSoftBodyNode:: set_wind_velocity(const LVector3 &velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!velocity.is_nan()); _soft->setWindVelocity(LVecBase3_to_btVector3(velocity)); @@ -1047,6 +1112,67 @@ set_wind_velocity(const LVector3 &velocity) { */ LVector3 BulletSoftBodyNode:: get_wind_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_soft->getWindVelocity()); } + +/** + * + */ +LPoint3 BulletSoftBodyNodeElement:: +get_pos() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LPoint3(_node.m_x); +} + +/** + * + */ +LVector3 BulletSoftBodyNodeElement:: +get_normal() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_node.m_n); +} + +/** + * + */ +LVector3 BulletSoftBodyNodeElement:: +get_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_node.m_v); +} + +/** + * + */ +PN_stdfloat BulletSoftBodyNodeElement:: +get_inv_mass() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_node.m_im; +} + +/** + * + */ +PN_stdfloat BulletSoftBodyNodeElement:: +get_area() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_node.m_area; +} + +/** + * + */ +int BulletSoftBodyNodeElement:: +is_attached() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_node.m_battach; +} diff --git a/panda/src/bullet/bulletSoftBodyNode.h b/panda/src/bullet/bulletSoftBodyNode.h index 0f37217dfe..600e185fac 100644 --- a/panda/src/bullet/bulletSoftBodyNode.h +++ b/panda/src/bullet/bulletSoftBodyNode.h @@ -43,12 +43,12 @@ PUBLISHED: INLINE ~BulletSoftBodyNodeElement(); INLINE static BulletSoftBodyNodeElement empty(); - INLINE LPoint3 get_pos() const; - INLINE LVector3 get_velocity() const; - INLINE LVector3 get_normal() const; - INLINE PN_stdfloat get_inv_mass() const; - INLINE PN_stdfloat get_area() const; - INLINE int is_attached() const; + LPoint3 get_pos() const; + LVector3 get_velocity() const; + LVector3 get_normal() const; + PN_stdfloat get_inv_mass() const; + PN_stdfloat get_area() const; + int is_attached() const; MAKE_PROPERTY(pos, get_pos); MAKE_PROPERTY(velocity, get_velocity); @@ -221,8 +221,8 @@ PUBLISHED: public: virtual btCollisionObject *get_object() const; - void sync_p2b(); - void sync_b2p(); + void do_sync_p2b(); + void do_sync_b2p(); protected: virtual void transform_changed(); @@ -240,6 +240,9 @@ private: static int get_point_index(LVecBase3 p, PTA_LVecBase3 points); static int next_line(const char *buffer); + BoundingBox do_get_aabb() const; + int do_get_closest_node_index(LVecBase3 point, bool local); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/bullet/bulletSoftBodyShape.cxx b/panda/src/bullet/bulletSoftBodyShape.cxx index f28050ae3b..9e6aa2929b 100644 --- a/panda/src/bullet/bulletSoftBodyShape.cxx +++ b/panda/src/bullet/bulletSoftBodyShape.cxx @@ -40,6 +40,7 @@ ptr() const { */ BulletSoftBodyNode *BulletSoftBodyShape:: get_body() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); if (_shape->m_body) { return (BulletSoftBodyNode *)_shape->m_body->getUserPointer(); diff --git a/panda/src/bullet/bulletSoftBodyWorldInfo.cxx b/panda/src/bullet/bulletSoftBodyWorldInfo.cxx index ee1fddbc96..2fb6957c29 100644 --- a/panda/src/bullet/bulletSoftBodyWorldInfo.cxx +++ b/panda/src/bullet/bulletSoftBodyWorldInfo.cxx @@ -26,6 +26,7 @@ BulletSoftBodyWorldInfo(btSoftBodyWorldInfo &info) : _info(info) { */ void BulletSoftBodyWorldInfo:: garbage_collect(int lifetime) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_sparsesdf.GarbageCollect(lifetime); } @@ -35,6 +36,7 @@ garbage_collect(int lifetime) { */ void BulletSoftBodyWorldInfo:: set_air_density(PN_stdfloat density) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.air_density = (btScalar)density; } @@ -44,6 +46,7 @@ set_air_density(PN_stdfloat density) { */ void BulletSoftBodyWorldInfo:: set_water_density(PN_stdfloat density) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.water_density = (btScalar)density; } @@ -53,6 +56,7 @@ set_water_density(PN_stdfloat density) { */ void BulletSoftBodyWorldInfo:: set_water_offset(PN_stdfloat offset) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.water_offset = (btScalar)offset; } @@ -62,6 +66,7 @@ set_water_offset(PN_stdfloat offset) { */ void BulletSoftBodyWorldInfo:: set_water_normal(const LVector3 &normal) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!normal.is_nan()); _info.water_normal.setValue(normal.get_x(), normal.get_y(), normal.get_z()); @@ -72,6 +77,7 @@ set_water_normal(const LVector3 &normal) { */ void BulletSoftBodyWorldInfo:: set_gravity(const LVector3 &gravity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!gravity.is_nan()); _info.m_gravity.setValue(gravity.get_x(), gravity.get_y(), gravity.get_z()); @@ -82,6 +88,7 @@ set_gravity(const LVector3 &gravity) { */ PN_stdfloat BulletSoftBodyWorldInfo:: get_air_density() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.air_density; } @@ -91,6 +98,7 @@ get_air_density() const { */ PN_stdfloat BulletSoftBodyWorldInfo:: get_water_density() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.water_density; } @@ -100,6 +108,7 @@ get_water_density() const { */ PN_stdfloat BulletSoftBodyWorldInfo:: get_water_offset() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.water_offset; } @@ -109,6 +118,7 @@ get_water_offset() const { */ LVector3 BulletSoftBodyWorldInfo:: get_water_normal() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_info.water_normal); } @@ -118,6 +128,7 @@ get_water_normal() const { */ LVector3 BulletSoftBodyWorldInfo:: get_gravity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_info.m_gravity); } diff --git a/panda/src/bullet/bulletSphereShape.I b/panda/src/bullet/bulletSphereShape.I index 340d9b7f2e..d410322298 100644 --- a/panda/src/bullet/bulletSphereShape.I +++ b/panda/src/bullet/bulletSphereShape.I @@ -21,26 +21,9 @@ INLINE BulletSphereShape:: } /** - * - */ -INLINE BulletSphereShape:: -BulletSphereShape(const BulletSphereShape ©) : - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletSphereShape:: -operator = (const BulletSphereShape ©) { - _shape = copy._shape; -} - -/** - * + * 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..7b3fee62a4 100644 --- a/panda/src/bullet/bulletSphereShape.cxx +++ b/panda/src/bullet/bulletSphereShape.cxx @@ -19,12 +19,35 @@ TypeHandle BulletSphereShape::_type_handle; * */ BulletSphereShape:: -BulletSphereShape(PN_stdfloat radius) { +BulletSphereShape(PN_stdfloat radius) : _radius(radius) { _shape = new btSphereShape(radius); _shape->setUserPointer(this); } +/** + * + */ +BulletSphereShape:: +BulletSphereShape(const BulletSphereShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _radius = copy._radius; +} + +/** + * + */ +void BulletSphereShape:: +operator = (const BulletSphereShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _radius = copy._radius; +} + + /** * */ @@ -57,8 +80,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 +109,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 815728c908..b11d1c4a32 100644 --- a/panda/src/bullet/bulletSphereShape.h +++ b/panda/src/bullet/bulletSphereShape.h @@ -31,9 +31,9 @@ private: INLINE BulletSphereShape() : _shape(NULL) {}; PUBLISHED: - BulletSphereShape(PN_stdfloat radius); - INLINE BulletSphereShape(const BulletSphereShape ©); - INLINE void operator = (const BulletSphereShape ©); + explicit BulletSphereShape(PN_stdfloat radius); + BulletSphereShape(const BulletSphereShape ©); + void operator = (const BulletSphereShape ©); INLINE ~BulletSphereShape(); INLINE PN_stdfloat get_radius() const; @@ -47,6 +47,7 @@ public: private: btSphereShape *_shape; + PN_stdfloat _radius; public: static void register_with_read_factory(); diff --git a/panda/src/bullet/bulletSphericalConstraint.cxx b/panda/src/bullet/bulletSphericalConstraint.cxx index 021dd69777..8686e1d546 100644 --- a/panda/src/bullet/bulletSphericalConstraint.cxx +++ b/panda/src/bullet/bulletSphericalConstraint.cxx @@ -61,6 +61,7 @@ ptr() const { */ void BulletSphericalConstraint:: set_pivot_a(const LPoint3 &pivot_a) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!pivot_a.is_nan()); _constraint->setPivotA(LVecBase3_to_btVector3(pivot_a)); @@ -71,6 +72,7 @@ set_pivot_a(const LPoint3 &pivot_a) { */ void BulletSphericalConstraint:: set_pivot_b(const LPoint3 &pivot_b) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!pivot_b.is_nan()); _constraint->setPivotB(LVecBase3_to_btVector3(pivot_b)); @@ -81,6 +83,7 @@ set_pivot_b(const LPoint3 &pivot_b) { */ LPoint3 BulletSphericalConstraint:: get_pivot_in_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_constraint->getPivotInA()); } @@ -90,6 +93,7 @@ get_pivot_in_a() const { */ LPoint3 BulletSphericalConstraint:: get_pivot_in_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_constraint->getPivotInB()); } diff --git a/panda/src/bullet/bulletSphericalConstraint.h b/panda/src/bullet/bulletSphericalConstraint.h index 4aad8b4ee9..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 diff --git a/panda/src/bullet/bulletTranslationalLimitMotor.I b/panda/src/bullet/bulletTranslationalLimitMotor.I index 2972994e39..7b41ab9618 100644 --- a/panda/src/bullet/bulletTranslationalLimitMotor.I +++ b/panda/src/bullet/bulletTranslationalLimitMotor.I @@ -14,164 +14,7 @@ /** * */ -INLINE bool BulletTranslationalLimitMotor:: -is_limited(int axis) const { +INLINE BulletTranslationalLimitMotor:: +~BulletTranslationalLimitMotor() { - nassertr((0 <= axis) && (axis <= 2), false); - return _motor.isLimited(axis); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_motor_enabled(int axis, bool enabled) { - - nassertv((0 <= axis) && (axis <= 2)); - _motor.m_enableMotor[axis] = enabled; -} - -/** - * - */ -INLINE bool BulletTranslationalLimitMotor:: -get_motor_enabled(int axis) const { - - nassertr((0 <= axis) && (axis <= 2), false); - return _motor.m_enableMotor[axis]; -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_low_limit(const LVecBase3 &limit) { - - nassertv(!limit.is_nan()); - _motor.m_lowerLimit = LVecBase3_to_btVector3(limit); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_high_limit(const LVecBase3 &limit) { - - nassertv(!limit.is_nan()); - _motor.m_upperLimit = LVecBase3_to_btVector3(limit); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_target_velocity(const LVecBase3 &velocity) { - - nassertv(!velocity.is_nan()); - _motor.m_targetVelocity = LVecBase3_to_btVector3(velocity); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_max_motor_force(const LVecBase3 &force) { - - nassertv(!force.is_nan()); - _motor.m_maxMotorForce = LVecBase3_to_btVector3(force); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_damping(PN_stdfloat damping) { - - _motor.m_damping = (btScalar)damping; -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_softness(PN_stdfloat softness) { - - _motor.m_limitSoftness = (btScalar)softness; -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_restitution(PN_stdfloat restitution) { - - _motor.m_restitution = (btScalar)restitution; -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_normal_cfm(const LVecBase3 &cfm) { - - nassertv(!cfm.is_nan()); - _motor.m_normalCFM = LVecBase3_to_btVector3(cfm); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_stop_cfm(const LVecBase3 &cfm) { - - nassertv(!cfm.is_nan()); - _motor.m_stopCFM = LVecBase3_to_btVector3(cfm); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_stop_erp(const LVecBase3 &erp) { - - nassertv(!erp.is_nan()); - _motor.m_stopERP = LVecBase3_to_btVector3(erp); -} - -/** - * Retrieves the current value of angle: 0 = free, 1 = at low limit, 2 = at - * high limit. - */ -INLINE int BulletTranslationalLimitMotor:: -get_current_limit(int axis) const { - - nassertr((0 <= axis) && (axis <= 2), false); - return _motor.m_currentLimit[axis]; -} - -/** - * - */ -INLINE LVector3 BulletTranslationalLimitMotor:: -get_current_error() const { - - return btVector3_to_LVector3(_motor.m_currentLimitError); -} - -/** - * - */ -INLINE LPoint3 BulletTranslationalLimitMotor:: -get_current_diff() const { - - return btVector3_to_LPoint3(_motor.m_currentLinearDiff); -} - -/** - * - */ -INLINE LVector3 BulletTranslationalLimitMotor:: -get_accumulated_impulse() const { - - return btVector3_to_LVector3(_motor.m_accumulatedImpulse); } diff --git a/panda/src/bullet/bulletTranslationalLimitMotor.cxx b/panda/src/bullet/bulletTranslationalLimitMotor.cxx index fdf0b55501..48dd7ae3e8 100644 --- a/panda/src/bullet/bulletTranslationalLimitMotor.cxx +++ b/panda/src/bullet/bulletTranslationalLimitMotor.cxx @@ -34,7 +34,182 @@ BulletTranslationalLimitMotor(const BulletTranslationalLimitMotor ©) /** * */ -BulletTranslationalLimitMotor:: -~BulletTranslationalLimitMotor() { +bool BulletTranslationalLimitMotor:: +is_limited(int axis) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + nassertr((0 <= axis) && (axis <= 2), false); + return _motor.isLimited(axis); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_motor_enabled(int axis, bool enabled) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv((0 <= axis) && (axis <= 2)); + _motor.m_enableMotor[axis] = enabled; +} + +/** + * + */ +bool BulletTranslationalLimitMotor:: +get_motor_enabled(int axis) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr((0 <= axis) && (axis <= 2), false); + return _motor.m_enableMotor[axis]; +} + + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_low_limit(const LVecBase3 &limit) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!limit.is_nan()); + _motor.m_lowerLimit = LVecBase3_to_btVector3(limit); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_high_limit(const LVecBase3 &limit) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!limit.is_nan()); + _motor.m_upperLimit = LVecBase3_to_btVector3(limit); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_target_velocity(const LVecBase3 &velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!velocity.is_nan()); + _motor.m_targetVelocity = LVecBase3_to_btVector3(velocity); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_max_motor_force(const LVecBase3 &force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!force.is_nan()); + _motor.m_maxMotorForce = LVecBase3_to_btVector3(force); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_damping(PN_stdfloat damping) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_damping = (btScalar)damping; +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_softness(PN_stdfloat softness) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_limitSoftness = (btScalar)softness; +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_restitution(PN_stdfloat restitution) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_restitution = (btScalar)restitution; +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_normal_cfm(const LVecBase3 &cfm) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!cfm.is_nan()); + _motor.m_normalCFM = LVecBase3_to_btVector3(cfm); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_stop_cfm(const LVecBase3 &cfm) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!cfm.is_nan()); + _motor.m_stopCFM = LVecBase3_to_btVector3(cfm); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_stop_erp(const LVecBase3 &erp) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!erp.is_nan()); + _motor.m_stopERP = LVecBase3_to_btVector3(erp); +} + +/** + * Retrieves the current value of angle: 0 = free, 1 = at low limit, 2 = at + * high limit. + */ +int BulletTranslationalLimitMotor:: +get_current_limit(int axis) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr((0 <= axis) && (axis <= 2), false); + return _motor.m_currentLimit[axis]; +} + +/** + * + */ +LVector3 BulletTranslationalLimitMotor:: +get_current_error() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_motor.m_currentLimitError); +} + +/** + * + */ +LPoint3 BulletTranslationalLimitMotor:: +get_current_diff() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LPoint3(_motor.m_currentLinearDiff); +} + +/** + * + */ +LVector3 BulletTranslationalLimitMotor:: +get_accumulated_impulse() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_motor.m_accumulatedImpulse); } diff --git a/panda/src/bullet/bulletTranslationalLimitMotor.h b/panda/src/bullet/bulletTranslationalLimitMotor.h index f339daefef..97c9909ede 100644 --- a/panda/src/bullet/bulletTranslationalLimitMotor.h +++ b/panda/src/bullet/bulletTranslationalLimitMotor.h @@ -28,26 +28,26 @@ class EXPCL_PANDABULLET BulletTranslationalLimitMotor { PUBLISHED: BulletTranslationalLimitMotor(const BulletTranslationalLimitMotor ©); - ~BulletTranslationalLimitMotor(); + INLINE ~BulletTranslationalLimitMotor(); - 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_max_motor_force(const LVecBase3 &force); - INLINE void set_damping(PN_stdfloat damping); - INLINE void set_softness(PN_stdfloat softness); - INLINE void set_restitution(PN_stdfloat restitution); - INLINE void set_normal_cfm(const LVecBase3 &cfm); - INLINE void set_stop_erp(const LVecBase3 &erp); - INLINE void set_stop_cfm(const LVecBase3 &cfm); + void set_motor_enabled(int axis, bool enable); + void set_low_limit(const LVecBase3 &limit); + void set_high_limit(const LVecBase3 &limit); + void set_target_velocity(const LVecBase3 &velocity); + void set_max_motor_force(const LVecBase3 &force); + void set_damping(PN_stdfloat damping); + void set_softness(PN_stdfloat softness); + void set_restitution(PN_stdfloat restitution); + void set_normal_cfm(const LVecBase3 &cfm); + void set_stop_erp(const LVecBase3 &erp); + void set_stop_cfm(const LVecBase3 &cfm); - INLINE bool is_limited(int axis) const; - INLINE bool get_motor_enabled(int axis) const; - INLINE int get_current_limit(int axis) const; - INLINE LVector3 get_current_error() const; - INLINE LPoint3 get_current_diff() const; - INLINE LVector3 get_accumulated_impulse() const; + bool is_limited(int axis) const; + bool get_motor_enabled(int axis) const; + int get_current_limit(int axis) const; + LVector3 get_current_error() const; + LPoint3 get_current_diff() const; + LVector3 get_accumulated_impulse() const; MAKE_PROPERTY(current_error, get_current_error); MAKE_PROPERTY(current_diff, get_current_diff); diff --git a/panda/src/bullet/bulletTriangleMesh.I b/panda/src/bullet/bulletTriangleMesh.I index 4c3b8f83b8..33b9b9bf47 100644 --- a/panda/src/bullet/bulletTriangleMesh.I +++ b/panda/src/bullet/bulletTriangleMesh.I @@ -14,19 +14,9 @@ /** * */ -INLINE BulletTriangleMesh:: -~BulletTriangleMesh() { - - delete _mesh; -} - -/** - * - */ -btTriangleMesh *BulletTriangleMesh:: +INLINE btStridingMeshInterface *BulletTriangleMesh:: ptr() const { - - return _mesh; + return (btStridingMeshInterface *)&_mesh; } /** @@ -34,7 +24,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..e2d3e0b242 100644 --- a/panda/src/bullet/bulletTriangleMesh.cxx +++ b/panda/src/bullet/bulletTriangleMesh.cxx @@ -23,150 +23,328 @@ 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 vertices in this triangle mesh. */ -int BulletTriangleMesh:: -get_num_triangles() const { +size_t BulletTriangleMesh:: +get_num_vertices() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); - return _mesh->getNumTriangles(); + return _vertices.size(); } /** - * + * Returns the vertex at the given vertex index. + */ +LPoint3 BulletTriangleMesh:: +get_vertex(size_t index) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + 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. + */ +LVecBase3i BulletTriangleMesh:: +get_triangle(size_t index) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + index *= 3; + nassertr(index + 2 < _indices.size(), LVecBase3i::zero()); + return LVecBase3i(_indices[index], _indices[index + 1], _indices[index + 2]); +} + +/** + * Returns the number of triangles in this triangle mesh. + * Assumes the lock(bullet global lock) is held by the caller + */ +size_t BulletTriangleMesh:: +do_get_num_triangles() const { + + return _indices.size() / 3; +} + +/** + * Returns the number of triangles in this triangle mesh. + */ +size_t BulletTriangleMesh:: +get_num_triangles() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_num_triangles(); +} + +/** + * 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) { + LightMutexHolder holder(BulletWorld::get_global_lock()); - _mesh->preallocateVertices(num_verts); - _mesh->preallocateIndices(num_indices); + _vertices.reserve(num_verts); + _indices.reserve(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. + * Assumes the lock(bullet global lock) is held by the caller */ void BulletTriangleMesh:: -add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remove_duplicate_vertices) { +do_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]; } /** + * 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) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_add_triangle(p0, p1, p2, remove_duplicate_vertices); +} + +/** + * 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) { + LightMutexHolder holder(BulletWorld::get_global_lock()); - _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 { + LightMutexHolder holder(BulletWorld::get_global_lock()); - 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) { + LightMutexHolder holder(BulletWorld::get_global_lock()); 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) { + LightMutexHolder holder(BulletWorld::get_global_lock()); - // Convert vertices - btVector3 *vertices = new btVector3[points.size()]; + btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0]; - 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++; + _indices.reserve(_indices.size() + indices.size()); + + 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 +352,9 @@ add_array(const PTA_LVecBase3 &points, const PTA_int &indices, bool remove_dupli */ void BulletTriangleMesh:: output(ostream &out) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); - out << get_type() << ", " << _mesh->getNumTriangles(); + out << get_type() << ", " << _indices.size() / 3 << " triangles"; } /** @@ -183,19 +362,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 +414,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 +437,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 +476,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 71dfe39a50..0f9cb8174b 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,20 +48,40 @@ 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; - MAKE_PROPERTY(num_triangles, get_num_triangles); +public: + size_t get_num_vertices() const; + LPoint3 get_vertex(size_t index) const; + + LVecBase3i get_triangle(size_t index) const; + + size_t do_get_num_triangles() const; + void do_add_triangle(const LPoint3 &p0, + const LPoint3 &p1, + const LPoint3 &p2, + bool remove_duplicate_vertices=false); + +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 btTriangleMesh *ptr() const; + 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.I b/panda/src/bullet/bulletTriangleMeshShape.I index abb51607b4..f178723ebc 100644 --- a/panda/src/bullet/bulletTriangleMeshShape.I +++ b/panda/src/bullet/bulletTriangleMeshShape.I @@ -11,27 +11,6 @@ * @date 2010-02-09 */ -/** - * - */ -INLINE BulletTriangleMeshShape:: -BulletTriangleMeshShape(const BulletTriangleMeshShape ©) : - _bvh_shape(copy._bvh_shape), - _gimpact_shape(copy._gimpact_shape), - _mesh(copy._mesh) { -} - -/** - * - */ -INLINE void BulletTriangleMeshShape:: -operator = (const BulletTriangleMeshShape ©) { - - _bvh_shape = copy._bvh_shape; - _gimpact_shape = copy._gimpact_shape; - _mesh = copy._mesh; -} - /** * */ diff --git a/panda/src/bullet/bulletTriangleMeshShape.cxx b/panda/src/bullet/bulletTriangleMeshShape.cxx index 62252db221..61905a406a 100644 --- a/panda/src/bullet/bulletTriangleMeshShape.cxx +++ b/panda/src/bullet/bulletTriangleMeshShape.cxx @@ -36,6 +36,7 @@ BulletTriangleMeshShape() : /** * The parameters 'compress' and 'bvh' are only used if 'dynamic' is set to * FALSE. + * Assumes the lock(bullet global lock) is held by the caller */ BulletTriangleMeshShape:: BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress, bool bvh) : @@ -50,7 +51,7 @@ BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress, b } // Assert that mesh has at least one triangle - if (mesh->get_num_triangles() == 0) { + if (mesh->do_get_num_triangles() == 0) { bullet_cat.warning() << "mesh has zero triangles! adding degenerated triangle." << endl; mesh->add_triangle(LPoint3::zero(), LPoint3::zero(), LPoint3::zero()); } @@ -78,6 +79,30 @@ BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress, b } } +/** + * + */ +BulletTriangleMeshShape:: +BulletTriangleMeshShape(const BulletTriangleMeshShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _bvh_shape = copy._bvh_shape; + _gimpact_shape = copy._gimpact_shape; + _mesh = copy._mesh; +} + +/** + * + */ +void BulletTriangleMeshShape:: +operator = (const BulletTriangleMeshShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _bvh_shape = copy._bvh_shape; + _gimpact_shape = copy._gimpact_shape; + _mesh = copy._mesh; +} + /** * */ @@ -100,6 +125,7 @@ ptr() const { */ void BulletTriangleMeshShape:: refit_tree(const LPoint3 &aabb_min, const LPoint3 &aabb_max) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!aabb_max.is_nan()); nassertv(!aabb_max.is_nan()); @@ -124,6 +150,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 +173,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 +213,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 022df31655..079fb2aeb1 100644 --- a/panda/src/bullet/bulletTriangleMeshShape.h +++ b/panda/src/bullet/bulletTriangleMeshShape.h @@ -31,9 +31,9 @@ private: INLINE BulletTriangleMeshShape(); PUBLISHED: - BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress=true, bool bvh=true); - INLINE BulletTriangleMeshShape(const BulletTriangleMeshShape ©); - INLINE void operator = (const BulletTriangleMeshShape ©); + explicit BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress=true, bool bvh=true); + BulletTriangleMeshShape(const BulletTriangleMeshShape ©); + void operator = (const BulletTriangleMeshShape ©); INLINE ~BulletTriangleMeshShape(); void refit_tree(const LPoint3 &aabb_min, const LPoint3 &aabb_max); @@ -53,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.I b/panda/src/bullet/bulletVehicle.I index b167ab5303..f4599a63e4 100644 --- a/panda/src/bullet/bulletVehicle.I +++ b/panda/src/bullet/bulletVehicle.I @@ -18,6 +18,7 @@ INLINE BulletVehicle:: ~BulletVehicle() { delete _vehicle; + delete _raycaster; } /** @@ -40,119 +41,3 @@ get_tuning() { return _tuning; } -/** - * Returns the number of wheels this vehicle has. - */ -INLINE int BulletVehicle:: -get_num_wheels() const { - - return _vehicle->getNumWheels(); -} - -/** - * - */ -void BulletVehicleTuning:: -set_suspension_stiffness(PN_stdfloat value) { - - _.m_suspensionStiffness = (btScalar)value; -} - -/** - * - */ -void BulletVehicleTuning:: -set_suspension_compression(PN_stdfloat value) { - - _.m_suspensionCompression = (btScalar)value; -} - -/** - * - */ -void BulletVehicleTuning:: -set_suspension_damping(PN_stdfloat value) { - - _.m_suspensionDamping = (btScalar)value; -} - -/** - * - */ -void BulletVehicleTuning:: -set_max_suspension_travel_cm(PN_stdfloat value) { - - _.m_maxSuspensionTravelCm = (btScalar)value; -} - -/** - * - */ -void BulletVehicleTuning:: -set_friction_slip(PN_stdfloat value) { - - _.m_frictionSlip = (btScalar)value; -} - -/** - * - */ -void BulletVehicleTuning:: -set_max_suspension_force(PN_stdfloat value) { - - _.m_maxSuspensionForce = (btScalar)value; -} - -/** - * - */ -PN_stdfloat BulletVehicleTuning:: -get_suspension_stiffness() const { - - return (PN_stdfloat)_.m_suspensionStiffness; -} - -/** - * - */ -PN_stdfloat BulletVehicleTuning:: -get_suspension_compression() const { - - return (PN_stdfloat)_.m_suspensionCompression; -} - -/** - * - */ -PN_stdfloat BulletVehicleTuning:: -get_suspension_damping() const { - - return (PN_stdfloat)_.m_suspensionDamping; -} - -/** - * - */ -PN_stdfloat BulletVehicleTuning:: -get_max_suspension_travel_cm() const { - - return (PN_stdfloat)_.m_maxSuspensionTravelCm; -} - -/** - * - */ -PN_stdfloat BulletVehicleTuning:: -get_friction_slip() const { - - return (PN_stdfloat)_.m_frictionSlip; -} - -/** - * - */ -PN_stdfloat BulletVehicleTuning:: -get_max_suspension_force() const { - - return (PN_stdfloat)_.m_maxSuspensionForce; -} diff --git a/panda/src/bullet/bulletVehicle.cxx b/panda/src/bullet/bulletVehicle.cxx index 381f8ab6c8..e2d74c131d 100644 --- a/panda/src/bullet/bulletVehicle.cxx +++ b/panda/src/bullet/bulletVehicle.cxx @@ -39,6 +39,7 @@ BulletVehicle(BulletWorld *world, BulletRigidBodyNode *chassis) { */ void BulletVehicle:: set_coordinate_system(BulletUpAxis up) { + LightMutexHolder holder(BulletWorld::get_global_lock()); switch (up) { case X_up: @@ -62,18 +63,30 @@ set_coordinate_system(BulletUpAxis up) { */ LVector3 BulletVehicle:: get_forward_vector() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_vehicle->getForwardVector()); } +/** + * Returns the chassis of this vehicle. The chassis is a rigid body node. + * Assumes the lock(bullet global lock) is held by the caller + */ +BulletRigidBodyNode *BulletVehicle:: +do_get_chassis() { + + btRigidBody *bodyPtr = _vehicle->getRigidBody(); + return (bodyPtr) ? (BulletRigidBodyNode *)bodyPtr->getUserPointer() : NULL; +} + /** * Returns the chassis of this vehicle. The chassis is a rigid body node. */ BulletRigidBodyNode *BulletVehicle:: get_chassis() { + LightMutexHolder holder(BulletWorld::get_global_lock()); - btRigidBody *bodyPtr = _vehicle->getRigidBody(); - return (bodyPtr) ? (BulletRigidBodyNode *)bodyPtr->getUserPointer() : NULL; + return do_get_chassis(); } /** @@ -82,6 +95,7 @@ get_chassis() { */ PN_stdfloat BulletVehicle:: get_current_speed_km_hour() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_vehicle->getCurrentSpeedKmHour(); } @@ -91,6 +105,7 @@ get_current_speed_km_hour() const { */ void BulletVehicle:: reset_suspension() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _vehicle->resetSuspension(); } @@ -100,8 +115,9 @@ reset_suspension() { */ PN_stdfloat BulletVehicle:: get_steering_value(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertr(idx < get_num_wheels(), 0.0f); + nassertr(idx < _vehicle->getNumWheels(), 0.0f); return rad_2_deg(_vehicle->getSteeringValue(idx)); } @@ -110,8 +126,9 @@ get_steering_value(int idx) const { */ void BulletVehicle:: set_steering_value(PN_stdfloat steering, int idx) { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertv(idx < get_num_wheels()); + nassertv(idx < _vehicle->getNumWheels()); _vehicle->setSteeringValue(deg_2_rad(steering), idx); } @@ -120,8 +137,9 @@ set_steering_value(PN_stdfloat steering, int idx) { */ void BulletVehicle:: apply_engine_force(PN_stdfloat force, int idx) { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertv(idx < get_num_wheels()); + nassertv(idx < _vehicle->getNumWheels()); _vehicle->applyEngineForce(force, idx); } @@ -130,8 +148,9 @@ apply_engine_force(PN_stdfloat force, int idx) { */ void BulletVehicle:: set_brake(PN_stdfloat brake, int idx) { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertv(idx < get_num_wheels()); + nassertv(idx < _vehicle->getNumWheels()); _vehicle->setBrake(brake, idx); } @@ -140,6 +159,7 @@ set_brake(PN_stdfloat brake, int idx) { */ void BulletVehicle:: set_pitch_control(PN_stdfloat pitch) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _vehicle->setPitchControl(pitch); } @@ -149,6 +169,7 @@ set_pitch_control(PN_stdfloat pitch) { */ BulletWheel BulletVehicle:: create_wheel() { + LightMutexHolder holder(BulletWorld::get_global_lock()); btVector3 pos(0.0, 0.0, 0.0); btVector3 direction = get_axis(_vehicle->getUpAxis()); @@ -182,24 +203,35 @@ get_axis(int idx) { } } +/** + * Returns the number of wheels this vehicle has. + */ +int BulletVehicle:: +get_num_wheels() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _vehicle->getNumWheels(); +} + /** * Returns the BulletWheel with index idx. Causes an AssertionError if idx is * equal or larger than the number of wheels. */ BulletWheel BulletVehicle:: get_wheel(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertr(idx < get_num_wheels(), BulletWheel::empty()); + nassertr(idx < _vehicle->getNumWheels(), BulletWheel::empty()); return BulletWheel(_vehicle->getWheelInfo(idx)); } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletVehicle:: -sync_b2p() { +do_sync_b2p() { - for (int i=0; i < get_num_wheels(); i++) { + for (int i=0; i < _vehicle->getNumWheels(); i++) { btWheelInfo info = _vehicle->getWheelInfo(i); PandaNode *node = (PandaNode *)info.m_clientInfo; @@ -216,3 +248,124 @@ sync_b2p() { } } } + +/** + * + */ +void BulletVehicleTuning:: +set_suspension_stiffness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _.m_suspensionStiffness = (btScalar)value; +} + +/** + * + */ +void BulletVehicleTuning:: +set_suspension_compression(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _.m_suspensionCompression = (btScalar)value; +} + +/** + * + */ +void BulletVehicleTuning:: +set_suspension_damping(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _.m_suspensionDamping = (btScalar)value; +} + +/** + * + */ +void BulletVehicleTuning:: +set_max_suspension_travel_cm(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _.m_maxSuspensionTravelCm = (btScalar)value; +} + +/** + * + */ +void BulletVehicleTuning:: +set_friction_slip(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _.m_frictionSlip = (btScalar)value; +} + +/** + * + */ +void BulletVehicleTuning:: +set_max_suspension_force(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _.m_maxSuspensionForce = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletVehicleTuning:: +get_suspension_stiffness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_.m_suspensionStiffness; +} + +/** + * + */ +PN_stdfloat BulletVehicleTuning:: +get_suspension_compression() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_.m_suspensionCompression; +} + +/** + * + */ +PN_stdfloat BulletVehicleTuning:: +get_suspension_damping() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_.m_suspensionDamping; +} + +/** + * + */ +PN_stdfloat BulletVehicleTuning:: +get_max_suspension_travel_cm() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_.m_maxSuspensionTravelCm; +} + +/** + * + */ +PN_stdfloat BulletVehicleTuning:: +get_friction_slip() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_.m_frictionSlip; +} + +/** + * + */ +PN_stdfloat BulletVehicleTuning:: +get_max_suspension_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_.m_maxSuspensionForce; +} + diff --git a/panda/src/bullet/bulletVehicle.h b/panda/src/bullet/bulletVehicle.h index 8b7f7862c6..9f9da751c3 100644 --- a/panda/src/bullet/bulletVehicle.h +++ b/panda/src/bullet/bulletVehicle.h @@ -32,19 +32,19 @@ class BulletWheel; class EXPCL_PANDABULLET BulletVehicleTuning { PUBLISHED: - INLINE void set_suspension_stiffness(PN_stdfloat value); - INLINE void set_suspension_compression(PN_stdfloat value); - INLINE void set_suspension_damping(PN_stdfloat value); - INLINE void set_max_suspension_travel_cm(PN_stdfloat value); - INLINE void set_friction_slip(PN_stdfloat value); - INLINE void set_max_suspension_force(PN_stdfloat value); + void set_suspension_stiffness(PN_stdfloat value); + void set_suspension_compression(PN_stdfloat value); + void set_suspension_damping(PN_stdfloat value); + void set_max_suspension_travel_cm(PN_stdfloat value); + void set_friction_slip(PN_stdfloat value); + void set_max_suspension_force(PN_stdfloat value); - INLINE PN_stdfloat get_suspension_stiffness() const; - INLINE PN_stdfloat get_suspension_compression() const; - INLINE PN_stdfloat get_suspension_damping() const; - INLINE PN_stdfloat get_max_suspension_travel_cm() const; - INLINE PN_stdfloat get_friction_slip() const; - INLINE PN_stdfloat get_max_suspension_force() const; + PN_stdfloat get_suspension_stiffness() const; + PN_stdfloat get_suspension_compression() const; + PN_stdfloat get_suspension_damping() const; + PN_stdfloat get_max_suspension_travel_cm() const; + PN_stdfloat get_friction_slip() const; + 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); @@ -87,7 +87,7 @@ PUBLISHED: // Wheels BulletWheel create_wheel(); - INLINE int get_num_wheels() const; + int get_num_wheels() const; BulletWheel get_wheel(int idx) const; MAKE_SEQ(get_wheels, get_num_wheels, get_wheel); @@ -102,8 +102,9 @@ PUBLISHED: public: INLINE btRaycastVehicle *get_vehicle() const; + BulletRigidBodyNode *do_get_chassis(); - void sync_b2p(); + void do_sync_b2p(); private: btRaycastVehicle *_vehicle; diff --git a/panda/src/bullet/bulletWheel.I b/panda/src/bullet/bulletWheel.I index ed724e184b..cbf82a40bc 100644 --- a/panda/src/bullet/bulletWheel.I +++ b/panda/src/bullet/bulletWheel.I @@ -40,74 +40,3 @@ empty() { return BulletWheel(info); } -/** - * - */ -INLINE bool BulletWheelRaycastInfo:: -is_in_contact() const { - - return _info.m_isInContact; -} - -/** - * - */ -INLINE PN_stdfloat BulletWheelRaycastInfo:: -get_suspension_length() const { - - return _info.m_suspensionLength; -} - -/** - * - */ -INLINE LPoint3 BulletWheelRaycastInfo:: -get_contact_point_ws() const { - - return btVector3_to_LPoint3(_info.m_contactPointWS); -} - -/** - * - */ -INLINE LPoint3 BulletWheelRaycastInfo:: -get_hard_point_ws() const { - - return btVector3_to_LPoint3(_info.m_hardPointWS); -} - -/** - * - */ -INLINE LVector3 BulletWheelRaycastInfo:: -get_contact_normal_ws() const { - - return btVector3_to_LVector3(_info.m_contactNormalWS); -} - -/** - * - */ -INLINE LVector3 BulletWheelRaycastInfo:: -get_wheel_direction_ws() const { - - return btVector3_to_LVector3(_info.m_wheelDirectionWS); -} - -/** - * - */ -INLINE LVector3 BulletWheelRaycastInfo:: -get_wheel_axle_ws() const { - - return btVector3_to_LVector3(_info.m_wheelAxleWS); -} - -/** - * - */ -INLINE PandaNode *BulletWheelRaycastInfo:: -get_ground_object() const { - - return _info.m_groundObject ? (PandaNode *)_info.m_groundObject : NULL; -} diff --git a/panda/src/bullet/bulletWheel.cxx b/panda/src/bullet/bulletWheel.cxx index 687e5ea892..149dbd8bf5 100644 --- a/panda/src/bullet/bulletWheel.cxx +++ b/panda/src/bullet/bulletWheel.cxx @@ -34,6 +34,7 @@ BulletWheelRaycastInfo(btWheelInfo::RaycastInfo &info) : _info(info) { */ BulletWheelRaycastInfo BulletWheel:: get_raycast_info() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return BulletWheelRaycastInfo(_info.m_raycastInfo); } @@ -43,6 +44,7 @@ get_raycast_info() const { */ PN_stdfloat BulletWheel:: get_suspension_rest_length() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.getSuspensionRestLength(); } @@ -52,6 +54,7 @@ get_suspension_rest_length() const { */ void BulletWheel:: set_suspension_stiffness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_suspensionStiffness = (btScalar)value; } @@ -61,6 +64,7 @@ set_suspension_stiffness(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_suspension_stiffness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_suspensionStiffness; } @@ -71,6 +75,7 @@ get_suspension_stiffness() const { */ void BulletWheel:: set_max_suspension_travel_cm(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_maxSuspensionTravelCm = (btScalar)value; } @@ -80,6 +85,7 @@ set_max_suspension_travel_cm(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_max_suspension_travel_cm() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_maxSuspensionTravelCm; } @@ -89,6 +95,7 @@ get_max_suspension_travel_cm() const { */ void BulletWheel:: set_friction_slip(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_frictionSlip = (btScalar)value; } @@ -98,6 +105,7 @@ set_friction_slip(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_friction_slip() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_frictionSlip; } @@ -107,6 +115,7 @@ get_friction_slip() const { */ void BulletWheel:: set_max_suspension_force(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_maxSuspensionForce = (btScalar)value; } @@ -116,6 +125,7 @@ set_max_suspension_force(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_max_suspension_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_maxSuspensionForce; } @@ -125,6 +135,7 @@ get_max_suspension_force() const { */ void BulletWheel:: set_wheels_damping_compression(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_wheelsDampingCompression = (btScalar)value; } @@ -134,6 +145,7 @@ set_wheels_damping_compression(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_wheels_damping_compression() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_wheelsDampingCompression; } @@ -143,6 +155,7 @@ get_wheels_damping_compression() const { */ void BulletWheel:: set_wheels_damping_relaxation(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_wheelsDampingRelaxation = (btScalar)value; } @@ -152,6 +165,7 @@ set_wheels_damping_relaxation(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_wheels_damping_relaxation() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_wheelsDampingRelaxation; } @@ -164,6 +178,7 @@ get_wheels_damping_relaxation() const { */ void BulletWheel:: set_roll_influence(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_rollInfluence = (btScalar)value; } @@ -174,6 +189,7 @@ set_roll_influence(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_roll_influence() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_rollInfluence; } @@ -183,6 +199,7 @@ get_roll_influence() const { */ void BulletWheel:: set_wheel_radius(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_wheelsRadius = (btScalar)value; } @@ -192,6 +209,7 @@ set_wheel_radius(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_wheel_radius() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_wheelsRadius; } @@ -201,6 +219,7 @@ get_wheel_radius() const { */ void BulletWheel:: set_steering(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_steering = (btScalar)value; } @@ -210,6 +229,7 @@ set_steering(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_steering() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_steering; } @@ -219,6 +239,7 @@ get_steering() const { */ void BulletWheel:: set_rotation(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_rotation = (btScalar)value; } @@ -228,6 +249,7 @@ set_rotation(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_rotation() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_rotation; } @@ -237,6 +259,7 @@ get_rotation() const { */ void BulletWheel:: set_delta_rotation(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_deltaRotation = (btScalar)value; } @@ -246,6 +269,7 @@ set_delta_rotation(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_delta_rotation() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_deltaRotation; } @@ -255,6 +279,7 @@ get_delta_rotation() const { */ void BulletWheel:: set_engine_force(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_engineForce = (btScalar)value; } @@ -264,6 +289,7 @@ set_engine_force(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_engine_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_engineForce; } @@ -273,6 +299,7 @@ get_engine_force() const { */ void BulletWheel:: set_brake(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_brake = (btScalar)value; } @@ -282,6 +309,7 @@ set_brake(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_brake() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_brake; } @@ -291,6 +319,7 @@ get_brake() const { */ void BulletWheel:: set_skid_info(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_skidInfo = (btScalar)value; } @@ -300,6 +329,7 @@ set_skid_info(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_skid_info() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_skidInfo; } @@ -309,6 +339,7 @@ get_skid_info() const { */ void BulletWheel:: set_wheels_suspension_force(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_wheelsSuspensionForce = (btScalar)value; } @@ -318,6 +349,7 @@ set_wheels_suspension_force(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_wheels_suspension_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_wheelsSuspensionForce; } @@ -327,6 +359,7 @@ get_wheels_suspension_force() const { */ void BulletWheel:: set_suspension_relative_velocity(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_suspensionRelativeVelocity = (btScalar)value; } @@ -336,6 +369,7 @@ set_suspension_relative_velocity(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_suspension_relative_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_suspensionRelativeVelocity; } @@ -345,6 +379,7 @@ get_suspension_relative_velocity() const { */ void BulletWheel:: set_clipped_inv_connection_point_cs(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_clippedInvContactDotSuspension = (btScalar)value; } @@ -354,6 +389,7 @@ set_clipped_inv_connection_point_cs(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_clipped_inv_connection_point_cs() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_clippedInvContactDotSuspension; } @@ -363,6 +399,7 @@ get_clipped_inv_connection_point_cs() const { */ void BulletWheel:: set_chassis_connection_point_cs(const LPoint3 &pos) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!pos.is_nan()); _info.m_chassisConnectionPointCS = LVecBase3_to_btVector3(pos); @@ -373,6 +410,7 @@ set_chassis_connection_point_cs(const LPoint3 &pos) { */ LPoint3 BulletWheel:: get_chassis_connection_point_cs() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_info.m_chassisConnectionPointCS); } @@ -383,6 +421,7 @@ get_chassis_connection_point_cs() const { */ void BulletWheel:: set_wheel_direction_cs(const LVector3 &dir) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!dir.is_nan()); _info.m_wheelDirectionCS = LVecBase3_to_btVector3(dir); @@ -393,6 +432,7 @@ set_wheel_direction_cs(const LVector3 &dir) { */ LVector3 BulletWheel:: get_wheel_direction_cs() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_info.m_wheelDirectionCS); } @@ -402,6 +442,7 @@ get_wheel_direction_cs() const { */ void BulletWheel:: set_wheel_axle_cs(const LVector3 &axle) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!axle.is_nan()); _info.m_wheelAxleCS = LVecBase3_to_btVector3(axle); @@ -412,6 +453,7 @@ set_wheel_axle_cs(const LVector3 &axle) { */ LVector3 BulletWheel:: get_wheel_axle_cs() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_info.m_wheelAxleCS); } @@ -421,6 +463,7 @@ get_wheel_axle_cs() const { */ void BulletWheel:: set_world_transform(const LMatrix4 &mat) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!mat.is_nan()); _info.m_worldTransform = LMatrix4_to_btTrans(mat); @@ -431,6 +474,7 @@ set_world_transform(const LMatrix4 &mat) { */ LMatrix4 BulletWheel:: get_world_transform() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btTrans_to_LMatrix4(_info.m_worldTransform); } @@ -440,6 +484,7 @@ get_world_transform() const { */ void BulletWheel:: set_front_wheel(bool value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_bIsFrontWheel = value; } @@ -449,6 +494,7 @@ set_front_wheel(bool value) { */ bool BulletWheel:: is_front_wheel() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _info.m_bIsFrontWheel; } @@ -458,6 +504,7 @@ is_front_wheel() const { */ void BulletWheel:: set_node(PandaNode *node) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_clientInfo = (void *)node; } @@ -468,6 +515,87 @@ set_node(PandaNode *node) { */ PandaNode *BulletWheel:: get_node() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (_info.m_clientInfo == NULL) ? NULL : (PandaNode *)_info.m_clientInfo; } + +/** + * + */ +bool BulletWheelRaycastInfo:: +is_in_contact() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _info.m_isInContact; +} + +/** + * + */ +PN_stdfloat BulletWheelRaycastInfo:: +get_suspension_length() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _info.m_suspensionLength; +} + +/** + * + */ +LPoint3 BulletWheelRaycastInfo:: +get_contact_point_ws() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LPoint3(_info.m_contactPointWS); +} + +/** + * + */ +LPoint3 BulletWheelRaycastInfo:: +get_hard_point_ws() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LPoint3(_info.m_hardPointWS); +} + +/** + * + */ +LVector3 BulletWheelRaycastInfo:: +get_contact_normal_ws() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_info.m_contactNormalWS); +} + +/** + * + */ +LVector3 BulletWheelRaycastInfo:: +get_wheel_direction_ws() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_info.m_wheelDirectionWS); +} + +/** + * + */ +LVector3 BulletWheelRaycastInfo:: +get_wheel_axle_ws() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_info.m_wheelAxleWS); +} + +/** + * + */ +PandaNode *BulletWheelRaycastInfo:: +get_ground_object() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _info.m_groundObject ? (PandaNode *)_info.m_groundObject : NULL; +} diff --git a/panda/src/bullet/bulletWheel.h b/panda/src/bullet/bulletWheel.h index 2b6ecd8a80..414ecd94f3 100644 --- a/panda/src/bullet/bulletWheel.h +++ b/panda/src/bullet/bulletWheel.h @@ -30,14 +30,14 @@ class EXPCL_PANDABULLET BulletWheelRaycastInfo { PUBLISHED: INLINE ~BulletWheelRaycastInfo(); - INLINE bool is_in_contact() const; - INLINE PN_stdfloat get_suspension_length() const; - INLINE LVector3 get_contact_normal_ws() const; - INLINE LVector3 get_wheel_direction_ws() const; - INLINE LVector3 get_wheel_axle_ws() const; - INLINE LPoint3 get_contact_point_ws() const; - INLINE LPoint3 get_hard_point_ws() const; - INLINE PandaNode *get_ground_object() const; + bool is_in_contact() const; + PN_stdfloat get_suspension_length() const; + LVector3 get_contact_normal_ws() const; + LVector3 get_wheel_direction_ws() const; + LVector3 get_wheel_axle_ws() const; + LPoint3 get_contact_point_ws() const; + LPoint3 get_hard_point_ws() const; + PandaNode *get_ground_object() const; MAKE_PROPERTY(in_contact, is_in_contact); MAKE_PROPERTY(suspension_length, get_suspension_length); diff --git a/panda/src/bullet/bulletWorld.I b/panda/src/bullet/bulletWorld.I index 958376ec60..61893a1531 100644 --- a/panda/src/bullet/bulletWorld.I +++ b/panda/src/bullet/bulletWorld.I @@ -50,28 +50,6 @@ INLINE BulletWorld:: delete _broadphase; } -/** - * - */ -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); -} - /** * */ @@ -117,125 +95,3 @@ get_dispatcher() const { return _dispatcher; } -/** - * - */ -INLINE int BulletWorld:: -get_num_rigid_bodies() const { - - return _bodies.size(); -} - -/** - * - */ -INLINE BulletRigidBodyNode *BulletWorld:: -get_rigid_body(int idx) const { - - nassertr(idx >= 0 && idx < (int)_bodies.size(), NULL); - return _bodies[idx]; -} - -/** - * - */ -INLINE int BulletWorld:: -get_num_soft_bodies() const { - - return _softbodies.size(); -} - -/** - * - */ -INLINE BulletSoftBodyNode *BulletWorld:: -get_soft_body(int idx) const { - - nassertr(idx >= 0 && idx < (int)_softbodies.size(), NULL); - return _softbodies[idx]; -} - -/** - * - */ -INLINE int BulletWorld:: -get_num_ghosts() const { - - return _ghosts.size(); -} - -/** - * - */ -INLINE BulletGhostNode *BulletWorld:: -get_ghost(int idx) const { - - nassertr(idx >= 0 && idx < (int)_ghosts.size(), NULL); - return _ghosts[idx]; -} - -/** - * - */ -INLINE int BulletWorld:: -get_num_characters() const { - - return _characters.size(); -} - -/** - * - */ -INLINE BulletBaseCharacterControllerNode *BulletWorld:: -get_character(int idx) const { - - nassertr(idx >= 0 && idx < (int)_characters.size(), NULL); - return _characters[idx]; -} - -/** - * - */ -INLINE int BulletWorld:: -get_num_vehicles() const { - - return _vehicles.size(); -} - -/** - * - */ -INLINE BulletVehicle *BulletWorld:: -get_vehicle(int idx) const { - - nassertr(idx >= 0 && idx < (int)_vehicles.size(), NULL); - return _vehicles[idx]; -} - -/** - * - */ -INLINE int BulletWorld:: -get_num_constraints() const { - - return _constraints.size(); -} - -/** - * - */ -INLINE BulletConstraint *BulletWorld:: -get_constraint(int idx) const { - - nassertr(idx >= 0 && idx < (int)_constraints.size(), NULL); - return _constraints[idx]; -} - -/** - * - */ -INLINE int BulletWorld:: -get_num_manifolds() const { - - return _world->getDispatcher()->getNumManifolds(); -} diff --git a/panda/src/bullet/bulletWorld.cxx b/panda/src/bullet/bulletWorld.cxx index e7f6f3e267..f3d48e3c74 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"); @@ -118,6 +118,17 @@ BulletWorld() { _world->getSolverInfo().m_numIterations = bullet_solver_iterations; } +/** + * + */ +LightMutex &BulletWorld:: +get_global_lock() { + + static LightMutex lock; + + return lock; +} + /** * */ @@ -127,11 +138,46 @@ get_world_info() { return BulletSoftBodyWorldInfo(_info); } +/** + * + */ +void BulletWorld:: +set_debug_node(BulletDebugNode *node) { + LightMutexHolder holder(get_global_lock()); + + nassertv(node); + if (node != _debug) { + if (_debug != nullptr) { + _debug->_debug_stale = false; + _debug->_debug_world = nullptr; + } + + _debug = node; + _world->setDebugDrawer(&(_debug->_drawer)); + } +} + +/** + * Removes a debug node that has been assigned to this BulletWorld. + */ +void BulletWorld:: +clear_debug_node() { + LightMutexHolder holder(get_global_lock()); + + if (_debug != nullptr) { + _debug->_debug_stale = false; + _debug->_debug_world = nullptr; + _world->setDebugDrawer(nullptr); + _debug = nullptr; + } +} + /** * */ void BulletWorld:: set_gravity(const LVector3 &gravity) { + LightMutexHolder holder(get_global_lock()); _world->setGravity(LVecBase3_to_btVector3(gravity)); _info.m_gravity.setValue(gravity.get_x(), gravity.get_y(), gravity.get_z()); @@ -142,6 +188,7 @@ set_gravity(const LVector3 &gravity) { */ void BulletWorld:: set_gravity(PN_stdfloat gx, PN_stdfloat gy, PN_stdfloat gz) { + LightMutexHolder holder(get_global_lock()); _world->setGravity(btVector3((btScalar)gx, (btScalar)gy, (btScalar)gz)); _info.m_gravity.setValue((btScalar)gx, (btScalar)gy, (btScalar)gz); @@ -152,6 +199,7 @@ set_gravity(PN_stdfloat gx, PN_stdfloat gy, PN_stdfloat gz) { */ const LVector3 BulletWorld:: get_gravity() const { + LightMutexHolder holder(get_global_lock()); return btVector3_to_LVector3(_world->getGravity()); } @@ -161,6 +209,7 @@ get_gravity() const { */ int BulletWorld:: do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) { + LightMutexHolder holder(get_global_lock()); _pstat_physics.start(); @@ -168,7 +217,7 @@ do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) { // Synchronize Panda to Bullet _pstat_p2b.start(); - sync_p2b(dt, num_substeps); + do_sync_p2b(dt, num_substeps); _pstat_p2b.stop(); // Simulation @@ -178,15 +227,13 @@ do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) { // Synchronize Bullet to Panda _pstat_b2p.start(); - sync_b2p(); + do_sync_b2p(); _info.m_sparsesdf.GarbageCollect(bullet_gc_lifetime); _pstat_b2p.stop(); // Render debug if (_debug) { - _pstat_debug.start(); - _debug->sync_b2p(_world); - _pstat_debug.stop(); + _debug->do_sync_b2p(_world); } _pstat_physics.stop(); @@ -195,52 +242,52 @@ do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) { } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -sync_p2b(PN_stdfloat dt, int num_substeps) { +do_sync_p2b(PN_stdfloat dt, int num_substeps) { - for (int i=0; i < get_num_rigid_bodies(); i++) { - get_rigid_body(i)->sync_p2b(); + for (int i=0; i < _bodies.size(); i++) { + _bodies[i]->do_sync_p2b(); } - for (int i=0; i < get_num_soft_bodies(); i++) { - get_soft_body(i)->sync_p2b(); + for (int i=0; i < _softbodies.size(); i++) { + _softbodies[i]->do_sync_p2b(); } - for (int i=0; i < get_num_ghosts(); i++) { - get_ghost(i)->sync_p2b(); + for (int i=0; i < _ghosts.size(); i++) { + _ghosts[i]->do_sync_p2b(); } - for (int i=0; i < get_num_characters(); i++) { - get_character(i)->sync_p2b(dt, num_substeps); + for (int i=0; i < _characters.size(); i++) { + _characters[i]->do_sync_p2b(dt, num_substeps); } } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -sync_b2p() { +do_sync_b2p() { - for (int i=0; i < get_num_vehicles(); i++) { - get_vehicle(i)->sync_b2p(); + for (int i=0; i < _vehicles.size(); i++) { + _vehicles[i]->do_sync_b2p(); } - for (int i=0; i < get_num_rigid_bodies(); i++) { - get_rigid_body(i)->sync_b2p(); + for (int i=0; i < _bodies.size(); i++) { + _bodies[i]->do_sync_b2p(); } - for (int i=0; i < get_num_soft_bodies(); i++) { - get_soft_body(i)->sync_b2p(); + for (int i=0; i < _softbodies.size(); i++) { + _softbodies[i]->do_sync_b2p(); } - for (int i=0; i < get_num_ghosts(); i++) { - get_ghost(i)->sync_b2p(); + for (int i=0; i < _ghosts.size(); i++) { + _ghosts[i]->do_sync_b2p(); } - for (int i=0; i < get_num_characters(); i++) { - get_character(i)->sync_b2p(); + for (int i=0; i < _characters.size(); i++) { + _characters[i]->do_sync_b2p(); } } @@ -249,24 +296,25 @@ sync_b2p() { */ void BulletWorld:: attach(TypedObject *object) { + LightMutexHolder holder(get_global_lock()); if (object->is_of_type(BulletGhostNode::get_class_type())) { - attach_ghost(DCAST(BulletGhostNode, object)); + do_attach_ghost(DCAST(BulletGhostNode, object)); } else if (object->is_of_type(BulletRigidBodyNode::get_class_type())) { - attach_rigid_body(DCAST(BulletRigidBodyNode, object)); + do_attach_rigid_body(DCAST(BulletRigidBodyNode, object)); } else if (object->is_of_type(BulletSoftBodyNode::get_class_type())) { - attach_soft_body(DCAST(BulletSoftBodyNode, object)); + do_attach_soft_body(DCAST(BulletSoftBodyNode, object)); } else if (object->is_of_type(BulletBaseCharacterControllerNode::get_class_type())) { - attach_character(DCAST(BulletBaseCharacterControllerNode, object)); + do_attach_character(DCAST(BulletBaseCharacterControllerNode, object)); } else if (object->is_of_type(BulletVehicle::get_class_type())) { - attach_vehicle(DCAST(BulletVehicle, object)); + do_attach_vehicle(DCAST(BulletVehicle, object)); } else if (object->is_of_type(BulletConstraint::get_class_type())) { - attach_constraint(DCAST(BulletConstraint, object)); + do_attach_constraint(DCAST(BulletConstraint, object)); } else { bullet_cat->error() << "not a bullet world object!" << endl; @@ -278,24 +326,25 @@ attach(TypedObject *object) { */ void BulletWorld:: remove(TypedObject *object) { + LightMutexHolder holder(get_global_lock()); if (object->is_of_type(BulletGhostNode::get_class_type())) { - remove_ghost(DCAST(BulletGhostNode, object)); + do_remove_ghost(DCAST(BulletGhostNode, object)); } else if (object->is_of_type(BulletRigidBodyNode::get_class_type())) { - remove_rigid_body(DCAST(BulletRigidBodyNode, object)); + do_remove_rigid_body(DCAST(BulletRigidBodyNode, object)); } else if (object->is_of_type(BulletSoftBodyNode::get_class_type())) { - remove_soft_body(DCAST(BulletSoftBodyNode, object)); + do_remove_soft_body(DCAST(BulletSoftBodyNode, object)); } else if (object->is_of_type(BulletBaseCharacterControllerNode::get_class_type())) { - remove_character(DCAST(BulletBaseCharacterControllerNode, object)); + do_remove_character(DCAST(BulletBaseCharacterControllerNode, object)); } else if (object->is_of_type(BulletVehicle::get_class_type())) { - remove_vehicle(DCAST(BulletVehicle, object)); + do_remove_vehicle(DCAST(BulletVehicle, object)); } else if (object->is_of_type(BulletConstraint::get_class_type())) { - remove_constraint(DCAST(BulletConstraint, object)); + do_remove_constraint(DCAST(BulletConstraint, object)); } else { bullet_cat->error() << "not a bullet world object!" << endl; @@ -307,6 +356,127 @@ remove(TypedObject *object) { */ void BulletWorld:: attach_rigid_body(BulletRigidBodyNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_attach_rigid_body(node); +} + +/** + * Deprecated.! Please use BulletWorld::remove + */ +void BulletWorld:: +remove_rigid_body(BulletRigidBodyNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_remove_rigid_body(node); +} + +/** + * Deprecated! Please use BulletWorld::attach + */ +void BulletWorld:: +attach_soft_body(BulletSoftBodyNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_attach_soft_body(node); +} + +/** + * Deprecated.! Please use BulletWorld::remove + */ +void BulletWorld:: +remove_soft_body(BulletSoftBodyNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_remove_soft_body(node); +} + +/** + * Deprecated! Please use BulletWorld::attach + */ +void BulletWorld:: +attach_ghost(BulletGhostNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_attach_ghost(node); +} + +/** + * Deprecated.! Please use BulletWorld::remove + */ +void BulletWorld:: +remove_ghost(BulletGhostNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_remove_ghost(node); +} + +/** + * Deprecated! Please use BulletWorld::attach + */ +void BulletWorld:: +attach_character(BulletBaseCharacterControllerNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_attach_character(node); +} + +/** + * Deprecated.! Please use BulletWorld::remove + */ +void BulletWorld:: +remove_character(BulletBaseCharacterControllerNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_remove_character(node); +} + +/** + * Deprecated! Please use BulletWorld::attach + */ +void BulletWorld:: +attach_vehicle(BulletVehicle *vehicle) { + LightMutexHolder holder(get_global_lock()); + + do_attach_vehicle(vehicle); +} + +/** + * Deprecated.! Please use BulletWorld::remove + */ +void BulletWorld:: +remove_vehicle(BulletVehicle *vehicle) { + LightMutexHolder holder(get_global_lock()); + + do_remove_vehicle(vehicle); +} + +/** + * Attaches a single constraint to a world. Collision checks between the + * linked objects will be disabled if the second parameter is set to TRUE. + */ +void BulletWorld:: +attach_constraint(BulletConstraint *constraint, bool linked_collision) { + LightMutexHolder holder(get_global_lock()); + + do_attach_constraint(constraint, linked_collision); +} + +/** + * Deprecated.! Please use BulletWorld::remove + */ +void BulletWorld:: +remove_constraint(BulletConstraint *constraint) { + LightMutexHolder holder(get_global_lock()); + + do_remove_constraint(constraint); +} + +/** + * Assumes the lock(bullet global lock) is held by the caller + */ +void BulletWorld:: +do_attach_rigid_body(BulletRigidBodyNode *node) { nassertv(node); @@ -326,10 +496,10 @@ attach_rigid_body(BulletRigidBodyNode *node) { } /** - * Deprecated.! Please use BulletWorld::remove + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -remove_rigid_body(BulletRigidBodyNode *node) { +do_remove_rigid_body(BulletRigidBodyNode *node) { nassertv(node); @@ -349,10 +519,10 @@ remove_rigid_body(BulletRigidBodyNode *node) { } /** - * Deprecated! Please use BulletWorld::attach + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -attach_soft_body(BulletSoftBodyNode *node) { +do_attach_soft_body(BulletSoftBodyNode *node) { nassertv(node); @@ -376,10 +546,10 @@ attach_soft_body(BulletSoftBodyNode *node) { } /** - * Deprecated.! Please use BulletWorld::remove + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -remove_soft_body(BulletSoftBodyNode *node) { +do_remove_soft_body(BulletSoftBodyNode *node) { nassertv(node); @@ -399,10 +569,10 @@ remove_soft_body(BulletSoftBodyNode *node) { } /** - * Deprecated! Please use BulletWorld::attach + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -attach_ghost(BulletGhostNode *node) { +do_attach_ghost(BulletGhostNode *node) { nassertv(node); @@ -440,10 +610,10 @@ enum CollisionFilterGroups { } /** - * Deprecated.! Please use BulletWorld::remove + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -remove_ghost(BulletGhostNode *node) { +do_remove_ghost(BulletGhostNode *node) { nassertv(node); @@ -463,10 +633,10 @@ remove_ghost(BulletGhostNode *node) { } /** - * Deprecated! Please use BulletWorld::attach + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -attach_character(BulletBaseCharacterControllerNode *node) { +do_attach_character(BulletBaseCharacterControllerNode *node) { nassertv(node); @@ -489,10 +659,10 @@ attach_character(BulletBaseCharacterControllerNode *node) { } /** - * Deprecated.! Please use BulletWorld::remove + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -remove_character(BulletBaseCharacterControllerNode *node) { +do_remove_character(BulletBaseCharacterControllerNode *node) { nassertv(node); @@ -511,10 +681,10 @@ remove_character(BulletBaseCharacterControllerNode *node) { } /** - * Deprecated! Please use BulletWorld::attach + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -attach_vehicle(BulletVehicle *vehicle) { +do_attach_vehicle(BulletVehicle *vehicle) { nassertv(vehicle); @@ -532,14 +702,14 @@ attach_vehicle(BulletVehicle *vehicle) { } /** - * Deprecated.! Please use BulletWorld::remove + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -remove_vehicle(BulletVehicle *vehicle) { +do_remove_vehicle(BulletVehicle *vehicle) { nassertv(vehicle); - remove_rigid_body(vehicle->get_chassis()); + do_remove_rigid_body(vehicle->do_get_chassis()); BulletVehicles::iterator found; PT(BulletVehicle) ptvehicle = vehicle; @@ -557,9 +727,10 @@ remove_vehicle(BulletVehicle *vehicle) { /** * Attaches a single constraint to a world. Collision checks between the * linked objects will be disabled if the second parameter is set to TRUE. + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -attach_constraint(BulletConstraint *constraint, bool linked_collision) { +do_attach_constraint(BulletConstraint *constraint, bool linked_collision) { nassertv(constraint); @@ -577,10 +748,10 @@ attach_constraint(BulletConstraint *constraint, bool linked_collision) { } /** - * Deprecated.! Please use BulletWorld::remove + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -remove_constraint(BulletConstraint *constraint) { +do_remove_constraint(BulletConstraint *constraint) { nassertv(constraint); @@ -597,11 +768,148 @@ remove_constraint(BulletConstraint *constraint) { } } +/** + * + */ +int BulletWorld:: +get_num_rigid_bodies() const { + LightMutexHolder holder(get_global_lock()); + + return _bodies.size(); +} + +/** + * + */ +BulletRigidBodyNode *BulletWorld:: +get_rigid_body(int idx) const { + LightMutexHolder holder(get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_bodies.size(), NULL); + return _bodies[idx]; +} + +/** + * + */ +int BulletWorld:: +get_num_soft_bodies() const { + LightMutexHolder holder(get_global_lock()); + + return _softbodies.size(); +} + +/** + * + */ +BulletSoftBodyNode *BulletWorld:: +get_soft_body(int idx) const { + LightMutexHolder holder(get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_softbodies.size(), NULL); + return _softbodies[idx]; +} + +/** + * + */ +int BulletWorld:: +get_num_ghosts() const { + LightMutexHolder holder(get_global_lock()); + + return _ghosts.size(); +} + +/** + * + */ +BulletGhostNode *BulletWorld:: +get_ghost(int idx) const { + LightMutexHolder holder(get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_ghosts.size(), NULL); + return _ghosts[idx]; +} + +/** + * + */ +int BulletWorld:: +get_num_characters() const { + LightMutexHolder holder(get_global_lock()); + + return _characters.size(); +} + +/** + * + */ +BulletBaseCharacterControllerNode *BulletWorld:: +get_character(int idx) const { + LightMutexHolder holder(get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_characters.size(), NULL); + return _characters[idx]; +} + +/** + * + */ +int BulletWorld:: +get_num_vehicles() const { + LightMutexHolder holder(get_global_lock()); + + return _vehicles.size(); +} + +/** + * + */ +BulletVehicle *BulletWorld:: +get_vehicle(int idx) const { + LightMutexHolder holder(get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_vehicles.size(), NULL); + return _vehicles[idx]; +} + +/** + * + */ +int BulletWorld:: +get_num_constraints() const { + LightMutexHolder holder(get_global_lock()); + + return _constraints.size(); +} + +/** + * + */ +BulletConstraint *BulletWorld:: +get_constraint(int idx) const { + LightMutexHolder holder(get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_constraints.size(), NULL); + return _constraints[idx]; +} + +/** + * + */ +int BulletWorld:: +get_num_manifolds() const { + LightMutexHolder holder(get_global_lock()); + + return _world->getDispatcher()->getNumManifolds(); +} + /** * */ BulletClosestHitRayResult BulletWorld:: ray_test_closest(const LPoint3 &from_pos, const LPoint3 &to_pos, const CollideMask &mask) const { + LightMutexHolder holder(get_global_lock()); nassertr(!from_pos.is_nan(), BulletClosestHitRayResult::empty()); nassertr(!to_pos.is_nan(), BulletClosestHitRayResult::empty()); @@ -619,6 +927,7 @@ ray_test_closest(const LPoint3 &from_pos, const LPoint3 &to_pos, const CollideMa */ BulletAllHitsRayResult BulletWorld:: ray_test_all(const LPoint3 &from_pos, const LPoint3 &to_pos, const CollideMask &mask) const { + LightMutexHolder holder(get_global_lock()); nassertr(!from_pos.is_nan(), BulletAllHitsRayResult::empty()); nassertr(!to_pos.is_nan(), BulletAllHitsRayResult::empty()); @@ -636,13 +945,16 @@ ray_test_all(const LPoint3 &from_pos, const LPoint3 &to_pos, const CollideMask & */ BulletClosestHitSweepResult BulletWorld:: sweep_test_closest(BulletShape *shape, const TransformState &from_ts, const TransformState &to_ts, const CollideMask &mask, PN_stdfloat penetration) const { + LightMutexHolder holder(get_global_lock()); nassertr(shape, BulletClosestHitSweepResult::empty()); - nassertr(shape->is_convex(), BulletClosestHitSweepResult::empty()); + + const btConvexShape *convex = (const btConvexShape *) shape->ptr(); + nassertr(convex->isConvex(), BulletClosestHitSweepResult::empty()); + nassertr(!from_ts.is_invalid(), BulletClosestHitSweepResult::empty()); nassertr(!to_ts.is_invalid(), BulletClosestHitSweepResult::empty()); - const btConvexShape *convex = (const btConvexShape *) shape->ptr(); const btVector3 from_pos = LVecBase3_to_btVector3(from_ts.get_pos()); const btVector3 to_pos = LVecBase3_to_btVector3(to_ts.get_pos()); const btTransform from_trans = LMatrix4_to_btTrans(from_ts.get_mat()); @@ -659,6 +971,7 @@ sweep_test_closest(BulletShape *shape, const TransformState &from_ts, const Tran */ bool BulletWorld:: filter_test(PandaNode *node0, PandaNode *node1) const { + LightMutexHolder holder(get_global_lock()); nassertr(node0, false); nassertr(node1, false); @@ -690,6 +1003,7 @@ filter_test(PandaNode *node0, PandaNode *node1) const { */ BulletContactResult BulletWorld:: contact_test(PandaNode *node, bool use_filter) const { + LightMutexHolder holder(get_global_lock()); btCollisionObject *obj = get_collision_object(node); @@ -715,6 +1029,7 @@ contact_test(PandaNode *node, bool use_filter) const { */ BulletContactResult BulletWorld:: contact_test_pair(PandaNode *node0, PandaNode *node1) const { + LightMutexHolder holder(get_global_lock()); btCollisionObject *obj0 = get_collision_object(node0); btCollisionObject *obj1 = get_collision_object(node1); @@ -734,6 +1049,7 @@ contact_test_pair(PandaNode *node0, PandaNode *node1) const { */ BulletPersistentManifold *BulletWorld:: get_manifold(int idx) const { + LightMutexHolder holder(get_global_lock()); nassertr(idx < get_num_manifolds(), NULL); @@ -768,6 +1084,7 @@ get_collision_object(PandaNode *node) { */ void BulletWorld:: set_group_collision_flag(unsigned int group1, unsigned int group2, bool enable) { + LightMutexHolder holder(get_global_lock()); if (bullet_filter_algorithm != FA_groups_mask) { bullet_cat.warning() << "filter algorithm is not 'groups-mask'" << endl; @@ -782,6 +1099,7 @@ set_group_collision_flag(unsigned int group1, unsigned int group2, bool enable) */ bool BulletWorld:: get_group_collision_flag(unsigned int group1, unsigned int group2) const { + LightMutexHolder holder(get_global_lock()); return _filter_cb2._collide[group1].get_bit(group2); } @@ -791,6 +1109,7 @@ get_group_collision_flag(unsigned int group1, unsigned int group2) const { */ void BulletWorld:: set_contact_added_callback(CallbackObject *obj) { + LightMutexHolder holder(get_global_lock()); _world->getSolverInfo().m_solverMode |= SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION; _world->getSolverInfo().m_solverMode |= SOLVER_USE_2_FRICTION_DIRECTIONS; @@ -804,6 +1123,7 @@ set_contact_added_callback(CallbackObject *obj) { */ void BulletWorld:: clear_contact_added_callback() { + LightMutexHolder holder(get_global_lock()); _world->getSolverInfo().m_solverMode &= ~SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION; _world->getSolverInfo().m_solverMode &= ~SOLVER_USE_2_FRICTION_DIRECTIONS; @@ -817,6 +1137,7 @@ clear_contact_added_callback() { */ void BulletWorld:: set_tick_callback(CallbackObject *obj, bool is_pretick) { + LightMutexHolder holder(get_global_lock()); nassertv(obj != NULL); _tick_callback_obj = obj; @@ -828,6 +1149,7 @@ set_tick_callback(CallbackObject *obj, bool is_pretick) { */ void BulletWorld:: clear_tick_callback() { + LightMutexHolder holder(get_global_lock()); _tick_callback_obj = NULL; _world->setInternalTickCallback(NULL); @@ -854,6 +1176,7 @@ tick_callback(btDynamicsWorld *world, btScalar timestep) { */ void BulletWorld:: set_filter_callback(CallbackObject *obj) { + LightMutexHolder holder(get_global_lock()); nassertv(obj != NULL); @@ -869,6 +1192,7 @@ set_filter_callback(CallbackObject *obj) { */ void BulletWorld:: clear_filter_callback() { + LightMutexHolder holder(get_global_lock()); _filter_cb3._filter_callback_obj = NULL; } diff --git a/panda/src/bullet/bulletWorld.h b/panda/src/bullet/bulletWorld.h index b077e05f7a..021b6ee268 100644 --- a/panda/src/bullet/bulletWorld.h +++ b/panda/src/bullet/bulletWorld.h @@ -37,6 +37,7 @@ #include "callbackObject.h" #include "collideMask.h" #include "luse.h" +#include "lightMutex.h" class BulletPersistentManifold; class BulletShape; @@ -62,48 +63,43 @@ PUBLISHED: BulletSoftBodyWorldInfo get_world_info(); // Debug - INLINE void set_debug_node(BulletDebugNode *node); - INLINE void clear_debug_node(); + void set_debug_node(BulletDebugNode *node); + void clear_debug_node(); INLINE BulletDebugNode *get_debug_node() const; INLINE bool has_debug_node() const; // AttachRemove void attach(TypedObject *object); + void remove(TypedObject *object); void attach_constraint(BulletConstraint *constraint, bool linked_collision=false); - void remove(TypedObject *object); - // Ghost object - INLINE int get_num_ghosts() const; - INLINE BulletGhostNode *get_ghost(int idx) const; + int get_num_ghosts() const; + BulletGhostNode *get_ghost(int idx) const; MAKE_SEQ(get_ghosts, get_num_ghosts, get_ghost); // Rigid body - INLINE int get_num_rigid_bodies() const; - INLINE BulletRigidBodyNode *get_rigid_body(int idx) const; + int get_num_rigid_bodies() const; + BulletRigidBodyNode *get_rigid_body(int idx) const; MAKE_SEQ(get_rigid_bodies, get_num_rigid_bodies, get_rigid_body); // Soft body - INLINE int get_num_soft_bodies() const; - INLINE BulletSoftBodyNode *get_soft_body(int idx) const; + int get_num_soft_bodies() const; + BulletSoftBodyNode *get_soft_body(int idx) const; MAKE_SEQ(get_soft_bodies, get_num_soft_bodies, get_soft_body); // Character controller - INLINE int get_num_characters() const; - INLINE BulletBaseCharacterControllerNode *get_character(int idx) const; + int get_num_characters() const; + BulletBaseCharacterControllerNode *get_character(int idx) const; MAKE_SEQ(get_characters, get_num_characters, get_character); - // Vehicle - void attach_vehicle(BulletVehicle *vehicle); - void remove_vehicle(BulletVehicle *vehicle); - - INLINE int get_num_vehicles() const; - INLINE BulletVehicle *get_vehicle(int idx) const; + int get_num_vehicles() const; + BulletVehicle *get_vehicle(int idx) const; MAKE_SEQ(get_vehicles, get_num_vehicles, get_vehicle); // Constraint - INLINE int get_num_constraints() const; - INLINE BulletConstraint *get_constraint(int idx) const; + int get_num_constraints() const; + BulletConstraint *get_constraint(int idx) const; MAKE_SEQ(get_constraints, get_num_constraints, get_constraint); // Raycast and other queries @@ -130,7 +126,7 @@ PUBLISHED: bool filter_test(PandaNode *node0, PandaNode *node1) const; // Manifolds - INLINE int get_num_manifolds() const; + int get_num_manifolds() const; BulletPersistentManifold *get_manifold(int idx) const; MAKE_SEQ(get_manifolds, get_num_manifolds, get_manifold); @@ -171,7 +167,7 @@ PUBLISHED: 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 +PUBLISHED: // Deprecated methods, will be removed soon void attach_ghost(BulletGhostNode *node); void remove_ghost(BulletGhostNode *node); @@ -184,6 +180,9 @@ PUBLISHED: // Deprecated methods, will become private soon void attach_character(BulletBaseCharacterControllerNode *node); void remove_character(BulletBaseCharacterControllerNode *node); + void attach_vehicle(BulletVehicle *vehicle); + void remove_vehicle(BulletVehicle *vehicle); + void remove_constraint(BulletConstraint *constraint); public: @@ -193,9 +192,29 @@ public: INLINE btBroadphaseInterface *get_broadphase() const; INLINE btDispatcher *get_dispatcher() const; + static LightMutex &get_global_lock(); + private: - void sync_p2b(PN_stdfloat dt, int num_substeps); - void sync_b2p(); + void do_sync_p2b(PN_stdfloat dt, int num_substeps); + void do_sync_b2p(); + + void do_attach_ghost(BulletGhostNode *node); + void do_remove_ghost(BulletGhostNode *node); + + void do_attach_rigid_body(BulletRigidBodyNode *node); + void do_remove_rigid_body(BulletRigidBodyNode *node); + + void do_attach_soft_body(BulletSoftBodyNode *node); + void do_remove_soft_body(BulletSoftBodyNode *node); + + void do_attach_character(BulletBaseCharacterControllerNode *node); + void do_remove_character(BulletBaseCharacterControllerNode *node); + + void do_attach_vehicle(BulletVehicle *vehicle); + void do_remove_vehicle(BulletVehicle *vehicle); + + void do_attach_constraint(BulletConstraint *constraint, bool linked_collision=false); + void do_remove_constraint(BulletConstraint *constraint); static void tick_callback(btDynamicsWorld *world, btScalar timestep); @@ -208,7 +227,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/cftalk/cfChannel.cxx b/panda/src/cftalk/cfChannel.cxx deleted file mode 100644 index e63ba79077..0000000000 --- a/panda/src/cftalk/cfChannel.cxx +++ /dev/null @@ -1,62 +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 cfChannel.cxx - * @author drose - * @date 2009-03-26 - */ - -#include "cfChannel.h" - -/** - * The DatagramGenerator and DatagramSink should be newly created on the free - * store (via the new operator). The CFChannel will take ownership of these - * pointers, and will delete them when it destructs. - */ -CFChannel:: -CFChannel(DatagramGenerator *dggen, DatagramSink *dgsink) : - _dggen(dggen), - _dgsink(dgsink), - _reader(dggen), - _writer(dgsink) -{ - bool ok1 = _reader.init(); - bool ok2 = _writer.init(); - nassertv(ok1 && ok2); -} - -/** - * - */ -CFChannel:: -~CFChannel() { - delete _dggen; - delete _dgsink; -} - -/** - * Delivers a single command to the process at the other end of the channel. - */ -void CFChannel:: -send_command(CFCommand *command) { - bool ok = _writer.write_object(command); - nassertv(ok); -} - -/** - * Receives a single command from the process at the other end of the channel. - * If no command is ready, the thread will block until one is. Returns NULL - * when the connection has been closed. - */ -PT(CFCommand) CFChannel:: -receive_command() { - TypedWritable *obj = _reader.read_object(); - CFCommand *command; - DCAST_INTO_R(command, obj, NULL); - return command; -} diff --git a/panda/src/cftalk/cfChannel.h b/panda/src/cftalk/cfChannel.h deleted file mode 100644 index e549327efa..0000000000 --- a/panda/src/cftalk/cfChannel.h +++ /dev/null @@ -1,44 +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 cfChannel.h - * @author drose - * @date 2009-03-26 - */ - -#ifndef CFCHANNEL_H -#define CFCHANNEL_H - -#include "pandabase.h" -#include "referenceCount.h" -#include "bamReader.h" -#include "bamWriter.h" -#include "cfCommand.h" - -/** - * Represents an open communication channel in the connected-frame protocol. - * Commands may be sent and received on this channel. - */ -class EXPCL_CFTALK CFChannel : public ReferenceCount { -public: - CFChannel(DatagramGenerator *dggen, DatagramSink *dgsink); - ~CFChannel(); - - void send_command(CFCommand *command); - PT(CFCommand) receive_command(); - -private: - DatagramGenerator *_dggen; - DatagramSink *_dgsink; - BamReader _reader; - BamWriter _writer; -}; - -#include "cfChannel.I" - -#endif diff --git a/panda/src/cftalk/cfCommand.I b/panda/src/cftalk/cfCommand.I deleted file mode 100644 index fea651c96f..0000000000 --- a/panda/src/cftalk/cfCommand.I +++ /dev/null @@ -1,41 +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 cfCommand.I - * @author drose - * @date 2009-02-19 - */ - -/** - * - */ -INLINE CFCommand:: -CFCommand() { -} - -/** - * - */ -INLINE CFDoCullCommand:: -CFDoCullCommand() { -} - -/** - * - */ -INLINE CFDoCullCommand:: -CFDoCullCommand(PandaNode *scene) : _scene(scene) { -} - -/** - * - */ -INLINE PandaNode *CFDoCullCommand:: -get_scene() const { - return _scene; -} diff --git a/panda/src/cftalk/cfCommand.cxx b/panda/src/cftalk/cfCommand.cxx deleted file mode 100644 index ca58163242..0000000000 --- a/panda/src/cftalk/cfCommand.cxx +++ /dev/null @@ -1,93 +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 cfCommand.cxx - * @author drose - * @date 2009-02-19 - */ - -#include "cfCommand.h" - -TypeHandle CFCommand::_type_handle; -TypeHandle CFDoCullCommand::_type_handle; - -/** - * - */ -CFCommand:: -~CFCommand() { -} - -/** - * Tells the BamReader how to create objects of type CFDoCullCommand. - */ -void CFDoCullCommand:: -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 CFDoCullCommand:: -write_datagram(BamWriter *manager, Datagram &dg) { - TypedWritable::write_datagram(manager, dg); - manager->write_pointer(dg, _scene); -} - -/** - * Called by the BamWriter when this object has not itself been modified - * recently, but it should check its nested objects for updates. - */ -void CFDoCullCommand:: -update_bam_nested(BamWriter *manager) { - manager->consider_update(_scene); -} - -/** - * Receives an array of pointers, one for each time manager->read_pointer() - * was called in fillin(). Returns the number of pointers processed. - */ -int CFDoCullCommand:: -complete_pointers(TypedWritable **p_list, BamReader *manager) { - int pi = TypedWritable::complete_pointers(p_list, manager); - - PandaNode *scene; - DCAST_INTO_R(scene, p_list[pi++], pi); - _scene = scene; - - return pi; -} - -/** - * This function is called by the BamReader's factory when a new object of - * type CFDoCullCommand is encountered in the Bam file. It should create the - * CFDoCullCommand and extract its information from the file. - */ -TypedWritable *CFDoCullCommand:: -make_from_bam(const FactoryParams ¶ms) { - CFDoCullCommand *node = new CFDoCullCommand; - DatagramIterator scan; - BamReader *manager; - - parse_params(params, scan, manager); - node->fillin(scan, manager); - - return node; -} - -/** - * This internal function is called by make_from_bam to read in all of the - * relevant data from the BamFile for the new CFDoCullCommand. - */ -void CFDoCullCommand:: -fillin(DatagramIterator &scan, BamReader *manager) { - TypedWritable::fillin(scan, manager); - manager->read_pointer(scan); -} diff --git a/panda/src/cftalk/cfCommand.h b/panda/src/cftalk/cfCommand.h deleted file mode 100644 index 84e7e1421c..0000000000 --- a/panda/src/cftalk/cfCommand.h +++ /dev/null @@ -1,98 +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 cfCommand.h - * @author drose - * @date 2009-02-19 - */ - -#ifndef CFCOMMAND_H -#define CFCOMMAND_H - -#include "pandabase.h" - -#include "typedWritableReferenceCount.h" -#include "pandaNode.h" - -/** - * A single command in the Connected-Frame protocol. This can be sent client- - * to-server or server-to-client. - * - * This is an abstract base class. Individual commands will specialize from - * this. - */ -class EXPCL_CFTALK CFCommand : public TypedWritableReferenceCount { -protected: - CFCommand(); - -PUBLISHED: - virtual ~CFCommand(); - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - TypedWritableReferenceCount::init_type(); - register_type(_type_handle, "CFCommand", - 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; -}; - -/** - * Starts the cull process for a particular DisplayRegion. - */ -class EXPCL_CFTALK CFDoCullCommand : public CFCommand { -protected: - INLINE CFDoCullCommand(); -PUBLISHED: - INLINE CFDoCullCommand(PandaNode *scene); - - INLINE PandaNode *get_scene() const; - -private: - PT(PandaNode) _scene; - -public: - static void register_with_read_factory(); - virtual void write_datagram(BamWriter *manager, Datagram &dg); - virtual void update_bam_nested(BamWriter *manager); - virtual int complete_pointers(TypedWritable **plist, BamReader *manager); - -protected: - static TypedWritable *make_from_bam(const FactoryParams ¶ms); - void fillin(DatagramIterator &scan, BamReader *manager); - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - CFCommand::init_type(); - register_type(_type_handle, "CFDoCullCommand", - CFCommand::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 "cfCommand.I" - -#endif diff --git a/panda/src/cftalk/config_cftalk.cxx b/panda/src/cftalk/config_cftalk.cxx deleted file mode 100644 index 42c5054526..0000000000 --- a/panda/src/cftalk/config_cftalk.cxx +++ /dev/null @@ -1,46 +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 config_cftalk.cxx - * @author drose - * @date 2009-03-26 - */ - -#include "config_cftalk.h" -#include "cfCommand.h" -#include "pandaSystem.h" - -ConfigureDef(config_cftalk); -NotifyCategoryDef(cftalk, ""); - -ConfigureFn(config_cftalk) { - init_libcftalk(); -} - -/** - * Initializes the library. This must be called at least once before any of - * the functions or classes in this library can be used. Normally it will be - * called by the static initializers and need not be called explicitly, but - * special cases exist. - */ -void -init_libcftalk() { - static bool initialized = false; - if (initialized) { - return; - } - initialized = true; - - CFCommand::init_type(); - CFDoCullCommand::init_type(); - - CFDoCullCommand::register_with_read_factory(); - - PandaSystem *ps = PandaSystem::get_global_ptr(); - ps->add_system("cftalk"); -} diff --git a/panda/src/cftalk/config_cftalk.h b/panda/src/cftalk/config_cftalk.h deleted file mode 100644 index 707c5f182d..0000000000 --- a/panda/src/cftalk/config_cftalk.h +++ /dev/null @@ -1,36 +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 config_cftalk.h - * @author drose - * @date 2009-03-26 - */ - -#ifndef CONFIG_CFTALK_H -#define CONFIG_CFTALK_H - -#include "pandabase.h" -#include "windowProperties.h" -#include "notifyCategoryProxy.h" -#include "configVariableBool.h" -#include "configVariableString.h" -#include "configVariableList.h" -#include "configVariableInt.h" -#include "configVariableEnum.h" -#include "configVariableFilename.h" -#include "coordinateSystem.h" -#include "dconfig.h" - -#include "pvector.h" - -ConfigureDecl(config_cftalk, EXPCL_CFTALK, EXPTP_CFTALK); -NotifyCategoryDecl(cftalk, EXPCL_CFTALK, EXPTP_CFTALK); - -extern EXPCL_CFTALK void init_libcftalk(); - -#endif /* CONFIG_CFTALK_H */ diff --git a/panda/src/cftalk/p3cftalk_composite1.cxx b/panda/src/cftalk/p3cftalk_composite1.cxx deleted file mode 100644 index 49525d942d..0000000000 --- a/panda/src/cftalk/p3cftalk_composite1.cxx +++ /dev/null @@ -1 +0,0 @@ -#include "cfCommand.cxx" diff --git a/panda/src/cftalk/p3cftalk_composite2.cxx b/panda/src/cftalk/p3cftalk_composite2.cxx deleted file mode 100644 index 79c8aa9fa6..0000000000 --- a/panda/src/cftalk/p3cftalk_composite2.cxx +++ /dev/null @@ -1 +0,0 @@ -#include "cfChannel.cxx" 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/gsgbase/displayRegionBase.cxx b/panda/src/cocoadisplay/cocoaGraphicsBuffer.I similarity index 58% rename from panda/src/gsgbase/displayRegionBase.cxx rename to panda/src/cocoadisplay/cocoaGraphicsBuffer.I index b3573b9e68..1611af94d1 100644 --- a/panda/src/gsgbase/displayRegionBase.cxx +++ b/panda/src/cocoadisplay/cocoaGraphicsBuffer.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 cocoaGraphicsBuffer.I + * @author rdb + * @date 2017-12-19 */ - -#include "displayRegionBase.h" - -TypeHandle DisplayRegionBase::_type_handle; - - -/** - * - */ -DisplayRegionBase:: -~DisplayRegionBase() { -} diff --git a/panda/src/cocoadisplay/cocoaGraphicsBuffer.h b/panda/src/cocoadisplay/cocoaGraphicsBuffer.h new file mode 100644 index 0000000000..ab666b6f75 --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsBuffer.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 cocoaGraphicsBuffer.h + * @author rdb + * @date 2017-12-19 + */ + +#ifndef COCOAGRAPHICSBUFFER_H +#define COCOAGRAPHICSBUFFER_H + +#include "pandabase.h" +#include "glgsg.h" + +/** + * This is a light wrapper around GLGraphicsBuffer (ie. FBOs) to interface + * with Cocoa contexts, so that it can be used without a host window. + */ +class CocoaGraphicsBuffer : public GLGraphicsBuffer { +public: + CocoaGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host); + + virtual bool begin_frame(FrameMode mode, Thread *current_thread); + virtual void end_frame(FrameMode mode, Thread *current_thread); + +protected: + virtual void close_buffer(); + virtual bool open_buffer(); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GLGraphicsBuffer::init_type(); + register_type(_type_handle, "CocoaGraphicsBuffer", + GLGraphicsBuffer::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 "cocoaGraphicsBuffer.I" + +#endif diff --git a/panda/src/cocoadisplay/cocoaGraphicsBuffer.mm b/panda/src/cocoadisplay/cocoaGraphicsBuffer.mm new file mode 100644 index 0000000000..ea6fb61e4e --- /dev/null +++ b/panda/src/cocoadisplay/cocoaGraphicsBuffer.mm @@ -0,0 +1,165 @@ +/** + * 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 cocoaGraphicsBuffer.mm + * @author rdb + * @date 2017-12-19 + */ + +#include "cocoaGraphicsBuffer.h" +#include "cocoaGraphicsStateGuardian.h" +#include "config_cocoadisplay.h" +#include "cocoaGraphicsPipe.h" + +#import + +TypeHandle CocoaGraphicsBuffer::_type_handle; + +/** + * + */ +CocoaGraphicsBuffer:: +CocoaGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe, + const string &name, + const FrameBufferProperties &fb_prop, + const WindowProperties &win_prop, + int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host) : // Ignore the host. + GLGraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, nullptr) +{ +} + +/** + * This function will be called within the draw thread before beginning + * rendering for a given frame. It should do whatever setup is required, and + * return true if the frame should be rendered, or false if it should be + * skipped. + */ +bool CocoaGraphicsBuffer:: +begin_frame(FrameMode mode, Thread *current_thread) { + if (_gsg == nullptr) { + return false; + } + + CocoaGraphicsStateGuardian *cocoagsg; + DCAST_INTO_R(cocoagsg, _gsg, false); + nassertr(cocoagsg->_context != nil, false); + + // Lock the context and make it current. + { + PStatTimer timer(_make_current_pcollector, current_thread); + cocoagsg->lock_context(); + [cocoagsg->_context makeCurrentContext]; + } + + return GLGraphicsBuffer::begin_frame(mode, current_thread); +} + +/** + * This function will be called within the draw thread after rendering is + * completed for a given frame. It should do whatever finalization is + * required. + */ +void CocoaGraphicsBuffer:: +end_frame(FrameMode mode, Thread *current_thread) { + nassertv(_gsg != nullptr); + + GLGraphicsBuffer::end_frame(mode, current_thread); + + // Release the context. + CocoaGraphicsStateGuardian *cocoagsg; + DCAST_INTO_V(cocoagsg, _gsg); + cocoagsg->unlock_context(); +} + +/** + * Opens the buffer right now. Called from the window thread. Returns true + * if the buffer is successfully opened, or false if there was a problem. + */ +bool CocoaGraphicsBuffer:: +open_buffer() { + CocoaGraphicsPipe *cocoa_pipe; + DCAST_INTO_R(cocoa_pipe, _pipe, false); + + // GSG CreationInitialization + CocoaGraphicsStateGuardian *cocoagsg; + if (_gsg == nullptr) { + // There is no old gsg. Create a new one. + cocoagsg = new CocoaGraphicsStateGuardian(_engine, _pipe, nullptr); + cocoagsg->choose_pixel_format(_fb_properties, cocoa_pipe->get_display_id(), false); + _gsg = cocoagsg; + } else { + // If the old gsg has the wrong pixel format, create a new one that shares + // with the old gsg. + DCAST_INTO_R(cocoagsg, _gsg, false); + if (!cocoagsg->get_fb_properties().subsumes(_fb_properties)) { + cocoagsg = new CocoaGraphicsStateGuardian(_engine, _pipe, cocoagsg); + cocoagsg->choose_pixel_format(_fb_properties, cocoa_pipe->get_display_id(), false); + _gsg = cocoagsg; + } + } + + FrameBufferProperties desired_props(_fb_properties); + + // Lock the context, so we can safely operate on it. + cocoagsg->lock_context(); + + // Make the context current and initialize what we need. + [cocoagsg->_context makeCurrentContext]; + [cocoagsg->_context update]; + cocoagsg->reset_if_new(); + + // These properties are determined by choose_pixel_format. + _fb_properties.set_force_hardware(cocoagsg->_fbprops.get_force_hardware()); + _fb_properties.set_force_software(cocoagsg->_fbprops.get_force_software()); + + bool success = GLGraphicsBuffer::open_buffer(); + if (success) { + rebuild_bitplanes(); + if (_needs_rebuild) { + // If it still needs rebuild, then something must have gone wrong. + success = false; + } + } + + if (success && !_fb_properties.verify_hardware_software + (desired_props, cocoagsg->get_gl_renderer())) { + GLGraphicsBuffer::close_buffer(); + success = false; + } + + // Release the context. + cocoagsg->unlock_context(); + + if (!success) { + return false; + } + + return true; +} + +/** + * Closes the buffer right now. Called from the window thread. + */ +void CocoaGraphicsBuffer:: +close_buffer() { + if (_gsg != nullptr) { + CocoaGraphicsStateGuardian *cocoagsg; + cocoagsg = DCAST(CocoaGraphicsStateGuardian, _gsg); + + if (cocoagsg != nullptr && cocoagsg->_context != nil) { + cocoagsg->lock_context(); + GLGraphicsBuffer::close_buffer(); + cocoagsg->unlock_context(); + } + _gsg.clear(); + } else { + GLGraphicsBuffer::close_buffer(); + } +} diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.I b/panda/src/cocoadisplay/cocoaGraphicsPipe.I index 874bf12fea..0ac4a79d83 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsPipe.I +++ b/panda/src/cocoadisplay/cocoaGraphicsPipe.I @@ -18,11 +18,3 @@ INLINE CGDirectDisplayID CocoaGraphicsPipe:: get_display_id() const { return _display; } - -/** - * Returns the Cocoa NSScreen pointer associated with this graphics pipe. - */ -INLINE NSScreen *CocoaGraphicsPipe:: -get_nsscreen() const { - return _screen; -} diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.h b/panda/src/cocoadisplay/cocoaGraphicsPipe.h index d53456e332..6f2f863e9c 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsPipe.h +++ b/panda/src/cocoadisplay/cocoaGraphicsPipe.h @@ -35,13 +35,10 @@ class FrameBufferProperties; */ class CocoaGraphicsPipe : public GraphicsPipe { public: - CocoaGraphicsPipe(); - CocoaGraphicsPipe(CGDirectDisplayID display); - CocoaGraphicsPipe(NSScreen *screen); + CocoaGraphicsPipe(CGDirectDisplayID display = CGMainDisplayID()); virtual ~CocoaGraphicsPipe(); INLINE CGDirectDisplayID get_display_id() const; - INLINE NSScreen *get_nsscreen() const; virtual string get_interface_name() const; static PT(GraphicsPipe) pipe_constructor(); @@ -64,11 +61,8 @@ protected: private: void load_display_information(); - // _display and _screen refer to the same thing, NSScreen being the tiny - // Cocoa wrapper around the Quartz display ID. NSScreen isn't generally - // useful, but we need it when creating the window. + // This is the Quartz display identifier. CGDirectDisplayID _display; - NSScreen *_screen; friend class CocoaGraphicsWindow; diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm index 10bef93e2e..5430373c55 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm @@ -12,10 +12,9 @@ */ #include "cocoaGraphicsPipe.h" -// #include "cocoaGraphicsBuffer.h" +#include "cocoaGraphicsBuffer.h" #include "cocoaGraphicsWindow.h" #include "cocoaGraphicsStateGuardian.h" -#include "cocoaPandaApp.h" #include "config_cocoadisplay.h" #include "frameBufferProperties.h" #include "displayInformation.h" @@ -30,104 +29,32 @@ TypeHandle CocoaGraphicsPipe::_type_handle; -static void init_app() { - if (NSApp == nil) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [CocoaPandaApp sharedApplication]; - -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; -#endif - [NSApp finishLaunching]; - [NSApp activateIgnoringOtherApps:YES]; - - // Put Cocoa into thread-safe mode by spawning a thread which immediately - // exits. - NSThread* thread = [[NSThread alloc] init]; - [thread start]; - [thread autorelease]; - } -} - /** - * Uses the main screen (the one the user is most likely to be working in at - * the moment). + * Takes a CoreGraphics display ID, which defaults to the main display. */ CocoaGraphicsPipe:: -CocoaGraphicsPipe() { +CocoaGraphicsPipe(CGDirectDisplayID display) : _display(display) { _supported_types = OT_window | OT_buffer | OT_texture_buffer; _is_valid = true; - init_app(); + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - _screen = [NSScreen mainScreen]; - NSNumber *num = [[_screen deviceDescription] objectForKey: @"NSScreenNumber"]; - _display = (CGDirectDisplayID) [num longValue]; + // Put Cocoa into thread-safe mode by spawning a thread which immediately + // exits. + NSThread* thread = [[NSThread alloc] init]; + [thread start]; + [thread autorelease]; + + // We used to also obtain the corresponding NSScreen here, but this causes + // the application icon to start bouncing, which may be undesirable for + // apps that will never open a window. _display_width = CGDisplayPixelsWide(_display); _display_height = CGDisplayPixelsHigh(_display); load_display_information(); cocoadisplay_cat.debug() - << "Creating CocoaGraphicsPipe for main screen " - << _screen << " with display ID " << _display << "\n"; -} - -/** - * Takes a CoreGraphics display ID. - */ -CocoaGraphicsPipe:: -CocoaGraphicsPipe(CGDirectDisplayID display) { - _supported_types = OT_window | OT_buffer | OT_texture_buffer; - _is_valid = true; - _display = display; - - init_app(); - - // Iterate over the screens to find the one with our display ID. - NSEnumerator *e = [[NSScreen screens] objectEnumerator]; - while (NSScreen *screen = (NSScreen *) [e nextObject]) { - NSNumber *num = [[screen deviceDescription] objectForKey: @"NSScreenNumber"]; - if (display == (CGDirectDisplayID) [num longValue]) { - _screen = screen; - break; - } - } - - _display_width = CGDisplayPixelsWide(_display); - _display_height = CGDisplayPixelsHigh(_display); - load_display_information(); - - cocoadisplay_cat.debug() - << "Creating CocoaGraphicsPipe for screen " - << _screen << " with display ID " << _display << "\n"; -} - -/** - * Takes an NSScreen pointer. - */ -CocoaGraphicsPipe:: -CocoaGraphicsPipe(NSScreen *screen) { - _supported_types = OT_window | OT_buffer | OT_texture_buffer; - _is_valid = true; - - init_app(); - - if (screen == nil) { - _screen = [NSScreen mainScreen]; - } else { - _screen = screen; - } - NSNumber *num = [[_screen deviceDescription] objectForKey: @"NSScreenNumber"]; - _display = (CGDirectDisplayID) [num longValue]; - - _display_width = CGDisplayPixelsWide(_display); - _display_height = CGDisplayPixelsHigh(_display); - load_display_information(); - - cocoadisplay_cat.debug() - << "Creating CocoaGraphicsPipe for screen " - << _screen << " with display ID " << _display << "\n"; + << "Creating CocoaGraphicsPipe for display ID " << _display << "\n"; } /** @@ -308,10 +235,12 @@ make_output(const string &name, flags, gsg, host); } - // Second thing to try: a GLGraphicsBuffer + // Second thing to try: a GLGraphicsBuffer. This requires a context, so if + // we don't have a host window, we instead create a CocoaGraphicsBuffer, + // which wraps around GLGraphicsBuffer and manages a context. if (retry == 1) { - if (!gl_support_fbo || host == NULL || + if (!gl_support_fbo || (flags & (BF_require_parasite | BF_require_window)) != 0) { return NULL; } @@ -334,33 +263,14 @@ make_output(const string &name, precertify = true; } } - return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop, - flags, gsg, host); - } -/* - // Third thing to try: a CocoaGraphicsBuffer - if (retry == 2) { - if (((flags&BF_require_parasite)!=0)|| - ((flags&BF_require_window)!=0)|| - ((flags&BF_resizeable)!=0)|| - ((flags&BF_size_track_host)!=0)|| - ((flags&BF_can_bind_layered)!=0)) { - return NULL; + if (host != NULL) { + return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop, + flags, gsg, host); + } else { + return new CocoaGraphicsBuffer(engine, this, name, fb_prop, win_prop, + flags, gsg, host); } - - if (!support_rtt) { - if (((flags&BF_rtt_cumulative)!=0)|| - ((flags&BF_can_bind_every)!=0)) { - // If we require Render-to-Texture, but can't be sure we support it, - // bail. - return NULL; - } - } - - return new CocoaGraphicsBuffer(engine, this, name, fb_prop, win_prop, - flags, gsg, host); } -*/ // Nothing else left to try. return NULL; diff --git a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.I b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.I index d593a3fe86..586df26850 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.I +++ b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.I @@ -19,3 +19,21 @@ INLINE const FrameBufferProperties &CocoaGraphicsStateGuardian:: get_fb_properties() const { return _fbprops; } + +/** + * Locks the context. + */ +INLINE void CocoaGraphicsStateGuardian:: +lock_context() { + nassertv(_context != nil); + CGLLockContext((CGLContextObj) [_context CGLContextObj]); +} + +/** + * Unlocks the context. + */ +INLINE void CocoaGraphicsStateGuardian:: +unlock_context() { + nassertv(_context != nil); + CGLUnlockContext((CGLContextObj) [_context CGLContextObj]); +} diff --git a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h index a2420e55c2..2e83e60c34 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h +++ b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h @@ -19,6 +19,7 @@ #include "glgsg.h" #import +#import /** * A tiny specialization on GLGraphicsStateGuardian to add some Cocoa-specific @@ -38,6 +39,9 @@ public: virtual ~CocoaGraphicsStateGuardian(); + INLINE void lock_context(); + INLINE void unlock_context(); + NSOpenGLContext *_share_context; NSOpenGLContext *_context; FrameBufferProperties _fbprops; diff --git a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm index 30630036ab..ec25b94486 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm @@ -24,6 +24,10 @@ #define kCGLRendererIDMatchingMask 0x00FE7F00 #endif +#ifndef NSAppKitVersionNumber10_7 +#define NSAppKitVersionNumber10_7 1138 +#endif + TypeHandle CocoaGraphicsStateGuardian::_type_handle; /** @@ -207,7 +211,8 @@ choose_pixel_format(const FrameBufferProperties &properties, attribs.push_back(NSOpenGLPFAAccelerated); } - attribs.push_back(NSOpenGLPFAWindow); + // This seems to cause getting a 3.2+ context to fail. + //attribs.push_back(NSOpenGLPFAWindow); if (need_pbuffer) { attribs.push_back(NSOpenGLPFAPixelBuffer); @@ -217,6 +222,16 @@ choose_pixel_format(const FrameBufferProperties &properties, attribs.push_back(NSOpenGLPFAScreenMask); attribs.push_back(CGDisplayIDToOpenGLDisplayMask(display)); + // Set OpenGL version if a minimum was requested. + if (gl_version.size() >= 1 && NSAppKitVersionNumber >= NSAppKitVersionNumber10_7) { + //NB. There is also NSOpenGLProfileVersion4_1Core, but this seems to cause + // a software implementation to be selected on my mac mini running 10.11. + if (gl_version[0] >= 4 || (gl_version.size() >= 2 && gl_version[0] == 3 && gl_version[1] >= 2)) { + attribs.push_back((NSOpenGLPixelFormatAttribute)99); // NSOpenGLPFAOpenGLProfile + attribs.push_back((NSOpenGLPixelFormatAttribute)0x3200); // NSOpenGLProfileVersion3_2Core + } + } + // End of the array attribs.push_back((NSOpenGLPixelFormatAttribute)0); diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index 6de003d2aa..39ecf09162 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -15,6 +15,7 @@ #include "cocoaGraphicsStateGuardian.h" #include "config_cocoadisplay.h" #include "cocoaGraphicsPipe.h" +#include "cocoaPandaApp.h" #include "graphicsPipe.h" #include "keyboardButton.h" @@ -65,6 +66,18 @@ CocoaGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, _fullscreen_mode = NULL; _windowed_mode = NULL; + // Now that we know for sure we want a window, we can create the Cocoa app. + // This will cause the application icon to appear and start bouncing. + if (NSApp == nil) { + [CocoaPandaApp sharedApplication]; + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; +#endif + [NSApp finishLaunching]; + [NSApp activateIgnoringOtherApps:YES]; + } + GraphicsWindowInputDevice device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); add_input_device(device); @@ -144,7 +157,7 @@ begin_frame(FrameMode mode, Thread *current_thread) { nassertr(_view != nil, false); // Place a lock on the context. - CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]); + cocoagsg->lock_context(); // Set the drawable. if (_properties.get_fullscreen()) { @@ -210,7 +223,7 @@ end_frame(FrameMode mode, Thread *current_thread) { CocoaGraphicsStateGuardian *cocoagsg; DCAST_INTO_V(cocoagsg, _gsg); - CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]); + cocoagsg->unlock_context(); if (mode == FM_render) { // end_render_texture(); @@ -239,7 +252,7 @@ end_flip() { CocoaGraphicsStateGuardian *cocoagsg; DCAST_INTO_V(cocoagsg, _gsg); - CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]); + cocoagsg->lock_context(); // Swap the front and back buffer. [cocoagsg->_context flushBuffer]; @@ -247,7 +260,7 @@ end_flip() { // Flush the window [[_view window] flushWindow]; - CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]); + cocoagsg->unlock_context(); } GraphicsWindow::end_flip(); } @@ -277,7 +290,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) { @@ -385,6 +412,16 @@ open_window() { } } + // Iterate over the screens to find the one with our display ID. + NSScreen *screen; + NSEnumerator *e = [[NSScreen screens] objectEnumerator]; + while (screen = (NSScreen *) [e nextObject]) { + NSNumber *num = [[screen deviceDescription] objectForKey: @"NSScreenNumber"]; + if (cocoa_pipe->_display == (CGDirectDisplayID) [num longValue]) { + break; + } + } + // Center the window if coordinates were set to -1 or -2 TODO: perhaps in // future, in the case of -1, it should use the origin used in a previous // run of Panda @@ -392,7 +429,7 @@ open_window() { if (parent_nsview != NULL) { container = [parent_nsview bounds]; } else { - container = [cocoa_pipe->_screen frame]; + container = [screen frame]; container.origin = NSMakePoint(0, 0); } int x = _properties.get_x_origin(); @@ -439,7 +476,7 @@ open_window() { _window = [[CocoaPandaWindow alloc] initWithContentRect: rect styleMask:windowStyle - screen:cocoa_pipe->_screen + screen:screen window:this]; if (_window == nil) { @@ -450,7 +487,7 @@ open_window() { } // Lock the context, so we can safely operate on it. - CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]); + cocoagsg->lock_context(); // Create the NSView to render to. NSRect rect = NSMakeRect(0, 0, _properties.get_x_size(), _properties.get_y_size()); @@ -574,7 +611,7 @@ open_window() { cocoagsg->reset_if_new(); // Release the context. - CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]); + cocoagsg->unlock_context(); if (!cocoagsg->is_valid()) { close_window(); @@ -623,9 +660,9 @@ close_window() { cocoagsg = DCAST(CocoaGraphicsStateGuardian, _gsg); if (cocoagsg != NULL && cocoagsg->_context != nil) { - CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]); + cocoagsg->lock_context(); [cocoagsg->_context clearDrawable]; - CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]); + cocoagsg->unlock_context(); } _gsg.clear(); } @@ -1415,9 +1452,9 @@ handle_close_event() { cocoagsg = DCAST(CocoaGraphicsStateGuardian, _gsg); if (cocoagsg != NULL && cocoagsg->_context != nil) { - CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]); + cocoagsg->lock_context(); [cocoagsg->_context clearDrawable]; - CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]); + cocoagsg->unlock_context(); } _gsg.clear(); } diff --git a/panda/src/cocoadisplay/config_cocoadisplay.mm b/panda/src/cocoadisplay/config_cocoadisplay.mm index 4f67bc1438..42f2f98a8f 100644 --- a/panda/src/cocoadisplay/config_cocoadisplay.mm +++ b/panda/src/cocoadisplay/config_cocoadisplay.mm @@ -12,6 +12,7 @@ */ #include "config_cocoadisplay.h" +#include "cocoaGraphicsBuffer.h" #include "cocoaGraphicsPipe.h" #include "cocoaGraphicsStateGuardian.h" #include "cocoaGraphicsWindow.h" @@ -40,6 +41,7 @@ init_libcocoadisplay() { } initialized = true; + CocoaGraphicsBuffer::init_type(); CocoaGraphicsPipe::init_type(); CocoaGraphicsStateGuardian::init_type(); CocoaGraphicsWindow::init_type(); diff --git a/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm b/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm index 28755a66dd..e85d1e08eb 100644 --- a/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm +++ b/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm @@ -1,4 +1,5 @@ #include "config_cocoadisplay.mm" +#include "cocoaGraphicsBuffer.mm" #include "cocoaGraphicsPipe.mm" #include "cocoaGraphicsStateGuardian.mm" #include "cocoaGraphicsWindow.mm" 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.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/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/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.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.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.I b/panda/src/collide/collisionVisualizer.I index 3c14f309cf..16e218f267 100644 --- a/panda/src/collide/collisionVisualizer.I +++ b/panda/src/collide/collisionVisualizer.I @@ -31,6 +31,7 @@ SolidInfo() { */ INLINE void CollisionVisualizer:: set_point_scale(PN_stdfloat point_scale) { + LightMutexHolder holder(_lock); _point_scale = point_scale; } @@ -39,6 +40,7 @@ set_point_scale(PN_stdfloat point_scale) { */ INLINE PN_stdfloat CollisionVisualizer:: get_point_scale() const { + LightMutexHolder holder(_lock); return _point_scale; } @@ -51,6 +53,7 @@ get_point_scale() const { */ INLINE void CollisionVisualizer:: set_normal_scale(PN_stdfloat normal_scale) { + LightMutexHolder holder(_lock); _normal_scale = normal_scale; } @@ -59,6 +62,7 @@ set_normal_scale(PN_stdfloat normal_scale) { */ INLINE PN_stdfloat CollisionVisualizer:: get_normal_scale() const { + LightMutexHolder holder(_lock); return _normal_scale; } diff --git a/panda/src/collide/collisionVisualizer.cxx b/panda/src/collide/collisionVisualizer.cxx index 19699b9d6f..204a39b6bd 100644 --- a/panda/src/collide/collisionVisualizer.cxx +++ b/panda/src/collide/collisionVisualizer.cxx @@ -41,7 +41,7 @@ TypeHandle CollisionVisualizer::_type_handle; * */ CollisionVisualizer:: -CollisionVisualizer(const string &name) : PandaNode(name) { +CollisionVisualizer(const string &name) : PandaNode(name), _lock("CollisionVisualizer") { set_cull_callback(); // We always want to render the CollisionVisualizer node itself (even if it @@ -51,6 +51,23 @@ CollisionVisualizer(const string &name) : PandaNode(name) { _normal_scale = 1.0f; } +/** + * Copy constructor. + */ +CollisionVisualizer:: +CollisionVisualizer(const CollisionVisualizer ©) : + PandaNode(copy), + _lock("CollisionVisualizer"), + _point_scale(copy._point_scale), + _normal_scale(copy._normal_scale) { + + set_cull_callback(); + + // We always want to render the CollisionVisualizer node itself (even if it + // doesn't appear to have any geometry within it). + set_internal_bounds(new OmniBoundingVolume()); +} + /** * */ @@ -64,6 +81,7 @@ CollisionVisualizer:: */ void CollisionVisualizer:: clear() { + LightMutexHolder holder(_lock); _data.clear(); } @@ -99,6 +117,8 @@ bool CollisionVisualizer:: cull_callback(CullTraverser *trav, CullTraverserData &data) { // Now we go through and actually draw our visualized collision solids. + LightMutexHolder holder(_lock); + Data::const_iterator di; for (di = _data.begin(); di != _data.end(); ++di) { const TransformState *net_transform = (*di).first; @@ -111,10 +131,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; @@ -260,6 +277,7 @@ output(ostream &out) const { void CollisionVisualizer:: begin_traversal() { CollisionRecorder::begin_traversal(); + LightMutexHolder holder(_lock); _data.clear(); } @@ -274,12 +292,13 @@ collision_tested(const CollisionEntry &entry, bool detected) { NodePath node_path = entry.get_into_node_path(); CPT(TransformState) net_transform = node_path.get_net_transform(); - const CollisionSolid *solid = entry.get_into(); - nassertv(solid != (CollisionSolid *)NULL); + CPT(CollisionSolid) solid = entry.get_into(); + nassertv(!solid.is_null()); - VizInfo &viz_info = _data[net_transform]; + LightMutexHolder holder(_lock); + VizInfo &viz_info = _data[move(net_transform)]; if (detected) { - viz_info._solids[solid]._detected_count++; + viz_info._solids[move(solid)]._detected_count++; if (entry.has_surface_point()) { CollisionPoint p; @@ -289,7 +308,7 @@ collision_tested(const CollisionEntry &entry, bool detected) { } } else { - viz_info._solids[solid]._missed_count++; + viz_info._solids[move(solid)]._missed_count++; } } diff --git a/panda/src/collide/collisionVisualizer.h b/panda/src/collide/collisionVisualizer.h index 27c2d257a4..88491c4e26 100644 --- a/panda/src/collide/collisionVisualizer.h +++ b/panda/src/collide/collisionVisualizer.h @@ -20,6 +20,7 @@ #include "collisionSolid.h" #include "nodePath.h" #include "pmap.h" +#include "lightMutex.h" #ifdef DO_COLLISION_RECORDING @@ -33,7 +34,8 @@ */ class EXPCL_PANDA_COLLIDE CollisionVisualizer : public PandaNode, public CollisionRecorder { PUBLISHED: - CollisionVisualizer(const string &name); + explicit CollisionVisualizer(const string &name); + CollisionVisualizer(const CollisionVisualizer ©); virtual ~CollisionVisualizer(); INLINE void set_point_scale(PN_stdfloat point_scale); @@ -89,6 +91,7 @@ private: Points _points; }; + LightMutex _lock; typedef pmap Data; Data _data; 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 57168aa866..a1a7264af0 100644 --- a/panda/src/cull/cullBinBackToFront.cxx +++ b/panda/src/cull/cullBinBackToFront.cxx @@ -99,7 +99,7 @@ draw(bool force, Thread *current_thread) { data_reader.set_object(object->_munged_data); data_reader.check_array_readers(); geom_reader.set_object(object->_geom); - geom_reader.draw(_gsg, object->_munger, &data_reader, force); + geom_reader.draw(_gsg, &data_reader, force); } else { // It has a callback associated. object->draw_callback(_gsg, force, current_thread); diff --git a/panda/src/cull/cullBinFixed.cxx b/panda/src/cull/cullBinFixed.cxx index 6bbce5ed78..212bc4f320 100644 --- a/panda/src/cull/cullBinFixed.cxx +++ b/panda/src/cull/cullBinFixed.cxx @@ -85,7 +85,7 @@ draw(bool force, Thread *current_thread) { data_reader.set_object(object->_munged_data); data_reader.check_array_readers(); geom_reader.set_object(object->_geom); - geom_reader.draw(_gsg, object->_munger, &data_reader, force); + geom_reader.draw(_gsg, &data_reader, force); } else { // It has a callback associated. object->draw_callback(_gsg, force, current_thread); diff --git a/panda/src/cull/cullBinFrontToBack.cxx b/panda/src/cull/cullBinFrontToBack.cxx index a532fef811..0fb05a68b5 100644 --- a/panda/src/cull/cullBinFrontToBack.cxx +++ b/panda/src/cull/cullBinFrontToBack.cxx @@ -99,7 +99,7 @@ draw(bool force, Thread *current_thread) { data_reader.set_object(object->_munged_data); data_reader.check_array_readers(); geom_reader.set_object(object->_geom); - geom_reader.draw(_gsg, object->_munger, &data_reader, force); + geom_reader.draw(_gsg, &data_reader, force); } else { // It has a callback associated. object->draw_callback(_gsg, force, current_thread); diff --git a/panda/src/cull/cullBinStateSorted.cxx b/panda/src/cull/cullBinStateSorted.cxx index 07f3f9c9a3..04890af48c 100644 --- a/panda/src/cull/cullBinStateSorted.cxx +++ b/panda/src/cull/cullBinStateSorted.cxx @@ -84,7 +84,7 @@ draw(bool force, Thread *current_thread) { data_reader.set_object(object->_munged_data); data_reader.check_array_readers(); geom_reader.set_object(object->_geom); - geom_reader.draw(_gsg, object->_munger, &data_reader, force); + geom_reader.draw(_gsg, &data_reader, force); } else { // It has a callback associated. object->draw_callback(_gsg, force, current_thread); diff --git a/panda/src/cull/cullBinUnsorted.cxx b/panda/src/cull/cullBinUnsorted.cxx index c9a68401b6..4ef8c35ae6 100644 --- a/panda/src/cull/cullBinUnsorted.cxx +++ b/panda/src/cull/cullBinUnsorted.cxx @@ -69,7 +69,7 @@ draw(bool force, Thread *current_thread) { data_reader.set_object(object->_munged_data); data_reader.check_array_readers(); geom_reader.set_object(object->_geom); - geom_reader.draw(_gsg, object->_munger, &data_reader, force); + geom_reader.draw(_gsg, &data_reader, force); } else { // It has a callback associated. object->draw_callback(_gsg, force, current_thread); diff --git a/panda/src/device/analogNode.h b/panda/src/device/analogNode.h index 862277e90b..d62eaa0d3b 100644 --- a/panda/src/device/analogNode.h +++ b/panda/src/device/analogNode.h @@ -38,7 +38,7 @@ */ class EXPCL_PANDA_DEVICE AnalogNode : public DataNode { PUBLISHED: - AnalogNode(ClientBase *client, const string &device_name); + explicit AnalogNode(ClientBase *client, const string &device_name); virtual ~AnalogNode(); INLINE bool is_valid() const; diff --git a/panda/src/device/buttonNode.h b/panda/src/device/buttonNode.h index 4399151d4f..6fce65a9e0 100644 --- a/panda/src/device/buttonNode.h +++ b/panda/src/device/buttonNode.h @@ -34,7 +34,7 @@ */ class EXPCL_PANDA_DEVICE ButtonNode : public DataNode { PUBLISHED: - ButtonNode(ClientBase *client, const string &device_name); + explicit ButtonNode(ClientBase *client, const string &device_name); 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/mouseAndKeyboard.h b/panda/src/device/mouseAndKeyboard.h index 673e999da2..24a6efe07a 100644 --- a/panda/src/device/mouseAndKeyboard.h +++ b/panda/src/device/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/device/trackerNode.h b/panda/src/device/trackerNode.h index 3c18f65294..628422324b 100644 --- a/panda/src/device/trackerNode.h +++ b/panda/src/device/trackerNode.h @@ -31,8 +31,8 @@ */ class EXPCL_PANDA_DEVICE TrackerNode : public DataNode { PUBLISHED: - TrackerNode(ClientBase *client, const string &device_name); - TrackerNode(ClientTrackerDevice *device); + explicit TrackerNode(ClientBase *client, const string &device_name); + explicit TrackerNode(ClientTrackerDevice *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/displayRegion.I b/panda/src/display/displayRegion.I index 6004791d31..8982461879 100644 --- a/panda/src/display/displayRegion.I +++ b/panda/src/display/displayRegion.I @@ -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 4ffa29285c..99aea51477 100644 --- a/panda/src/display/displayRegion.cxx +++ b/panda/src/display/displayRegion.cxx @@ -84,22 +84,30 @@ DisplayRegion:: */ void DisplayRegion:: cleanup() { - set_camera(NodePath()); + CDStageWriter cdata(_cycler, 0); + if (cdata->_camera_node != nullptr) { + // We need to tell the old camera we're not using it anymore. + cdata->_camera_node->remove_display_region(this); + } + cdata->_camera_node = nullptr; + cdata->_camera = NodePath(); - CDCullWriter cdata(_cycler_cull, true); - cdata->_cull_result = NULL; + CDCullWriter cdata_cull(_cycler_cull, true); + cdata_cull->_cull_result = nullptr; } /** * Sets the lens index, allows for multiple lenses to be attached to a camera. * This is useful for a variety of setups, such as fish eye rendering. The * default is 0. + * + * 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. */ void DisplayRegion:: set_lens_index(int index) { - int pipeline_stage = Thread::get_current_pipeline_stage(); - nassertv(pipeline_stage == 0); - CDWriter cdata(_cycler); + Thread *current_thread = Thread::get_current_thread(); + CDWriter cdata(_cycler, true, current_thread); cdata->_lens_index = index; } @@ -107,12 +115,14 @@ set_lens_index(int index) { * Changes the portion of the framebuffer this DisplayRegion corresponds to. * The parameters range from 0 to 1, where 0,0 is the lower left corner and * 1,1 is the upper right; (0, 1, 0, 1) represents the whole screen. + * + * 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. */ void DisplayRegion:: set_dimensions(int i, const LVecBase4 &dimensions) { - int pipeline_stage = Thread::get_current_pipeline_stage(); - nassertv(pipeline_stage == 0); - CDWriter cdata(_cycler); + Thread *current_thread = Thread::get_current_thread(); + CDWriter cdata(_cycler, true, current_thread); cdata->_regions[i]._dimensions = dimensions; @@ -145,15 +155,13 @@ is_stereo() const { * * The camera is actually set via a NodePath, which clarifies which instance * of the camera (if there happen to be multiple instances) we should use. + * + * 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. */ void DisplayRegion:: set_camera(const NodePath &camera) { - int pipeline_stage = Thread::get_current_pipeline_stage(); - - // We allow set_camera(NodePath()) to happen in cleanup(), which can be - // called from any pipeline stage. - nassertv(pipeline_stage == 0 || camera.is_empty()); - CDStageWriter cdata(_cycler, 0); + CDWriter cdata(_cycler, true); Camera *camera_node = (Camera *)NULL; if (!camera.is_empty()) { @@ -181,16 +189,17 @@ set_camera(const NodePath &camera) { /** * Sets the active flag associated with the DisplayRegion. If the * DisplayRegion is marked inactive, nothing is rendered. + * + * 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. */ void DisplayRegion:: set_active(bool active) { - int pipeline_stage = Thread::get_current_pipeline_stage(); - nassertv(pipeline_stage == 0); - CDLockedReader cdata(_cycler); + Thread *current_thread = Thread::get_current_thread(); + CDWriter cdata(_cycler, true, current_thread); if (active != cdata->_active) { - CDWriter cdataw(_cycler, cdata); - cdataw->_active = active; + cdata->_active = active; win_display_regions_changed(); } } @@ -199,15 +208,17 @@ set_active(bool active) { * Sets the sort value associated with the DisplayRegion. Within a window, * DisplayRegions will be rendered in order from the lowest sort value to the * highest. + * + * 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. */ void DisplayRegion:: set_sort(int sort) { - nassertv(Thread::get_current_pipeline_stage() == 0); - CDLockedReader cdata(_cycler); + Thread *current_thread = Thread::get_current_thread(); + CDWriter cdata(_cycler, true, current_thread); if (sort != cdata->_sort) { - CDWriter cdataw(_cycler, cdata); - cdataw->_sort = sort; + cdata->_sort = sort; win_display_regions_changed(); } } @@ -332,12 +343,14 @@ get_cull_traverser() { * * This is particularly useful when rendering cube maps and/or stereo * textures. + * + * 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. */ void DisplayRegion:: set_target_tex_page(int page) { - int pipeline_stage = Thread::get_current_pipeline_stage(); - nassertv(pipeline_stage == 0); - CDWriter cdata(_cycler); + Thread *current_thread = Thread::get_current_thread(); + CDWriter cdata(_cycler, true, current_thread); cdata->_target_tex_page = page; } @@ -555,9 +568,6 @@ compute_pixels() { */ void DisplayRegion:: compute_pixels_all_stages() { - int pipeline_stage = Thread::get_current_pipeline_stage(); - nassertv(pipeline_stage == 0); - if (_window != (GraphicsOutput *)NULL) { OPEN_ITERATE_ALL_STAGES(_cycler) { CDStageWriter cdata(_cycler, pipeline_stage); 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 045c534ebe..f65c5377fc 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -153,7 +153,6 @@ GraphicsEngine(Pipeline *pipeline) : _windows_sorted = true; _window_sort_index = 0; - _needs_open_windows = false; set_threading_model(GraphicsThreadingModel(threading_model)); if (!_threading_model.is_default()) { @@ -326,13 +325,10 @@ make_output(GraphicsPipe *pipe, // Sanity check everything. - GraphicsThreadingModel threading_model = get_threading_model(); nassertr(pipe != (GraphicsPipe *)NULL, NULL); if (gsg != (GraphicsStateGuardian *)NULL) { nassertr(pipe == gsg->get_pipe(), NULL); nassertr(this == gsg->get_engine(), NULL); - nassertr(threading_model.get_draw_name() == - gsg->get_threading_model().get_draw_name(), NULL); } // Are we really asking for a callback window? @@ -346,8 +342,8 @@ make_output(GraphicsPipe *pipe, if (this_gsg != (GraphicsStateGuardian *)NULL) { CallbackGraphicsWindow *window = new CallbackGraphicsWindow(this, pipe, name, fb_prop, win_prop, flags, this_gsg); window->_sort = sort; - do_add_window(window, threading_model); - do_add_gsg(window->get_gsg(), pipe, threading_model); + do_add_window(window); + do_add_gsg(window->get_gsg(), pipe); display_cat.info() << "Created output of type CallbackGraphicsWindow\n"; return window; } @@ -386,8 +382,8 @@ make_output(GraphicsPipe *pipe, (host->get_fb_properties().subsumes(fb_prop))) { ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags); buffer->_sort = sort; - do_add_window(buffer, threading_model); - do_add_gsg(host->get_gsg(), pipe, threading_model); + do_add_window(buffer); + do_add_gsg(host->get_gsg(), pipe); display_cat.info() << "Created output of type ParasiteBuffer\n"; return buffer; } @@ -398,8 +394,8 @@ make_output(GraphicsPipe *pipe, if (force_parasite_buffer && can_use_parasite) { ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags); buffer->_sort = sort; - do_add_window(buffer, threading_model); - do_add_gsg(host->get_gsg(), pipe, threading_model); + do_add_window(buffer); + do_add_gsg(host->get_gsg(), pipe); display_cat.info() << "Created output of type ParasiteBuffer\n"; return buffer; } @@ -412,17 +408,15 @@ make_output(GraphicsPipe *pipe, pipe->make_output(name, fb_prop, win_prop, flags, this, gsg, host, retry, precertify); if (window != (GraphicsOutput *)NULL) { window->_sort = sort; - if ((precertify) && (gsg != 0) && (window->get_gsg()==gsg)) { - do_add_window(window, threading_model); - do_add_gsg(window->get_gsg(), pipe, threading_model); + if (precertify && gsg != nullptr && window->get_gsg() == gsg) { + do_add_window(window); display_cat.info() << "Created output of type " << window->get_type() << "\n"; return window; } - do_add_window(window, threading_model); + do_add_window(window); open_windows(); if (window->is_valid()) { - do_add_gsg(window->get_gsg(), pipe, threading_model); display_cat.info() << "Created output of type " << window->get_type() << "\n"; @@ -462,8 +456,8 @@ make_output(GraphicsPipe *pipe, if (can_use_parasite) { ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags); buffer->_sort = sort; - do_add_window(buffer, threading_model); - do_add_gsg(host->get_gsg(), pipe, threading_model); + do_add_window(buffer); + do_add_gsg(host->get_gsg(), pipe); display_cat.info() << "Created output of type ParasiteBuffer\n"; return buffer; } @@ -479,30 +473,24 @@ make_output(GraphicsPipe *pipe, * shouldn't be called by user code as make_output normally does this under * the hood; it may be useful in esoteric cases in which a custom window * object is used. + * + * This can be called during the rendering loop, unlike make_output(); the + * window will be opened before the next frame begins rendering. Because it + * doesn't call open_windows(), however, it's not guaranteed that the window + * will succeed opening even if it returns true. */ bool GraphicsEngine:: add_window(GraphicsOutput *window, int sort) { - nassertr(window != NULL, false); - - GraphicsThreadingModel threading_model = get_threading_model(); + nassertr(window != nullptr, false); nassertr(this == window->get_engine(), false); window->_sort = sort; - do_add_window(window, threading_model); + do_add_window(window); - open_windows(); - if (window->is_valid()) { - do_add_gsg(window->get_gsg(), window->get_pipe(), threading_model); + display_cat.info() + << "Added output of type " << window->get_type() << "\n"; - display_cat.info() - << "Added output of type " << window->get_type() << "\n"; - - return true; - - } else { - remove_window(window); - return false; - } + return true; } /** @@ -537,6 +525,17 @@ remove_window(GraphicsOutput *window) { } count = _windows.erase(ptwin); } + + // Also check whether it is in _new_windows. + { + MutexHolder new_windows_holder(_new_windows_lock, current_thread); + size_t old_size = _new_windows.size(); + _new_windows.erase(std::remove(_new_windows.begin(), _new_windows.end(), ptwin), _new_windows.end()); + if (count == 0 && _new_windows.size() < old_size) { + count = 1; + } + } + if (count == 0) { // Never heard of this window. Do nothing. return false; @@ -584,6 +583,8 @@ void GraphicsEngine:: remove_all_windows() { Thread *current_thread = Thread::get_current_thread(); + ReMutexHolder holder(_lock, current_thread); + // Let's move the _windows vector into a local copy first, and walk through // that local copy, just in case someone we call during the loop attempts to // modify _windows. I don't know what code would be doing this, but it @@ -601,6 +602,11 @@ remove_all_windows() { } } + { + MutexHolder new_windows_holder(_new_windows_lock, current_thread); + _new_windows.clear(); + } + _app.do_close(this, current_thread); _app.do_pending(this, current_thread); terminate_threads(current_thread); @@ -694,14 +700,12 @@ render_frame() { } #endif - if (_needs_open_windows) { - // Make sure our buffers and windows are fully realized before we render a - // frame. We do this particularly to realize our offscreen buffers, so - // that we don't render a frame before the offscreen buffers are ready - // (which might result in a frame going by without some textures having - // been rendered). - open_windows(); - } + // Make sure our buffers and windows are fully realized before we render a + // frame. We do this particularly to realize our offscreen buffers, so + // that we don't render a frame before the offscreen buffers are ready + // (which might result in a frame going by without some textures having + // been rendered). + open_windows(); ClockObject *global_clock = ClockObject::get_global_clock(); @@ -945,10 +949,58 @@ open_windows() { ReMutexHolder holder(_lock, current_thread); - if (!_windows_sorted) { - do_resort_windows(); + pvector new_windows; + { + MutexHolder new_windows_holder(_new_windows_lock, current_thread); + if (_new_windows.empty()) { + return; + } + + for (auto it = _new_windows.begin(); it != _new_windows.end(); ++it) { + GraphicsOutput *window = *it; + + WindowRenderer *cull = + get_window_renderer(_threading_model.get_cull_name(), + _threading_model.get_cull_stage()); + WindowRenderer *draw = + get_window_renderer(_threading_model.get_draw_name(), + _threading_model.get_draw_stage()); + + if (_threading_model.get_cull_sorting()) { + cull->add_window(cull->_cull, window); + draw->add_window(draw->_draw, window); + } else { + cull->add_window(cull->_cdraw, window); + } + + // Ask the pipe which thread it prefers to run its windowing commands in + // (the "window thread"). This is the thread that handles the commands + // to open, resize, etc. the window. X requires this to be done in the + // app thread (along with all the other windows, since X is strictly + // single-threaded), but Windows requires this to be done in draw + // (because once an OpenGL context has been bound in a given thread, it + // cannot subsequently be bound in any other thread, and we have to bind + // a context in open_window()). + + switch (window->get_pipe()->get_preferred_window_thread()) { + case GraphicsPipe::PWT_app: + _app.add_window(_app._window, window); + break; + + case GraphicsPipe::PWT_draw: + draw->add_window(draw->_window, window); + break; + } + + _windows.push_back(window); + } + + // Steal the list, since remove_window() may remove from _new_windows. + new_windows.swap(_new_windows); } + do_resort_windows(); + // We do it twice, to allow both cull and draw to process the window. for (int i = 0; i < 2; ++i) { _app.do_windows(this, current_thread); @@ -970,7 +1022,15 @@ open_windows() { } } - _needs_open_windows = false; + // Now go through the list again to check whether they opened successfully. + for (auto it = new_windows.begin(); it != new_windows.end(); ++it) { + GraphicsOutput *window = *it; + if (window->is_valid()) { + do_add_gsg(window->get_gsg(), window->get_pipe()); + } else { + remove_window(window); + } + } } /** @@ -1060,17 +1120,34 @@ extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) { // has finished its current task. WindowRenderer *wr = get_window_renderer(draw_name, 0); RenderThread *thread = (RenderThread *)wr; - MutexHolder holder2(thread->_cv_mutex); + MutexHolder cv_holder(thread->_cv_mutex); while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } - // OK, now the draw thread is idle. That's really good enough for our - // purposes; we don't *actually* need to make the draw thread do the work - // --it's sufficient that it's not doing anything else while we access the - // GSG. - return gsg->extract_texture_data(tex); + // Temporarily set this so that it accesses data from the current thread. + int pipeline_stage = Thread::get_current_pipeline_stage(); + int draw_pipeline_stage = thread->get_pipeline_stage(); + thread->set_pipeline_stage(pipeline_stage); + + // Now that the draw thread is idle, signal it to do the extraction task. + thread->_gsg = gsg; + thread->_texture = tex; + thread->_thread_state = TS_do_extract; + thread->_cv_start.notify(); + thread->_cv_mutex.release(); + thread->_cv_mutex.acquire(); + + //XXX is this necessary, or is acquiring the mutex enough? + while (thread->_thread_state != TS_wait) { + thread->_cv_done.wait(); + } + + thread->set_pipeline_stage(draw_pipeline_stage); + thread->_gsg = nullptr; + thread->_texture = nullptr; + return thread->_result; } } @@ -1090,6 +1167,7 @@ extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) { void GraphicsEngine:: dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, GraphicsStateGuardian *gsg) { nassertv(sattr->get_shader() != (Shader *)NULL); + nassertv(gsg != nullptr); ReMutexHolder holder(_lock); @@ -1098,26 +1176,43 @@ dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, Graph string draw_name = gsg->get_threading_model().get_draw_name(); if (draw_name.empty()) { // A single-threaded environment. No problem. + gsg->set_state_and_transform(state, TransformState::make_identity()); + gsg->dispatch_compute(work_groups[0], work_groups[1], work_groups[2]); } else { // A multi-threaded environment. We have to wait until the draw thread // has finished its current task. WindowRenderer *wr = get_window_renderer(draw_name, 0); RenderThread *thread = (RenderThread *)wr; - MutexHolder holder2(thread->_cv_mutex); + MutexHolder cv_holder(thread->_cv_mutex); while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } - // OK, now the draw thread is idle. That's really good enough for our - // purposes; we don't *actually* need to make the draw thread do the work - // --it's sufficient that it's not doing anything else while we access the - // GSG. - } + // Temporarily set this so that it accesses data from the current thread. + int pipeline_stage = Thread::get_current_pipeline_stage(); + int draw_pipeline_stage = thread->get_pipeline_stage(); + thread->set_pipeline_stage(pipeline_stage); - gsg->set_state_and_transform(state, TransformState::make_identity()); - gsg->dispatch_compute(work_groups[0], work_groups[1], work_groups[2]); + // Now that the draw thread is idle, signal it to do the compute task. + thread->_gsg = gsg; + thread->_state = state.p(); + thread->_work_groups = work_groups; + thread->_thread_state = TS_do_compute; + thread->_cv_start.notify(); + thread->_cv_mutex.release(); + thread->_cv_mutex.acquire(); + + //XXX is this necessary, or is acquiring the mutex enough? + while (thread->_thread_state != TS_wait) { + thread->_cv_done.wait(); + } + + thread->set_pipeline_stage(draw_pipeline_stage); + thread->_gsg = nullptr; + thread->_state = nullptr; + } } /** @@ -1271,7 +1366,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); @@ -1380,7 +1475,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; @@ -1849,16 +1944,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; } @@ -1937,10 +2022,10 @@ do_draw(GraphicsOutput *win, GraphicsStateGuardian *gsg, DisplayRegion *dr, Thre * list of windows, and to request that the window be opened. */ void GraphicsEngine:: -do_add_window(GraphicsOutput *window, - const GraphicsThreadingModel &threading_model) { - nassertv(window != NULL); - ReMutexHolder holder(_lock); +do_add_window(GraphicsOutput *window) { + nassertv(window != nullptr); + + MutexHolder holder(_new_windows_lock); nassertv(window->get_engine() == this); // We have a special counter that is unique per window that allows us to @@ -1948,50 +2033,13 @@ do_add_window(GraphicsOutput *window, window->_internal_sort_index = _window_sort_index; ++_window_sort_index; - _windows_sorted = false; - _windows.push_back(window); - - WindowRenderer *cull = - get_window_renderer(threading_model.get_cull_name(), - threading_model.get_cull_stage()); - WindowRenderer *draw = - get_window_renderer(threading_model.get_draw_name(), - threading_model.get_draw_stage()); - - if (threading_model.get_cull_sorting()) { - cull->add_window(cull->_cull, window); - draw->add_window(draw->_draw, window); - } else { - cull->add_window(cull->_cdraw, window); - } - -/* - * Ask the pipe which thread it prefers to run its windowing commands in (the - * "window thread"). This is the thread that handles the commands to open, - * resize, etc. the window. X requires this to be done in the app thread - * (along with all the other windows, since X is strictly single-threaded), - * but Windows requires this to be done in draw (because once an OpenGL - * context has been bound in a given thread, it cannot subsequently be bound - * in any other thread, and we have to bind a context in open_window()). - */ - - switch (window->get_pipe()->get_preferred_window_thread()) { - case GraphicsPipe::PWT_app: - _app.add_window(_app._window, window); - break; - - case GraphicsPipe::PWT_draw: - draw->add_window(draw->_window, window); - break; - } - if (display_cat.is_debug()) { display_cat.debug() << "Created " << window->get_type() << " " << (void *)window << "\n"; } window->request_open(); - _needs_open_windows = true; + _new_windows.push_back(window); } /** @@ -2000,13 +2048,12 @@ do_add_window(GraphicsOutput *window, * variables based on the gsg's capabilities. */ void GraphicsEngine:: -do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe, - const GraphicsThreadingModel &threading_model) { +do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe) { nassertv(gsg != NULL); ReMutexHolder holder(_lock); nassertv(gsg->get_pipe() == pipe && gsg->get_engine() == this); - gsg->_threading_model = threading_model; + gsg->_threading_model = _threading_model; if (!_default_loader.is_null()) { gsg->set_loader(_default_loader); } @@ -2014,8 +2061,8 @@ do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe, auto_adjust_capabilities(gsg); WindowRenderer *draw = - get_window_renderer(threading_model.get_draw_name(), - threading_model.get_draw_stage()); + get_window_renderer(_threading_model.get_draw_name(), + _threading_model.get_draw_stage()); draw->add_gsg(gsg); } @@ -2574,6 +2621,17 @@ thread_main() { do_pending(_engine, current_thread); break; + case TS_do_compute: + nassertd(_gsg != nullptr && _state != nullptr) break; + _gsg->set_state_and_transform(_state, TransformState::make_identity()); + _gsg->dispatch_compute(_work_groups[0], _work_groups[1], _work_groups[2]); + break; + + case TS_do_extract: + nassertd(_gsg != nullptr && _texture != nullptr) break; + _result = _gsg->extract_texture_data(_texture); + break; + case TS_terminate: do_pending(_engine, current_thread); do_close(_engine, current_thread); diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 04f0993dfa..c21bec1d73 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); @@ -123,6 +123,8 @@ public: TS_do_flip, TS_do_release, TS_do_windows, + TS_do_compute, + TS_do_extract, TS_terminate, TS_done }; @@ -142,11 +144,11 @@ 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(Windows wlist, Thread *current_thread); void cull_to_bins(GraphicsOutput *win, GraphicsStateGuardian *gsg, DisplayRegion *dr, SceneSetup *scene_setup, CullResult *cull_result, Thread *current_thread); @@ -166,10 +168,8 @@ private: void do_draw(GraphicsOutput *win, GraphicsStateGuardian *gsg, DisplayRegion *dr, Thread *current_thread); - void do_add_window(GraphicsOutput *window, - const GraphicsThreadingModel &threading_model); - void do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe, - const GraphicsThreadingModel &threading_model); + void do_add_window(GraphicsOutput *window); + void do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe); void do_remove_window(GraphicsOutput *window, Thread *current_thread); void do_resort_windows(); void terminate_threads(Thread *current_thread); @@ -301,6 +301,13 @@ private: ConditionVar _cv_start; ConditionVar _cv_done; ThreadState _thread_state; + + // These are stored for extract_texture_data and dispatch_compute. + GraphicsStateGuardian *_gsg; + Texture *_texture; + const RenderState *_state; + LVecBase3i _work_groups; + bool _result; }; WindowRenderer *get_window_renderer(const string &name, int pipeline_stage); @@ -308,8 +315,11 @@ private: Pipeline *_pipeline; Windows _windows; bool _windows_sorted; + + // This lock protects the next two fields. + Mutex _new_windows_lock; unsigned int _window_sort_index; - bool _needs_open_windows; + pvector _new_windows; WindowRenderer _app; typedef pmap Threads; 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/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 5b46251ba8..d590aa8d37 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" @@ -280,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++; } /** @@ -289,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); + } } /** @@ -742,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; @@ -750,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()) { @@ -768,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; } @@ -903,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: { @@ -1049,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); @@ -1066,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; } @@ -1086,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); @@ -1097,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; } @@ -1138,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 @@ -1155,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(); } @@ -1186,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(); @@ -1206,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: { @@ -1268,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; } @@ -1283,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; @@ -1297,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); @@ -1319,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(); @@ -1371,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); @@ -1403,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(); @@ -1430,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()) { @@ -1454,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); @@ -1470,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 { @@ -1493,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; @@ -1503,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 { @@ -1513,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(); @@ -1526,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; @@ -1536,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(); @@ -1549,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(); @@ -1565,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; @@ -1576,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(); @@ -1670,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, @@ -1684,8 +1769,8 @@ 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()); @@ -1773,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; @@ -2279,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 @@ -2353,7 +2436,6 @@ draw_points(const GeomPrimitivePipelineReader *, bool) { */ void GraphicsStateGuardian:: end_draw_primitives() { - _munger = NULL; _data_reader = NULL; } @@ -2635,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); @@ -2644,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) { @@ -2663,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++; } } } @@ -2690,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) { @@ -2707,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) { @@ -2966,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. */ @@ -3098,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(); } @@ -3115,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; } } @@ -3134,6 +3220,7 @@ async_reload_texture(TextureContext *tc) { _supports_compressed_texture); request->set_priority(priority); _loader->load_async(request); + return (AsyncFuture *)request.p(); } /** @@ -3143,52 +3230,31 @@ 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(); + bool is_point = node->is_of_type(PointLight::get_class_type()); + nassertr(node->is_of_type(DirectionalLight::get_class_type()) || + node->is_of_type(Spotlight::get_class_type()) || + is_point, nullptr); - 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); + } } + // The light's shadow map should have been created by set_shadow_caster(). + nassertr(light->_shadow_map != nullptr, nullptr); + // See if we already have a buffer. If not, create one. - if (light->_sbuffers.count(this) == 0) { - if (host == (GraphicsOutputBase *)NULL) { - host = _current_display_region->get_window(); - } - nassertr(host != NULL, NULL); - - // Nope, the light doesn't have a buffer for our GSG. Make one. - return make_shadow_buffer(light_np, host); - - } else { + if (light->_sbuffers.count(this) != 0) { // There's already a buffer - use that. - return light->_sbuffers[this]->get_texture(); + return light->_shadow_map; } -} - -/** - * Creates a depth buffer for shadow mapping. This is a convenience function - * for the ShaderGenerator; putting this directly in the ShaderGenerator would - * cause circular dependency issues. Returns the depth texture. - */ -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); - - PT(LightLensNode) light = DCAST(LightLensNode, light_np.node()); - if (light == NULL || !light->_shadow_caster) { - return NULL; - } - - bool is_point = light->is_of_type(PointLight::get_class_type()); - - nassertr(light->_sbuffers.count(this) == 0, NULL); if (display_cat.is_debug()) { display_cat.debug() @@ -3197,55 +3263,15 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) { << ", sort=" << light->_sb_sort << "\n"; } - // Determine the properties for creating the depth buffer. - FrameBufferProperties fbp; - fbp.set_depth_bits(shadow_depth_bits); - - WindowProperties props = WindowProperties::size(light->_sb_size[0], light->_sb_size[1]); - int flags = GraphicsPipe::BF_refuse_window; - if (is_point) { - flags |= GraphicsPipe::BF_size_square; + if (host == nullptr) { + nassertr(_current_display_region != nullptr, nullptr); + host = _current_display_region->get_window(); } + nassertr(host != nullptr, nullptr); - // Create the buffer - PT(GraphicsOutput) sbuffer = get_engine()->make_output(get_pipe(), light->get_name(), - light->_sb_sort, fbp, props, flags, this, DCAST(GraphicsOutput, host)); - nassertr(sbuffer != NULL, NULL); - - // Create a texture and fill it in with some data to workaround an OpenGL - // error - PT(Texture) tex = new Texture(light->get_name()); - if (is_point) { - if (light->_sb_size[0] != light->_sb_size[1]) { - display_cat.error() - << "PointLight shadow buffers must have an equal width and height!\n"; - } - tex->setup_cube_map(light->_sb_size[0], Texture::T_unsigned_byte, Texture::F_depth_component); - } else { - tex->setup_2d_texture(light->_sb_size[0], light->_sb_size[1], Texture::T_unsigned_byte, Texture::F_depth_component); - } - tex->make_ram_image(); - sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth); - - // Set the wrap mode - if (is_point) { - tex->set_wrap_u(SamplerState::WM_clamp); - tex->set_wrap_v(SamplerState::WM_clamp); - } else { - tex->set_wrap_u(SamplerState::WM_border_color); - tex->set_wrap_v(SamplerState::WM_border_color); - tex->set_border_color(LVecBase4(1, 1, 1, 1)); - } - - // Note: cube map shadow filtering doesn't seem to work in Cg. - if (get_supports_shadow_filter() && !is_point) { - // If we have the ARB_shadow extension, enable shadow filtering. - tex->set_minfilter(SamplerState::FT_shadow); - tex->set_magfilter(SamplerState::FT_shadow); - } else { - tex->set_minfilter(SamplerState::FT_linear); - tex->set_magfilter(SamplerState::FT_linear); - } + // Nope, the light doesn't have a buffer for our GSG. Make one. + GraphicsOutput *sbuffer = make_shadow_buffer(light, light->_shadow_map, + DCAST(GraphicsOutput, host)); // Assign display region(s) to the buffer and camera if (is_point) { @@ -3261,9 +3287,112 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) { dr->set_camera(light_np); dr->set_clear_depth_active(true); } - light->_sbuffers[this] = sbuffer; - return tex; + light->_sbuffers[this] = sbuffer; + return light->_shadow_map; +} + +/** + * 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. A derived GSG can override this + * if it knows that a particular buffer type works best for shadow rendering. + */ +GraphicsOutput *GraphicsStateGuardian:: +make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host) { + bool is_point = light->is_of_type(PointLight::get_class_type()); + + // Determine the properties for creating the depth buffer. + FrameBufferProperties fbp; + fbp.set_depth_bits(shadow_depth_bits); + + WindowProperties props = WindowProperties::size(light->_sb_size); + int flags = GraphicsPipe::BF_refuse_window; + if (is_point) { + flags |= GraphicsPipe::BF_size_square; + } + + // Create the buffer. This is a bit tricky because make_output() can only + // be called from the app thread, but it won't cause issues as long as the + // pipe can precertify the buffer, which it can in most cases. + GraphicsOutput *sbuffer = get_engine()->make_output(get_pipe(), + light->get_name(), light->_sb_sort, fbp, props, flags, this, host); + + if (sbuffer != nullptr) { + sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth); + } + return sbuffer; +} + +/** + * 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 } /** diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index a5dbce4a11..9b7855dc31 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -286,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: @@ -367,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, @@ -424,7 +423,10 @@ 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) make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host); + PT(Texture) get_dummy_shadow_map(Texture::TextureType texture_type) const; + virtual GraphicsOutput *make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host); + + virtual void ensure_generated_shader(const RenderState *state); #ifdef DO_PSTATS static void init_frame_pstats(); @@ -446,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(); @@ -457,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; @@ -495,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; diff --git a/panda/src/display/graphicsWindowProcCallbackData.I b/panda/src/display/graphicsWindowProcCallbackData.I index 55fc325ad9..eb12340a30 100644 --- a/panda/src/display/graphicsWindowProcCallbackData.I +++ b/panda/src/display/graphicsWindowProcCallbackData.I @@ -32,7 +32,7 @@ get_graphics_window() const { /** * Returns the Windows proc hwnd parameter. */ -INLINE int GraphicsWindowProcCallbackData:: +INLINE uintptr_t GraphicsWindowProcCallbackData:: get_hwnd() const { return _hwnd; } @@ -65,7 +65,7 @@ get_lparam() const { * Sets the Windows proc hwnd parameter. */ INLINE void GraphicsWindowProcCallbackData:: -set_hwnd(int hwnd) { +set_hwnd(uintptr_t hwnd) { _hwnd = hwnd; } diff --git a/panda/src/display/graphicsWindowProcCallbackData.h b/panda/src/display/graphicsWindowProcCallbackData.h index 9e3c747d50..d417a3595e 100644 --- a/panda/src/display/graphicsWindowProcCallbackData.h +++ b/panda/src/display/graphicsWindowProcCallbackData.h @@ -32,7 +32,7 @@ public: INLINE GraphicsWindow* get_graphics_window() const; #ifdef WIN32 - INLINE void set_hwnd(int hwnd); + INLINE void set_hwnd(uintptr_t hwnd); INLINE void set_msg(int msg); INLINE void set_wparam(int wparam); INLINE void set_lparam(int lparam); @@ -42,7 +42,7 @@ PUBLISHED: virtual void output(ostream &out) const; #ifdef WIN32 - INLINE int get_hwnd() const; + INLINE uintptr_t get_hwnd() const; INLINE int get_msg() const; INLINE int get_wparam() const; INLINE int get_lparam() const; @@ -55,7 +55,7 @@ PUBLISHED: private: GraphicsWindow* _graphicsWindow; #ifdef WIN32 - int _hwnd; + uintptr_t _hwnd; int _msg; int _wparam; int _lparam; diff --git a/panda/src/display/pythonGraphicsWindowProc.cxx b/panda/src/display/pythonGraphicsWindowProc.cxx index d114d9ba3d..fe2013bed1 100644 --- a/panda/src/display/pythonGraphicsWindowProc.cxx +++ b/panda/src/display/pythonGraphicsWindowProc.cxx @@ -47,7 +47,7 @@ PythonGraphicsWindowProc:: LONG PythonGraphicsWindowProc:: wnd_proc(GraphicsWindow* graphicsWindow, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam){ GraphicsWindowProcCallbackData cdata(graphicsWindow); - cdata.set_hwnd((int)hwnd); + cdata.set_hwnd((uintptr_t)hwnd); cdata.set_msg(msg); cdata.set_wparam(wparam); cdata.set_lparam(lparam); 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.cxx b/panda/src/display/windowProperties.cxx index c0af101b41..298bed4b45 100644 --- a/panda/src/display/windowProperties.cxx +++ b/panda/src/display/windowProperties.cxx @@ -133,6 +133,12 @@ clear_default() { * size is the only property that matters to buffers. */ WindowProperties WindowProperties:: +size(const LVecBase2i &size) { + WindowProperties props; + props.set_size(size); + return props; +} +WindowProperties WindowProperties:: size(int x_size, int y_size) { WindowProperties props; props.set_size(x_size, y_size); diff --git a/panda/src/display/windowProperties.h b/panda/src/display/windowProperties.h index efb58255b6..16a65c0096 100644 --- a/panda/src/display/windowProperties.h +++ b/panda/src/display/windowProperties.h @@ -49,7 +49,10 @@ 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(const LVecBase2i &size); static WindowProperties size(int x_size, int y_size); bool operator == (const WindowProperties &other) const; 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/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/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.I b/panda/src/dxgsg9/dxGraphicsStateGuardian9.I index 7b5133ccf4..94e50a5f7d 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.I +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.I @@ -127,7 +127,7 @@ get_safe_buffer_start() { // buffer, and then pointing to the first multiple of 0x10000 within that // buffer. _temp_buffer = new unsigned char[0x1ffff]; - _safe_buffer_start = (unsigned char *)(((long)_temp_buffer + 0xffff) & ~0xffff); + _safe_buffer_start = (unsigned char *)(((uintptr_t)_temp_buffer + 0xffff) & ~0xffff); } return _safe_buffer_start; diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index 4b5efc2b8c..8326068dc1 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; @@ -4938,7 +4936,7 @@ draw_primitive_up(D3DPRIMITIVETYPE primitive_type, _d3d_device->DrawPrimitiveUP(primitive_type, primitive_count, buffer_start, stride); - } else if ((((long)buffer_end ^ (long)buffer_start) & ~0xffff) == 0) { + } else if ((((uintptr_t)buffer_end ^ (uintptr_t)buffer_start) & ~0xffff) == 0) { // No problem; we can draw the buffer directly. _d3d_device->DrawPrimitiveUP(primitive_type, primitive_count, buffer_start, stride); @@ -4980,7 +4978,7 @@ draw_indexed_primitive_up(D3DPRIMITIVETYPE primitive_type, (primitive_type, min_index, max_index - min_index + 1, num_primitives, index_data, index_type, buffer, stride); - } else if ((((long)buffer_end ^ (long)buffer_start) & ~0xffff) == 0) { + } else if ((((uintptr_t)buffer_end ^ (uintptr_t)buffer_start) & ~0xffff) == 0) { // No problem; we can draw the buffer directly. _d3d_device->DrawIndexedPrimitiveUP (primitive_type, min_index, max_index - min_index + 1, num_primitives, 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 fada562e55..a29dff17b3 100644 --- a/panda/src/dxgsg9/dxIndexBufferContext9.cxx +++ b/panda/src/dxgsg9/dxIndexBufferContext9.cxx @@ -168,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/dxgsg9/dxTextureContext9.cxx b/panda/src/dxgsg9/dxTextureContext9.cxx index 39b92342aa..c089c26e8a 100644 --- a/panda/src/dxgsg9/dxTextureContext9.cxx +++ b/panda/src/dxgsg9/dxTextureContext9.cxx @@ -365,6 +365,12 @@ create_texture(DXScreenData &scrn) { shrink_original = true; } + if (target_width == 0 || target_height == 0) { + // We can't create a zero-sized texture, so change it to 1x1. + target_width = 1; + target_height = 1; + } + const char *error_message; error_message = "create_texture failed: couldn't find compatible device Texture Pixel Format for input texture"; @@ -680,7 +686,7 @@ create_texture(DXScreenData &scrn) { << "NumColorChannels: " << num_color_channels << "; NumAlphaBits: " << num_alpha_bits << "; targetbpp: " <get_ram_mipmap_image(mip_level); - nassertr(!image.is_null(), E_FAIL); BYTE *pixels = (BYTE*) image.p(); DWORD width = (DWORD) get_texture()->get_expected_mipmap_x_size(mip_level); DWORD height = (DWORD) get_texture()->get_expected_mipmap_y_size(mip_level); int component_width = get_texture()->get_component_width(); - size_t view_size = get_texture()->get_ram_mipmap_view_size(mip_level); - pixels += view_size * get_view(); size_t page_size = get_texture()->get_expected_ram_mipmap_page_size(mip_level); - pixels += page_size * depth_index; + size_t view_size; + vector_uchar clear_data; + if (page_size > 0) { + if (image.is_null()) { + // Make an image, filled with the texture's clear color. + image = get_texture()->make_ram_mipmap_image(mip_level); + nassertr(!image.is_null(), E_FAIL); + pixels = (BYTE *)image.p(); + } + view_size = image.size(); + pixels += view_size * get_view(); + pixels += page_size * depth_index; + } else { + // This is a 0x0 texture, which gets loaded as though it were 1x1. + width = 1; + height = 1; + clear_data = get_texture()->get_clear_data(); + pixels = clear_data.data(); + view_size = clear_data.size(); + } if (get_texture()->get_texture_type() == Texture::TT_cube_map) { nassertr(IS_VALID_PTR(_d3d_cube_texture), E_FAIL); @@ -1909,7 +1931,9 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) { DWORD flags; D3DCOLOR color; - color = 0xFF000000; + LColor scaled = tex->get_clear_color().fmin(LColor(1)).fmax(LColor::zero()); + scaled *= 255; + color = D3DCOLOR_RGBA((int)scaled[0], (int)scaled[1], (int)scaled[2], (int)scaled[3]); flags = D3DCLEAR_TARGET; if (device -> Clear (NULL, NULL, flags, color, 0.0f, 0) == D3D_OK) { } @@ -1933,9 +1957,8 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) { return S_OK; } - return E_FAIL; } - nassertr(IS_VALID_PTR((BYTE*)image.p()), E_FAIL); + //nassertr(IS_VALID_PTR((BYTE*)image.p()), E_FAIL); nassertr(IS_VALID_PTR(_d3d_texture), E_FAIL); PStatTimer timer(GraphicsStateGuardian::_load_texture_pcollector); diff --git a/panda/src/dxgsg9/wdxGraphicsWindow9.cxx b/panda/src/dxgsg9/wdxGraphicsWindow9.cxx index 4dd8285f69..ee19e9137a 100644 --- a/panda/src/dxgsg9/wdxGraphicsWindow9.cxx +++ b/panda/src/dxgsg9/wdxGraphicsWindow9.cxx @@ -880,10 +880,10 @@ choose_device() { << ", Driver: " << adapter_info.Driver << ", DriverVersion: (" << HIWORD(DrvVer->HighPart) << "." << LOWORD(DrvVer->HighPart) << "." << HIWORD(DrvVer->LowPart) << "." << LOWORD(DrvVer->LowPart) - << ")\nVendorID: 0x" << (void*) adapter_info.VendorId - << " DeviceID: 0x" << (void*) adapter_info.DeviceId - << " SubsysID: 0x" << (void*) adapter_info.SubSysId - << " Revision: 0x" << (void*) adapter_info.Revision << endl; + << ")\nVendorID: 0x" << hex << adapter_info.VendorId + << " DeviceID: 0x" << adapter_info.DeviceId + << " SubsysID: 0x" << adapter_info.SubSysId + << " Revision: 0x" << adapter_info.Revision << dec << endl; HMONITOR _monitor = dxpipe->__d3d9->GetAdapterMonitor(i); if (_monitor == NULL) { 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 133bddc874..63b114ed54 100644 --- a/panda/src/egg2pg/eggSaver.cxx +++ b/panda/src/egg2pg/eggSaver.cxx @@ -52,6 +52,7 @@ #include "modelNode.h" #include "animBundleNode.h" #include "animChannelMatrixXfmTable.h" +#include "characterJointEffect.h" #include "characterJoint.h" #include "character.h" #include "string_utils.h" @@ -155,6 +156,16 @@ convert_node(const WorkingNodePath &node_path, EggGroupNode *egg_parent, convert_character_node(DCAST(Character, node), node_path, egg_parent, has_decal); } else { + // Is this a ModelNode that represents an exposed joint? If so, skip it, + // as we'll take care of it when building the joint hierarchy. + if (node->get_type() == ModelNode::get_class_type()) { + ModelNode *model_node = (ModelNode *)node; + if (model_node->get_preserve_transform() == ModelNode::PT_net && + model_node->has_effect(CharacterJointEffect::get_class_type())) { + return; + } + } + // Just a generic node. EggGroup *egg_group = new EggGroup(node->get_name()); egg_parent->add_child(egg_group); @@ -354,6 +365,17 @@ convert_character_bundle(PartGroup *bundleNode, EggGroupNode *egg_parent, Charac EggGroup *joint = new EggGroup(bundleNode->get_name()); joint->add_matrix4(transformd); joint->set_group_type(EggGroup::GT_joint); + + // Is this joint exposed? + NodePathCollection coll = character_joint->get_net_transforms(); + for (size_t i = 0; i < coll.size(); ++i) { + const NodePath &np = coll[i]; + if (np.get_name() == bundleNode->get_name() && np.node()->is_of_type(ModelNode::get_class_type())) { + joint->set_dcs_type(EggGroup::DC_net); + break; + } + } + joint_group = joint; egg_parent->add_child(joint_group); if (joint_map != NULL) { @@ -384,16 +406,33 @@ convert_character_node(Character *node, const WorkingNodePath &node_path, // A sequence node gets converted to an ordinary EggGroup, we only apply the // appropriate switch attributes to turn it into a sequence. - // We have to use DT_structured since it is the only mode that preserves the - // node hierarchy, including LODNodes and CollisionNodes that may be under - // this Character node. EggGroup *egg_group = new EggGroup(node->get_name()); - egg_group->set_dart_type(EggGroup::DT_structured); egg_parent->add_child(egg_group); apply_node_properties(egg_group, node); CharacterJointMap joint_map; - recurse_nodes(node_path, egg_group, has_decal, &joint_map); + bool is_structured = false; + + int num_children = node->get_num_children(); + for (int i = 0; i < num_children; i++) { + PandaNode *child = node->get_child(i); + convert_node(WorkingNodePath(node_path, child), egg_parent, has_decal, &joint_map); + + TypeHandle type = child->get_type(); + if (child->get_num_children() > 0 || + (type != GeomNode::get_class_type() && type != ModelNode::get_class_type())) { + is_structured = true; + } + } + + // We have to use DT_structured if it is necessary to preserve any node + // hierarchy, such as LODNodes and CollisionNodes that may be under this + // Character node. + if (is_structured) { + egg_group->set_dart_type(EggGroup::DT_structured); + } else { + egg_group->set_dart_type(EggGroup::DT_default); + } // turn it into a switch.. egg_group->set_switch_flag(true); @@ -940,6 +979,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..a11fb6b36a --- /dev/null +++ b/panda/src/event/asyncFuture.I @@ -0,0 +1,205 @@ +/** + * 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), + _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_finished; +} + +/** + * 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) { + nassertv(!done()); + _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); +} + +INLINE void AsyncFuture:: +set_result(const EventParameter &result) { + set_result(result.get_ptr(), result.get_ptr()); +} + +/** + * Creates a new future that returns `done()` when all of the contained + * futures are done. + * + * Calling `cancel()` on the returned future will result in all contained + * futures that have not yet finished to be cancelled. + */ +INLINE AsyncFuture *AsyncFuture:: +gather(Futures futures) { + if (futures.empty()) { + AsyncFuture *fut = new AsyncFuture; + fut->_future_state = (AtomicAdjust::Integer)FS_finished; + return fut; + } else if (futures.size() == 1) { + return futures[0].p(); + } else { + return (AsyncFuture *)new AsyncGatheringFuture(move(futures)); + } +} + +/** + * Tries to atomically lock the future, assuming it is pending. Returns false + * if it is not in the pending state, implying it's either done or about to be + * cancelled. + */ +INLINE bool AsyncFuture:: +try_lock_pending() { + return set_future_state(FS_locked_pending); +} + +/** + * Should be called after try_lock_pending() returns true. + */ +INLINE void AsyncFuture:: +unlock(FutureState new_state) { + nassertv(new_state != FS_locked_pending); + FutureState orig_state = (FutureState)AtomicAdjust::set(_future_state, (AtomicAdjust::Integer)new_state); + nassertv(orig_state == FS_locked_pending); +} + +/** + * Atomically changes the future state from pending to another state. Returns + * true if successful, false if the future was already done. + * Note that once a future is in a "done" state (ie. cancelled or finished) it + * can never change state again. + */ +INLINE bool AsyncFuture:: +set_future_state(FutureState state) { + FutureState orig_state = (FutureState) + AtomicAdjust::compare_and_exchange( + _future_state, + (AtomicAdjust::Integer)FS_pending, + (AtomicAdjust::Integer)state); + + while (orig_state == FS_locked_pending) { + Thread::force_yield(); + orig_state = (FutureState)AtomicAdjust::compare_and_exchange( + _future_state, + (AtomicAdjust::Integer)FS_pending, + (AtomicAdjust::Integer)state); + } + + return orig_state == FS_pending; +} + +/** + * Returns the number of futures that were passed to the constructor. + */ +INLINE size_t AsyncGatheringFuture:: +get_num_futures() const { + return _futures.size(); +} + +/** + * Returns the nth future that was passed into the constructor. + */ +INLINE AsyncFuture *AsyncGatheringFuture:: +get_future(size_t i) const { + nassertr(i < _futures.size(), nullptr); + return _futures[i].p(); +} + +/** + * Returns the result of the nth future that was passed into the constructor. + */ +INLINE TypedObject *AsyncGatheringFuture:: +get_result(size_t i) const { + nassertr(i < _futures.size(), nullptr); + return _futures[i]->get_result(); +} diff --git a/panda/src/event/asyncFuture.cxx b/panda/src/event/asyncFuture.cxx new file mode 100644 index 0000000000..f1aaaba842 --- /dev/null +++ b/panda/src/event/asyncFuture.cxx @@ -0,0 +1,376 @@ +/** + * 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; +TypeHandle AsyncGatheringFuture::_type_handle; + +/** + * Destroys the future. Assumes notify_done() has already been called. + */ +AsyncFuture:: +~AsyncFuture() { + // If this triggers, the future destroyed before it was cancelled, which is + // not valid. Unless we should simply call cancel() here? + nassertv(_waiting.empty()); +} + +/** + * Cancels the future. Returns true if it was cancelled, or false if the + * future was already done. Either way, done() will return true after this + * call returns. + * + * In the case of a task, this is equivalent to remove(). + */ +bool AsyncFuture:: +cancel() { + if (set_future_state(FS_cancelled)) { + // The compare-swap operation succeeded, so schedule the callbacks. + notify_done(false); + return true; + } else { + // It's already done. + return false; + } +} + +/** + * + */ +void AsyncFuture:: +output(ostream &out) const { + out << get_type(); + FutureState state = (FutureState)AtomicAdjust::get(_future_state); + switch (state) { + case FS_pending: + case FS_locked_pending: + out << " (pending)"; + break; + case FS_finished: + out << " (finished)"; + break; + case FS_cancelled: + out << " (cancelled)"; + break; + default: + out << " (**INVALID**)"; + break; + } +} + +/** + * Waits until the future is done. + */ +void AsyncFuture:: +wait() { + if (done()) { + return; + } + + PStatTimer timer(AsyncTaskChain::_wait_pcollector); + if (task_cat.is_debug()) { + task_cat.debug() + << "Waiting for future " << *this << "\n"; + } + + // Continue to yield while the future isn't done. It may be more efficient + // to use a condition variable, but let's not add the extra complexity + // unless we're sure that we need it. + do { + Thread::force_yield(); + } while (!done()); +} + +/** + * Waits until the future is done, or until the timeout is reached. + */ +void AsyncFuture:: +wait(double timeout) { + if (done()) { + return; + } + + PStatTimer timer(AsyncTaskChain::_wait_pcollector); + if (task_cat.is_debug()) { + task_cat.debug() + << "Waiting up to " << timeout << " seconds for future " << *this << "\n"; + } + + // Continue to yield while the future isn't done. It may be more efficient + // to use a condition variable, but let's not add the extra complexity + // unless we're sure that we need it. + ClockObject *clock = ClockObject::get_global_clock(); + double end = clock->get_real_time() + timeout; + do { + Thread::force_yield(); + } while (!done() && clock->get_real_time() < end); +} + +/** + * Schedules the done callbacks. Called after the future has just entered the + * 'done' state. + * @param clean_exit true if finished successfully, false if cancelled. + */ +void AsyncFuture:: +notify_done(bool clean_exit) { + nassertv(done()); + + // This will only be called by the thread that managed to set the + // _future_state away from the "pending" state, so this is thread safe. + + Futures::iterator it; + for (it = _waiting.begin(); it != _waiting.end(); ++it) { + AsyncFuture *fut = *it; + if (fut->is_task()) { + // It's a task. Make it active again. + wake_task((AsyncTask *)fut); + } else { + // It's a gathering future. Decrease the pending count on it, and if + // we're the last one, call notify_done() on it. + AsyncGatheringFuture *gather = (AsyncGatheringFuture *)fut; + if (!AtomicAdjust::dec(gather->_num_pending)) { + if (gather->set_future_state(FS_finished)) { + gather->notify_done(true); + } + } + } + } + _waiting.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)); + } +} + +/** + * 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) { + // We don't strictly need to lock the future since only one thread is + // allowed to call set_result(), but we might as well. + FutureState orig_state = (FutureState)AtomicAdjust:: + compare_and_exchange(_future_state, (AtomicAdjust::Integer)FS_pending, + (AtomicAdjust::Integer)FS_locked_pending); + + while (orig_state == FS_locked_pending) { + Thread::force_yield(); + orig_state = (FutureState)AtomicAdjust:: + compare_and_exchange(_future_state, (AtomicAdjust::Integer)FS_pending, + (AtomicAdjust::Integer)FS_locked_pending); + } + + if (orig_state == FS_pending) { + _result = ptr; + _result_ref = ref_ptr; + unlock(FS_finished); + + // OK, now our thread owns the _waiting vector et al. + notify_done(true); + + } else if (orig_state == FS_cancelled) { + // This was originally illegal, but there is a chance that the future was + // cancelled while another thread was setting the result. So, we drop + // this, but we can issue a warning. + task_cat.warning() + << "Ignoring set_result() called on cancelled " << *this << "\n"; + + } else { + task_cat.error() + << "set_result() was called on finished " << *this << "\n"; + } +} + +/** + * Indicates that the given task is waiting for this future to complete. When + * the future is done, it will reactivate the given task. If this is called + * while the future is already done, schedules the task immediately. + * Assumes the manager's lock is not held. + * @returns true if the future was pending, false if it was already done. + */ +bool AsyncFuture:: +add_waiting_task(AsyncTask *task) { + nassertr(task->is_runnable(), false); + + // We have to make sure we're not going to change state while we're in the + // process of adding the task. + if (try_lock_pending()) { + if (_manager == nullptr) { + _manager = task->_manager; + } + + _waiting.push_back(task); + + // Unlock the state. + unlock(); + nassertr(task->_manager == nullptr || task->_manager == _manager, true); + return true; + } else { + // It's already done. Wake the task immediately. + wake_task(task); + return false; + } +} + +/** + * Reactivates the given task. Assumes the manager lock is not held. + */ +void AsyncFuture:: +wake_task(AsyncTask *task) { + cerr << "waking task\n"; + AsyncTaskManager *manager = task->_manager; + if (manager == nullptr) { + // If it's an unscheduled task, schedule it on the same manager as the + // rest of the waiting tasks. + manager = _manager; + if (manager == nullptr) { + manager = AsyncTaskManager::get_global_ptr(); + } + } + + MutexHolder holder(manager->_lock); + switch (task->_state) { + case AsyncTask::S_servicing_removed: + nassertv(task->_manager == _manager); + // Re-adding a self-removed task; this just means clearing the removed + // flag. + task->_state = AsyncTask::S_servicing; + return; + + case AsyncTask::S_inactive: + // Schedule it immediately. + nassertv(task->_manager == nullptr); + + if (task_cat.is_debug()) { + task_cat.debug() + << "Adding " << *task << " (woken by future " << *this << ")\n"; + } + + { + manager->_lock.release(); + task->upon_birth(manager); + manager->_lock.acquire(); + nassertv(task->_manager == nullptr && + task->_state == AsyncTask::S_inactive); + + AsyncTaskChain *chain = manager->do_find_task_chain(task->_chain_name); + if (chain == nullptr) { + task_cat.warning() + << "Creating implicit AsyncTaskChain " << task->_chain_name + << " for " << manager->get_type() << " " << manager->get_name() << "\n"; + chain = manager->do_make_task_chain(task->_chain_name); + } + chain->do_add(task); + } + return; + + case AsyncTask::S_awaiting: + nassertv(task->_manager == _manager); + task->_state = AsyncTask::S_active; + task->_chain->_active.push_back(task); + --task->_chain->_num_awaiting_tasks; + return; + + default: + nassertv(false); + return; + } +} + +/** + * @see AsyncFuture::gather + */ +AsyncGatheringFuture:: +AsyncGatheringFuture(AsyncFuture::Futures futures) : + _futures(move(futures)), + _num_pending(0) { + + bool any_pending = false; + + AsyncFuture::Futures::const_iterator it; + for (it = _futures.begin(); it != _futures.end(); ++it) { + AsyncFuture *fut = *it; + // If this returns true, the future is not yet done and we need to + // register ourselves with it. This creates a circular reference, but it + // is resolved when the future is completed or cancelled. + if (fut->try_lock_pending()) { + if (_manager == nullptr) { + _manager = fut->_manager; + } + fut->_waiting.push_back((AsyncFuture *)this); + AtomicAdjust::inc(_num_pending); + fut->unlock(); + any_pending = true; + } + } + if (!any_pending) { + // Start in the done state if all the futures we were passed are done. + // Note that it is only safe to set this member in this manner if indeed + // no other future holds a reference to us. + _future_state = (AtomicAdjust::Integer)FS_finished; + } +} + +/** + * Cancels all the futures. Returns true if any futures were cancelled. + * Makes sure that all the futures finish before this one is marked done, in + * order to maintain the guarantee that calling result() is safe when done() + * returns true. + */ +bool AsyncGatheringFuture:: +cancel() { + if (!done()) { + // Temporarily increase the pending count so that the notify_done() + // callbacks won't end up causing it to be set to "finished". + AtomicAdjust::inc(_num_pending); + + bool any_cancelled = false; + AsyncFuture::Futures::const_iterator it; + for (it = _futures.begin(); it != _futures.end(); ++it) { + AsyncFuture *fut = *it; + if (fut->cancel()) { + any_cancelled = true; + } + } + + // Now change state to "cancelled" and call the notify_done() callbacks. + // Don't call notify_done() if another thread has beaten us to it. + if (set_future_state(FS_cancelled)) { + notify_done(false); + } + + // Decreasing the pending count is kind of pointless now, so we do it only + // in a debug build. + nassertr(!AtomicAdjust::dec(_num_pending), any_cancelled); + return any_cancelled; + } else { + return false; + } +} diff --git a/panda/src/event/asyncFuture.h b/panda/src/event/asyncFuture.h new file mode 100644 index 0000000000..cbed94b158 --- /dev/null +++ b/panda/src/event/asyncFuture.h @@ -0,0 +1,200 @@ +/** + * 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 "eventParameter.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 tasks waiting for this future and automatically reactivates them + * upon this future's completion. + * + * A task itself is also a subclass of AsyncFuture. Other subclasses are + * not generally necessary, except to override the function of `cancel()`. + * + * Until the future is done, it is "owned" by the resolver thread, though it's + * still legal for other threads to query its state. When the resolver thread + * resolves this future using `set_result()`, or any thread calls `cancel()`, + * it instantly enters the "done" state, after which the result becomes a + * read-only field that all threads can access. + * + * When the future returns true for done(), a thread can use cancelled() to + * determine whether the future was cancelled or get_result() to access the + * result of the operation. Not all operations define a meaningful result + * value, so some will always return nullptr. + * + * In Python, the `cancelled()`, `wait()` and `get_result()` methods are + * wrapped up into a single `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, which only suspends the current task and not the entire thread. + * + * 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(PyObject *timeout = Py_None) 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); + + EXTENSION(PyObject *add_done_callback(PyObject *self, PyObject *fn)); + + EXTENSION(static PyObject *gather(PyObject *args)); + + virtual void output(ostream &out) const; + + BLOCKING void wait(); + BLOCKING 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); + INLINE void set_result(const EventParameter &result); + +public: + void set_result(TypedObject *ptr, ReferenceCount *ref_ptr); + + INLINE TypedObject *get_result() const; + INLINE void get_result(TypedObject *&ptr, ReferenceCount *&ref_ptr) const; + + typedef pvector Futures; + INLINE static AsyncFuture *gather(Futures futures); + + virtual bool is_task() const {return false;} + + void notify_done(bool clean_exit); + bool add_waiting_task(AsyncTask *task); + +private: + void wake_task(AsyncTask *task); + +protected: + enum FutureState { + // Pending states + FS_pending, + FS_locked_pending, + + // Done states + FS_finished, + FS_cancelled, + }; + INLINE bool try_lock_pending(); + INLINE void unlock(FutureState new_state = FS_pending); + INLINE bool set_future_state(FutureState state); + + AsyncTaskManager *_manager; + TypedObject *_result; + PT(ReferenceCount) _result_ref; + AtomicAdjust::Integer _future_state; + + string _done_event; + + // Tasks and gathering futures waiting for this one to complete. + Futures _waiting; + + friend class AsyncGatheringFuture; + friend class AsyncTaskChain; + 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; +}; + +/** + * Specific future that collects the results of several futures. + */ +class EXPCL_PANDA_EVENT AsyncGatheringFuture FINAL : public AsyncFuture { +private: + AsyncGatheringFuture(AsyncFuture::Futures futures); + +public: + virtual bool cancel() override; + + INLINE size_t get_num_futures() const; + INLINE AsyncFuture *get_future(size_t i) const; + INLINE TypedObject *get_result(size_t i) const; + +private: + const Futures _futures; + AtomicAdjust::Integer _num_pending; + + friend class AsyncFuture; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + AsyncFuture::init_type(); + register_type(_type_handle, "AsyncGatheringFuture", + AsyncFuture::get_class_type()); + } + virtual TypeHandle get_type() const override { + return get_class_type(); + } + virtual TypeHandle force_init_type() override {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#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..3150c09d7d --- /dev/null +++ b/panda/src/event/asyncFuture_ext.cxx @@ -0,0 +1,318 @@ +/** + * 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 "asyncTaskSequence.h" +#include "eventParameter.h" +#include "paramValue.h" +#include "pythonTask.h" + +#ifdef HAVE_PYTHON + +#ifndef CPPPARSER +extern struct Dtool_PyTypedObject Dtool_AsyncFuture; +extern struct Dtool_PyTypedObject Dtool_ParamValueBase; +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 if (future->is_of_type(AsyncTaskSequence::get_class_type())) { + // If it's an AsyncTaskSequence, get the result for each task. + const AsyncTaskSequence *task = (const AsyncTaskSequence *)future; + Py_ssize_t num_tasks = (Py_ssize_t)task->get_num_tasks(); + PyObject *results = PyTuple_New(num_tasks); + + for (Py_ssize_t i = 0; i < num_tasks; ++i) { + PyObject *result = get_done_result(task->get_task(i)); + if (result != nullptr) { + // This steals a reference. + PyTuple_SET_ITEM(results, i, result); + } else { + Py_DECREF(results); + return nullptr; + } + } + return results; + + } else if (future->is_of_type(AsyncGatheringFuture::get_class_type())) { + // If it's an AsyncGatheringFuture, get the result for each future. + const AsyncGatheringFuture *gather = (const AsyncGatheringFuture *)future; + Py_ssize_t num_futures = (Py_ssize_t)gather->get_num_futures(); + PyObject *results = PyTuple_New(num_futures); + + for (Py_ssize_t i = 0; i < num_futures; ++i) { + PyObject *result = get_done_result(gather->get_future((size_t)i)); + if (result != nullptr) { + // This steals a reference. + PyTuple_SET_ITEM(results, i, result); + } else { + Py_DECREF(results); + return nullptr; + } + } + return results; + + } else { + // It's any other future. + ReferenceCount *ref_ptr; + TypedObject *ptr; + future->get_result(ptr, ref_ptr); + + if (ptr == nullptr) { + Py_INCREF(Py_None); + return Py_None; + } + + TypeHandle type = ptr->get_type(); + if (type.is_derived_from(ParamValueBase::get_class_type())) { + // If this is a ParamValueBase, return the 'value' property. + // EventStoreInt and Double are not exposed to Python for some reason. + if (type == EventStoreInt::get_class_type()) { + return Dtool_WrapValue(((EventStoreInt *)ptr)->get_value()); + } else if (type == EventStoreDouble::get_class_type()) { + return Dtool_WrapValue(((EventStoreDouble *)ptr)->get_value()); + } + + ParamValueBase *value = (ParamValueBase *)ptr; + PyObject *wrap = DTool_CreatePyInstanceTyped + ((void *)value, Dtool_ParamValueBase, false, false, type.get_index()); + if (wrap != nullptr) { + PyObject *value = PyObject_GetAttrString(wrap, "value"); + if (value != nullptr) { + return value; + } + PyErr_Restore(nullptr, nullptr, nullptr); + Py_DECREF(wrap); + } + } + + if (ref_ptr != nullptr) { + ref_ptr->ref(); + } + + return DTool_CreatePyInstanceTyped + ((void *)ptr, Dtool_TypedObject, (ref_ptr != nullptr), false, + type.get_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((char*)"concurrent.futures._base.CancelledError", + (char*)"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(PyObject *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 + if (timeout == Py_None) { + _this->wait(); + } else { + PyObject *num = PyNumber_Float(timeout); + if (num != nullptr) { + _this->wait(PyFloat_AS_DOUBLE(num)); + } else { + return Dtool_Raise_ArgTypeError(timeout, 0, "result", "float"); + } + } +#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((char*)"concurrent.futures._base.TimeoutError", + (char*)"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); +} + +/** + * Schedules the given function to be run as soon as the future is complete. + * This is also called if the future is cancelled. + * If the future is already done, the callback is scheduled right away. + */ +PyObject *Extension:: +add_done_callback(PyObject *self, PyObject *fn) { + if (!PyCallable_Check(fn)) { + return Dtool_Raise_ArgTypeError(fn, 0, "add_done_callback", "callable"); + } + + PythonTask *task = new PythonTask(fn); + Py_DECREF(task->_args); + task->_args = PyTuple_Pack(1, self); + task->_append_task = false; + task->_ignore_return = true; + + // If this is an AsyncTask, make sure it is scheduled on the same chain. + if (_this->is_task()) { + AsyncTask *this_task = (AsyncTask *)_this; + task->set_task_chain(this_task->get_task_chain()); + } + + _this->add_waiting_task(task); + + Py_INCREF(Py_None); + return Py_None; +} + +/** + * Creates a new future that returns `done()` when all of the contained + * futures are done. + * + * Calling `cancel()` on the returned future will result in all contained + * futures that have not yet finished to be cancelled. + */ +PyObject *Extension:: +gather(PyObject *args) { + if (!PyTuple_Check(args)) { + return Dtool_Raise_TypeError("args is not a tuple"); + } + + Py_ssize_t size = Py_SIZE(args); + AsyncFuture::Futures futures; + futures.reserve(size); + + for (Py_ssize_t i = 0; i < size; ++i) { + PyObject *item = PyTuple_GET_ITEM(args, i); + if (DtoolInstance_Check(item)) { + AsyncFuture *fut = (AsyncFuture *)DtoolInstance_UPCAST(item, Dtool_AsyncFuture); + if (fut != nullptr) { + futures.push_back(fut); + continue; + } +#if PY_VERSION_HEX >= 0x03050000 + } else if (PyCoro_CheckExact(item)) { + // We allow passing in a coroutine instead of a future. This causes it + // to be scheduled as a task. + futures.push_back(new PythonTask(item)); + continue; +#endif + } + return Dtool_Raise_ArgTypeError(item, i, "gather", "coroutine, task or future"); + } + + AsyncFuture *future = AsyncFuture::gather(move(futures)); + if (future != nullptr) { + future->ref(); + return DTool_CreatePyInstanceTyped((void *)future, Dtool_AsyncFuture, true, false, future->get_type_index()); + } else { + return PyErr_NoMemory(); + } +} + +#endif diff --git a/panda/src/event/asyncFuture_ext.h b/panda/src/event/asyncFuture_ext.h new file mode 100644 index 0000000000..21786ee17b --- /dev/null +++ b/panda/src/event/asyncFuture_ext.h @@ -0,0 +1,41 @@ +/** + * 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(PyObject *timeout = Py_None) const; + + PyObject *add_done_callback(PyObject *self, PyObject *fn); + + static PyObject *gather(PyObject *args); +}; + +#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..9f70f0e5ad 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,25 @@ 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 + (current_thread->_current_task, nullptr, (TypedReferenceCount *)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 +432,35 @@ 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 + (current_thread->_current_task, (TypedReferenceCount *)this, 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 +530,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..d514b2f5a3 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,9 @@ protected: void jump_to_task_chain(AsyncTaskManager *manager); DoneStatus unlock_and_do_task(); + virtual bool cancel() FINAL; + virtual bool is_task() const FINAL {return true;} + virtual bool is_runnable(); virtual DoneStatus do_task(); virtual void upon_birth(AsyncTaskManager *manager); @@ -115,11 +119,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 +137,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 +156,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..47299d31ef 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,19 @@ 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->set_future_state(clean_exit ? AsyncFuture::FS_finished + : AsyncFuture::FS_cancelled)) { + task->notify_done(clean_exit); + } + + task->_manager = nullptr; + if (upon_death) { _manager->_lock.release(); task->upon_death(_manager, clean_exit); @@ -899,7 +909,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..85727d2d7c 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,8 @@ NotifyCategoryDef(event, ""); NotifyCategoryDef(task, ""); ConfigureFn(config_event) { + AsyncFuture::init_type(); + AsyncGatheringFuture::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..996587885e 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -19,28 +19,53 @@ #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), + _ignore_return(false), + _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 +85,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 +123,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 @@ -139,9 +170,7 @@ get_args() { } this->ref(); - PyObject *self = - DTool_CreatePyInstanceTyped(this, Dtool_TypedReferenceCount, - true, false, get_type_index()); + PyObject *self = DTool_CreatePyInstance(this, Dtool_PythonTask, true, false); PyTuple_SET_ITEM(with_task, num_args, self); return with_task; @@ -166,15 +195,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 +232,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 +448,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 +489,167 @@ 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. + if (fut != (AsyncFuture *)this && !fut->done()) { + if (fut->is_task()) { + // This is actually a task, do we need to schedule it with the + // manager? This allows doing something like + // await Task.pause(1.0) + // directly instead of having to do: + // await taskMgr.add(Task.pause(1.0)) + AsyncTask *task = (AsyncTask *)fut; + _manager->add(task); + } + if (fut->add_waiting_task(this)) { + if (task_cat.is_debug()) { + task_cat.debug() + << *this << " is now awaiting <" << *fut << ">.\n"; + } + } 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. @@ -461,7 +664,7 @@ do_python_task() { return DS_interrupt; } - if (result == Py_None) { + if (result == Py_None || _ignore_return) { Py_DECREF(result); return DS_done; } @@ -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); @@ -640,15 +861,10 @@ void PythonTask:: call_function(PyObject *function) { if (function != Py_None) { this->ref(); - PyObject *self = - DTool_CreatePyInstanceTyped(this, Dtool_TypedReferenceCount, - true, false, get_type_index()); - PyObject *args = Py_BuildValue("(O)", self); - Py_DECREF(self); - - PyObject *result = PyObject_CallObject(function, args); + PyObject *self = DTool_CreatePyInstance(this, Dtool_PythonTask, true, false); + PyObject *result = PyObject_CallFunctionObjArgs(function, self, nullptr); Py_XDECREF(result); - Py_DECREF(args); + Py_DECREF(self); } } diff --git a/panda/src/event/pythonTask.h b/panda/src/event/pythonTask.h index d97be5795e..48826769de 100644 --- a/panda/src/event/pythonTask.h +++ b/panda/src/event/pythonTask.h @@ -20,29 +20,39 @@ #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 { +class PythonTask FINAL : public AsyncTask { PUBLISHED: PythonTask(PyObject *function = Py_None, const string &name = string()); virtual ~PythonTask(); 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,22 @@ 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 _ignore_return; + 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/config_express.cxx b/panda/src/express/config_express.cxx index dd590fcb38..042b6d8db1 100644 --- a/panda/src/express/config_express.cxx +++ b/panda/src/express/config_express.cxx @@ -194,40 +194,3 @@ get_config_express() { static DConfig config_express; return config_express; } - -#ifdef ANDROID -static JavaVM *panda_jvm = NULL; - -/** - * Called by Java when loading this library. - */ -jint JNI_OnLoad(JavaVM *jvm, void *reserved) { - panda_jvm = jvm; - return JNI_VERSION_1_4; -} - -/** - * Returns a pointer to the JavaVM object. - */ -JavaVM *get_java_vm() { - nassertr(panda_jvm != NULL, NULL); - return panda_jvm; -} - -/** - * Returns a JNIEnv object for the current thread. If it doesn't already - * exist, attaches the JVM to this thread. - */ -JNIEnv *get_jni_env() { - nassertr(panda_jvm != NULL, NULL); - JNIEnv *env = NULL; - int status = panda_jvm->GetEnv((void**) &env, JNI_VERSION_1_4); - - if (status < 0 || env == NULL) { - express_cat.error() << "JVM is not available in this thread!\n"; - return NULL; - } - - return env; -} -#endif diff --git a/panda/src/express/config_express.h b/panda/src/express/config_express.h index 53f33ca36a..eabe20f834 100644 --- a/panda/src/express/config_express.h +++ b/panda/src/express/config_express.h @@ -28,10 +28,6 @@ #include "executionEnvironment.h" #include "lineStream.h" -#ifdef ANDROID -#include -#endif - ConfigureDecl(config_express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS); NotifyCategoryDecl(express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS); NotifyCategoryDecl(clock, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS); @@ -65,9 +61,4 @@ END_PUBLISH extern EXPCL_PANDAEXPRESS void init_libexpress(); -#ifdef ANDROID -extern EXPCL_PANDAEXPRESS JavaVM *get_java_vm(); -extern EXPCL_PANDAEXPRESS JNIEnv *get_jni_env(); -#endif - #endif /* __CONFIG_UTIL_H__ */ diff --git a/panda/src/express/datagram.I b/panda/src/express/datagram.I index 0ad4007b26..fa6063805b 100644 --- a/panda/src/express/datagram.I +++ b/panda/src/express/datagram.I @@ -408,6 +408,17 @@ get_array() const { */ INLINE PTA_uchar Datagram:: modify_array() { + if (_data == (uchar *)NULL) { + // Create a new array. + _data = PTA_uchar::empty_array(0); + + } else if (_data.get_ref_count() != 1) { + // Copy on write. + PTA_uchar new_data = PTA_uchar::empty_array(0); + new_data.v() = _data.v(); + _data = new_data; + } + return _data; } 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/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.h b/panda/src/express/multifile.h index 5c6ec16feb..f290c98602 100644 --- a/panda/src/express/multifile.h +++ b/panda/src/express/multifile.h @@ -136,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; diff --git a/panda/src/express/ordered_vector.I b/panda/src/express/ordered_vector.I index 24a74c6a45..287998b85c 100644 --- a/panda/src/express/ordered_vector.I +++ b/panda/src/express/ordered_vector.I @@ -109,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. */ @@ -127,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. */ @@ -530,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. */ diff --git a/panda/src/express/ordered_vector.h b/panda/src/express/ordered_vector.h index 7eacfc375e..e9e3a03693 100644 --- a/panda/src/express/ordered_vector.h +++ b/panda/src/express/ordered_vector.h @@ -147,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; @@ -201,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); diff --git a/panda/src/express/p3express_composite2.cxx b/panda/src/express/p3express_composite2.cxx index 95c8b0e5fb..5caa03986d 100644 --- a/panda/src/express/p3express_composite2.cxx +++ b/panda/src/express/p3express_composite2.cxx @@ -12,9 +12,6 @@ #include "threadSafePointerToBase.cxx" #include "trueClock.cxx" #include "typedReferenceCount.cxx" -#include "vector_uchar.cxx" -#include "vector_double.cxx" -#include "vector_float.cxx" #include "virtualFile.cxx" #include "virtualFileComposite.cxx" #include "virtualFileList.cxx" 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.h b/panda/src/express/pointerTo.h index 1c4db74bea..62abdf8e57 100644 --- a/panda/src/express/pointerTo.h +++ b/panda/src/express/pointerTo.h @@ -70,7 +70,7 @@ class PointerTo : public PointerToBase { public: typedef TYPENAME PointerToBase::To To; PUBLISHED: - CONSTEXPR PointerTo() NOEXCEPT DEFAULT_CTOR; + ALWAYS_INLINE_CONSTEXPR PointerTo() NOEXCEPT DEFAULT_CTOR; ALWAYS_INLINE PointerTo(To *ptr) NOEXCEPT; INLINE PointerTo(const PointerTo ©); @@ -133,7 +133,7 @@ class ConstPointerTo : public PointerToBase { public: typedef TYPENAME PointerToBase::To To; PUBLISHED: - CONSTEXPR ConstPointerTo() NOEXCEPT DEFAULT_CTOR; + ALWAYS_INLINE_CONSTEXPR ConstPointerTo() NOEXCEPT DEFAULT_CTOR; ALWAYS_INLINE ConstPointerTo(const To *ptr) NOEXCEPT; INLINE ConstPointerTo(const PointerTo ©); INLINE ConstPointerTo(const ConstPointerTo ©); diff --git a/panda/src/express/pointerToArray.I b/panda/src/express/pointerToArray.I index 7bf8b89d4e..c3a10f3737 100644 --- a/panda/src/express/pointerToArray.I +++ b/panda/src/express/pointerToArray.I @@ -581,6 +581,19 @@ node_unref() const { return ((To *)(this->_void_ptr))->node_unref(); } +/** + * Counts the frequency at which the given element occurs in the vector. + */ +template +INLINE size_t PointerToArray:: +count(const Element &value) const { + if ((this->_void_ptr) != nullptr) { + return std::count(begin(), end(), value); + } else { + return 0; + } +} + /** * */ @@ -1006,6 +1019,19 @@ node_unref() const { return ((To *)(this->_void_ptr))->node_unref(); } +/** + * Counts the frequency at which the given element occurs in the vector. + */ +template +INLINE size_t ConstPointerToArray:: +count(const Element &value) const { + if ((this->_void_ptr) != nullptr) { + return std::count(begin(), end(), value); + } else { + return 0; + } +} + /** * */ diff --git a/panda/src/express/pointerToArray.h b/panda/src/express/pointerToArray.h index d486f1b00f..762a6cb8ce 100644 --- a/panda/src/express/pointerToArray.h +++ b/panda/src/express/pointerToArray.h @@ -105,13 +105,15 @@ 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; + INLINE size_t count(const Element &) const; + #ifdef HAVE_PYTHON EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags)); EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const); @@ -214,6 +216,8 @@ public: INLINE void node_ref() const; INLINE bool node_unref() const; + INLINE size_t count(const Element &) const; + // Reassignment is by pointer, not memberwise as with a vector. INLINE PointerToArray & operator = (ReferenceCountedVector *ptr); @@ -247,6 +251,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. @@ -255,17 +261,17 @@ 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; + INLINE size_t count(const Element &) const; + #ifdef HAVE_PYTHON EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const); EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const); @@ -289,7 +295,6 @@ 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 ©); @@ -342,6 +347,8 @@ PUBLISHED: INLINE void node_ref() const; INLINE bool node_unref() const; + INLINE size_t count(const Element &) const; + // Reassignment is by pointer, not memberwise as with a vector. INLINE ConstPointerToArray & operator = (ReferenceCountedVector *ptr); diff --git a/panda/src/express/pointerToArray_ext.I b/panda/src/express/pointerToArray_ext.I index 7502334e6e..644b5f0645 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,113 @@ __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); + return; + } +#endif + + // In Python 2, there was also an older buffer protocol, supported by eg. + // str and array objects. +#if PY_MAJOR_VERSION < 3 + // The old, deprecated buffer interface, as used by eg. the array module. + const void *buffer; + Py_ssize_t buffer_len; + if (!PyUnicode_CheckExact(data) && + PyObject_AsReadBuffer(data, &buffer, &buffer_len) == 0) { + if (buffer_len % sizeof(Element) != 0) { + PyErr_Format(PyExc_ValueError, + "byte buffer is not a multiple of %zu bytes", + sizeof(Element)); + return; + } + + if (buffer_len > 0) { + this->_this->resize(buffer_len / sizeof(Element)); + memcpy(this->_this->p(), buffer, buffer_len); + } else { + this->_this->clear(); + } + + return; + } +#endif + + Dtool_Raise_TypeError("PointerToArray.set_data() requires a buffer object"); +} + +/** + * 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 +265,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 +352,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 +529,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 b85e6ec856..919f596c33 100644 --- a/panda/src/express/pointerToBase.h +++ b/panda/src/express/pointerToBase.h @@ -31,7 +31,7 @@ public: typedef T To; protected: - CONSTEXPR PointerToBase() NOEXCEPT 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/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/virtualFileMountAndroidAsset.cxx b/panda/src/express/virtualFileMountAndroidAsset.cxx index 3c85aaebb7..d7ffb6254a 100644 --- a/panda/src/express/virtualFileMountAndroidAsset.cxx +++ b/panda/src/express/virtualFileMountAndroidAsset.cxx @@ -78,7 +78,7 @@ is_regular_file(const Filename &file) const { AAsset* asset; asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN); - express_cat.error() << "is_regular_file " << file << " - " << asset << "\n"; + //express_cat.error() << "is_regular_file " << file << " - " << asset << "\n"; if (asset == NULL) { return false; 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/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/glesgsg/glesgsg.h b/panda/src/glesgsg/glesgsg.h index c0afb58b56..64d64db216 100644 --- a/panda/src/glesgsg/glesgsg.h +++ b/panda/src/glesgsg/glesgsg.h @@ -62,6 +62,9 @@ // #include #endif +// Some implementations (Arch Linux) set this in glext.h +typedef char GLchar; + #include "panda_esglext.h" // This helps to keep the source clean of hundreds of ifdefs. 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 8497c7de71..9f6e242968 100644 --- a/panda/src/glstuff/glCgShaderContext_src.cxx +++ b/panda/src/glstuff/glCgShaderContext_src.cxx @@ -357,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. @@ -399,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()) { @@ -410,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) { @@ -1073,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/glGeomMunger_src.cxx b/panda/src/glstuff/glGeomMunger_src.cxx index b26f2bbb10..5a1649f832 100644 --- a/panda/src/glstuff/glGeomMunger_src.cxx +++ b/panda/src/glstuff/glGeomMunger_src.cxx @@ -23,13 +23,8 @@ ALLOC_DELETED_CHAIN_DEF(CLP(GeomMunger)); CLP(GeomMunger):: CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) : StandardMunger(gsg, state, 4, NT_uint8, C_color), - _texture((const TextureAttrib *)state->get_attrib(TextureAttrib::get_class_slot())), - _tex_gen((const TexGenAttrib *)state->get_attrib(TexGenAttrib::get_class_slot())) -{ - // Set a callback to unregister ourselves when either the Texture or the - // TexGen object gets deleted. - _texture.set_callback(this); - _tex_gen.set_callback(this); + _texture(nullptr), + _tex_gen(nullptr) { _flags = 0; @@ -38,6 +33,15 @@ CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) : } else if (gl_parallel_arrays) { _flags |= F_parallel_arrays; } + + if ((_flags & F_parallel_arrays) == 0) { + // Set a callback to unregister ourselves when either the Texture or the + // TexGen object gets deleted. + _texture = (const TextureAttrib *)state->get_attrib(TextureAttrib::get_class_slot()); + _tex_gen = (const TexGenAttrib *)state->get_attrib(TexGenAttrib::get_class_slot()); + _texture.set_callback(this); + _tex_gen.set_callback(this); + } } /** diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index a4daaf3931..43f3dbd809 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -213,12 +213,20 @@ begin_frame(FrameMode mode, Thread *current_thread) { return false; } - if (!_host->begin_frame(FM_parasite, current_thread)) { - if (GLCAT.is_debug()) { - GLCAT.debug() - << get_name() << "'s host is not ready\n"; + if (_host != nullptr) { + if (!_host->begin_frame(FM_parasite, current_thread)) { + if (GLCAT.is_debug()) { + GLCAT.debug() + << get_name() << "'s host is not ready\n"; + } + return false; + } + } else { + // We don't have a host window, which is possible for CocoaGraphicsBuffer. + _gsg->set_current_properties(&get_fb_properties()); + if (!_gsg->begin_frame(current_thread)) { + return false; } - return false; } // Figure out the desired size of the buffer. @@ -235,7 +243,7 @@ begin_frame(FrameMode mode, Thread *current_thread) { } } if (_creation_flags & GraphicsPipe::BF_size_track_host) { - if (_host->get_size() != _size) { + if (_host != nullptr && _host->get_size() != _size) { // We also need to rebuild if we need to change size. _needs_rebuild = true; } @@ -356,7 +364,7 @@ rebuild_bitplanes() { // Calculate bitplane size. This can be larger than the buffer. if (_creation_flags & GraphicsPipe::BF_size_track_host) { - if (_host->get_size() != _size) { + if (_host != nullptr && _host->get_size() != _size) { set_size_and_recalc(_host->get_x_size(), _host->get_y_size()); } @@ -1253,7 +1261,11 @@ end_frame(FrameMode mode, Thread *current_thread) { generate_mipmaps(); } - _host->end_frame(FM_parasite, current_thread); + if (_host != nullptr) { + _host->end_frame(FM_parasite, current_thread); + } else { + glgsg->end_frame(current_thread); + } if (mode == FM_render) { trigger_flip(); @@ -1315,8 +1327,11 @@ bool CLP(GraphicsBuffer):: open_buffer() { report_my_gl_errors(); - // Double check that we have a host - nassertr(_host != 0, false); + // Double check that we have a valid gsg + nassertr(_gsg != nullptr, false); + if (!_gsg->is_valid()) { + return false; + } // Count total color buffers. int totalcolor = @@ -1491,8 +1506,10 @@ open_buffer() { _fb_properties.set_back_buffers(0); _fb_properties.set_indexed_color(0); _fb_properties.set_rgb_color(1); - _fb_properties.set_force_hardware(_host->get_fb_properties().get_force_hardware()); - _fb_properties.set_force_software(_host->get_fb_properties().get_force_software()); + if (_host != nullptr) { + _fb_properties.set_force_hardware(_host->get_fb_properties().get_force_hardware()); + _fb_properties.set_force_software(_host->get_fb_properties().get_force_software()); + } _is_valid = true; _needs_rebuild = true; @@ -1501,6 +1518,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 != nullptr) ? _host : this; +} + /** * Closes the buffer right now. Called from the window thread. */ @@ -1689,7 +1716,7 @@ report_my_errors(int line, const char *file) { */ void CLP(GraphicsBuffer):: check_host_valid() { - if ((_host == 0)||(!_host->is_valid())) { + if (_host != nullptr && !_host->is_valid()) { _rb_data_size_bytes = 0; if (_rb_context != NULL) { // We must delete this object first, because when the GSG destructs, so diff --git a/panda/src/glstuff/glGraphicsBuffer_src.h b/panda/src/glstuff/glGraphicsBuffer_src.h index 9f99850a7f..31f7c91912 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(); @@ -84,8 +86,6 @@ protected: void report_my_errors(int line, const char *file); -private: - void bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane plane, GLenum attachpoint); void bind_slot_multisample(bool rb_resize, Texture **attach, diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 5c73772b50..af84e2bafd 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -155,7 +155,11 @@ null_glBlendColor(GLclampf, GLclampf, GLclampf, GLclampf) { // drawing GUIs and such. static const string default_vshader = #ifndef OPENGLES +#ifdef __APPLE__ // Apple's GL 3.2 contexts require at least GLSL 1.50. + "#version 150\n" +#else "#version 130\n" +#endif "in vec4 p3d_Vertex;\n" "in vec4 p3d_Color;\n" "in vec2 p3d_MultiTexCoord0;\n" @@ -174,12 +178,16 @@ 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 = #ifndef OPENGLES +#ifdef __APPLE__ // Apple's GL 3.2 contexts require at least GLSL 1.50. + "#version 150\n" +#else "#version 130\n" +#endif "in vec2 texcoord;\n" "in vec4 color;\n" "out vec4 p3d_FragColor;\n" @@ -1139,7 +1147,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 @@ -1305,7 +1313,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 @@ -2135,7 +2143,7 @@ reset() { _glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) get_extension_func("glGenerateMipmap"); _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) - get_extension_func("glRenderbufferStorageMultisampleEXT"); + get_extension_func("glRenderbufferStorageMultisample"); _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) get_extension_func("glBlitFramebuffer"); @@ -2550,8 +2558,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 @@ -2580,6 +2588,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 @@ -3122,6 +3135,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 @@ -3929,7 +3946,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 @@ -3948,7 +3964,7 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader, } #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); @@ -4013,10 +4029,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() @@ -6447,6 +6463,15 @@ 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: + case Texture::F_depth_component32: + component_type = Texture::T_float; + break; + default: if (_current_properties->get_srgb_color()) { if (_current_properties->get_alpha_bits()) { @@ -7536,6 +7561,36 @@ bind_light(Spotlight *light_obj, const NodePath &light, int light_id) { } #endif // SUPPORT_FIXED_FUNCTION +/** + * Creates a depth buffer for shadow mapping. A derived GSG can override this + * if it knows that a particular buffer type works best for shadow rendering. + */ +GraphicsOutput *CLP(GraphicsStateGuardian):: +make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host) { + // We override this to circumvent the fact that GraphicsEngine::make_output + // can only be called from the app thread. + if (!_supports_framebuffer_object) { + return GraphicsStateGuardian::make_shadow_buffer(light, tex, host); + } + + bool is_point = light->is_of_type(PointLight::get_class_type()); + + // Determine the properties for creating the depth buffer. + FrameBufferProperties fbp; + fbp.set_depth_bits(shadow_depth_bits); + + WindowProperties props = WindowProperties::size(light->get_shadow_buffer_size()); + int flags = GraphicsPipe::BF_refuse_window; + if (is_point) { + flags |= GraphicsPipe::BF_size_square; + } + + CLP(GraphicsBuffer) *sbuffer = new CLP(GraphicsBuffer)(get_engine(), get_pipe(), light->get_name(), fbp, props, flags, this, host); + sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth); + get_engine()->add_window(sbuffer, light->get_shadow_buffer_sort()); + return sbuffer; +} + #ifdef SUPPORT_IMMEDIATE_MODE /** * Uses the ImmediateModeSender to draw a series of primitives of the @@ -9260,18 +9315,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; @@ -10333,8 +10392,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) { @@ -10350,7 +10408,7 @@ set_state_and_transform(const RenderState *target, // 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 @@ -10897,25 +10955,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 } /** @@ -10925,7 +10978,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; } @@ -12299,20 +12361,20 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, if (_supports_clear_texture) { // We can do that with the convenient glClearTexImage // function. - string clear_data = tex->get_clear_data(); + vector_uchar clear_data = tex->get_clear_data(); _glClearTexImage(gtc->_index, n - mipmap_bias, external_format, - component_type, (void *)clear_data.data()); + component_type, (void *)&clear_data[0]); continue; } } else { if (_supports_clear_buffer) { // For buffer textures we need to clear the underlying // storage. - string clear_data = tex->get_clear_data(); + vector_uchar clear_data = tex->get_clear_data(); _glClearBufferData(GL_TEXTURE_BUFFER, internal_format, external_format, - component_type, (const void *)clear_data.data()); + component_type, (const void *)&clear_data[0]); continue; } } @@ -12702,6 +12764,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); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index bf95facb90..2b357ece5b 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, @@ -382,6 +381,8 @@ public: int light_id); #endif + virtual GraphicsOutput *make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host); + LVecBase4 get_light_color(Light *light) const; #ifdef SUPPORT_IMMEDIATE_MODE @@ -567,7 +568,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 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 41b004775f..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; } @@ -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 3457a8b265..340cfd099c 100644 --- a/panda/src/gobj/geom.I +++ b/panda/src/gobj/geom.I @@ -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(). @@ -499,22 +512,17 @@ CData() : * */ INLINE Geom::CData:: -CData(const Geom::CData ©) : - _data(copy._data), - _primitives(copy._primitives), - _primitive_type(copy._primitive_type), - _shade_model(copy._shade_model), - _geom_rendering(copy._geom_rendering), - _modified(copy._modified), - _internal_bounds(copy._internal_bounds), - _nested_vertices(copy._nested_vertices), - _internal_bounds_stale(copy._internal_bounds_stale), - _bounds_type(copy._bounds_type), - _user_bounds(copy._user_bounds) +CData(GeomVertexData *data) : + _data(data), + _primitive_type(PT_none), + _shade_model(SM_uniform), + _geom_rendering(0), + _nested_vertices(0), + _internal_bounds_stale(true), + _bounds_type(BoundingVolume::BT_default) { } - /** * */ diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index eb44a46abb..efdd4ba5ca 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -46,13 +46,7 @@ make_cow_copy() { * */ Geom:: -Geom(const GeomVertexData *data) { - // Let's ensure the vertex data gets set on all stages at once. - OPEN_ITERATE_ALL_STAGES(_cycler) { - CDStageWriter cdata(_cycler, pipeline_stage); - cdata->_data = (GeomVertexData *)data; - } - CLOSE_ITERATE_ALL_STAGES(_cycler); +Geom(const GeomVertexData *data) : _cycler(CData((GeomVertexData *)data)) { } /** @@ -294,10 +288,10 @@ 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(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 @@ -327,7 +321,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,7 +329,7 @@ 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); @@ -348,9 +342,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 +372,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; @@ -627,7 +625,7 @@ unify_in_place(int max_indices, bool preserve_order) { } else { // We have already encountered another primitive of this type. Combine // them. - combine_primitives((*npi).second, primitive, current_thread); + combine_primitives((*npi).second, move(primitive), current_thread); } } @@ -655,28 +653,73 @@ unify_in_place(int max_indices, bool preserve_order) { // 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); @@ -925,7 +968,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); } @@ -1166,14 +1210,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); } /** @@ -1218,6 +1261,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) { @@ -1439,31 +1485,29 @@ reset_geom_rendering(Geom::CData *cdata) { * is modified to append the vertices from b_prim, which is unmodified. */ void Geom:: -combine_primitives(GeomPrimitive *a_prim, const GeomPrimitive *b_prim, +combine_primitives(GeomPrimitive *a_prim, CPT(GeomPrimitive) b_prim, Thread *current_thread) { nassertv(a_prim != b_prim); nassertv(a_prim->get_type() == b_prim->get_type()); - CPT(GeomPrimitive) b_prim2 = b_prim; - - if (a_prim->get_index_type() != b_prim2->get_index_type()) { - GeomPrimitive::NumericType index_type = max(a_prim->get_index_type(), b_prim2->get_index_type()); + if (a_prim->get_index_type() != b_prim->get_index_type()) { + GeomPrimitive::NumericType index_type = max(a_prim->get_index_type(), b_prim->get_index_type()); a_prim->set_index_type(index_type); - if (b_prim2->get_index_type() != index_type) { - PT(GeomPrimitive) b_prim_copy = b_prim2->make_copy(); + if (b_prim->get_index_type() != index_type) { + PT(GeomPrimitive) b_prim_copy = b_prim->make_copy(); b_prim_copy->set_index_type(index_type); - b_prim2 = b_prim_copy; + b_prim = b_prim_copy; } } - if (!b_prim2->is_indexed()) { - PT(GeomPrimitive) b_prim_copy = b_prim2->make_copy(); + if (!b_prim->is_indexed()) { + PT(GeomPrimitive) b_prim_copy = b_prim->make_copy(); b_prim_copy->make_indexed(); - b_prim2 = b_prim_copy; + b_prim = b_prim_copy; } PT(GeomVertexArrayData) a_vertices = a_prim->modify_vertices(); - CPT(GeomVertexArrayData) b_vertices = b_prim2->get_vertices(); + CPT(GeomVertexArrayData) b_vertices = b_prim->get_vertices(); if (a_prim->requires_unused_vertices()) { GeomVertexReader index(b_vertices, 0); @@ -1484,7 +1528,7 @@ combine_primitives(GeomPrimitive *a_prim, const GeomPrimitive *b_prim, if (a_prim->is_composite()) { // Also copy the ends array. PTA_int a_ends = a_prim->modify_ends(); - CPTA_int b_ends = b_prim2->get_ends(); + CPTA_int b_ends = b_prim->get_ends(); for (size_t i = 0; i < b_ends.size(); ++i) { a_ends.push_back(b_ends[i] + orig_a_vertices); } @@ -1701,10 +1745,10 @@ 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(); diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h index a1408878d8..ec81442703 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; @@ -196,7 +196,7 @@ private: void reset_geom_rendering(CData *cdata); - void combine_primitives(GeomPrimitive *a_prim, const GeomPrimitive *b_prim, + void combine_primitives(GeomPrimitive *a_prim, CPT(GeomPrimitive) b_prim, Thread *current_thread); private: @@ -302,7 +302,8 @@ private: class EXPCL_PANDA_GOBJ CData : public CycleData { public: INLINE CData(); - INLINE CData(const CData ©); + INLINE CData(GeomVertexData *data); + ALLOC_DELETED_CHAIN(CData); virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; @@ -429,7 +430,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/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 1dceada335..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); + } } /** diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index b96249cc83..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. @@ -2200,9 +2206,21 @@ get_vertex(int i) const { // The indexed case. nassertr(i >= 0 && i < get_num_vertices(), -1); - GeomVertexReader index(_vertices, 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. @@ -2229,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 0880e77c7a..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(); @@ -371,6 +371,7 @@ 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; 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 351f56cd53..624525360d 100644 --- a/panda/src/gobj/geomVertexArrayData.I +++ b/panda/src/gobj/geomVertexArrayData.I @@ -229,8 +229,20 @@ mark_used() { * */ INLINE GeomVertexArrayData::CData:: -CData() : - _usage_hint(UH_unspecified), +CData(UsageHint usage_hint) : + _usage_hint(usage_hint), + _rw_lock("GeomVertexArrayData::CData::_rw_lock") +{ +} + +/** + * + */ +INLINE GeomVertexArrayData::CData:: +CData(GeomVertexArrayData::CData &&from) NOEXCEPT : + _usage_hint(move(from._usage_hint)), + _buffer(move(from._buffer)), + _modified(move(from._modified)), _rw_lock("GeomVertexArrayData::CData::_rw_lock") { } diff --git a/panda/src/gobj/geomVertexArrayData.cxx b/panda/src/gobj/geomVertexArrayData.cxx index 2747e22395..50a4529159 100644 --- a/panda/src/gobj/geomVertexArrayData.cxx +++ b/panda/src/gobj/geomVertexArrayData.cxx @@ -77,16 +77,10 @@ GeomVertexArrayData:: GeomVertexArrayData(const GeomVertexArrayFormat *array_format, GeomVertexArrayData::UsageHint usage_hint) : SimpleLruPage(0), - _array_format(array_format) + _array_format(array_format), + _cycler(CData(usage_hint)), + _contexts(nullptr) { - OPEN_ITERATE_ALL_STAGES(_cycler) { - CDStageWriter cdata(_cycler, pipeline_stage); - cdata->_usage_hint = usage_hint; - } - CLOSE_ITERATE_ALL_STAGES(_cycler); - - _contexts = NULL; - set_lru_size(0); nassertv(_array_format->is_registered()); } @@ -99,10 +93,9 @@ GeomVertexArrayData(const GeomVertexArrayData ©) : CopyOnWriteObject(copy), SimpleLruPage(copy), _array_format(copy._array_format), - _cycler(copy._cycler) + _cycler(copy._cycler), + _contexts(nullptr) { - _contexts = NULL; - copy.mark_used_lru(); set_lru_size(get_data_size_bytes()); diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h index c16c5831d9..4a49d26881 100644 --- a/panda/src/gobj/geomVertexArrayData.h +++ b/panda/src/gobj/geomVertexArrayData.h @@ -150,7 +150,8 @@ private: // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA_GOBJ CData : public CycleData { public: - INLINE CData(); + INLINE CData(UsageHint usage_hint = UH_unspecified); + INLINE CData(CData &&from) NOEXCEPT; INLINE CData(const CData ©); INLINE void operator = (const CData ©); 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 888db97c88..5a24b166a5 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,9 +141,9 @@ 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(); } @@ -151,10 +151,10 @@ get_array(int i) const { * Equivalent to get_array(i).get_handle(). */ INLINE CPT(GeomVertexArrayDataHandle) GeomVertexData:: -get_array_handle(int i) const { +get_array_handle(size_t i) const { Thread *current_thread = Thread::get_current_thread(); CDReader cdata(_cycler, current_thread); - nassertr(i >= 0 && i < (int)cdata->_arrays.size(), NULL); + nassertr(i < cdata->_arrays.size(), nullptr); return new GeomVertexArrayDataHandle(cdata->_arrays[i].get_read_pointer(), current_thread); } @@ -168,7 +168,7 @@ get_array_handle(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); } @@ -177,7 +177,7 @@ modify_array(int i) { * Equivalent to modify_array(i).modify_handle(). */ INLINE PT(GeomVertexArrayDataHandle) GeomVertexData:: -modify_array_handle(int i) { +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); @@ -192,7 +192,7 @@ modify_array_handle(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); } @@ -583,17 +583,14 @@ CData() : * */ INLINE GeomVertexData::CData:: -CData(const GeomVertexData::CData ©) : - _usage_hint(copy._usage_hint), - _format(copy._format), - _arrays(copy._arrays), - _transform_table(copy._transform_table), - _transform_blend_table(copy._transform_blend_table), - _slider_table(copy._slider_table), - _animated_vertices(copy._animated_vertices), - _animated_vertices_modified(copy._animated_vertices_modified), - _modified(copy._modified) +CData(const GeomVertexFormat *format, GeomVertexData::UsageHint usage_hint) : + _format(format), + _usage_hint(usage_hint) { + size_t num_arrays = format->get_num_arrays(); + for (size_t i = 0; i < num_arrays; ++i) { + _arrays.push_back(new GeomVertexArrayData(format->get_array(i), usage_hint)); + } } /** @@ -756,7 +753,7 @@ GeomVertexDataPipelineReader(const GeomVertexData *object, * */ INLINE void GeomVertexDataPipelineReader:: -set_object(CPT(GeomVertexData) object) { +set_object(const GeomVertexData *object) { #ifdef DO_PIPELINING if (_cdata != NULL) { unref_delete((CycleData *)_cdata); @@ -764,7 +761,7 @@ set_object(CPT(GeomVertexData) object) { #endif // DO_PIPELINING _array_readers.clear(); - _object.swap(object); + _object = (GeomVertexData *)object; _cdata = (GeomVertexData::CData *)_object->_cycler.read_unlocked(_current_thread); _got_array_readers = false; @@ -888,9 +885,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 cfa47d9949..7d8e0e63f3 100644 --- a/panda/src/gobj/geomVertexData.cxx +++ b/panda/src/gobj/geomVertexData.cxx @@ -67,24 +67,10 @@ GeomVertexData(const string &name, _char_pcollector(PStatCollector(_animation_pcollector, name)), _skinning_pcollector(_char_pcollector, "Skinning"), _morphs_pcollector(_char_pcollector, "Morphs"), - _blends_pcollector(_char_pcollector, "Calc blends") + _blends_pcollector(_char_pcollector, "Calc blends"), + _cycler(GeomVertexData::CData(format, usage_hint)) { nassertv(format->is_registered()); - - // Create some empty arrays as required by the format. Let's ensure the - // vertex data gets set on all stages at once. - OPEN_ITERATE_ALL_STAGES(_cycler) { - CDStageWriter cdata(_cycler, pipeline_stage); - cdata->_format = format; - cdata->_usage_hint = usage_hint; - int num_arrays = format->get_num_arrays(); - for (int i = 0; i < num_arrays; i++) { - PT(GeomVertexArrayData) array = new GeomVertexArrayData - (format->get_array(i), usage_hint); - cdata->_arrays.push_back(array.p()); - } - } - CLOSE_ITERATE_ALL_STAGES(_cycler); } /** @@ -1850,17 +1836,21 @@ do_transform_vector_column(const GeomVertexFormat *format, GeomVertexRewriter &d bool normalize = false; if (data_column->get_contents() == C_normal) { // This is to preserve perpendicularity to the surface. - LVecBase3 scale, shear, hpr; - if (decompose_matrix(mat.get_upper_3(), scale, shear, hpr) && - IS_NEARLY_EQUAL(scale[0], scale[1]) && - IS_NEARLY_EQUAL(scale[0], scale[2])) { - if (scale[0] == 1) { + LVecBase3 scale_sq(mat.get_row3(0).length_squared(), + mat.get_row3(1).length_squared(), + mat.get_row3(2).length_squared()); + if (IS_THRESHOLD_EQUAL(scale_sq[0], scale_sq[1], 2.0e-3f) && + IS_THRESHOLD_EQUAL(scale_sq[0], scale_sq[2], 2.0e-3f)) { + // There is a uniform scale. + LVecBase3 scale, shear, hpr; + if (IS_THRESHOLD_EQUAL(scale_sq[0], 1, 2.0e-3f)) { // No scale to worry about. xform = mat; - } else { - // Simply take the uniform scale out of the transformation. Not sure - // if it might be better to just normalize? + } else if (decompose_matrix(mat.get_upper_3(), scale, shear, hpr)) { + // Make a new matrix with scale/translate taken out of the equation. compose_matrix(xform, LVecBase3(1, 1, 1), shear, hpr, LVecBase3::zero()); + } else { + normalize = true; } } else { // There is a non-uniform scale, so we need to do all this to preserve @@ -2565,8 +2555,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) { @@ -2586,8 +2576,8 @@ 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(); diff --git a/panda/src/gobj/geomVertexData.h b/panda/src/gobj/geomVertexData.h index 8e236029ae..d86dd36fc4 100644 --- a/panda/src/gobj/geomVertexData.h +++ b/panda/src/gobj/geomVertexData.h @@ -105,13 +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 CPT(GeomVertexArrayDataHandle) get_array_handle(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 PT(GeomVertexArrayDataHandle) modify_array_handle(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; @@ -293,7 +293,8 @@ private: class EXPCL_PANDA_GOBJ CData : public CycleData { public: INLINE CData(); - INLINE CData(const CData ©); + INLINE CData(const GeomVertexFormat *format, UsageHint usage_hint); + ALLOC_DELETED_CHAIN(CData); virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; @@ -432,7 +433,7 @@ public: INLINE UpdateSeq get_modified() const; protected: - PT(GeomVertexData) _object; + GeomVertexData *_object; Thread *_current_thread; GeomVertexData::CData *_cdata; }; @@ -440,6 +441,8 @@ 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: @@ -448,7 +451,7 @@ public: ALLOC_DELETED_CHAIN(GeomVertexDataPipelineReader); - INLINE void set_object(CPT(GeomVertexData) object); + INLINE void set_object(const GeomVertexData *object); INLINE const GeomVertexData *get_object() const; INLINE void check_array_readers() const; @@ -504,6 +507,8 @@ 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: @@ -516,10 +521,10 @@ public: 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/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.I b/panda/src/gobj/shader.I index fe74b2bda6..fcefc1e235 100644 --- a/panda/src/gobj/shader.I +++ b/panda/src/gobj/shader.I @@ -694,9 +694,9 @@ read_datagram(DatagramIterator &scan) { * */ INLINE Shader::ShaderFile:: -ShaderFile(const string &shared) : +ShaderFile(string shared) : _separate(false), - _shared(shared) + _shared(move(shared)) { } @@ -704,17 +704,14 @@ ShaderFile(const string &shared) : * */ INLINE Shader::ShaderFile:: -ShaderFile(const string &vertex, - const string &fragment, - const string &geometry, - const string &tess_control, - const string &tess_evaluation) : +ShaderFile(string vertex, string fragment, string geometry, + string tess_control, string tess_evaluation) : _separate(true), - _vertex(vertex), - _fragment(fragment), - _geometry(geometry), - _tess_control(tess_control), - _tess_evaluation(tess_evaluation) + _vertex(move(vertex)), + _fragment(move(fragment)), + _geometry(move(geometry)), + _tess_control(move(tess_control)), + _tess_evaluation(move(tess_evaluation)) { } diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index 27a055a794..add7acef2f 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; @@ -3074,7 +3179,7 @@ load_compute(ShaderLanguage lang, const Filename &fn) { * Loads the shader, using the string as shader body. */ PT(Shader) Shader:: -make(const string &body, ShaderLanguage lang) { +make(string body, ShaderLanguage lang) { if (lang == SL_GLSL) { shader_cat.error() << "GLSL shaders must have separate shader bodies!\n"; @@ -3092,7 +3197,7 @@ make(const string &body, ShaderLanguage lang) { } #endif - ShaderFile sbody(body); + ShaderFile sbody(move(body)); if (cache_generated_shaders) { ShaderTable::const_iterator i = _make_table.find(sbody); @@ -3103,7 +3208,7 @@ make(const string &body, ShaderLanguage lang) { PT(Shader) shader = new Shader(lang); shader->_filename = ShaderFile("created-shader"); - shader->_text = sbody; + shader->_text = move(sbody); #ifdef HAVE_CG if (lang == SL_Cg) { @@ -3118,7 +3223,7 @@ make(const string &body, ShaderLanguage lang) { #endif if (cache_generated_shaders) { - _make_table[sbody] = shader; + _make_table[shader->_text] = shader; } if (dump_generated_shaders) { @@ -3130,7 +3235,7 @@ make(const string &body, ShaderLanguage lang) { pofstream s; s.open(fn.c_str(), ios::out | ios::trunc); - s << body; + s << shader->get_text(); s.close(); } return shader; @@ -3140,9 +3245,8 @@ make(const string &body, ShaderLanguage lang) { * Loads the shader, using the strings as shader bodies. */ PT(Shader) Shader:: -make(ShaderLanguage lang, const string &vertex, const string &fragment, - const string &geometry, const string &tess_control, - const string &tess_evaluation) { +make(ShaderLanguage lang, string vertex, string fragment, string geometry, + string tess_control, string tess_evaluation) { #ifndef HAVE_CG if (lang == SL_Cg) { shader_cat.error() << "Support for Cg shaders is not enabled.\n"; @@ -3155,7 +3259,8 @@ make(ShaderLanguage lang, const string &vertex, const string &fragment, return NULL; } - ShaderFile sbody(vertex, fragment, geometry, tess_control, tess_evaluation); + ShaderFile sbody(move(vertex), move(fragment), move(geometry), + move(tess_control), move(tess_evaluation)); if (cache_generated_shaders) { ShaderTable::const_iterator i = _make_table.find(sbody); @@ -3166,7 +3271,7 @@ make(ShaderLanguage lang, const string &vertex, const string &fragment, PT(Shader) shader = new Shader(lang); shader->_filename = ShaderFile("created-shader"); - shader->_text = sbody; + shader->_text = move(sbody); #ifdef HAVE_CG if (lang == SL_Cg) { @@ -3179,7 +3284,7 @@ make(ShaderLanguage lang, const string &vertex, const string &fragment, #endif if (cache_generated_shaders) { - _make_table[sbody] = shader; + _make_table[shader->_text] = shader; } return shader; @@ -3189,7 +3294,7 @@ make(ShaderLanguage lang, const string &vertex, const string &fragment, * Loads the compute shader from the given string. */ PT(Shader) Shader:: -make_compute(ShaderLanguage lang, const string &body) { +make_compute(ShaderLanguage lang, string body) { if (lang != SL_GLSL) { shader_cat.error() << "Only GLSL compute shaders are currently supported.\n"; @@ -3198,7 +3303,7 @@ make_compute(ShaderLanguage lang, const string &body) { ShaderFile sbody; sbody._separate = true; - sbody._compute = body; + sbody._compute = move(body); if (cache_generated_shaders) { ShaderTable::const_iterator i = _make_table.find(sbody); @@ -3209,10 +3314,10 @@ make_compute(ShaderLanguage lang, const string &body) { PT(Shader) shader = new Shader(lang); shader->_filename = ShaderFile("created-shader"); - shader->_text = sbody; + shader->_text = move(sbody); if (cache_generated_shaders) { - _make_table[sbody] = shader; + _make_table[shader->_text] = shader; } return shader; @@ -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..996ee1b7aa 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, @@ -83,7 +84,7 @@ PUBLISHED: }; static PT(Shader) load(const Filename &file, ShaderLanguage lang = SL_none); - static PT(Shader) make(const string &body, ShaderLanguage lang = SL_none); + static PT(Shader) make(string body, ShaderLanguage lang = SL_none); static PT(Shader) load(ShaderLanguage lang, const Filename &vertex, const Filename &fragment, const Filename &geometry = "", @@ -91,11 +92,11 @@ PUBLISHED: const Filename &tess_evaluation = ""); static PT(Shader) load_compute(ShaderLanguage lang, const Filename &fn); static PT(Shader) make(ShaderLanguage lang, - const string &vertex, const string &fragment, - const string &geometry = "", - const string &tess_control = "", - const string &tess_evaluation = ""); - static PT(Shader) make_compute(ShaderLanguage lang, const string &body); + string vertex, string fragment, + string geometry = "", + string tess_control = "", + string tess_evaluation = ""); + static PT(Shader) make_compute(ShaderLanguage lang, string body); INLINE Filename get_filename(ShaderType type = ST_none) const; INLINE void set_filename(ShaderType type, const Filename &filename); @@ -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 { @@ -451,12 +464,9 @@ public: class ShaderFile : public ReferenceCount { public: INLINE ShaderFile() {}; - INLINE ShaderFile(const string &shared); - INLINE ShaderFile(const string &vertex, - const string &fragment, - const string &geometry, - const string &tess_control, - const string &tess_evaluation); + INLINE ShaderFile(string shared); + INLINE ShaderFile(string vertex, string fragment, string geometry, + string tess_control, string tess_evaluation); INLINE void write_datagram(Datagram &dg) const; INLINE void read_datagram(DatagramIterator &source); diff --git a/panda/src/gobj/shaderBuffer.I b/panda/src/gobj/shaderBuffer.I index eb51f42f4e..fbd0b42e46 100644 --- a/panda/src/gobj/shaderBuffer.I +++ b/panda/src/gobj/shaderBuffer.I @@ -19,7 +19,8 @@ INLINE ShaderBuffer:: ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint) : Namable(name), _data_size_bytes(size), - _usage_hint(usage_hint) { + _usage_hint(usage_hint), + _contexts(nullptr) { } /** @@ -31,7 +32,8 @@ ShaderBuffer(const string &name, pvector initial_data, UsageHint Namable(name), _data_size_bytes(initial_data.size()), _usage_hint(usage_hint), - _initial_data(initial_data) { + _initial_data(initial_data), + _contexts(nullptr) { } /** diff --git a/panda/src/gobj/shaderBuffer.cxx b/panda/src/gobj/shaderBuffer.cxx index a7cd5f8150..f0b45df540 100644 --- a/panda/src/gobj/shaderBuffer.cxx +++ b/panda/src/gobj/shaderBuffer.cxx @@ -16,6 +16,14 @@ TypeHandle ShaderBuffer::_type_handle; +/** + * Destructor. + */ +ShaderBuffer:: +~ShaderBuffer() { + release_all(); +} + /** * */ diff --git a/panda/src/gobj/shaderBuffer.h b/panda/src/gobj/shaderBuffer.h index 549ebdc536..8d48fed425 100644 --- a/panda/src/gobj/shaderBuffer.h +++ b/panda/src/gobj/shaderBuffer.h @@ -32,8 +32,10 @@ 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); + ~ShaderBuffer(); + + 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 29b1702280..8f3a91a54c 100644 --- a/panda/src/gobj/texture.I +++ b/panda/src/gobj/texture.I @@ -279,12 +279,12 @@ clear_clear_color() { * Returns the raw image data for a single pixel if it were set to the clear * color. */ -INLINE string Texture:: +INLINE vector_uchar Texture:: get_clear_data() const { CDReader cdata(_cycler); - unsigned char data[16]; - size_t size = do_get_clear_data(cdata, data); - return string((char *)data, size); + vector_uchar data(16); + data.resize(do_get_clear_data(cdata, &data[0])); + return data; } /** @@ -2324,6 +2324,61 @@ get_unsigned_short(const unsigned char *&p) { return (double)v.us / 65535.0; } +/** + * This is used by store() to retrieve the next consecutive component value + * from the indicated element of the array, which is taken to be an array of + * unsigned ints. + */ +INLINE double Texture:: +get_unsigned_int(const unsigned char *&p) { + union { + unsigned int ui; + uchar uc[4]; + } v; + v.uc[0] = (*p++); + v.uc[1] = (*p++); + v.uc[2] = (*p++); + v.uc[3] = (*p++); + return (double)v.ui / 4294967295.0; +} + +/** + * This is used by store() to retrieve the next consecutive component value + * from the indicated element of the array, which is taken to be an array of + * floats. + */ +INLINE double Texture:: +get_float(const unsigned char *&p) { + double v = *((float *)p); + p += 4; + return v; +} + +/** + * This is used by store() to retrieve the next consecutive component value + * from the indicated element of the array, which is taken to be an array of + * half-floats. + */ +INLINE double Texture:: +get_half_float(const unsigned char *&p) { + union { + uint32_t ui; + float uf; + } v; + uint16_t in = *(uint16_t *)p; + p += 2; + uint32_t t1 = in & 0x7fff; // Non-sign bits + uint32_t t2 = in & 0x8000; // Sign bit + uint32_t t3 = in & 0x7c00; // Exponent + t1 <<= 13; // Align mantissa on MSB + t2 <<= 16; // Shift sign bit into position + t1 += 0x38000000; // Adjust bias + t1 = (t3 == 0 ? 0 : t1); // Denormals-as-zero + t1 |= t2; // Re-insert sign bit + v.ui = t1; + return v.uf; +} + /** * Returns true if the indicated filename ends in .txo or .txo.pz or .txo.gz, * false otherwise. diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index d3dd47dbb9..c4a8149ab4 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(); } /** @@ -5087,7 +5088,8 @@ do_store_one(CData *cdata, PNMImage &pnmimage, int z, int n) { return convert_to_pnmimage(pnmimage, do_get_expected_mipmap_x_size(cdata, n), do_get_expected_mipmap_y_size(cdata, n), - cdata->_num_components, cdata->_component_width, + cdata->_num_components, cdata->_component_type, + is_srgb(cdata->_format), cdata->_ram_images[n]._image, do_get_ram_mipmap_page_size(cdata, n), z); } @@ -5110,12 +5112,14 @@ do_store_one(CData *cdata, PfmFile &pfm, int z, int n) { if (cdata->_component_type != T_float) { // PfmFile by way of PNMImage. PNMImage pnmimage; - bool success = convert_to_pnmimage(pnmimage, - do_get_expected_mipmap_x_size(cdata, n), - do_get_expected_mipmap_y_size(cdata, n), - cdata->_num_components, cdata->_component_width, - cdata->_ram_images[n]._image, - do_get_ram_mipmap_page_size(cdata, n), z); + bool success = + convert_to_pnmimage(pnmimage, + do_get_expected_mipmap_x_size(cdata, n), + do_get_expected_mipmap_y_size(cdata, n), + cdata->_num_components, cdata->_component_type, + is_srgb(cdata->_format), + cdata->_ram_images[n]._image, + do_get_ram_mipmap_page_size(cdata, n), z); if (!success) { return false; } @@ -5583,10 +5587,28 @@ do_get_clear_data(const CData *cdata, unsigned char *into) const { nassertr(cdata->_has_clear_color, 0); nassertr(cdata->_num_components <= 4, 0); - // TODO: encode the color into the sRGB color space if used switch (cdata->_component_type) { case T_unsigned_byte: - { + if (is_srgb(cdata->_format)) { + xel color; + xelval alpha; + encode_sRGB_uchar(cdata->_clear_color, color, alpha); + switch (cdata->_num_components) { + case 2: + into[1] = (unsigned char)color.g; + case 1: + into[0] = (unsigned char)color.r; + break; + case 4: + into[3] = (unsigned char)alpha; + case 3: // BGR <-> RGB + into[0] = (unsigned char)color.b; + into[1] = (unsigned char)color.g; + into[2] = (unsigned char)color.r; + break; + } + break; + } else { LColor scaled = cdata->_clear_color.fmin(LColor(1)).fmax(LColor::zero()); scaled *= 255; switch (cdata->_num_components) { @@ -8036,25 +8058,28 @@ convert_from_pfm(PTA_uchar &image, size_t page_size, int z, */ bool Texture:: convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size, - int num_components, int component_width, - CPTA_uchar image, size_t page_size, int z) { + int num_components, ComponentType component_type, + bool is_srgb, CPTA_uchar image, size_t page_size, int z) { xelval maxval = 0xff; - if (component_width > 1) { + if (component_type != T_unsigned_byte && component_type != T_byte) { maxval = 0xffff; } - pnmimage.clear(x_size, y_size, num_components, maxval); + ColorSpace color_space = is_srgb ? CS_sRGB : CS_linear; + pnmimage.clear(x_size, y_size, num_components, maxval, nullptr, color_space); bool has_alpha = pnmimage.has_alpha(); bool is_grayscale = pnmimage.is_grayscale(); int idx = page_size * z; nassertr(idx + page_size <= image.size(), false); - const unsigned char *p = &image[idx]; - if (component_width == 1) { - xel *array = pnmimage.get_array(); + xel *array = pnmimage.get_array(); + xelval *alpha = pnmimage.get_alpha_array(); + + switch (component_type) { + case T_unsigned_byte: if (is_grayscale) { + const unsigned char *p = &image[idx]; if (has_alpha) { - xelval *alpha = pnmimage.get_alpha_array(); for (int j = y_size-1; j >= 0; j--) { xel *row = array + j * x_size; xelval *alpha_row = alpha + j * x_size; @@ -8071,9 +8096,10 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size, } } } + nassertr(p == &image[idx] + page_size, false); } else { + const unsigned char *p = &image[idx]; if (has_alpha) { - xelval *alpha = pnmimage.get_alpha_array(); for (int j = y_size-1; j >= 0; j--) { xel *row = array + j * x_size; xelval *alpha_row = alpha + j * x_size; @@ -8094,29 +8120,78 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size, } } } + nassertr(p == &image[idx] + page_size, false); } + break; - } else if (component_width == 2) { - for (int j = y_size-1; j >= 0; j--) { - for (int i = 0; i < x_size; i++) { - if (is_grayscale) { - pnmimage.set_gray(i, j, get_unsigned_short(p)); - } else { - pnmimage.set_blue(i, j, get_unsigned_short(p)); - pnmimage.set_green(i, j, get_unsigned_short(p)); - pnmimage.set_red(i, j, get_unsigned_short(p)); - } - if (has_alpha) { - pnmimage.set_alpha(i, j, get_unsigned_short(p)); + case T_unsigned_short: + { + const uint16_t *p = (const uint16_t *)&image[idx]; + + for (int j = y_size-1; j >= 0; j--) { + xel *row = array + j * x_size; + xelval *alpha_row = alpha + j * x_size; + for (int i = 0; i < x_size; i++) { + PPM_PUTB(row[i], *p++); + if (!is_grayscale) { + PPM_PUTG(row[i], *p++); + PPM_PUTR(row[i], *p++); + } + if (has_alpha) { + alpha_row[i] = *p++; + } } } + nassertr((const unsigned char *)p == &image[idx] + page_size, false); } + break; - } else { + case T_unsigned_int: + { + const uint32_t *p = (const uint32_t *)&image[idx]; + + for (int j = y_size-1; j >= 0; j--) { + xel *row = array + j * x_size; + xelval *alpha_row = alpha + j * x_size; + for (int i = 0; i < x_size; i++) { + PPM_PUTB(row[i], (*p++) >> 16u); + if (!is_grayscale) { + PPM_PUTG(row[i], (*p++) >> 16u); + PPM_PUTR(row[i], (*p++) >> 16u); + } + if (has_alpha) { + alpha_row[i] = (*p++) >> 16u; + } + } + } + nassertr((const unsigned char *)p == &image[idx] + page_size, false); + } + break; + + case T_half_float: + { + const unsigned char *p = &image[idx]; + + for (int j = y_size-1; j >= 0; j--) { + for (int i = 0; i < x_size; i++) { + pnmimage.set_blue(i, j, get_half_float(p)); + if (!is_grayscale) { + pnmimage.set_green(i, j, get_half_float(p)); + pnmimage.set_red(i, j, get_half_float(p)); + } + if (has_alpha) { + pnmimage.set_alpha(i, j, get_half_float(p)); + } + } + } + nassertr(p == &image[idx] + page_size, false); + } + break; + + default: return false; } - nassertr(p == &image[idx] + page_size, false); return true; } diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index e40ca6f8e5..5816b8c538 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; @@ -264,7 +265,7 @@ PUBLISHED: INLINE LColor get_clear_color() const; INLINE void set_clear_color(const LColor &color); INLINE void clear_clear_color(); - INLINE string get_clear_data() const; + INLINE vector_uchar get_clear_data() const; MAKE_PROPERTY2(clear_color, has_clear_color, get_clear_color, set_clear_color, clear_clear_color); @@ -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(); @@ -798,7 +801,8 @@ private: int z, const PfmFile &pfm, int num_components, int component_width); static bool convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size, - int num_components, int component_width, + int num_components, + ComponentType component_type, bool is_srgb, CPTA_uchar image, size_t page_size, int z); static bool convert_to_pfm(PfmFile &pfm, int x_size, int y_size, @@ -853,6 +857,9 @@ private: INLINE static void store_scaled_short(unsigned char *&p, int value, double scale); INLINE static double get_unsigned_byte(const unsigned char *&p); INLINE static double get_unsigned_short(const unsigned char *&p); + INLINE static double get_unsigned_int(const unsigned char *&p); + INLINE static double get_float(const unsigned char *&p); + INLINE static double get_half_float(const unsigned char *&p); INLINE static bool is_txo_filename(const Filename &fullpath); INLINE static bool is_dds_filename(const Filename &fullpath); 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/texturePeeker.cxx b/panda/src/gobj/texturePeeker.cxx index 42e619e501..b4e1be9224 100644 --- a/panda/src/gobj/texturePeeker.cxx +++ b/panda/src/gobj/texturePeeker.cxx @@ -82,6 +82,18 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) { _get_component = Texture::get_unsigned_short; break; + case Texture::T_unsigned_int: + _get_component = Texture::get_unsigned_int; + break; + + case Texture::T_float: + _get_component = Texture::get_float; + break; + + case Texture::T_half_float: + _get_component = Texture::get_half_float; + break; + default: // Not supported. _image.clear(); @@ -123,7 +135,6 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) { break; case Texture::F_rgb: - case Texture::F_srgb: case Texture::F_rgb5: case Texture::F_rgb8: case Texture::F_rgb12: @@ -135,7 +146,6 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) { break; case Texture::F_rgba: - case Texture::F_srgb_alpha: case Texture::F_rgbm: case Texture::F_rgba4: case Texture::F_rgba5: @@ -146,6 +156,25 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) { case Texture::F_rgb10_a2: _get_texel = get_texel_rgba; break; + + case Texture::F_srgb: + if (_component_type == Texture::T_unsigned_byte) { + _get_texel = get_texel_srgb; + } else { + gobj_cat.error() + << "sRGB texture should have component type T_unsigned_byte\n"; + } + break; + + case Texture::F_srgb_alpha: + if (_component_type == Texture::T_unsigned_byte) { + _get_texel = get_texel_srgba; + } else { + gobj_cat.error() + << "sRGB texture should have component type T_unsigned_byte\n"; + } + break; + default: // Not supported. gobj_cat.error() << "Unsupported texture peeker format: " @@ -570,3 +599,27 @@ get_texel_rgba(LColor &color, const unsigned char *&p, GetComponentFunc *get_com color[0] = (*get_component)(p); color[3] = (*get_component)(p); } + +/** + * Gets the color of the texel at byte p, given that the texture is in format + * F_srgb or similar. + */ +void TexturePeeker:: +get_texel_srgb(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) { + color[2] = decode_sRGB_float(*p++); + color[1] = decode_sRGB_float(*p++); + color[0] = decode_sRGB_float(*p++); + color[3] = 1.0f; +} + +/** + * Gets the color of the texel at byte p, given that the texture is in format + * F_srgb_alpha or similar. + */ +void TexturePeeker:: +get_texel_srgba(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) { + color[2] = decode_sRGB_float(*p++); + color[1] = decode_sRGB_float(*p++); + color[0] = decode_sRGB_float(*p++); + color[3] = (*get_component)(p); +} diff --git a/panda/src/gobj/texturePeeker.h b/panda/src/gobj/texturePeeker.h index cb503bf041..ffe3aba2b1 100644 --- a/panda/src/gobj/texturePeeker.h +++ b/panda/src/gobj/texturePeeker.h @@ -79,6 +79,8 @@ private: static void get_texel_la(LColor &color, const unsigned char *&p, GetComponentFunc *get_component); static void get_texel_rgb(LColor &color, const unsigned char *&p, GetComponentFunc *get_component); static void get_texel_rgba(LColor &color, const unsigned char *&p, GetComponentFunc *get_component); + static void get_texel_srgb(LColor &color, const unsigned char *&p, GetComponentFunc *get_component); + static void get_texel_srgba(LColor &color, const unsigned char *&p, GetComponentFunc *get_component); int _x_size; int _y_size; 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 507fa41fc4..e81d40c15f 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; } } @@ -87,6 +84,29 @@ set_ram_image(PyObject *image, Texture::CompressionMode compression, } #endif +#if PY_MAJOR_VERSION < 3 + // The old, deprecated buffer interface, as used by eg. the array module. + const void *buffer; + Py_ssize_t buffer_len; + if (!PyUnicode_CheckExact(image) && + PyObject_AsReadBuffer(image, &buffer, &buffer_len) == 0) { + if (compression == Texture::CM_off) { + int component_width = _this->get_component_width(); + if (buffer_len % component_width != 0) { + PyErr_Format(PyExc_ValueError, + "byte buffer is not a multiple of %d bytes", + component_width); + return; + } + } + + PTA_uchar data = PTA_uchar::empty_array(buffer_len, Texture::get_class_type()); + memcpy(data.p(), buffer, buffer_len); + _this->set_ram_image(MOVE(data), compression, page_size); + return; + } +#endif + Dtool_Raise_ArgTypeError(image, 0, "Texture.set_ram_image", "CPTA_uchar or buffer"); } @@ -99,15 +119,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; } } 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/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/vertexDataSaveFile.cxx b/panda/src/gobj/vertexDataSaveFile.cxx index 0aa8abff56..fd7087ba5d 100644 --- a/panda/src/gobj/vertexDataSaveFile.cxx +++ b/panda/src/gobj/vertexDataSaveFile.cxx @@ -23,6 +23,11 @@ #include #endif // _WIN32 +#if defined(__ANDROID__) && !defined(HAVE_LOCKF) +// Needed for flock. +#include +#endif + /** * */ 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/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.h b/panda/src/grutil/pfmVizzer.h index e8423f8dd1..e04e8a06bc 100644 --- a/panda/src/grutil/pfmVizzer.h +++ b/panda/src/grutil/pfmVizzer.h @@ -29,7 +29,7 @@ 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 c01c851345..8aac5f0e9a 100644 --- a/panda/src/grutil/shaderTerrainMesh.cxx +++ b/panda/src/grutil/shaderTerrainMesh.cxx @@ -150,6 +150,8 @@ void ShaderTerrainMesh::do_extract_heightfield() { } _heightfield_tex->set_minfilter(SamplerState::FT_linear); _heightfield_tex->set_magfilter(SamplerState::FT_linear); + _heightfield_tex->set_wrap_u(SamplerState::WM_clamp); + _heightfield_tex->set_wrap_v(SamplerState::WM_clamp); } /** @@ -513,15 +515,15 @@ 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); 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/lmatrix4_src.I b/panda/src/linmath/lmatrix4_src.I index 32250339c3..d5c16d5ce7 100644 --- a/panda/src/linmath/lmatrix4_src.I +++ b/panda/src/linmath/lmatrix4_src.I @@ -1706,3 +1706,19 @@ INLINE_LINMATH int FLOATNAME(UnalignedLMatrix4):: get_num_components() const { return 16; } + +/** + * + */ +INLINE_LINMATH bool FLOATNAME(UnalignedLMatrix4):: +operator == (const FLOATNAME(UnalignedLMatrix4) &other) const { + return memcmp(get_data(), other.get_data(), sizeof(FLOATTYPE) * 16) == 0; +} + +/** + * + */ +INLINE_LINMATH bool FLOATNAME(UnalignedLMatrix4):: +operator != (const FLOATNAME(UnalignedLMatrix4) &other) const { + return !operator == (other); +} diff --git a/panda/src/linmath/lmatrix4_src.h b/panda/src/linmath/lmatrix4_src.h index 0b9f1557e5..de2d2fbf78 100644 --- a/panda/src/linmath/lmatrix4_src.h +++ b/panda/src/linmath/lmatrix4_src.h @@ -345,6 +345,9 @@ PUBLISHED: INLINE_LINMATH const FLOATTYPE *get_data() const; INLINE_LINMATH int get_num_components() const; + INLINE_LINMATH bool operator == (const FLOATNAME(UnalignedLMatrix4) &other) const; + INLINE_LINMATH bool operator != (const FLOATNAME(UnalignedLMatrix4) &other) const; + public: typedef UNALIGNED_LINMATH_MATRIX(FLOATTYPE, 4, 4) UMatrix4; UMatrix4 _m; diff --git a/panda/src/linmath/lquaternion_src.cxx b/panda/src/linmath/lquaternion_src.cxx index 7ffb7766a3..cd48827eea 100644 --- a/panda/src/linmath/lquaternion_src.cxx +++ b/panda/src/linmath/lquaternion_src.cxx @@ -26,6 +26,29 @@ pure_imaginary(const FLOATNAME(LVector3) &v) { return FLOATNAME(LQuaternion)(0, v[0], v[1], v[2]); } +/** + * Returns a new quaternion that represents this quaternion raised to the + * given power. + */ +FLOATNAME(LQuaternion) FLOATNAME(LQuaternion):: +__pow__(FLOATTYPE power) const { + if (IS_NEARLY_ZERO(power)) { + return FLOATNAME(LQuaternion)(1, 0, 0, 0); + } + + FLOATTYPE l = length(); + FLOATTYPE norm = _v(0) / l; + if (IS_NEARLY_EQUAL(cabs(norm), (FLOATTYPE)1)) { + return FLOATNAME(LQuaternion)(cpow(_v(0), power), 0, 0, 0); + } + + FLOATTYPE angle = acos(norm); + FLOATTYPE angle2 = angle * power; + FLOATTYPE mag = cpow(l, power - 1); + FLOATTYPE mult = mag * (sin(angle2) / sin(angle)); + return FLOATNAME(LQuaternion)(cos(angle2) * mag * l, _v(1) * mult, _v(2) * mult, _v(3) * mult); +} + /** * Based on the quat lib from VRPN. */ diff --git a/panda/src/linmath/lquaternion_src.h b/panda/src/linmath/lquaternion_src.h index 429b86a0cc..00ba2a934f 100644 --- a/panda/src/linmath/lquaternion_src.h +++ b/panda/src/linmath/lquaternion_src.h @@ -57,6 +57,8 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LMatrix3) operator *(const FLOATNAME(LMatrix3) &); INLINE_LINMATH FLOATNAME(LMatrix4) operator *(const FLOATNAME(LMatrix4) &); + FLOATNAME(LQuaternion) __pow__(FLOATTYPE) const; + INLINE_LINMATH bool almost_equal( const FLOATNAME(LQuaternion) &other) const; INLINE_LINMATH bool almost_equal( diff --git a/panda/src/linmath/lvecBase4_src.I b/panda/src/linmath/lvecBase4_src.I index 70a376a3bf..bdf7e917b6 100644 --- a/panda/src/linmath/lvecBase4_src.I +++ b/panda/src/linmath/lvecBase4_src.I @@ -903,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); +} + /** * */ @@ -912,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; +} + /** * */ @@ -950,3 +971,22 @@ INLINE_LINMATH const FLOATTYPE *FLOATNAME(UnalignedLVecBase4):: get_data() const { return &_v(0); } + +/** + * + */ +INLINE_LINMATH bool FLOATNAME(UnalignedLVecBase4):: +operator == (const FLOATNAME(UnalignedLVecBase4) &other) const { + return (_v(0) == other._v(0) && + _v(1) == other._v(1) && + _v(2) == other._v(2) && + _v(3) == other._v(3)); +} + +/** + * + */ +INLINE_LINMATH bool FLOATNAME(UnalignedLVecBase4):: +operator != (const FLOATNAME(UnalignedLVecBase4) &other) const { + return !operator == (other); +} diff --git a/panda/src/linmath/lvecBase4_src.h b/panda/src/linmath/lvecBase4_src.h index b6e1a4df35..d04201d988 100644 --- a/panda/src/linmath/lvecBase4_src.h +++ b/panda/src/linmath/lvecBase4_src.h @@ -230,8 +230,10 @@ 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; @@ -241,6 +243,9 @@ PUBLISHED: INLINE_LINMATH const FLOATTYPE *get_data() const; CONSTEXPR static int get_num_components() { return 4; } + INLINE_LINMATH bool operator == (const FLOATNAME(UnalignedLVecBase4) &other) const; + INLINE_LINMATH bool operator != (const FLOATNAME(UnalignedLVecBase4) &other) const; + public: typedef FLOATTYPE numeric_type; typedef UNALIGNED_LINMATH_MATRIX(FLOATTYPE, 1, 4) UVector4; 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/fftCompressor.cxx b/panda/src/mathutil/fftCompressor.cxx index f96c6f5f7c..0a5b7425e1 100644 --- a/panda/src/mathutil/fftCompressor.cxx +++ b/panda/src/mathutil/fftCompressor.cxx @@ -29,18 +29,14 @@ #undef howmany #endif -#ifdef PHAVE_DRFFTW_H - #include "drfftw.h" -#else - #include "rfftw.h" -#endif +#include "fftw3.h" // These FFTW support objects can only be defined if we actually have the FFTW // library available. -static rfftw_plan get_real_compress_plan(int length); -static rfftw_plan get_real_decompress_plan(int length); +static fftw_plan get_real_compress_plan(int length); +static fftw_plan get_real_decompress_plan(int length); -typedef pmap RealPlans; +typedef pmap RealPlans; static RealPlans _real_compress_plans; static RealPlans _real_decompress_plans; @@ -262,20 +258,31 @@ write_reals(Datagram &datagram, const PN_stdfloat *array, int length) { } // Now generate the Fourier transform. - double *data = (double *)alloca(length * sizeof(double)); + int fft_length = length / 2 + 1; + fftw_complex *fft_bins = (fftw_complex *)alloca(fft_length * sizeof(fftw_complex)); + + // This is for an in-place transform. It doesn't violate strict aliasing + // rules because &fft_bins[0][0] is still a double pointer. This saves on + // precious stack space. + double *data = &fft_bins[0][0]; int i; for (i = 0; i < length; i++) { data[i] = array[i]; } - double *half_complex = (double *)alloca(length * sizeof(double)); - - rfftw_plan plan = get_real_compress_plan(length); - rfftw_one(plan, data, half_complex); + // Note: This is an in-place DFT. `data` and `fft_bins` are aliases. + fftw_plan plan = get_real_compress_plan(length); + fftw_execute_dft_r2c(plan, data, fft_bins); // Now encode the numbers, run-length encoded by size, so we only write out // the number of bits we need for each number. + // Note that Panda3D has conventionally always used FFTW2's halfcomplex + // format for serializing the bins. In short, this means that for an n-length + // FFT, it stores: + // 1) The real components for bins 0 through floor(n/2), followed by... + // 2) The imaginary components for bins floor((n+1)/2)-1 through 1. + // (Imaginary component for bin 0 is never stored, as that's always zero.) vector_double run; RunWidth run_width = RW_invalid; @@ -286,8 +293,18 @@ write_reals(Datagram &datagram, const PN_stdfloat *array, int length) { static const double max_range_16 = 32767.0; static const double max_range_8 = 127.0; - double scale_factor = get_scale_factor(i, length); - double num = cfloor(half_complex[i] / scale_factor + 0.5); + int bin; // which FFT bin we're storing + int j; // 0=real; 1=imag + if (i < fft_length) { + bin = i; + j = 0; + } else { + bin = length - i; + j = 1; + } + + double scale_factor = get_scale_factor(bin, fft_length); + double num = cfloor(fft_bins[bin][j] / scale_factor + 0.5); // How many bits do we need to encode this integer? double a = fabs(num); @@ -313,16 +330,30 @@ write_reals(Datagram &datagram, const PN_stdfloat *array, int length) { // across a single intervening zero, don't interrupt the run just for // that. if (run_width == RW_8 && num_width == RW_0) { - if (i + 1 >= length || half_complex[i + 1] != 0.0) { + if (run.back() != 0) { num_width = RW_8; } } if (num_width != run_width) { // Now we need to flush the last run. + + // First, however, take care of the special case above: if we're + // switching from RW_8 to RW_0, there could be a zero at the end, which + // should be reclaimed into the RW_0 run. + bool reclaimed_zero = (run_width == RW_8 && num_width == RW_0 && + run.back() == 0); + if (reclaimed_zero) { + run.pop_back(); + } + num_written += write_run(datagram, run_width, run); run.clear(); run_width = num_width; + + if (reclaimed_zero) { + run.push_back(0); + } } run.push_back(num); @@ -595,14 +626,36 @@ read_reals(DatagramIterator &di, vector_stdfloat &array) { nassertr(num_read == length, false); nassertr((int)half_complex.size() == length, false); + int fft_length = length / 2 + 1; + fftw_complex *fft_bins = (fftw_complex *)alloca(fft_length * sizeof(fftw_complex)); + int i; - for (i = 0; i < length; i++) { - half_complex[i] *= get_scale_factor(i, length); + for (i = 0; i < fft_length; i++) { + double scale_factor = get_scale_factor(i, fft_length); + + // For an explanation of this, see the compression code's comment about the + // halfcomplex format. + + fft_bins[i][0] = half_complex[i] * scale_factor; + if (i == 0) { + // First bin doesn't store imaginary component + fft_bins[i][1] = 0.0; + } else if ((i == fft_length - 1) && !(length & 1)) { + // Last bin doesn't store imaginary component with even lengths + fft_bins[i][1] = 0.0; + } else { + fft_bins[i][1] = half_complex[length - i] * scale_factor; + } } - double *data = (double *)alloca(length * sizeof(double)); - rfftw_plan plan = get_real_decompress_plan(length); - rfftw_one(plan, &half_complex[0], data); + // This is for an in-place transform. It doesn't violate strict aliasing + // rules because &fft_bins[0][0] is still a double pointer. This saves on + // precious stack space. + double *data = &fft_bins[0][0]; + + // Note: This is an in-place DFT. `data` and `fft_bins` are aliases. + fftw_plan plan = get_real_decompress_plan(length); + fftw_execute_dft_c2r(plan, fft_bins, data); double scale = 1.0 / (double)length; array.reserve(array.size() + length); @@ -770,14 +823,14 @@ free_storage() { for (pi = _real_compress_plans.begin(); pi != _real_compress_plans.end(); ++pi) { - rfftw_destroy_plan((*pi).second); + fftw_destroy_plan((*pi).second); } _real_compress_plans.clear(); for (pi = _real_decompress_plans.begin(); pi != _real_decompress_plans.end(); ++pi) { - rfftw_destroy_plan((*pi).second); + fftw_destroy_plan((*pi).second); } _real_decompress_plans.clear(); #endif @@ -933,17 +986,18 @@ read_run(DatagramIterator &di, vector_double &run) { } /** - * Returns the appropriate scaling for the given position within the - * halfcomplex array. + * Returns the appropriate scaling for the given bin in the FFT output. + * + * The scale factor is the value of one integer in the quantized data. As such, + * greater bins (higher, more noticeable frequencies) have *lower* scaling + * factors, which means greater precision. */ double FFTCompressor:: get_scale_factor(int i, int length) const { - int m = (length / 2) + 1; - int k = (i < m) ? i : length - i; - nassertr(k >= 0 && k < m, 1.0); + nassertr(i < length, 1.0); return _fft_offset + - _fft_factor * pow((double)(m-1 - k) / (double)(m-1), _fft_exponent); + _fft_factor * pow((double)(length - i) / (double)(length), _fft_exponent); } /** @@ -997,7 +1051,7 @@ get_compressability(const PN_stdfloat *data, int length) const { * Returns a FFTW plan suitable for compressing a float array of the indicated * length. */ -static rfftw_plan +static fftw_plan get_real_compress_plan(int length) { RealPlans::iterator pi; pi = _real_compress_plans.find(length); @@ -1005,8 +1059,8 @@ get_real_compress_plan(int length) { return (*pi).second; } - rfftw_plan plan; - plan = rfftw_create_plan(length, FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE); + fftw_plan plan; + plan = fftw_plan_dft_r2c_1d(length, NULL, NULL, FFTW_ESTIMATE); _real_compress_plans.insert(RealPlans::value_type(length, plan)); return plan; @@ -1016,7 +1070,7 @@ get_real_compress_plan(int length) { * Returns a FFTW plan suitable for decompressing a float array of the * indicated length. */ -static rfftw_plan +static fftw_plan get_real_decompress_plan(int length) { RealPlans::iterator pi; pi = _real_decompress_plans.find(length); @@ -1024,8 +1078,8 @@ get_real_decompress_plan(int length) { return (*pi).second; } - rfftw_plan plan; - plan = rfftw_create_plan(length, FFTW_COMPLEX_TO_REAL, FFTW_ESTIMATE); + fftw_plan plan; + plan = fftw_plan_dft_c2r_1d(length, NULL, NULL, FFTW_ESTIMATE); _real_decompress_plans.insert(RealPlans::value_type(length, plan)); return plan; 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.cxx b/panda/src/movies/flacAudioCursor.cxx index 839fc8724c..a4c33822cd 100644 --- a/panda/src/movies/flacAudioCursor.cxx +++ b/panda/src/movies/flacAudioCursor.cxx @@ -17,7 +17,6 @@ #include "config_movies.h" #define DR_FLAC_IMPLEMENTATION -#define DR_FLAC_NO_STDIO extern "C" { #include "dr_flac.h" } 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/cftalk/cfChannel.I b/panda/src/movies/opusAudio.I similarity index 83% rename from panda/src/cftalk/cfChannel.I rename to panda/src/movies/opusAudio.I index 4bb6a1c3fa..23f144fe3a 100644 --- a/panda/src/cftalk/cfChannel.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 cfChannel.I - * @author drose - * @date 2009-03-26 + * @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/pipeline/asyncTaskBase.I b/panda/src/movies/opusAudioCursor.I similarity index 82% rename from panda/src/pipeline/asyncTaskBase.I rename to panda/src/movies/opusAudioCursor.I index 09abcb3a3d..1e923c3a80 100644 --- a/panda/src/pipeline/asyncTaskBase.I +++ b/panda/src/movies/opusAudioCursor.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 opusAudioCursor.I + * @author rdb + * @date 2017-05-24 */ 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.cxx b/panda/src/nativenet/socket_address.cxx old mode 100755 new mode 100644 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/connectionManager.cxx b/panda/src/net/connectionManager.cxx index f7fdbe7e3e..8a0bfc1e86 100644 --- a/panda/src/net/connectionManager.cxx +++ b/panda/src/net/connectionManager.cxx @@ -26,7 +26,7 @@ #elif defined(WIN32_VC) || defined(WIN64_VC) #include // For gethostname() #include // For GetAdaptersAddresses() -#elif defined(ANDROID) +#elif defined(__ANDROID__) #include #else #include @@ -538,7 +538,7 @@ scan_interfaces() { PANDA_FREE_ARRAY(addresses); } -#elif defined(ANDROID) +#elif defined(__ANDROID__) // TODO: implementation using netlink_socket? #else // WIN32_VC 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 5f02cefa56..a87853c4bb 100644 --- a/panda/src/ode/odeTriMeshData.cxx +++ b/panda/src/ode/odeTriMeshData.cxx @@ -99,6 +99,7 @@ OdeTriMeshData(const NodePath& model, bool use_normals) : write_faces(odetrimeshdata_cat.debug()); +#ifdef dSINGLE if (!use_normals) { build_single(_vertices, sizeof(StridedVertex), _num_vertices, _faces, _num_faces * 3, sizeof(StridedTri)); @@ -107,6 +108,16 @@ OdeTriMeshData(const NodePath& model, bool use_normals) : _faces, _num_faces * 3, sizeof(StridedTri), _normals); } +#else + if (!use_normals) { + build_double(_vertices, sizeof(StridedVertex), _num_vertices, + _faces, _num_faces * 3, sizeof(StridedTri)); + } else { + build_double1(_vertices, sizeof(StridedVertex), _num_vertices, + _faces, _num_faces * 3, sizeof(StridedTri), + _normals); + } +#endif preprocess(); } diff --git a/panda/src/pandabase/pandasymbols.h b/panda/src/pandabase/pandasymbols.h index 4e7389dadc..cc02ff0e12 100644 --- a/panda/src/pandabase/pandasymbols.h +++ b/panda/src/pandabase/pandasymbols.h @@ -429,7 +429,4 @@ #define EXPCL_PANDA_PANDABASE EXPCL_PANDA #define EXPTP_PANDA_PANDABASE EXPTP_PANDA -#define EXPCL_PANDA_HELIX EXPCL_PANDA -#define EXPTP_PANDA_HELIX EXPTP_PANDA - #endif 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 1f4ab5c2cb..44e588dd89 100644 --- a/panda/src/parametrics/parametricCurveCollection.h +++ b/panda/src/parametrics/parametricCurveCollection.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 c95f9437c4..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. * @@ -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 b6bb5911f4..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()), - (const 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 2aae972296..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 } @@ -33,7 +33,7 @@ CullableObject(CPT(Geom) geom, CPT(RenderState) 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(CPT(Geom) geom, CPT(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,7 @@ 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); } /** 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 aa4a3af3c2..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,16 +32,13 @@ #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(CPT(Geom) geom, CPT(RenderState) state, @@ -52,9 +47,8 @@ public: 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); @@ -75,7 +69,6 @@ public: public: CPT(Geom) _geom; - PT(GeomMunger) _munger; CPT(GeomVertexData) _munged_data; CPT(RenderState) _state; CPT(TransformState) _internal_transform; @@ -115,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; @@ -126,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 7b850fafc6..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. } } 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 04207356bd..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; } } @@ -1439,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..40014c53ee 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,17 @@ 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(); + } + + // This is needed since _sorted_on_lights is not yet populated. + new_attrib->_sort_seq = UpdateSeq::old(); + return return_new(new_attrib); } @@ -861,21 +862,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 +1014,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 +1050,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 +1116,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..71bbb3a561 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,56 @@ get_shader() const { * */ void NodePath:: -set_shader_input(const ShaderInput *inp) { +set_shader_input(const 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:: +void NodePath:: +set_shader_input(ShaderInput &&inp) { + nassertv_always(!is_empty()); + + PandaNode *pnode = node(); + const RenderAttrib *attrib = + pnode->get_attrib(ShaderAttrib::get_class_slot()); + if (attrib != nullptr) { + const ShaderAttrib *sa = (const ShaderAttrib *)attrib; + pnode->set_attrib(sa->set_shader_input(move(inp))); + } else { + // Create a new ShaderAttrib for this node. + CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make()); + pnode->set_attrib(sa->set_shader_input(move(inp))); + } +} + +/** + * + */ +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 +5597,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 +5632,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 +5648,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 +5657,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 +5678,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 +5805,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 +5838,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 6aeeda6d01..c5a3902c32 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -629,10 +629,14 @@ 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(const ShaderInput &input); + 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,18 +658,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); @@ -908,7 +914,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)); @@ -932,9 +942,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 * @@ -1029,6 +1039,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 60cbe19a1c..1c7576290a 100644 --- a/panda/src/pgraph/nodePath_ext.cxx +++ b/panda/src/pgraph/nodePath_ext.cxx @@ -13,6 +13,7 @@ #include "nodePath_ext.h" #include "typedWritable_ext.h" +#include "shaderInput_ext.h" #include "shaderAttrib.h" #ifdef HAVE_PYTHON @@ -25,35 +26,8 @@ extern struct Dtool_PyTypedObject Dtool_LPoint3d; #else extern struct Dtool_PyTypedObject Dtool_LPoint3f; #endif -extern struct Dtool_PyTypedObject Dtool_Texture; extern struct Dtool_PyTypedObject Dtool_NodePath; -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_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; +extern struct Dtool_PyTypedObject Dtool_PandaNode; #endif // CPPPARSER /** @@ -140,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; @@ -157,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; } @@ -170,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; } @@ -217,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"); @@ -233,12 +228,34 @@ 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)); + } } /** @@ -278,153 +295,13 @@ set_shader_inputs(PyObject *args, PyObject *kwargs) { } CPT_InternalName name(string(buffer, length)); - ShaderInput *input = nullptr; - - if (PyTuple_CheckExact(value)) { - // A tuple is interpreted as a vector. - Py_ssize_t size = PyTuple_GET_SIZE(value); - if (size > 4) { - Dtool_Raise_TypeError("NodePath.set_shader_inputs tuple input should not have more than 4 scalars"); - return; - } - // 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)); - } - input = new ShaderInput(name, vec); - } else { - LVecBase4i vec(0); - for (Py_ssize_t i = 0; i < size; ++i) { - vec[i] = (int)PyLong_AsLong(PyTuple_GET_ITEM(value, i)); - } - input = new ShaderInput(name, vec); - } - - } else if (DtoolCanThisBeAPandaInstance(value)) { - Dtool_PyInstDef *inst = (Dtool_PyInstDef *)value; - void *ptr; - - if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_Texture))) { - input = new ShaderInput(name, (Texture *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_NodePath))) { - input = new ShaderInput(name, *(const NodePath *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_float))) { - input = new ShaderInput(name, *(const PTA_float *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_double))) { - input = new ShaderInput(name, *(const PTA_double *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_int))) { - input = new ShaderInput(name, *(const PTA_int *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4f))) { - input = new ShaderInput(name, *(const PTA_LVecBase4f *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3f))) { - input = new ShaderInput(name, *(const PTA_LVecBase3f *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2f))) { - input = new ShaderInput(name, *(const PTA_LVecBase2f *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4f))) { - input = new ShaderInput(name, *(const PTA_LMatrix4f *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3f))) { - input = new ShaderInput(name, *(const PTA_LMatrix3f *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4d))) { - input = new ShaderInput(name, *(const PTA_LVecBase4d *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3d))) { - input = new ShaderInput(name, *(const PTA_LVecBase3d *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2d))) { - input = new ShaderInput(name, *(const PTA_LVecBase2d *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4d))) { - input = new ShaderInput(name, *(const PTA_LMatrix4d *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3d))) { - input = new ShaderInput(name, *(const PTA_LMatrix3d *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4i))) { - input = new ShaderInput(name, *(const PTA_LVecBase4i *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3i))) { - input = new ShaderInput(name, *(const PTA_LVecBase3i *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2i))) { - input = new ShaderInput(name, *(const PTA_LVecBase2i *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4f))) { - input = new ShaderInput(name, *(const LVecBase4f *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3f))) { - input = new ShaderInput(name, *(const LVecBase3f *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2f))) { - input = new ShaderInput(name, *(const LVecBase2f *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4d))) { - input = new ShaderInput(name, *(const LVecBase4d *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3d))) { - input = new ShaderInput(name, *(const LVecBase3d *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2d))) { - input = new ShaderInput(name, *(const LVecBase2d *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4i))) { - input = new ShaderInput(name, *(const LVecBase4i *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3i))) { - input = new ShaderInput(name, *(const LVecBase3i *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2i))) { - input = new ShaderInput(name, *(const LVecBase2i *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ShaderBuffer))) { - input = new ShaderInput(name, (ShaderBuffer *)ptr); - - } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ParamValueBase))) { - input = new ShaderInput(name, (ParamValueBase *)ptr); - - } else { - Dtool_Raise_TypeError("unknown type passed to NodePath.set_shader_inputs"); - return; - } - - } else if (PyFloat_Check(value)) { - input = new ShaderInput(name, LVecBase4(PyFloat_AS_DOUBLE(value), 0, 0, 0)); - -#if PY_MAJOR_VERSION < 3 - } else if (PyInt_Check(value)) { - input = new ShaderInput(name, LVecBase4i((int)PyInt_AS_LONG(value), 0, 0, 0)); -#endif - - } else if (PyLong_Check(value)) { - input = new ShaderInput(name, LVecBase4i((int)PyLong_AsLong(value), 0, 0, 0)); - - } else { - Dtool_Raise_TypeError("unknown type passed to NodePath.set_shader_inputs"); - return; - } - - attrib->_inputs[move(name)] = input; + ShaderInput &input = attrib->_inputs[name]; + invoke_extension(&input).__init__(move(name), value); } - node->set_attrib(ShaderAttrib::return_new(attrib)); + 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 44c78378db..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,14 +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..96b94dd0c2 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); @@ -3811,9 +3798,13 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { int pi = CycleData::complete_pointers(p_list, manager); // Get the state and transform pointers. - _state = DCAST(RenderState, p_list[pi++]); - _transform = DCAST(TransformState, p_list[pi++]); - _prev_transform = _transform; + RenderState *state; + DCAST_INTO_R(state, p_list[pi++], pi); + _state = state; + + TransformState *transform; + DCAST_INTO_R(transform, p_list[pi++], pi); + _prev_transform = _transform = transform; /* * Finalize these pointers now to decrement their artificially-held reference @@ -3830,7 +3821,9 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { // Get the effects pointer. - _effects = DCAST(RenderEffects, p_list[pi++]); + RenderEffects *effects; + DCAST_INTO_R(effects, p_list[pi++], pi); + _effects = effects; /* * Finalize these pointers now to decrement their artificially-held reference @@ -3855,7 +3848,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 +3907,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 e9f3204ccd..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, @@ -827,6 +838,7 @@ private: friend class PandaNodePipelineReader; friend class EggLoader; friend class Extension; + friend class CullTraverserData; }; /** @@ -877,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; @@ -906,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.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..130d85c1b8 100644 --- a/panda/src/pgraph/shaderAttrib.cxx +++ b/panda/src/pgraph/shaderAttrib.cxx @@ -25,6 +25,8 @@ #include "datagram.h" #include "datagramIterator.h" #include "nodePath.h" +#include "paramNodePath.h" +#include "paramTexture.h" #include "shaderBuffer.h" TypeHandle ShaderAttrib::_type_handle; @@ -193,17 +195,32 @@ clear_flag(int flag) const { * */ CPT(RenderAttrib) ShaderAttrib:: -set_shader_input(const ShaderInput *input) const { +set_shader_input(const 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(), input)); } else { i->second = input; } return return_new(result); } +/** + * + */ +CPT(RenderAttrib) ShaderAttrib:: +set_shader_input(ShaderInput &&input) const { + ShaderAttrib *result = new ShaderAttrib(*this); + Inputs::iterator i = result->_inputs.find(input.get_name()); + if (i == result->_inputs.end()) { + result->_inputs.insert(Inputs::value_type(input.get_name(), move(input))); + } else { + i->second = move(input); + } + return return_new(result); +} + /** * Sets the geometry instance count. Do not confuse this with instanceTo, * which is used for animation instancing, and has nothing to do with this. A @@ -248,13 +265,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 +279,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 +292,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 +321,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 +352,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 +378,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 +406,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 +449,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 +487,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 +508,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 +540,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 +648,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 +681,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 +712,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 ebdcca499c..02a3189233 100644 --- a/panda/src/pgraph/shaderAttrib.h +++ b/panda/src/pgraph/shaderAttrib.h @@ -71,8 +71,10 @@ PUBLISHED: CPT(RenderAttrib) clear_shader() const; // Shader Inputs - CPT(RenderAttrib) set_shader_input(const ShaderInput *inp) const; + CPT(RenderAttrib) set_shader_input(const ShaderInput &input) 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; @@ -90,6 +92,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; @@ -104,8 +110,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; @@ -127,7 +133,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: @@ -145,10 +150,13 @@ 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() { @@ -157,6 +165,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..ee3c5a8165 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. */ @@ -830,6 +825,7 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { } } _on_stages.sort(); + _off_stages.sort(); _sort_seq = UpdateSeq::old(); _filtered_seq = UpdateSeq::old(); 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/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 f0d84ffb9a..9d198fac6f 100644 --- a/panda/src/pgraphnodes/directionalLight.cxx +++ b/panda/src/pgraphnodes/directionalLight.cxx @@ -56,9 +56,7 @@ 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); } @@ -69,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..1df14eb971 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; } @@ -32,6 +41,9 @@ set_shadow_caster(bool caster) { } _shadow_caster = caster; set_active(caster); + if (caster) { + setup_shadow_map(); + } } /** @@ -56,6 +68,17 @@ set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int buffer_so _sb_sort = buffer_sort; } set_active(caster); + if (caster) { + setup_shadow_map(); + } +} + +/** + * Returns the sort of the shadow buffer to be created for this light source. + */ +INLINE int LightLensNode:: +get_shadow_buffer_sort() const { + return _sb_sort; } /** @@ -73,8 +96,9 @@ INLINE void LightLensNode:: set_shadow_buffer_size(const LVecBase2i &size) { if (size != _sb_size) { clear_shadow_buffers(); + _sb_size = size; + setup_shadow_map(); } - _sb_size = size; } /** diff --git a/panda/src/pgraphnodes/lightLensNode.cxx b/panda/src/pgraphnodes/lightLensNode.cxx index bc3e134116..b79b5e8dc0 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,8 +63,13 @@ 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) { + if (_shadow_caster) { + setup_shadow_map(); + } } /** @@ -67,20 +78,65 @@ LightLensNode(const LightLensNode ©) : */ void LightLensNode:: clear_shadow_buffers() { + if (_shadow_map) { + // Clear it to all ones, so that any shaders that might still be using + // it will see the shadows being disabled. + _shadow_map->clear_image(); + } + ShadowBuffers::iterator it; for(it = _sbuffers.begin(); it != _sbuffers.end(); ++it) { - PT(Texture) tex = (*it).second->get_texture(); - if (tex) { - // Clear it to all ones, so that any shaders that might still be using - // it will see the shadows being disabled. - tex->set_clear_color(LColor(1)); - tex->clear_image(); - } (*it).first->remove_window((*it).second); } _sbuffers.clear(); } +/** + * Creates the shadow map texture. Can be overridden. + */ +void LightLensNode:: +setup_shadow_map() { + if (_shadow_map != nullptr && + _shadow_map->get_x_size() == _sb_size[0] && + _shadow_map->get_y_size() == _sb_size[1]) { + // Nothing to do. + return; + } + + if (_shadow_map == nullptr) { + _shadow_map = new Texture(get_name()); + } + + _shadow_map->setup_2d_texture(_sb_size[0], _sb_size[1], Texture::T_unsigned_byte, Texture::F_depth_component); + _shadow_map->set_clear_color(LColor(1)); + _shadow_map->set_wrap_u(SamplerState::WM_border_color); + _shadow_map->set_wrap_v(SamplerState::WM_border_color); + _shadow_map->set_border_color(LColor(1)); + _shadow_map->set_minfilter(SamplerState::FT_shadow); + _shadow_map->set_magfilter(SamplerState::FT_shadow); +} + +/** + * 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..03c1342927 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,13 +32,17 @@ 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); + INLINE int get_shadow_buffer_sort() const; + INLINE LVecBase2i get_shadow_buffer_size() const; INLINE void set_shadow_buffer_size(const LVecBase2i &size); @@ -50,16 +55,27 @@ PUBLISHED: protected: LightLensNode(const LightLensNode ©); void clear_shadow_buffers(); + virtual void setup_shadow_map(); LVecBase2i _sb_size; bool _shadow_caster; + bool _has_specular_color; int _sb_sort; + PT(Texture) _shadow_map; + // 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(); @@ -95,7 +111,6 @@ private: static TypeHandle _type_handle; friend class GraphicsStateGuardian; - friend class ShaderGenerator; }; INLINE ostream &operator << (ostream &out, const LightLensNode &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 d3f7192248..02cc33fda9 100644 --- a/panda/src/pgraphnodes/pointLight.cxx +++ b/panda/src/pgraphnodes/pointLight.cxx @@ -17,6 +17,7 @@ #include "bamReader.h" #include "datagram.h" #include "datagramIterator.h" +#include "config_pgraphnodes.h" TypeHandle PointLight::_type_handle; @@ -61,9 +62,7 @@ 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); @@ -98,7 +97,6 @@ PointLight(const string &name) : PointLight:: PointLight(const PointLight ©) : LightLensNode(copy), - _has_specular_color(copy._has_specular_color), _cycler(copy._cycler) { } @@ -187,6 +185,35 @@ bind(GraphicsStateGuardianBase *gsg, const NodePath &light, int light_id) { gsg->bind_light(this, light, light_id); } +/** + * Creates the shadow map texture. Can be overridden. + */ +void PointLight:: +setup_shadow_map() { + if (_shadow_map != nullptr && _shadow_map->get_x_size() == _sb_size[0]) { + // Nothing to do. + return; + } + + if (_sb_size[0] != _sb_size[1]) { + pgraphnodes_cat.error() + << "PointLight shadow buffers must have an equal width and height!\n"; + } + + if (_shadow_map == nullptr) { + _shadow_map = new Texture(get_name()); + } + + _shadow_map->setup_cube_map(_sb_size[0], Texture::T_unsigned_byte, Texture::F_depth_component); + _shadow_map->set_clear_color(LColor(1)); + _shadow_map->set_wrap_u(SamplerState::WM_clamp); + _shadow_map->set_wrap_v(SamplerState::WM_clamp); + + // Note: cube map shadow filtering doesn't seem to work in Cg. + _shadow_map->set_minfilter(SamplerState::FT_linear); + _shadow_map->set_magfilter(SamplerState::FT_linear); +} + /** * Tells the BamReader how to create objects of type PointLight. */ diff --git a/panda/src/pgraphnodes/pointLight.h b/panda/src/pgraphnodes/pointLight.h index c0907dba01..ba3a334d47 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,7 +63,7 @@ public: int light_id); private: - bool _has_specular_color; + virtual void setup_shadow_map(); // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA_PGRAPHNODES CData : public CycleData { 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..f741b02db8 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,415 @@ 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? + bool skip = false; + switch (info._mode) { + case TextureStage::M_normal: + if (!shader_attrib->auto_normal_on() || + (!key._lighting && (key._outputs & AuxBitplaneAttrib::ABO_aux_normal) == 0)) { + skip = true; + } 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 { + skip = true; + } + break; + case TextureStage::M_gloss: + if (key._lighting && shader_attrib->auto_gloss_on()) { + info._flags = ShaderKey::TF_map_gloss; + } else { + skip = true; + } + break; + case TextureStage::M_height: + if (parallax_mapping_samples > 0) { + info._flags = ShaderKey::TF_map_height; + } else { + skip = true; + } + break; } - if ((mode == TextureStage::M_glow)||(mode == TextureStage::M_modulate_glow)) { - _map_index_glow = i; + // We can't just drop a disabled slot from the list, since then the + // indices for the texture stages will no longer match up. So we keep it, + // but set it to a noop state to indicate that it should be skipped. + if (skip) { + info._type = Texture::TT_1d_texture; + info._mode = TextureStage::M_modulate; + info._flags = 0; + key._textures.push_back(info); + continue; } - 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 +610,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 +691,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 +708,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 +862,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 +910,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 +936,153 @@ 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]; + if (tex._mode == TextureStage::M_modulate && tex._flags == 0) { + // Skip this stage. + continue; + } + + 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 { - // 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"; + for (size_t i = 0; i < key._textures.size(); ++i) { + const ShaderKey::TextureInfo &tex = key._textures[i]; + if (tex._mode == TextureStage::M_modulate && tex._flags == 0) { + // Skip this stage. + continue; } - if (tex_matrix != NULL && tex_matrix->has_stage(stage)) { + switch (tex._gen_mode) { + case TexGenAttrib::M_off: + // Cg seems to be able to optimize this temporary away when appropriate. + 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._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 +1097,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._mode == TextureStage::M_modulate && tex._flags == 0) { + // Skip this stage. + continue; + } + 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 +1149,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 +1269,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 +1288,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 +1311,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 +1326,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 +1343,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 +1442,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, true, 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 +1525,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 +1569,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 +1587,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 +1674,30 @@ 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_undefined: 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; @@ -1573,19 +1710,16 @@ combine_source_as_string(CPT(TextureStage) stage, short num, bool alpha, bool si case TextureStage::CS_last_saved_result: csource << "last_saved_result"; break; - 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 +1730,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 +1754,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 a1d83f0919..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,67 +85,94 @@ 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() { 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 badf4544d9..99432a904b 100644 --- a/panda/src/pgraphnodes/spotlight.cxx +++ b/panda/src/pgraphnodes/spotlight.cxx @@ -65,9 +65,7 @@ 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); } @@ -78,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..daa454aff1 100644 --- a/panda/src/physics/physicalNode.cxx +++ b/panda/src/physics/physicalNode.cxx @@ -12,6 +12,7 @@ */ #include "physicalNode.h" +#include "physicsManager.h" // static stuff. TypeHandle PhysicalNode::_type_handle; @@ -38,6 +39,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 +78,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 +106,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/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/cycleData.I b/panda/src/pipeline/cycleData.I index 53c0224eab..b66184d145 100644 --- a/panda/src/pipeline/cycleData.I +++ b/panda/src/pipeline/cycleData.I @@ -10,10 +10,3 @@ * @author drose * @date 2002-02-21 */ - -/** - * - */ -INLINE CycleData:: -CycleData() { -} diff --git a/panda/src/pipeline/cycleData.h b/panda/src/pipeline/cycleData.h index 4a020c9940..72ba074088 100644 --- a/panda/src/pipeline/cycleData.h +++ b/panda/src/pipeline/cycleData.h @@ -49,7 +49,9 @@ class EXPCL_PANDA_PIPELINE CycleData #endif // DO_PIPELINING { public: - INLINE CycleData(); + INLINE CycleData() DEFAULT_CTOR; + INLINE CycleData(CycleData &&from) DEFAULT_CTOR; + INLINE CycleData(const CycleData ©) DEFAULT_CTOR; virtual ~CycleData(); virtual CycleData *make_copy() const=0; 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/pipeline.I b/panda/src/pipeline/pipeline.I index 8eedc7b363..9d3512ae6c 100644 --- a/panda/src/pipeline/pipeline.I +++ b/panda/src/pipeline/pipeline.I @@ -45,7 +45,7 @@ get_num_stages() const { */ INLINE int Pipeline:: get_num_cyclers() const { - ReMutexHolder holder(_lock); + MutexHolder holder(_lock); return _num_cyclers; } #endif // THREADED_PIPELINE @@ -58,7 +58,7 @@ get_num_cyclers() const { */ INLINE int Pipeline:: get_num_dirty_cyclers() const { - ReMutexHolder holder(_lock); + MutexHolder holder(_lock); return _num_dirty_cyclers; } #endif // THREADED_PIPELINE diff --git a/panda/src/pipeline/pipeline.cxx b/panda/src/pipeline/pipeline.cxx index bcd555f003..e6fd1b6cee 100644 --- a/panda/src/pipeline/pipeline.cxx +++ b/panda/src/pipeline/pipeline.cxx @@ -13,7 +13,6 @@ #include "pipeline.h" #include "pipelineCyclerTrueImpl.h" -#include "reMutexHolder.h" #include "configVariableInt.h" #include "config_pipeline.h" @@ -27,7 +26,9 @@ Pipeline(const string &name, int num_stages) : Namable(name), #ifdef THREADED_PIPELINE _num_stages(num_stages), - _lock("Pipeline") + _cycle_lock("Pipeline cycle"), + _lock("Pipeline"), + _next_cycle_seq(1) #else _num_stages(1) #endif @@ -91,87 +92,166 @@ cycle() { } pvector< PT(CycleData) > saved_cdatas; - saved_cdatas.reserve(_num_dirty_cyclers); { - ReMutexHolder holder(_lock); - if (_num_stages == 1) { - // No need to cycle if there's only one stage. - nassertv(_dirty._next == &_dirty); - return; + ReMutexHolder cycle_holder(_cycle_lock); + int prev_seq, next_seq; + PipelineCyclerLinks prev_dirty; + { + // We can't hold the lock protecting the linked lists during the cycling + // itself, since it could cause a deadlock. + MutexHolder holder(_lock); + if (_num_stages == 1) { + // No need to cycle if there's only one stage. + nassertv(_dirty._next == &_dirty); + return; + } + + nassertv(!_cycling); + _cycling = true; + + // Increment the cycle sequence number, which is used by this method to + // communicate with remove_cycler() about the status of dirty cyclers. + prev_seq = next_seq = _next_cycle_seq; + if (++next_seq == 0) { + // Skip 0, which is a reserved number used to indicate a clean cycler. + ++next_seq; + } + _next_cycle_seq = next_seq; + + // Move the dirty list to prev_dirty, for processing. + prev_dirty.make_head(); + prev_dirty.take_list(_dirty); + + saved_cdatas.reserve(_num_dirty_cyclers); + _num_dirty_cyclers = 0; } - nassertv(!_cycling); - _cycling = true; - - // Move the dirty list to prev_dirty, for processing. - PipelineCyclerLinks prev_dirty; - prev_dirty.make_head(); - prev_dirty.take_list(_dirty); - _num_dirty_cyclers = 0; - + // This is duplicated for different number of stages, as an optimization. switch (_num_stages) { case 2: while (prev_dirty._next != &prev_dirty) { - PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next; - cycler->remove_from_list(); - ReMutexHolder holder2(cycler->_lock); + PipelineCyclerLinks *link = prev_dirty._next; + while (link != &prev_dirty) { + PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)link; - // We save the result of cycle(), so that we can defer the side- - // effects that might occur when CycleDatas destruct, at least until - // the end of this loop. - saved_cdatas.push_back(cycler->cycle_2()); + if (!cycler->_lock.try_acquire()) { + // No big deal, just move on to the next one for now, and we'll + // come back around to it. It's important not to block here in + // order to prevent one cycler from deadlocking another. + if (link->_prev != &prev_dirty || link->_next != &prev_dirty) { + link = cycler->_next; + continue; + } else { + // Well, we are the last cycler left, so we might as well wait. + // This is necessary to trigger the deadlock detection code. + cycler->_lock.acquire(); + } + } - if (cycler->_dirty) { - // The cycler is still dirty after cycling. Keep it on the dirty - // list for next time. - cycler->insert_before(&_dirty); - ++_num_dirty_cyclers; - } else { - // The cycler is now clean. Add it back to the clean list. + MutexHolder holder(_lock); + cycler->remove_from_list(); + + // We save the result of cycle(), so that we can defer the side- + // effects that might occur when CycleDatas destruct, at least until + // the end of this loop. + saved_cdatas.push_back(cycler->cycle_2()); + + // cycle_2() won't leave a cycler dirty. Add it to the clean list. + nassertd(!cycler->_dirty) break; cycler->insert_before(&_clean); #ifdef DEBUG_THREADS inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); #endif + cycler->_lock.release(); + break; } } break; case 3: while (prev_dirty._next != &prev_dirty) { - PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next; - cycler->remove_from_list(); - ReMutexHolder holder2(cycler->_lock); + PipelineCyclerLinks *link = prev_dirty._next; + while (link != &prev_dirty) { + PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)link; - saved_cdatas.push_back(cycler->cycle_3()); + if (!cycler->_lock.try_acquire()) { + // No big deal, just move on to the next one for now, and we'll + // come back around to it. It's important not to block here in + // order to prevent one cycler from deadlocking another. + if (link->_prev != &prev_dirty || link->_next != &prev_dirty) { + link = cycler->_next; + continue; + } else { + // Well, we are the last cycler left, so we might as well wait. + // This is necessary to trigger the deadlock detection code. + cycler->_lock.acquire(); + } + } - if (cycler->_dirty) { - cycler->insert_before(&_dirty); - ++_num_dirty_cyclers; - } else { - cycler->insert_before(&_clean); + MutexHolder holder(_lock); + cycler->remove_from_list(); + + saved_cdatas.push_back(cycler->cycle_3()); + + if (cycler->_dirty) { + // The cycler is still dirty. Add it back to the dirty list. + nassertd(cycler->_dirty == prev_seq) break; + cycler->insert_before(&_dirty); + cycler->_dirty = next_seq; + ++_num_dirty_cyclers; + } else { + // The cycler is now clean. Add it back to the clean list. + cycler->insert_before(&_clean); #ifdef DEBUG_THREADS - inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); + inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); #endif + } + cycler->_lock.release(); + break; } } break; default: while (prev_dirty._next != &prev_dirty) { - PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next; - cycler->remove_from_list(); - ReMutexHolder holder2(cycler->_lock); + PipelineCyclerLinks *link = prev_dirty._next; + while (link != &prev_dirty) { + PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)link; - saved_cdatas.push_back(cycler->cycle()); + if (!cycler->_lock.try_acquire()) { + // No big deal, just move on to the next one for now, and we'll + // come back around to it. It's important not to block here in + // order to prevent one cycler from deadlocking another. + if (link->_prev != &prev_dirty || link->_next != &prev_dirty) { + link = cycler->_next; + continue; + } else { + // Well, we are the last cycler left, so we might as well wait. + // This is necessary to trigger the deadlock detection code. + cycler->_lock.acquire(); + } + } - if (cycler->_dirty) { - cycler->insert_before(&_dirty); - ++_num_dirty_cyclers; - } else { - cycler->insert_before(&_clean); + MutexHolder holder(_lock); + cycler->remove_from_list(); + + saved_cdatas.push_back(cycler->cycle()); + + if (cycler->_dirty) { + // The cycler is still dirty. Add it back to the dirty list. + nassertd(cycler->_dirty == prev_seq) break; + cycler->insert_before(&_dirty); + cycler->_dirty = next_seq; + ++_num_dirty_cyclers; + } else { + // The cycler is now clean. Add it back to the clean list. + cycler->insert_before(&_clean); #ifdef DEBUG_THREADS - inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); + inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); #endif + } + cycler->_lock.release(); + break; } } break; @@ -203,7 +283,9 @@ void Pipeline:: set_num_stages(int num_stages) { nassertv(num_stages >= 1); #ifdef THREADED_PIPELINE - ReMutexHolder holder(_lock); + // Make sure it's not currently cycling. + ReMutexHolder cycle_holder(_cycle_lock); + MutexHolder holder(_lock); if (num_stages != _num_stages) { // We need to lock every PipelineCycler object attached to this pipeline @@ -261,9 +343,10 @@ set_num_stages(int num_stages) { */ void Pipeline:: add_cycler(PipelineCyclerTrueImpl *cycler) { - ReMutexHolder holder(_lock); + // It's safe to add it to the list while cycling, since the _clean list is + // not touched during the cycle loop. + MutexHolder holder(_lock); nassertv(!cycler->_dirty); - nassertv(!_cycling); cycler->insert_before(&_clean); ++_num_cyclers; @@ -285,15 +368,16 @@ void Pipeline:: add_dirty_cycler(PipelineCyclerTrueImpl *cycler) { nassertv(cycler->_lock.debug_is_locked()); - ReMutexHolder holder(_lock); - nassertv(_num_stages != 1); - nassertv(!_cycling); + // It's safe to add it to the list while cycling, since it's not currently + // on the dirty list. + MutexHolder holder(_lock); nassertv(!cycler->_dirty); + nassertv(_num_stages != 1); // Remove it from the "clean" list and add it to the "dirty" list. cycler->remove_from_list(); cycler->insert_before(&_dirty); - cycler->_dirty = true; + cycler->_dirty = _next_cycle_seq; ++_num_dirty_cyclers; #ifdef DEBUG_THREADS @@ -311,9 +395,42 @@ void Pipeline:: remove_cycler(PipelineCyclerTrueImpl *cycler) { nassertv(cycler->_lock.debug_is_locked()); - ReMutexHolder holder(_lock); - nassertv(!_cycling); + MutexHolder holder(_lock); + // If it's dirty, it may currently be processed by cycle(), so we need to be + // careful not to cause a race condition. It's safe for us to remove it + // during cycle only if it's 0 (clean) or _next_cycle_seq (scheduled for the + // next cycle, so not owned by the current one). + while (cycler->_dirty != 0 && cycler->_dirty != _next_cycle_seq) { + if (_cycle_lock.try_acquire()) { + // OK, great, we got the lock, so it finished cycling already. + nassertv(!_cycling); + + --_num_cyclers; + cycler->remove_from_list(); + + cycler->_dirty = false; + --_num_dirty_cyclers; + + #ifdef DEBUG_THREADS + inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), -1); + inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); + #endif + + _cycle_lock.release(); + return; + } else { + // It's possibly currently being cycled. We will wait for the cycler + // to be done with it, so that we can safely remove it. + _lock.release(); + cycler->_lock.release(); + Thread::force_yield(); + cycler->_lock.acquire(); + _lock.acquire(); + } + } + + // It's not being owned by a cycle operation, so it's fair game. --_num_cyclers; cycler->remove_from_list(); @@ -322,7 +439,7 @@ remove_cycler(PipelineCyclerTrueImpl *cycler) { #endif if (cycler->_dirty) { - cycler->_dirty = false; + cycler->_dirty = 0; --_num_dirty_cyclers; #ifdef DEBUG_THREADS inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); @@ -341,7 +458,9 @@ remove_cycler(PipelineCyclerTrueImpl *cycler) { */ void Pipeline:: iterate_all_cycler_types(CallbackFunc *func, void *data) const { - ReMutexHolder holder(_lock); + // Make sure it's not currently cycling. + ReMutexHolder cycle_holder(_cycle_lock); + MutexHolder holder(_lock); TypeCount::const_iterator ci; for (ci = _all_cycler_types.begin(); ci != _all_cycler_types.end(); ++ci) { func((*ci).first, (*ci).second, data); @@ -356,7 +475,9 @@ iterate_all_cycler_types(CallbackFunc *func, void *data) const { */ void Pipeline:: iterate_dirty_cycler_types(CallbackFunc *func, void *data) const { - ReMutexHolder holder(_lock); + // Make sure it's not currently cycling. + ReMutexHolder cycle_holder(_cycle_lock); + MutexHolder holder(_lock); TypeCount::const_iterator ci; for (ci = _dirty_cycler_types.begin(); ci != _dirty_cycler_types.end(); ++ci) { func((*ci).first, (*ci).second, data); diff --git a/panda/src/pipeline/pipeline.h b/panda/src/pipeline/pipeline.h index 32e9cafad7..1dbba9429f 100644 --- a/panda/src/pipeline/pipeline.h +++ b/panda/src/pipeline/pipeline.h @@ -18,6 +18,8 @@ #include "pipelineCyclerLinks.h" #include "namable.h" #include "pset.h" +#include "pmutex.h" +#include "mutexHolder.h" #include "reMutex.h" #include "reMutexHolder.h" #include "selectThreadImpl.h" // for THREADED_PIPELINE definition @@ -85,7 +87,16 @@ private: // This is true only during cycle(). bool _cycling; - ReMutex _lock; + // This increases with every cycle run. If the _dirty field of a cycler is + // set to the same value as this, it indicates that it is scheduled for the + // next cycle. + unsigned int _next_cycle_seq; + + // This lock is always held during cycle(). + ReMutex _cycle_lock; + + // This lock protects the data stored on this Pipeline. + Mutex _lock; #endif // THREADED_PIPELINE }; diff --git a/panda/src/pipeline/pipelineCycler.I b/panda/src/pipeline/pipelineCycler.I index 8efce5450a..06c6c1e5dc 100644 --- a/panda/src/pipeline/pipelineCycler.I +++ b/panda/src/pipeline/pipelineCycler.I @@ -25,6 +25,16 @@ PipelineCycler(Pipeline *pipeline) : { } +/** + * + */ +template +INLINE PipelineCycler:: +PipelineCycler(CycleDataType &&initial_data, Pipeline *pipeline) : + PipelineCyclerBase(new CycleDataType(move(initial_data)), pipeline) +{ +} + /** * */ @@ -182,6 +192,17 @@ PipelineCycler(Pipeline *pipeline) : { } +/** + * + */ +template +INLINE PipelineCycler:: +PipelineCycler(CycleDataType &&initial_data, Pipeline *pipeline) : + _typed_data(move(initial_data)), + PipelineCyclerBase(&_typed_data, pipeline) +{ +} + /** * */ diff --git a/panda/src/pipeline/pipelineCycler.h b/panda/src/pipeline/pipelineCycler.h index f87475c192..adc7abd1a8 100644 --- a/panda/src/pipeline/pipelineCycler.h +++ b/panda/src/pipeline/pipelineCycler.h @@ -45,7 +45,9 @@ template struct PipelineCycler : public PipelineCyclerBase { public: - INLINE PipelineCycler(Pipeline *pipeline = NULL); + INLINE PipelineCycler(Pipeline *pipeline = nullptr); + INLINE PipelineCycler(CycleDataType &&initial_data, Pipeline *pipeline = nullptr); + INLINE PipelineCycler(const PipelineCycler ©); INLINE void operator = (const PipelineCycler ©); diff --git a/panda/src/pipeline/pipelineCyclerTrueImpl.I b/panda/src/pipeline/pipelineCyclerTrueImpl.I index 2c393d0546..fa6a97499b 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(); @@ -383,7 +393,7 @@ cycle_2() { _data[1]._cdata = _data[0]._cdata; // No longer dirty. - _dirty = false; + _dirty = 0; return last_val; } @@ -413,7 +423,7 @@ cycle_3() { if (_data[2]._cdata == _data[1]._cdata) { // No longer dirty. - _dirty = false; + _dirty = 0; } return last_val; diff --git a/panda/src/pipeline/pipelineCyclerTrueImpl.cxx b/panda/src/pipeline/pipelineCyclerTrueImpl.cxx index b1466ce155..f943a030f8 100644 --- a/panda/src/pipeline/pipelineCyclerTrueImpl.cxx +++ b/panda/src/pipeline/pipelineCyclerTrueImpl.cxx @@ -24,7 +24,7 @@ PipelineCyclerTrueImpl:: PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) : _pipeline(pipeline), - _dirty(false), + _dirty(0), _lock(this) { if (_pipeline == (Pipeline *)NULL) { @@ -46,7 +46,7 @@ PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) : PipelineCyclerTrueImpl:: PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl ©) : _pipeline(copy._pipeline), - _dirty(false), + _dirty(0), _lock(this) { ReMutexHolder holder(_lock); @@ -278,7 +278,7 @@ cycle() { } // No longer dirty. - _dirty = false; + _dirty = 0; return last_val; } diff --git a/panda/src/pipeline/pipelineCyclerTrueImpl.h b/panda/src/pipeline/pipelineCyclerTrueImpl.h index 3aa7ffc232..59896c8b18 100644 --- a/panda/src/pipeline/pipelineCyclerTrueImpl.h +++ b/panda/src/pipeline/pipelineCyclerTrueImpl.h @@ -122,7 +122,10 @@ private: }; CycleDataNode *_data; int _num_stages; - bool _dirty; + + // This is 0 if it's clean, or set to Pipeline::_next_cycle_seq if it's + // scheduled to be cycled during the next cycle() call. + unsigned int _dirty; CyclerMutex _lock; 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 b6ae48469a..10d7b6e44f 100644 --- a/panda/src/pipeline/thread.I +++ b/panda/src/pipeline/thread.I @@ -78,7 +78,7 @@ get_pipeline_stage() const { // 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); + __builtin_assume(pipeline_stage >= 0); return pipeline_stage; #else return _pipeline_stage; @@ -277,9 +277,9 @@ preempt() { * AsyncTaskManager), if any, or NULL if the thread is not currently servicing * a task. */ -INLINE AsyncTaskBase *Thread:: +INLINE TypedReferenceCount *Thread:: get_current_task() const { - return _current_task; + return (TypedReferenceCount *)_current_task; } /** @@ -300,6 +300,17 @@ prepare_for_exit() { ThreadImpl::prepare_for_exit(); } +#ifdef ANDROID +/** + * Enables interaction with the Java VM on Android. Returns null if the + * thread is not attached to the Java VM (or bind_thread was not called). + */ +INLINE JNIEnv *Thread:: +get_jni_env() const { + return _impl.get_jni_env(); +} +#endif + /** * Stores a PStats index to be associated with this thread. This is used * internally by the PStatClient; you should not need to call this directly. diff --git a/panda/src/pipeline/thread.h b/panda/src/pipeline/thread.h index 2240174f02..730deb5872 100644 --- a/panda/src/pipeline/thread.h +++ b/panda/src/pipeline/thread.h @@ -23,12 +23,16 @@ #include "pnotify.h" #include "config_pipeline.h" +#ifdef ANDROID +typedef struct _JNIEnv JNIEnv; +#endif + class Mutex; 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 +93,7 @@ PUBLISHED: BLOCKING INLINE void join(); INLINE void preempt(); - INLINE AsyncTaskBase *get_current_task() const; + INLINE TypedReferenceCount *get_current_task() const; INLINE void set_python_index(int index); @@ -100,6 +104,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); @@ -118,6 +132,10 @@ public: INLINE void set_pstats_callback(PStatsCallback *pstats_callback); INLINE PStatsCallback *get_pstats_callback() const; +#ifdef ANDROID + INLINE JNIEnv *get_jni_env() const; +#endif + private: static void init_main_thread(); static void init_external_thread(); @@ -132,7 +150,7 @@ private: int _pipeline_stage; PStatsCallback *_pstats_callback; bool _joinable; - AsyncTaskBase *_current_task; + AtomicAdjust::Pointer _current_task; int _python_index; @@ -174,7 +192,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/pipeline/threadPosixImpl.I b/panda/src/pipeline/threadPosixImpl.I index 93bdce8e8c..62b6026061 100644 --- a/panda/src/pipeline/threadPosixImpl.I +++ b/panda/src/pipeline/threadPosixImpl.I @@ -21,6 +21,9 @@ ThreadPosixImpl(Thread *parent_obj) : _joinable = false; _detached = false; _status = S_new; +#ifdef ANDROID + _jni_env = nullptr; +#endif } /** @@ -60,6 +63,9 @@ bind_thread(Thread *thread) { } int result = pthread_setspecific(_pt_ptr_index, thread); nassertv(result == 0); +#ifdef ANDROID + bind_java_thread(); +#endif } /** @@ -112,3 +118,13 @@ yield() { INLINE void ThreadPosixImpl:: consider_yield() { } + +#ifdef ANDROID +/** + * Returns the JNIEnv object for the current thread. + */ +INLINE JNIEnv *ThreadPosixImpl:: +get_jni_env() const { + return _jni_env; +} +#endif diff --git a/panda/src/pipeline/threadPosixImpl.cxx b/panda/src/pipeline/threadPosixImpl.cxx index 54b3dfd630..5104c67926 100644 --- a/panda/src/pipeline/threadPosixImpl.cxx +++ b/panda/src/pipeline/threadPosixImpl.cxx @@ -24,6 +24,8 @@ #ifdef ANDROID #include "config_express.h" #include + +static JavaVM *java_vm = nullptr; #endif pthread_key_t ThreadPosixImpl::_pt_ptr_index = 0; @@ -183,6 +185,53 @@ get_unique_id() const { return strm.str(); } +#ifdef ANDROID +/** + * Attaches the thread to the Java virtual machine. If this returns true, a + * JNIEnv pointer can be acquired using get_jni_env(). + */ +bool ThreadPosixImpl:: +attach_java_vm() { + JNIEnv *env; + string thread_name = _parent_obj->get_name(); + JavaVMAttachArgs args; + args.version = JNI_VERSION_1_2; + args.name = thread_name.c_str(); + args.group = nullptr; + if (java_vm->AttachCurrentThread(&env, &args) != 0) { + thread_cat.error() + << "Failed to attach Java VM to thread " + << _parent_obj->get_name() << "!\n"; + _jni_env = nullptr; + return false; + } + _jni_env = env; + return true; +} + +/** + * Binds the Panda thread to the current thread, assuming that the current + * thread is already a valid attached Java thread. Called by JNI_OnLoad. + */ +void ThreadPosixImpl:: +bind_java_thread() { + Thread *thread = Thread::get_current_thread(); + nassertv(thread != nullptr); + + // Get the JNIEnv for this Java thread, and store it on the corresponding + // Panda thread object. + JNIEnv *env; + if (java_vm->GetEnv((void **)&env, JNI_VERSION_1_4) == JNI_OK) { + nassertv(thread->_impl._jni_env == nullptr || thread->_impl._jni_env == env); + thread->_impl._jni_env = env; + } else { + thread_cat->error() + << "Called bind_java_thread() on thread " + << *thread << ", which is not attached to Java VM!\n"; + } +} +#endif // ANDROID + /** * The entry point of each thread. */ @@ -209,14 +258,7 @@ root_func(void *data) { #ifdef ANDROID // Attach the Java VM to allow calling Java functions in this thread. - JavaVM *jvm = get_java_vm(); - JNIEnv *env; - if (jvm == NULL || jvm->AttachCurrentThread(&env, NULL) != 0) { - thread_cat.error() - << "Failed to attach Java VM to thread " - << self->_parent_obj->get_name() << "!\n"; - env = NULL; - } + self->attach_java_vm(); #endif self->_parent_obj->thread_main(); @@ -238,8 +280,10 @@ root_func(void *data) { } #ifdef ANDROID - if (env != NULL) { - jvm->DetachCurrentThread(); + // We cannot let the thread end without detaching it. + if (self->_jni_env != nullptr) { + java_vm->DetachCurrentThread(); + self->_jni_env = nullptr; } #endif @@ -276,4 +320,17 @@ init_pt_ptr_index() { nassertv(result == 0); } +#ifdef ANDROID +/** + * Called by Java when loading this library from the Java virtual machine. + */ +jint JNI_OnLoad(JavaVM *jvm, void *reserved) { + // Store the JVM pointer globally. + java_vm = jvm; + + ThreadPosixImpl::bind_java_thread(); + return JNI_VERSION_1_4; +} +#endif // ANDROID + #endif // THREAD_POSIX_IMPL diff --git a/panda/src/pipeline/threadPosixImpl.h b/panda/src/pipeline/threadPosixImpl.h index 0168c38665..ee279c98dd 100644 --- a/panda/src/pipeline/threadPosixImpl.h +++ b/panda/src/pipeline/threadPosixImpl.h @@ -25,6 +25,10 @@ #include +#ifdef ANDROID +typedef struct _JNIEnv JNIEnv; +#endif + class Thread; /** @@ -53,6 +57,12 @@ public: INLINE static void yield(); INLINE static void consider_yield(); +#ifdef ANDROID + INLINE JNIEnv *get_jni_env() const; + bool attach_java_vm(); + static void bind_java_thread(); +#endif + private: static void *root_func(void *data); static void init_pt_ptr_index(); @@ -72,6 +82,10 @@ private: bool _detached; PStatus _status; +#ifdef ANDROID + JNIEnv *_jni_env; +#endif + static pthread_key_t _pt_ptr_index; static bool _got_pt_ptr_index; }; diff --git a/panda/src/pnmimage/convert_srgb.I b/panda/src/pnmimage/convert_srgb.I index 57d8d42170..f49cc38203 100644 --- a/panda/src/pnmimage/convert_srgb.I +++ b/panda/src/pnmimage/convert_srgb.I @@ -153,3 +153,17 @@ encode_sRGB_uchar(const LColorf &color, xel &into, xelval &into_alpha) { into_alpha = (xelval) (color[3] * 255.f + 0.5f); #endif } + + +/** + * Double-precision versions of the above. + */ +INLINE void +encode_sRGB_uchar(const LColord &color, xel &into) { + return encode_sRGB_uchar(LCAST(float, color), into); +} + +INLINE void +encode_sRGB_uchar(const LColord &color, xel &into, xelval &into_alpha) { + return encode_sRGB_uchar(LCAST(float, color), into, into_alpha); +} diff --git a/panda/src/pnmimage/convert_srgb.h b/panda/src/pnmimage/convert_srgb.h index dada8918b3..86bc8b07bf 100644 --- a/panda/src/pnmimage/convert_srgb.h +++ b/panda/src/pnmimage/convert_srgb.h @@ -46,6 +46,11 @@ EXPCL_PANDA_PNMIMAGE INLINE void encode_sRGB_uchar(const LColorf &from, EXPCL_PANDA_PNMIMAGE INLINE void encode_sRGB_uchar(const LColorf &from, xel &into, xelval &into_alpha); +EXPCL_PANDA_PNMIMAGE INLINE void encode_sRGB_uchar(const LColord &from, + xel &into); +EXPCL_PANDA_PNMIMAGE INLINE void encode_sRGB_uchar(const LColord &from, + xel &into, xelval &into_alpha); + // Use these functions if you know that SSE2 support is available. Otherwise, // they will crash! #if defined(__SSE2__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) diff --git a/panda/src/pnmimage/pnmFileTypeRegistry.cxx b/panda/src/pnmimage/pnmFileTypeRegistry.cxx index 261f311f11..96e24ef1bb 100644 --- a/panda/src/pnmimage/pnmFileTypeRegistry.cxx +++ b/panda/src/pnmimage/pnmFileTypeRegistry.cxx @@ -49,16 +49,19 @@ register_type(PNMFileType *type) { } // Make sure we haven't already registered this type. - Handles::iterator hi = _handles.find(type->get_type()); - if (hi != _handles.end()) { - pnmimage_cat->warning() - << "Attempt to register PNMFileType " << type->get_name() - << " (" << type->get_type() << ") more than once.\n"; - return; + TypeHandle handle = type->get_type(); + if (handle != PNMFileType::get_class_type()) { + Handles::iterator hi = _handles.find(handle); + if (hi != _handles.end()) { + pnmimage_cat->warning() + << "Attempt to register PNMFileType " << type->get_name() + << " (" << type->get_type() << ") more than once.\n"; + return; + } + _handles.insert(Handles::value_type(handle, type)); } _types.push_back(type); - _handles.insert(Handles::value_type(type->get_type(), type)); // Collect the unique extensions associated with the type. pset unique_extensions; @@ -82,6 +85,37 @@ register_type(PNMFileType *type) { _requires_sort = true; } +/** + * Removes a PNMFileType previously passed to register_type. + */ +void PNMFileTypeRegistry:: +unregister_type(PNMFileType *type) { + if (pnmimage_cat->is_debug()) { + pnmimage_cat->debug() + << "Unregistering image type " << type->get_name() << "\n"; + } + + TypeHandle handle = type->get_type(); + if (handle != PNMFileType::get_class_type()) { + Handles::iterator hi = _handles.find(handle); + if (hi != _handles.end()) { + _handles.erase(hi); + } + } + + _types.erase(std::remove(_types.begin(), _types.end(), type), + _types.end()); + + Extensions::iterator ei; + for (ei = _extensions.begin(); ei != _extensions.end(); ++ei) { + Types &types = ei->second; + types.erase(std::remove(types.begin(), types.end(), type), + types.end()); + } + + _requires_sort = true; +} + /** * Returns the total number of types registered. */ diff --git a/panda/src/pnmimage/pnmFileTypeRegistry.h b/panda/src/pnmimage/pnmFileTypeRegistry.h index aec9327d49..9c8702a961 100644 --- a/panda/src/pnmimage/pnmFileTypeRegistry.h +++ b/panda/src/pnmimage/pnmFileTypeRegistry.h @@ -33,6 +33,7 @@ public: ~PNMFileTypeRegistry(); void register_type(PNMFileType *type); + void unregister_type(PNMFileType *type); PUBLISHED: int get_num_types() const; diff --git a/panda/src/pnmimage/pnmImage.I b/panda/src/pnmimage/pnmImage.I index 173f543e1e..d4a7026483 100644 --- a/panda/src/pnmimage/pnmImage.I +++ b/panda/src/pnmimage/pnmImage.I @@ -80,7 +80,7 @@ to_val(float input_value) const { switch (_xel_encoding) { case XE_generic: case XE_generic_alpha: - return clamp_val((int)(input_value * get_maxval() + 0.5f)); + return (int)(min(1.0f, max(0.0f, input_value)) * get_maxval() + 0.5f); case XE_generic_sRGB: case XE_generic_sRGB_alpha: 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..e17e0e9df8 100644 --- a/panda/src/pnmimagetypes/pnmFileTypeStbImage.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypeStbImage.cxx @@ -62,7 +62,7 @@ static const char *const stb_extensions[] = { // Expose the extensions that we don't already expose through other loaders. -#ifndef HAVE_JPEG +#if !defined(HAVE_JPEG) && !defined(ANDROID) "jpg", "jpeg", #endif #ifndef HAVE_PNG @@ -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.cxx b/panda/src/putil/bamReader.cxx index 8831a1ca7e..30ad07415a 100644 --- a/panda/src/putil/bamReader.cxx +++ b/panda/src/putil/bamReader.cxx @@ -1159,6 +1159,16 @@ p_read_object() { // This object had already existed; thus, we are just receiving an // update for it. + if (_object_pointers.find(object_id) != _object_pointers.end()) { + // Aieee! This object isn't even complete from the last time we + // encountered it in the stream! This should never happen. Something's + // corrupt or the stream was maliciously crafted. + bam_cat.error() + << "Found object " << object_id << " in bam stream again while " + << "trying to resolve its own pointers.\n"; + return 0; + } + // Update _now_creating during this call so if this function calls // read_pointer() or register_change_this() we'll match it up properly. // This might recursively call back into this p_read_object(), so be @@ -1345,35 +1355,39 @@ resolve_object_pointers(TypedWritable *object, if (child_id == 0) { // A NULL pointer is a NULL pointer. references.push_back((TypedWritable *)NULL); - - } else { - // See if we have the pointer available now. - CreatedObjs::const_iterator oi = _created_objs.find(child_id); - if (oi == _created_objs.end()) { - // No, too bad. - is_complete = false; - - } else { - const CreatedObj &child_obj = (*oi).second; - if (!child_obj._created) { - // The child object hasn't yet been created. - is_complete = false; - } else if (child_obj._change_this != NULL || child_obj._change_this_ref != NULL) { - // It's been created, but the pointer might still change. - is_complete = false; - } else { - if (require_fully_complete && - _object_pointers.find(child_id) != _object_pointers.end()) { - // It's not yet complete itself. - is_complete = false; - - } else { - // Yes, it's ready. - references.push_back(child_obj._ptr); - } - } - } + continue; } + + // See if we have the pointer available now. + CreatedObjs::const_iterator oi = _created_objs.find(child_id); + if (oi == _created_objs.end()) { + // No, too bad. + is_complete = false; + break; + } + + const CreatedObj &child_obj = (*oi).second; + if (!child_obj._created) { + // The child object hasn't yet been created. + is_complete = false; + break; + } + + if (child_obj._change_this != NULL || child_obj._change_this_ref != NULL) { + // It's been created, but the pointer might still change. + is_complete = false; + break; + } + + if (require_fully_complete && + _object_pointers.find(child_id) != _object_pointers.end()) { + // It's not yet complete itself. + is_complete = false; + break; + } + + // Yes, it's ready. + references.push_back(child_obj._ptr); } if (is_complete) { @@ -1433,33 +1447,33 @@ resolve_cycler_pointers(PipelineCyclerBase *cycler, if (child_id == 0) { // A NULL pointer is a NULL pointer. references.push_back((TypedWritable *)NULL); - - } else { - // See if we have the pointer available now. - CreatedObjs::const_iterator oi = _created_objs.find(child_id); - if (oi == _created_objs.end()) { - // No, too bad. - is_complete = false; - - } else { - const CreatedObj &child_obj = (*oi).second; - if (child_obj._change_this != NULL || child_obj._change_this_ref != NULL) { - // It's been created, but the pointer might still change. - is_complete = false; - - } else { - if (require_fully_complete && - _object_pointers.find(child_id) != _object_pointers.end()) { - // It's not yet complete itself. - is_complete = false; - - } else { - // Yes, it's ready. - references.push_back(child_obj._ptr); - } - } - } + continue; } + + // See if we have the pointer available now. + CreatedObjs::const_iterator oi = _created_objs.find(child_id); + if (oi == _created_objs.end()) { + // No, too bad. + is_complete = false; + break; + } + + const CreatedObj &child_obj = (*oi).second; + if (child_obj._change_this != NULL || child_obj._change_this_ref != NULL) { + // It's been created, but the pointer might still change. + is_complete = false; + break; + } + + if (require_fully_complete && + _object_pointers.find(child_id) != _object_pointers.end()) { + // It's not yet complete itself. + is_complete = false; + break; + } + + // Yes, it's ready. + references.push_back(child_obj._ptr); } if (is_complete) { 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/clockObject.I b/panda/src/putil/clockObject.I index d9bcac3a7c..f8a0bbc02b 100644 --- a/panda/src/putil/clockObject.I +++ b/panda/src/putil/clockObject.I @@ -213,10 +213,12 @@ check_errors(Thread *current_thread) { */ INLINE ClockObject *ClockObject:: get_global_clock() { - if (_global_clock == (ClockObject *)NULL) { + ClockObject *clock = (ClockObject *)AtomicAdjust::get_ptr(_global_clock); + if (UNLIKELY(clock == nullptr)) { make_global_clock(); + clock = (ClockObject *)_global_clock; } - return _global_clock; + return clock; } /** diff --git a/panda/src/putil/clockObject.cxx b/panda/src/putil/clockObject.cxx index d53f4ea329..09353ab739 100644 --- a/panda/src/putil/clockObject.cxx +++ b/panda/src/putil/clockObject.cxx @@ -21,21 +21,16 @@ void (*ClockObject::_start_clock_wait)() = ClockObject::dummy_clock_wait; void (*ClockObject::_start_clock_busy_wait)() = ClockObject::dummy_clock_wait; void (*ClockObject::_stop_clock_wait)() = ClockObject::dummy_clock_wait; -ClockObject *ClockObject::_global_clock; +AtomicAdjust::Pointer ClockObject::_global_clock = nullptr; TypeHandle ClockObject::_type_handle; /** * */ ClockObject:: -ClockObject() : _ticks(get_class_type()) { +ClockObject(Mode mode) : _ticks(get_class_type()), _mode(mode) { _true_clock = TrueClock::get_global_ptr(); - // Each clock except for the application global clock is created in M_normal - // mode. The application global clock is later reset to respect clock_mode, - // which comes from the Config.prc file. - _mode = M_normal; - _start_short_time = _true_clock->get_short_time(); _start_long_time = _true_clock->get_long_time(); _actual_frame_time = 0.0; @@ -523,7 +518,7 @@ wait_until(double want_time) { */ void ClockObject:: make_global_clock() { - nassertv(_global_clock == (ClockObject *)NULL); + nassertv(_global_clock == nullptr); ConfigVariableEnum clock_mode ("clock-mode", ClockObject::M_normal, @@ -532,9 +527,13 @@ make_global_clock() { "effects like simulated reduced frame rate. See " "ClockObject::set_mode().")); - _global_clock = new ClockObject; - _global_clock->set_mode(clock_mode); - _global_clock->ref(); + ClockObject *clock = new ClockObject(clock_mode); + clock->local_object(); + + if (AtomicAdjust::compare_and_exchange_ptr(_global_clock, nullptr, clock) != nullptr) { + // Another thread beat us to it. + delete clock; + } } /** diff --git a/panda/src/putil/clockObject.h b/panda/src/putil/clockObject.h index ca7e3d2116..bcea511f29 100644 --- a/panda/src/putil/clockObject.h +++ b/panda/src/putil/clockObject.h @@ -68,7 +68,7 @@ PUBLISHED: M_integer_limited, }; - ClockObject(); + ClockObject(Mode mode = M_normal); ClockObject(const ClockObject ©); INLINE ~ClockObject(); @@ -172,7 +172,7 @@ private: typedef CycleDataWriter CDWriter; typedef CycleDataStageReader CDStageReader; - static ClockObject *_global_clock; + static AtomicAdjust::Pointer _global_clock; public: static TypeHandle get_class_type() { 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/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..8962ddbb2b --- /dev/null +++ b/panda/src/putil/datagramBuffer.h @@ -0,0 +1,64 @@ +/** + * 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 "datagramGenerator.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/datagramInputFile.cxx b/panda/src/putil/datagramInputFile.cxx index 1ac14b58ed..f3bd8a081e 100644 --- a/panda/src/putil/datagramInputFile.cxx +++ b/panda/src/putil/datagramInputFile.cxx @@ -147,37 +147,34 @@ get_datagram(Datagram &data) { // Make sure we have a reasonable datagram size for putting into memory. nassertr(num_bytes == (size_t)num_bytes, false); - // Now, read the datagram itself. + // Now, read the datagram itself. We construct an empty datagram, use + // pad_bytes to make it big enough, and read *directly* into the datagram's + // internal buffer. Doing this saves us a copy operation. + data = Datagram(); - // If the number of bytes is large, we will need to allocate a temporary - // buffer from the heap. Otherwise, we can get away with allocating it on - // the stack, via alloca(). - if (num_bytes > 65536) { - char *buffer = (char *)PANDA_MALLOC_ARRAY(num_bytes); - nassertr(buffer != (char *)NULL, false); + streamsize bytes_read = 0; + while (bytes_read < num_bytes) { + streamsize bytes_left = num_bytes - bytes_read; - _in->read(buffer, num_bytes); - if (_in->fail() || _in->eof()) { - _error = true; - PANDA_FREE_ARRAY(buffer); - return false; - } + // Hold up a second - datagrams >4MB are pretty large by bam/network + // standards. Let's take it 4MB at a time just in case the length is + // corrupt, so we don't allocate potentially a few GBs of RAM only to + // find a truncated file. + bytes_left = min(bytes_left, (streamsize)4*1024*1024); - data = Datagram(buffer, num_bytes); - PANDA_FREE_ARRAY(buffer); + PTA_uchar buffer = data.modify_array(); + buffer.resize(buffer.size() + bytes_left); + unsigned char *ptr = &buffer.p()[bytes_read]; - } else { - char *buffer = (char *)alloca(num_bytes); - nassertr(buffer != (char *)NULL, false); - - _in->read(buffer, num_bytes); + _in->read((char *)ptr, bytes_left); if (_in->fail() || _in->eof()) { _error = true; return false; } - data = Datagram(buffer, num_bytes); + bytes_read += bytes_left; } + Thread::consider_yield(); return true; 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/p3putil_composite1.cxx b/panda/src/putil/p3putil_composite1.cxx index 3fd7673773..4fc6fcaf70 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..26728f1e01 100644 --- a/panda/src/putil/simpleHashMap.I +++ b/panda/src/putil/simpleHashMap.I @@ -15,16 +15,59 @@ * */ 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()); + + for (size_t i = 0; i < _num_entries; ++i) { + new(&_table[i]) TableEntry(copy._table[i]); + } + + // Copy the index array. + memcpy(get_index_array(), copy.get_index_array(), _table_size * sizeof(int) * sparsity); +} + +/** + * + */ +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 +77,53 @@ 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()); + for (size_t i = 0; i < _num_entries; ++i) { + new(&_table[i]) TableEntry(copy._table[i]); + } + + // Copy the index array. + memcpy(get_index_array(), copy.get_index_array(), _table_size * sizeof(int) * sparsity); + } + 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 +159,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 +179,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 +203,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 +212,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 +244,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 +330,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 +357,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 +447,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 +462,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 +490,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 +504,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 +537,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 +560,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 +618,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 +658,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 +676,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/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 b4b007f998..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,7 +305,7 @@ 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 = move(data); } @@ -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/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 36ef375afe..9475eabf55 100644 --- a/panda/src/text/config_text.cxx +++ b/panda/src/text/config_text.cxx @@ -52,7 +52,13 @@ 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.")); + "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, diff --git a/panda/src/text/config_text.h b/panda/src/text/config_text.h index 4e5ca1a6ed..895072fdf6 100644 --- a/panda/src/text/config_text.h +++ b/panda/src/text/config_text.h @@ -31,6 +31,7 @@ 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 f066e07c35..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 } /** @@ -305,6 +322,55 @@ get_kerning(int first, int second) const { 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. */ @@ -328,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 26433deef6..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 @@ -125,6 +127,9 @@ 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(); void update_filters(); @@ -171,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 7b8470116b..49c507dafd 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; @@ -1406,7 +1411,12 @@ assemble_row(TextAssembler::TextRow &row, 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) { @@ -1448,10 +1458,29 @@ 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(); @@ -1597,10 +1626,11 @@ assemble_row(TextAssembler::TextRow &row, } if (first_glyph != (TextGlyph *)NULL) { - assert(!first_glyph->is_whitespace()); advance = first_glyph->get_advance() * advance_scale; - swap(placement._glyph, first_glyph); - placed_glyphs.push_back(placement); + if (!first_glyph->is_whitespace()) { + swap(placement._glyph, first_glyph); + placed_glyphs.push_back(placement); + } } // Check if there is a second glyph to create a hacky ligature or some @@ -1613,10 +1643,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); @@ -1640,6 +1676,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 64b4c7ef03..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(); @@ -247,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/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 db600e14de..a9c6b113a2 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]; @@ -2410,9 +2397,6 @@ upload_texture(TinyTextureContext *gtc, bool force, bool uses_mipmaps) { PStatTimer timer(_load_texture_pcollector); CPTA_uchar src_image = tex->get_uncompressed_ram_image(); - if (src_image.is_null()) { - return false; - } #ifdef DO_PSTATS _data_transferred_pcollector.add_level(tex->get_ram_image_size()); @@ -2451,56 +2435,70 @@ upload_texture(TinyTextureContext *gtc, bool force, bool uses_mipmaps) { for (int level = 0; level < gltex->num_levels; ++level) { ZTextureLevel *dest = &gltex->levels[level]; - switch (tex->get_format()) { - case Texture::F_rgb: - case Texture::F_rgb5: - case Texture::F_rgb8: - case Texture::F_rgb12: - case Texture::F_rgb332: - copy_rgb_image(dest, xsize, ysize, gtc, level); - break; + if (tex->has_ram_mipmap_image(level)) { + switch (tex->get_format()) { + case Texture::F_rgb: + case Texture::F_rgb5: + case Texture::F_rgb8: + case Texture::F_rgb12: + case Texture::F_rgb332: + copy_rgb_image(dest, xsize, ysize, gtc, level); + break; - case Texture::F_rgba: - case Texture::F_rgbm: - case Texture::F_rgba4: - case Texture::F_rgba5: - case Texture::F_rgba8: - case Texture::F_rgba12: - case Texture::F_rgba16: - case Texture::F_rgba32: - copy_rgba_image(dest, xsize, ysize, gtc, level); - break; + case Texture::F_rgba: + case Texture::F_rgbm: + case Texture::F_rgba4: + case Texture::F_rgba5: + case Texture::F_rgba8: + case Texture::F_rgba12: + case Texture::F_rgba16: + case Texture::F_rgba32: + copy_rgba_image(dest, xsize, ysize, gtc, level); + break; - case Texture::F_luminance: - copy_lum_image(dest, xsize, ysize, gtc, level); - break; + case Texture::F_luminance: + copy_lum_image(dest, xsize, ysize, gtc, level); + break; - case Texture::F_red: - copy_one_channel_image(dest, xsize, ysize, gtc, level, 0); - break; + case Texture::F_red: + copy_one_channel_image(dest, xsize, ysize, gtc, level, 0); + break; - case Texture::F_green: - copy_one_channel_image(dest, xsize, ysize, gtc, level, 1); - break; + case Texture::F_green: + copy_one_channel_image(dest, xsize, ysize, gtc, level, 1); + break; - case Texture::F_blue: - copy_one_channel_image(dest, xsize, ysize, gtc, level, 2); - break; + case Texture::F_blue: + copy_one_channel_image(dest, xsize, ysize, gtc, level, 2); + break; - case Texture::F_alpha: - copy_alpha_image(dest, xsize, ysize, gtc, level); - break; + case Texture::F_alpha: + copy_alpha_image(dest, xsize, ysize, gtc, level); + break; - case Texture::F_luminance_alphamask: - case Texture::F_luminance_alpha: - copy_la_image(dest, xsize, ysize, gtc, level); - break; + case Texture::F_luminance_alphamask: + case Texture::F_luminance_alpha: + copy_la_image(dest, xsize, ysize, gtc, level); + break; - default: - tinydisplay_cat.error() - << "Unsupported texture format " - << tex->get_format() << "!\n"; - return false; + default: + tinydisplay_cat.error() + << "Unsupported texture format " + << tex->get_format() << "!\n"; + return false; + } + } else { + // Fill the mipmap with a solid color. + LColor scaled = tex->get_clear_color().fmin(LColor(1)).fmax(LColor::zero()); + scaled *= 255; + unsigned int clear = RGBA8_TO_PIXEL((int)scaled[0], (int)scaled[1], + (int)scaled[2], (int)scaled[3]); + unsigned int *dpix = (unsigned int *)dest->pixmap; + int pixel_count = xsize * ysize; + while (pixel_count-- > 0) { + *dpix = clear; + ++dpix; + } } bytecount += xsize * ysize * 4; @@ -2572,6 +2570,13 @@ upload_simple_texture(TinyTextureContext *gtc) { */ bool TinyGraphicsStateGuardian:: setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) { + if (x_size == 0 || y_size == 0) { + // A texture without pixels gets turned into a 1x1 texture. + x_size = 1; + y_size = 1; + num_levels = 1; + } + int s_bits = get_tex_shift(x_size); int t_bits = get_tex_shift(y_size); 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/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/wgldisplay/wglGraphicsWindow.cxx b/panda/src/wgldisplay/wglGraphicsWindow.cxx index d6fe0799b9..06fc3d3ed4 100644 --- a/panda/src/wgldisplay/wglGraphicsWindow.cxx +++ b/panda/src/wgldisplay/wglGraphicsWindow.cxx @@ -386,7 +386,7 @@ print_pfd(PIXELFORMATDESCRIPTOR *pfd, char *msg) { wgldisplay_cat.debug() << msg << ", " << OGLDrvStrings[drvtype] << " driver\n" - << "PFD flags: 0x" << (void*)pfd->dwFlags << " (" + << "PFD flags: 0x" << hex << pfd->dwFlags << dec << " (" << PRINT_FLAG(GENERIC_ACCELERATED) << PRINT_FLAG(GENERIC_FORMAT) << PRINT_FLAG(DOUBLEBUFFER) diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index 1ccede6968..054581e1fd 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 3ea2cd3fb0..c57a580aa7 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -683,6 +683,12 @@ set_properties_now(WindowProperties &properties) { } else { XDefineCursor(_display, _xwindow, None); } + + // Regrab the mouse if we changed the cursor, otherwise it won't update. + if (!properties.has_mouse_mode() && + _properties.get_mouse_mode() != WindowProperties::M_absolute) { + properties.set_mouse_mode(_properties.get_mouse_mode()); + } } if (properties.has_foreground()) { @@ -2051,7 +2057,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; @@ -2280,9 +2286,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); @@ -2330,21 +2338,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/eggImportOptions.mel b/pandatool/src/mayaprogs/eggImportOptions.mel old mode 100755 new mode 100644 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/scripts/MayaPandaTool.mel b/pandatool/src/scripts/MayaPandaTool.mel old mode 100755 new mode 100644 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/music-box/music/musicbox.ogg b/samples/music-box/music/musicbox.ogg old mode 100755 new mode 100644 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 100644 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 100644 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 100644 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 100644 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 100644 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 100644 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/audio/conftest.py b/tests/audio/conftest.py new file mode 100644 index 0000000000..60636e6d6e --- /dev/null +++ b/tests/audio/conftest.py @@ -0,0 +1,6 @@ +import pytest +from panda3d.core import * + +@pytest.fixture(scope='module') +def audiomgr(): + return AudioManager.create_AudioManager() diff --git a/tests/audio/test_loading.py b/tests/audio/test_loading.py new file mode 100644 index 0000000000..ea3bdb2cd9 --- /dev/null +++ b/tests/audio/test_loading.py @@ -0,0 +1,5 @@ +import pytest + +def test_missing_file(audiomgr): + sound = audiomgr.get_sound('/not/a/valid/file.ogg') + assert str(sound).startswith('NullAudioSound') 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..c66ef2f54a --- /dev/null +++ b/tests/display/conftest.py @@ -0,0 +1,73 @@ +import pytest + + +@pytest.fixture(scope='session') +def graphics_pipe(): + from panda3d.core import GraphicsPipeSelection + + pipe = GraphicsPipeSelection.get_global_ptr().make_default_pipe() + + if pipe is None or not pipe.is_valid(): + pytest.skip("GraphicsPipe is invalid") + + yield pipe + + +@pytest.fixture(scope='session') +def graphics_engine(): + from panda3d.core import GraphicsEngine + + engine = GraphicsEngine.get_global_ptr() + yield engine + + # This causes GraphicsEngine to also terminate the render threads. + engine.remove_all_windows() + + +@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) + + +@pytest.fixture(scope='module') +def gsg(graphics_pipe, graphics_engine): + "Returns a windowless GSG that can be used for offscreen rendering." + from panda3d.core import GraphicsPipe, FrameBufferProperties, WindowProperties + + fbprops = FrameBufferProperties() + fbprops.force_hardware = True + + buffer = graphics_engine.make_output( + graphics_pipe, + 'buffer', + 0, + fbprops, + WindowProperties.size(32, 32), + GraphicsPipe.BF_refuse_window + ) + graphics_engine.open_windows() + + assert buffer is not None + yield buffer.gsg + + if buffer is not None: + graphics_engine.remove_window(buffer) 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_glsl_shader.py b/tests/display/test_glsl_shader.py new file mode 100644 index 0000000000..f4ff2d7ea0 --- /dev/null +++ b/tests/display/test_glsl_shader.py @@ -0,0 +1,277 @@ +from panda3d import core +import pytest +from _pytest.outcomes import Failed + + +# This is the template for the compute shader that is used by run_glsl_test. +# It defines an assert() macro that writes failures to a buffer, indexed by +# line number. +# The reset() function serves to prevent the _triggered variable from being +# optimized out in the case that the assertions are being optimized out. +GLSL_COMPUTE_TEMPLATE = """#version {version} + +layout(local_size_x = 1, local_size_y = 1) in; + +{preamble} + +layout(r8ui) uniform writeonly uimageBuffer _triggered; + +void _reset() {{ + imageStore(_triggered, 0, uvec4(0, 0, 0, 0)); +}} + +void _assert(bool cond, int line) {{ + if (!cond) {{ + imageStore(_triggered, line, uvec4(1)); + }} +}} + +#define assert(cond) _assert(cond, __LINE__) + +void main() {{ + _reset(); +{body} +}} +""" + + +def run_glsl_test(gsg, body, preamble="", inputs={}, version=430): + """ Runs a GLSL test on the given GSG. The given body is executed in the + main function and should call assert(). The preamble should contain all + of the shader inputs. """ + + if not gsg.supports_compute_shaders or not gsg.supports_glsl: + pytest.skip("compute shaders not supported") + + __tracebackhide__ = True + + preamble = preamble.strip() + body = body.rstrip().lstrip('\n') + code = GLSL_COMPUTE_TEMPLATE.format(version=version, preamble=preamble, body=body) + line_offset = code[:code.find(body)].count('\n') + 1 + shader = core.Shader.make_compute(core.Shader.SL_GLSL, code) + assert shader, code + + # Create a buffer to hold the results of the assertion. We use one byte + # per line of shader code, so we can show which lines triggered. + result = core.Texture("") + result.set_clear_color((0, 0, 0, 0)) + result.setup_buffer_texture(code.count('\n'), core.Texture.T_unsigned_byte, + core.Texture.F_r8i, core.GeomEnums.UH_static) + + # Build up the shader inputs + attrib = core.ShaderAttrib.make(shader) + for name, value in inputs.items(): + attrib = attrib.set_shader_input(name, value) + attrib = attrib.set_shader_input('_triggered', result) + + # Run the compute shader. + engine = core.GraphicsEngine.get_global_ptr() + try: + engine.dispatch_compute((1, 1, 1), attrib, gsg) + except AssertionError as exc: + assert False, "Error executing compute shader:\n" + code + + # Download the texture to check whether the assertion triggered. + assert engine.extract_texture_data(result, gsg) + triggered = result.get_ram_image() + if any(triggered): + count = len(triggered) - triggered.count(0) + lines = body.split('\n') + formatted = '' + for i, line in enumerate(lines): + if triggered[i + line_offset]: + formatted += '=> ' + line + '\n' + else: + formatted += ' ' + line + '\n' + pytest.fail("{0} GLSL assertions triggered:\n{1}".format(count, formatted)) + + +def test_glsl_test(gsg): + "Test to make sure that the GLSL tests work correctly." + + run_glsl_test(gsg, "assert(true);") + + +def test_glsl_test_fail(gsg): + "Same as above, but making sure that the failure case works correctly." + + with pytest.raises(Failed): + run_glsl_test(gsg, "assert(false);") + + +def test_glsl_sampler(gsg): + tex1 = core.Texture("") + tex1.setup_1d_texture(1, core.Texture.T_unsigned_byte, core.Texture.F_rgba8) + tex1.set_clear_color((0, 2 / 255.0, 1, 1)) + + tex2 = core.Texture("") + tex2.setup_2d_texture(1, 1, core.Texture.T_float, core.Texture.F_rgba32) + tex2.set_clear_color((1.0, 2.0, -3.14, 0.0)) + + preamble = """ + uniform sampler1D tex1; + uniform sampler2D tex2; + """ + code = """ + assert(texelFetch(tex1, 0, 0) == vec4(0, 2 / 255.0, 1, 1)); + assert(texelFetch(tex2, ivec2(0, 0), 0) == vec4(1.0, 2.0, -3.14, 0.0)); + """ + run_glsl_test(gsg, code, preamble, {'tex1': tex1, 'tex2': tex2}), code + + +def test_glsl_image(gsg): + tex1 = core.Texture("") + tex1.setup_1d_texture(1, core.Texture.T_unsigned_byte, core.Texture.F_rgba8) + tex1.set_clear_color((0, 2 / 255.0, 1, 1)) + + tex2 = core.Texture("") + tex2.setup_2d_texture(1, 1, core.Texture.T_float, core.Texture.F_rgba32) + tex2.set_clear_color((1.0, 2.0, -3.14, 0.0)) + + preamble = """ + layout(rgba8) uniform image1D tex1; + layout(rgba32f) uniform image2D tex2; + """ + code = """ + assert(imageLoad(tex1, 0) == vec4(0, 2 / 255.0, 1, 1)); + assert(imageLoad(tex2, ivec2(0, 0)) == vec4(1.0, 2.0, -3.14, 0.0)); + """ + run_glsl_test(gsg, code, preamble, {'tex1': tex1, 'tex2': tex2}), code + + +def test_glsl_ssbo(gsg): + from struct import pack + num1 = pack('= (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_future_gather(): + fut1 = core.AsyncFuture() + fut2 = core.AsyncFuture() + + # 0 and 1 arguments are special + assert core.AsyncFuture.gather().done() + assert core.AsyncFuture.gather(fut1) == fut1 + + # Gathering not-done futures + gather = core.AsyncFuture.gather(fut1, fut2) + assert not gather.done() + + # One future done + fut1.set_result(1) + assert not gather.done() + + # Two futures done + fut2.set_result(2) + assert gather.done() + + assert not gather.cancelled() + assert tuple(gather.result()) == (1, 2) + + +def test_future_gather_cancel_inner(): + fut1 = core.AsyncFuture() + fut2 = core.AsyncFuture() + + # Gathering not-done futures + gather = core.AsyncFuture.gather(fut1, fut2) + assert not gather.done() + + # One future cancelled + fut1.cancel() + assert not gather.done() + + # Two futures cancelled + fut2.set_result(2) + assert gather.done() + + assert not gather.cancelled() + with pytest.raises(CancelledError): + assert gather.result() + + +def test_future_gather_cancel_outer(): + fut1 = core.AsyncFuture() + fut2 = core.AsyncFuture() + + # Gathering not-done futures + gather = core.AsyncFuture.gather(fut1, fut2) + assert not gather.done() + + assert gather.cancel() + assert gather.done() + assert gather.cancelled() + + with pytest.raises(CancelledError): + assert gather.result() + + +def test_future_done_callback(): + fut = core.AsyncFuture() + + # Use the list hack since Python 2 doesn't have the "nonlocal" keyword. + called = [False] + def on_done(arg): + assert arg == fut + called[0] = True + + fut.add_done_callback(on_done) + fut.cancel() + assert fut.done() + + task_mgr = core.AsyncTaskManager.get_global_ptr() + task_mgr.poll() + assert called[0] + + +def test_future_done_callback_already_done(): + # Same as above, but with the future already done when add_done_callback + # is called. + fut = core.AsyncFuture() + fut.cancel() + assert fut.done() + + # Use the list hack since Python 2 doesn't have the "nonlocal" keyword. + called = [False] + def on_done(arg): + assert arg == fut + called[0] = True + + fut.add_done_callback(on_done) + + task_mgr = core.AsyncTaskManager.get_global_ptr() + task_mgr.poll() + assert called[0] + + +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/gobj/test_texture.py b/tests/gobj/test_texture.py new file mode 100644 index 0000000000..8fa0ed27c8 --- /dev/null +++ b/tests/gobj/test_texture.py @@ -0,0 +1,90 @@ +from panda3d.core import Texture, PNMImage +from array import array + + +def image_from_stored_pixel(component_type, format, data): + """ Creates a 1-pixel texture with the given settings and pixel data, + then returns a PNMImage as result of calling texture.store(). """ + + tex = Texture("") + tex.setup_1d_texture(1, component_type, format) + tex.set_ram_image(data) + + img = PNMImage() + assert tex.store(img) + return img + + +def test_texture_store_unsigned_byte(): + data = array('B', (2, 1, 0, 0xff)) + img = image_from_stored_pixel(Texture.T_unsigned_byte, Texture.F_rgba, data) + + assert img.maxval == 0xff + pix = img.get_pixel(0, 0) + assert tuple(pix) == (0, 1, 2, 0xff) + + +def test_texture_store_unsigned_short(): + data = array('H', (2, 1, 0, 0xffff)) + img = image_from_stored_pixel(Texture.T_unsigned_short, Texture.F_rgba, data) + + assert img.maxval == 0xffff + pix = img.get_pixel(0, 0) + assert tuple(pix) == (0, 1, 2, 0xffff) + + +def test_texture_store_unsigned_int(): + data = array('I', (0x30000, 2, 0, 0xffffffff)) + img = image_from_stored_pixel(Texture.T_unsigned_int, Texture.F_rgba, data) + + assert img.maxval == 0xffff + pix = img.get_pixel(0, 0) + assert tuple(pix) == (0, 0, 3, 0xffff) + + +def test_texture_store_float(): + data = array('f', (0.5, 0.0, -2.0, 10000.0)) + img = image_from_stored_pixel(Texture.T_float, Texture.F_rgba, data) + + assert img.maxval == 0xffff + col = img.get_xel_a(0, 0) + assert col.almost_equal((0.0, 0.0, 0.5, 1.0), 1 / 255.0) + + +def test_texture_store_half(): + # Python's array class doesn't support half floats, so we hardcode the + # binary representation of these numbers: + data = array('H', ( + # -[exp][mantissa] + 0b0011110000000000, # 1.0 + 0b1100000000000000, # -2.0 + 0b0111101111111111, # 65504.0 + 0b0011010101010101, # 0.333251953125 + )) + img = image_from_stored_pixel(Texture.T_half_float, Texture.F_rgba, data) + + assert img.maxval == 0xffff + col = img.get_xel_a(0, 0) + assert col.almost_equal((1.0, 0.0, 1.0, 0.333251953125), 1 / 255.0) + + +def test_texture_store_srgb(): + # 188 = roughly middle gray + data = array('B', [188, 188, 188]) + img = image_from_stored_pixel(Texture.T_unsigned_byte, Texture.F_srgb, data) + + # We allow some imprecision. + assert img.maxval == 0xff + col = img.get_xel(0, 0) + assert col.almost_equal((0.5, 0.5, 0.5), 1 / 255.0) + + +def test_texture_store_srgb_alpha(): + # 188 = middle gray + data = array('B', [188, 188, 188, 188]) + img = image_from_stored_pixel(Texture.T_unsigned_byte, Texture.F_srgb_alpha, data) + + # We allow some imprecision. + assert img.maxval == 0xff + col = img.get_xel_a(0, 0) + assert col.almost_equal((0.5, 0.5, 0.5, 188 / 255.0), 1 / 255.0) diff --git a/tests/gobj/test_texture_peek.py b/tests/gobj/test_texture_peek.py new file mode 100644 index 0000000000..accd1b02d0 --- /dev/null +++ b/tests/gobj/test_texture_peek.py @@ -0,0 +1,96 @@ +from panda3d.core import Texture, LColor +from array import array + + +def peeker_from_pixel(component_type, format, data): + """ Creates a 1-pixel texture with the given settings and pixel data, + then returns a TexturePeeker as result of calling texture.peek(). """ + + tex = Texture("") + tex.setup_1d_texture(1, component_type, format) + tex.set_ram_image(data) + peeker = tex.peek() + assert peeker.has_pixel(0, 0) + return peeker + + +def test_texture_peek_ubyte(): + maxval = 255 + data = array('B', (2, 1, 0, maxval)) + peeker = peeker_from_pixel(Texture.T_unsigned_byte, Texture.F_rgba, data) + + col = LColor() + peeker.fetch_pixel(col, 0, 0) + col *= maxval + assert col == (0, 1, 2, maxval) + + +def test_texture_peek_ushort(): + maxval = 65535 + data = array('H', (2, 1, 0, maxval)) + peeker = peeker_from_pixel(Texture.T_unsigned_short, Texture.F_rgba, data) + + col = LColor() + peeker.fetch_pixel(col, 0, 0) + col *= maxval + assert col == (0, 1, 2, maxval) + + +def test_texture_peek_uint(): + maxval = 4294967295 + data = array('I', (2, 1, 0, maxval)) + peeker = peeker_from_pixel(Texture.T_unsigned_int, Texture.F_rgba, data) + + col = LColor() + peeker.fetch_pixel(col, 0, 0) + col *= maxval + assert col == (0, 1, 2, maxval) + + +def test_texture_peek_float(): + data = array('f', (1.0, 0.0, -2.0, 10000.0)) + peeker = peeker_from_pixel(Texture.T_float, Texture.F_rgba, data) + + col = LColor() + peeker.fetch_pixel(col, 0, 0) + assert col == (-2.0, 0.0, 1.0, 10000.0) + + +def test_texture_peek_half(): + # Python's array class doesn't support half floats, so we hardcode the + # binary representation of these numbers: + data = array('H', ( + 0b0011110000000000, # 1.0 + 0b1100000000000000, # -2.0 + 0b0111101111111111, # 65504.0 + 0b0011010101010101, # 0.333251953125 + )) + peeker = peeker_from_pixel(Texture.T_half_float, Texture.F_rgba, data) + + col = LColor() + peeker.fetch_pixel(col, 0, 0) + assert col == (65504.0, -2.0, 1.0, 0.333251953125) + + +def test_texture_peek_srgb(): + # 188 = roughly middle gray + data = array('B', [188, 188, 188]) + peeker = peeker_from_pixel(Texture.T_unsigned_byte, Texture.F_srgb, data) + + col = LColor() + peeker.fetch_pixel(col, 0, 0) + + # We allow some imprecision. + assert col.almost_equal((0.5, 0.5, 0.5, 1.0), 1 / 255.0) + + +def test_texture_peek_srgba(): + # 188 = middle gray + data = array('B', [188, 188, 188, 188]) + peeker = peeker_from_pixel(Texture.T_unsigned_byte, Texture.F_srgb_alpha, data) + + col = LColor() + peeker.fetch_pixel(col, 0, 0) + + # We allow some imprecision. + assert col.almost_equal((0.5, 0.5, 0.5, 188 / 255.0), 1 / 255.0) 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_light.py b/tests/pgraph/test_light.py new file mode 100644 index 0000000000..f303080b33 --- /dev/null +++ b/tests/pgraph/test_light.py @@ -0,0 +1,20 @@ +from panda3d import core + +def luminance(col): + return 0.2126 * col[0] + 0.7152 * col[1] + 0.0722 * col[2] + + +def test_light_colortemp(): + # Default is all white, assuming a D65 white point. + light = core.PointLight("light") + assert light.color == (1, 1, 1, 1) + assert light.color_temperature == 6500 + + # When setting color temp, it should preserve luminance. + for temp in range(2000, 15000): + light.color_temperature = temp + assert abs(luminance(light.color) - 1.0) < 0.001 + + # Setting it to the white point will make a white color. + light.color_temperature = 6500 + assert light.color.almost_equal((1, 1, 1, 1), 0.001) diff --git a/tests/pgraph/test_lightattrib.py b/tests/pgraph/test_lightattrib.py new file mode 100644 index 0000000000..752f4c2717 --- /dev/null +++ b/tests/pgraph/test_lightattrib.py @@ -0,0 +1,74 @@ +from panda3d import core + +# Some dummy lights we can use for our light attributes. +spot = core.NodePath(core.Spotlight("spot")) +point = core.NodePath(core.PointLight("point")) +ambient = core.NodePath(core.AmbientLight("ambient")) + + +def test_lightattrib_compose_add(): + # Tests a case in which a child node adds another light. + lattr1 = core.LightAttrib.make() + lattr1 = lattr1.add_on_light(spot) + + lattr2 = core.LightAttrib.make() + lattr2 = lattr2.add_on_light(point) + + lattr3 = lattr1.compose(lattr2) + assert lattr3.get_num_on_lights() == 2 + + assert spot in lattr3.on_lights + assert point in lattr3.on_lights + + +def test_lightattrib_compose_subtract(): + # Tests a case in which a child node disables a light. + lattr1 = core.LightAttrib.make() + lattr1 = lattr1.add_on_light(spot) + lattr1 = lattr1.add_on_light(point) + + lattr2 = core.LightAttrib.make() + lattr2 = lattr2.add_off_light(ambient) + lattr2 = lattr2.add_off_light(point) + + lattr3 = lattr1.compose(lattr2) + assert lattr3.get_num_on_lights() == 1 + + assert spot in lattr3.on_lights + assert point not in lattr3.on_lights + assert ambient not in lattr3.on_lights + + +def test_lightattrib_compose_both(): + # Tests a case in which a child node both enables and disables a light. + lattr1 = core.LightAttrib.make() + lattr1 = lattr1.add_on_light(spot) + lattr1 = lattr1.add_on_light(point) + + lattr2 = core.LightAttrib.make() + lattr2 = lattr2.add_on_light(ambient) + lattr2 = lattr2.add_on_light(spot) + lattr2 = lattr2.add_off_light(point) + + lattr3 = lattr1.compose(lattr2) + assert lattr3.get_num_on_lights() == 2 + + assert spot in lattr3.on_lights + assert point not in lattr3.on_lights + assert ambient in lattr3.on_lights + + +def test_lightattrib_compose_alloff(): + # Tests a case in which a child node disables all lights. + lattr1 = core.LightAttrib.make() + lattr1 = lattr1.add_on_light(spot) + lattr1 = lattr1.add_on_light(point) + assert lattr1.get_num_on_lights() == 2 + + lattr2 = core.LightAttrib.make_all_off() + assert lattr2.has_all_off() + + lattr3 = lattr1.compose(lattr2) + assert lattr3.get_num_on_lights() == 0 + assert lattr3.get_num_off_lights() == 0 + assert lattr3.has_all_off() 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 diff --git a/tests/prc/test_config_page.py b/tests/prc/test_config_page.py new file mode 100644 index 0000000000..5ca6cdb6ef --- /dev/null +++ b/tests/prc/test_config_page.py @@ -0,0 +1,12 @@ +from panda3d import core + +def test_load_unload_page(): + var = core.ConfigVariableInt("test-var", 1) + assert var.value == 1 + + page = core.load_prc_file_data("test_load_unload_page", "test-var 2") + assert page + assert var.value == 2 + + assert core.unload_prc_file(page) + assert var.value == 1 diff --git a/tests/putil/test_datagram.py b/tests/putil/test_datagram.py new file mode 100644 index 0000000000..4a34bc88a2 --- /dev/null +++ b/tests/putil/test_datagram.py @@ -0,0 +1,149 @@ +import pytest +from panda3d import core + +# Fixtures for generating interesting datagrams (and verification functions) on +# the fly... + +@pytest.fixture(scope='module', + params=[False, True], + ids=['stdfloat_float', 'stdfloat_double']) +def datagram_small(request): + """Returns a small datagram, along with a verification function.""" + dg = core.Datagram() + + dg.set_stdfloat_double(request.param) + + dg.add_uint8(3) + dg.add_uint16(14159) + dg.add_uint32(0xDEADBEEF) + dg.add_uint64(0x0123456789ABCDEF) + + dg.add_int8(-77) + dg.add_int16(-1) + dg.add_int32(-972965890) + dg.add_int64(-1001001001001001) + + dg.add_string('this is a string') + dg.add_string32('this is another string') + dg.add_string('this is yet a third string') + + dg.add_stdfloat(800.2) + dg.add_stdfloat(3.1415926) + dg.add_stdfloat(2.7182818) + + def readback_function(dgi): + assert dgi.get_remaining_size() > 0 + + assert dgi.get_uint8() == 3 + assert dgi.get_uint16() == 14159 + assert dgi.get_uint32() == 0xDEADBEEF + assert dgi.get_uint64() == 0x0123456789ABCDEF + + assert dgi.get_int8() == -77 + assert dgi.get_int16() == -1 + assert dgi.get_int32() == -972965890 + assert dgi.get_int64() == -1001001001001001 + + assert dgi.get_string() == 'this is a string' + assert dgi.get_string32() == 'this is another string' + assert dgi.get_string() == 'this is yet a third string' + + assert dgi.get_stdfloat() == pytest.approx(800.2) + assert dgi.get_stdfloat() == pytest.approx(3.1415926) + assert dgi.get_stdfloat() == pytest.approx(2.7182818) + + assert dgi.get_remaining_size() == 0 + + return dg, readback_function + +@pytest.fixture(scope='module') +def datagram_large(): + """Returns a big datagram, along with a verification function.""" + + dg = core.Datagram() + for x in range(2000000): + dg.add_uint32(x) + dg.add_string('the magic words are squeamish ossifrage') + + def readback_function(dgi): + assert dgi.get_remaining_size() > 0 + + for x in range(2000000): + assert dgi.get_uint32() == x + assert dgi.get_string() == 'the magic words are squeamish ossifrage' + + assert dgi.get_remaining_size() == 0 + + return dg, readback_function + +def test_iterator(datagram_small): + """This tests Datagram/DatagramIterator, and sort of serves as a self-check + of the test fixtures too.""" + dg, verify = datagram_small + + dgi = core.DatagramIterator(dg) + verify(dgi) + + +# These test DatagramInputFile/DatagramOutputFile: + +def do_file_test(dg, verify, filename): + dof = core.DatagramOutputFile() + dof.open(filename) + dof.put_datagram(dg) + dof.close() + + dg2 = core.Datagram() + dif = core.DatagramInputFile() + dif.open(filename) + assert dif.get_datagram(dg2) + dif.close() + + # This is normally saved by the DatagramOutputFile header. We cheat here. + dg2.set_stdfloat_double(dg.get_stdfloat_double()) + + dgi = core.DatagramIterator(dg2) + verify(dgi) + +def test_file_small(datagram_small, tmpdir): + """This tests DatagramOutputFile/DatagramInputFile on small datagrams.""" + dg, verify = datagram_small + + p = tmpdir.join('datagram.bin') + filename = core.Filename.from_os_specific(str(p)) + + do_file_test(dg, verify, filename) + +def test_file_large(datagram_large, tmpdir): + """This tests DatagramOutputFile/DatagramInputFile on very large datagrams.""" + dg, verify = datagram_large + + p = tmpdir.join('datagram.bin') + filename = core.Filename.from_os_specific(str(p)) + + do_file_test(dg, verify, filename) + +def test_file_corrupt(datagram_small, tmpdir): + """This tests DatagramInputFile's handling of a corrupt size header.""" + dg, verify = datagram_small + + p = tmpdir.join('datagram.bin') + filename = core.Filename.from_os_specific(str(p)) + + dof = core.DatagramOutputFile() + dof.open(filename) + dof.put_datagram(dg) + dof.close() + + # Corrupt the size header to 4GB + with p.open(mode='wb') as f: + f.seek(0) + f.write(b'\xFF\xFF\xFF\xFF') + + dg2 = core.Datagram() + dif = core.DatagramInputFile() + dif.open(filename) + assert not dif.get_datagram(dg2) + dif.close() + + # Should we test that dg2 is unmodified? diff --git a/tests/text/test_text_assemble.py b/tests/text/test_text_assemble.py new file mode 100644 index 0000000000..959097029c --- /dev/null +++ b/tests/text/test_text_assemble.py @@ -0,0 +1,7 @@ +from panda3d import core + +def test_text_assemble_null(): + # Tests that no is_whitespace() assert occurs + assembler = core.TextAssembler(core.TextEncoder()) + assembler.set_wtext(u"\0test") + assembler.assemble_text()