diff --git a/README.md b/README.md index 474ccedc57..fde01c38f0 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ into an existing Python installation is using the following command: 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: @@ -51,6 +53,9 @@ depending on whether you are on a 32-bit or 64-bit system: 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: 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/contrib/src/sceneeditor/seFileSaver.py b/contrib/src/sceneeditor/seFileSaver.py index 73c71f5901..317c9427f2 100644 --- a/contrib/src/sceneeditor/seFileSaver.py +++ b/contrib/src/sceneeditor/seFileSaver.py @@ -488,7 +488,7 @@ class FileSaver: out_file.write (i2+ "alight = AmbientLight(\'"+ light.getName() +"\')\n") out_file.write (i2+ "alight.setColor(VBase4("+ str(light.getLightColor().getX())+ "," + str(light.getLightColor().getY())+ "," + str(light.getLightColor().getZ()) + "," + str(light.getLightColor().getW()) + "))\n") out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n") - out_file.write (i2+ "self."+light.getName()+"= render.attachNewNode(alight.upcastToPandaNode())\n") + out_file.write (i2+ "self."+light.getName()+"= render.attachNewNode(alight)\n") out_file.write (i2+ "self."+light.getName()+".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n") out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n") out_file.write (i2+ "self.LightTypes[\'" + light.getName() + "\']=\'" + type + "\'\n") @@ -503,7 +503,7 @@ class FileSaver: #out_file.write (i2+ "alight.setPoint(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n") out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n") - out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToPandaNode())\n") + out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight)\n") out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") out_file.write (i2+ "self."+light.getName()+ ".setHpr(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n") out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n") @@ -521,7 +521,7 @@ class FileSaver: out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n") out_file.write (i2+ "alight.setAttenuation(Vec3("+ str(light.getAttenuation().getX()) + "," + str(light.getAttenuation().getY()) + "," + str(light.getAttenuation().getZ()) + "))\n") out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n") - out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToPandaNode())\n") + out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight)\n") out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n") out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n") @@ -539,7 +539,7 @@ class FileSaver: out_file.write (i2+ "alight.setAttenuation(Vec3("+ str(light.getAttenuation().getX()) + "," + str(light.getAttenuation().getY()) + "," + str(light.getAttenuation().getZ()) + "))\n") out_file.write (i2+ "alight.setExponent(" +str(light.getExponent()) +")\n") out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n") - out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToLensNode())\n") + out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight)\n") out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n") out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") out_file.write (i2+ "self."+light.getName()+ ".setHpr(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n") diff --git a/contrib/src/sceneeditor/seLights.py b/contrib/src/sceneeditor/seLights.py index 1a42e9045a..ccc58ce0cd 100644 --- a/contrib/src/sceneeditor/seLights.py +++ b/contrib/src/sceneeditor/seLights.py @@ -63,13 +63,8 @@ class seLight(NodePath): self.lence = lence self.active = True - if isinstance(light, Spotlight): - node = light.upcastToLensNode() - else: - node = light.upcastToPandaNode() - # Attach node to self - self.LightNode=parent.attachNewNode(node) + self.LightNode=parent.attachNewNode(light) self.LightNode.setTag("Metadata",tag) if(self.type=='spot'): self.LightNode.setHpr(self.orientation) @@ -418,8 +413,6 @@ class seLightManager(NodePath): ################################################################# type = lower(light.getType().getName()) - light.upcastToNamable() - specularColor = VBase4(1) position = Point3(0,0,0) orientation = Vec3(1,0,0) diff --git a/direct/src/actor/Actor.py b/direct/src/actor/Actor.py index 8a7ef093b0..b9ba299aaa 100644 --- a/direct/src/actor/Actor.py +++ b/direct/src/actor/Actor.py @@ -1103,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: diff --git a/direct/src/dcparser/dcClass.cxx b/direct/src/dcparser/dcClass.cxx index 513d13fd90..d11a116415 100644 --- a/direct/src/dcparser/dcClass.cxx +++ b/direct/src/dcparser/dcClass.cxx @@ -514,15 +514,9 @@ receive_update_broadcast_required_owner(PyObject *distobj, for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { DCField *field = get_inherited_field(i); if (field->as_molecular_field() == (DCMolecularField *)NULL && - field->is_required()) { + field->is_required() && (field->is_ownrecv() || field->is_broadcast())) { packer.begin_unpack(field); - if (field->is_ownrecv()) { - field->receive_update(packer, distobj); - } else { - // It's not an ownrecv field; skip over it. It's difficult to filter - // this on the server, ask Roger for the reason. - packer.unpack_skip(); - } + field->receive_update(packer, distobj); if (!packer.end_unpack()) { break; } @@ -946,19 +940,17 @@ ai_format_generate(PyObject *distobj, DOID_TYPE do_id, bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0); if (has_optional_fields) { - packer.raw_pack_uint16(STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER); + packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER); } else { - packer.raw_pack_uint16(STATESERVER_OBJECT_GENERATE_WITH_REQUIRED); + packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED); } + packer.raw_pack_uint32(do_id); // Parent is a bit overloaded; this parent is not about inheritance, this // one is about the visibility container parent, i.e. the zone parent: - if (parent_id) { - packer.raw_pack_uint32(parent_id); - } + packer.raw_pack_uint32(parent_id); packer.raw_pack_uint32(zone_id); packer.raw_pack_uint16(_number); - packer.raw_pack_uint32(do_id); // Specify all of the required fields. int num_fields = get_num_inherited_fields(); @@ -1009,77 +1001,6 @@ ai_format_generate(PyObject *distobj, DOID_TYPE do_id, return Datagram(packer.get_data(), packer.get_length()); } #endif // HAVE_PYTHON -#ifdef HAVE_PYTHON -/** - * Generates a datagram containing the message necessary to create a new - * database distributed object from the AI. - * - * First Pass is to only include required values (with Defaults). - */ -Datagram DCClass:: -ai_database_generate_context( - unsigned int context_id, DOID_TYPE parent_id, ZONEID_TYPE zone_id, - CHANNEL_TYPE owner_channel, - CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const -{ - DCPacker packer; - packer.raw_pack_uint8(1); - packer.RAW_PACK_CHANNEL(database_server_id); - packer.RAW_PACK_CHANNEL(from_channel_id); - // packer.raw_pack_uint8('A'); - packer.raw_pack_uint16(STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT); - packer.raw_pack_uint32(parent_id); - packer.raw_pack_uint32(zone_id); - packer.RAW_PACK_CHANNEL(owner_channel); - packer.raw_pack_uint16(_number); // DCD class ID - packer.raw_pack_uint32(context_id); - - // Specify all of the required fields. - int num_fields = get_num_inherited_fields(); - for (int i = 0; i < num_fields; ++i) { - DCField *field = get_inherited_field(i); - if (field->is_required() && field->as_molecular_field() == NULL) { - packer.begin_pack(field); - packer.pack_default_value(); - packer.end_pack(); - } - } - - return Datagram(packer.get_data(), packer.get_length()); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -Datagram DCClass:: -ai_database_generate_context_old( - unsigned int context_id, DOID_TYPE parent_id, ZONEID_TYPE zone_id, - CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const -{ - DCPacker packer; - packer.raw_pack_uint8(1); - packer.RAW_PACK_CHANNEL(database_server_id); - packer.RAW_PACK_CHANNEL(from_channel_id); - // packer.raw_pack_uint8('A'); - packer.raw_pack_uint16(STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT); - packer.raw_pack_uint32(parent_id); - packer.raw_pack_uint32(zone_id); - packer.raw_pack_uint16(_number); // DCD class ID - packer.raw_pack_uint32(context_id); - - // Specify all of the required fields. - int num_fields = get_num_inherited_fields(); - for (int i = 0; i < num_fields; ++i) { - DCField *field = get_inherited_field(i); - if (field->is_required() && field->as_molecular_field() == NULL) { - packer.begin_pack(field); - packer.pack_default_value(); - packer.end_pack(); - } - } - - return Datagram(packer.get_data(), packer.get_length()); -} -#endif // HAVE_PYTHON /** * Write a string representation of this instance to . diff --git a/direct/src/dcparser/dcClass.h b/direct/src/dcparser/dcClass.h index 9e981a0097..73ad9e43d3 100644 --- a/direct/src/dcparser/dcClass.h +++ b/direct/src/dcparser/dcClass.h @@ -117,11 +117,6 @@ PUBLISHED: Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id, ZONEID_TYPE zone_id, PyObject *optional_fields) const; - Datagram ai_database_generate_context(unsigned int context_id, DOID_TYPE parent_id, ZONEID_TYPE zone_id, CHANNEL_TYPE owner_channel, - CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const; - Datagram ai_database_generate_context_old(unsigned int context_id, DOID_TYPE parent_id, ZONEID_TYPE zone_id, - CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const; - #endif public: diff --git a/direct/src/dcparser/dcField.cxx b/direct/src/dcparser/dcField.cxx index 88b9786f51..a44412638a 100644 --- a/direct/src/dcparser/dcField.cxx +++ b/direct/src/dcparser/dcField.cxx @@ -391,7 +391,7 @@ Datagram DCField:: client_format_update(DOID_TYPE do_id, PyObject *args) const { DCPacker packer; - packer.raw_pack_uint16(CLIENT_OBJECT_UPDATE_FIELD); + packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD); packer.raw_pack_uint32(do_id); packer.raw_pack_uint16(_number); @@ -417,7 +417,7 @@ ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyOb packer.raw_pack_uint8(1); packer.RAW_PACK_CHANNEL(to_id); packer.RAW_PACK_CHANNEL(from_id); - packer.raw_pack_uint16(STATESERVER_OBJECT_UPDATE_FIELD); + packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD); packer.raw_pack_uint32(do_id); packer.raw_pack_uint16(_number); diff --git a/direct/src/dcparser/dcmsgtypes.h b/direct/src/dcparser/dcmsgtypes.h index e5bbe81614..0e83f384c0 100644 --- a/direct/src/dcparser/dcmsgtypes.h +++ b/direct/src/dcparser/dcmsgtypes.h @@ -17,16 +17,13 @@ // This file defines the server message types used within this module. It // duplicates some symbols defined in MsgTypes.py and AIMsgTypes.py. -#define CLIENT_OBJECT_UPDATE_FIELD 24 -#define CLIENT_CREATE_OBJECT_REQUIRED 34 -#define CLIENT_CREATE_OBJECT_REQUIRED_OTHER 35 +#define CLIENT_OBJECT_SET_FIELD 120 +#define CLIENT_ENTER_OBJECT_REQUIRED 142 +#define CLIENT_ENTER_OBJECT_REQUIRED_OTHER 143 -#define STATESERVER_OBJECT_GENERATE_WITH_REQUIRED 2001 -#define STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER 2003 -#define STATESERVER_OBJECT_UPDATE_FIELD 2004 -#define STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT 2050 -#define STATESERVER_OBJECT_CREATE_WITH_REQUIR_OTHER_CONTEXT 2051 -#define STATESERVER_BOUNCE_MESSAGE 2086 +#define STATESERVER_CREATE_OBJECT_WITH_REQUIRED 2000 +#define STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER 2001 +#define STATESERVER_OBJECT_SET_FIELD 2020 #define CLIENT_OBJECT_GENERATE_CMU 9002 diff --git a/direct/src/directtools/DirectSelection.py b/direct/src/directtools/DirectSelection.py index ef14fb429b..49410656b3 100644 --- a/direct/src/directtools/DirectSelection.py +++ b/direct/src/directtools/DirectSelection.py @@ -623,8 +623,8 @@ class SelectionRay(SelectionQueue): def pickBitMask(self, bitMask = BitMask32.allOff(), targetNodePath = None, skipFlags = SKIP_ALL): - if parentNodePath is None: - parentNodePath = render + if targetNodePath is None: + targetNodePath = render self.collideWithBitMask(bitMask) self.pick(targetNodePath) # Determine collision entry diff --git a/direct/src/distributed/CRDataCache.py b/direct/src/distributed/CRDataCache.py index 85036544de..b28453c0a3 100755 --- a/direct/src/distributed/CRDataCache.py +++ b/direct/src/distributed/CRDataCache.py @@ -2,6 +2,8 @@ from direct.distributed.CachedDOData import CachedDOData from panda3d.core import ConfigVariableInt +__all__ = ["CRDataCache"] + class CRDataCache: # Stores cached data for DistributedObjects between instantiations on the client diff --git a/direct/src/distributed/ClientRepositoryBase.py b/direct/src/distributed/ClientRepositoryBase.py index 826d3058fd..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: @@ -263,7 +263,7 @@ class ClientRepositoryBase(ConnectionRepository): distObj.setLocation(parentId, zoneId) distObj.updateRequiredFields(dclass, di) # updateRequiredFields calls announceGenerate - print("New DO:%s, dclass:%s"%(doId, dclass.getName())) + self.notify.debug("New DO:%s, dclass:%s" % (doId, dclass.getName())) return distObj def generateWithRequiredOtherFields(self, dclass, doId, di, @@ -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/ConnectionRepository.py b/direct/src/distributed/ConnectionRepository.py index 105b77a9a9..4d6dec5b81 100644 --- a/direct/src/distributed/ConnectionRepository.py +++ b/direct/src/distributed/ConnectionRepository.py @@ -10,6 +10,7 @@ from .PyDatagramIterator import PyDatagramIterator import types import gc +__all__ = ["ConnectionRepository", "GCTrigger"] class ConnectionRepository( DoInterestManager, DoCollectionManager, CConnectionRepository): diff --git a/direct/src/distributed/DistributedNode.py b/direct/src/distributed/DistributedNode.py index 1b0c3412dc..1a88fcc02a 100644 --- a/direct/src/distributed/DistributedNode.py +++ b/direct/src/distributed/DistributedNode.py @@ -15,6 +15,8 @@ class DistributedNode(DistributedObject.DistributedObject, NodePath): self.DistributedNode_initialized = 1 self.gotStringParentToken = 0 DistributedObject.DistributedObject.__init__(self, cr) + if not self.this: + NodePath.__init__(self, "DistributedNode") # initialize gridParent self.gridParent = None diff --git a/direct/src/distributed/DistributedNodeUD.py b/direct/src/distributed/DistributedNodeUD.py index 21d6a2dbbb..39c0489f5e 100755 --- a/direct/src/distributed/DistributedNodeUD.py +++ b/direct/src/distributed/DistributedNodeUD.py @@ -1,4 +1,3 @@ -#from otp.ai.AIBaseGlobal import * from .DistributedObjectUD import DistributedObjectUD class DistributedNodeUD(DistributedObjectUD): diff --git a/direct/src/distributed/DistributedObjectAI.py b/direct/src/distributed/DistributedObjectAI.py index efe5cd02bf..32b2374fc5 100644 --- a/direct/src/distributed/DistributedObjectAI.py +++ b/direct/src/distributed/DistributedObjectAI.py @@ -146,8 +146,6 @@ class DistributedObjectAI(DistributedObjectBase): barrier.cleanup() self.__barriers = {} - self.air.stopTrackRequestDeletedDO(self) - # DCR: I've re-enabled this block of code so that Toontown's # AI won't leak channels. # Let me know if it causes trouble. @@ -155,10 +153,9 @@ class DistributedObjectAI(DistributedObjectBase): ### block until a solution is thought out of how to prevent ### this delete message or to handle this message better # TODO: do we still need this check? - if not hasattr(self, "doNotDeallocateChannel"): - if self.air and not hasattr(self.air, "doNotDeallocateChannel"): - if self.air.minChannel <= self.doId <= self.air.maxChannel: - self.air.deallocateChannel(self.doId) + if not getattr(self, "doNotDeallocateChannel", False): + if self.air: + self.air.deallocateChannel(self.doId) self.air = None self.parentId = None @@ -200,9 +197,6 @@ class DistributedObjectAI(DistributedObjectBase): """ pass - def addInterest(self, zoneId, note="", event=None): - self.air.addInterest(self.doId, zoneId, note, event) - def b_setLocation(self, parentId, zoneId): self.d_setLocation(parentId, zoneId) self.setLocation(parentId, zoneId) @@ -274,9 +268,6 @@ class DistributedObjectAI(DistributedObjectBase): dclass.receiveUpdateOther(self, di) - def sendSetZone(self, zoneId): - self.air.sendSetZone(self, zoneId) - def startMessageBundle(self, name): self.air.startMessageBundle(name) def sendMessageBundle(self): @@ -349,10 +340,10 @@ class DistributedObjectAI(DistributedObjectBase): self.air.sendUpdate(self, fieldName, args) def GetPuppetConnectionChannel(self, doId): - return doId + (1 << 32) + return doId + (1001 << 32) def GetAccountConnectionChannel(self, doId): - return doId + (3 << 32) + return doId + (1003 << 32) def GetAccountIDFromChannelCode(self, channel): return channel >> 32 @@ -482,7 +473,6 @@ class DistributedObjectAI(DistributedObjectBase): (self.__class__, doId)) return self.air.requestDelete(self) - self.air.startTrackRequestDeletedDO(self) self._DOAI_requestedDelete = True def taskName(self, taskString): @@ -581,3 +571,5 @@ class DistributedObjectAI(DistributedObjectBase): """ This is a no-op on the AI. """ pass + def setAI(self, aiChannel): + self.air.setAI(self.doId, aiChannel) diff --git a/direct/src/distributed/DistributedObjectBase.py b/direct/src/distributed/DistributedObjectBase.py index af5f2678f3..3d9b525067 100755 --- a/direct/src/distributed/DistributedObjectBase.py +++ b/direct/src/distributed/DistributedObjectBase.py @@ -1,4 +1,3 @@ - from direct.showbase.DirectObject import DirectObject from direct.directnotify.DirectNotifyGlobal import directNotify @@ -93,3 +92,11 @@ class DistributedObjectBase(DirectObject): def hasParentingRules(self): return self.dclass.getFieldByName('setParentingRules') != None + + def delete(self): + """ + Override this to handle cleanup right before this object + gets deleted. + """ + + pass diff --git a/direct/src/distributed/DistributedObjectUD.py b/direct/src/distributed/DistributedObjectUD.py index c67d0004cc..99762cc1d3 100755 --- a/direct/src/distributed/DistributedObjectUD.py +++ b/direct/src/distributed/DistributedObjectUD.py @@ -270,10 +270,10 @@ class DistributedObjectUD(DistributedObjectBase): self.air.sendUpdate(self, fieldName, args) def GetPuppetConnectionChannel(self, doId): - return doId + (1 << 32) + return doId + (1001 << 32) def GetAccountConnectionChannel(self, doId): - return doId + (3 << 32) + return doId + (1003 << 32) def GetAccountIDFromChannelCode(self, channel): return channel >> 32 diff --git a/direct/src/distributed/DoInterestManager.py b/direct/src/distributed/DoInterestManager.py index eb616d3f26..08a2f21657 100755 --- a/direct/src/distributed/DoInterestManager.py +++ b/direct/src/distributed/DoInterestManager.py @@ -111,7 +111,7 @@ class DoInterestManager(DirectObject.DirectObject): self._allInterestsCompleteCallbacks = [] def __verbose(self): - return self.InterestDebug or self.getVerbose() + return self.InterestDebug.getValue() or self.getVerbose() def _getAnonymousEvent(self, desc): return 'anonymous-%s-%s' % (desc, DoInterestManager._SerialGen.next()) @@ -504,18 +504,23 @@ class DoInterestManager(DirectObject.DirectObject): 'trying to set interest to invalid parent: %s' % parentId) datagram = PyDatagram() # Add message type - datagram.addUint16(CLIENT_ADD_INTEREST) - datagram.addUint16(handle) - datagram.addUint32(contextId) - datagram.addUint32(parentId) if isinstance(zoneIdList, list): vzl = list(zoneIdList) vzl.sort() uniqueElements(vzl) + datagram.addUint16(CLIENT_ADD_INTEREST_MULTIPLE) + datagram.addUint32(contextId) + datagram.addUint16(handle) + datagram.addUint32(parentId) + datagram.addUint16(len(vzl)) for zone in vzl: datagram.addUint32(zone) else: - datagram.addUint32(zoneIdList) + datagram.addUint16(CLIENT_ADD_INTEREST) + datagram.addUint32(contextId) + datagram.addUint16(handle) + datagram.addUint32(parentId) + datagram.addUint32(zoneIdList) self.send(datagram) def _sendRemoveInterest(self, handle, contextId): @@ -530,9 +535,8 @@ class DoInterestManager(DirectObject.DirectObject): datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_REMOVE_INTEREST) + datagram.addUint32(contextId) datagram.addUint16(handle) - if contextId != 0: - datagram.addUint32(contextId) self.send(datagram) if __debug__: state = DoInterestManager._interests[handle] @@ -583,8 +587,8 @@ class DoInterestManager(DirectObject.DirectObject): This handles the interest done messages and may dispatch an event """ assert DoInterestManager.notify.debugCall() - handle = di.getUint16() contextId = di.getUint32() + handle = di.getUint16() if self.__verbose(): print('CR::INTEREST.interestDone(handle=%s)' % handle) DoInterestManager.notify.debug( diff --git a/direct/src/distributed/MsgTypes.py b/direct/src/distributed/MsgTypes.py index b5e5d260df..14ce6ba55e 100644 --- a/direct/src/distributed/MsgTypes.py +++ b/direct/src/distributed/MsgTypes.py @@ -3,104 +3,140 @@ from direct.showbase.PythonUtil import invertDictLossless MsgName2Id = { - # 2 new params: passwd, char bool 0/1 1 = new account - # 2 new return values: 129 = not found, 12 = bad passwd, - 'CLIENT_LOGIN': 1, - 'CLIENT_LOGIN_RESP': 2, - 'CLIENT_GET_AVATARS': 3, + 'CLIENT_HELLO': 1, + 'CLIENT_HELLO_RESP': 2, + + # Sent by the client when it's leaving. + 'CLIENT_DISCONNECT': 3, + # Sent by the server when it is dropping the connection deliberately. - 'CLIENT_GO_GET_LOST': 4, - 'CLIENT_GET_AVATARS_RESP': 5, - 'CLIENT_CREATE_AVATAR': 6, - 'CLIENT_CREATE_AVATAR_RESP': 7, - 'CLIENT_GET_FRIEND_LIST': 10, - 'CLIENT_GET_FRIEND_LIST_RESP': 11, - 'CLIENT_GET_AVATAR_DETAILS': 14, - 'CLIENT_GET_AVATAR_DETAILS_RESP': 15, - 'CLIENT_LOGIN_2': 16, - 'CLIENT_LOGIN_2_RESP': 17, + 'CLIENT_EJECT': 4, - 'CLIENT_OBJECT_UPDATE_FIELD': 24, - 'CLIENT_OBJECT_UPDATE_FIELD_RESP': 24, - 'CLIENT_OBJECT_DISABLE': 25, - 'CLIENT_OBJECT_DISABLE_RESP': 25, - 'CLIENT_OBJECT_DISABLE_OWNER': 26, - 'CLIENT_OBJECT_DISABLE_OWNER_RESP': 26, - 'CLIENT_OBJECT_DELETE': 27, - 'CLIENT_OBJECT_DELETE_RESP': 27, - 'CLIENT_SET_ZONE_CMU': 29, - 'CLIENT_REMOVE_ZONE': 30, - 'CLIENT_SET_AVATAR': 32, - 'CLIENT_CREATE_OBJECT_REQUIRED': 34, - 'CLIENT_CREATE_OBJECT_REQUIRED_RESP': 34, - 'CLIENT_CREATE_OBJECT_REQUIRED_OTHER': 35, - 'CLIENT_CREATE_OBJECT_REQUIRED_OTHER_RESP': 35, - 'CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER': 36, - 'CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER_RESP':36, + 'CLIENT_HEARTBEAT': 5, - 'CLIENT_REQUEST_GENERATES': 36, + 'CLIENT_OBJECT_SET_FIELD': 120, + 'CLIENT_OBJECT_SET_FIELDS': 121, + 'CLIENT_OBJECT_LEAVING': 132, + 'CLIENT_OBJECT_LEAVING_OWNER': 161, + 'CLIENT_ENTER_OBJECT_REQUIRED': 142, + 'CLIENT_ENTER_OBJECT_REQUIRED_OTHER': 143, + 'CLIENT_ENTER_OBJECT_REQUIRED_OWNER': 172, + 'CLIENT_ENTER_OBJECT_REQUIRED_OTHER_OWNER': 173, - 'CLIENT_DISCONNECT': 37, + 'CLIENT_DONE_INTEREST_RESP': 204, - 'CLIENT_GET_STATE_RESP': 47, - 'CLIENT_DONE_INTEREST_RESP': 48, - - 'CLIENT_DELETE_AVATAR': 49, - - 'CLIENT_DELETE_AVATAR_RESP': 5, - - 'CLIENT_HEARTBEAT': 52, - 'CLIENT_FRIEND_ONLINE': 53, - 'CLIENT_FRIEND_OFFLINE': 54, - 'CLIENT_REMOVE_FRIEND': 56, - - 'CLIENT_CHANGE_PASSWORD': 65, - - 'CLIENT_SET_NAME_PATTERN': 67, - 'CLIENT_SET_NAME_PATTERN_ANSWER': 68, - - 'CLIENT_SET_WISHNAME': 70, - 'CLIENT_SET_WISHNAME_RESP': 71, - 'CLIENT_SET_WISHNAME_CLEAR': 72, - 'CLIENT_SET_SECURITY': 73, - - 'CLIENT_SET_DOID_RANGE': 74, - - 'CLIENT_GET_AVATARS_RESP2': 75, - 'CLIENT_CREATE_AVATAR2': 76, - 'CLIENT_SYSTEM_MESSAGE': 78, - 'CLIENT_SET_AVTYPE': 80, - - 'CLIENT_GET_PET_DETAILS': 81, - 'CLIENT_GET_PET_DETAILS_RESP': 82, - - 'CLIENT_ADD_INTEREST': 97, - 'CLIENT_REMOVE_INTEREST': 99, - 'CLIENT_OBJECT_LOCATION': 102, - - 'CLIENT_LOGIN_3': 111, - 'CLIENT_LOGIN_3_RESP': 110, - - 'CLIENT_GET_FRIEND_LIST_EXTENDED': 115, - 'CLIENT_GET_FRIEND_LIST_EXTENDED_RESP': 116, - - 'CLIENT_SET_FIELD_SENDABLE': 120, - - 'CLIENT_SYSTEMMESSAGE_AKNOWLEDGE': 123, - 'CLIENT_CHANGE_GENERATE_ORDER': 124, - - # new toontown specific login message, adds last logged in, and if child account has parent acount - 'CLIENT_LOGIN_TOONTOWN': 125, - 'CLIENT_LOGIN_TOONTOWN_RESP': 126, + 'CLIENT_ADD_INTEREST': 200, + 'CLIENT_ADD_INTEREST_MULTIPLE': 201, + 'CLIENT_REMOVE_INTEREST': 203, + 'CLIENT_OBJECT_LOCATION': 140, + # These are sent internally inside the Astron cluster. - 'STATESERVER_OBJECT_GENERATE_WITH_REQUIRED': 2001, - 'STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER': 2003, - 'STATESERVER_OBJECT_UPDATE_FIELD': 2004, - 'STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT': 2050, - 'STATESERVER_OBJECT_CREATE_WITH_REQUIR_OTHER_CONTEXT': 2051, - 'STATESERVER_BOUNCE_MESSAGE': 2086, + # Message Director control messages: + 'CONTROL_CHANNEL': 1, + 'CONTROL_ADD_CHANNEL': 9000, + 'CONTROL_REMOVE_CHANNEL': 9001, + 'CONTROL_ADD_RANGE': 9002, + 'CONTROL_REMOVE_RANGE': 9003, + 'CONTROL_ADD_POST_REMOVE': 9010, + 'CONTROL_CLEAR_POST_REMOVES': 9011, + + # State Server control messages: + 'STATESERVER_CREATE_OBJECT_WITH_REQUIRED': 2000, + 'STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER': 2001, + 'STATESERVER_DELETE_AI_OBJECTS': 2009, + 'STATESERVER_OBJECT_GET_FIELD': 2010, + 'STATESERVER_OBJECT_GET_FIELD_RESP': 2011, + 'STATESERVER_OBJECT_GET_FIELDS': 2012, + 'STATESERVER_OBJECT_GET_FIELDS_RESP': 2013, + 'STATESERVER_OBJECT_GET_ALL': 2014, + 'STATESERVER_OBJECT_GET_ALL_RESP': 2015, + 'STATESERVER_OBJECT_SET_FIELD': 2020, + 'STATESERVER_OBJECT_SET_FIELDS': 2021, + 'STATESERVER_OBJECT_DELETE_FIELD_RAM': 2030, + 'STATESERVER_OBJECT_DELETE_FIELDS_RAM': 2031, + 'STATESERVER_OBJECT_DELETE_RAM': 2032, + 'STATESERVER_OBJECT_SET_LOCATION': 2040, + 'STATESERVER_OBJECT_CHANGING_LOCATION': 2041, + 'STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED': 2042, + 'STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER': 2043, + 'STATESERVER_OBJECT_GET_LOCATION': 2044, + 'STATESERVER_OBJECT_GET_LOCATION_RESP': 2045, + 'STATESERVER_OBJECT_SET_AI': 2050, + 'STATESERVER_OBJECT_CHANGING_AI': 2051, + 'STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED': 2052, + 'STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED_OTHER': 2053, + 'STATESERVER_OBJECT_GET_AI': 2054, + 'STATESERVER_OBJECT_GET_AI_RESP': 2055, + 'STATESERVER_OBJECT_SET_OWNER': 2060, + 'STATESERVER_OBJECT_CHANGING_OWNER': 2061, + 'STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED': 2062, + 'STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_OTHER': 2063, + 'STATESERVER_OBJECT_GET_OWNER': 2064, + 'STATESERVER_OBJECT_GET_OWNER_RESP': 2065, + 'STATESERVER_OBJECT_GET_ZONE_OBJECTS': 2100, + 'STATESERVER_OBJECT_GET_ZONES_OBJECTS': 2102, + 'STATESERVER_OBJECT_GET_CHILDREN': 2104, + 'STATESERVER_OBJECT_GET_ZONE_COUNT': 2110, + 'STATESERVER_OBJECT_GET_ZONE_COUNT_RESP': 2111, + 'STATESERVER_OBJECT_GET_ZONES_COUNT': 2112, + 'STATESERVER_OBJECT_GET_ZONES_COUNT_RESP': 2113, + 'STATESERVER_OBJECT_GET_CHILD_COUNT': 2114, + 'STATESERVER_OBJECT_GET_CHILD_COUNT_RESP': 2115, + 'STATESERVER_OBJECT_DELETE_ZONE': 2120, + 'STATESERVER_OBJECT_DELETE_ZONES': 2122, + 'STATESERVER_OBJECT_DELETE_CHILDREN': 2124, + # DBSS-backed-object messages: + 'DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS': 2200, + 'DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS_OTHER': 2201, + 'DBSS_OBJECT_GET_ACTIVATED': 2207, + 'DBSS_OBJECT_GET_ACTIVATED_RESP': 2208, + 'DBSS_OBJECT_DELETE_FIELD_DISK': 2230, + 'DBSS_OBJECT_DELETE_FIELDS_DISK': 2231, + 'DBSS_OBJECT_DELETE_DISK': 2232, + + # Database Server control messages: + 'DBSERVER_CREATE_OBJECT': 3000, + 'DBSERVER_CREATE_OBJECT_RESP': 3001, + 'DBSERVER_OBJECT_GET_FIELD': 3010, + 'DBSERVER_OBJECT_GET_FIELD_RESP': 3011, + 'DBSERVER_OBJECT_GET_FIELDS': 3012, + 'DBSERVER_OBJECT_GET_FIELDS_RESP': 3013, + 'DBSERVER_OBJECT_GET_ALL': 3014, + 'DBSERVER_OBJECT_GET_ALL_RESP': 3015, + 'DBSERVER_OBJECT_SET_FIELD': 3020, + 'DBSERVER_OBJECT_SET_FIELDS': 3021, + 'DBSERVER_OBJECT_SET_FIELD_IF_EQUALS': 3022, + 'DBSERVER_OBJECT_SET_FIELD_IF_EQUALS_RESP': 3023, + 'DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS': 3024, + 'DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS_RESP': 3025, + 'DBSERVER_OBJECT_SET_FIELD_IF_EMPTY': 3026, + 'DBSERVER_OBJECT_SET_FIELD_IF_EMPTY_RESP': 3027, + 'DBSERVER_OBJECT_DELETE_FIELD': 3030, + 'DBSERVER_OBJECT_DELETE_FIELDS': 3031, + 'DBSERVER_OBJECT_DELETE': 3032, + + # Client Agent control messages: + 'CLIENTAGENT_SET_STATE': 1000, + 'CLIENTAGENT_SET_CLIENT_ID': 1001, + 'CLIENTAGENT_SEND_DATAGRAM': 1002, + 'CLIENTAGENT_EJECT': 1004, + 'CLIENTAGENT_DROP': 1005, + 'CLIENTAGENT_GET_NETWORK_ADDRESS': 1006, + 'CLIENTAGENT_GET_NETWORK_ADDRESS_RESP': 1007, + 'CLIENTAGENT_DECLARE_OBJECT': 1010, + 'CLIENTAGENT_UNDECLARE_OBJECT': 1011, + 'CLIENTAGENT_ADD_SESSION_OBJECT': 1012, + 'CLIENTAGENT_REMOVE_SESSION_OBJECT': 1013, + 'CLIENTAGENT_SET_FIELDS_SENDABLE': 1014, + 'CLIENTAGENT_OPEN_CHANNEL': 1100, + 'CLIENTAGENT_CLOSE_CHANNEL': 1101, + 'CLIENTAGENT_ADD_POST_REMOVE': 1110, + 'CLIENTAGENT_CLEAR_POST_REMOVES': 1111, + 'CLIENTAGENT_ADD_INTEREST': 1200, + 'CLIENTAGENT_ADD_INTEREST_MULTIPLE': 1201, + 'CLIENTAGENT_REMOVE_INTEREST': 1203, } # create id->name table for debugging 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/PyDatagram.py b/direct/src/distributed/PyDatagram.py index 74e73fbf28..eebc06e440 100755 --- a/direct/src/distributed/PyDatagram.py +++ b/direct/src/distributed/PyDatagram.py @@ -7,7 +7,7 @@ from panda3d.core import Datagram from panda3d.direct import * # Import the type numbers -#from otp.ai.AIMsgTypes import * +from direct.distributed.MsgTypes import * class PyDatagram(Datagram): @@ -47,13 +47,10 @@ class PyDatagram(Datagram): self.addUint16(code) -# def addServerControlHeader(self, code): -# self.addInt8(1) -# self.addChannel(CONTROL_MESSAGE) -# self.addUint16(code) -# def addOldServerControlHeader(self, code): -# self.addChannel(CONTROL_MESSAGE) -# self.addUint16(code) + def addServerControlHeader(self, code): + self.addInt8(1) + self.addChannel(CONTROL_CHANNEL) + self.addUint16(code) def putArg(self, arg, subatomicType, divisor=1): if (divisor == 1): diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx index 1781422930..67d6ec1658 100644 --- a/direct/src/distributed/cConnectionRepository.cxx +++ b/direct/src/distributed/cConnectionRepository.cxx @@ -301,8 +301,8 @@ check_datagram() { switch (_msg_type) { #ifdef HAVE_PYTHON - case CLIENT_OBJECT_UPDATE_FIELD: - case STATESERVER_OBJECT_UPDATE_FIELD: + case CLIENT_OBJECT_SET_FIELD: + case STATESERVER_OBJECT_SET_FIELD: if (_handle_c_updates) { if (_has_owner_view) { if (!handle_update_field_owner()) { @@ -494,7 +494,7 @@ send_message_bundle(unsigned int channel, unsigned int sender_channel) { dg.add_int8(1); dg.add_uint64(channel); dg.add_uint64(sender_channel); - dg.add_uint16(STATESERVER_BOUNCE_MESSAGE); + //dg.add_uint16(STATESERVER_BOUNCE_MESSAGE); // add each bundled message BundledMsgVector::const_iterator bmi; for (bmi = _bundle_msgs.begin(); bmi != _bundle_msgs.end(); bmi++) { @@ -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 @@ -899,11 +899,11 @@ describe_message(ostream &out, const string &prefix, packer.RAW_UNPACK_CHANNEL(); // msg_sender msg_type = packer.raw_unpack_uint16(); - is_update = (msg_type == STATESERVER_OBJECT_UPDATE_FIELD); + is_update = (msg_type == STATESERVER_OBJECT_SET_FIELD); } else { msg_type = packer.raw_unpack_uint16(); - is_update = (msg_type == CLIENT_OBJECT_UPDATE_FIELD); + is_update = (msg_type == CLIENT_OBJECT_SET_FIELD); } if (!is_update) { @@ -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/cDistributedSmoothNodeBase.cxx b/direct/src/distributed/cDistributedSmoothNodeBase.cxx index 70a50f33f5..14a665d9b9 100644 --- a/direct/src/distributed/cDistributedSmoothNodeBase.cxx +++ b/direct/src/distributed/cDistributedSmoothNodeBase.cxx @@ -278,12 +278,12 @@ begin_send_update(DCPacker &packer, const string &field_name) { packer.RAW_PACK_CHANNEL(_do_id); packer.RAW_PACK_CHANNEL(_ai_id); // packer.raw_pack_uint8('A'); - packer.raw_pack_uint16(STATESERVER_OBJECT_UPDATE_FIELD); + packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD); packer.raw_pack_uint32(_do_id); packer.raw_pack_uint16(field->get_number()); } else { - packer.raw_pack_uint16(CLIENT_OBJECT_UPDATE_FIELD); + packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD); packer.raw_pack_uint32(_do_id); packer.raw_pack_uint16(field->get_number()); } diff --git a/direct/src/filter/CommonFilters.py b/direct/src/filter/CommonFilters.py index 1d49b065d4..16e061f3d7 100644 --- a/direct/src/filter/CommonFilters.py +++ b/direct/src/filter/CommonFilters.py @@ -132,6 +132,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"]) @@ -339,7 +342,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]) diff --git a/direct/src/gui/DirectEntry.py b/direct/src/gui/DirectEntry.py index 2eb7607400..6955e550a2 100644 --- a/direct/src/gui/DirectEntry.py +++ b/direct/src/gui/DirectEntry.py @@ -59,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), @@ -159,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/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py index 6836f7465f..3180d0dfce 100644 --- a/direct/src/gui/DirectGuiBase.py +++ b/direct/src/gui/DirectGuiBase.py @@ -80,7 +80,8 @@ __all__ = ['DirectGuiBase', 'DirectGuiWidget'] from panda3d.core import * -from panda3d.direct import get_config_showbase +from direct.showbase import ShowBaseGlobal +from direct.showbase.ShowBase import ShowBase from . import DirectGuiGlobals as DGG from .OnscreenText import * from .OnscreenGeom import * @@ -633,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()) @@ -662,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: @@ -723,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']: @@ -1024,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/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/ShadowDemo.py b/direct/src/showbase/ShadowDemo.py index daadd58c11..efcc491e71 100755 --- a/direct/src/showbase/ShadowDemo.py +++ b/direct/src/showbase/ShadowDemo.py @@ -201,13 +201,13 @@ def arbitraryShadow(node): ##b.reparentTo((base.localAvatar)) ##a = AmbientLight('cloudAmbientHi') ##a.setColor(Vec4(0.9, 0.9, 0.9, 1.000)) -##aNP = s.attachNewNode(a.upcastToPandaNode()) +##aNP = s.attachNewNode(a) ##b.setLight(aNP) ##d = DirectionalLight("chernabogDirectionalLight") ##d.setDirection(Vec3(0, 1, 0)) ##d.setColor(Vec4(1)) ###d.setColor(Vec4(0.9, 0.7, 0.7, 1.000)) -##dNP = s.attachNewNode(d.upcastToPandaNode()) +##dNP = s.attachNewNode(d) ##b.setLight(dNP) ## ##ival = Sequence(LerpPosInterval(bs.lightPath, 0.0, Vec3(-200, 0, 50)), diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 99a8b77abf..367ae57d1a 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): @@ -1097,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,8 +1272,7 @@ class ShowBase(DirectObject.DirectObject): if win == None: win = self.win - if win != None and win.getSideBySideStereo() and \ - win.hasSize() and win.getSbsLeftYSize() != 0: + 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"): @@ -1542,8 +1553,7 @@ class ShowBase(DirectObject.DirectObject): # mouse activity. mw = self.buttonThrowers[0].getParent() mouseRecorder = MouseRecorder('mouse') - self.recorder.addRecorder( - 'mouse', mouseRecorder.upcastToRecorderBase()) + self.recorder.addRecorder('mouse', mouseRecorder) np = mw.getParent().attachNewNode(mouseRecorder) mw.reparentTo(np) @@ -2726,9 +2736,10 @@ class ShowBase(DirectObject.DirectObject): # changed and update the camera lenses and aspect2d parameters self.adjustWindowAspectRatio(self.getAspectRatio()) - if win.getSideBySideStereo() and win.hasSize() and win.getSbsLeftYSize() != 0: + 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: 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/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/dtool/metalibs/dtoolconfig/pydtool.cxx b/dtool/metalibs/dtoolconfig/pydtool.cxx index d43ca96f9a..b83fb05e11 100644 --- a/dtool/metalibs/dtoolconfig/pydtool.cxx +++ b/dtool/metalibs/dtoolconfig/pydtool.cxx @@ -1,6 +1,6 @@ /* * This file was generated by: - * interrogate -D EXPCL_DTOOLCONFIG= -nodb -python -promiscuous -I/home/rdb/panda3d-git/built/include -module panda3d.interrogatedb -library interrogatedb -string -true-names -do-module -oc pydtool.cxx ../../src/interrogatedb/interrogate_interface.h ../../src/interrogatedb/interrogate_request.h + * interrogate -D EXPCL_DTOOLCONFIG= -nodb -python -promiscuous -I../../../built/include -module panda3d.interrogatedb -library interrogatedb -string -true-names -do-module -oc pydtool.cxx ../../src/interrogatedb/interrogate_interface.h ../../src/interrogatedb/interrogate_request.h * */ @@ -15,7 +15,7 @@ #define PY_SSIZE_T_CLEAN 1 #if PYTHON_FRAMEWORK - #include "Python/Python.h" + #include #else #include "Python.h" #endif @@ -85,6 +85,9 @@ static PyObject *_inP07ytsqGH(PyObject *self, PyObject *args); static PyObject *_inP07yt7shV(PyObject *self, PyObject *args); static PyObject *_inP07ytA1eF(PyObject *self, PyObject *args); static PyObject *_inP07yt776V(PyObject *self, PyObject *args); +static PyObject *_inP07ytryup(PyObject *self, PyObject *args); +static PyObject *_inP07ytiytI(PyObject *self, PyObject *args); +static PyObject *_inP07ytZc07(PyObject *self, PyObject *args); static PyObject *_inP07ytfaH0(PyObject *self, PyObject *args); static PyObject *_inP07ytGB9D(PyObject *self, PyObject *args); static PyObject *_inP07ytsxxs(PyObject *self, PyObject *args); @@ -94,6 +97,7 @@ static PyObject *_inP07yt4Px8(PyObject *self, PyObject *args); static PyObject *_inP07ytNHcs(PyObject *self, PyObject *args); static PyObject *_inP07ytqHrb(PyObject *self, PyObject *args); static PyObject *_inP07ytaOqq(PyObject *self, PyObject *args); +static PyObject *_inP07ytpTBb(PyObject *self, PyObject *args); static PyObject *_inP07ytqWOw(PyObject *self, PyObject *args); static PyObject *_inP07ytHu7x(PyObject *self, PyObject *args); static PyObject *_inP07ytwGnA(PyObject *self, PyObject *args); @@ -1237,6 +1241,56 @@ _inP07yt776V(PyObject *, PyObject *args) { return (PyObject *)NULL; } +/* + * Python simple wrapper for + * char const *interrogate_make_seq_scoped_name(MakeSeqIndex make_seq) + */ +static PyObject * +_inP07ytryup(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_make_seq_scoped_name((MakeSeqIndex)param0); +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromString(return_value); +#else + return PyString_FromString(return_value); +#endif + } + return (PyObject *)NULL; +} + +/* + * Python simple wrapper for + * bool interrogate_make_seq_has_comment(ElementIndex element) + */ +static PyObject * +_inP07ytiytI(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_make_seq_has_comment((ElementIndex)param0); + return PyBool_FromLong(return_value); + } + return (PyObject *)NULL; +} + +/* + * Python simple wrapper for + * char const *interrogate_make_seq_comment(ElementIndex element) + */ +static PyObject * +_inP07ytZc07(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_make_seq_comment((ElementIndex)param0); +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromString(return_value); +#else + return PyString_FromString(return_value); +#endif + } + return (PyObject *)NULL; +} + /* * Python simple wrapper for * char const *interrogate_make_seq_num_name(MakeSeqIndex make_seq) @@ -1397,6 +1451,20 @@ _inP07ytaOqq(PyObject *, PyObject *args) { return (PyObject *)NULL; } +/* + * Python simple wrapper for + * bool interrogate_type_is_global(TypeIndex type) + */ +static PyObject * +_inP07ytpTBb(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_global((TypeIndex)param0); + return PyBool_FromLong(return_value); + } + return (PyObject *)NULL; +} + /* * Python simple wrapper for * char const *interrogate_type_name(TypeIndex type) @@ -2416,6 +2484,9 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_wrapper_unique_name", &_inP07yt7shV, METH_VARARGS }, { "interrogate_get_wrapper_by_unique_name", &_inP07ytA1eF, METH_VARARGS }, { "interrogate_make_seq_seq_name", &_inP07yt776V, METH_VARARGS }, + { "interrogate_make_seq_scoped_name", &_inP07ytryup, METH_VARARGS }, + { "interrogate_make_seq_has_comment", &_inP07ytiytI, METH_VARARGS }, + { "interrogate_make_seq_comment", &_inP07ytZc07, METH_VARARGS }, { "interrogate_make_seq_num_name", &_inP07ytfaH0, METH_VARARGS }, { "interrogate_make_seq_element_name", &_inP07ytGB9D, METH_VARARGS }, { "interrogate_number_of_global_types", &_inP07ytsxxs, METH_VARARGS }, @@ -2425,6 +2496,7 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_get_type_by_name", &_inP07ytNHcs, METH_VARARGS }, { "interrogate_get_type_by_scoped_name", &_inP07ytqHrb, METH_VARARGS }, { "interrogate_get_type_by_true_name", &_inP07ytaOqq, METH_VARARGS }, + { "interrogate_type_is_global", &_inP07ytpTBb, METH_VARARGS }, { "interrogate_type_name", &_inP07ytqWOw, METH_VARARGS }, { "interrogate_type_scoped_name", &_inP07ytHu7x, METH_VARARGS }, { "interrogate_type_true_name", &_inP07ytwGnA, METH_VARARGS }, diff --git a/dtool/src/attach/ctallihave b/dtool/src/attach/ctallihave deleted file mode 100755 index 5e6f7f88ed..0000000000 --- a/dtool/src/attach/ctallihave +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/perl - -if ($#ARGV != -1) { - exit print "Usage: ctihave\n" ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "not configured for using CTtools\n" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$projs = $ENV{"CTPROJS"} ; -@projsplit = split( / +/, $projs ) ; - -foreach $item ( @projsplit ) { - @items = split( /:/, $item ) ; - $thisproj = $items[0] ; - $thisflav = $items[1] ; - $thisspec = &CTResolveSpec( $thisproj, $thisflav ) ; - $result = $result . &CTCMIHave( $thisproj, $thisflav, $thisspec ) ; -} -if ( $result ne "" ) { - @splitlist = split( /\n/, $result ) ; - foreach $item ( @splitlist ) { - print $item . "\n" ; - } -} diff --git a/dtool/src/attach/ctattach.drv b/dtool/src/attach/ctattach.drv deleted file mode 100755 index c5b02718b8..0000000000 --- a/dtool/src/attach/ctattach.drv +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/perl - -# acceptable forms: -# ctattach - give usage message -# ctattach project - attach to the personal flavor of the project -# ctattach project flavor - attach to a specific flavor of the project -# ctattach - - list projects that can be attached to -# ctattach project - - list flavors of a given project -# ctattach - flavor - list projects with a certain flavor -# ctattach -def project flavor - attach to project, setting CTDEFAULT_FLAV -# to flavor for the scope of this attach - -sub CTAttachUsage { - print STDERR "Usage: ctattach -def project flavor -or-\n" ; - print STDERR " ctattach project [flavor] -or-\n" ; - print STDERR " ctattach project - -or-\n" ; - print STDERR " ctattach - [flavor]\n" ; - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; - exit; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "\$" . "DTOOL environment must be set to use CTtools\n" ; -} - -require "$tool/built/include/ctattch.pl" ; - -$tmpname = "/tmp/script.$$" ; - -if ( $#ARGV == -1 ) { - &CTUDebug( "got no arguments\n" ) ; - &CTAttachUsage ; -} - -$idx = 0 ; -$proj = "" ; -$flav = "" ; -$noflav = 0 ; -$defflav = "" ; -$spread = 0 ; -$anydef = 0 ; - -# -# parse arguemnts -# - -if ( $ARGV[$idx] eq "-def" ) { - &CTUDebug( "got '-def' parameter\n" ) ; - if ( $#ARGV < ($idx + 2) ) { - &CTUDebug( "not enough arguments after -def\n" ) ; - &CTAttachUsage ; - } - $defflav = $ARGV[$idx+2] ; - $spread = 1; - &CTUDebug( "spread default flavor is '$defflav'\n" ) ; - $idx++ ; -} else { - if ( $ENV{"CTDEFAULT_FLAV"} ne "" ) { - $defflav = $ENV{"CTDEFAULT_FLAV"} ; - &CTUDebug( "environment default flavor is '$defflav'\n" ) ; - } -} - -$proj = $ARGV[$idx] ; -&CTUDebug( "project is '$proj'\n" ) ; - -if ( $defflav eq "" ) { - $defflav = "default" ; - &CTUDebug( "no environmental default, using 'default'\n" ) ; -} - -if ( $#ARGV > $idx ) { - $flav = $ARGV[$idx+1] ; - &CTUDebug( "provided flavor is '$flav'\n" ) ; -} else { - if ( $proj ne "-" ) { - $flav = $defflav; - &CTUDebug( "using environment default flavor '$flav'\n" ) ; - $noflav = 1 ; - } -} - -if (( $noflav == 1 ) || ( $flav eq "default" )) { - $anydef = 1 ; -} - -# -# act on the arguments we got -# - -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctvspec.pl" ; - -if (( $proj eq "-" ) || ( $flav eq "-" )) { - if ( $#ARGV == 0 ) { - # list projects that can be attached to - print STDERR "Projects that can be attached to:\n" ; - $_ = &CTListAllProjects ; - @projlist = split ; - foreach $item ( @projlist ) { - print STDERR " $item\n" ; - } - } elsif ( $proj eq "-" ) { - # list project that have a given flavor - print STDERR "Projects that have a '$flav' flavor:\n" ; - $_ = &CTListAllProjects ; - @projlist = split ; - foreach $item ( @projlist ) { - $tmp = &CTResolveSpec( $item, $flav ) ; - if ( $tmp ne "" ) { - print STDERR " $item\n" ; - } - } - } else { - # list flavors of a given project - print STDERR "Flavors of project '$proj':\n" ; - $_ = &CTListAllFlavors( $proj ) ; - @flavlist = split ; - foreach $item ( @flavlist ) { - print STDERR " $item\n" ; - } - } - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; -} else { - # output a real attachment - $curflav = &CTQueryProj( $proj ) ; - if (( $curflav eq "" ) || ( $noflav == 0 )) { - $envsep{"PATH"} = ":" ; - $envsep{"LD_LIBRARY_PATH"} = ":" ; - $envsep{"DYLD_LIBRARY_PATH"} = ":" ; - $envsep{"PFPATH"} = ":" ; - $envsep{"SSPATH"} = ":" ; - $envsep{"STKPATH"} = ":" ; - $envsep{"DC_PATH"} = ":" ; - $spec = &CTAttachCompute( $proj, $flav, $anydef ) ; - if ( $spec eq "" ) { - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; - } else { - &CTAttachWriteScript( $tmpname ) ; - print $tmpname . "\n" ; - } - } else { - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; - } -} diff --git a/dtool/src/attach/ctattachcc b/dtool/src/attach/ctattachcc deleted file mode 100755 index 0a9993c453..0000000000 --- a/dtool/src/attach/ctattachcc +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/perl - -if ( $#ARGV != 5 ) { - print STDERR "This is for internal use by attach ONLY\n" ; - exit ; -} - -$root = $ARGV[0] ; -$view = $ARGV[1] ; -$branch = $ARGV[2] ; -$label = $ARGV[3] ; -$vobname = $ARGV[4] ; -$proj = $ARGV[5] ; -$tmpname = "/tmp/config.$$" ; - -$emitted = 0 ; - -$ctdebug = $ENV{"CTATTACH_DEBUG"} ; - -if ($ctdebug) { - print STDERR "Params:\n 0: '$root'\n 1: '$view'\n 2: '$branch'\n" ; - print STDERR " 3: '$label'\n 4: '$vobname'\n 5: '$proj'\n" ; - print STDERR "making branch and label types for view " . $view . "\n" ; - print STDERR "executing: /usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null\n" ; - print STDERR "executing: /usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null\n" ; -} -system "/usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null\n" ; -system "/usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null\n" ; - -if ($ctdebug) { - print STDERR "creating/updating the config-spec for view " . $view . "\n" ; -} -open( CTINTERFACE, "/usr/atria/bin/cleartool catcs -tag $view |" ) ; -open( TMPFILE, "> $tmpname" ) ; -while ( ) { - if ( $_ =~ "CHECKEDOUT" ) { - if ($ctdebug) { - print STDERR "case 1:\noutputting: '$_'\n" ; - } - print TMPFILE "$_" ; - } elsif (( $_ =~ /^element \*/ ) && ( $_ =~ "/main/LATEST" ) && - !( $_ =~ /\/$proj\// )) { - if ( ! $emitted ) { - $emitted = 1 ; - print TMPFILE "element $root/... .../$branch/LATEST\n" ; - print TMPFILE "element $root/... $label -mkbranch $branch\n" ; - print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; - if ($ctdebug) { - print STDERR "case 2:\n" ; - print STDERR "outputting: 'element $root/... .../$branch/LATEST'\n" ; - print STDERR "outputting: 'element $root/... $label -mkbranch $branch'\n" ; - print STDERR "outputting: 'element $root/... /main/LATEST -mkbranch $branch'\n" ; - } - } - if ($ctdebug) { - print STDERR "case 3:\n" ; - print STDERR "outputting: '$_'\n" ; - } - print TMPFILE "$_" ; - } elsif ( $_ =~ /\/$proj\// ) { - if ( ! $emitted ) { - $emitted = 1 ; - print TMPFILE "element $root/... .../$branch/LATEST\n" ; - print TMPFILE "element $root/... $label -mkbranch $branch\n" ; - print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; - if ($ctdebug) { - print STDERR "case 4:\n" ; - print STDERR "outputting: 'element $root/... .../$branch/LATEST'\n" ; - print STDERR "outputting: 'element $root/... $label -mkbranch $branch'\n" ; - print STDERR "outputting: 'element $root/... /main/LATEST -mkbranch $branch'\n" ; - } - } - } else { - if ($ctdebug) { - print STDERR "case 5:\n" ; - print STDERR "outputting: '$_'\n" ; - } - print TMPFILE "$_" ; - } -} -close( CTINTERFACE ) ; -close( TMPFILE ) ; -if ($ctdebug) { - print STDERR "output to execute: '/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm $tmpname'\n" ; -} -system "/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm $tmpname\n" ; diff --git a/dtool/src/attach/ctattch.pl b/dtool/src/attach/ctattch.pl deleted file mode 100644 index 69f1a2ed3b..0000000000 --- a/dtool/src/attach/ctattch.pl +++ /dev/null @@ -1,530 +0,0 @@ -require "$tool/built/include/ctutils.pl" ; - -$shell_type = "csh" ; -if ( $ENV{"SHELL_TYPE"} ne "" ) { - if ( $ENV{"SHELL_TYPE"} eq "sh" ) { - $shell_type = "sh" ; - } -} - -$docnt = 0 ; -@attachqueue = () ; - -require "$tool/built/include/ctquery.pl" ; - -# force set a variable in the 'new' environment -# input is in: -# $_[0] = variable -# $_[1] = value -# -# output is in: -# %newenv = variable marked to be set to value -sub CTAttachSet { - if ( ( $_[0] ne "" ) && ( $_[1] ne "" ) ) { - &CTUDebug( "setting " . $_[0] . " to '" . $_[1] . "'\n" ) ; - $newenv{$_[0]} = $_[1] ; - } -} - -# get a variable from the environment and split it out to unified format -# (ie: space separated) -# input is in: -# $_[0] = variable to get -# -# output is in: -# string returned with value -sub CTSpoolEnv { - local( $ret ) = $ENV{$_[0]} ; - if ( $envsep{$_[0]} ne "" ) { - local( @splitlist ) = split( $envsep{$_[0]}, $ret ); - $ret = join( " ", @splitlist ) ; - } - $ret ; -} - -# modify a possibly existing variable to have a value in the 'new' environment -# input is in: -# $_[0] = variable -# $_[1] = value -# $_[2] = root -# $_[3] = project -# -# output is in: -# %newenv = variable adjusted to have the new value -sub CTAttachMod { - &CTUDebug( "in CTAttachMod\n" ) ; - if ( $_[0] eq "CTPROJS" ) { - # as part of the system, this one is special - &CTUDebug( "doing a mod on $CTPROJS\n" ) ; - if ( $newenv{$_[0]} eq "" ) { - $newenv{$_[0]} = $ENV{$_[0]} ; - } - local( $proj ) = $_[3] ; - $proj =~ tr/A-Z/a-z/ ; - local( $curflav ) = &CTQueryProj( $proj ) ; - if ( $curflav ne "" ) { - local( $tmp ) = $_[3] . ":" . $curflav ; - if ( $newenv{$_[0]} =~ /$tmp/ ) { - local( $hold ) = $newenv{$_[0]} ; - $hold =~ s/$tmp/$_[1]/ ; - &CTUDebug( "already attached to " . $_[3] . " changing '" . - $tmp . "' to '" . $_[1] . "' yielding '" . $hold . - "'\n" ) ; - $newenv{$_[0]} = $hold ; - } else { - &CTUDebug( "prepending '" . $_[1] . "' to CTPROJS\n" ) ; - $newenv{$_[0]} = $_[1] . " " . $newenv{$_[0]} ; - } - } else { - &CTUDebug( "writing '" . $_[1] . "' to CTPROJS\n" ) ; - if ( $newenv{$_[0]} eq "" ) { - $newenv{$_[0]} = $_[1] ; - } else { - $newenv{$_[0]} = $_[1] . " " . $newenv{$_[0]} ; - } - } - } elsif ( ( $_[0] ne "" ) && ( $_[1] ne "" ) ) { - local( $dosimple ) = 0 ; - if ( $newenv{$_[0]} eq "" ) { - # not in our 'new' environment yet, add it. - # may still be empty - $newenv{$_[0]} = &CTSpoolEnv( $_[0] ) ; - } - if ( ! ( $newenv{$_[0]} =~ /$_[1]/ )) { - &CTUDebug( "'" . $_[1] . "' exists in " . $_[0] . - " testing for simple modification\n" ) ; - # if it's in there already, we're done before we started. - if ( $_[1] =~ /^$_[2]/ ) { - &CTUDebug( "new value contains root '" . $_[2] . - "', may not be able to do simple edit\n" ) ; - # damn, might need to do an in-place edit - local( $curroot ) = $ENV{$_[3]} ; - &CTUDebug( "current root for '" . $_[3] . "' is '" . - $curroot . "'\n" ) ; - if ( $curroot eq "" ) { - &CTUDebug( "can do simple edit\n" ) ; - $dosimple = 1 ; - } else { - local( $test ) = $_[1] ; - $test =~ s/^$_[2]// ; - $test = $curroot . $test ; - if ( $newenv{$_[0]} =~ /$test/ ) { - # there it is. in-place edit - local( $foo ) = $newenv{$_[0]} ; - $foo =~ s/$test/$_[1]/ ; - &CTUDebug( "doing in-place edit on " . $_[0] . - " changing '" . $test . "' to '" . - $_[1] . "' yielding '" . $foo . "'\n" ) ; - $newenv{$_[0]} = $foo ; - } else { - &CTUDebug( "'" . $test . "' did not appear in $_[0]." . - " Simple edit\n" ) ; - $dosimple = 1 ; - } - } - } else { - &CTUDebug( "new value does not contain root '" . $_[2] . - "', can do simple edit\n" ) ; - # don't have to sweat in-place edits - $dosimple = 1 ; - } - } - if ( $dosimple ) { - if ( $newenv{$_[0]} eq "" ) { - &CTUDebug( "no pre-existing value in " . $_[0] . - " setting it to '" . $_[1] . "'\n" ) ; - $newenv{$_[0]} = $_[1] ; - } elsif ( $envpostpend{$_[0]} ) { - &CTUDebug( "post-pending '" . $_[1] . "' to " . $_[0] . - "\n" ) ; - $newenv{$_[0]} = $newenv{$_[0]} . " " . $_[1] ; - } elsif ( $envpostpendexceptions{$_[0]}{$_[1]} ) { - &CTUDebug( "post-pending (by exception) '" . $_[1] . "' to '" . $_[0] . - "'\n" ) ; - $newenv{$_[0]} = $newenv{$_[0]} . " " . $_[1] ; - } else { - &CTUDebug( "pre-pending '" . $_[1] . "' to " . $_[0] . - "\n" ) ; - $newenv{$_[0]} = $_[1] . " " . $newenv{$_[0]} ; - } - } - } -} - -require "$tool/built/include/ctcm.pl" ; - -# given the project and flavor, build the lists of variables to set/modify -# input is in: -# $_[0] = project -# $_[1] = flavor -# $_[2] = is some kind of default? -# -# output is in: -# return value is config line -# %newenv = an image of those parts of the environment we want to change -# %envsep = seperator -# %envcmd = set or setenv -# %envdo = direct commands to add to attach script -# %envpostpend = flag that variable should be postpended -sub CTAttachCompute { - &CTUDebug( "in CTAttachCompute\n" ) ; - local( $done ) = 0 ; - local( $flav ) = $_[1] ; - local( $prevflav ) = &CTQueryProj( $_[0] ) ; - local( $spec ) ; - local( $root ) ; - if ( $_[2] && ( $prevflav ne "" )) { - # want some form of default attachment, and are already attached. Short - # circuit. - $done = 1 ; - } - - # - # choose real flavor and find/validate root - # - while ( ! $done ) { - $spec = &CTResolveSpec( $_[0], $flav ) ; - #print STDERR "spec line '" . $spec . "' flav: '" . $flav ."'\n"; - &CTUDebug( "spec line = '$spec'\n" ) ; - if ( $spec ne "" ) { - $root = &CTComputeRoot( $_[0], $flav, $spec ) ; - &CTCMSetup( $_[0], $spec, $flav ) ; - if ( -e $root ) { - $done = 1 ; - } - } else { - print STDERR "could not resolve '" . $flav . "'\n" ; - $done = 1 ; - } - if (( ! $done ) && $_[2] ) { - if ( $flav eq "install" ) { - # oh my! are we ever in trouble - # want some sort of default, but couldn't get to what we wanted - print STDERR "ctattach to install failed\n" ; - $spec = "" ; - $done = 1 ; - } elsif ( $flav eq "release" ) { - $flav = "install" ; - } elsif ( $flav eq "ship" ) { - $flav = "release" ; - } else { - $flav = "install" ; - } - } elsif ( ! $done ) { - $spec = "" ; - print STDERR "resolved '" . $flav . "' but '" . $root . - "' does not exist\n" ; - $done = 1 ; - } - } - - # - # start real work - # - if ( $spec ne "" ) { - local( $proj ) = $_[0] ; - $proj =~ tr/a-z/A-Z/ ; - local( $item ) ; - - # we scan the .init file first because if there are needed sub-attaches - # they must happen before the rest of our work - local( $init ) = "$root/built/etc/$_[0].init" ; - local( %localmod ); - local( %localset ); - local( %localsep ); - local( %localcmd ); - local( %localdo ); - local( $localdocnt ) = 0 ; - local( %localpost ); - local( %localpostexceptions ) = () ; - if ( -e $init ) { - &CTUDebug( "scanning " . $_[0] . ".init\n" ) ; - local( @linesplit ) ; - local( $linetmp ) ; - local( $loop ) ; - local( $looptmp ) ; - local( *INITFILE ) ; - open( INITFILE, "< $init" ) ; - while ( ) { - s/\n$// ; - @linesplit = split( /\#/ ) ; - $_ = $linesplit[0] ; - if ( $_ =~ /^MODABS/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - $linesplitjoin = join( " ", @linesplit ) ; - if ( $linesplit[0] eq "-" ) { - shift( @linesplit ) ; - $linesplitjoin = join( " ", @linesplit ) ; - $localpostexceptions{$linetmp}{$linesplitjoin} = 1 ; - &CTUDebug( "Creating post-pend exception for '" . - $linetmp . "':'" . $linesplitjoin . "'\n" ) ; - } - if ( $localmod{$linetmp} eq "" ) { - $localmod{$linetmp} = $linesplitjoin ; - } else { - $localmod{$linetmp} = $localmod{$linetmp} . " " . - $linesplitjoin ; - } - } elsif ( $_ =~ /^MODREL/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - $postexception = 0 ; - foreach $loop ( @linesplit ) { - if ( $loop eq "-" ) { - $postexception = 1 ; - next ; - } - $looptmp = $root . "/" . &CTUShellEval($loop) ; - if ( $postexception ) { - $localpostexceptions{$linetmp}{$looptmp} = 1 ; - &CTUDebug( "Creating post-pend exception for '" . - $linetmp . "':'" . $looptmp . "'\n" ) ; - } - if ( -e $looptmp ) { - if ( $localmod{$linetmp} eq "" ) { - $localmod{$linetmp} = $looptmp ; - } else { - $localmod{$linetmp} = $localmod{$linetmp} . " " . - $looptmp ; - } - } - } - } elsif ( $_ =~ /^SETABS/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - if ( $localset{$linetmp} eq "" ) { - $localset{$linetmp} = join( " ", @linesplit ) ; - } else { - $localset{$linetmp} = $localset{$linetmp} . " " . - join( " ", @linesplit ) ; - } - } elsif ( $_ =~ /^SETREL/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - $looptmp = $root . "/" . &CTUShellEval($loop) ; - if ( -e $looptmp ) { - if ( $localset{$linetmp} eq "" ) { - $localset{$linetmp} = $looptmp ; - } else { - $localset{$linetmp} = $localset{$linetmp} . " " . - $looptmp ; - } - } - } - } elsif ( $_ =~ /^SEP/ ) { - @linesplit = split ; - $localsep{$linesplit[1]} = $linesplit[2] ; - } elsif ( $_ =~ /^CMD/ ) { - @linesplit = split ; - $localcmd{$linesplit[1]} = $linesplit[2] ; - } elsif ( $_ =~ /^DOCSH/ ) { - if ( $shell_type ne "sh" ) { - @linesplit = split ; - shift( @linesplit ) ; - $localdo{$localdocnt} = join( " ", @linesplit ) ; - $localdocnt++ ; - } - } elsif ( $_ =~ /^DOSH/ ) { - if ( $shell_type eq "sh" ) { - @linesplit = split ; - shift( @linesplit ) ; - $localdo{$localdocnt} = join( " ", @linesplit ) ; - $localdocnt++ ; - } - } elsif ( $_ =~ /^DO/ ) { - @linesplit = split ; - shift( @linesplit ) ; - $localdo{$localdocnt} = join( " ", @linesplit ) ; - $localdocnt++ ; - } elsif ( $_ =~ /^POSTPEND/ ) { - @linesplit = split ; - $localpost{$linesplit[1]} = 1 ; - } elsif ( $_ =~ /^ATTACH/ ) { - @linesplit = split ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - push( @attachqueue, $loop ) ; - } - } elsif ( $_ ne "" ) { - print STDERR "Unknown .init directive '$_'\n" ; - } - } - close( INITFILE ) ; - } - - # now handle sub-attaches - &CTUDebug( "performing sub-attaches\n" ) ; - while ( @attachqueue != () ) { - $item = shift( @attachqueue ) ; - &CTUDebug( "attaching to " . $item . "\n" ) ; - &CTAttachCompute( $item, $defflav, 1 ) ; - } - - # now we will do our extentions, then apply the mods from the .init - # file, if any - &CTUDebug( "extending paths\n" ) ; - local( $type ) = &CTSpecType( $spec ) ; - if ( $type eq "vroot" ) { - &CTAttachMod( "PATH", "/usr/atria/bin", $root, $proj ) ; - } - - # For now, we will not check whether the various /bin, /lib, - # /inc directories exist before adding them to the paths. This - # helps when attaching to unitialized trees that do not have - # these directories yet (but will shortly). - - # However, we *will* filter out any trees whose name ends in - # "MODELS". These don't have subdirectories that we care about - # in the normal sense. - if ( ! ( $proj =~ /MODELS$/ ) ) { - - $item = $root . "/built/bin" ; - #if ( -e $item ) { - &CTAttachMod( "PATH", $item, $root, $proj ) ; - #} - - $item = $root . "/built/lib" ; - #if ( -e $item ) { - &CTAttachMod( "PATH", $item, $root, $proj ) ; - &CTAttachMod( "LD_LIBRARY_PATH", $item, $root, $proj ) ; - &CTAttachMod( "DYLD_LIBRARY_PATH", $item, $root, $proj ) ; - #} - - $item = $root . "/built/include" ; - #if ( -e $item ) { - &CTAttachMod( "CT_INCLUDE_PATH", $item, $root, $proj ) ; - #} - - $item = $root . "/built/etc" ; - #if ( -e $item ) { - &CTAttachMod( "ETC_PATH", $item, $root, $proj ) ; - #} - } - - &CTAttachMod( "CTPROJS", $proj . ":" . $flav, $root, $proj ) ; - &CTAttachSet( $proj, $root ) ; - - # run thru the stuff saved up from the .init file - foreach $item ( keys %localsep ) { - $envsep{$item} = $localsep{$item} ; - } - foreach $item ( keys %localpost ) { - $envpostpend{$item} = $localpost{$item} ; - } - %envpostpendexceptions = %localpostexceptions; - foreach $item ( keys %localmod ) { - local( @splitthis ) = split( / +/, $localmod{$item} ) ; - local( $thing ) ; - foreach $thing ( @splitthis ) { - &CTAttachMod( $item, $thing, $root, $proj ) ; - } - } - foreach $item ( keys %localset ) { - &CTAttachSet( $item, $localset{$item} ) ; - } - foreach $item ( keys %localcmd ) { - $envcmd{$item} = $localcmd{$item} ; - } - for ($item = 0; $item < $localdocnt; $item++) { - $envdo{$docnt} = $localdo{$item} ; - $docnt++ ; - } - %envpostpendexceptions = () ; - } - - &CTUDebug( "out of CTAttachCompute\n" ) ; - $spec ; -} - -# write a script to NOT change the environment -# Input is: -# $_[0] = filename -sub CTAttachWriteNullScript { - &CTUDebug( "in CTAttachWriteNullScript\n" ) ; - local( *OUTFILE ) ; - open( OUTFILE, ">$_[0]" ) ; - print OUTFILE "#!/bin/" . $shell_type . " -f\n" ; - print OUTFILE "echo No attachment actions performed\n" ; - print OUTFILE "rm -f $_[0]\n" ; - close( OUTFILE ) ; - &CTUDebug( "out of CTAtachWriteNullScript\n" ) ; -} - -# write a script to setup the environment -# Input is: -# $_[0] = filename -sub CTAttachWriteScript { - &CTUDebug( "in CTAttachWriteScript\n" ) ; - local( *OUTFILE ) ; - open( OUTFILE, ">$_[0]" ) ; - print OUTFILE "#!/bin/" . $shell_type . " -f\n" ; - local( $item ) ; - - foreach $item ( keys %newenv ) { - local( $sep ) = " " ; - if ( $envsep{$item} ne "" ) { - $sep = $envsep{$item} ; - } - local( @splitlist ) = split( / +/, $newenv{$item} ) ; - local( $outval ) = join( $sep, @splitlist ) ; - - if ( $shell_type eq "sh" ) { - print OUTFILE "$item=\"" . $outval . "\"\n" ; - if ( $envcmd{$item} ne "set" ) { - print OUTFILE "export $item\n" ; - } - } else { - if ( $envcmd{$item} ne "" ) { - print OUTFILE $envcmd{$item} . " $item " ; - if ( $envcmd{$item} eq "set" ) { - print OUTFILE "= ( " ; - } - print OUTFILE $outval ; - if ( $envcmd{$item} eq "set" ) { - print OUTFILE ")" ; - } - print OUTFILE "\n" ; - } else { - print OUTFILE "setenv $item \"$outval\"\n" ; - if ( $ctdebug ) { - print OUTFILE "echo setting " . $item . " to '" . $outval . "'\n" ; - } - } - } - } - - #if ( $newenv{"CDPATH"} ne "" ) { - # if ( $shell_type ne "sh" ) { - # print OUTFILE "set cdpath = ( \$" . "CDPATH )\n" ; - # if ( $ctdebug ) { - # print OUTFILE "echo assigning cdpath\n" ; - # } - # } - #} - for ($item = 0; $item < $docnt; $item++) { - print OUTFILE $envdo{$item} . "\n" ; - if ( $ctdebug ) { - print OUTFILE "echo doing '" . $envdo{$item} . "'\n" ; - } - } - if (! $ctdebug) { - print OUTFILE "rm -f $_[0]\n" ; - } else { - print OUTFILE "echo end of script $_[0]\n" ; - print STDERR "no self-destruct script '" . $_[0] . "'\n" ; - } - close( OUTFILE ) ; - &CTUDebug( "out of CTAttachWriteScript\n" ) ; -} - -1; diff --git a/dtool/src/attach/ctattch.pl.rnd b/dtool/src/attach/ctattch.pl.rnd deleted file mode 100644 index 02edd6103b..0000000000 --- a/dtool/src/attach/ctattch.pl.rnd +++ /dev/null @@ -1,954 +0,0 @@ -require "$tool/built/include/ctutils.pl" ; - -# get list of all projects -sub CTAttachListProj { - if ($ctdebug ne "") { - print STDERR "in CTAttachListProj\n" ; - } - local( $ret ) = "" ; - local( $done ) = 0 ; - local( *DIRFILES ) ; - open( DIRFILES, "(cd /var/etc ; /bin/ls -1 *.vspec ; echo blahblah) |" ) ; - while ( ! $done ) { - $_ = ; - s/\n$// ; - if ( $_ eq "blahblah" ) { - $done = 1 ; - } else { - s/.vspec$// ; - $ret = $ret . " " . $_ ; - } - } - close( DIRFILES ) ; - if ($ctdebug ne "") { - print STDERR "out of CTAttachListProj\n" ; - } - $ret ; -} - -# get list of flavors for a project -# $_[0] = project -sub CTAttachListFlav { - if ($ctdebug) { - print STDERR "in CTAttachListFlav\n" ; - } - local( $ret ) = "" ; - $vobname = $_[0] ; - if ( -e "/var/etc/$_[0].vspec" ) { - local( *SPECFILE ) ; - open( SPECFILE, " ) { - if ( $_ =~ /^VOBNAME/ ) { - @partlist = split( /=/ ) ; - $vobname = $partlist[1] ; - $vobname =~ s/\n$// ; - } else { - @partlist = split( /:/ ) ; - $ret = $ret . " " . $partlist[0] ; - } - } - close( SPECFILE ) ; - } else { - print STDERR "CTAttachListFlav: cannot locate '/var/etc/$_[0]'\n" ; - } - if ($ctdebug) { - print STDERR "out of CTAttachListFlav\n" ; - } - $ret ; -} - -# get the flavor line for the given project -# $_[0] = project -# $_[1] = flavor -sub CTAttachFindFlav { - if ($ctdebug) { - print STDERR "in CTAttachFindFlav\n" ; - } - local( $ret ) = "" ; - $vobname = $_[0] ; - if ( -e "/var/etc/$_[0].vspec" ) { - local( *SPECFILE ) ; - open( SPECFILE, " ) && ! $done ) { - s/\n$// ; - if ( $_ =~ /^VOBNAME/ ) { - @partlist = split( /=/ ) ; - $vobname = $partlist[1] ; - } else { - @partlist = split( /:/ ) ; - if ( $partlist[0] eq $_[1] ) { - $done = 1 ; - $ret = join( " ", @partlist ); - } - } - } - close( SPECFILE ) ; - } else { - print STDERR "CTAttachFindFlav: cannot locate '/var/etc/$_[0]'\n" ; - } - if ($ctdebug) { - if ($ret ne "") { - print STDERR "found flavor " . $_[1] . " of project " . $_[0] . "\n" ; - } else { - print STDERR "did not find flavor " . $_[1] . " of project " . $_[0] . "\n" ; - } - print STDERR "out of CTAttachFindFlav\n" ; - } - $ret ; -} - -# given the project and flavor, resolve the final config line -# $_[0] = project -# $_[1] = flavor -sub CTAttachResolve { - if ($ctdebug) { - print STDERR "in CTAttachResolve\n" ; - } - local( $spec ) = &CTAttachFindFlav( $_[0], $_[1] ) ; - local( $ret ) = "" ; - if ( $spec ne "" ) { - local( @speclist ) ; - @speclist = split( / +/, $spec ) ; - if ( $speclist[1] eq "root" ) { - $ret = join( " " , @speclist ) ; - if ($ctdebug) { - print STDERR "resolved to a 'root'\n" ; - } - } elsif ( $speclist[1] eq "vroot" ) { - if ( $ENV{"HAVE_ATRIA"} ne "" ) { - $ret = join( " " , @speclist ) ; - if ($ctdebug) { - print STDERR "resolved to a 'vroot'\n" ; - } - } - } elsif ( $speclist[1] eq "ref" ) { - local( $tmp ) = &CTUShellEval( $speclist[2] ) ; - if ($ctdebug) { - print STDERR "resolved to a 'ref', recursing\n" ; - } - $ret = &CTAttachResolve( $_[0], $tmp ) ; - } else { - print STDERR "CTAttachResolve: unknown flavor type '$speclist[1]'\n" ; - } - } - if ($ctdebug) { - print STDERR "out of CTAttachResolve\n" ; - } - $ret ; -} - -# given the config line, determine the view name -# $_[0] = config line -sub CTAttachComputeView { - if ($ctdebug) { - print STDERR "in CTAttachComputeView\n" ; - } - local( $ret ) = "" ; - if ( $_[0] ne "" ) { - local( @speclist ) ; - @speclist = split( / +/, $_[0] ) ; - if ( $speclist[1] eq "vroot" ) { - local( $vname ) = $speclist[0] ; - shift( @speclist ) ; - shift( @speclist ) ; - local( $item ) ; - local( @itemlist ) ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "VN" ) { - $vname = $itemlist[1] ; - } - } - $ret = &CTUShellEval( $vname ) ; - } - } - if ($ctdebug) { - print STDERR "config line '" . $_[0] . "' yields view name '" . $ret . "'\n" ; - print STDERR "out of CTAttachComputeView\n" ; - } - $ret ; -} - -# given the config line, determine the branch name -# $_[0] = config line -sub CTAttachComputeBranch { - if ($ctdebug) { - print STDERR "in CTAttachComputeBranch\n" ; - } - local( $ret ) = "" ; - if ( $_[0] ne "" ) { - local( @speclist ) ; - @speclist = split( / +/, $_[0] ) ; - if ( $speclist[1] eq "vroot" ) { - local( $bname ) = &CTAttachComputeView( $_[0] ) ; - shift( @speclist ) ; - shift( @speclist ) ; - local( $item ) ; - local( @itemlist ) ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "BN" ) { - $bname = $itemlist[1] ; - } - } - $ret = &CTUShellEval( $bname ) ; - } - } - if ($ctdebug) { - print STDERR "config line '" . $_[0] . "' yields branch name '" . $ret . "'\n" ; - print STDERR "out of CTAttachComputeBranch\n" ; - } - $ret ; -} - -# given the config line, determine the label name -# $_[0] = config line -sub CTAttachComputeLabel { - if ($ctdebug) { - print STDERR "in CTAttachComputeLabel\n" ; - } - local( $ret ) = "" ; - if ( $_[0] ne "" ) { - local( @speclist ) ; - @speclist = split( / +/, $_[0] ) ; - if ( $speclist[1] eq "vroot" ) { - local( $lname ) = &CTAttachComputeView( $_[0] ) ; - shift( @speclist ) ; - shift( @speclist ) ; - local( $item ) ; - local( @itemlist ) ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "LB" ) { - $lname = $itemlist[1] ; - } - } - $ret = &CTUShellEval( $lname ) ; - $ret =~ tr/a-z/A-Z/ ; - } - } - if ($ctdebug) { - print STDERR "config line '" . $_[0] . "' yields label name '" . $ret . "'\n" ; - print STDERR "out of CTAttachComputeLabel\n" ; - } - $ret ; -} - -# given the project name and config line, determine the root of the project -# $_[0] = project -# $_[1] = config line -sub CTAttachComputeRoot { - if ($ctdebug) { - print STDERR "in CTAttachComputeRoot\n" ; - } - local( $ret ) = "" ; - if ( $_[1] ne "" ) { - local( @speclist ) ; - @speclist = split( / +/, $_[1] ) ; - if ( $speclist[1] eq "root" ) { - $ret = $speclist[2] ; - } elsif ( $speclist[1] eq "vroot" ) { - $ret = &CTAttachComputeView( $_[1] ) ; - $ret = "/view/$ret/vobs/$vobname" ; - } else { - print STDERR "CTAttachComputeRoot: unknown flavor type '$speclist[1]'\n" ; - } - } - if ($ctdebug) { - print STDERR "out of CTAttachComputeRoot\n" ; - } - $ret ; -} - -# given the project name and config line, determine the root of the project as -# needed by the config spec. -# $_[0] = project -# $_[1] = config line -sub CTAttachComputeElemRoot { - if ($ctdebug) { - print STDERR "in CTAttachComputeElemRoot\n" ; - } - local( $ret ) = "" ; - if ( $_[1] ne "" ) { - local( @speclist ) ; - @speclist = split( / +/, $_[1] ) ; - if ( $speclist[1] eq "root" ) { - $ret = $speclist[2] ; - } elsif ( $speclist[1] eq "vroot" ) { - $ret = &CTAttachComputeView( $_[1] ) ; - $ret = "/vobs/$vobname" ; - } else { - print STDERR "CTAttachComputeElemRoot: unknown flavor type '$speclist[1]'\n" ; - } - } - if ($ctdebug) { - print STDERR "out of CTAttachComputeElemRoot\n" ; - } - $ret ; -} - -# do whatever setup is needed for ClearCase -# input is in: -# $_[0] = project -# $_[1] = $spec -sub CTAttachCCSetup { - if ($ctdebug) { - print STDERR "in CTAttachCCSetup\n" ; - } - local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1] ) ; - local( $view ) = &CTAttachComputeView( $_[1] ) ; - local( $branch ) = &CTAttachComputeBranch( $_[1] ) ; - local( $label ) = &CTAttachComputeLabel( $_[1] ) ; - local( *CTINTERFACE ) ; - local( *TMPFILE ) ; - local( $tmpname ) = "/tmp/config.$$" ; - local( $emitted ) = 0 ; - - if ($ctdebug) { - print STDERR "checking for existance of view '" . $view . "'\n" ; - } - open( CTINTERFACE, "/usr/atria/bin/cleartool lsview $view |" ) ; - $_ = ; - close( CTINTERFACE ) ; - if ( $_ eq "" ) { # need to make the view - if ($ctdebug) { - print STDERR "creating view '" . $view . "'\n" ; - } - system "umask 2 ; /usr/atria/bin/cleartool mkview -tag $view /var/views/$view.vws 2> /dev/null > /dev/null ; /usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null\n" ; - } elsif ( ! ( $_ =~ /\*/ )) { # need to start the view - if ($ctdebug) { - print STDERR "starting view '" . $view . "'\n" ; - } - system "/usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null &\n" ; - } - - if ($ctdebug) { - print STDERR "making branch and label types for view " . $view . "\n" ; - } - system "/usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null &\n" ; - system "/usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null &\n" ; - - if ($ctdebug) { - print STDERR "creating/updating the config-spec for view " . $view . "\n" ; - } - open( CTINTERFACE, "/usr/atria/bin/cleartool catcs -tag $view |" ) ; - open( TMPFILE, "> $tmpname" ) ; - while ( ) { - if ( $_ =~ "CHECKEDOUT" ) { - print TMPFILE "$_" ; - } elsif (( $_ =~ /^element \*/ ) && ( $_ =~ "/main/LATEST" ) && - !( $_ =~ /$_[0]/ )) { - if ( ! $emitted ) { - $emitted = 1 ; - print TMPFILE "element $root/... .../$branch/LATEST\n" ; - print TMPFILE "element $root/... $label -mkbranch $branch\n" ; - print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; - } - print TMPFILE "$_" ; - } elsif ( $_ =~ /$_[0]/ ) { - if ( ! $emitted ) { - $emitted = 1 ; - print TMPFILE "element $root/... .../$branch/LATEST\n" ; - print TMPFILE "element $root/... $label -mkbranch $branch\n" ; - print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; - } - } else { - print TMPFILE "$_" ; - } - } - close( CTINTERFACE ) ; - close( TMPFILE ) ; - system "/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm $tmpname &\n" ; - if ($ctdebug) { - print STDERR "out of CTAttachCCSetup\n" ; - } -} - -# do whatever setup is needed for ClearCase, but do it in the background -# input is in: -# $_[0] = project -# $_[1] = $spec -sub CTAttachCCSetupBG { - if ($ctdebug) { - print STDERR "in CTAttachCCSetupBG\n" ; - } - local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1] ) ; - local( $view ) = &CTAttachComputeView( $_[1] ) ; - local( $branch ) = &CTAttachComputeBranch( $_[1] ) ; - local( $label ) = &CTAttachComputeLabel( $_[1] ) ; - - system "$tool/bin/ctattachcc $root $view $branch $label $vobname $_[0]\n" ; - - if ($ctdebug) { - print STDERR "out of CTAttachCCSetupBG\n" ; - } -} - -# prepend an entry onto the envmod of the given key. -# input is in: -# $_[0] = key -# $_[1] = data -# -# output is in: -# %envmod = has 'data' prepended at 'key' -sub CTAttachPrependMod { - if ( $envmod{$_[0]} eq "" ) { - $envmod{$_[0]} = $_[1] ; - } else { - $envmod{$_[0]} = $_[1] . " " . $envmod{$_[0]} ; - } -} - -# postpend an entry onto the envmod of the given key. -# input is in: -# $_[0] = key -# $_[1] = data -# -# output is in: -# %envmod = has 'data' postpended at 'key' -sub CTAttachPostpendMod { - if ( $envmod{$_[0]} eq "" ) { - $envmod{$_[0]} = $_[1] ; - } else { - $envmod{$_[0]} = $envmod{$_[0]} . " " . $_[1] ; - } -} - -# pre/post-pend an entry onto the envmod of the given key, as set/controlled -# by envpospend, et al. -# input is in: -# $_[0] = key -# $_[1] = data -# -# output is in: -# %envmod = data pre/post pended at the given key -sub CTAttachAddToMod { - if ($envpostpend{$_[0]} ne "") { - &CTAttachPostpendMod( $_[0], $_[1] ) ; - } else { - &CTAttachPrependMod( $_[0], $_[1] ) ; - } -} - -# prepend the given entry to the envset of the given key -# input is in: -# $_[0] = key -# $_[1] = data -# -# output is in: -# %envset = prepended at key with data -sub CTAttachPrependSet { - local( $sep ) = " " ; - if ( $envsep{$_[0]} ne "" ) { - $sep = $envsep{$_[0]} ; - } - if ($envset{$_[0]} ne "") { - $envset{$_[0]} = $_[1] . $sep . $envset{$_[0]} ; - } else { - $envset{$_[0]} = $_[1] ; - } -} - -# postpend the given entry to the envset of the given key -# input is in: -# $_[0] = key -# $_[1] = data -# -# output is in: -# %envset = postpended at key with data -sub CTAttachPostpendSet { - local( $sep ) = " " ; - if ( $envsep{$_[0]} ne "" ) { - $sep = $envsep{$_[0]} ; - } - if ($envset{$_[0]} ne "") { - $envset{$_[0]} = $envset{$_[0]} . $sep . $_[1] ; - } else { - $envset{$_[0]} = $_[1] ; - } -} - -# pre/post-pend an entry onto the envset of the given key, as set/controlled -# by envpospend, et al. -# input is in: -# $_[0] = key -# $_[1] = data -# -# output is in: -# %envset = data pre/post pended at the given key -sub CTAttachAddToSet { - if ($envpostpend{$_[0]} ne "") { - &CTAttachPostpendSet( $_[0], $_[1] ) ; - } else { - &CTAttachPrependSet( $_[0], $_[1] ) ; - } -} - -$docnt = 0 ; -@attachqueue = () ; - -require "$tool/built/include/ctquery.pl" ; - -# given the project and flavor, build the lists of variables to set/modify -# input is in: -# $_[0] = project -# $_[1] = flavor -# $_[2] = is some kind of default? -# -# output is in: -# return value is config line -# %envmod = environment variables to modify -# %envset = environment variables to outright set -# %envsep = seperator -# %envcmd = set or setenv -# %envdo = direct commands to add to attach script -# %envpostpend = flag that variable should be postpended -sub CTAttachCompute { - if ($ctdebug) { - print STDERR "in CTAttachCompute\n" ; - } - local( $done ) = 0 ; - local( $flav ) = $_[1] ; - local( $prevflav ) = &CTQueryProj( $_[0] ) ; - local( $spec ) ; - local( $root ) ; - if ( $_[2] && ( $prevflav ne "" )) { - # short circuit attaching, we're already there. - $done = 1 ; - } - while ( ! $done ) { - $spec = &CTAttachResolve( $_[0], $flav ) ; - if ( $ctdebug ne "" ) { - print STDERR "spec line = '$spec'\n" ; - } - if ( $spec ne "" ) { - $root = &CTAttachComputeRoot( $_[0], $spec ) ; - if ( -e $root ) { - $done = 1 ; - if ( $spec =~ /vroot/ ) { - &CTAttachCCSetupBG( $_[0], $spec ) ; - } - } elsif ( $spec =~ /vroot/ ) { - &CTAttachCCSetup( $_[0], $spec ) ; - if ( -e $root ) { - $done = 1 ; - } - } - } - if (( ! $done ) && $_[2] ) { - if ( $flav eq "install" ) { - # oh my! are we ever in trouble - print STDERR "you are in a strange alien universe\n" ; - $spec = "" ; - $done = 1 ; - } elsif ( $flav eq "release" ) { - $flav = "install" ; - } elsif ( $flav eq "ship" ) { - $flav = "release" ; - } else { - $flav = "ship" ; - } - } - } - - if ( $spec ne "" ) { - local( $proj ) = $_[0] ; - $proj =~ tr/a-z/A-Z/ ; - local( $view ) = &CTAttachComputeView( $spec ) ; - - if ($ctdebug) { - print STDERR "extending paths\n" ; - } - - &CTAttachAddToMod( "PATH", $root . "/bin" ) ; - &CTAttachAddToMod( "LD_LIBRARY_PATH", $root . "/lib" ) ; - &CTAttachAddToMod( "DYLD_LIBRARY_PATH", $root . "/lib" ) ; - #&CTAttachAddToMod( "CDPATH", $root . "/src/all" ) ; - &CTAttachAddToMod( "CT_INCLUDE_PATH", $root . "/include" ) ; - &CTAttachAddToMod( "DC_PATH", $root . "/etc" ) ; - &CTAttachAddToMod( "PFPATH", $root . "/etc/models" ) ; - &CTAttachAddToMod( "SSPATH", $root . "/lib/ss" ) ; - &CTAttachAddToMod( "STKPATH", $root . "/lib/stk" ) ; - &CTAttachAddToMod( "CTPROJS", $proj . ":" . $flav ) ; - $envset{$proj} = $root; - -# if ( $view ne "" ) { -# &CTAttachCCSetup( $_[0], $spec ) ; -# } - - if ( -e "$root/etc/$_[0].init" ) { - if ($ctdebug) { - print STDERR "scanning .init file\n" ; - } - local( @linesplit ) ; - local( $linetmp ) ; - local( $loop ); - local( *INITFILE ) ; - if ( -x "$root/etc/$_[0].init" ) { - open( INITFILE, "$root/etc/$_[0].init $_[0] $_[1] $root |" ) ; - } else { - open( INITFILE, "< $root/etc/$_[0].init" ) ; - } - while ( ) { - s/\n$// ; - if ( $_ =~ /^MODABS/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - &CTAttachPostpendMod( $linetmp, &CTUShellEval(join(" ", @linesplit))) ; - } elsif ( $_ =~ /^MODREL/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - &CTAttachPostpendMod( $linetmp, $root . "/" . &CTUShellEval($loop)) ; - } - } elsif ( $_ =~ /^SETABS/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - &CTAttachPrependSet( $linetmp, &CTUShellEval(join(" ", @linesplit))) ; - } elsif ( $_ =~ /^SETREL/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - &CTAttachPrependSet( $linetmp, $root . "/" . &CTUShellEval($loop)) ; - } - } elsif ( $_ =~ /^SEP/ ) { - @linesplit = split ; - $envsep{$linesplit[1]} = $linesplit[2] ; - } elsif ( $_ =~ /^CMD/ ) { - @linesplit = split ; - $envcmd{$linesplit[1]} = $linesplit[2] ; - } elsif ( $_ =~ /^DO/ ) { - @linesplit = split ; - shift( @linesplit ) ; - $envdo{$docnt} = join( " ", @linesplit ) ; - $docnt++ ; - } elsif ( $_ =~ /^POSTPEND/ ) { - @linesplit = split ; - $envpospend{$linesplit[1]} = 1 ; - } elsif ( $_ =~ /^ATTACH/ ) { - @linesplit = split ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - push( @attachqueue, $loop ) ; - } - } else { - print STDERR "Unknown .init directive '$_'\n" ; - } - } - close( INITFILE ) ; - } - - # save mods away until after sub-attach - local( %locmod ) ; - local( $item ) ; - - foreach $item ( keys %envmod ) { - $locmod{$item} = $envmod{$item} ; - delete $envmod{$item} ; - } - - # do sub-attaches - while ( @attachqueue != () ) { - $item = shift( @attachqueue ) ; - &CTAttachCompute( $item, $defflav, 1 ) ; - } - - # restore saved mods and merge them in with existing - foreach $item ( keys %locmod ) { - $envmod{$item} = $locmod{$item} ; - delete $locmod{$item} ; - } - - &CTAttachCheckVars( $_[0], $spec ) ; - } - if ($ctdebug) { - print STDERR "out of CTAttachCompute\n" ; - } - $spec ; -} - -# take a mod list and merge it into set. Uniqueifying as we go. -# input is in: -# $_[0] = mod list -# $_[1] = key -# -# output is: -# %envset = now has the mod line merged in with it. -sub CTAttachMergeToSet { - if ( $ctdebug ) { - print STDERR "trying to add '$_[0]' to '$envset{$_[1]}'\n" ; - } - local( @splitlist ) ; - local( $loop ) ; - local( $sep ) = " " ; - if ( $envsep{$_[1]} ne "" ) { - $sep = $envsep{$_[1]} ; - } - @splitlist = split( / /, $_[0] ) ; - foreach $loop ( @splitlist ) { - if ( ! (( $envset{$_[1]} eq $loop ) || - ( $envset{$_[1]} =~ /^$loop$sep/ ) || - ( $envset{$_[1]} =~ /$sep$loop$/ ) || - ( $envset{$_[1]} =~ /$sep$loop$sep/ ))) { - &CTAttachPostpendSet( $_[1], $loop ) ; - } - } - if ( $ctdebug ) { - print STDERR "yielding '$envset{$_[1]}'\n" ; - } -} - -# Perform cleanup operations on the variable that are going to be set/modified -# eg: -# * check to see if we're already attached to the project, and alter sets -# based on that -# * move mods of pre-existing variables to sets w/ the changes included -# * move mods of non-existing variables to sets -# -# input: -# $_[0] = project -# $_[1] = config line -sub CTAttachCheckVars { - if ($ctdebug) { - print STDERR "in CTAttachCheckVars\n" ; - } - local( $prevflav ) = &CTQueryProj( $_[0] ) ; - local( $proj ) = $_[0] ; - $proj =~ tr/a-z/A-Z/ ; - local( $atria ) = "/usr/atria/bin" ; - if ( $ENV{"HAVE_ATRIA"} ne "" ) { - if ( !( $ENV{"PATH"} =~ /$atria/ )) { - $envmod{"PATH"} = "$atria " . $envmod{"PATH"} ; - } - } - if ( $prevflav ne "" ) { # are already attached to the project - if ( $ctdebug ne "" ) { - print STDERR "am already attached\n" ; - } - local( $prevspec ) = &CTAttachResolve( $_[0], $prevflav ) ; - local( $prevroot ) = &CTAttachComputeRoot( $_[0], $prevspec ) ; - local( $root ) = &CTAttachComputeRoot( $_[0], $_[1] ) ; - local( $loop ) ; - local( $item ) ; - local( @splitlist ) ; - local( $modsave ) ; - foreach $item ( keys %envmod ) { - if ( $ENV{$item} ne "" ) { - if ( $ctdebug ne "" ) { - print STDERR "'$item' is already in the environment\n" ; - } - if ( $item eq "CTPROJS" ) { - local( $prevmark ) = $proj . ":" . $prevflav ; - local( $curmark ) = $envmod{$item} ; - if ( $ctdebug ne "" ) { - print STDERR "changing '$prevmark' to '$curmark' yielding " ; - } - if ( ! $gotenv{$item} ) { - $envset{$item} = $ENV{$item} ; - } - $envset{$item} =~ s/$prevmark/$curmark/ ; - if ( $ctdebug ne "" ) { - print STDERR "'$envset{$item}'\n" ; - } - delete $envmod{$item} ; - } else { - local( $src ) ; - if ( $gotenv{$item} ) { - $src = $envset{$item} ; - } else { - $src = $ENV{$item} ; - } - if ( $envsep{$item} ne "" ) { - @splitlist = split( $envsep{$item}, $src ) ; - } else { - @splitlist = split( / +/, $src ) ; - } - $modsave = $envmod{$item} ; - delete $envmod{$item} ; - foreach $loop ( @splitlist ) { - $loop =~ s/$prevroot/$root/ ; - &CTAttachPostpendMod( $item, $loop ) ; - } - if ( $ctdebug ne "" ) { - print STDERR "env '$src' -> '$envmod{$item}'\n" ; - } - @splitlist = split( / +/, $modsave ) ; - foreach $loop ( @splitlist ) { - if ( ! (( $envmod{$item} eq $loop ) || - ( $envmod{$item} =~ /^$loop / ) || - ( $envmod{$item} =~ / $loop$/ ) || - ( $envmod{$item} =~ / $loop / ))) { - &CTAttachAddToMod( $item, $loop ) ; - } - } - if ( $ctdebug ne "" ) { - print STDERR "env final = '$envmod{$item}'\n" ; - } - } - } - if ( $envmod{$item} ne "" ) { - $envset{$item} = $envmod{$item} ; - if ( $envsep{$item} ne "" ) { - $envset{$item} =~ s/ /$envsep{$item}/g ; - } - # &CTAttachMergeToSet( $envmod{$item}, $item ) ; - delete $envmod{$item} ; - $gotenv{$item} = 1 ; - } - } - } else { # not already attached. mods -> sets - if ( $ctdebug ne "" ) { - print STDERR "am not already attached\n" ; - } - local( $item ) ; - local( $loop ) ; - local( $modsave ) ; - local( @splitlist ) ; - foreach $item ( keys %envmod ) { - if ( $ENV{$item} ne "" ) { - local( $src ) ; - if ( $gotenv{$item} ) { - $src = $envset{$item} ; - } else { - $src = $ENV{$item} ; - } - if ( $envsep{$item} ne "" ) { - @splitlist = split( $envsep{$item}, $src ) ; - } else { - @splitlist = split( / +/, $src ) ; - } - $modsave = $envmod{$item} ; - delete $envmod{$item} ; - foreach $loop ( @splitlist ) { - &CTAttachPostpendMod( $item, $loop ) ; - } - if ( $ctdebug ne "" ) { - print STDERR "env '$src' -> '$envmod{$item}'\n" ; - } - @splitlist = split( / +/, $modsave ) ; - foreach $loop ( @splitlist ) { - if ( ! (( $envmod{$item} eq $loop ) || - ( $envmod{$item} =~ /^$loop / ) || - ( $envmod{$item} =~ / $loop$/ ) || - ( $envmod{$item} =~ / $loop / ))) { - &CTAttachAddToMod( $item, $loop ) ; - } - } - if ( $ctdebug ne "" ) { - print STDERR "env final = '$envmod{$item}'\n" ; - } - } - $envset{$item} = $envmod{$item} ; - if ( $envsep{$item} ne "" ) { - $envset{$item} =~ s/ /$envsep{$item}/g ; - } - # &CTAttachMergeToSet( $envmod{$item}, $item ) ; - delete $envmod{$item} ; - $gotenv{$item} = 1 ; - } - } - if ($ctdebug) { - print STDERR "out of CTAttachCheckVars\n" ; - } -} - -# write a script to NOT change the environment -# Input is: -# $_[0] = filename -sub CTAttachWriteNullScript { - if ($ctdebug) { - print STDERR "in CTAttachWriteNullScript\n" ; - } - local( *OUTFILE ) ; - open( OUTFILE, ">$_[0]" ) ; - print OUTFILE "#!/bin/csh -f\n" ; - print OUTFILE "echo No attachment actions performed\n" ; - print OUTFILE "/sbin/rm $_[0]\n" ; - close( OUTFILE ) ; - if ($ctdebug) { - print STDERR "out of CTAtachWriteNullScript\n" ; - } -} - -# write a script to setup the environment -# Input is: -# $_[0] = filename -sub CTAttachWriteScript { - if ($ctdebug) { - print STDERR "in CTAttachWriteScript\n" ; - } - local( *OUTFILE ) ; - open( OUTFILE, ">$_[0]" ) ; - print OUTFILE "#!/bin/csh -f\n" ; - local( $item ) ; - foreach $item ( keys %envset ) { - if ( $envcmd{$item} ne "" ) { - print OUTFILE $envcmd{$item} . " $item " ; - if ( $envcmd{$item} eq "set" ) { - print OUTFILE "= " ; - } - print OUTFILE $envset{$item} . "\n" ; - } else { - print OUTFILE "setenv $item \"$envset{$item}\"\n" ; - } - } - foreach $item ( keys %envmod ) { - print STDERR "SHOULD NOT BE HERE\n" ; - if ( $envcmd{$item} ne "" ) { - print OUTFILE $envcmd{$item} . " $item " ; - if ( $envcmd{$item} eq "set" ) { - print OUTFILE "= ( " ; - } else { - print OUTFILE "\"" ; - } - } else { - print OUTFILE "setenv $item \"" ; - } - if ( $envsep{$item} ne "" ) { - @itemlist = split( / +/, $envmod{$item} ) ; - foreach $tmp ( @itemlist ) { - print OUTFILE $tmp . $envsep{$item} ; - } - } else { - print OUTFILE $envmod{$item} ; - } - if ( $envcmd{$item} ne "" ) { - if ( $envcmd{$item} eq "set" ) { - print OUTFILE ")" ; - } else { - print OUTFILE "\"" ; - } - print OUTFILE "\n" ; - } else { - print OUTFILE $ENV{$item} . "\"\n" ; - } - } - #if (( $envset{"CDPATH"} ne "" ) || ( $envmod{"CDPATH"} ne "" )) { - # print OUTFILE "set cdpath = ( \$" . "CDPATH )\n" ; - #} - foreach $item ( keys %envdo ) { - print OUTFILE $envdo{$item} . "\n" ; - } - if (! $ctdebug) { - print OUTFILE "/sbin/rm $_[0]\n" ; - } else { - print STDERR "no self-destruct script '" . $_[0] . "'\n" ; - } - close( OUTFILE ) ; - if ($ctdebug) { - print STDERR "out of CTAttachWriteScript\n" ; - } -} - -1; diff --git a/dtool/src/attach/ctccase.pl b/dtool/src/attach/ctccase.pl deleted file mode 100644 index b02c2c59b1..0000000000 --- a/dtool/src/attach/ctccase.pl +++ /dev/null @@ -1,430 +0,0 @@ -# given the config line, determine the view name -# $_[0] = config line -# $_[1] = flavor -# $_[2] = project -sub CTAttachComputeView { - &CTUDebug( "in CTAttachComputeView\n" ) ; - local( $ret ) = &CTResolveSpecName( $_[2], $_[1] ) ; - local( $options ) = &CTSpecOptions( $_[0] ) ; - if ( $options ne "" ) { - local( $name ) = &CTSpecFindOption( $options, "name" ) ; - if ( $name ne "" ) { - &CTUDebug( "found a name '" . $name . "'\n" ) ; - $ret = $name ; - } else { - &CTUDebug( "no name option found, going with default\n" ) ; - } - } - &CTUDebug( "config line '" . $_[0] . "' yields view name '" . $ret . - "'\n" . "out of CTAttachComputeView\n" ) ; - $ret ; -} - -# given the config line, determine the branch name -# $_[0] = config line -# $_[1] = flavor -# $_[2] = project -sub CTAttachComputeBranch { - &CTUDebug( "in CTAttachComputeBranch\n" ) ; - local( $ret ) = &CTAttachComputeView( $_[0], $_[1], $_[2] ) ; - &CTUDebug( "config line '" . $_[0] . "' yields branch name '" . $ret . - "'\n" . "out of CTAttachComputeBranch\n" ) ; - $ret ; -} - -# given the config line, determine the label name -# $_[0] = config line -# $_[1] = flavor -# $_[2] = project -sub CTAttachComputeLabel { - &CTUDebug( "in CTAttachComputeLabel\n" ) ; - local( $ret ) = &CTAttachComputeView( $_[0], $_[1], $_[2] ) ; - $ret =~ tr/a-z/A-Z/ ; - &CTUDebug( "config line '" . $_[0] . "' yields label name '" . $ret . - "'\n" . "out of CTAttachComputeLabel\n" ) ; - $ret ; -} - -# given the project name and config line, determine the root of the project as -# needed by the config spec. -# $_[0] = project -# $_[1] = config line -# $_[2] = flavor -sub CTAttachComputeElemRoot { - &CTUDebug( "in CTAttachComputeElemRoot\n" ) ; - local( $ret ) = "/vobs/$_[0]" ; - &CTUDebug( "out of CTAttachComputeElemRoot\n" ) ; - $ret ; -} - -# do whatever setup is needed for ClearCase -# input is in: -# $_[0] = project -# $_[1] = $spec -# $_[2] = flavor -sub CTAttachCCSetup { - &CTUDebug( "in CTAttachCCSetup\n" ) ; - local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1], $_[2] ) ; - local( $view ) = &CTAttachComputeView( $_[1], $_[2], $_[0] ) ; - local( $branch ) = &CTAttachComputeBranch( $_[1], $_[2], $_[0] ) ; - local( $label ) = &CTAttachComputeLabel( $_[1], $_[2], $_[0] ) ; - local( *CTINTERFACE ) ; - local( *TMPFILE ) ; - local( $tmpname ) = "/tmp/config.$$" ; - local( $emitted ) = 0 ; - - &CTUDebug( "checking for existance of view '" . $view . "'\n" ) ; - open( CTINTERFACE, "/usr/atria/bin/cleartool lsview $view |" ) ; - $_ = ; - close( CTINTERFACE ) ; - if ( $_ eq "" ) { # need to make the view - &CTUDebug( "creating view '" . $view . "'\n" ) ; - system "umask 2 ; /usr/atria/bin/cleartool mkview -tag $view /var/views/$view.vws 2> /dev/null > /dev/null ; /usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null\n" ; - } elsif ( ! ( $_ =~ /\*/ )) { # need to start the view - &CTUDebug( "starting view '" . $view . "'\n" ) ; - system "/usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null &\n" ; - } - - &CTUDebug( "making branch and label types for view " . $view . "\n" ) ; - system "/usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null &\n" ; - system "/usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null &\n" ; - - &CTUDebug( "creating/updating the config-spec for view " . $view . "\n" ) ; - open( CTINTERFACE, "/usr/atria/bin/cleartool catcs -tag $view |" ) ; - open( TMPFILE, "> $tmpname" ) ; - while ( ) { - if ( $_ =~ "CHECKEDOUT" ) { - print TMPFILE "$_" ; - } elsif (( $_ =~ /^element \*/ ) && ( $_ =~ "/main/LATEST" ) && - !( $_ =~ /$_[0]/ )) { - if ( ! $emitted ) { - $emitted = 1 ; - print TMPFILE "element $root/... .../$branch/LATEST\n" ; - print TMPFILE "element $root/... $label -mkbranch $branch\n" ; - print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; - } - print TMPFILE "$_" ; - } elsif ( $_ =~ /$_[0]/ ) { - if ( ! $emitted ) { - $emitted = 1 ; - print TMPFILE "element $root/... .../$branch/LATEST\n" ; - print TMPFILE "element $root/... $label -mkbranch $branch\n" ; - print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; - } - } else { - print TMPFILE "$_" ; - } - } - close( CTINTERFACE ) ; - close( TMPFILE ) ; - system "/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm -f $tmpname &\n" ; - &CTUDebug( "out of CTAttachCCSetup\n" ) ; -} - -# do whatever setup is needed for ClearCase, but do it in the background -# input is in: -# $_[0] = project -# $_[1] = $spec -# $_[2] = flavor -sub CTAttachCCSetupBG { - &CTUDebug( "in CTAttachCCSetupBG\n" ) ; - local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1], $_[2] ) ; - local( $view ) = &CTAttachComputeView( $_[1], $_[2], $_[0] ) ; - local( $branch ) = &CTAttachComputeBranch( $_[1], $_[2], $_[0] ) ; - local( $label ) = &CTAttachComputeLabel( $_[1], $_[2], $_[0] ) ; - - system "$tool/bin/ctattachcc $root $view $branch $label $vobname $_[0]\n" ; - - &CTUDebug( "out of CTAttachCCSetupBG\n" ) ; -} - -# given a possibly empty string, format it into a comment or -nc -# input is in: -# $_[0] = possible comment string -# -# output is: -# string for use by ClearCase functions -sub CTCcaseFormatComment { - local( $ret ) = "" ; - if ( $_[0] eq "" ) { - $ret = "-nc" ; - } else { - $ret = "-c \"" . $_[0] . "\"" ; - } - $ret ; -} - -# make a versioned directory -# input is in: -# $_[0] = directory to create -# $_[1] = curr dir -# $_[2] = possible comment -# -# output: -# return success or failure -sub CTCcaseMkdir { - &CTUDebug( "in CTCcaseMkdir\n" ) ; - local( $ret ) = 0 ; - local( $dir ) = $_[0] ; - if ( ! ( $dir =~ /^\// )) { - $dir = $_[1] . "/" . $dir ; - } - local( $comment) = &CTCcaseFormatComment( $_[2] ) ; - # first we have to check out the parent directory - local( @alist ) = split( /\//, $dir ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $dir . "' is '" . $parent . "'\n" ) ; - $ret = system( "cleartool co -nc $parent\n" ) ; - if ( $ret == 0 ) { - # now make the dir - $ret = &CTURetCode( system( "cleartool mkdir " . $comment . - " $dir\n" )) ; - } else { - $ret = 0 ; - } - &CTUDebug( "out of CTCcaseMkdir\n" ) ; - $ret ; -} - -# make a versioned element -# input is in: -# $_[0] = element to version -# $_[1] = curr dir -# $_[2] = possible comment -# $_[3] = possible eltype -# -# output: -# return success or failure -sub CTCcaseMkelem { - &CTUDebug( "in CTCcaseMkelem\n" ) ; - local( $ret ) = 0 ; - local( $elem ) = $_[0] ; - if ( ! ( $elem =~ /^\// )) { - $elem = $_[1] . "/" . $elem ; - } - local( $comment) = &CTCcaseFormatComment( $_[2] ) ; - local( $eltype ) = $_[3] ; - if ( $eltype ne "" ) { - $eltype = "-eltype " . $eltype ; - } - # first we have to check out the parent directory - local( @alist ) = split( /\//, $elem ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; - $ret = system( "cleartool co -nc $parent\n" ) ; - if ( $ret != 0 ) { - &CTUDebug( "checking out the dirctory gave return code: " . $ret . - "\n" ) ; - $ret = 0 ; - } - # now make the elem - $ret = &CTURetCode( system( "cleartool mkelem " . $comment . " " . - $eltype . " $elem\n" )) ; - &CTUDebug( "out of CTCcaseMkelem\n" ) ; - $ret ; -} - -# done here so there will be coherence if multiple deltas are done -require "ctime.pl" ; -$timestamp = &ctime(time) ; -$timestamp =~ s/\n$// ; -@timelist = split( /\s+/, $timestamp ) ; -$timestamp = $timelist[2] . $timelist[1] . $timelist[5] . "_" . $timelist[3] ; -$timestamp =~ s/:/_/g ; - -# delta an element -# input is in: -# $_[0] = element to delta -# -# output: -# return success or failure -sub CTCcaseDelta { - require "$tool/built/include/ctdelta.pl" ; - - &CTUDebug( "in CTCcaseDelta\n" ) ; - local( $ret ) = 0 ; - # this is ripped from the old ctdelta script - &CTDeltaCheckin( $_[0] ) ; - local( $ver ) = &CTDeltaGetVersion( $_[0] ) ; - &CTUDebug( "got version '" . $ver . "'\n" ) ; - if ( &CTDeltaOk( $ver )) { - local( @verlist ) = split( /\//, $ver ) ; - pop( @verlist ) ; - pop( @verlist ) ; - local( $ver2 ) = join( "/", @verlist ) ; - &CTUDebug( "ver2 = '" . $ver2 . "'\n" ) ; - &CTDeltaSafeMerge( $_[0], $ver, $ver2 ) ; - system "cleartool checkin -nc $_[0] 2> /dev/null > /dev/null" ; - &CTUDebug( "merge complete, doing branch check\n" ) ; - &CTDeltaBranchCheck( $_[0], $ver, $timestamp ) ; - &CTUDebug( "logging potentially felonious activity for future" . - " incrimination\n" ) ; - &CTDeltaLog( $_[0], $ver, $ver2 ) ; - # better detection needs to be done - $ret = 1 ; - } else { - &CTUDebug( "cannot merge '" . $_[0] . "', no branches.\n" ) ; - } - &CTUDebug( "out of CTCcaseDelta\n" ) ; - $ret ; -} - -# checkout an element -# input is in: -# $_[0] = element to checkout -# $_[1] = possible comment -# -# output: -# return success or failure -sub CTCcaseCheckout { - &CTUDebug( "in CTCcaseCheckout\n" ) ; - local( $comment) = &CTCcaseFormatComment( $_[1] ) ; - local( $ret ) = &CTURetCode( system( "cleartool co " . $comment . - " $_[0]\n" )) ; - &CTUDebug( "out of CTCcaseCheckout\n" ) ; - $ret ; -} - -# checkin an element -# input is in: -# $_[0] = element to checkin - -# -# output: -# return success or failure -sub CTCcaseCheckin { - &CTUDebug( "in CTCcaseCheckin\n" ) ; - local( $comment) = &CTCcaseFormatComment( $_[1] ) ; - local( $ret ) = &CTURetCode( system( "cleartool ci " . $comment . - " $_[0]\n" )) ; - &CTUDebug( "out of CTCcaseCheckin\n" ) ; - $ret ; -} - -# uncheckout an element -# input is in: -# $_[0] = element to uncheckout -# -# output: -# return success or failure -sub CTCcaseUncheckout { - require "$tool/built/include/unco.pl" ; - &CTUDebug( "in CTCcaseUncheckout\n" ) ; - local( $ret ) = 1 ; - # need better error checking on this - system( "cleartool unco -rm $_[0]\n" ) ; - &CTUncoDoIt( $_[0] ) ; - &CTUDebug( "out of CTCcaseUncheckout\n" ) ; - $ret ; -} - -# figure out what all I have checked out or on my branch -# input is in: -# $_[0] = project -# $_[1] = flavor -# $_[2] = spec line -# -# output: -# return a \n serperated list of elements checked out -sub CTCcaseIHave { - &CTUDebug( "in CTCcaseIHave\n" ) ; - local( $ret ) = "" ; - local( $branch ) = &CTAttachComputeBranch( $_[2], $_[1], $_[0] ) ; - local( $root ) = &CTProjRoot( $_[0] ) ; - local( *OUTPUT ) ; - open( OUTPUT, "cleartool find " . $root . " -element \"brtype(" . - $branch . ")\" -nxn -print |" ) ; - while ( ) { - $ret = $ret . $_ ; - } - close( OUTPUT ) ; - &CTUDebug( "out of CTCcaseIHave\n" ) ; - $ret ; -} - -# remove a versioned element -# input is in: -# $_[0] = element to remove -# $_[1] = curr dir -# -# output: -# return success or failure -sub CTCcaseRmElem { - &CTUDebug( "in CTCcaseRmElem\n" ) ; - local( $ret ) = 0 ; - local( $elem ) = $_[0] ; - if ( ! ( $elem =~ /^\// )) { - $elem = $_[1] . "/" . $elem ; - } - # first we have to check out the parent directory - local( @alist ) = split( /\//, $elem ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; - $ret = system( "cleartool co -nc $parent\n" ) ; - if ( $ret == 0 ) { - # now nuke the element - $ret = &CTURetCode( system( "cleartool rmname $elem\n" )) ; - } else { - $ret = 0 ; - } - &CTUDebug( "out of CTCcaseRmElem\n" ) ; - $ret ; -} - -# mv a versioned element from one name to another -# input is in: -# $_[0] = from element -# $_[1] = to element -# $_[2] = current directory -# -# output: -# return success or failure -sub CTCcaseMv { - &CTUDebug( "in CTCcaseMv\n" ) ; - local( $ret ) = 0 ; - local( $elem ) = $_[0] ; - if ( ! ( $elem =~ /^\// )) { - $elem = $_[2] . "/" . $elem ; - } - # first we have to check out the parent directory - local( @alist ) = split( /\//, $elem ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; - local( $elem2 ) = $_[1] ; - if ( ! ( $elem2 =~ /^\// )) { - $elem2 = $_[2] . "/" . $elem2 ; - } - local( @alist ) = split( /\//, $elem2 ) ; - pop( @alist ) ; - local( $parent2 ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem2 . "' is '" . $parent2 . - "'\n" ) ; - system( "cleartool co -nc $parent\n" ) ; - system( "cleartool co -nc $parent2\n" ) ; - $ret = &CTURetCode( system( "cleartool mv $elem $elem2\n" )) ; - &CTUDebug( "out of CTCcaseMv\n" ) ; - $ret ; -} - -# build a list of targets -# input is in: -# $_[0] = targets -# -# output: -# return success or failure -sub CTCcaseMake { - &CTUDebug( "in CTCcaseMake\n" ) ; - local( $ret ) = 0 ; - local( $line ) = "clearmake -C gnu " . $_[0] . - " |& grep -v \"^clearmake: Warning: Config\"\n" ; - &CTUDebug( "line = '" . $line . "'\n" ) ; - $ret = &CTURetCode( system( $line )) ; - &CTUDebug( "out of CTCcaseMake\n" ) ; - $ret ; -} - -1; diff --git a/dtool/src/attach/ctci b/dtool/src/attach/ctci deleted file mode 100644 index abf0a5fe03..0000000000 --- a/dtool/src/attach/ctci +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/perl - -sub CTCiUsage { - print STDERR "Usage: ctci [-c \"comment\"] [-nc] element-name [...]\n" ; - print STDERR "Options:\n" ; - print STDERR " -c \"comment\" : provide a comment about this action\n" ; - print STDERR " -nc : expect no comment on this action\n" ; - exit; -} - -if ( $#ARGV < 0 ) { - &CTCiUsage ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured for CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$comment = "" ; - -$skip = 0 ; - -@files = () ; - -foreach $item ( @ARGV ) { - if ( $skip == 0 ) { - if ( $item eq "-nc" ) { - &CTUDebug( "-nc processed\n" ) ; - } elsif ( $item eq "-c" ) { - $skip = 1 ; - } else { - push( @files, $item ) ; - &CTUDebug( "added '" . $item . "' to files to be processed\n" ) ; - } - } elsif ( $skip == 1 ) { - $comment = $item ; - &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; - $skip = 0 ; - } else { - &CTUDebug( "got to unknown skip value! (" . $skip . ")\n" ) ; - $skip = 0 ; - } -} - - -if ($#files < 0 ) { - &CTCiUsage ; -} - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @files ) { - if ( -e $item ) { - if ( ! &CTCMCheckin( $item, $projname, $spec, $comment ) ) { - print STDERR "Could not checkin '$item'\n" ; - } - } else { - print STDERR "No such file '$item'.\n" ; - } -} diff --git a/dtool/src/attach/ctcm.pl b/dtool/src/attach/ctcm.pl deleted file mode 100644 index cfda27e72e..0000000000 --- a/dtool/src/attach/ctcm.pl +++ /dev/null @@ -1,579 +0,0 @@ -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; - -# given a spec line, do the 'correct' setup for it -# input is in: -# $_[0] = project -# $_[1] = spec line -# $_[2] = flavor -sub CTCMSetup { - local( $type ) = &CTSpecType( $_[1] ) ; - if ( $type eq "vroot" ) { - &CTUDebug( "running setup for an atria tree\n" ) ; - if ( $ENV{"HAVE_ATRIA"} eq "yes" ) { - require "$tool/built/include/ctccase.pl" ; - &CTAttachCCSetup( $_[0], $_[1], $_[2] ) ; - } else { - &CTUDebug( "don't HAVE_ATRIA!\n" ) ; - } - # if we don't have atria, and it's a vroot, well.. - } elsif ( $type eq "croot" ) { - &CTUDebug( "running setup for CVS\n" ) ; - require "$tool/built/include/ctcvs.pl" ; - local( $serve ) = &CTCvsServerLine( $_[0], $_[1] ) ; - local( $thing ) = &CTCvsLogin( $serve ) ; - if ( ! $thing ) { - print STDERR "CVS login failed given server line '" . $serve . - "'\n" ; - } - } - # no other types have any work that needs to be done at this time -} - -# given a directory, make sure it's versioned -# input is in: -# $_[0] = directory -# $_[1] = project -# $_[2] = spec line -# $_[3] = comment (optional, "" if none) -# -# output: -# return success or failure -sub CTCMMkdir { - &CTUDebug( "in CTCMMkdir\n" ) ; - local( $ret ) = 0 ; - # first check that the directory is in the project, and is not the root - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if (( $_[0] =~ /^$root/ ) && ( $_[0] ne $root )) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - if ( $isok ) { - # ok, it is. Does one already exist? - if ( -e $_[0] ) { - # already one there, nothing to do - &CTUDebug( "directory '" . $_[0] . "' already exists\n" ) ; - $ret = 1 ; - } else { - # now switch off on how to actually do it - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseMkdir( $_[0], $pwd, $_[3] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolMkdir( $_[0], $pwd, $_[3] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsMkdir( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - print STDERR "CTCMMkdir::error! got invalid spec type '" . - $type . "'\n" ; - } - } - } else { - print STDERR "directory '" . $_[0] . "' not in project '" . $_[1] . - "' or is the root.\n" ; - } - &CTUDebug( "out of CTCMMkdir\n" ) ; - $ret ; -} - -# given a file, make sure it's versioned -# input is in: -# $_[0] = file -# $_[1] = project -# $_[2] = spec line -# $_[3] = comment (optional, "" if none) -# $_[4] = eltype (optional, "" if none) -# -# output: -# return success or failure -sub CTCMMkelem { - &CTUDebug( "in CTCMMkelem\n" ) ; - local( $ret ) = 0; - # first check that the directory is in the project - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $isok ) = 0 ; - local( $pwd ) = &CTUCurrDir() ; - # synth an eltype if there is none - if ( ! -e $_[0] ) { - # need it to already exist - $isok = 0 ; - } else { - if ( -d $_[0] ) { - # wrong command for a directory - $isok = 0 ; - } else { - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseMkelem( $_[0], $pwd, $_[3], $_[4] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolMkelem( $_[0], $pwd, $_[3], $_[4] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsMkelem( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - print STDERR "CTCMMkelem::error! got invalid spec type '" . - $type . "'\n" ; - } - } - &CTUDebug( "out of CTCMMkelem\n" ) ; - $ret ; -} - -# given an element, delta it in -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCMDelta { - &CTUDebug( "in CTCMDelta\n" ) ; - local( $ret ) = 0 ; - # first check that the element is in the project - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( ! -e $_[0] ) { - # can't delta something that doesn't exist - $isok = 0 ; - } else { - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseDelta( $_[0] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolDelta( $_[0] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsDelta( $_[0], $_[1], $_[2] ) ; - } else { - print STDERR "CTCMDelta::error! got invalid spec type '" . $type . - "'\n" ; - } - } else { - &CTUDebug( "failed delta pre-checks\n" ) ; - } - &CTUDebug( "out of CTCMDelta\n" ) ; - $ret ; -} - -# given an element, check it out -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# $_[3] = comment (optional, "" if none) -# -# output: -# return success or failure -sub CTCMCheckout { - &CTUDebug( "in CTCMCheckout\n" ) ; - local( $ret ) = 0 ; - # first check that the element is in the project - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( ! -e $_[0] ) { - # can't checkout something that doesn't exist - $isok = 0 ; - } else { - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseCheckout( $_[0], $_[3] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolCheckout( $_[0], $_[3] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsCheckout( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - print STDERR "CTCMCheckout::error! got invalid spec type '" . - $type . "'\n" ; - } - } - &CTUDebug( "out of CTCMCheckout\n" ) ; - $ret ; -} - -# given an element, check it in -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# $_[3] = comment (optional, "" if none) -# -# output: -# return success or failure -sub CTCMCheckin { - &CTUDebug( "in CTCMCheckin\n" ) ; - local( $ret ) = 0 ; - # first check that the element is in the project - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( ! -e $_[0] ) { - # can't checkin something that doesn't exist - $isok = 0 ; - } else { - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseCheckin( $_[0], $_[3] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolCheckin( $_[0], $_[3] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsCheckin( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - print STDERR "CTCMCheckin::error! got invalid spec type '" . - $type . "'\n" ; - } - } - &CTUDebug( "out of CTCMCheckin\n" ) ; - $ret ; -} - -# given an element, uncheck it out -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCMUncheckout { - &CTUDebug( "in CTCMUncheckout\n" ) ; - local( $ret ) = 0 ; - # first check that the element is in the project - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( ! -e $_[0] ) { - # can't uncheckout something that doesn't exist - $isok = 0 ; - } else { - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseUncheckout( $_[0] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolUncheckout( $_[0] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsUncheckout( $_[0], $_[1], $_[2] ) ; - } else { - print STDERR "CTCMUncheckout::error! got invalid spec type '" . - $type . "'\n" ; - } - } - &CTUDebug( "out of CTCMUncheckout\n" ) ; - $ret ; -} - -# figure out what all I have checked out in a project -# input is in: -# $_[0] = project -# $_[1] = flavor -# $_[2] = spec line -# -# output: -# return a \n serperated list of elements checked out -sub CTCMIHave { - &CTUDebug( "in CTCMIHave\n" ) ; - local( $ret ) = "" ; - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseIHave( $_[0], $_[1], $_[2] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolIHave( $_[0], $_[1], $_[2] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsIHave( $_[0], $_[1], $_[2] ) ; - } else { - print STDERR "CTCMIHave::error! got invalid spec type '" . $type . - "'\n" ; - } - &CTUDebug( "out of CTCMIHave\n" ) ; - $ret ; -} - -# given an element, remove it from the repository -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCMRmElem { - &CTUDebug( "in CTCMRmElem\n" ) ; - local( $ret ) = 0 ; - # first check that the element is in the project - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( ! -e $_[0] ) { - # can't rmname something that doesn't exist - $isok = 0 ; - } else { - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseRnElem( $_[0], $pwd ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolRmElem( $_[0], $pwd ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsRmElem( $_[0], $_[1], $_[2] ) ; - } else { - print STDERR "CTCMRmElem::error! got invalid spec type '" . - $type . "'\n" ; - } - } - &CTUDebug( "out of CTCMRmElem\n" ) ; - $ret ; -} - -# move an element from one name to another -# input is in: -# $_[0] = from element -# $_[1] = to element -# $_[2] = project -# $_[3] = spec line -# -# output: -# return success or failure -sub CTCMMv { - &CTUDebug( "in CTCMMv\n" ) ; - local( $ret ) = 0 ; - # first check that the from and to are in the project - local( $flav ) = &CTQueryProj( $_[2] ) ; - local( $root ) = &CTComputeRoot( $_[2], $flav, $_[3] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - if ( $isok ) { - if ( $_[1] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[1] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[3] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseMv( $_[0], $_[1], $pwd ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolMv( $_[0], $_[1], $pwd ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsMv( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - print STDERR "CTCMMv::error! got invalid spec type '" . - $type . "'\n" ; - } - } - &CTUDebug( "out of CTCMMv\n" ) ; - $ret ; -} - -# give a list of targets, build them -# input is in: -# $_[0] = targets -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCMMake { - &CTUDebug( "in CTCMMake\n" ) ; - local( $ret ) = 0 ; - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseMake( $_[0] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolMake( $_[0] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsMake( $_[0] ) ; - } else { - print STDERR "CTCMMake::error! got invalid spec type '" . $type . - "'\n" ; - } - &CTUDebug( "out of CTCMMake\n" ) ; - $ret ; -} - -1; diff --git a/dtool/src/attach/ctco b/dtool/src/attach/ctco deleted file mode 100644 index a090e68dba..0000000000 --- a/dtool/src/attach/ctco +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/perl - -sub CTCoUsage { - print STDERR "Usage: ctco [-c \"comment\"] [-nc] element-name [...]\n" ; - print STDERR "Options:\n" ; - print STDERR " -c \"comment\" : provide a comment about this action\n" ; - print STDERR " -nc : expect no comment on this action\n" ; - exit; -} - -if ( $#ARGV < 0 ) { - &CTCoUsage ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured for CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$comment = "" ; - -$skip = 0 ; - -@files = () ; - -foreach $item ( @ARGV ) { - if ( $skip == 0 ) { - if ( $item eq "-nc" ) { - &CTUDebug( "-nc processed\n" ) ; - } elsif ( $item eq "-c" ) { - $skip = 1 ; - } else { - push( @files, $item ) ; - &CTUDebug( "added '" . $item . "' to files to be processed\n" ) ; - } - } elsif ( $skip == 1 ) { - $comment = $item ; - &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; - $skip = 0 ; - } else { - &CTUDebug( "got to unknown skip value! (" . $skip . ")\n" ) ; - $skip = 0 ; - } -} - -if ( $#files < 0 ) { - &CTCoUsage ; -} - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @files ) { - if ( -e $item ) { - if ( ! &CTCMCheckout( $item, $projname, $spec, $comment ) ) { - print STDERR "Could not checkout '$item'\n" ; - } - } else { - print STDERR "No such file '$item'.\n" ; - } -} diff --git a/dtool/src/attach/ctcvs.pl b/dtool/src/attach/ctcvs.pl deleted file mode 100644 index 8e50d75adb..0000000000 --- a/dtool/src/attach/ctcvs.pl +++ /dev/null @@ -1,451 +0,0 @@ -# given a possibly empty string, format it into a comment or -nc -# input is in: -# $_[0] = possible comment string -# -# output is: -# string for use by CVS functions -sub CTCvsFormatComment { - local( $ret ) = "" ; - if ( $_[0] ne "" ) { - $ret = "-m \"" . $_[0] . "\"" ; - } - $ret ; -} - -# given a project and spec line, compute the server line -# input is in: -# $_[0] = project -# $_[1] = spec line -# -# output: -# return a sever line, or "" if not a croot -sub CTCvsServerLine { - &CTUDebug( "in CTCvsServerLine\n" ) ; - local( $ret ) = "" ; - local( $type ) = &CTSpecType( $_[1] ) ; - if ( $type eq "croot" ) { - local( $options ) = &CTSpecOptions( $_[1] ) ; - local( $sline ) = &CTSpecFindOption( $options, "server" ) ; - if ( $sline ne "" ) { - $ret = join( ":", split( /,/, $sline )); - } - } - &CTUDebug( "out of CTCvsServerLine\n" ) ; - $ret ; -} - -# if needed log into a cvs server -# input is in: -# $_[0] = server line -# -# output: -# return success or failure -sub CTCvsLogin { - &CTUDebug( "in CTCvsLogin\n" ) ; - local( $ret ) = 0 ; - &CTUDebug( "server line is '" . $_[0] . "'\n" ) ; - if ( $_[0] ne "" ) { - # ok. we actually have something, lets look in .cvspass - local( $path ) ; - local( *PASSFILE ) ; - if ( $ENV{"PENV"} eq "WIN32" ) { - $path = $ENV{"HOME"} . "/.cvspass" ; - } else { - # $path = "~/.cvspass" ; - $path = $ENV{"HOME"} . "/.cvspass" ; - } - &CTUDebug( "looking for '" . $path . "'\n" ) ; - if ( -e $path ) { - local( $passdone ) = 0 ; - local( $ok ) = 0 ; - open( PASSFILE, "< $path" ) ; - while ( ) { - s/\n$// ; - local( @line ) = split ; - # ok, the server line is in [0] and the password in [1]. - &CTUDebug( "server line from .cvspass is '" . $line[0] . - "'\n" ) ; - if ( $line[0] eq $_[0] ) { - # we're fine, we're already logged in to that - $ret = 1 ; - $passdone = 1; - } - } - if ( ! $passdone ) { - # ran out of lines in the file - local( $line ) = "cvs -d " . $_[0] . " login >/dev/null" ; - &CTUDebug( "about to run '" . $line . "'\n" ) ; - $ret = &CTURetCode( system( $line )) ; - } - } else { - &CTUDebug( $path . " file does not exist\n" ) ; - local( $line ) = "cvs -d " . $_[0] . " login >/dev/null" ; - &CTUDebug( "about to run '" . $line . "'\n" ) ; - $ret = &CTURetCode( system( $line )) ; - } - } - &CTUDebug( "out of CTCvsLogin\n" ) ; - $ret ; -} - -require "$tool/built/include/ctproj.pl" ; - -# add a versioned element to the repository -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# $_[3] = possible comment -# -# output: -# return success or failure -sub CTCvsAdd { - &CTUDebug( "in CTCvsAdd\n" ) ; - # first we need to 'login' to the repository - local( $comment ) = &CTCvsFormatComment( $_[3] ) ; - local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ; - local( $ret ) = &CTCvsLogin( $serve ) ; - if ( $ret ) { - # now issue the add command - local( $root ) = &CTProjRoot( $_[1] ) ; - local( $line ) = "" ; - local( $elem ) = $_[0] ; - if ( $elem =~ /^\// ) { - local( $proj ) = $_[1] ; - $proj =~ tr/a-z/A-Z/ ; - $line = "cd \$" . $proj . "; " ; - $elem =~ s/^$root\/// ; - } - $line = $line . "cvs -d " . $serve . " add " . $comment . " $elem" ; - &CTUDebug( "about to execute '" . $line . "'\n" ) ; - $ret = &CTURetCode( system( $line )) ; - } - &CTUDebug( "out of CTCvsAdd\n" ) ; - $ret ; -} - -# ci a versioned element to the repository -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# $_[3] = possible comment -# -# output: -# return success or failure -sub CTCvsCi { - &CTUDebug( "in CTCvsCi\n" ) ; - # first we need to 'login' to the repository - local( $comment ) = &CTCvsFormatComment( $_[3] ) ; - local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ; - local( $ret ) = &CTCvsLogin( $serve ) ; - if ( $ret ) { - # now issue the add command - local( $root ) = &CTProjRoot( $_[1] ) ; - local( $line ) = "" ; - local( $elem ) = $_[0] ; - if ( $elem =~ /^\// ) { - local ( $proj ) = $_[1] ; - $proj =~ tr/a-z/A-Z/ ; - $line = "cd \$" . $proj . "; " ; - $elem =~ s/^$root\/// ; - } - $line = $line . "cvs -d " . $serve . " ci " . $comment . " $elem" ; - &CTUDebug( "about to execute '" . $line . "'\n" ) ; - $ret = &CTURetCode( system( $line )) ; - } - &CTUDebug( "out of CTCvsCi\n" ) ; - $ret ; -} - -# rm a versioned element from the repository -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCvsRm { - &CTUDebug( "in CTCvsRm\n" ) ; - # first we need to 'login' to the repository - local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ; - local( $ret ) = &CTCvsLogin( $serve ) ; - if ( $ret ) { - # now issue the add command - $ret = &CTURetCode( system( "cvs -d " . $serve . " rm $_[0]\n" )) ; - } - &CTUDebug( "out of CTCvsRm\n" ) ; - $ret ; -} - -# make a versioned directory -# input is in: -# $_[0] = directory to create -# $_[1] = project -# $_[2] = spec line -# $_[3] = possible comment -# -# output: -# return success or failure -sub CTCvsMkdir { - &CTUDebug( "in CTCvsMkdir\n" ) ; - local( $ret ) = 0 ; - # first make the dir - $ret = &CTURetCode( system( "mkdir $_[0]\n" )) ; - if ( $ret ) { - # now version it - $ret = &CTCvsAdd( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - &CTUDebug( "could not create directory '" . $_[0] . "'\n" ) ; - $ret = 0 ; - } - &CTUDebug( "out of CTCvsMkdir\n" ) ; - $ret ; -} - -# make a versioned element -# input is in: -# $_[0] = element to version -# $_[1] = project -# $_[2] = spec line -# $_[3] = possible comment -# -# output: -# return success or failure -sub CTCvsMkelem { - &CTUDebug( "in CTCvsMkelem\n" ) ; - # first cvs add the file - local( $ret ) = &CTCvsAdd( $_[0], $_[1], $_[2], $_[3] ) ; - if ( $ret ) { - # now commit it - $ret = &CTCvsCi( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - &CTUDebug( "could not CVS add '" . $_[0] . "'\n" ) ; - $ret = 0 ; - } - &CTUDebug( "out of CTCvsMkelem\n" ) ; - $ret ; -} - -# delta an element -# input is in: -# $_[0] = element to delta -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCvsDelta { - &CTUDebug( "in CTCvsDelta\n" ) ; - local( $ret ) = 0 ; - # for lack of better idea, this is going to be just checkin for now - if ( -d $_[0] ) { - # we don't version directories in CVS - $ret = 1 ; - } else { - $ret = &CTCvsCi( $_[0], $_[1], $_[2] ) ; - } - &CTUDebug( "out of CTCvsDelta\n" ) ; - $ret ; -} - -# checkout an element -# input is in: -# $_[0] = element to checkout -# $_[1] = project -# $_[2] = spec line -# $_[3] = possible comment -# -# output: -# return success or failure -sub CTCvsCheckout { - &CTUDebug( "in CTCvsCheckout\n" ) ; - local( $ret ) = 1 ; - # for my limited understanding of CVS, there doesn't seem to be any - # 'checkout' for it. - &CTUDebug( "out of CTCvsCheckout\n" ) ; - $ret ; -} - -# checkin an element -# input is in: -# $_[0] = element to checkin -# $_[1] = project -# $_[2] = spec line -# $_[3] = possible comment -# -# output: -# return success or failure -sub CTCvsCheckin { - &CTUDebug( "in CTCvsCheckin\n" ) ; - local( $ret ) = 0 ; - if ( -d $_[0] ) { - # we don't version directories in CVS - $ret = 1 ; - } else { - $ret = &CTCvsCi( $_[0], $_[1], $_[2], $_[3] ) ; - } - &CTUDebug( "out of CTCvsCheckin\n" ) ; - $ret ; -} - -# uncheckout an element -# input is in: -# $_[0] = element to uncheckout -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCvsUncheckout { - &CTUDebug( "in CTCvsUncheckout\n" ) ; - local( $ret ) = 0 ; - if ( -d $_[0] ) { - # we don't version directories in CVS - $ret = 1 ; - } else { - $ret = &CTURetCode( system( "rm $_[0]" ) ) ; - if ( $ret ) { - local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ; - $ret = &CTCvsLogin( $serve ) ; - if ( $ret ) { - $ret = &CTURetCode( system( "cvs -d " . $serve . " update " . - $_[0] )) ; - } - } - } - &CTUDebug( "out of CTCvsUncheckout\n" ) ; - $ret ; -} - -# figure out what all I have checked out -# input is in: -# $_[0] = project -# $_[1] = flavor -# $_[2] = spec line -# -# output: -# return a \n serperated list of elements checked out -sub CTCvsIHave { - &CTUDebug( "in CTCvsIHave\n" ) ; - local( $ret ) = "" ; - local( $proj ) = $_[0] ; - $proj =~ tr/a-z/A-Z/ ; - local( $line ) = "cd \$" . $proj . "; " ; - local( $serve ) = &CTCvsServerLine( $_[0], $_[2] ) ; - local( $ok ) = &CTCvsLogin( $serve ) ; - if ( $ok ) { - $line = $line . "cvs -n -d " . $serve . " update 2>/dev/null" ; - local( $hold ) = ""; - local( *OUTPUT ) ; - open( OUTPUT, $line . " |" ) ; - while ( ) { - $hold = $hold . $_ ; - } - close( OUTPUT ) ; - local( @lines ) = split( /\n/, $hold ) ; - local( $item ) ; - foreach $item ( @lines ) { - if ( $item =~ /^\?/ ) { - # things that start with a ? are ignored - } elsif ( $item =~ /^cvs/ ) { - # messages from the server are also ignored - } elsif ( $item =~ /^P/ ) { - # new files are ignored - } elsif ( $item =~ /^U/ ) { - # updates are ignored - } elsif ( $item =~ /^M/ ) { - # here's one we modified - local( @foo ) = split( / /, $item ) ; - $ret = $ret . $foo[1] . "\n" ; - } else { - # don't what this means, better complain - local( @foo ) = split( / /, $item ) ; - print STDERR "got unknown update code '" . $foo[0] . - "' for file '" . $foo[1] . "'\n" ; - } - } - } - &CTUDebug( "out of CTCvsIHave\n" ) ; - $ret ; -} - -# remove an element from the repository -# input is in: -# $_[0] = element to uncheckout -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCvsRmElem { - &CTUDebug( "in CTCvsRmElem\n" ) ; - local( $ret ) = 0 ; - if ( -d $_[0] ) { - # CVS doesn't really do this. If there are no files in the directory, - # the next time an update -P is run, it will be deleted. - $ret = 1 ; - } else { - $ret = &CTURetCode( system( "rm $_[0]" ) ) ; - if ( $ret ) { - $ret = &CTCvsRm( $_[0], $_[1], $_[2] ) ; - if ( $ret ) { - $ret = &CTCvsCi( $_[0], $_[1], $_[2] ) ; - } - } - } - &CTUDebug( "out of CTCvsRmElem\n" ) ; - $ret ; -} - -# move a versioned element from one name to another -# input is in: -# $_[0] = from element -# $_[1] = to element -# $_[2] = project -# $_[3] = spec line -# -# output: -# return success or failure -sub CTCvsMv { - &CTUDebug( "in CTCvsMv\n" ) ; - local( $ret ) = 0 ; - if ( -d $_[0] ) { - # don't have code to do directories yet. See pp 54 of the CVS book - $ret = 0 ; - } else { - $ret = &CTURetCode( system( "mv $_[0] $_[1]" ) ) ; - if ( $ret ) { - $ret = &CTCvsRm( $_[0], $_[2], $_[3] ) ; - if ( $ret ) { - $ret = &CTCvsAdd( $_[1], $_[2], $_[3] ); - if ( $ret ) { - $ret = &CTCvsCi( $_[0], $_[2], $_[3] ) ; - if ( $ret ) { - $ret = &CTCvsCi( $_[1], $_[2], $_[3] ) ; - } - } - } - } - } - &CTUDebug( "out of CTCvsMv\n" ) ; - $ret ; -} - -# build a list of targets -# input is in: -# $_[0] = targets -# -# output: -# return success or failure -sub CTCvsMake { - &CTUDebug( "in CTCvsMake\n" ) ; - local( $ret ) = 0 ; - local( $line ) = "make " . $_[0] . "\n" ; - $ret = &CTURetCode( system( $line )) ; - &CTUDebug( "out of CTCvsMake\n" ) ; - $ret ; -} - -1; diff --git a/dtool/src/attach/ctdelta b/dtool/src/attach/ctdelta deleted file mode 100755 index 395c2a37c9..0000000000 --- a/dtool/src/attach/ctdelta +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/perl - -if ($#ARGV < 0) { - exit print "Usage: ctdelta element-name [...]\n" ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "not configured for using ct-tools\n" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$comment = "" ; -$skip = 0 ; - -@files = () ; - -foreach $item ( @ARGV ) { - if ( $skip == 0 ) { - if ( $item eq "-nc" ) { - &CTUDebug( "-nc processed\n" ) ; - } elsif ( $item eq "-c" ) { - $skip = 1 ; - } else { - push( @files, $item ) ; - &CTUDebug( "added '" . $item . "' to files to be processed\n" ) ; - } - } elsif ( $skip == 1 ) { - $comment = $item ; - &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; - $skip = 0 ; - } else { - &CTUDebug( "got to unknown skip value! (" . $skip . ")\n" ) ; - $skip = 0 ; - } -} - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @files ) { - if ( -e $item ) { - &CTCMCheckin( $item, $projname, $spec ) ; - if ( ! &CTCMDelta( $item, $projname, $spec ) ) { - print STDERR "Could not delta '$item'\n" ; - } - } else { - print STDERR "No such file '$item'.\n" ; - } -} diff --git a/dtool/src/attach/ctdelta.pl b/dtool/src/attach/ctdelta.pl deleted file mode 100644 index 2b870fa495..0000000000 --- a/dtool/src/attach/ctdelta.pl +++ /dev/null @@ -1,232 +0,0 @@ -# Check in element if needed -# Input is: -# $_[0] = element name -sub CTDeltaCheckin { - local( $cmd ) = "cleartool ci -nc $_[0] 2> /dev/null > /dev/null" ; - system $cmd ; -} - -# get the version of an element -# Input is: -# $_[0] = element name -sub CTDeltaGetVersion { - local( *CMDFILE ) ; - open( CMDFILE, "cleartool describe -short $_[0] |" ) ; - $_ = ; - close( CMDFILE ) ; - s/\n$// ; - s/^.*@@// ; - s/\"$// ; - $_ ; -} - -# Is it ok to try a merge on this version? -# Input is: -# $_[0] = version -sub CTDeltaOk { - local( $ret ) ; - local( @verlist ) ; - @verlist = split( /\//, $_[0] ) ; - pop( @verlist ) ; - if ( $#verlist > 1 ) { - $ret = 1 ; - } else { - $ret = 0 ; - } - $ret ; -} - -# get the comments from a version of an element -# Input is: -# $_[0] = element name -# $_[1] = version -# -# output in: -# @CTDeltaComments -sub CTDeltaGetComments { - local( *CMDFILE ) ; - local( $done ) = 0 ; - local( $end ) = " element type:" ; - local( $tmp ) = "cleartool describe $_[0]" . "@@" . "$_[1] |" ; - open( CMDFILE, $tmp ) ; - $_ = ; - $_ = ; - while ( ! $done ) { - $_ = ; - if ( $_ =~ /^$end/ ) { - $done = 1 ; - } else { - s/^ // ; - s/^ // ; - s/^\"// ; - s/\n$// ; - s/\"$// ; - push( @CTDeltaComments, $_ ) ; - } - } - close( CMDFILE ) ; -} - -# try automatic merge. If it fails, use xmerge -# Input is: -# $_[0] = element name -# $_[1] = source version -# $_[2] = target version -sub CTDeltaSafeMerge { - @CTDeltaComments = (); - &CTDeltaGetComments($_[0], $_[1]); - local( $ret ) ; - $ret = "cleartool checkout -branch $_[2] -nc $_[0] 2> /dev/null > /dev/null" ; - $ret = system $ret ; - if ( $ret != 0 ) { - print STDERR "got return value $ret from checkout on '$_[0]" . "@@" . "$_[2]'\n" ; - exit -1; - } - local( $item ) ; - foreach $item ( @CTDeltaComments ) { - $ret = "cleartool chevent -append -c \"" . $item . "\" $_[0]" . "@@" . "$_[2]" . "/LATEST 2> /dev/null > /dev/null" ; - system $ret ; - } - print STDERR "merging '$_[0]'...\n" ; - $ret = "cleartool merge -abort -to $_[0] -version $_[1] 2> /dev/null > /dev/null" ; - $ret = system $ret ; - if ( $ret != 0 ) { - $ret = system "cleartool xmerge -to $_[0] -version $_[1]" ; - } - if ( ! -d $_[0] ) { - system "rm $_[0]" . ".contrib" ; - } - $ret ; -} - -# test a branch for 'triviality' -# Input is: -# $_[0] = element name -# $_[1] = branch name -# -# Output is: -# true/false -sub CTDeltaTestBranch { - local( *CTCMD ) ; - local( $ret ) ; - local( $done ) = 0 ; - local( $bfrom ) ; - local( @blist ) ; - local( $bto ) ; - local( $bdiff ) ; - local( $blast ) ; - @blist = split( /\//, $_[1] ) ; - pop( @blist ) ; - $ret = join( "/", @blist ) ; - $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ; - open( CTCMD, $ret ) ; - while ( ! $done ) { - $_ = ; - if ( $_ =~ /^ branched from version/ ) { - $done = 1 ; - } - } - close( CTCMD ) ; - s/^ branched from version: // ; - s/\n$// ; - $bfrom = $_ ; - @blist = split( /\//, $_ ) ; - pop( @blist ) ; - push( @blist, "LATEST" ) ; - $ret = join( "/", @blist ) ; - $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ; - open( CTCMD, $ret ) ; - $_ = ; - close( CTCMD ) ; - s/\n$// ; - s/^.*@@// ; - s/\"$// ; - $bto = $_ ; - @blist = split( /\//, $bfrom ) ; - $bfrom = pop( @blist ) ; - @blist = split( /\//, $bto ) ; - $bto = pop( @blist ) ; - $bdiff = $bto - $bfrom ; - $ret = "cleartool describe $_[0]" . "@@" . "$_[1] |" ; - open( CTCMD, $ret ) ; - $_ = ; - close( CTCMD ) ; - s/\n$// ; - s/^.*@@// ; - s/\"$// ; - @blist = split( /\//, $_ ) ; - $blast = pop( @blist ) ; - if (( $bdiff > 1 ) || ( $blast > 1 )) { - $ret = 0 ; - } else { - $ret = 1 ; - } -} - -# check for trivial branch elimination -# Input is: -# $_[0] = element name -# $_[1] = last branch version -# $_[2] = timestamp string -sub CTDeltaBranchCheck { - local( $test ) = &CTDeltaTestBranch( $_[0], $_[1] ) ; - local( $cmd ) ; - local( @blist ) ; - local( $branch ) ; - @blist = split( /\//, $_[1] ) ; - if ( $test ) { - pop( @blist ) ; - $cmd = join( "/", @blist ) ; - $branch = join( "/", @blist ) ; - $cmd = "cleartool rmbranch -force $_[0]" . "@@" . "$cmd 2> /dev/null > /dev/null" ; - print STDERR "deleting branch '$branch'...\n" ; - system $cmd ; - } else { - pop( @blist ) ; - $branch = join( "/", @blist ) ; - $test = pop( @blist ) ; - $test = $test . $_[2] ; - $cmd = "cleartool mkbrtype -c \"non-trivial branch\" $test 2> /dev/null > /dev/null" ; - system $cmd ; - $cmd = "cleartool chtype -c \"renaming non-trivial branch\" $test $_[0]" . "@@" . "$branch 2> /dev/null > /dev/null" ; - print STDERR "renaming branch '$branch'...\n" ; - system $cmd ; - } -} - -# log merge to /usr/local/etc/delta_log -# Input is: -# $_[0] = element name -# $_[1] = source version -# $_[2] = target version -sub CTDeltaLog { - local( *LOGFILE ) ; - local( *CMDFILE ) ; - local( $cmd ) ; - open( LOGFILE, ">>/usr/local/etc/delta_log" ) ; - print LOGFILE $_[0] . ": " . $_[1] . " -> " . $_[2] . " : " ; - if ( $ctdebug ne "" ) { - print STDERR "CTDeltaLog: outputting '" . $_[0] . ": " . $_[1] . " -> " . $_[2] . " : '\n" ; - } - $cmd = "ypmatch `whoami` passwd | cut -d: -f5 |" ; - open( CMDFILE, $cmd ) ; - $_ = ; - s/\n$//; - print LOGFILE $_ . " " ; - if ( $ctdebug ne "" ) { - print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ; - } - close( CMDFILE ) ; - $cmd = "/bin/date '+%m/%d/%y %H:%M:%S' |" ; - open( CMDFILE, $cmd ) ; - $_ = ; - s/\n$//; - print LOGFILE $_ . "\n" ; - if ( $ctdebug ne "" ) { - print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ; - } - close( CMDFILE ) ; - close( LOGFILE ) ; -} - -1; diff --git a/dtool/src/attach/ctdelta.pl.rnd b/dtool/src/attach/ctdelta.pl.rnd deleted file mode 100644 index dde5feb574..0000000000 --- a/dtool/src/attach/ctdelta.pl.rnd +++ /dev/null @@ -1,232 +0,0 @@ -# Check in element if needed -# Input is: -# $_[0] = element name -sub CTDeltaCheckin { - local( $cmd ) = "cleartool ci -nc $_[0] 2> /dev/null > /dev/null" ; - system $cmd ; -} - -# get the version of an element -# Input is: -# $_[0] = element name -sub CTDeltaGetVersion { - local( *CMDFILE ) ; - open( CMDFILE, "cleartool describe -short $_[0] |" ) ; - $_ = ; - close( CMDFILE ) ; - s/\n$// ; - s/^.*@@// ; - s/\"$// ; - $_ ; -} - -# Is it ok to try a merge on this version? -# Input is: -# $_[0] = version -sub CTDeltaOk { - local( $ret ) ; - local( @verlist ) ; - @verlist = split( /\//, $_[0] ) ; - pop( @verlist ) ; - if ( $#verlist > 1 ) { - $ret = 1 ; - } else { - $ret = 0 ; - } - $ret ; -} - -# get the comments from a version of an element -# Input is: -# $_[0] = element name -# $_[1] = version -# -# output in: -# @CTDeltaComments -sub CTDeltaGetComments { - local( *CMDFILE ) ; - local( $done ) = 0 ; - local( $end ) = " element type:" ; - local( $tmp ) = "cleartool describe $_[0]" . "@@" . "$_[1] |" ; - open( CMDFILE, $tmp ) ; - $_ = ; - $_ = ; - while ( ! $done ) { - $_ = ; - if ( $_ =~ /^$end/ ) { - $done = 1 ; - } else { - s/^ // ; - s/^ // ; - s/^\"// ; - s/\n$// ; - s/\"$// ; - push( @CTDeltaComments, $_ ) ; - } - } - close( CMDFILE ) ; -} - -# try automatic merge. If it fails, use xmerge -# Input is: -# $_[0] = element name -# $_[1] = source version -# $_[2] = target version -sub CTDeltaSafeMerge { - @CTDeltaComments = (); - &CTDeltaGetComments($_[0], $_[1]); - local( $ret ) ; - $ret = "cleartool checkout -branch $_[2] -nc $_[0] 2> /dev/null > /dev/null" ; - $ret = system $ret ; - if ( $ret != 0 ) { - print STDERR "got return value $ret from checkout on '$_[0]" . "@@" . "$_[2]'\n" ; - exit -1; - } - local( $item ) ; - foreach $item ( @CTDeltaComments ) { - $ret = "cleartool chevent -append -c \"" . $item . "\" $_[0]" . "@@" . "$_[2]" . "/LATEST 2> /dev/null > /dev/null" ; - system $ret ; - } - print STDERR "merging '$_[0]'...\n" ; - $ret = "cleartool merge -abort -to $_[0] -version $_[1] 2> /dev/null > /dev/null" ; - $ret = system $ret ; - if ( $ret != 0 ) { - $ret = system "cleartool xmerge -to $_[0] -version $_[1]" ; - } - if ( ! -d $_[0] ) { - system "rm $_[0]" . ".contrib" ; - } - $ret ; -} - -# test a branch for 'triviality' -# Input is: -# $_[0] = element name -# $_[1] = branch name -# -# Output is: -# true/false -sub CTDeltaTestBranch { - local( *CTCMD ) ; - local( $ret ) ; - local( $done ) = 0 ; - local( $bfrom ) ; - local( @blist ) ; - local( $bto ) ; - local( $bdiff ) ; - local( $blast ) ; - @blist = split( /\//, $_[1] ) ; - pop( @blist ) ; - $ret = join( "/", @blist ) ; - $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ; - open( CTCMD, $ret ) ; - while ( ! $done ) { - $_ = ; - if ( $_ =~ /^ branched from version/ ) { - $done = 1 ; - } - } - close( CTCMD ) ; - s/^ branched from version: // ; - s/\n$// ; - $bfrom = $_ ; - @blist = split( /\//, $_ ) ; - pop( @blist ) ; - push( @blist, "LATEST" ) ; - $ret = join( "/", @blist ) ; - $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ; - open( CTCMD, $ret ) ; - $_ = ; - close( CTCMD ) ; - s/\n$// ; - s/^.*@@// ; - s/\"$// ; - $bto = $_ ; - @blist = split( /\//, $bfrom ) ; - $bfrom = pop( @blist ) ; - @blist = split( /\//, $bto ) ; - $bto = pop( @blist ) ; - $bdiff = $bto - $bfrom ; - $ret = "cleartool describe $_[0]" . "@@" . "$_[1] |" ; - open( CTCMD, $ret ) ; - $_ = ; - close( CTCMD ) ; - s/\n$// ; - s/^.*@@// ; - s/\"$// ; - @blist = split( /\//, $_ ) ; - $blast = pop( @blist ) ; - if (( $bdiff > 1 ) || ( $blast > 1 )) { - $ret = 0 ; - } else { - $ret = 1 ; - } -} - -# check for trivial branch elimination -# Input is: -# $_[0] = element name -# $_[1] = last branch version -# $_[2] = timestamp string -sub CTDeltaBranchCheck { - local( $test ) = &CTDeltaTestBranch( $_[0], $_[1] ) ; - local( $cmd ) ; - local( @blist ) ; - local( $branch ) ; - @blist = split( /\//, $_[1] ) ; - if ( $test ) { - pop( @blist ) ; - $cmd = join( "/", @blist ) ; - $branch = join( "/", @blist ) ; - $cmd = "cleartool rmbranch -force $_[0]" . "@@" . "$cmd 2> /dev/null > /dev/null" ; - print STDERR "deleting branch '$branch'...\n" ; - system $cmd ; - } else { - pop( @blist ) ; - $branch = join( "/", @blist ) ; - $test = pop( @blist ) ; - $test = $test . $_[2] ; - $cmd = "cleartool mkbrtype -c \"non-trivial branch\" $test 2> /dev/null > /dev/null" ; - system $cmd ; - $cmd = "cleartool chtype -c \"renaming non-trivial branch\" $test $_[0]" . "@@" . "$branch 2> /dev/null > /dev/null" ; - print STDERR "renaming branch '$branch'...\n" ; - system $cmd ; - } -} - -# log merge to /var/etc/delta_log -# Input is: -# $_[0] = element name -# $_[1] = source version -# $_[2] = target version -sub CTDeltaLog { - local( *LOGFILE ) ; - local( *CMDFILE ) ; - local( $cmd ) ; - open( LOGFILE, ">>/var/etc/delta_log" ) ; - print LOGFILE $_[0] . ": " . $_[1] . " -> " . $_[2] . " : " ; - if ( $ctdebug ne "" ) { - print STDERR "CTDeltaLog: outputting '" . $_[0] . ": " . $_[1] . " -> " . $_[2] . " : '\n" ; - } - $cmd = "ypmatch `whoami` passwd | cut -d: -f5 |" ; - open( CMDFILE, $cmd ) ; - $_ = ; - s/\n$//; - print LOGFILE $_ . " " ; - if ( $ctdebug ne "" ) { - print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ; - } - close( CMDFILE ) ; - $cmd = "/bin/date '+%m/%d/%y %H:%M:%S' |" ; - open( CMDFILE, $cmd ) ; - $_ = ; - s/\n$//; - print LOGFILE $_ . "\n" ; - if ( $ctdebug ne "" ) { - print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ; - } - close( CMDFILE ) ; - close( LOGFILE ) ; -} - -1; diff --git a/dtool/src/attach/ctihave b/dtool/src/attach/ctihave deleted file mode 100755 index c6cfe5267f..0000000000 --- a/dtool/src/attach/ctihave +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/perl - -if ($#ARGV != -1) { - exit print "Usage: ctihave\n" ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "not configured for using CTtools\n" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -if ( $projname eq "" ) { - exit print "Not currently in any project tree\n" ; -} - -$result = &CTCMIHave( $projname, $flav, $spec ) ; -if ( $result ne "" ) { - @splitlist = split( /\n/, $result ) ; - foreach $item ( @splitlist ) { - print $item . "\n" ; - } -} diff --git a/dtool/src/attach/ctmake b/dtool/src/attach/ctmake deleted file mode 100644 index 56326744ab..0000000000 --- a/dtool/src/attach/ctmake +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/perl - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "not configured for using ct-tools\n" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -$line = join( " ", @ARGV ) ; - -if ( ! &CTCMMake( $line, $projname, $spec ) ) { - print STDERR "Could not make '$line'\n" ; -} diff --git a/dtool/src/attach/ctmkdir b/dtool/src/attach/ctmkdir deleted file mode 100644 index 678dd9494e..0000000000 --- a/dtool/src/attach/ctmkdir +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/perl - -sub CTMkDirUsage { - print STDERR "Usage: ctmkdir [-c \"comment\"] [-nc] dir-name [...]\n" ; - print STDERR "Options:\n" ; - print STDERR " -c \"comment\" : provide a comment about this action\n" ; - print STDERR " -nc : expect no comment on this action\n" ; - exit ; -} - -if ( $#ARGV < 0 ) { - &CTMkDirUsage ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured for CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$comment = "" ; -if ( $ARGV[0] eq "-nc" ) { - shift( @ARGV ) ; - &CTUDebug( "-nc processed\n" ) ; -} -if ( $ARGV[0] eq "-c" ) { - shift( @ARGV ) ; - $comment = $ARGV[0] ; - shift( @ARGV ) ; - &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; -} - -if ( $#ARGV < 0 ) { - &CTMkDirUsage ; -} - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @ARGV ) { - if ( -e $item ) { - print STDERR "Name collision on directory '$item'\n" ; - } else { - if ( ! &CTCMMkdir( $item, $projname, $spec, $comment ) ) { - print STDERR "Could name make directory '$item'\n" ; - } - } -} diff --git a/dtool/src/attach/ctmkelem b/dtool/src/attach/ctmkelem deleted file mode 100644 index 3d0018cad7..0000000000 --- a/dtool/src/attach/ctmkelem +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/perl - -sub CTMkElemUsage { - print STDERR "Usage: ctmkelem [-c \"comment\"] [-nc] [-eltype type] element-name [...]\n" ; - print STDERR "Options:\n" ; - print STDERR " -c \"comment\" : provide a comment about this action\n" ; - print STDERR " -nc : expect no comment on this action\n" ; - print STDERR " -eltype type : element type\n" ; - exit ; -} - -if ( $#ARGV < 0 ) { - &CTMkElemUsage ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured for CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$comment = "" ; -$eltype = "" ; - -$done = 0 ; - -while ( ! $done ) { - $done = 1 ; - if ( $ARGV[0] eq "-nc" ) { - shift( @ARGV ) ; - &CTUDebug( "-nc processed\n" ) ; - $done = 0 ; - } - if ( $ARGV[0] eq "-c" ) { - shift( @ARGV ) ; - $comment = $ARGV[0] ; - shift( @ARGV ) ; - &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; - $done = 0 ; - } - if ( $ARGV[0] eq "-eltype" ) { - shift( @ARGV ) ; - $eltype = $ARGV[0] ; - shift( @ARGV ) ; - &CTUDebug( "setting eltype to '" . $eltype . "'\n" ) ; - $done = 0 ; - } -} - -if ( $#ARGV < 0 ) { - &CTMkElemUsage ; -} - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @ARGV ) { - if ( -e $item ) { - if ( -d $item ) { - print STDERR "Cannot mkelem on an existing directory." . - " Ctmkdir it first.\n" ; - } else { - if ( ! &CTCMMkelem( $item, $projname, $spec, $comment, $eltype )) { - print STDERR "Could not make a versioned element of '" . - $item . "'\n" ; - } - } - } else { - print STDERR "No such file '$item'.\n" ; - } -} diff --git a/dtool/src/attach/ctmv b/dtool/src/attach/ctmv deleted file mode 100644 index 6b4985429e..0000000000 --- a/dtool/src/attach/ctmv +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/perl - -if ( $#ARGV != 1 ) { - exit print "Usage: ctmv from-element to-element\n" ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured for CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -$from = $ARGV[0] ; -$to = $ARGV[1] ; - -if ( -e $from ) { - if ( -e $to ) { - print STDERR "'$to' already exists.\n" ; - } else { - if ( ! &CTCMMv( $from, $to, $projname, $spec ) ) { - } - } -} else { - print STDERR "No such element '$from'.\n" ; -} diff --git a/dtool/src/attach/ctntool.pl b/dtool/src/attach/ctntool.pl deleted file mode 100644 index 6920a3ee23..0000000000 --- a/dtool/src/attach/ctntool.pl +++ /dev/null @@ -1,258 +0,0 @@ -# given a possibly empty string, format it into a comment or -nc -# input is in: -# $_[0] = possible comment string -# -# output is: -# string for use by neartool functions -sub CTNtoolFormatComment { - local( $ret ) = "" ; - if ( $_[0] eq "" ) { - $ret = "-nc" ; - } else { - $ret = "-c \"" . $_[0] . "\"" ; - } - $ret ; -} - -# make a versioned directory -# input is in: -# $_[0] = directory to create -# $_[1] = curr dir -# $_[2] = possible comment -# -# output: -# return success or failure -sub CTNtoolMkdir { - &CTUDebug( "in CTNtoolMkdir\n" ) ; - local( $ret ) = 0 ; - local( $dir ) = $_[0] ; - if ( ! ( $dir =~ /^\// )) { - $dir = $_[1] . "/" . $dir ; - } - local( $comment ) = &CTNtoolFormatComment( $_[2] ) ; - # first we have to check out the parent directory - local( @alist ) = split( /\//, $dir ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $dir . "' is '" . $parent . "'\n" ) ; - $ret = system( "neartool co -nc $parent\n" ) ; - if ( $ret == 0 ) { - # now make the dir - $ret = &CTURetCode( system( "neartool mkdir " . $comment . - " $dir\n" )) ; - } else { - $ret = 0 ; - } - &CTUDebug( "out of CTNtoolMkdir\n" ) ; - $ret ; -} - -# make a versioned element -# input is in: -# $_[0] = element to version -# $_[1] = curr dir -# $_[2] = possible comment -# $_[3] = possible eltype -# -# output: -# return success or failure -sub CTNtoolMkelem { - &CTUDebug( "in CTNtoolMkelem\n" ) ; - local( $ret ) = 0 ; - local( $elem ) = $_[0] ; - if ( ! ( $elem =~ /^\// )) { - $elem = $_[1] . "/" . $elem ; - } - local( $comment ) = &CTNtoolFormatComment( $_[2] ) ; - local( $eltype ) = $_[3] ; - if ( $eltype ne "" ) { - $eltype = "-eltype " . $eltype ; - } - local( $line ) = "neartool mkelem " . $comment . " " . $eltype . " " . - $elem . "\n" ; - &CTUDebug( $line ) ; - $ret = &CTURetCode( system( $line )) ; - &CTUDebug( "out of CTNtoolMkelem\n" ) ; - $ret ; -} - -# delta an element -# input is in: -# $_[0] = element to delta -# -# output: -# return success or failure -sub CTNtoolDelta { - &CTUDebug( "in CTNtoolDelta\n" ) ; - local( $ret ) = 0 ; - # as Dave points out, when working off-line, delta is the same as checkin - $ret = &CTURetCode( system( "neartool ci " . $_[0] )) ; - &CTUDebug( "out of CTNtoolDelta\n" ) ; - $ret ; -} - -# checkout an element -# input is in: -# $_[0] = element to checkout -# $_[1] = possible comment -# -# output: -# return success or failure -sub CTNtoolCheckout { - &CTUDebug( "in CTNtoolCheckout\n" ) ; - local( $ret ) = 0 ; - local( $comment ) = &CTNtoolFormatComment( $_[1] ) ; - if ( ! -d $_[0] ) { - $ret = &CTURetCode( system( "neartool co " . $comment . " " . - $_[0] )) ; - } else { - # neartool doesn't do anything about checking out directories - $ret = 1 ; - } - &CTUDebug( "out of CTNtoolCheckout\n" ) ; - $ret ; -} - -# checkin an element -# input is in: -# $_[0] = element to checkin -# $_[1] = possible comment -# -# output: -# return success or failure -sub CTNtoolCheckin { - &CTUDebug( "in CTNtoolCheckin\n" ) ; - local( $ret ) = 0 ; - local( $comment ) = &CTNtoolFormatComment( $_[1] ) ; - $ret = &CTURetCode( system( "neartool ci " . $comment . " " . $_[0] )) ; - &CTUDebug( "out of CTNtoolCheckin\n" ) ; - $ret ; -} - -# uncheckout an element -# input is in: -# $_[0] = element to uncheckout -# -# output: -# return success or failure -sub CTNtoolUncheckout { - &CTUDebug( "in CTNtoolUncheckout\n" ) ; - local( $ret ) = 0 ; - $ret = &CTURetCode( system( "neartool unco " . $_[0] )) ; - &CTUDebug( "out of CTNtoolUncheckout\n" ) ; - $ret ; -} - -# figure out what all I have checked out -# input is in: -# $_[0] = project -# $_[1] = flavor -# $_[2] = spec line -# -# output: -# return a \n serperated list of elements checked out -sub CTNtoolIHave { - &CTUDebug( "in CTNtoolIHave\n" ) ; - local( $ret ) = "" ; - local( $root ) = &CTProjRoot( $_[0] ) ; - local( *OUTPUT ) ; - open( OUTPUT, "neartool find " . $root . " |" ) ; - while ( ) { - $ret = $ret . $_ ; - } - close( OUTPUT ) ; - &CTUDebug( "out of CTNToolIHave\n" ) ; - $ret ; -} - -# remove a versioned element -# input is in: -# $_[0] = element to remove -# $_[1] = curr dir -# -# output: -# return success or failure -sub CTNtoolRmElem { - &CTUDebug( "in CTNtoolRmElem\n" ) ; - local( $ret ) = 0 ; - local( $elem ) = $_[0] ; - if ( ! ( $elem =~ /^\// )) { - $elem = $_[1] . "/" . $elem ; - } - # first we have to check out the parent directory - local( @alist ) = split( /\//, $elem ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; - $ret = system( "neartool co -nc $parent\n" ) ; - if ( $ret == 0 ) { - # now nuke the element - $ret = &CTURetCode( system( "neartool rmname $elem\n" )) ; - } else { - $ret = 0 ; - } - &CTUDebug( "out of CTNtoolRmElem\n" ) ; - $ret ; -} - -# mv a versioned element from one name to another -# input is in: -# $_[0] = from element -# $_[1] = to element -# $_[2] = current directory -# -# output: -# return success or failure -sub CTNtoolMv { - &CTUDebug( "in CTNtoolMv\n" ) ; - local( $ret ) = 0 ; - local( $elem ) = $_[0] ; - if ( ! ( $elem =~ /^\// )) { - $elem = $_[2] . "/" . $elem ; - } - # first we have to check out the parent directory - local( @alist ) = split( /\//, $elem ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; - local( $elem2 ) = $_[1] ; - if ( ! ( $elem2 =~ /^\// )) { - $elem2 = $_[2] . "/" . $elem2 ; - } - @alist = split( /\//, $elem2 ) ; - pop( @alist ) ; - local( $parent2 ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem2 . "' is '" . $parent2 . - "'\n" ) ; - $ret = system( "neartool co -nc $parent\n" ) ; - if ( $ret == 0 ) { - $ret = system( "neartool co -nc $parent2\n" ) ; - if ( $ret == 0 ) { - # now move the element - $ret = &CTURetCode( system( "neartool mv $elem $elem2\n" )) ; - } else { - $ret = 0 ; - } - } else { - $ret = 0 ; - } - &CTUDebug( "out of CTNtoolMv\n" ) ; - $ret ; -} - -# build a list of targets -# input is in: -# $_[0] = targets -# -# output: -# return success or failure -sub CTNtoolMake { - &CTUDebug( "in CTNtoolMake\n" ) ; - local( $ret ) = 0 ; - local( $line ) = "make " . $_[0] . "\n" ; - $ret = &CTURetCode( system( $line )) ; - &CTUDebug( "out of CTNtoolMake\n" ) ; - $ret ; -} - -1; diff --git a/dtool/src/attach/ctproj.pl b/dtool/src/attach/ctproj.pl deleted file mode 100644 index 38f5fd269b..0000000000 --- a/dtool/src/attach/ctproj.pl +++ /dev/null @@ -1,60 +0,0 @@ -require "$tool/built/include/ctutils.pl" ; - -# return the root of the given project. -sub CTProjRoot { - local( $CTPRtmp ) = $_[0] ; - $CTPRtmp =~ tr/a-z/A-Z/ ; - local( $CTPRret ) = $ENV{ $CTPRtmp } ; - $CTPRret ; -} - -# return the package we're currently in. -# input: -# $_[0] = project -sub CTProjPkg { - local( $CTPPret ) = &CTUCurrDir() ; - local( $CTPPtmp ) = $_[0] ; - $CTPPtmp =~ tr/a-z/A-Z/ ; - $CTPPret =~ s/$ENV{ $CTPPtmp }// ; - $CTPPret =~ s/\/src\/// ; - $CTPPret =~ s/\/metalibs\/// ; - $CTPPret ; -} - -# reutrn the project containing the given directory. If no directory is given, -# return the project containing the current directory. -sub CTProj { - local( $CTPdir ) ; - if ($_[0] eq "") { - $CTPdir = &CTUCurrDir() ; - } else { - # provided directory - $CTPdir = $_[0] ; - } - local( $CTPprojs ) = $ENV{"CTPROJS"} ; - local( $CTPdone ) = "" ; - local( @CTPlist ) ; - @CTPlist = split( / /, $CTPprojs ) ; - local( @CTPlist2 ) ; - local( $CTPtry ) ; - while (( $CTPdone eq "" ) && ( @CTPlist != () )){ - # pop the first one off the list - $CTPtmp = $CTPlist[0] ; - shift( @CTPlist ) ; - # split the project from it's flavor - @CTPlist2 = split( /:/, $CTPtmp ); - $CTPtry = &CTProjRoot( $CTPlist2[0] ) ; - # is CTPtry prefix of CTPdir? if so we have our winner - if ( $CTPdir =~ /^$CTPtry/ ) { - $CTPdone = "yep" ; - } - } - if ( $CTPdone eq "" ) { - $CTPtry = "" ; - } else { - $CTPtry = $CTPlist2[0] ; - } - $CTPtry ; -} - -1; diff --git a/dtool/src/attach/ctquery b/dtool/src/attach/ctquery deleted file mode 100755 index 89a2380ed8..0000000000 --- a/dtool/src/attach/ctquery +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/perl - -# acceptable forms: -# ctquery - list all attached projects and flavors -# ctquery project - list the attached flavor of the named project -# ctquery - flavor - list all attached projects who are attached with a -# given flavor - -$projs = $ENV{"CTPROJS"} ; -@projlist = split( / +/, $projs ) ; - -if ( $#ARGV == -1 ) { - # list all projects and flavors - print "Currently attached projects (and flavors):\n" ; - foreach $pair ( @projlist ) { - @pairlist = split( /:/, $pair ) ; - ( $pairtmp = $pairlist[0] ) =~ tr/A-Z/a-z/ ; - print " $pairtmp ($pairlist[1])\n" ; - } -} elsif (( $#ARGV == 0 ) && !($ARGV[0] =~ /^\-/)) { - # list the attached flavor of the named project - foreach $pair ( @projlist ) { - @pairlist = split( /:/, $pair ) ; - ( $pairtmp = $pairlist[0] ) =~ tr/A-Z/a-z/ ; - if ( $pairtmp eq $ARGV[0] ) { - print "$pairlist[1]\n" ; - } - } -} elsif (( $#ARGV == 1 ) && ( $ARGV[0] eq "-" )){ - # list all attached projects who are attached with a given flavor - foreach $pair ( @projlist ) { - @pairlist = split( /:/, $pair ) ; - if ( $pairlist[1] eq $ARGV[1] ) { - $pairlist[0] =~ tr/A-Z/a-z/ ; - print "$pairlist[0]\n" ; - } - } -} else { - print "Usage: ctquery [project] -or-\n" ; - print " ctquery - flavor\n" ; - exit ; -} diff --git a/dtool/src/attach/ctquery.pl b/dtool/src/attach/ctquery.pl deleted file mode 100644 index d93d42e9c7..0000000000 --- a/dtool/src/attach/ctquery.pl +++ /dev/null @@ -1,37 +0,0 @@ -# return the attached flavor of given project (or empty string) -sub CTQueryProj { - local( $projs ) = $ENV{"CTPROJS"} ; - local( @projlist ) ; - @projlist = split( / +/, $projs ) ; - local( $pair ) ; - local( @pairlist ) ; - local( $ret ) = "" ; - foreach $pair ( @projlist ) { - @pairlist = split( /:/, $pair ) ; - $pairlist[0] =~ tr/A-Z/a-z/ ; - if ( $pairlist[0] eq $_[0] ) { - $ret = $pairlist[1] ; - } - } - $ret ; -} - -# return all projects attached with a given flavor -sub CTQueryFlav { - local( $projs ) = $ENV{"CTPROJS"} ; - local( @projlist ) ; - @projlist = split( / +/, $projs ) ; - local( $pair ) ; - local( @pairlist ) ; - local( $ret ) = "" ; - foreach $pair ( @projlist ) { - @pairlist = split( /:/, $pair ) ; - if ( $pairlist[1] eq $_[0] ) { - $pairlist[0] =~ tr/A-Z/a-z/ ; - $ret = $ret . " $pairlist[0]" ; - } - } - $ret ; -} - -1; diff --git a/dtool/src/attach/ctrm b/dtool/src/attach/ctrm deleted file mode 100644 index 7cac8c290e..0000000000 --- a/dtool/src/attach/ctrm +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/perl - -if ( $#ARGV < 0 ) { - exit print "Usage: ctrmelem element-name [...]\n" ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured for CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @ARGV ) { - if ( -e $item ) { - if ( ! &CTCMRmElem( $item, $projname, $spec ) ) { - print STDERR "Could not rmname '$item'\n" ; - } - } else { - print STDERR "No such file '$item'.\n" ; - } -} diff --git a/dtool/src/attach/ctsanity b/dtool/src/attach/ctsanity deleted file mode 100644 index 9d658275bb..0000000000 --- a/dtool/src/attach/ctsanity +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/perl - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured to run CTtools" ; -} - -sub CTSanityUsage { - print STDERR "Usage: ctsanity [-v]\n" ; - print STDERR "Options:\n" ; - print STDERR " -v : sanity check the .vspec files \n" ; - exit ; -} - -if ( $#ARGV == -1 ) { - &CTSanityUsage ; -} - -$check_vspecs = 0 ; - -foreach $item ( @ARGV ) { - if ( $item eq "-v" ) { - $check_vspecs = 1 ; - } else { - print STDERR "unknown option '" . $item . "'\n" ; - $CTSanityUsage ; - } -} - -require "$tool/built/include/ctvspec.pl" ; - -if ( $check_vspecs ) { - local( $projs ) = &CTListAllProjects ; - local( @projlist ) = split( / +/, $projs ) ; - local( $item ) ; - foreach $item ( @projlist ) { - print STDERR "checking " . $item . ".vspec:\n" ; - local( $ctsavedebug ) = $ctdebug ; - $ctdebug = 1 ; - &CTReadVSpec( $item ) ; - $ctdebug = $ctsavedebug ; - } -} diff --git a/dtool/src/attach/cttimewarp b/dtool/src/attach/cttimewarp deleted file mode 100755 index e64d121e61..0000000000 --- a/dtool/src/attach/cttimewarp +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/perl - -if ($#ARGV < 0) { - exit print "Usage: cttimewarp [-clear] label [time]\n" ; -} - -@arglist = @ARGV ; - -$clear = 0 ; - -if ( $arglist[0] =~ /^-c/ ) { - $clear = 1 ; - shift( @arglist ) ; -} - -if ( @arglist == () ) { - if ( $clear ) { - exit print "Usage: cttimewarp -clear label\n" ; - } else { - exit print "Usage: cttimewarp label time\n" ; - } -} - -$label = $arglist[0] ; -shift( @arglist ) ; - -if (( ! $clear ) && ( @arglist == () )) { - exit print "Usage: cttimewarp label time\n" ; -} - -$time = $arglist[0] ; - -if ( $clear ) { - $cmd = "cleartool find . -version \"lbtype(" . $label . - ")\" -exec 'cleartool rmlabel -c \"untimewarping\" " . $label . - ' $CLEARCASE_XPN' . "'\n" ; - system( $cmd ) ; -} else { - $cmd = "cleartool mklabel -replace -recurse -c \"rolling time back to " . - $time . "\" -version /main/'{\!created_since(" . $time . ")}' " . - $label . " .\n" ; - system( $cmd ) ; -} diff --git a/dtool/src/attach/ctunattach.drv b/dtool/src/attach/ctunattach.drv deleted file mode 100644 index daea40d5eb..0000000000 --- a/dtool/src/attach/ctunattach.drv +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/perl - -# acceptable forms: -# ctunattach project - attach to the personal flavor of the project - -sub CTUnattachUsage { - print STDERR "Usage: ctattach project(s)\n" ; - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; - exit; -} - -$tool = $ENV{"DTOOL"} ; - -require "$tool/built/include/ctattch.pl" ; -require "$tool/built/include/ctunattach.pl" ; -require "$tool/built/include/ctquery.pl" ; - -$tmpname = "/tmp/script.$$" ; - -if ( $#ARGV == -1 ) { - &CTUnattachUsage ; -} - -foreach $proj ( @ARGV ) { - &CTUDebug( "project is '$proj'\n" ) ; - - $curflav = &CTQueryProj( $proj ) ; - if ( $curflav ne "" ) { - $envsep{"PATH"} = ":" ; - $envsep{"LD_LIBRARY_PATH"} = ":" ; - $envsep{"DYLD_LIBRARY_PATH"} = ":" ; - $envsep{"PFPATH"} = ":" ; - $envsep{"SSPATH"} = ":" ; - $envsep{"STKPATH"} = ":" ; - $envsep{"DC_PATH"} = ":" ; - $spec = &CTUnattachCompute( $proj, $curflav ) ; - if ( $spec eq "" ) { - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; - } else { - &CTUnattachWriteScript( $tmpname ) ; - print $tmpname . "\n" ; - } - } else { - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; - } -} diff --git a/dtool/src/attach/ctunattach.pl b/dtool/src/attach/ctunattach.pl deleted file mode 100644 index 08e867921f..0000000000 --- a/dtool/src/attach/ctunattach.pl +++ /dev/null @@ -1,251 +0,0 @@ -require "$tool/built/include/ctquery.pl" ; - -$shell_type = "csh" ; -if ( $ENV{"SHELL_TYPE"} ne "" ) { - if ( $ENV{"SHELL_TYPE"} eq "sh" ) { - $shell_type = "sh" ; - } -} - -# remove a value from a variable. If it is the only thing remaining in the -# variable, add it to the unset list. -# input is in: -# $_[0] = variable -# $_[1] = value -# -# output is in: -# %newenv = an image of how we want the environment to be -# @unset = a list of variables to unset -sub CTUnattachMod { - &CTUDebug( "in CTUnattachMod\n" ) ; - local( $done ) = 0 ; - # if we didn't get any data, nothing really to do - if ( $_[0] eq "" ) { $done = 1 ; } - if ( $_[1] eq "" ) { $done = 1 ; } - # if the variable is already set to be unset, nothing really to do - if ( join( " ", @unset ) =~ /$_[0]/ ) { $done = 1 ; } - # if the variable isn't in newenv, move it there, if it's empty mark it - # for unsetting - if ( $newenv{$_[0]} eq "" ) { - $newenv{$_[0]} = &CTSpoolEnv( $_[0] ) ; - if ( $newenv{$_[0]} eq "" ) { - push( @unset, $_[0] ) ; - delete $newenv{$_[0]} ; - $done = 1 ; - } - } - # if the value does not appear in the variable, nothing really to do - if ( ! ( $newenv{$_[0]} =~ /$_[1]/ ) ) { $done = 1 ; } - # now down to the real work - if ( ! $done ) { - # if the variable is exactly the value, mark it for unsetting - if ( $newenv{$_[0]} eq $_[1] ) { - push( @unset, $_[0] ) ; - delete $newenv{$_[0]} ; - } elsif ( $newenv{$_[0]} =~ / $_[1]/ ) { - local( $tmp ) = $newenv{$_[0]} ; - $tmp =~ s/ $_[1]// ; - $newenv{$_[0]} = $tmp ; - } elsif ( $newenv{$_[0]} =~ /$_[1] / ) { - local( $tmp ) = $newenv{$_[0]} ; - $tmp =~ s/$_[1] // ; - $newenv{$_[0]} = $tmp ; - } else { - print STDERR "ERROR: variable '" . $_[0] . "' contains '" . - $_[1] . "' (in '" . $newenv{$_[0]} . - "'), but I am too stupid to figure out how to remove it.\n" ; - } - } -} - -# given the project and flavor, build the lists of variables to set/modify -# input is in: -# $_[0] = project -# $_[1] = flavor -# -# output is in: -# return value is config line -# %newenv = an image of what we want the environment to look like -# @unset = list of variables to be unset -# %envsep = seperator -# %envcmd = set or setenv -# %envpostpend = flag that variable should be postpended -sub CTUnattachCompute { - &CTUDebug( "in CTUnattachCompute\n" ) ; - local( $flav ) = $_[1] ; - local( $spec ) = &CTResolveSpec( $_[0], $flav ) ; - local( $root ) = &CTComputeRoot( $_[0], $flav, $spec ) ; - - if ( $spec ne "" ) { - local( $proj ) = $_[0] ; - $proj =~ tr/a-z/A-Z/ ; - local( $item ) ; - - # since we don't have to worry about sub-attaches, it doesn't matter - # if we scan the .init file first or not. So we won't. - &CTUDebug( "extending paths\n" ) ; - - $item = $root . "/built/bin" ; - &CTUnattachMod( "PATH", $item ) ; - $item = $root . "/built/lib" ; - if ( $ENV{"PENV"} eq "WIN32" ) { - &CTUnattachMod( "PATH", $item ) ; - } - &CTUnattachMod( "LD_LIBRARY_PATH", $item ) ; - &CTUnattachMod( "DYLD_LIBRARY_PATH", $item ) ; - #$item = $root . "/src/all" ; - #&CTUnattachMod( "CDPATH", $item ) ; - $item = $root . "/built/include" ; - &CTUnattachMod( "CT_INCLUDE_PATH", $item ) ; - $item = $root . "/built/etc" ; - &CTUnattachMod( "ETC_PATH", $item ) ; - $item = $proj . ":" . $flav ; - &CTUnattachMod( "CTPROJS", $item ) ; - push( @unset, $proj ) ; - - if ( -e "$root/built/etc/$_[0].init" ) { - &CTUDebug( "scanning $_[0].init file\n" ) ; - local( @linesplit ) ; - local( $linetmp ) ; - local( $loop ); - local( *INITFILE ) ; - if ( -x "$root/built/etc/$_[0].init" ) { - open( INITFILE, "$root/built/etc/$_[0].init $_[0] $_[1] $root |" ) ; - } else { - open( INITFILE, "< $root/built/etc/$_[0].init" ) ; - } - while ( ) { - s/\n$// ; - @linesplit = split( /\#/ ) ; - $_ = $linesplit[0] ; - if ( $_ =~ /^MODABS/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - &CTUnattachMod( $linetmp, $loop ) ; - } - } elsif ( $_ =~ /^MODREL/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - &CTUnattachMod( $linetmp, $root . "/" . $loop ) ; - } - } elsif ( $_ =~ /^SETABS/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - push( @unset, $linetmp ) ; - } elsif ( $_ =~ /^SETREL/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - push( @unset, $linetmp ) ; - } elsif ( $_ =~ /^SEP/ ) { - @linesplit = split ; - $envsep{$linesplit[1]} = $linesplit[2] ; - } elsif ( $_ =~ /^CMD/ ) { - @linesplit = split ; - $envcmd{$linesplit[1]} = $linesplit[2] ; - } elsif ( $_ =~ /^DOCSH/ ) { - &CTUDebug( "ignoring DO command in .init file\n" ) ; - } elsif ( $_ =~ /^DOSH/ ) { - &CTUDebug( "ignoring DO command in .init file\n" ) ; - } elsif ( $_ =~ /^DO/ ) { - &CTUDebug( "ignoring DO command in .init file\n" ) ; - } elsif ( $_ =~ /^POSTPEND/ ) { - @linesplit = split ; - $envpospend{$linesplit[1]} = 1 ; - } elsif ( $_ =~ /^ATTACH/ ) { - &CTUDebug( "ignoring ATTACH command in .init file\n" ) ; - } else { - print STDERR "Unknown .init directive '$_'\n" ; - } - } - close( INITFILE ) ; - } - } - &CTUDebug( "out of CTUnattachCompute\n" ) ; - $spec ; -} - -# write a script to setup the environment -# Input is: -# $_[0] = filename -sub CTUnattachWriteScript { - &CTUDebug( "in CTAttachWriteScript\n" ) ; - local( *OUTFILE ) ; - open( OUTFILE, ">$_[0]" ) ; - print OUTFILE "#!/bin/" . $shell_type . " -f\n" ; - local( $item ) ; - #local( $unsetcdpath ) = 0 ; - #local( $modcdpath ) = 0 ; - - foreach $item ( @unset ) { - #if ( $item eq "CDPATH" ) { $unsetcdpath = 1 ; } - - if ( $shell_type eq "sh" ) { - print OUTFILE "$item=\n" ; - if ( $envcmd{$item} ne "set" ) { - print OUTFILE "export $item\n" ; - } - } else { - if ( $envcmd{$item} ne "" ) { - print OUTFILE "un" . $envcmd{$item} . " $item\n" ; - } else { - print OUTFILE "unsetenv $item\n" ; - } - } - } - foreach $item ( keys %newenv ) { - #if ( $item eq "CDPATH" ) { $modcdpath = 1 ; } - - local( $sep ) = " " ; - if ( $envsep{$item} ne "" ) { - $sep = $envsep{$item} ; - } - local( @splitlist ) = split( / +/, $newenv{$item} ) ; - local( $outval ) = join( $sep, @splitlist ) ; - - if ( $shell_type eq "sh" ) { - print OUTFILE "$item=\"" . $outval . "\"\n" ; - if ( $envcmd{$item} ne "set" ) { - print OUTFILE "export $item\n" ; - } - } else { - if ( $envcmd{$item} ne "" ) { - PRINT OUTFILE $envcmd{$item} . " $item " ; - if ( $envcmd{$item} eq "set" ) { - print OUTFILE " = ( " ; - } - print OUTFILE $outval ; - if ( $envcmd{$item} eq "set" ) { - print OUTFILE ")" ; - } - print OUTFILE "\n" ; - } else { - print OUTFILE "setenv $item \"$outval\"\n" ; - } - } - } - #if ( $unsetcdpath ) { - # if ( $shell_type ne "sh" ) { - # print OUTFILE "unset cdpath\n" ; - # } - #} elsif ( $modcdpath ) { - # if ( $shell_type ne "sh" ) { - # print OUTFILE "set cdpath = ( \$" . "CDPATH )\n" ; - # } - #} - - if (! $ctdebug) { - print OUTFILE "rm -f $_[0]\n" ; - } else { - print STDERR "no self-destruct script '" . $_[0] . "'\n" ; - } - close( OUTFILE ) ; - &CTUDebug( "out of CTUnattachWriteScript\n" ) ; -} - -1; diff --git a/dtool/src/attach/ctunco b/dtool/src/attach/ctunco deleted file mode 100644 index d96eab41dc..0000000000 --- a/dtool/src/attach/ctunco +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/perl - -if ( $#ARGV < 0 ) { - exit print "Usage ctunco element-name [...]\n" ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "not configured for using CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @ARGV ) { - if ( -e $item ) { - if ( ! &CTCMUncheckout( $item, $projname, $spec ) ) { - print STDERR "Could not uncheckout '$item'\n" ; - } - } else { - print STDERR "No such file '$item'.\n" ; - } -} diff --git a/dtool/src/attach/ctutils.pl b/dtool/src/attach/ctutils.pl deleted file mode 100644 index a32dd5a6f3..0000000000 --- a/dtool/src/attach/ctutils.pl +++ /dev/null @@ -1,47 +0,0 @@ -# evaluate the given parameter to expand shell variables -sub CTUShellEval { - local( *CTUSEFILE ) ; - open( CTUSEFILE, "echo $_[0] |" ) ; - local( $CTUSEret ) = ; - close( CTUSEFILE ) ; - $CTUSEret =~ s/\n$// ; - $CTUSEret ; -} - -# if debug is on, print the argument -sub CTUDebug { - if ( $ctdebug ) { - print STDERR $_[0] ; - } -} - -use Cwd ; -# get current directory -sub CTUCurrDir { - local( $pwd ) = getcwd() ; - if ( $pwd =~ /^\/vobs/ ) { - local( *VFILE ) ; - open( VFILE, "cleartool pwv -short |" ) ; - local( $view ) = ; - close( VFILE ) ; - $view =~ s/\n$// ; - $pwd = "/view/" . $view . $pwd ; - } - $pwd ; -} - -# turn a shell return code into a success/fail flag -sub CTURetCode { - local( $ret ) ; - if ( $_[0] == 0 ) { - $ret = 1 ; - } else { - $ret = 0 ; - } - $ret ; -} - -$ctdebug = $ENV{"CTATTACH_DEBUG"} ; -$ctvspec_path = '/usr/local/etc' unless $ctvspec_path = $ENV{'CTVSPEC_PATH'}; - -1; diff --git a/dtool/src/attach/ctvspec.pl b/dtool/src/attach/ctvspec.pl deleted file mode 100644 index 8f556d2ee7..0000000000 --- a/dtool/src/attach/ctvspec.pl +++ /dev/null @@ -1,360 +0,0 @@ -require "$tool/built/include/ctutils.pl" ; - -# read a .vspec file into a map -# $_[0] = project -# on exit $ctvspecs{} will contain the data -# -# vspec format: -# tag:type:other data -# -# type: ref, root, vroot, croot -# other data: -# ref: name=_____ - required, take to refference -# root: path=_____ - required, path of tree root -# vroot: name=_____ - optional, name of view to use (if not tag) -# croot: path=_____ - required, local path of tree root -# server=_____ - required, CVS server string, ',' for ':' - -sub CTReadVSpec { - &CTUDebug( "reading vspec file for project " . $_[0] . "\n" ) ; - local( $ret ) = "" ; - local( $thisproj ) = $_[0] ; - if ( -e "$ctvspec_path/$thisproj.vspec" ) { - %ctvspecs = () ; - local( *SPECFILE ) ; - open( SPECFILE, "<$ctvspec_path/$thisproj.vspec" ) ; - local( @partlist ) ; - while ( $_ = ) { - s/\n$// ; - @partlist = split( /\#/ ) ; - $_ = $partlist[0] ; - if ( $_ ne "" ) { - @partlist = split( /:/ ); - local( $tag ) = $partlist[0] ; - shift( @partlist ) ; - local( $spec ) = join( ":", @partlist ) ; - if ( &CTValidateSpec( $spec ) ) { - $ctvspecs{$tag} = $spec ; - if ( $ctdebug ) { - print STDERR "tag(" . $tag . ") = " . $spec . "\n" ; - } - } - } - } - close( SPECFILE ) ; - $ctvspec_read = $_[0] ; - } else { - print STDERR "CTReadVSpec: cannot locate '$ctvspec_path/$thisproj.vspec'\n" ; - print STDERR "(did you forget to run the \$WINTOOLS/cp_vspec script?)\n" ; - } -} - -# given a spec line return it's type -# $_[0] = spec line - -sub CTSpecType { - local( @speclist ) = split( /:/, $_[0] ) ; - $speclist[0] ; -} - -# given a spec line return it's options if any -# $_[0] = spec line - -sub CTSpecOptions { - local( @speclist ) = split( /:/, $_[0] ) ; - shift( @speclist ) ; - join( ":", @speclist ) ; -} - -# given the options part of a spec line, find a given option -# $_[0] = options line -# $_[1] = desired option - -sub CTSpecFindOption { - local( $ret ) = "" ; - local( @options ) = split( /:/, $_[0] ) ; - local( $item ) ; - local( @itemlist ) ; - foreach $item ( @options ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq $_[1] ) { - $ret = $itemlist[1] ; - } - } - $ret ; -} - -# resolve a final spec line for a given flavor -# $_[0] = project -# $_[1] = flavor - -sub CTResolveSpec { - &CTUDebug( "in CTResolveSpec\n" ) ; - local( $proj ) = $_[0] ; - $proj =~ tr/A-Z/a-z/ ; - if ( $ctvspec_read ne $proj ) { - &CTReadVSpec( $proj ) ; - } - local( $spec ) = $ctvspecs{$_[1]} ; - local( $ret ) = "" ; - if ( $spec ne "" ) { - local( $type ) = &CTSpecType( $spec ) ; - local( @speclist ) = split( /:/, &CTSpecOptions( $spec ) ) ; - if ( $type eq "ref" ) { - local( @optionlist ) = split( /=/, $speclist[0] ) ; - if ( $optionlist[0] ne "name" ) { - print STDERR "bad data attached to flavor " . $_[1] . - " of project " . $proj . "\n" ; - } else { - local( $tmp ) = &CTUShellEval( $optionlist[1] ) ; - if ( $ctdebug ) { - print STDERR "resolved a 'ref' to " . $tmp . - ", recuring\n" ; - } - $ret = &CTResolveSpec( $proj, $tmp ) ; - } - } else { - $ret = $spec ; - } - } - if ( $ret eq "" ) { - print STDERR "unknown flavor " . $_[1] . " of project " . $proj . - "\n" ; - } - &CTUDebug( "out of CTResolveSpec\n" ) ; - $ret ; -} - -# resolve the final name for a given flavor -# $_[0] = project -# $_[1] = flavor - -sub CTResolveSpecName { - &CTUDebug( "in CTResolveSpecName\n" ) ; - local( $proj ) = $_[0] ; - $proj =~ tr/A-Z/a-z/ ; - if ( $ctvspec_read ne $proj ) { - &CTReadVSpec( $proj ) ; - } - local( $spec ) = $ctvspecs{$_[1]} ; - local( $ret ) = $_[1] ; - if ( $spec ne "" ) { - local( $type ) = &CTSpecType( $spec ) ; - local( @speclist ) = split( /:/, &CTSpecOptions( $spec ) ) ; - if ( $type eq "ref" ) { - local( @optionlist ) = split( /=/, $speclist[0] ) ; - if ( $optionlist[0] ne "name" ) { - print STDERR "bad data attached to flavor " . $_[1] . - " of project " . $proj . "\n" ; - } else { - local( $tmp ) = &CTUShellEval( $optionlist[1] ) ; - if ( $ctdebug ) { - print STDERR "resolved a 'ref' to " . $tmp . - ", recuring\n" ; - } - $ret = &CTResolveSpecName( $proj, $tmp ) ; - } - } - } - if ( $ret eq "" ) { - print STDERR "unknown flavor " . $_[1] . " of project " . $proj . - "\n" ; - } - &CTUDebug( "out of CTResolveSpecName\n" ) ; - $ret ; -} - -# validate a spec line -# $_[0] = spec line - -sub CTValidateSpec { - local( $ret ) = 0 ; - local( $type ) = &CTSpecType( $_[0] ) ; - local( @speclist ) = split( /:/, &CTSpecOptions( $_[0] ) ) ; - local( $have_error ) = 0 ; - local( $item ) ; - local( @itemlist ) ; - if ( $type eq "ref" ) { - local( $have_name ) = 0 ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "name" ) { - if ( $have_name ) { - $have_error = 1; - &CTUDebug( "multiple name options on 'ref'\n" ) ; - } - $have_name = 1; - } else { - &CTUDebug( "invalid option on 'ref' = " . $item . "\n" ) ; - $have_error = 1 ; - } - } - if ( ! $have_error ) { - if ( $have_name ) { - $ret = 1 ; - } - } - } elsif ( $type eq "root" ) { - local( $have_path ) = 0 ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "path" ) { - if ( $have_path ) { - $have_error = 1 ; - &CTUDebug( "multiple path options on 'root'\n" ) ; - } - $have_path = 1 ; - } else { - &CTUDebug( "invalid option on 'root' = " . $item . "\n" ) ; - $have_error = 1 ; - } - } - if ( ! $have_error ) { - if ( $have_path ) { - $ret = 1 ; - } - } - } elsif ( $type eq "vroot" ) { - local( $have_name ) = 0 ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "name" ) { - if ( $have_name ) { - $have_error = 1 ; - &CTUDebug( "multiple name options on 'vroot'\n" ) ; - } - $have_name = 1 ; - } else { - &CTUDebug( "invalid option on 'vroot' = " . $item . "\n" ) ; - $have_error = 1 ; - } - } - if ( ! $have_error ) { - $ret = 1 ; - } - } elsif ( $type eq "croot" ) { - local( $have_path ) = 0 ; - local( $have_server ) = 0 ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "path" ) { - if ( $have_path ) { - $have_error = 1 ; - &CTUDebug( "multiple path options on 'croot'\n" ) ; - } - $have_path = 1 ; - } elsif ( $itemlist[0] eq "server" ) { - if ( $have_server ) { - $have_error = 1 ; - &CTUDebug( "multiple server options on 'croot'\n" ) ; - } - $have_server = 1 ; - } else { - &CTUDebug( "invalid option on 'croot' = " . $item . "\n" ) ; - $have_error = 1 ; - } - } - if ( ! $have_error ) { - if ( $have_path && $have_server ) { - $ret = 1 ; - } - } - } else { - &CTUDebug( "unknow spec type '" . $speclist[0] . "'\n" ) ; - } - $ret ; -} - -# get a list of all projects - -sub CTListAllProjects { - &CTUDebug( "in CTListAllProjects\n" ) ; - local( $ret ) = "" ; - local( $done ) = 0 ; - local( *DIRFILES ) ; - open( DIRFILES, "(cd $ctvspec_path ; /bin/ls -1 *.vspec ; echo blahblah) |" ) ; - while ( ! $done ) { - $_ = ; - s/\n$// ; - if ( $_ eq "blahblah" ) { - $done = 1 ; - } else { - s/.vspec$// ; - if ( $_ ne "" ) { - if ( $ret eq "" ) { - $ret = $_ ; - } else { - $ret = $ret . " " . $_ ; - } - } - } - } - close( DIRFILES ) ; - &CTUDebug( "final list of projects '" . $ret . "'\n" . - "out of CTListAllProjects\n" ) ; - $ret ; -} - -# list all flavors of a project -# $_[0] = project - -sub CTListAllFlavors { - &CTUDebug( "in CTListAllFlavors\n" ) ; - local( $proj ) = $_[0] ; - $proj =~ tr/A-Z/a-z/ ; - if ( $ctvspec_read ne $proj ) { - &CTReadVSpec( $proj ) ; - } - local( $ret ) = ""; - local( $item ) ; - foreach $item ( keys %ctvspecs ) { - if ( $ret eq "" ) { - $ret = $item ; - } else { - $ret = $ret . " " . $item ; - } - } - &CTUDebug( "out of CTListAllFlavors\n" ) ; - $ret ; -} - -# given a project and a spec, determine the local root of the project -# $_[0] = project -# $_[1] = flavor -# $_[2] = spec line - -sub CTComputeRoot { - &CTUDebug( "in CTComputeRoot\n" ) ; - local( $proj ) = $_[0] ; - $proj =~ tr/A-Z/a-z/ ; - if ( $ctvspec_read ne $proj ) { - &CTReadVSpec( $proj ) ; - } - local( $ret ) = "" ; - local( $type ) = &CTSpecType( $_[2] ) ; - local( $options ) = &CTSpecOptions( $_[2] ) ; - local( $vname ) = &CTResolveSpecName( $proj, $_[1] ) ; - &CTUDebug( "type = '" . $type . "' with options '" . $options . "'\n" ) ; - if ( $type eq "root" ) { - $ret = &CTSpecFindOption( $options, "path" ) ; - } elsif ( $type eq "vroot" ) { - local( $name ) = &CTSpecFindOption( $options, "name" ) ; - if ( $name ne "" ) { - $ret = "/view/$name/vobs/$proj" ; - } else { - $ret = "/view/$vname/vobs/$proj" ; - } - } elsif ( $type eq "croot" ) { - $ret = &CTSpecFindOption( $options, "path" ) ; - } elsif ( $ctdebug) { - print STDERR "unknown flavor type '" . $type . "'\n" ; - } - &CTUDebug( "returning '" . $ret . "'\n" ) ; - &CTUDebug( "out of CTComputeRoot\n" ) ; - $ret ; -} - -%ctvspecs = () ; -$ctvspec_read = "" ; - -1; diff --git a/dtool/src/attach/dtool.alias b/dtool/src/attach/dtool.alias deleted file mode 100644 index eaebd79b6f..0000000000 --- a/dtool/src/attach/dtool.alias +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/csh -f - -setenv OS `uname` -setenv USER `whoami` - -if ( -e $DTOOL/bin/neartool ) setenv HAVE_NEARTOOL "yes" -if ( ! $?HAVE_NEARTOOL ) setenv HAVE_NEARTOOL "no" -if ( ! $?HAVE_ATRIA ) setenv HAVE_ATRIA "no" - -set host=$HOST -if ( ! $?TERM ) setenv TERM "none" -if ( $TERM == "iris-ansi" || $TERM == "iris-ansi-net" ) then - alias ctshowprojs 'echo -n "\033P1.y"$USER"@"$host" -- "$CTPROJS"\033\\"; echo -n "\033P3.y"`echo $CTPROJS | cut -f1 -d:`\($host\)"\033\\"' -else if ( $TERM == "xterm" || $TERM == "color-xterm" || $TERM == "cygwin" ) then - alias ctshowprojs 'echo -n "\033]2;"$USER"@"$host" -- "$CTPROJS"\007"; echo -n "\033]1;"`echo $CTPROJS | cut -f1 -d:`\($host\)"\007"' -else - alias ctshowprojs 'echo $CTPROJS' -endif - -alias ctattach 'source `ctattach.drv \!*`; ctshowprojs' -alias cta 'ctattach' -alias cta-ship 'setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV ship ; ctattach \!* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' -alias cta-release 'setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV release ; ctattach \!* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' -alias cta-install 'setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV install ; ctattach \!* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' -alias ctunattach 'source `ctunattach.drv \!*`; ctshowprojs' -alias ctuna 'ctunattach' - -#Modifications to emacs alias by Jason -#To allow for NTEmacs to run like emacs on unix boxes -if (($OS == "CYGWIN_NT-4.0") || ($OS == "CYGWIN_NT-5.0" ) || ($OS == "CYGWIN_NT-5.1" )) then - alias emacs 'emacs -T "$USER@$HOST $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`($HOST)" -bg #002040 -fg #00C0FF -cr yellow -ms yellow -l `cygpath -w ~/.emacs` $CTEMACS_OPTS' -else - alias emacs 'emacs -T "$USER@$HOST $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`($HOST)" $CTEMACS_OPTS' -endif - -alias rlogin 'rlogin \!*; ctshowprojs' -alias telnet 'telnet \!*; ctshowprojs' diff --git a/dtool/src/attach/dtool.alias-sh b/dtool/src/attach/dtool.alias-sh deleted file mode 100644 index 54dddf782b..0000000000 --- a/dtool/src/attach/dtool.alias-sh +++ /dev/null @@ -1,46 +0,0 @@ -#! /bin/sh - -if [ -e $DTOOL/bin/neartool ]; then HAVE_NEARTOOL="yes"; fi -if [ -z "$HAVE_NEARTOOL" ]; then HAVE_NEARTOOL="no"; fi -export HAVE_NEARTOOL - -if [ -z "$HAVE_ATRIA" ]; then HAVE_ATRIA="no"; fi -export HAVE_ATRIA - -if [ $HAVE_NEARTOOL = "yes" ]; then - alias ct='neartool' -else - alias ct='cleartool' -fi - -alias ctshowprojs='echo -n "\033P1.y"$USER"@"$HOST" -- "$CTPROJS"\033\\"; echo -n "\033P3.y"`echo $CTPROJS | cut -f1 -d:`\(`uname -n`\)"\033\\"' -alias ctattach='source `ctattach.drv $*`; ctshowprojs' -alias cta='ctattach' -alias cta-ship='setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV ship ; ctattach $* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' -alias cta-release='setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV release ; ctattach $* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' -alias cta-install='setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV install ; ctattach $* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' -alias ctunattach='source `ctunattach.drv $*`; ctshowprojs' -alias ctuna='ctunattach' - -if [ "$PENV" = "WIN32_DREAMCAST" ]; then - alias ctci='neartool ci' - alias ctco='neartool co' - alias ctmake='gmake' - alias emacs='/emacs/bin/runemacs' - alias emacs='/emacs/bin/runemacs -T "`logname`@`uname -n` $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`(`uname -n`)" $CTEMACS_OPTS' - -elif [ "$HAVE_NEARTOOL" = "yes" ]; then - alias ctci='neartool ci' - alias ctco='neartool co' - alias ctmake='make' - alias emacs='emacs -T "`logname`@`uname -n` $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`(`uname -n`)" $CTEMACS_OPTS' - -elif [ "$HAVE_ATRIA" = "yes" ]; then - alias ctci='cleartool ci' - alias ctco='cleartool co' - alias ctmake 'clearmake -C gnu $* |& grep -v "^clearmake: Warning: Config"' - alias emacs='emacs -T "`logname`@`uname -n` $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`(`uname -n`)" $CTEMACS_OPTS' -fi - -alias rlogin='rlogin $*; ctshowprojs' -alias telnet='telnet $*; ctshowprojs' diff --git a/dtool/src/attach/dtool.cshrc b/dtool/src/attach/dtool.cshrc deleted file mode 100644 index 948f190625..0000000000 --- a/dtool/src/attach/dtool.cshrc +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/csh -f - -setenv OS `uname` - -# careful, security exploit here -setenv LD_LIBRARY_PATH "." -setenv DYLD_LIBRARY_PATH "." - -setenv CTEMACS_FOREHIGHLIGHT white -setenv CTEMACS_BACKHIGHLIGHT blue - -# Setup the initial path -if ( $OS == "Linux" ) then - set path = ( /bin /bin /usr/bin /sbin /usr/sbin /usr/bin/X11 \ - /usr/etc /usr/local/bin /var/local/bin ~/bin ) -else if ( $OS == "Darwin" ) then - set path = ( /bin /usr/bin /sbin /usr/sbin /usr/local/bin ~/bin $path ) -else if ( $OS == "IRIX64" ) then - set path = ( /var/local/bin ~/bin /usr/local/prman/bin \ - /usr/sbin /usr/bsd /sbin /usr/bin /bin /usr/bin/X11 /usr/etc \ - /usr/demos/bin /usr/local/bin ) - setenv LD_LIBRARY_PATH "/usr/local/lib:." -else if (($OS == "CYGWIN_NT-6.1-WOW64") || ($OS == "CYGWIN_NT-6.1") || ($OS == "CYGWIN_NT-5.1") || ($OS == "CYGWIN_NT-6.0") || ($OS == "CYGWIN_NT-6.0-WOW64") || ($OS == "CYGWIN_NT-5.2-WOW64") || ($OS == "CYGWIN_NT-5.0") || ( $OS == "CYGWIN_NT-4.0" ) || ( $OS == "WINNT" )) then - set path = ( /bin /usr/bin /usr/lib /usr/local/bin $path . ) - if ( $?LIB ) then - setenv LIB "$LIB;"`cygpath -w /usr/lib` - else - setenv LIB `cygpath -w /usr/lib` - endif -else if (( $OS == "CYGWIN_98-4.10" ) || ( $OS == "WIN95" )) then - set path = ( /bin /usr/local/bin /contrib/bin /msvc98/Bin \ - /mscommon/MSDev98/Bin /mscommon/Tools /usr/lib $path ) - setenv LIB `cygpath -w /msvc98/mfc/lib`\;`cygpath -w /msvc98/lib`\;`cygpath -w /usr/lib` - setenv INCLUDE `cygpath -w /msvc98/Include` -else - set path = ( /var/local/bin ~/bin /usr/local/prman/bin \ - /usr/sbin /usr/bsd /sbin /usr/bin /bin /usr/bin/X11 /usr/etc \ - /usr/demos/bin /usr/local/bin ) -endif - -# Setup the initial manpath -#if ( $OS == "Linux" ) then -#setenv MANPATH "/usr/local/man:/usr/man/preformat:/usr/man:/usr/X11R6/man" -#else if ( $OS == "IRIX64" ) then -#setenv MANPATH "/usr/share/catman:/usr/catman:/usr/local/share/catman:/usr/local/share/man:/usr/local/man" -#else if (( $OS == "CYGWIN_NT-5.1") || ( $OS == "CYGWIN_NT-5.2-WOW64" ) || ( $OS == "CYGWIN_NT-5.0") || ( $OS == "CYGWIN_NT-4.0" ) || ( $OS == "CYGWIN_98-4.10" ) || ( $OS == "WIN95" )) then -#setenv MANPATH "/usr/man:/contrib/man" -#else -#setenv MANPATH "/usr/share/catman:/usr/catman:/usr/local/share/catman:/usr/local/share/man:/usr/local/man" -#endif - -setenv CT_INCLUDE_PATH "." -#set cdpath = ( . ) -#setenv CDPATH "." -setenv DC_PATH "." -setenv SSPATH "." -setenv STKPATH "." - -if ( ! $?HAVE_ATRIA ) then - if ( -e /usr/atria ) then - /usr/atria/bin/cleartool mount -all >& /dev/null - if ( $status == 0 ) setenv HAVE_ATRIA "yes" - endif -endif - -if ( ! $?CTDEFAULT_FLAV ) setenv CTDEFAULT_FLAV "default" -if ( ! $?CTEMACS_OPTS ) setenv CTEMACS_OPTS "" - -if ( -e /usr/atria/bin ) set path = ( /usr/atria/bin $path ) -rehash - -if ( ! $?PENV ) then - if ( $OS == "Linux" ) then - setenv PENV "Linux" - else if ( $OS == "IRIX64" ) then - setenv PENV "SGI" - else if (( $OS == "CYGWIN_NT-5.1") || ( $OS == "CYGWIN_NT-5.0") || ( $OS == "CYGWIN_NT-6.0")|| ( $OS == "CYGWIN_NT-4.0" ) || ( $OS == "CYGWIN_98-4.10" ) || ( $OS == "WIN95" )) then - setenv PENV "WIN32" - else - setenv PENV "SGI" - endif -endif - -if ( ! $?DTOOL ) setenv DTOOL /beta/player/bootstrap/dtool -if ( $#argv == 0 ) then - setenv SETUP_SCRIPT `$DTOOL/built/bin/ctattach.drv dtool default` -else - setenv SETUP_SCRIPT `$DTOOL/built/bin/ctattach.drv dtool $argv[1]` -endif - -if($SETUP_SCRIPT == "") then - echo "error: ctattach.drv returned NULL string for setup_script filename!" - echo " 'dtool/built/bin/ctattach.drv' probably doesnt exist, need to make install on dtool to copy it from dtool\src\attach" - exit -endif - -source $SETUP_SCRIPT - - diff --git a/dtool/src/attach/dtool.emacs b/dtool/src/attach/dtool.emacs deleted file mode 100644 index dae0bde1cc..0000000000 --- a/dtool/src/attach/dtool.emacs +++ /dev/null @@ -1,295 +0,0 @@ -;; make the mouse pointer avoid the text point -;; Actually, everyone really hates this. -;(cond (window-system -; (require 'avoid) -; (mouse-avoidance-mode 'cat-and-mouse))) - -;; Make sure utf-8 is at the top of the coding-system list; Panda's -;; TextNode uses utf-8 encoding natively, so we may have some -;; documents and code written in utf-8. - -(prefer-coding-system 'utf-8) - - -;; make sure we have the compile library available to us -(load-library "compile") -;; Comment given for last checkout command -(setq last-co-comment "") -;; Comment given for last checkin command -(setq last-ci-comment "") -;; Target given for the last local make -(setq last-lm-target "realinstall") -;; Target given for the last global make -(setq last-gm-target "install") -;; Host given for the last ctrelease -(setq last-rel-host "") -;; Host given for the last ctship -(setq last-ship-host "") - -;; check the environment -(setq ct-tool (getenv "DTOOL")) -(setq have-atria (let ((h-a (getenv "HAVE_ATRIA"))) - (if (string= h-a "yes") t '()))) -;; (setq have-neartool (let ((h-n (getenv "HAVE_NEARTOOL"))) -;; (if (string= h-n "yes") t '()))) -(setq is-cygwin (or (string= (getenv "OS") "CYGWIN_NT-4.0") - (string= (getenv "OS") "CYGWIN_NT-5.0") - (string= (getenv "OS") "CYGWIN_NT-5.1"))) - -;; (setq ct-command (cond -;; (is-cygwin "bash /install/tool/bin/neartool") -;; (have-atria "cleartool") -;; (have-neartool "neartool") -;; t nil)) - -;; Load the Hightlight coloring scheme -(if is-cygwin -;; (let ((filename (concat (getenv "CYGWIN_ROOT") "install\\tool\\etc\\color.emacs"))) - (let ((filename (concat (getenv "CYGWIN_ROOT") ct-tool "\\etc\\color.emacs"))) - (if (file-readable-p filename) (load filename)))) - -;; Checkout element in the current buffer -(defun ct-checkout-curr (comment) - "Checkout version in current buffer with COMMENT." - (interactive (list (read-string "Comment: " last-co-comment))) - (setq last-co-comment comment) - (setq pname (file-name-nondirectory (buffer-file-name))) - (ct-shell-command-verbose - (concat "ctco -c " (ct-quote-string comment) " " pname)) - (ct-find-curr-file-again nil) -) - -;; Uncheckout element in the current buffer -(defun ct-uncheckout-curr () - "Uncheckout version in current buffer and remove private data." - (interactive) - (if (y-or-n-p "Ok to un-checkout? ") - (progn - (setq pname (file-name-nondirectory (buffer-file-name))) - (ct-shell-command-verbose (concat "ctunco " pname)) - (ct-find-curr-file-again t) - ) - (progn - (message "Uncheckout canceled.") - ) - ) -) - -;; Checkin element in the current buffer -(defun ct-checkin-curr () - "Checkin version in current buffer." - (interactive) - (setq pname (file-name-nondirectory (buffer-file-name))) - (setq option nil) - (while (not option) - (setq choice (read-string "Comment: s (same), n (new), l (list): " "s")) - (cond - ((equal choice "s") - (setq option "-nc")) - ((equal choice "n") - (setq comment (read-string "Comment: " last-ci-comment)) - (setq last-ci-comment comment) - (setq option (concat "-c " (ct-quote-string comment)))) - ((equal choice "l") - (ct-shell-command-verbose (concat "ctihave " pname))) - (t - (message (concat "Unrecognized choice: " choice ".")) - (sleep-for 2)))) - - (ct-shell-command-verbose (concat "ctci " option " " pname)) - (ct-find-curr-file-again t) -) - -;; Delta element in the current buffer -(defun ct-delta-curr () - "Delta element in current buffer." - (interactive) - (if (y-or-n-p "Ok to delta? ") - (progn - (setq pname (file-name-nondirectory (buffer-file-name))) - (ct-shell-command-verbose (concat "ctdelta " pname)) - (ct-find-curr-file-again t) - ) - (progn - (message "Delta canceled.") - ) - ) -) - -;; List element checkout data for the current buffer -(defun ct-lscheckout-curr () - "List checkout for the current buffer." - (interactive) - (setq pname (file-name-nondirectory (buffer-file-name))) - (ct-shell-command-verbose (concat "ctihave &")) -) - -;; List elements in the current directory that are checked out -(defun ct-lscheckout-curr-dir () - "List checkouts for the current directory." - (ct-shell-command-verbose (concat "ctihave &")) -) - -;; call clearmake in the local directory -(defun ct-local-make () - "Build TARGET from the current directory." - (interactive) - (setq target (read-string "Local build target: " last-lm-target)) - (setq last-lm-target target) - (if have-atria - (compile-internal - (concat "clearmake -C gnu " target - " |& grep -v \"clearmake: Warning: Config\"") - "No more errors.") - (compile-internal - (concat "make " target) "No more errors.")) -) - -;; call clearmake in the project root directory -(defun ct-global-make () - "Build TARGET from the project root." - (interactive) - (setq target (read-string "Global build target: " last-gm-target)) - (setq last-gm-target target) - (cond - (have-atria - (compile-internal - (concat "cd `ctproj -r` ; clearmake -C gnu " target - " |& grep -v \"clearmake: Warning: Config\"") - "No more errors.")) - (is-cygwin - (compile-internal - (concat "bash -f 'cd `ctproj -r` ; make " target "'") "No more errors.")) - (t - (compile-internal - (concat "cd `ctproj -r` ; make " target) "No more errors.")) - ) - ) - -;; Do an xdiff on the current buffer to see what is different about this -;; file from the previous version. -(defun ct-xdiff-curr () - "Show changes to element in current buffer from the previous version." - (interactive) - (setq pname (file-name-nondirectory (buffer-file-name))) - (if is-cygwin - (ct-shell-command-verbose (concat ct-command " xdiff -pre " pname)) - - ; The is a hack to deal with the fact that diff returns 1 if the - ; two files do not match. - (ct-shell-command-verbose (concat ct-command " xdiff -pre " pname ";:&"))) - ) - - -;; Make a new element for the current buffer -(defun ct-mk-elem () - (interactive) - (if (y-or-n-p (format "Make new element for %s? " - (file-name-nondirectory (buffer-name)))) - (progn - (write-file (buffer-file-name)) - (ct-shell-command-verbose - (concat "ctmkelem -eltype text_file -c '' " - (file-name-nondirectory (buffer-name)))) - ) - (progn - (message "Make element canceled.") - ) - ) - ) - -;; utility functions -(defun ct-shell-command-verbose (command) - "Execute COMMAND in shell with message." - (interactive "Shell command: \n") - (message (concat "Executing: " command " ...")) - (shell-command command) - (message "Done.") -) - -(defun ct-find-curr-file-again (read-only) - "Read in the currect file again, READONLY (t) or not (nil)." - (setq pname (buffer-file-name)) - (setq linenum (1+ (count-lines 1 (point)))) - (kill-buffer (buffer-name)) - (if read-only - (find-file-read-only pname) - (find-file pname)) - (goto-line linenum) -) - -(defun ct-quote-string (string) - "Enclose STRING in single or double quotes." - (setq has-double (string-match "\"" string)) - (setq has-single (string-match "'" string)) - (cond - ((or (and (not has-single) (not has-double)) - (and has-double (not has-single))) - (concat "'" string "'")) - ((and has-single (not has-double)) - (concat "\"" string "\"")) - (t - (message (concat "Can't quote string correctly: " string)) - (sleep-for 3) - (concat "\"" string "\""))) -) - -;; default key bindings -(global-set-key "\C-xco" 'ct-checkout-curr) -(global-set-key "\C-xcu" 'ct-uncheckout-curr) -(global-set-key "\C-xci" 'ct-checkin-curr) -(global-set-key "\C-xcd" 'ct-delta-curr) -(global-set-key "\C-xcl" 'ct-lscheckout-curr) -(global-set-key "\C-xcL" 'ct-lscheckout-curr-dir) -(global-set-key "\C-xcm" 'ct-local-make) -(global-set-key "\C-xcM" 'ct-global-make) -(global-set-key "\C-xcx" 'ct-xdiff-curr) -(global-set-key "\C-xce" 'ct-mk-elem) - -;; ok, lets make sure we load all other .emacs files we might need. This is -;; attach related code. - -(defun ct-load-project-emacs-file (proj-name) - (if (string= proj-name "DTOOL") nil - (let ((pre-name (getenv proj-name))) - (if pre-name - (let ((filename (concat pre-name "/built/etc/" - (downcase proj-name) ".emacs"))) - (if (file-readable-p filename) - (load filename)) - ))) - ) - ) - -(defun ct-break-space-colon-str (string) - (if (string= string "") - '() - (let ((substr-end (string-match ":" string 0))) - (cons (substring string 0 substr-end) - (let ((new-string-start (string-match " " string 0))) - (if (eq nil new-string-start) - '() - (ct-break-space-colon-str - (strip-spaces (substring string (match-end 0))))) - ))) - ) - ) - -(defun strip-spaces (string) - (if (= (string-to-char string) 32) - (if (= (length string) 1) "" - (strip-spaces (substring string 1))) - string) - ) - -(defun ct-load-project-emacs-files () - "Load project specific .emacs files" - (let ((ctprojs (getenv "CTPROJS"))) - (if ctprojs - (mapcar 'ct-load-project-emacs-file - (reverse (ct-break-space-colon-str ctprojs))) - )) - ) - -;; get all of the project specific .emacs files -(ct-load-project-emacs-files) diff --git a/dtool/src/attach/dtool.init b/dtool/src/attach/dtool.init deleted file mode 100644 index 89f62e7047..0000000000 --- a/dtool/src/attach/dtool.init +++ /dev/null @@ -1,10 +0,0 @@ -MODREL ETC_PATH built/etc -DOCSH source $DTOOL/built/etc/dtool.alias -DOCSH unsetenv LASTLOGIN -DOCSH setenv OS_VER `uname -r` -DOCSH ctshowprojs -DOSH source $DTOOL/built/etc/dtool.alias-sh -DOSH LASTLOGIN= -DOSH export LASTLOGIN -DOSH OS_VER=`uname -r` -DOSH export OS_VER diff --git a/dtool/src/attach/dtool.sh b/dtool/src/attach/dtool.sh deleted file mode 100755 index 598cd37cc0..0000000000 --- a/dtool/src/attach/dtool.sh +++ /dev/null @@ -1,81 +0,0 @@ -#! /bin/sh - -OS=`uname` -export OS - -# Setup the initial path -if [ $OS = "Linux" ]; then - PATH=/var/local/bin:~/bin:.:/usr/sbin:/sbin:/usr/bin:/bin:/usr/bin/X11:/usr/etc:/usr/local/bin -elif [ $OS = "IRIX64" ]; then - PATH=/var/local/bin:/usr/local/bin/ptools:~/bin:/usr/local/prman/bin:.:/usr/sbin:/usr/bsd:/sbin:/usr/bin:/bin:/usr/bin/X11:/usr/etc:/usr/demos/bin:/usr/local/bin -elif [ $OS = "CYGWIN_98-4.10" ]; then - PATH=/usr/local/bin:/bin:/CYGNUS/CYGWIN~1/H-I586~1/BIN:/WINDOWS:/WINDOWS:/WINDOWS/COMMAND:/DMI/BIN:/KATANA/UTL/DEV/MAKE:/KATANA/UTL/DEV/HITACHI -else - PATH=/var/local/bin:/usr/local/bin/ptools:~/bin:/usr/local/prman/bin:.:/usr/sbin:/usr/bsd:/sbin:/usr/bin:/bin:/usr/bin/X11:/usr/etc:/usr/demos/bin:/usr/local/bin -fi - -# Setup the initial manpath -#if [ $OS = "Linux" ]; then -# MANPATH=/usr/local/man:/usr/man/preformat:/usr/man:/usr/X11R6/man -#elif [ $OS = "IRIX64" ]; then -# MANPATH=/usr/share/catman:/usr/catman:/usr/local/share/catman:/usr/local/share/man:/usr/local/man -#elif [ $OS = "CYGWIN_98-4.10" ]; then -# MANPATH=/usr/local/man -#else -# MANPATH=/usr/share/catman:/usr/catman:/usr/local/share/catman:/usr/local/share/man:/usr/local/man -#fi -#export MANPATH - -LD_LIBRARY_PATH="." -export LD_LIBRARY_PATH -DYLD_LIBRARY_PATH="." -export DYLD_LIBRARY_PATH -CT_INCLUDE_PATH="." -export CT_INCLUDE_PATH -#cdpath=. -#CDPATH="." -#export CDPATH -DC_PATH="." -export DC_PATH -SSPATH="." -export SSPATH -STKPATH="." -export STKPATH -SHELL_TYPE="sh" -export SHELL_TYPE - -if [ -e /usr/atria ]; then - if /usr/atria/bin/cleartool mount -all > /dev/null 2>&1; then - HAVE_ATRIA=yes - export HAVE_ATRIA - fi -fi - -if [ -z "$CTDEFAULT_FLAV" ]; then - CTDEFAULT_FLAV="default" - export CTDEFAULT_FLAV -fi - -if [ -z "$DTOOL" ]; then - DTOOL=/beta/player/bootstrap/tool - export DTOOL -fi - -if [ -z "$PENV" ]; then - if [ $OS = "Linux" ]; then - PENV="Linux" - elif [ $OS = "IRIX64" ]; then - PENV="SGI" - elif [ $OS = "CYGWIN_98-4.10" ]; then - PENV="WIN32_DREAMCAST" - else - PENV="SGI" - fi -fi -export PENV - -if [ -z "$1" ]; then - source `$DTOOL/built/bin/ctattach.drv dtool default` -else - source `$DTOOL/built/bin/ctattach.drv dtool $1` -fi diff --git a/dtool/src/attach/get-cttree b/dtool/src/attach/get-cttree deleted file mode 100755 index d0e68e4f3f..0000000000 --- a/dtool/src/attach/get-cttree +++ /dev/null @@ -1,104 +0,0 @@ -#! /bin/sh -# -# get-cttree.sh -# -# Usage: -# -# get-cttree.sh [opts] output-file.tgz -# -# This script must be executed from within a project tree. -# -# Options: -# -# None at present. -# -#ENDCOMMENT - -while getopts "h" flag; do - case $flag in - h) sed '/#ENDCOMMENT/,$d' <$0 >&2 - exit 1;; - \?) exit 1; - esac -done - -shift `expr $OPTIND - 1` -output=$1 -projroot=`ctproj -r` - -if [ -z "$projroot" ]; then - echo "" - echo "You must execute this script in a project tree." - echo "" - exit 1 -fi - -if [ -z "$output" ]; then - sed '/#ENDCOMMENT/,$d' <$0 >&2 - exit 1 -fi - - -# Perform some sanity checks on input parameters. - -if [ ! -d "$projroot" ]; then - echo "" - echo "$projroot is not a directory!" - echo "" - exit 1 -fi - -if [ `basename $output .tgz` = `basename $output` ]; then - echo "" - echo "$output should end in .tgz" - echo "" - exit 1 -fi - -if [ ! -d /usr/atria ]; then - echo "" - echo "This script is intended to be run on an actual ClearCase vobs." - echo "" - exit 1 -fi - -projname=`basename $projroot` -projtop=`dirname $projroot` - -if [ "$projname" = "tool" ]; then - echo "" - echo "This script should not be used on the tool tree." - echo "" - exit 1 -fi - -if [ -f "$output" ]; then - if rm -i $output; then - echo "" - else - echo "Not overwriting $output" - exit 1 - fi -elif [ -r "$output" -o -w "$output" ]; then - echo "Cannot overwrite $output" - exit 1 -else - echo "" -fi - -# Check to make sure the local machine doesn't have anything checked out. -cd $projroot -outfile=/tmp/gc.$username.$projname.out -cleartool lsco -s -me -recurse >$outfile -if [ -s $outfile ]; then - echo "" - echo "Cannot build tarball; files still checked out in vobs:" - sed 's/^/ /;s/\.ct0\.//' $outfile - rm -f $outfile - echo "" - exit 1 -fi -rm -f $outfile - -(cd $projtop; cleartool find $projname -nxn -print | grep -v '/lost+found' | cpio -H tar -v -o | gzip) >$output - diff --git a/dtool/src/attach/get-delta b/dtool/src/attach/get-delta deleted file mode 100755 index 5ddb09a6a1..0000000000 --- a/dtool/src/attach/get-delta +++ /dev/null @@ -1,481 +0,0 @@ -#! /usr/local/bin/bash -# -# get-delta.sh -# -# Usage: -# -# get-delta.sh [opts] output-file.sh [file ...] -# -# This script must be executed from within a project tree. It -# examines the set of files that have been checked out (via neartool) -# and modified, and generates a script file that can be used to apply -# the changes made back to the main ClearCase vobs. -# -# By default, it generates a script for all checked-out files. You -# can restrict its operation to certain files and/or directories by -# listing them on the command line. -# -# Options: -# -# -c collapse versioning information on local copy after completion. -# Use this option with caution, as it is irreversible (and -# noninterruptible). Once the versioning information has been -# collapsed, it will be impossible to regenerate a script -# representing the changes that have been made locally; you -# should only do this when you are sure that your changes have -# been successfully applied to the other end. -# -# On the other hand, if you forget to run get-delta with -c after -# you have successfully applied your changes, you may -# inadvertently attempt to apply them again if you subsequently -# try to apply more changes. -# -#ENDCOMMENT - -function usage { - sed '/#ENDCOMMENT/,$d' <$0 >&2 - exit 1 -} - -# -# list_comments ( dirname basename ) -# -# Writes to stdout any comments associated with checked-out versions of -# the indicated file, in order. -# -function list_comments { - local dirname=$1 - local basename=$2 - local filename=$dirname/$basename - local file comment version - - if [ -f $dirname/.ct0.$basename ]; then - # Now look for comments, in version-number order. - - # We use a series of ls commands so we don't try to sort the - # filenames between the one-, two-, and three-digit version - # numbers. - - for file in `(cd $dirname; ls .ct[0-9].$basename; ls .ct[0-9][0-9].$basename; ls .ct[0-9][0-9][0-9].$basename) 2>/dev/null`; do - version=`echo $file | sed "s/^\.ct\([0-9]*\).*$/\1/"` - comment=$dirname/.ct${version}comment.$basename - if [ -f $comment ]; then - cat $comment - fi - done - fi -} - -# -# get_fullpath ( local_dir ) -# -# Sets $fullpath to the fully-qualified pathname associated with $local_dir. -# -function get_fullpath { - local local_dir=$1 - - if [ -z "$local_dir" ]; then - fullpath=`pwd` - else - if [ ! -d "$local_dir" ]; then - echo "Invalid directory: $local_dir" 1>&2 - exit 1 - fi - # If we use pwd instead of /bin/pwd, $PWD will be used, which will give - # the wrong answer - fullpath=`(cd $local_dir; /bin/pwd)` - fi -} - - -# -# get_rel_dir ( root_dir local_dir ) -# -# Sets $rel_dir to the string which represents $local_dir relative to -# $root_dir. This is a simple string-prefix operation, and could fail -# in some obscure cases. -# -function get_rel_dir { - get_fullpath $1 - local root_dir=$fullpath - - get_fullpath $2 - local local_dir=$fullpath - - # Now remove the initial prefix. - if [ "$root_dir" = "$local_dir" ]; then - rel_dir="." - else - rel_dir=`echo $local_dir | sed 's:^'$root_dir/'::'` - - if [ "$rel_dir" = "$local_dir" ]; then - echo "$local_dir is not a directory within $root_dir." 1>&2 - exit 1 - fi - fi -} - - -collapse= -while getopts "ch" flag; do - case $flag in - c) collapse=y;; - h) usage;; - \?) exit 1; - esac -done - -shift `expr $OPTIND - 1` -output=$1 -shift -projroot=`ctproj -r` - -if [ -z "$projroot" ]; then - echo "You must execute this script in a project tree." - exit 1 -fi - -if [ -z "$output" ]; then - usage -fi - - -# Perform some sanity checks on input parameters. - -if [ ! -d "$projroot" ]; then - echo "$projroot is not a directory!" - exit 1 -fi - -if [ `basename $output .sh` = `basename $output` ]; then - echo "$output should end in .sh" - exit 1 -fi - -if [ -f "$output" ]; then - rm -i $output - if [ -f "$output" ]; then - echo "Not overwriting $output" - exit 1 - fi -elif [ -e "$output" ]; then - echo "Cannot overwrite $output" - exit 1 -fi - -echo "" - -# Temporary files we'll build up as we process the files. - -base=`basename $output` -temp_ct0=/tmp/gd.ct0.$base -temp_checkout=/tmp/gd.checkout.$base -temp_dirs=/tmp/gd.dirs.$base -temp_files=/tmp/gd.files.$base -temp_diffs=/tmp/gd.diffs.$base - -rm -f $temp_ct0 $temp_checkout $temp_dirs $temp_files $temp_diffs -touch $temp_ct0 $temp_dirs $temp_files - - -# Get the list of files we'll want to delta in. - -if [ $# -eq 0 ]; then - # No explicit files, get all of them. - (cd $projroot; find . -name .ct0.\* -print) >>$temp_ct0 -else - # An explicit list of files. - for filename in $*; do - if [ -f $filename ]; then - dirname=`dirname $filename` - basename=`basename $filename` - - if [ -f $dirname/.ct0.$basename ]; then - get_rel_dir $projroot $dirname - echo ./$rel_dir/.ct0.$basename >>$temp_ct0 - else - echo $filename has no versions. - fi - - elif [ -d $filename ]; then - get_rel_dir $projroot $filename - - (cd $projroot; find ./$rel_dir -name .ct0.\* -print) >>$temp_ct0 - - else - echo $filename not found. - fi - done -fi - - -# Now start to build up the script. - -echo "#! /bin/sh" >$output -chmod 755 $output - -if [ ! -w $output ]; then - echo "Cannot write to $output!" - exit 1 -fi - -projname=`basename $projroot` - -# This part we cat in quoted, verbatim. -cat << 'EOF' >>$output - -any_opts= -list= -checkout= -patch= -cleanup= -checkin= -delta= -help= -while getopts "lopcidfh" flag; do - any_opts=y - only_list=y - case $flag in - l) list=y;; - o) checkout=y; only_list=;; - p) patch=y; only_list=;; - c) cleanup=y; only_list=;; - i) checkin=y; only_list=;; - d) delta=y; only_list=;; - f) checkout=y - patch=y - cleanup=y - checkin=y - delta=y - only_list=;; - h) help=y;; - \?) exit 1; - esac -done - -EOF - -# This part we cat in unquoted, so we can substitute the projname -# variable. - -cat << EOFOUTER >>$output -if [ \$help ]; then -cat << 'EOF' - -This patch file was generated using get-delta on a remote $projname tree. -It's designed to be run one time to apply the changes made remotely -back to the main branch of the tree. - -It should be run from within your own view, somewhere within the -$projname hierarchy on the ClearCase system. - -Options: - - -l List information about the patch file, including the creation - date and the list of modified files. - - -o Checkout all the relevant files and perform other ClearCase - operations (like renaming, creating, and removing files). - - -p Apply the relevant patches to all files after they have been - checked out. If this operation fails, the rest of the script - will not continue. - - -c Cleanup after successfully patching by removing .orig and .rej - files. - - -i Checkin modified files after successfully patching. - - -d Perform final merge by executing ctdelta on modified files. - - -f Perform full checkout/patch/merge cycle. This is equivalent to - specifying -opcid. - - -h This help page. - -If no options are specified, the default is -opc. - -EOF -exit 0 -fi - -if [ -z "\$any_opts" ]; then - checkout=y - patch=y - cleanup=y -fi -EOFOUTER - -any_merged= - -# We start with the commands given in the project's .ctcmds file. -ctcmds=$projroot/.ctcmds -if [ -f $ctcmds ]; then - any_merged=y - cat $ctcmds >>$temp_checkout - if [ $collapse ]; then - rm $ctcmds - fi -fi - - -for ct0 in `cat $temp_ct0`; do - dir=`dirname $ct0` - base=`basename $ct0 | sed 's/^\.ct0\.//'` - file=$dir/$base - ctnew=$projroot/$dir/.ctnew.$base - - if (cd $projroot; diff -u $ct0 $file >>$temp_diffs); then - # If diff returned success, it means the files were identical. In - # this case, don't bother checking it out. - echo "$file is unchanged." - - else - # Otherwise, the files were genuinely different. Check it out on - # the remote end. - echo $file - - if [ -f $ctnew ]; then - # This file is newly created. We need to create an element for - # it on the remote end. - eltype=`cat $ctnew` - if grep -x $dir $temp_dirs >/dev/null; then - echo directory already checked out >/dev/null - else - echo "ctco -nc $dir" >>$temp_checkout - echo $dir >> $temp_dirs - fi - echo "touch $file" >>$temp_checkout - echo "ctmkelem -eltype $eltype $file << 'EOF' 2>/dev/null" >>$temp_checkout - list_comments $projroot/$dir $base >>$temp_checkout - echo EOF >>$temp_checkout - - echo $file >> $temp_files - else - echo "ctco $file << 'EOF' 2>/dev/null" >>$temp_checkout - list_comments $projroot/$dir $base >>$temp_checkout - echo EOF >>$temp_checkout - - echo $file >> $temp_files - fi - fi - - if [ $collapse ]; then - echo Collapsing $file - neartool collapse $projroot/$file - fi -done -echo "" - -# Handle -l, list files modified -echo "" >>$output -echo 'if [ $list ]; then' >>$output -echo ' echo ""' >>$output -echo ' echo Patch file for '$projname' tree, built on '`date` >> $output -echo ' echo File was built by '`whoami` on `hostname` >>$output -echo ' echo ""' >>$output -echo ' echo Affected files are:' >>$output -echo ' cat <>$output -sed "s/^\./ $projname/" $temp_files >>$output -echo 'EOF' >>$output -echo ' echo ""' >>$output -echo 'fi' >>$output - -# Everything else depends on being in the proper tree. - -cat <>$output -if [ \$only_list ]; then - exit 0 -fi - -projroot=\`ctproj -r\` -if [ -z "\$projroot" ]; then - echo "" - echo "You must execute this script within the $projname tree." - echo "" - exit 1 -fi -if [ \`basename \$projroot\` != "$projname" ]; then - echo "" - echo "This script is intended for the $projname tree." - echo "" - exit 1 -fi -if [ ! -d /usr/atria ]; then - echo "" - echo "This script is intended to be run on an actual ClearCase vobs." - echo "" - exit 1 -fi -tmpfile=\`whoami\`-merge-$projname.tmp -cd \$projroot -touch \$tmpfile -EOF - - -# Handle -o, checkout stuff (and perform general ClearCase changes) -echo "" >>$output -echo 'if [ $checkout ]; then' >>$output -if [ -f $temp_checkout ]; then - cat $temp_checkout >> $output -else - echo 'echo Nothing to checkout.' >>$output -fi -echo 'fi' >>$output - -# Handle -p, apply patch -echo "" >>$output -echo 'if [ $patch ]; then' >>$output -if [ -f $temp_diffs ]; then - any_merged=y; - - echo " echo ''" >> $output - echo " echo Applying patches." >> $output - echo " if sed 's/^X//' << 'EOF' | patch -fsu; then" >>$output - - sed 's/^/X/' < $temp_diffs >>$output - echo EOF >>$output - - echo " echo All patches applied successfully." >>$output - echo " echo ''" >>$output - echo " else" >>$output - echo " echo Some conflicts detected:" >>$output - echo " find . -name '*.rej' -newer \$tmpfile -print" >>$output - echo " rm -f \$tmpfile" >>$output - echo " exit 1" >>$output - echo " fi" >>$output -else - echo ' echo No patches to apply.' >>$output -fi -echo 'fi' >>$output -echo "rm -f \$tmpfile" >>$output - -# Handle -c, cleanup -echo "" >>$output -echo 'if [ $cleanup ]; then' >>$output -sed 's/^\(.*\)$/ rm -f \1.orig \1.rej/' <$temp_files >>$output -echo 'fi' >>$output - -# Handle -i, checkin -echo "" >>$output -echo 'if [ $checkin ]; then' >>$output -sed 's/^\(.*\)$/ ctci -nc \1/' <$temp_files >>$output -sed 's/^\(.*\)$/ ctci -nc \1/' <$temp_dirs >>$output -echo 'fi' >>$output - -# Handle -d, delta -echo "" >>$output -echo 'if [ $delta ]; then' >>$output -sed 's/^\(.*\)$/ ctdelta \1/' <$temp_files >>$output -sed 's/^\(.*\)$/ ctdelta \1/' <$temp_dirs >>$output -echo 'fi' >>$output - -rm -f $temp_ct0 $temp_checkout $temp_dirs $temp_files $temp_diffs - -if [ -z "$any_merged" ]; then - echo "Nothing to do!" - echo "" - rm -f $output - exit 1 -fi - diff --git a/dtool/src/attach/neartool b/dtool/src/attach/neartool deleted file mode 100755 index a737597238..0000000000 --- a/dtool/src/attach/neartool +++ /dev/null @@ -1,1056 +0,0 @@ -#! /usr/local/bin/bash -# -# neartool - a Clearcase emulator for working off-line. -# -# Usage: -# -# neartool mkelem [-c "comment"] [-nc] [-eltype type] filename -# Marks a new file as a versioned element and checks it out. -# -# neartool mkdir [-c "comment"] [-nc] new_dir -# Creates a new Clearcase directory. -# -# neartool mv old-file new-file -# Renames or moves a versioned element. -# -# neartool mv file1 file2 file3 ... to-dir -# Moves a number of versioned elements into a new directory. -# -# neartool (co|checkout) [-c "comment"] [-nc] filename -# "checks out" a file by saving its current version and making a new -# version writable. The file is also marked as "checked-out". -# -# neartool (unco|uncheckout) filename -# Reverses the effect of the previous checkout. -# -# neartool (ci|checkin) [-c "comment"] [-nc] filename -# Marks the file as "checked in" and finalizes its most recent -# version, making it read-only. -# -# neartool revert filename -# Backs up a version on a checked-in filename. -# -# neartool revertall filename -# Reverts a filename all the way to its original state as extracted -# from the Clearcase vobs. -# -# neartool collapse filename -# Collapse all versioning information and mark the file as -# "checked-in" and merged as is. Presumably this should only be -# done after merging the changes back into the actual clearcase -# vobs. -# -# neartool rmname filename -# Removes a filename from a Clearcase directory. -# -# neartool find root [opts] -# Finds all elements with versions at root or below. The options -# are ignored. This is just a hack to support ctihave. -# -# neartool (lsco|lscheckout) [-l] file1 file2 file3... -# Lists which of the files are currently checked out, if any. If no -# filenames are given, lists all of the checked out files in the -# current directory. The -l option is ignored. -# -# neartool diff [-pre] filename -# Performs a diff between the indicated filename and its previous -# version. The option -pre is ignored. -# -# neartool xdiff [-pre] filename -# Performs an xdiff between the indicated filename and its previous -# version. The option -pre is ignored. -# -#ENDCOMMENT - - -# -# get_nth_param ( param-no param1 param2 param3 ... ) -# -# Sets the variable nth_param to the value of the parameter -# corresponding to param-no. -# -function get_nth_param { - local param_no=$1 - shift $param_no - nth_param=$1 -} - -# -# get_last_param ( param1 param2 param3 ... ) -# -# Sets the variable last_param to the value of the last non-blank -# parameter. -# -function get_last_param { - last_param="$1" - shift - while [ ! -z "$*" ]; do - last_param="$1" - shift - done -} - -# -# format_comment ( comment_str ) -# -# Writes the parameter -c '$comment_str', or -nc if the comment is -# empty, to the standard output. Handles nested quotes appropriately. -# -function format_comment { - local comment="$*" - - if [ -z "$comment" ]; then - echo "-nc" - else - # Escaping single quotes inside a single-quoted string doesn't - # seem to work too reliably. Instead, we'll just remove single - # quotes. So there. - - # This absurd number of backslashes is needed to output a single - # backslash through all this nesting. - # echo "-c '"`echo $comment | sed "s/'/\\\\\\\\'/g"`"'" - echo "-c '"`echo $comment | sed "s/'//g"`"'" - fi -} - -# -# list_comments ( dirname basename ) -# -# Writes to stdout any comments associated with checked-out versions of -# the indicated file, in order. -# -function list_comments { - local dirname=$1 - local basename=$2 - local filename=$dirname/$basename - local file comment version - - if [ -f $dirname/.ct0.$basename ]; then - # Now look for comments, in version-number order. - - # We use a series of ls commands so we don't try to sort the - # filenames between the one-, two-, and three-digit version - # numbers. - - for file in `(cd $dirname; ls .ct[0-9].$basename; ls .ct[0-9][0-9].$basename; ls .ct[0-9][0-9][0-9].$basename) 2>/dev/null`; do - version=`echo $file | sed "s/^\.ct\([0-9]*\).*$/\1/"` - comment=$dirname/.ct${version}comment.$basename - if [ -f $comment ]; then - sed s'/^/ /' <$comment - fi - done - fi -} - -# -# get_highest_version ( dirname basename ) -# -# Sets the variable $version to the numeric value that is the highest -# existing version number defined for the given filename. If the -# filename has no previous version numbers, sets $version to -1. -# -function get_highest_version { - local dirname=$1 - local basename=$2 - local filename=$dirname/$basename - local last - - if [ -f $dirname/.ct0.$basename ]; then - # If there are any versions at all, get the highest-numbered one - # and return it. - - # We use a series of ls commands so we don't try to sort the - # filenames between the one-, two-, and three-digit version - # numbers. - - last=`(cd $dirname; ls .ct[0-9].$basename; ls .ct[0-9][0-9].$basename; ls .ct[0-9][0-9][0-9].$basename) 2>/dev/null | tail -1` - version=`echo $last | sed "s/^\.ct\([0-9]*\).*$/\1/"` - else - # If there aren't any versions yet, the highest-numbered existing - # version number is -1. - version=-1 - fi -} - -# -# is_ctinternal ( basename ) -# -# Returns success if the filename matches one of the internal filenames -# generated by this script, failure if it is a perfectly ordinary -# non-ct file. -# -function is_ctinternal { - local basename=$1 - - case $basename in - .ct[0-9].*|ct[0-9][0-9].*|ct[0-9][0-9][0-9].*|\ - .ct[0-9]comment.*|.ct[0-9][0-9]comment.*|.ct[0-9][0-9][0-9]comment*|\ - .ctco.*|.ctnew.*) - return 0;; - esac - return 1 -} - -# -# is_install_dir ( dirname ) -# -# Returns success if the directory is any of the directories known to -# be install directories, such as inc, lib, lib/ss, etc. -# -function is_install_dir { - local dirname=$1 - - get_rel_dir $projroot $dirname - - case $rel_dir in - inc) return 0;; - inc/*) return 0;; - include/*) return 0;; - lib) return 0;; - lib/*) return 0;; - esac - return 1 -} - -# -# rm_all_ctinternals ( dirname basename ) -# -# Removes all the ctinternal files associated with the indicated file. -# -function rm_all_ctinternals { - local dirname=$1 - local basename=$2 - - rm -f $dirname/.ct[0-9].$basename - rm -f $dirname/.ct[0-9][0-9].$basename - rm -f $dirname/.ct[0-9][0-9][0-9].$basename - rm -f $dirname/.ct[0-9]comment.$basename - rm -f $dirname/.ct[0-9][0-9]comment.$basename - rm -f $dirname/.ct[0-9][0-9][0-9]comment.$basename - rm -f $dirname/.ctnew.$basename - rm -f $dirname/.ctco.$basename -} - -# -# sane_filename ( dirname basename ) -# -# Performs some sanity checks on the filename. If the file is -# unusable, exits the script with a status of 1. -# -function sane_filename { - local dirname=$1 - local basename=$2 - local filename=$dirname/$basename - - if [ -z "$basename" ]; then - echo "Filename unspecified." 1>&2 - exit 1 - fi - - if is_ctinternal $basename; then - echo "$filename is an internal filename and should not be checked out." 1>&2 - exit 1 - fi - - # It's easy to accidentally attempt to check something out from the - # inc directory. Let's detect this. - - if is_install_dir $dirname; then - echo "$basename is in an install directory." 1>&2 - echo "It should probably not be checked out." 1>&2 - exit 1 - fi - - if [ -d $filename ]; then - # Let's just quietly ignore directories. If we issue the warning - # statement, it makes ctaddtgt and ctaddpkg seem like they're - # failing. - # echo "Cannot operate on directories." 1>&2 - exit 0 - fi -} - -# -# get_fullpath ( local_dir ) -# -# Sets $fullpath to the fully-qualified pathname associated with $local_dir. -# -function get_fullpath { - local local_dir=$1 - - if [ -z "$local_dir" ]; then - fullpath=`pwd` - else - if [ ! -d "$local_dir" ]; then - echo "Invalid directory: $local_dir" 1>&2 - exit 1 - fi - # If we use pwd instead of /bin/pwd, $PWD will be used, which will give - # the wrong answer - fullpath=`(cd $local_dir; /bin/pwd)` - fi -} - - -# -# get_rel_dir ( root_dir local_dir ) -# -# Sets $rel_dir to the string which represents $local_dir relative to -# $root_dir. This is a simple string-prefix operation, and could fail -# in some obscure cases. -# -function get_rel_dir { - get_fullpath $1 - local root_dir=$fullpath - - get_fullpath $2 - local local_dir=$fullpath - - # Now remove the initial prefix. - if [ "$root_dir" = "$local_dir" ]; then - rel_dir="." - else - rel_dir=`echo $local_dir | sed 's:^'$root_dir/'::'` - - if [ "$rel_dir" = "$local_dir" ]; then - echo "$local_dir is not a directory within $root_dir." 1>&2 - exit 1 - fi - fi -} - -function ctmkelem { - local filename dirname basename - local comment eltype - - eltype=text_file - while getopts c:n:e: flag; do - case $flag in - c) comment="$OPTARG";; - n) comment="";; - e) case $OPTARG in - ltype) get_nth_param $OPTIND "$@" - eltype=$nth_param - OPTIND=`expr $OPTIND + 1`;; - *) echo Invalid switch -e$OPTARG - exit 1; - esac;; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - ctco=$dirname/.ctco.$basename - if [ -f $ctco ]; then - echo "$filename is already checked out." 1>&2 - exit 1 - fi - - if [ ! -f $filename ]; then - # If the file doesn't exist, create it. - echo -n "" >$filename - fi - - get_highest_version $dirname $basename - - if [ $version -ne -1 ]; then - echo "$filename already has versions." 1>&2 - exit 1 - fi - - if [ ! -w $filename ]; then - echo "$filename has no write permission, it's probably already a versioned element." 1>&2 - exit 1 - fi - - # Create a ctnew file to indicate the file is newly created. - ctnew=$dirname/.ctnew.$basename - echo $eltype >$ctnew - chmod uga-w $ctnew - - # Now "check out" the new filename by creating an empty first version. - - next=$dirname/.ct0.$basename - nextcomment=$dirname/.ct0comment.$basename - - if [ ! -z "$comment" ]; then - echo $comment >$nextcomment - chmod uga-w $nextcomment - fi - echo -n "" > $next - - date >$ctco - done -} - -function ctmkdir { - local filename dirname basename - local comment - - while getopts c:n: flag; do - case $flag in - c) comment="$OPTARG";; - n) comment="";; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - if [ -z "$basename" ]; then - echo "Filename unspecified." 1>&2 - exit 1 - fi - - if is_ctinternal $basename; then - echo "$filename is an internal filename and should not be checked out." 1>&2 - exit 1 - fi - - if mkdir $filename; then - # The directory is successfully made; record this in the - # Clearcase instructions. - get_rel_dir $projroot $dirname - echo "ctco -nc $rel_dir" >>$projroot/.ctcmds - echo "ctmkdir `format_comment $comment` $rel_dir/$basename" >>$projroot/.ctcmds - else - echo "Unable to create directory $filename." 1>&2 - fi - done -} - -# -# do_ctmv ( oldname newname ) -# -# The implementation of ctmv. This renames a single file, possibly -# placing it in a new directory. -# -function do_ctmv { - local oldfilename olddirname oldbasename - local newfilename newdirname newbasename - - oldfilename=$1 - olddirname=`dirname $oldfilename` - oldbasename=`basename $oldfilename` - - newfilename=$2 - newdirname=`dirname $newfilename` - newbasename=`basename $newfilename` - - sane_filename $olddirname $oldbasename - sane_filename $newdirname $newbasename - - if [ ! -f $oldfilename ]; then - echo "$oldfilename does not exist." 1>&2 - exit 1 - fi - - if [ -f $newfilename ]; then - echo "$newfilename already exists--remove it first." 1>&2 - exit 1 - fi - - oldctnew=$olddirname/.ctnew.$oldfilename - - if [ ! -f $oldctnew ]; then - # The filename exists on the actual Clearcase vobs. We need to - # record the renaming. - - get_rel_dir $projroot $olddirname - local oldroot=$rel_dir - get_rel_dir $projroot $newdirname - local newroot=$rel_dir - - echo "ctco -nc $oldroot" >> $projroot/.ctcmds - if [ "$oldroot" != "$newroot" ]; then - echo "ctco -nc $newroot" >> $projroot/.ctcmds - fi - echo "ctmv $oldroot/$oldbasename $newroot/$newbasename" >> $projroot/.ctcmds - fi - - # Now rename our local copy, and all of its version tracking stuff. - get_fullpath $newdirname - local newroot=$fullpath - - (cd $olddirname; - for file in \ - `ls .ct[0-9].$oldbasename \ - .ct[0-9][0-9].$oldbasename \ - .ct[0-9][0-9][0-9].$oldbasename \ - .ct[0-9]comment.$oldbasename \ - .ct[0-9][0-9]comment.$oldbasename \ - .ct[0-9][0-9][0-9]comment.$oldbasename \ - .ctnew.$oldbasename \ - .ctco.$oldbasename \ - $oldbasename 2>/dev/null`; do - ctprefix=`echo $file | sed 's:'$oldbasename'$::'` - mv -i $file $newroot/$ctprefix$newbasename - done) -} - -function ctmv { - local source destination - - while getopts "" flag; do - case $flag in - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - get_last_param "$@" - destination=$last_param; - - if [ ! -d "$destination" ]; then - # If we're not moving to a directory, we are renaming a file--and - # thus we can only allow two parameters. - source=$1 - shift 2 - if [ ! -z "$*" ]; then - echo Last filename must be a directory. - exit 1 - fi - - do_ctmv $source $destination - else - - # Otherwise, we're moving a slew of files to a directory. Get - # each filename and move it. - for source in "$@"; do - if [ "$source" != "$destination" ]; then - do_ctmv $source $destination/$source - fi - done - fi -} - -function ctco { - local filename dirname basename - - while getopts c:n: flag; do - case $flag in - c) comment="$OPTARG";; - n) comment="";; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - okflag=y - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - okflag= - else - ctco=$dirname/.ctco.$basename - if [ -f $ctco ]; then - echo "$filename is already checked out." 1>&2 - okflag= - else - if [ -w $filename ]; then - echo "$filename has write permission, it's probably not a versioned element." 1>&2 - echo "Try neartool mkelem $filename." 1>&2 - okflag= - fi - fi - fi - - if [ ! -z "$okflag" ]; then - get_highest_version $dirname $basename - next=$dirname/.ct`expr $version + 1`.$basename - nextcomment=$dirname/.ct`expr $version + 1`comment.$basename - - if [ ! -z "$comment" ]; then - echo $comment >$nextcomment - chmod uga-w $nextcomment - fi - - mv $filename $next - cp $next $filename - chmod ug+w $filename - - date >$ctco - fi - done -} - - -function ctunco { - local ignore filename dirname basename - - while getopts "r:" flag; do - case $flag in - r) ignore=;; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - ctco=$dirname/.ctco.$basename - if [ ! -f $ctco ]; then - echo "$filename is not checked out." 1>&2 - exit 1 - fi - - get_highest_version $dirname $basename - last=$dirname/.ct$version.$basename - lastcomment=$dirname/.ct${version}comment.$basename - - mv -f $last $filename - rm -f $lastcomment - rm $ctco - touch $filename # For emacs, and correct make behavior. - done -} - - -function ctci { - local filename dirname basename - local comment - - while getopts c:n: flag; do - case $flag in - c) comment="$OPTARG";; - n) comment="";; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - - ctco=$dirname/.ctco.$basename - if [ ! -f $ctco ]; then - echo "$filename is not checked out." 1>&2 - exit 1 - fi - - # We touch the file, so emacs will note that it has changed. - touch $filename - chmod uga-w $filename - - rm $ctco - done -} - - -function ctrevert { - local filename dirname basename - - while getopts "" flag; do - case $flag in - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - ctco=$dirname/.ctco.$basename - if [ -f $ctco ]; then - echo "$filename is currently checked out. Use neartool unco $filename." 1>&2 - exit 1 - fi - - get_highest_version $dirname $basename - - if [ $version -eq -1 ]; then - echo "$filename has no versions." 1>&2 - exit 1 - fi - - last=$dirname/.ct$version.$basename - lastcomment=$dirname/.ct${version}comment.$basename - - mv -f $last $filename - rm -f $lastcomment - - if [ $version -eq 0 ]; then - ctnew=$dirname/.ctnew.$basename - if [ -f $ctnew ]; then - echo "Removing newly created element $filename." 1>&2 - rm -f $ctnew $filename - fi - fi - done -} - -function ctrevertall { - local filename dirname basename - - while getopts "" flag; do - case $flag in - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - ct0=$dirname/.ct0.$basename - if [ ! -f $ct0 ]; then - echo "$filename has no versions." 1>&2 - else - ctnew=$dirname/.ctnew.$basename - if [ ! -f $ctnew ]; then - # If we didn't newly create this file, preserve its original version. - mv -f $ct0 $filename - fi - rm_all_ctinternals $dirname $basename - fi - done -} - -function ctcollapse { - local filename dirname basename - - while getopts "" flag; do - case $flag in - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - rm_all_ctinternals $dirname $basename - - chmod uga-w $filename - touch $filename # For Emacs' benefit. - done -} - -function ctrmname { - local filename dirname basename - while getopts "" flag; do - case $flag in - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - ctnew=$dirname/.ctnew.$basename - - if [ ! -f $ctnew ]; then - # The filename exists on the actual Clearcase vobs. We need to - # record the removing. - - get_rel_dir $projroot $dirname - local root=$rel_dir - - echo "ctco -nc $root" >> $projroot/.ctcmds - echo "ctrm $root/$basename" >> $projroot/.ctcmds - fi - - # Now remove all of the versioned stuff. - ctcollapse $filename - - # And remove the file itself. - rm -f $filename - done -} - - -function ctfind { - local root=$1 - - if [ -z "$root" ]; then - echo "Specify a starting point of the find." 1>&2 - exit 1 - fi - - find $root -name .ct0.\* -print | sed 's/\.ct0\.//' -} - -function ctdescribe { - local filename dirname basename - - while getopts "" flag; do - case $flag in - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in `ls "$@"`; do - dirname=`dirname $filename` - basename=`basename $filename` - - ctco=$dirname/.ctco.$basename - get_highest_version $dirname $basename - version=`expr $version + 1` - - if [ -f $ctco ]; then - if [ $version -eq 0 ]; then - echo "$filename is checked out with no versions." - else - echo "$filename is checked out as version $version." - list_comments $dirname $basename - fi - else - if [ $version -eq 0 ]; then - echo "$filename has not been checked out and has no versions." - else - echo "$filename is checked in as version $version." - list_comments $dirname $basename - fi - fi - done -} - -function ctlsco { - local filename dirname basename - local ignore - while getopts "l" flag; do - case $flag in - l) ignore=;; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - for filename in `ls "$@"`; do - dirname=`dirname $filename` - basename=`basename $filename` - - ctco=$dirname/.ctco.$basename - if [ -f $ctco ]; then - ctdescribe $filename - fi - done -} - -function ctdiff { - local filename dirname basename - local graphical - local ignore - local diff - - while getopts "gp:" flag; do - case $flag in - g) graphical=y;; - p) ignore=;; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - diff=diff - if [ $graphical ]; then - # The user requested a graphical difference; use gdiff if we can - # find it. Otherwise, fall back to diff. - if which gdiff 2>/dev/null >/dev/null; then - diff=gdiff - fi - fi - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - get_highest_version $dirname $basename - - if [ $version -eq -1 ]; then - echo "$filename has no versions." 1>&2 - exit 1 - fi - - last=$dirname/.ct$version.$basename - - # First, call diff (the real diff) just to see if the files are - # different at all. - - if diff $last $filename >/dev/null; then - echo Files are identical. - - else - # If the files ARE different, then invoke diff again (which - # might be gdiff, this time) to actually report the differences. - # We must do it twice like this because gdiff might not return - # non-zero if the files are different. - - $diff $last $filename - fi - done -} - - -function usage { - sed '/#ENDCOMMENT/,$d' <$0 >&2 - exit 1 -} - - -# -# Main entry point -# - -command=$1 -if [ -z "$command" ]; then - usage -fi - -shift - -projroot=`ctproj -r` - -if [ -z "$projroot" ]; then - echo "Not currently in a project tree." 1>&2 - exit 1 -fi - -case $command in - mkelem) ctmkelem "$@";; - mkdir) ctmkdir "$@";; - mv) ctmv "$@";; - co|checkout) ctco "$@";; - unco|uncheckout) ctunco "$@";; - ci|checkin) ctci "$@";; - revert) ctrevert "$@";; - revertall) ctrevertall "$@";; - collapse) ctcollapse "$@";; - rmname) ctrmname "$@";; - find) ctfind "$@";; - lsco|lscheckout) ctlsco "$@";; - describe) ctdescribe "$@";; - diff) ctdiff "$@";; - xdiff) ctdiff -g "$@";; - h|help|-h) usage;; - *) echo "Invalid option: $command" 1>&2 - exit 1; -esac diff --git a/dtool/src/attach/unco.pl b/dtool/src/attach/unco.pl deleted file mode 100644 index dd383ab99f..0000000000 --- a/dtool/src/attach/unco.pl +++ /dev/null @@ -1,31 +0,0 @@ -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctdelta.pl" ; - -# Remove a branch for an element if needed -# input is in: -# $_[0] = element -sub CTUncoDoIt { - local( $elem ) = $_[0] ; - local( $ver ) = &CTDeltaGetVersion( $elem ) ; - if ( $ctdebug ne "" ) { - print STDERR "Unco script: got version '" . $ver . "'\n" ; - } - local( @verlist ) ; - @verlist = split( /\//, $ver ) ; - local( $vlast ) = pop( @verlist ) ; - if ( $ctdebug ne "" ) { - print STDERR "Unco script: last part of version is '" . $vlast . "'\n" ; - } - if ( $#verlist > 1 ) { - local( $branch ) = join( "/", @verlist ) ; - if ( $vlast == 0 ) { - local( $cmd ) = "cleartool rmbranch -force -nc $elem" . "@@" . "$branch" ; - if ( $ctdebug ne "" ) { - print STDERR "Unco script: command is '" . $cmd . "'\n" ; - } - system $cmd ; - } - } -} - -1; diff --git a/dtool/src/attach/update-cttree b/dtool/src/attach/update-cttree deleted file mode 100755 index 2abf87ee15..0000000000 --- a/dtool/src/attach/update-cttree +++ /dev/null @@ -1,189 +0,0 @@ -#! /bin/sh -# -# update-cttree.sh -# -# Usage: -# -# update-cttree.sh [opts] hostname -# -# Uses rsh and rdist to update the indicated host with a fresh copy of the -# current project tree. -# -# This script must be executed from within a project tree. -# -# Options: -# -# -u username Specify the login name on the remote host. -# -# -d dir Specify the player install dir on the remote host. This -# the directory above the project-tree-specific directory -# like 'panda' or 'tool'. The default is 'player'. -# -# -t Touch the build-request timestamp file after updating. -# This assumes there's a cron job running on the remote -# machine checking the date on this file from time to time. -# -# -f Assume the user knows what he/she is doing, and don't bother -# to check that there are no files checked out in the vobs -# before releasing. This can save considerable time when the -# system is extremely slow; however, it can be dangerous -# to accidentally release a checked-out file (because the -# file will then be write-access on the remote host, and -# neartool will not be able to track local changes made to it.) -# -#ENDCOMMENT - -username=`whoami` -dirname=player -touch_request= -cocky_user= - -while getopts "u:d:tfh" flag; do - case $flag in - u) username=$OPTARG;; - d) dirname=$OPTARG;; - t) touch_request=y;; - f) cocky_user=y;; - h) sed '/#ENDCOMMENT/,$d' <$0 >&2 - exit 1;; - \?) exit 1; - esac -done - -shift `expr $OPTIND - 1` -remote_host=$1 -projroot=`ctproj -r` - -if [ -z "$projroot" ]; then - echo "" - echo "You must execute this script in a project tree." - echo "" - exit 1 -fi - -if [ -z "$remote_host" ]; then - echo "" - echo "You must specify a remote hostname. -h for help." - echo "" - exit 1 -fi - -if [ ! -d /usr/atria ]; then - echo "" - echo "This script is intended to be run on an actual ClearCase vobs." - echo "" - exit 1 -fi - -projname=`basename $projroot` -projtop=`dirname $projroot` - -if [ "$projname" = "tool" ]; then - echo "" - echo "This script should not be used on the tool tree." - echo "" - exit 1 -fi - -outfile=/tmp/uc.$username.$projname.$remote_host.out -errfile=/tmp/uc.$username.$projname.$remote_host.err -rm -f $outfile $errfile - -# Check to make sure we can run rsh to the remote machine, and that -# the remote machine doesn't have anything checked out. - -if rsh $remote_host -l $username "cd $dirname; find $projname -name .ct0.\* -print" >$outfile 2>$errfile; then - if [ ! -f $outfile ]; then - echo "" - echo "Error in processing; unable to generate $outfile." - echo "" - rm -f $outfile $errfile - exit 1 - fi - if [ ! -f $errfile ]; then - echo "" - echo "Error in processing; unable to generate $errfile." - echo "" - rm -f $outfile $errfile - exit 1 - fi - if [ -s $errfile ]; then - echo "" - echo "Unable to scan project tree $dirname/$projname on $remote_host." - echo "" - rm -f $outfile $errfile - exit 1 - fi - if [ -s $outfile ]; then - echo "" - echo "Cannot update $remote_host; files still checked out on remote:" - sed 's/^/ /;s/\.ct0\.//' $outfile - rm -f $outfile $errfile - echo "" - exit 1 - fi -else - echo "" - echo "Cannot rsh to $remote_host as $username." - echo "" - rm -f $outfile $errfile - exit 1 -fi - -# Check to make sure the local machine doesn't have anything checked out. -if [ -z "$cocky_user" ]; then - cd $projroot - cleartool lsco -s -me -recurse >$outfile - if [ -s $outfile ]; then - echo "" - echo "Cannot update from "`hostname`"; files still checked out in vobs:" - sed 's/^/ /;s/\.ct0\.//' $outfile - rm -f $outfile $errfile - echo "" - exit 1 - fi -fi - -rm -f $outfile $errfile - - -# -# Get the complete list of files in the tree we need to update. -# -cd $projtop -filelist=${outfile}.files -rm -f $filelist -cleartool find $projname -nxn -print | grep -v '/lost+found' > $filelist - -# -# Now build up a number of rdist files, as needed, to update these files. -# We have to do this in stages because there seems to be a limit of about -# 2000 files in one rdist file. -# -numlines=`wc -l $filelist | awk '{ print $1 }'` -echo $projname contains $numlines files. - -startline=1 -while [ $startline -le $numlines ]; do - echo "FILES = (" >> $outfile - tail +$startline $filelist | head -2000 >> $outfile - echo ")" >> $outfile - - echo '${FILES} -> '$username@$remote_host >>$outfile - echo " install $dirname;" >> $outfile - - if [ $touch_request ]; then - echo " cmdspecial \"touch $dirname/$projname/build-request\" ;" >>$outfile - fi - - if rdist -onochkowner,nochkgroup,numchkgroup,whole,nodescend -f $outfile; then - rm -f $outfile - else - echo "Error in rdist." - rm -f $outfile $filelist $errfile - exit 1 - fi - startline=`expr $startline + 2000` -done - -rm -f $filelist $errfile 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/dtoolbase/dtool_platform.h b/dtool/src/dtoolbase/dtool_platform.h index 4cb25abc3a..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 diff --git a/dtool/src/dtoolutil/executionEnvironment.cxx b/dtool/src/dtoolutil/executionEnvironment.cxx index ad782b521e..c4f2e5093e 100644 --- a/dtool/src/dtoolutil/executionEnvironment.cxx +++ b/dtool/src/dtoolutil/executionEnvironment.cxx @@ -540,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. @@ -578,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]; @@ -833,8 +832,6 @@ read_args() { } #endif // _WIN32 -#endif // ANDROID - if (_dtool_name.empty()) { _dtool_name = _binary_name; } diff --git a/dtool/src/dtoolutil/filename.cxx b/dtool/src/dtoolutil/filename.cxx index c862c0aabf..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; 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/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/dtool/src/dtoolutil/vector_string.cxx b/dtool/src/dtoolutil/vector_string.cxx index 2b7ba4202a..0e5df2ce1d 100644 --- a/dtool/src/dtoolutil/vector_string.cxx +++ b/dtool/src/dtoolutil/vector_string.cxx @@ -13,8 +13,8 @@ #include "vector_string.h" -#define EXPCL EXPCL_DTOOLCONFIG -#define EXPTP EXPTP_DTOOLCONFIG +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE std::string #define NAME vector_string 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/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 3a861a4844..96420ef663 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -3500,6 +3500,10 @@ write_function_for_name(ostream &out, Object *obj, out << " */\n"; + if (has_this && obj == nullptr) { + assert(obj != nullptr); + } + out << function_name << " {\n"; if (has_this) { @@ -7007,7 +7011,7 @@ record_object(TypeIndex type_index) { object->_methods.push_back(function); } } - if (itype.derivation_has_downcast(di)) { + /*if (itype.derivation_has_downcast(di)) { // Downcasts are methods of the base class, not the child class. TypeIndex base_type_index = itype.get_derivation(di); @@ -7020,7 +7024,7 @@ record_object(TypeIndex type_index) { pobject->_methods.push_back(function); } } - } + }*/ } } diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index 6cbf8f02c0..63d579c68f 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -2603,7 +2603,7 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, const CPPStructType::Base &base = (*bi); if (base._vis <= V_public) { CPPType *base_type = TypeManager::resolve_type(base._base, scope); - TypeIndex base_index = get_type(base_type, true); + TypeIndex base_index = get_type(base_type, false); if (base_index == 0) { if (base_type != NULL) { diff --git a/dtool/src/interrogate/interrogate_module.cxx b/dtool/src/interrogate/interrogate_module.cxx index 5feae341b7..8c843e9da1 100644 --- a/dtool/src/interrogate/interrogate_module.cxx +++ b/dtool/src/interrogate/interrogate_module.cxx @@ -76,6 +76,79 @@ upcase_string(const string &str) { } */ +/** + * Finds a dependency cycle between the given dependency mapping, starting at + * the node that is already placed in the given cycle vector. + */ +static bool find_dependency_cycle(vector_string &cycle, map > &dependencies) { + assert(!cycle.empty()); + + const set &deps = dependencies[cycle.back()]; + for (auto it = deps.begin(); it != deps.end(); ++it) { + auto it2 = std::find(cycle.begin(), cycle.end(), *it); + if (it2 != cycle.end()) { + // Chop off the part of the chain that is not relevant. + cycle.erase(cycle.begin(), it2); + cycle.push_back(*it); + return true; + } + + // Recurse. + cycle.push_back(*it); + if (find_dependency_cycle(cycle, dependencies)) { + return true; + } + cycle.pop_back(); + } + + return false; +} + +/** + * Given that a direct link has been established between the two libraries, + * finds the two types that make up this relationship and prints out the + * nature of their dependency. + */ +static bool print_dependent_types(const string &lib1, const string &lib2) { + for (int ti = 0; ti < interrogate_number_of_global_types(); ti++) { + TypeIndex thetype = interrogate_get_global_type(ti); + if (interrogate_type_has_module_name(thetype) && + interrogate_type_has_library_name(thetype) && + lib1 == interrogate_type_library_name(thetype) && + module_name == interrogate_type_module_name(thetype)) { + + // Get the dependencies for this library. + int num_derivations = interrogate_type_number_of_derivations(thetype); + for (int di = 0; di < num_derivations; ++di) { + TypeIndex basetype = interrogate_type_get_derivation(thetype, di); + if (interrogate_type_is_global(basetype) && + interrogate_type_has_library_name(basetype) && + interrogate_type_library_name(basetype) == lib2) { + cerr + << " " << interrogate_type_scoped_name(thetype) << " (" + << lib1 << ") inherits from " + << interrogate_type_scoped_name(basetype) << " (" << lib2 << ")\n"; + return true; + } + } + + // It also counts if this is a typedef pointing to another type. + if (interrogate_type_is_typedef(thetype)) { + TypeIndex wrapped = interrogate_type_wrapped_type(thetype); + if (interrogate_type_is_global(wrapped) && + interrogate_type_has_library_name(wrapped) && + interrogate_type_library_name(wrapped) == lib2) { + cerr + << " " << interrogate_type_scoped_name(thetype) << " (" + << lib1 << ") is a typedef to " + << interrogate_type_scoped_name(wrapped) << " (" << lib2 << ")\n"; + } + } + } + } + return false; +} + int write_python_table_native(ostream &out) { out << "\n#include \"dtoolbase.h\"\n" << "#include \"interrogate_request.h\"\n\n" @@ -83,7 +156,7 @@ int write_python_table_native(ostream &out) { int count = 0; - vector_string libraries; + map > dependencies; // out << "extern \"C\" {\n"; @@ -99,23 +172,112 @@ int write_python_table_native(ostream &out) { // name add it to set of libraries if (interrogate_function_has_library_name(function_index)) { string library_name = interrogate_function_library_name(function_index); - if (std::find(libraries.begin(), libraries.end(), library_name) == libraries.end()) { - libraries.push_back(library_name); - } + dependencies[library_name]; } // } } - for (int ti = 0; ti < interrogate_number_of_types(); ti++) { - TypeIndex thetype = interrogate_get_type(ti); + for (int ti = 0; ti < interrogate_number_of_global_types(); ti++) { + TypeIndex thetype = interrogate_get_global_type(ti); if (interrogate_type_has_module_name(thetype) && module_name == interrogate_type_module_name(thetype)) { if (interrogate_type_has_library_name(thetype)) { string library_name = interrogate_type_library_name(thetype); + set &deps = dependencies[library_name]; + + // Get the dependencies for this library. + int num_derivations = interrogate_type_number_of_derivations(thetype); + for (int di = 0; di < num_derivations; ++di) { + TypeIndex basetype = interrogate_type_get_derivation(thetype, di); + if (interrogate_type_is_global(basetype) && + interrogate_type_has_library_name(basetype)) { + string baselib = interrogate_type_library_name(basetype); + if (baselib != library_name) { + deps.insert(move(baselib)); + } + } + } + + if (interrogate_type_is_typedef(thetype)) { + TypeIndex wrapped = interrogate_type_wrapped_type(thetype); + if (interrogate_type_is_global(wrapped) && + interrogate_type_has_library_name(wrapped)) { + string wrappedlib = interrogate_type_library_name(wrapped); + if (wrappedlib != library_name) { + deps.insert(move(wrappedlib)); + } + } + } + } + } + } + + // Now add the libraries in their proper ordering, based on dependencies. + vector_string libraries; + while (libraries.size() < dependencies.size()) { + // We have this check to make sure we don't enter an infinite loop. + bool added_any = false; + + for (auto it = dependencies.begin(); it != dependencies.end(); ++it) { + const string &library_name = it->first; + set &deps = dependencies[library_name]; + + // Remove the dependencies that have already been added from the deps. + if (!deps.empty()) { + for (auto li = libraries.begin(); li != libraries.end(); ++li) { + deps.erase(*li); + } + } + + if (deps.empty()) { + // OK, no remaining dependencies, so we can add this. if (std::find(libraries.begin(), libraries.end(), library_name) == libraries.end()) { libraries.push_back(library_name); + added_any = true; } } } + + if (!added_any) { + // Oh dear, we must have hit a circular dependency. Go through the + // remaining libraries to figure it out and print it. + cerr << "Circular dependency between libraries detected:\n"; + for (auto it = dependencies.begin(); it != dependencies.end(); ++it) { + const string &library_name = it->first; + set &deps = dependencies[library_name]; + if (deps.empty()) { + continue; + } + + // But since it does indicate a potential architectural flaw, we do + // want to let the user know about this. + vector_string cycle; + cycle.push_back(library_name); + if (!find_dependency_cycle(cycle, dependencies)) { + continue; + } + assert(cycle.size() >= 2); + + // Show the cycle of library dependencies. + auto ci = cycle.begin(); + cerr << " " << *ci; + for (++ci; ci != cycle.end(); ++ci) { + cerr << " -> " << *ci; + } + cerr << "\n"; + + // Now print out the actual types that make up the cycle. + ci = cycle.begin(); + string prev = *ci; + for (++ci; ci != cycle.end(); ++ci) { + print_dependent_types(prev, *ci); + prev = *ci; + } + + // We have to arbitrarily break one of the dependencies in order to be + // able to proceed. Break the first dependency. + dependencies[cycle[0]].erase(cycle[1]); + } + } } vector_string::const_iterator ii; diff --git a/dtool/src/interrogatedb/interrogateType.cxx b/dtool/src/interrogatedb/interrogateType.cxx index a977ff987a..a6d6e45487 100644 --- a/dtool/src/interrogatedb/interrogateType.cxx +++ b/dtool/src/interrogatedb/interrogateType.cxx @@ -112,14 +112,16 @@ operator = (const InterrogateType ©) { /** * Combines type with the other similar definition. If one type is "fully - * defined" and the other one isn't, the fully-defined type wins. + * defined" and the other one isn't, the fully-defined type wins. If both + * types are fully defined, whichever type is marked "global" wins. */ void InterrogateType:: merge_with(const InterrogateType &other) { // The only thing we care about copying from the non-fully-defined type // right now is the global flag. - if (is_fully_defined()) { + if (is_fully_defined() && + (!other.is_fully_defined() || (other._flags & F_global) == 0)) { // We win. _flags |= (other._flags & F_global); diff --git a/dtool/src/interrogatedb/interrogate_interface.cxx b/dtool/src/interrogatedb/interrogate_interface.cxx index bd5493fc39..6e948134d3 100644 --- a/dtool/src/interrogatedb/interrogate_interface.cxx +++ b/dtool/src/interrogatedb/interrogate_interface.cxx @@ -509,6 +509,12 @@ interrogate_get_type_by_true_name(const char *type_name) { return InterrogateDatabase::get_ptr()->lookup_type_by_true_name(type_name); } +bool +interrogate_type_is_global(TypeIndex type) { + // cerr << "interrogate_type_is_global(" << type << ")\n"; + return InterrogateDatabase::get_ptr()->get_type(type).is_global(); +} + const char * interrogate_type_name(TypeIndex type) { // cerr << "interrogate_type_name(" << type << ")\n"; diff --git a/dtool/src/interrogatedb/interrogate_interface.h b/dtool/src/interrogatedb/interrogate_interface.h index 63e385d527..6c434d7833 100644 --- a/dtool/src/interrogatedb/interrogate_interface.h +++ b/dtool/src/interrogatedb/interrogate_interface.h @@ -367,6 +367,7 @@ EXPCL_INTERROGATEDB TypeIndex interrogate_get_type(int n); EXPCL_INTERROGATEDB TypeIndex interrogate_get_type_by_name(const char *type_name); EXPCL_INTERROGATEDB TypeIndex interrogate_get_type_by_scoped_name(const char *type_name); EXPCL_INTERROGATEDB TypeIndex interrogate_get_type_by_true_name(const char *type_name); +EXPCL_INTERROGATEDB bool interrogate_type_is_global(TypeIndex type); EXPCL_INTERROGATEDB const char *interrogate_type_name(TypeIndex type); EXPCL_INTERROGATEDB const char *interrogate_type_scoped_name(TypeIndex type); EXPCL_INTERROGATEDB const char *interrogate_type_true_name(TypeIndex type); diff --git a/dtool/src/interrogatedb/py_compat.cxx b/dtool/src/interrogatedb/py_compat.cxx old mode 100755 new mode 100644 index f7f02607c0..f0dd42cf73 --- a/dtool/src/interrogatedb/py_compat.cxx +++ b/dtool/src/interrogatedb/py_compat.cxx @@ -12,6 +12,7 @@ */ #include "py_compat.h" +#include "py_panda.h" #ifdef HAVE_PYTHON diff --git a/dtool/src/interrogatedb/py_compat.h b/dtool/src/interrogatedb/py_compat.h old mode 100755 new mode 100644 diff --git a/dtool/src/interrogatedb/py_panda.h b/dtool/src/interrogatedb/py_panda.h index 74f13680f8..f4edc8f4ad 100644 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -22,6 +22,7 @@ #include "pnotify.h" #include "vector_uchar.h" +#include "register_type.h" #if defined(HAVE_PYTHON) && !defined(CPPPARSER) diff --git a/dtool/src/interrogatedb/py_wrappers.cxx b/dtool/src/interrogatedb/py_wrappers.cxx old mode 100755 new mode 100644 diff --git a/dtool/src/interrogatedb/py_wrappers.h b/dtool/src/interrogatedb/py_wrappers.h old mode 100755 new mode 100644 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/panda/src/cftalk/cfChannel.I b/dtool/src/parser-inc/fftw3.h similarity index 75% rename from panda/src/cftalk/cfChannel.I rename to dtool/src/parser-inc/fftw3.h index 4bb6a1c3fa..c5b3b8131d 100644 --- a/panda/src/cftalk/cfChannel.I +++ b/dtool/src/parser-inc/fftw3.h @@ -6,7 +6,9 @@ * 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 fftw.h + * @author cfsworks + * @date 2018-02-17 */ + +typedef struct _fftw_plan fftw_plan; 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/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/rfftw.h b/dtool/src/parser-inc/rfftw.h deleted file mode 100644 index 47bb2102d1..0000000000 --- a/dtool/src/parser-inc/rfftw.h +++ /dev/null @@ -1,16 +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 rfftw.h - * @author drose - * @date 2007-06-27 - */ - -typedef struct _rfftw_plan rfftw_plan; - - 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/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.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/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 diff --git a/makepanda/installer.nsi b/makepanda/installer.nsi old mode 100755 new mode 100644 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 64508f3308..9a9d1451c0 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -711,8 +711,7 @@ if (COMPILER == "MSVC"): if (PkgSkip("HARFBUZZ")==0): LibName("HARFBUZZ", GetThirdpartyDir() + "harfbuzz/lib/harfbuzz.lib") IncDirectory("HARFBUZZ", GetThirdpartyDir() + "harfbuzz/include/harfbuzz") - if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/rfftw.lib") - if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/fftw.lib") + if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/fftw3.lib") if (PkgSkip("ARTOOLKIT")==0):LibName("ARTOOLKIT",GetThirdpartyDir() + "artoolkit/lib/libAR.lib") if (PkgSkip("OPENCV")==0): LibName("OPENCV", GetThirdpartyDir() + "opencv/lib/cv.lib") if (PkgSkip("OPENCV")==0): LibName("OPENCV", GetThirdpartyDir() + "opencv/lib/highgui.lib") @@ -879,7 +878,7 @@ 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")) @@ -896,7 +895,7 @@ if (COMPILER=="GCC"): SmartPkgEnable("VRPN", "", ("vrpn", "quat"), ("vrpn", "quat.h", "vrpn/vrpn_Types.h")) SmartPkgEnable("BULLET", "bullet", ("BulletSoftBody", "BulletDynamics", "BulletCollision", "LinearMath"), ("bullet", "bullet/btBulletDynamicsCommon.h")) SmartPkgEnable("VORBIS", "vorbisfile",("vorbisfile", "vorbis", "ogg"), ("ogg/ogg.h", "vorbis/vorbisfile.h")) - SmartPkgEnable("OPUS", "opusfile", ("opusfile", "opus", "ogg"), ("ogg/ogg.h", "opus/opusfile.h")) + SmartPkgEnable("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") @@ -1007,7 +1006,7 @@ if (COMPILER=="GCC"): if GetTarget() == 'android': LibName("ALWAYS", '-llog') - LibName("ALWAYS", '-landroid') + LibName("ANDROID", '-landroid') LibName("JNIGRAPHICS", '-ljnigraphics') for pkg in MAYAVERSIONS: @@ -1315,7 +1314,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() @@ -1323,32 +1328,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 @@ -1374,7 +1389,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. @@ -1501,12 +1516,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' @@ -1758,8 +1780,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' @@ -1772,6 +1797,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' @@ -1795,9 +1821,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" @@ -1808,8 +1851,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") @@ -1828,14 +1871,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)) @@ -1944,6 +1980,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 @@ -2197,6 +2252,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) @@ -2407,7 +2465,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' @@ -3320,15 +3378,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 @@ -3648,7 +3697,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/ @@ -5145,8 +5194,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') @@ -5158,15 +5209,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/ @@ -5174,7 +5225,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') @@ -6594,6 +6645,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']) + # # DIRECTORY: pandatool/src/deploy-stub # @@ -6990,16 +7064,19 @@ This package contains the SDK for development with Panda3D, install panda3d-runt /etc/Confauto.prc /etc/Config.prc /usr/share/panda3d -/usr/share/mime-info/panda3d.mime -/usr/share/mime-info/panda3d.keys -/usr/share/mime/packages/panda3d.xml -/usr/share/application-registry/panda3d.applications -/usr/share/applications/*.desktop /etc/ld.so.conf.d/panda3d.conf /usr/%_lib/panda3d """ + PYTHON_SITEPACKAGES + """ /usr/include/panda3d """ +if not PkgSkip("PVIEW"): + INSTALLER_SPEC_FILE += \ +"""/usr/share/applications/pview.desktop +/usr/share/mime-info/panda3d.mime +/usr/share/mime-info/panda3d.keys +/usr/share/mime/packages/panda3d.xml +/usr/share/application-registry/panda3d.applications +""" RUNTIME_INSTALLER_SPEC_FILE=""" Summary: Runtime binary and browser plugin for the Panda3D Game Engine @@ -7106,8 +7183,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() @@ -7272,8 +7349,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 @@ -7496,6 +7573,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") @@ -7530,6 +7719,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 a787653e1e..c21322f94a --- a/makepanda/makepanda.vcproj +++ b/makepanda/makepanda.vcproj @@ -3736,18 +3736,6 @@ - - - - - - - - - - - - diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index fd62b0b5e5..cb2f5a7ae3 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -37,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 @@ -57,6 +59,13 @@ 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 @@ -290,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: @@ -344,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: @@ -413,13 +457,13 @@ def CrossCompiling(): return GetTarget() != GetHost() def GetCC(): - if TARGET == 'darwin' or TARGET == 'freebsd': + 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' or TARGET == 'freebsd': + if TARGET in ('darwin', 'freebsd', 'android'): return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'clang++') else: return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'g++') @@ -727,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 @@ -825,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) @@ -857,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 @@ -1117,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") ######################################################################## # @@ -1485,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. """ @@ -2336,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!') @@ -2350,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") ######################################################################## ## @@ -2626,7 +2760,12 @@ 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. @@ -2635,6 +2774,10 @@ def SetupBuildEnvironment(compiler): 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. SetupVisualStudioEnviron() @@ -2669,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)) @@ -2731,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 @@ -2847,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) @@ -3102,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() @@ -3117,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() @@ -3152,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" @@ -3274,6 +3379,9 @@ 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: @@ -3315,5 +3423,9 @@ def TargetAdd(target, dummy=0, opts=[], input=[], dep=[], ipath=None, winrc=None if ext in (".obj", ".tlb", ".res", ".plugin", ".app") or ext in SUFFIX_DLL or ext in SUFFIX_LIB: t.deps[FindLocation("platform.dat", [])] = 1 + if target.endswith(".obj") and any(x.endswith(".in") for x in input): + if not CrossCompiling(): + t.deps[FindLocation("interrogate_module.exe", [])] = 1 + if target.endswith(".pz") and not CrossCompiling(): t.deps[FindLocation("pzip.exe", [])] = 1 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 290ec03e13..ecd87cbb66 100644 --- a/makepanda/test_imports.py +++ b/makepanda/test_imports.py @@ -98,7 +98,6 @@ import direct.distributed.InterestWatcher import direct.distributed.MsgTypes import direct.distributed.MsgTypesCMU import direct.distributed.NetMessenger -import direct.distributed.OldClientRepository import direct.distributed.ParentMgr import direct.distributed.PyDatagram import direct.distributed.PyDatagramIterator 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 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/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/openalAudioManager.cxx b/panda/src/audiotraits/openalAudioManager.cxx index 2e82a9c718..cbdaadbfa1 100644 --- a/panda/src/audiotraits/openalAudioManager.cxx +++ b/panda/src/audiotraits/openalAudioManager.cxx @@ -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; 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 da379bfd5c..d3c5ff7291 100644 --- a/panda/src/audiotraits/openalAudioSound.cxx +++ b/panda/src/audiotraits/openalAudioSound.cxx @@ -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) { 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 e7abe41886..eafc561c67 100644 --- a/panda/src/bullet/bulletBaseCharacterControllerNode.h +++ b/panda/src/bullet/bulletBaseCharacterControllerNode.h @@ -43,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 384aace8b6..f432db4a69 100644 --- a/panda/src/bullet/bulletBoxShape.I +++ b/panda/src/bullet/bulletBoxShape.I @@ -28,20 +28,3 @@ INLINE BulletBoxShape:: delete _shape; } - -/** - * - */ -INLINE BulletBoxShape:: -BulletBoxShape(const BulletBoxShape ©) : - _shape(copy._shape), _half_extents(copy._half_extents) { -} - -/** - * - */ -INLINE void BulletBoxShape:: -operator = (const BulletBoxShape ©) { - _shape = copy._shape; - _half_extents = copy._half_extents; -} diff --git a/panda/src/bullet/bulletBoxShape.cxx b/panda/src/bullet/bulletBoxShape.cxx index 02b5af45b3..9ae2e16a11 100644 --- a/panda/src/bullet/bulletBoxShape.cxx +++ b/panda/src/bullet/bulletBoxShape.cxx @@ -28,6 +28,28 @@ BulletBoxShape(const LVecBase3 &halfExtents) : _half_extents(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); } diff --git a/panda/src/bullet/bulletBoxShape.h b/panda/src/bullet/bulletBoxShape.h index d7fa1c5169..bfb9207e61 100644 --- a/panda/src/bullet/bulletBoxShape.h +++ b/panda/src/bullet/bulletBoxShape.h @@ -33,8 +33,8 @@ private: PUBLISHED: explicit BulletBoxShape(const LVecBase3 &halfExtents); - INLINE BulletBoxShape(const BulletBoxShape ©); - INLINE void operator = (const BulletBoxShape ©); + BulletBoxShape(const BulletBoxShape ©); + void operator = (const BulletBoxShape ©); INLINE ~BulletBoxShape(); LVecBase3 get_half_extents_without_margin() const; diff --git a/panda/src/bullet/bulletCapsuleShape.I b/panda/src/bullet/bulletCapsuleShape.I index feb6cdff57..2491d55d0d 100644 --- a/panda/src/bullet/bulletCapsuleShape.I +++ b/panda/src/bullet/bulletCapsuleShape.I @@ -30,26 +30,6 @@ INLINE BulletCapsuleShape:: delete _shape; } -/** - * - */ -INLINE BulletCapsuleShape:: -BulletCapsuleShape(const BulletCapsuleShape ©) : - _shape(copy._shape), - _radius(copy._radius), - _height(copy._height) { -} - -/** - * - */ -INLINE void BulletCapsuleShape:: -operator = (const BulletCapsuleShape ©) { - _shape = copy._shape; - _radius = copy._radius; - _height = copy._height; -} - /** * Returns the radius that was used to construct this capsule. */ diff --git a/panda/src/bullet/bulletCapsuleShape.cxx b/panda/src/bullet/bulletCapsuleShape.cxx index 9f389985e3..1c396bbdfb 100644 --- a/panda/src/bullet/bulletCapsuleShape.cxx +++ b/panda/src/bullet/bulletCapsuleShape.cxx @@ -38,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; +} + + /** * */ @@ -122,6 +148,7 @@ fillin(DatagramIterator &scan, BamReader *manager) { break; } + nassertv(_shape); _shape->setUserPointer(this); _shape->setMargin(margin); } diff --git a/panda/src/bullet/bulletCapsuleShape.h b/panda/src/bullet/bulletCapsuleShape.h index 5b031cc548..e376674976 100644 --- a/panda/src/bullet/bulletCapsuleShape.h +++ b/panda/src/bullet/bulletCapsuleShape.h @@ -30,8 +30,8 @@ private: PUBLISHED: explicit BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); - INLINE BulletCapsuleShape(const BulletCapsuleShape ©); - INLINE void operator = (const BulletCapsuleShape ©); + BulletCapsuleShape(const BulletCapsuleShape ©); + void operator = (const BulletCapsuleShape ©); INLINE ~BulletCapsuleShape(); INLINE PN_stdfloat get_radius() const; 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 0b8574c709..b2c43d8bb4 100644 --- a/panda/src/bullet/bulletCharacterControllerNode.h +++ b/panda/src/bullet/bulletCharacterControllerNode.h @@ -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 bf618a862b..b9c05664b7 100644 --- a/panda/src/bullet/bulletConeShape.I +++ b/panda/src/bullet/bulletConeShape.I @@ -30,26 +30,6 @@ INLINE BulletConeShape:: delete _shape; } -/** - * - */ -INLINE BulletConeShape:: -BulletConeShape(const BulletConeShape ©) : - _shape(copy._shape), - _radius(copy._radius), - _height(copy._height) { -} - -/** - * - */ -INLINE void BulletConeShape:: -operator = (const BulletConeShape ©) { - _shape = copy._shape; - _radius = copy._radius; - _height = copy._height; -} - /** * Returns the radius that was passed into the constructor. */ diff --git a/panda/src/bullet/bulletConeShape.cxx b/panda/src/bullet/bulletConeShape.cxx index 80e91a038f..30b24a5a18 100644 --- a/panda/src/bullet/bulletConeShape.cxx +++ b/panda/src/bullet/bulletConeShape.cxx @@ -38,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; +} + /** * */ @@ -122,6 +147,7 @@ fillin(DatagramIterator &scan, BamReader *manager) { break; } + nassertv(_shape); _shape->setUserPointer(this); _shape->setMargin(margin); } diff --git a/panda/src/bullet/bulletConeShape.h b/panda/src/bullet/bulletConeShape.h index 081367449b..4d40c61da9 100644 --- a/panda/src/bullet/bulletConeShape.h +++ b/panda/src/bullet/bulletConeShape.h @@ -30,8 +30,8 @@ private: PUBLISHED: explicit BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); - INLINE BulletConeShape(const BulletConeShape ©); - INLINE void operator = (const BulletConeShape ©); + BulletConeShape(const BulletConeShape ©); + void operator = (const BulletConeShape ©); INLINE ~BulletConeShape(); INLINE PN_stdfloat get_radius() const; 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 bc4f548dfb..d7fabee508 100644 --- a/panda/src/bullet/bulletConeTwistConstraint.h +++ b/panda/src/bullet/bulletConeTwistConstraint.h @@ -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 9b3df8dfe2..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); 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 909dfd2d7e..47e0b62704 100644 --- a/panda/src/bullet/bulletConvexPointCloudShape.I +++ b/panda/src/bullet/bulletConvexPointCloudShape.I @@ -28,30 +28,3 @@ INLINE BulletConvexPointCloudShape:: delete _shape; } - -/** - * - */ -INLINE BulletConvexPointCloudShape:: -BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©) : - _scale(copy._scale), - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletConvexPointCloudShape:: -operator = (const BulletConvexPointCloudShape ©) { - _scale = copy._scale; - _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 1527a712b3..fb7f62287b 100644 --- a/panda/src/bullet/bulletConvexPointCloudShape.cxx +++ b/panda/src/bullet/bulletConvexPointCloudShape.cxx @@ -84,6 +84,38 @@ BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale) { _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. */ diff --git a/panda/src/bullet/bulletConvexPointCloudShape.h b/panda/src/bullet/bulletConvexPointCloudShape.h index 293ece996b..da403d8794 100644 --- a/panda/src/bullet/bulletConvexPointCloudShape.h +++ b/panda/src/bullet/bulletConvexPointCloudShape.h @@ -33,11 +33,11 @@ private: PUBLISHED: explicit BulletConvexPointCloudShape(const PTA_LVecBase3 &points, LVecBase3 scale=LVecBase3(1.)); explicit BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale=LVecBase3(1.)); - INLINE BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©); - INLINE void operator = (const BulletConvexPointCloudShape ©); + 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); diff --git a/panda/src/bullet/bulletCylinderShape.I b/panda/src/bullet/bulletCylinderShape.I index c389813444..54616905f9 100644 --- a/panda/src/bullet/bulletCylinderShape.I +++ b/panda/src/bullet/bulletCylinderShape.I @@ -28,47 +28,3 @@ INLINE BulletCylinderShape:: delete _shape; } - -/** - * - */ -INLINE BulletCylinderShape:: -BulletCylinderShape(const BulletCylinderShape ©) : - _shape(copy._shape), _half_extents(copy._half_extents) { -} - -/** - * - */ -INLINE void BulletCylinderShape:: -operator = (const BulletCylinderShape ©) { - _shape = copy._shape; - _half_extents = copy._half_extents; -} - -/** - * - */ -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 fec07ce484..8f95bc5591 100644 --- a/panda/src/bullet/bulletCylinderShape.cxx +++ b/panda/src/bullet/bulletCylinderShape.cxx @@ -39,6 +39,7 @@ BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up) : break; } + nassertv(_shape); _shape->setUserPointer(this); } @@ -66,9 +67,32 @@ BulletCylinderShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) { 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; +} + /** * */ @@ -78,6 +102,36 @@ 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. */ @@ -150,6 +204,7 @@ fillin(DatagramIterator &scan, BamReader *manager) { break; } + nassertv(_shape); _shape->setUserPointer(this); _shape->setMargin(margin); } diff --git a/panda/src/bullet/bulletCylinderShape.h b/panda/src/bullet/bulletCylinderShape.h index 2cdd854ac4..f53962ec6f 100644 --- a/panda/src/bullet/bulletCylinderShape.h +++ b/panda/src/bullet/bulletCylinderShape.h @@ -31,13 +31,13 @@ private: PUBLISHED: explicit BulletCylinderShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); explicit BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up=Z_up); - INLINE BulletCylinderShape(const BulletCylinderShape ©); - INLINE void operator = (const BulletCylinderShape ©); + 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); diff --git a/panda/src/bullet/bulletDebugNode.cxx b/panda/src/bullet/bulletDebugNode.cxx index b4575dd3cf..743b5163ff 100644 --- a/panda/src/bullet/bulletDebugNode.cxx +++ b/panda/src/bullet/bulletDebugNode.cxx @@ -31,8 +31,10 @@ PStatCollector BulletDebugNode::_pstat_debug("App:Bullet:DoPhysics:Debug"); * */ BulletDebugNode:: -BulletDebugNode(const char *name) : PandaNode(name), _debug_stale(true) { +BulletDebugNode(const char *name) : PandaNode(name) { + _debug_stale = false; + _debug_world = nullptr; _wireframe = true; _constraints = true; _bounds = false; @@ -167,7 +169,7 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) { PT(Geom) debug_triangles; { - LightMutexHolder holder(_lock); + LightMutexHolder holder(BulletWorld::get_global_lock()); if (_debug_world == nullptr) { return; } @@ -268,8 +270,7 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) { * */ void BulletDebugNode:: -sync_b2p(btDynamicsWorld *world) { - LightMutexHolder holder(_lock); +do_sync_b2p(btDynamicsWorld *world) { _debug_world = world; _debug_stale = true; diff --git a/panda/src/bullet/bulletDebugNode.h b/panda/src/bullet/bulletDebugNode.h index dabe9d13fa..c780429a79 100644 --- a/panda/src/bullet/bulletDebugNode.h +++ b/panda/src/bullet/bulletDebugNode.h @@ -17,7 +17,6 @@ #include "pandabase.h" #include "bullet_includes.h" -#include "lightMutex.h" /** * @@ -55,7 +54,7 @@ public: 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; @@ -101,7 +100,6 @@ private: int _mode; }; - LightMutex _lock; DebugDraw _drawer; bool _debug_stale; 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 a2446ffcd4..f571df1ad6 100644 --- a/panda/src/bullet/bulletGenericConstraint.h +++ b/panda/src/bullet/bulletGenericConstraint.h @@ -57,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 87a1624f3d..a76c0ce8a1 100644 --- a/panda/src/bullet/bulletGhostNode.h +++ b/panda/src/bullet/bulletGhostNode.h @@ -34,8 +34,8 @@ PUBLISHED: 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); @@ -43,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(); @@ -57,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 5bfceb1e5b..5a2702be9a 100644 --- a/panda/src/bullet/bulletHeightfieldShape.I +++ b/panda/src/bullet/bulletHeightfieldShape.I @@ -32,33 +32,3 @@ INLINE BulletHeightfieldShape:: delete _shape; delete [] _data; } - -/** - * - */ -INLINE BulletHeightfieldShape:: -BulletHeightfieldShape(const BulletHeightfieldShape ©) : - _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)); -} - -/** - * - */ -INLINE void BulletHeightfieldShape:: -operator = (const BulletHeightfieldShape ©) { - _shape = copy._shape; - _num_rows = copy._num_rows; - _num_cols = copy._num_cols; - - size_t size = (size_t)_num_rows * (size_t)_num_cols; - _data = new btScalar[size]; - memcpy(_data, copy._data, size * sizeof(btScalar)); -} diff --git a/panda/src/bullet/bulletHeightfieldShape.cxx b/panda/src/bullet/bulletHeightfieldShape.cxx index 6ff476c0a6..1a01876401 100644 --- a/panda/src/bullet/bulletHeightfieldShape.cxx +++ b/panda/src/bullet/bulletHeightfieldShape.cxx @@ -61,6 +61,7 @@ ptr() const { */ void BulletHeightfieldShape:: set_use_diamond_subdivision(bool flag) { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _shape->setUseDiamondSubdivision(flag); } @@ -104,6 +105,42 @@ BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up) : _shape->setUserPointer(this); } +/** + * + */ +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. */ @@ -169,8 +206,9 @@ fillin(DatagramIterator &scan, BamReader *manager) { _num_cols = scan.get_int32(); size_t size = (size_t)_num_rows * (size_t)_num_cols; - delete[] _data; + delete [] _data; _data = new float[size]; + for (size_t i = 0; i < size; ++i) { _data[i] = scan.get_stdfloat(); } diff --git a/panda/src/bullet/bulletHeightfieldShape.h b/panda/src/bullet/bulletHeightfieldShape.h index 2f70fddac8..1bfc6c41c3 100644 --- a/panda/src/bullet/bulletHeightfieldShape.h +++ b/panda/src/bullet/bulletHeightfieldShape.h @@ -34,8 +34,8 @@ private: PUBLISHED: explicit BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up=Z_up); explicit BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up=Z_up); - INLINE BulletHeightfieldShape(const BulletHeightfieldShape ©); - INLINE void operator = (const BulletHeightfieldShape ©); + BulletHeightfieldShape(const BulletHeightfieldShape ©); + void operator = (const BulletHeightfieldShape ©); INLINE ~BulletHeightfieldShape(); void set_use_diamond_subdivision(bool flag=true); 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 12a1b1b2c2..dc6768490b 100644 --- a/panda/src/bullet/bulletHingeConstraint.h +++ b/panda/src/bullet/bulletHingeConstraint.h @@ -69,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 ed4a356f9a..b99d0c7561 100644 --- a/panda/src/bullet/bulletMinkowskiSumShape.I +++ b/panda/src/bullet/bulletMinkowskiSumShape.I @@ -30,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()); -} - /** * */ diff --git a/panda/src/bullet/bulletMinkowskiSumShape.cxx b/panda/src/bullet/bulletMinkowskiSumShape.cxx index 3dc618adac..cc56d39884 100644 --- a/panda/src/bullet/bulletMinkowskiSumShape.cxx +++ b/panda/src/bullet/bulletMinkowskiSumShape.cxx @@ -33,6 +33,30 @@ BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b) _shape->setUserPointer(this); } +/** + * + */ +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,6 +66,48 @@ 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. */ diff --git a/panda/src/bullet/bulletMinkowskiSumShape.h b/panda/src/bullet/bulletMinkowskiSumShape.h index 2e66ab9cae..c8629f2e33 100644 --- a/panda/src/bullet/bulletMinkowskiSumShape.h +++ b/panda/src/bullet/bulletMinkowskiSumShape.h @@ -32,14 +32,14 @@ private: PUBLISHED: explicit BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b); - INLINE BulletMinkowskiSumShape(const BulletMinkowskiSumShape ©); - INLINE void operator = (const BulletMinkowskiSumShape ©); + 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; 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 9defaedc51..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; +} + /** * */ @@ -51,6 +71,38 @@ 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. */ diff --git a/panda/src/bullet/bulletMultiSphereShape.h b/panda/src/bullet/bulletMultiSphereShape.h index 11d3ee936f..d6fd7af7b7 100644 --- a/panda/src/bullet/bulletMultiSphereShape.h +++ b/panda/src/bullet/bulletMultiSphereShape.h @@ -31,13 +31,13 @@ private: PUBLISHED: explicit BulletMultiSphereShape(const PTA_LVecBase3 &points, const PTA_stdfloat &radii); - INLINE BulletMultiSphereShape(const BulletMultiSphereShape ©); - INLINE void operator = (const BulletMultiSphereShape ©); + 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); 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 2b410fb0e9..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()); +} + /** * */ diff --git a/panda/src/bullet/bulletPlaneShape.h b/panda/src/bullet/bulletPlaneShape.h index 4455328f10..610c5d2412 100644 --- a/panda/src/bullet/bulletPlaneShape.h +++ b/panda/src/bullet/bulletPlaneShape.h @@ -33,12 +33,12 @@ private: PUBLISHED: explicit BulletPlaneShape(const LVector3 &normal, PN_stdfloat constant); - INLINE BulletPlaneShape(const BulletPlaneShape ©); - INLINE void operator = (const BulletPlaneShape ©); + 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 b8c921e99d..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()); } 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 931e51ec80..6a670867c5 100644 --- a/panda/src/bullet/bulletSliderConstraint.h +++ b/panda/src/bullet/bulletSliderConstraint.h @@ -70,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 1d5a70d93f..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; @@ -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 a5177c572a..d410322298 100644 --- a/panda/src/bullet/bulletSphereShape.I +++ b/panda/src/bullet/bulletSphereShape.I @@ -20,24 +20,6 @@ INLINE BulletSphereShape:: delete _shape; } -/** - * - */ -INLINE BulletSphereShape:: -BulletSphereShape(const BulletSphereShape ©) : - _shape(copy._shape), - _radius(copy._radius) { -} - -/** - * - */ -INLINE void BulletSphereShape:: -operator = (const BulletSphereShape ©) { - _shape = copy._shape; - _radius = copy._radius; -} - /** * Returns the radius that was used to construct this sphere. */ diff --git a/panda/src/bullet/bulletSphereShape.cxx b/panda/src/bullet/bulletSphereShape.cxx index 4cda14882b..7b3fee62a4 100644 --- a/panda/src/bullet/bulletSphereShape.cxx +++ b/panda/src/bullet/bulletSphereShape.cxx @@ -25,6 +25,29 @@ BulletSphereShape(PN_stdfloat radius) : _radius(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; +} + + /** * */ diff --git a/panda/src/bullet/bulletSphereShape.h b/panda/src/bullet/bulletSphereShape.h index c16a1279a6..b11d1c4a32 100644 --- a/panda/src/bullet/bulletSphereShape.h +++ b/panda/src/bullet/bulletSphereShape.h @@ -32,8 +32,8 @@ private: PUBLISHED: explicit BulletSphereShape(PN_stdfloat radius); - INLINE BulletSphereShape(const BulletSphereShape ©); - INLINE void operator = (const BulletSphereShape ©); + BulletSphereShape(const BulletSphereShape ©); + void operator = (const BulletSphereShape ©); INLINE ~BulletSphereShape(); INLINE PN_stdfloat get_radius() const; 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/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 b6965303aa..33b9b9bf47 100644 --- a/panda/src/bullet/bulletTriangleMesh.I +++ b/panda/src/bullet/bulletTriangleMesh.I @@ -19,34 +19,6 @@ ptr() const { return (btStridingMeshInterface *)&_mesh; } -/** - * Returns the number of vertices in this triangle mesh. - */ -INLINE size_t BulletTriangleMesh:: -get_num_vertices() const { - return _vertices.size(); -} - -/** - * Returns the vertex at the given vertex index. - */ -INLINE LPoint3 BulletTriangleMesh:: -get_vertex(size_t index) const { - nassertr(index < _vertices.size(), LPoint3::zero()); - const btVector3 &vertex = _vertices[index]; - return LPoint3(vertex[0], vertex[1], vertex[2]); -} - -/** - * Returns the vertex indices making up the given triangle index. - */ -INLINE LVecBase3i BulletTriangleMesh:: -get_triangle(size_t index) const { - index *= 3; - nassertr(index + 2 < _indices.size(), LVecBase3i::zero()); - return LVecBase3i(_indices[index], _indices[index + 1], _indices[index + 2]); -} - /** * */ diff --git a/panda/src/bullet/bulletTriangleMesh.cxx b/panda/src/bullet/bulletTriangleMesh.cxx index 3138e28efd..e2d3e0b242 100644 --- a/panda/src/bullet/bulletTriangleMesh.cxx +++ b/panda/src/bullet/bulletTriangleMesh.cxx @@ -36,12 +36,58 @@ BulletTriangleMesh() _mesh.addIndexedMesh(mesh); } +/** + * Returns the number of vertices in this triangle mesh. + */ +size_t BulletTriangleMesh:: +get_num_vertices() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + 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 { - return _indices.size() / 3; + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_num_triangles(); } /** @@ -51,6 +97,8 @@ get_num_triangles() const { */ void BulletTriangleMesh:: preallocate(int num_verts, int num_indices) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + _vertices.reserve(num_verts); _indices.reserve(num_indices); @@ -66,9 +114,11 @@ preallocate(int num_verts, int num_indices) { * 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()); @@ -96,6 +146,21 @@ add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remov 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. @@ -105,6 +170,8 @@ add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remov */ void BulletTriangleMesh:: set_welding_distance(PN_stdfloat distance) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + _welding_distance = distance; } @@ -114,6 +181,8 @@ set_welding_distance(PN_stdfloat distance) { */ PN_stdfloat BulletTriangleMesh:: get_welding_distance() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + return _welding_distance; } @@ -129,6 +198,8 @@ get_welding_distance() const { */ void BulletTriangleMesh:: add_geom(const Geom *geom, bool remove_duplicate_vertices, const TransformState *ts) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + nassertv(geom); nassertv(ts); @@ -241,6 +312,8 @@ add_geom(const Geom *geom, bool remove_duplicate_vertices, const TransformState */ void BulletTriangleMesh:: add_array(const PTA_LVecBase3 &points, const PTA_int &indices, bool remove_duplicate_vertices) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0]; _indices.reserve(_indices.size() + indices.size()); @@ -279,7 +352,9 @@ add_array(const PTA_LVecBase3 &points, const PTA_int &indices, bool remove_dupli */ void BulletTriangleMesh:: output(ostream &out) const { - out << get_type() << ", " << get_num_triangles() << " triangles"; + LightMutexHolder holder(BulletWorld::get_global_lock()); + + out << get_type() << ", " << _indices.size() / 3 << " triangles"; } /** diff --git a/panda/src/bullet/bulletTriangleMesh.h b/panda/src/bullet/bulletTriangleMesh.h index 64973d83e8..0f9cb8174b 100644 --- a/panda/src/bullet/bulletTriangleMesh.h +++ b/panda/src/bullet/bulletTriangleMesh.h @@ -55,10 +55,16 @@ PUBLISHED: virtual void write(ostream &out, int indent_level) const; public: - INLINE size_t get_num_vertices() const; - INLINE LPoint3 get_vertex(size_t index) const; + size_t get_num_vertices() const; + LPoint3 get_vertex(size_t index) const; - INLINE LVecBase3i get_triangle(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); 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 22775a6e0a..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()); diff --git a/panda/src/bullet/bulletTriangleMeshShape.h b/panda/src/bullet/bulletTriangleMeshShape.h index b76227b46e..079fb2aeb1 100644 --- a/panda/src/bullet/bulletTriangleMeshShape.h +++ b/panda/src/bullet/bulletTriangleMeshShape.h @@ -32,8 +32,8 @@ private: PUBLISHED: explicit BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress=true, bool bvh=true); - INLINE BulletTriangleMeshShape(const BulletTriangleMeshShape ©); - INLINE void operator = (const BulletTriangleMeshShape ©); + BulletTriangleMeshShape(const BulletTriangleMeshShape ©); + void operator = (const BulletTriangleMeshShape ©); INLINE ~BulletTriangleMeshShape(); void refit_tree(const LPoint3 &aabb_min, const LPoint3 &aabb_max); 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 c2ecd2cec3..61893a1531 100644 --- a/panda/src/bullet/bulletWorld.I +++ b/panda/src/bullet/bulletWorld.I @@ -50,19 +50,6 @@ INLINE BulletWorld:: delete _broadphase; } -/** - * - */ -INLINE void BulletWorld:: -set_debug_node(BulletDebugNode *node) { - nassertv(node); - if (node != _debug) { - clear_debug_node(); - _debug = node; - _world->setDebugDrawer(&(_debug->_drawer)); - } -} - /** * */ @@ -108,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 a7942aee02..f3d48e3c74 100644 --- a/panda/src/bullet/bulletWorld.cxx +++ b/panda/src/bullet/bulletWorld.cxx @@ -118,6 +118,17 @@ BulletWorld() { _world->getSolverInfo().m_numIterations = bullet_solver_iterations; } +/** + * + */ +LightMutex &BulletWorld:: +get_global_lock() { + + static LightMutex lock; + + return lock; +} + /** * */ @@ -127,13 +138,34 @@ 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) { - LightMutexHolder holder(_debug->_lock); + _debug->_debug_stale = false; _debug->_debug_world = nullptr; _world->setDebugDrawer(nullptr); _debug = nullptr; @@ -145,6 +177,7 @@ clear_debug_node() { */ 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()); @@ -155,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); @@ -165,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()); } @@ -174,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(); @@ -181,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 @@ -191,13 +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) { - _debug->sync_b2p(_world); + _debug->do_sync_b2p(_world); } _pstat_physics.stop(); @@ -206,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(); } } @@ -260,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; @@ -289,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; @@ -318,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); @@ -337,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); @@ -360,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); @@ -387,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); @@ -410,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); @@ -451,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); @@ -474,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); @@ -500,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); @@ -522,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); @@ -543,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; @@ -568,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); @@ -588,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); @@ -608,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()); @@ -630,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()); @@ -647,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()); @@ -670,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); @@ -701,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); @@ -726,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); @@ -745,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); @@ -779,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; @@ -793,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); } @@ -802,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; @@ -815,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; @@ -828,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; @@ -839,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); @@ -865,6 +1176,7 @@ tick_callback(btDynamicsWorld *world, btScalar timestep) { */ void BulletWorld:: set_filter_callback(CallbackObject *obj) { + LightMutexHolder holder(get_global_lock()); nassertv(obj != NULL); @@ -880,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 558d086b37..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); + 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); 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/animControl.cxx b/panda/src/chan/animControl.cxx index 09660d22c0..35e56374f7 100644 --- a/panda/src/chan/animControl.cxx +++ b/panda/src/chan/animControl.cxx @@ -103,7 +103,7 @@ fail_anim(PartBundle *part) { */ AnimControl:: ~AnimControl() { - get_part()->set_control_effect(this, 0.0f); + get_part()->control_removed(this); } /** diff --git a/panda/src/chan/partBundle.cxx b/panda/src/chan/partBundle.cxx index 3fb797e3c3..f519d09a60 100644 --- a/panda/src/chan/partBundle.cxx +++ b/panda/src/chan/partBundle.cxx @@ -539,6 +539,25 @@ control_activated(AnimControl *control) { } } +/** + * Called by the AnimControl when it destructs. This needs to remove the + * AnimControl pointer from all pipeline stages. + */ +void PartBundle:: +control_removed(AnimControl *control) { + nassertv(control->get_part() == this); + + OPEN_ITERATE_ALL_STAGES(_cycler) { + CDStageWriter cdata(_cycler, pipeline_stage); + ChannelBlend::iterator cbi = cdata->_blend.find(control); + if (cbi != cdata->_blend.end()) { + cdata->_blend.erase(cbi); + cdata->_anim_changed = true; + } + } + CLOSE_ITERATE_ALL_STAGES(_cycler); +} + /** * The internal implementation of bind_anim(), this receives a pointer to an * uninitialized AnimControl and fills it in if the bind is successful. diff --git a/panda/src/chan/partBundle.h b/panda/src/chan/partBundle.h index 60d2b66b8b..80ddb4122d 100644 --- a/panda/src/chan/partBundle.h +++ b/panda/src/chan/partBundle.h @@ -149,6 +149,7 @@ public: // The following functions aren't really part of the public interface; // they're just public so we don't have to declare a bunch of friends. virtual void control_activated(AnimControl *control); + void control_removed(AnimControl *control); INLINE void set_update_delay(double delay); bool do_bind_anim(AnimControl *control, AnimBundle *anim, @@ -204,6 +205,7 @@ private: typedef CycleDataLockedReader CDLockedReader; typedef CycleDataReader CDReader; typedef CycleDataWriter CDWriter; + typedef CycleDataStageWriter CDStageWriter; public: static void register_with_read_factory(); diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm index 0baa94f8ed..5430373c55 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm @@ -15,7 +15,6 @@ #include "cocoaGraphicsBuffer.h" #include "cocoaGraphicsWindow.h" #include "cocoaGraphicsStateGuardian.h" -#include "cocoaPandaApp.h" #include "config_cocoadisplay.h" #include "frameBufferProperties.h" #include "displayInformation.h" diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index 426c06cc78..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" @@ -68,6 +69,8 @@ CocoaGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, // 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 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 d58137ce68..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; @@ -257,6 +277,7 @@ output(ostream &out) const { void CollisionVisualizer:: begin_traversal() { CollisionRecorder::begin_traversal(); + LightMutexHolder holder(_lock); _data.clear(); } @@ -271,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; @@ -286,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 29d920f92d..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 @@ -34,6 +35,7 @@ class EXPCL_PANDA_COLLIDE CollisionVisualizer : public PandaNode, public CollisionRecorder { PUBLISHED: 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/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/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 9c11fc9bdc..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; + } } /** @@ -1927,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 @@ -1938,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); } /** @@ -1990,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); } @@ -2004,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); } @@ -2564,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 d18d86f690..c21bec1d73 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -123,6 +123,8 @@ public: TS_do_flip, TS_do_release, TS_do_windows, + TS_do_compute, + TS_do_extract, TS_terminate, TS_done }; @@ -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/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 2d07f2c971..d590aa8d37 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -3231,9 +3231,10 @@ async_reload_texture(TextureContext *tc) { PT(Texture) GraphicsStateGuardian:: get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) { 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(PointLight::get_class_type()) || - node->is_of_type(Spotlight::get_class_type()), NULL); + node->is_of_type(Spotlight::get_class_type()) || + is_point, nullptr); LightLensNode *light = (LightLensNode *)node; if (light == nullptr || !light->_shadow_caster) { @@ -3246,20 +3247,49 @@ get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) { } } + // 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; } + + if (display_cat.is_debug()) { + display_cat.debug() + << "Constructing shadow buffer for light '" << light->get_name() + << "', size=" << light->_sb_size[0] << "x" << light->_sb_size[1] + << ", sort=" << light->_sb_sort << "\n"; + } + + if (host == nullptr) { + nassertr(_current_display_region != nullptr, nullptr); + host = _current_display_region->get_window(); + } + nassertr(host != nullptr, nullptr); + + // 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) { + for (int i = 0; i < 6; ++i) { + PT(DisplayRegion) dr = sbuffer->make_mono_display_region(0, 1, 0, 1); + dr->set_lens_index(i); + dr->set_target_tex_page(i); + dr->set_camera(light_np); + dr->set_clear_depth_active(true); + } + } else { + PT(DisplayRegion) dr = sbuffer->make_mono_display_region(0, 1, 0, 1); + dr->set_camera(light_np); + dr->set_clear_depth_active(true); + } + + light->_sbuffers[this] = sbuffer; + return light->_shadow_map; } /** @@ -3299,101 +3329,33 @@ get_dummy_shadow_map(Texture::TextureType texture_type) const { } /** - * 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. + * 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. */ -PT(Texture) GraphicsStateGuardian:: -make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) { - // Make sure everything is valid. - PandaNode *node = light_np.node(); - nassertr(node->is_of_type(DirectionalLight::get_class_type()) || - node->is_of_type(PointLight::get_class_type()) || - node->is_of_type(Spotlight::get_class_type()), NULL); - - LightLensNode *light = (LightLensNode *)node; - if (light == NULL || !light->_shadow_caster) { - return NULL; - } - +GraphicsOutput *GraphicsStateGuardian:: +make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host) { 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() - << "Constructing shadow buffer for light '" << light->get_name() - << "', size=" << light->_sb_size[0] << "x" << light->_sb_size[1] - << ", 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]); + WindowProperties props = WindowProperties::size(light->_sb_size); int flags = GraphicsPipe::BF_refuse_window; if (is_point) { flags |= GraphicsPipe::BF_size_square; } - // 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 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); - // 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); + if (sbuffer != nullptr) { + sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth); } - 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); - } - - // Assign display region(s) to the buffer and camera - if (is_point) { - for (int i = 0; i < 6; ++i) { - PT(DisplayRegion) dr = sbuffer->make_mono_display_region(0, 1, 0, 1); - dr->set_lens_index(i); - dr->set_target_tex_page(i); - dr->set_camera(light_np); - dr->set_clear_depth_active(true); - } - } else { - PT(DisplayRegion) dr = sbuffer->make_mono_display_region(0, 1, 0, 1); - dr->set_camera(light_np); - dr->set_clear_depth_active(true); - } - light->_sbuffers[this] = sbuffer; - - return tex; + return sbuffer; } /** diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 8a0ca541d0..9b7855dc31 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -424,7 +424,7 @@ public: PT(Texture) get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host=NULL); PT(Texture) get_dummy_shadow_map(Texture::TextureType texture_type) const; - PT(Texture) make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host); + virtual GraphicsOutput *make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host); virtual void ensure_generated_shader(const RenderState *state); 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/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 bd5e964f52..16a65c0096 100644 --- a/panda/src/display/windowProperties.h +++ b/panda/src/display/windowProperties.h @@ -52,6 +52,7 @@ PUBLISHED: 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/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 b51d67c564..8326068dc1 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -4936,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); @@ -4978,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/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/egg2pg/eggSaver.cxx b/panda/src/egg2pg/eggSaver.cxx index 5eeb98f42c..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); diff --git a/panda/src/event/asyncFuture.I b/panda/src/event/asyncFuture.I index 37dca79038..a11fb6b36a 100644 --- a/panda/src/event/asyncFuture.I +++ b/panda/src/event/asyncFuture.I @@ -103,6 +103,11 @@ 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()); diff --git a/panda/src/event/asyncFuture.h b/panda/src/event/asyncFuture.h index e3a835eea3..cbed94b158 100644 --- a/panda/src/event/asyncFuture.h +++ b/panda/src/event/asyncFuture.h @@ -86,6 +86,7 @@ PUBLISHED: 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: @@ -185,10 +186,10 @@ public: register_type(_type_handle, "AsyncGatheringFuture", AsyncFuture::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/event/asyncTask.cxx b/panda/src/event/asyncTask.cxx index 17f0a0630d..9f70f0e5ad 100644 --- a/panda/src/event/asyncTask.cxx +++ b/panda/src/event/asyncTask.cxx @@ -402,8 +402,7 @@ unlock_and_do_task() { nassertr(current_thread->_current_task == nullptr, DS_interrupt); void *ptr = AtomicAdjust::compare_and_exchange_ptr - ((void * TVOLATILE &)current_thread->_current_task, - (void *)nullptr, (void *)this); + (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. @@ -437,8 +436,7 @@ unlock_and_do_task() { nassertr(current_thread->_current_task == this, status); ptr = AtomicAdjust::compare_and_exchange_ptr - ((void * TVOLATILE &)current_thread->_current_task, - (void *)this, (void *)nullptr); + (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. 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/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/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 e3dcc2f110..762a6cb8ce 100644 --- a/panda/src/express/pointerToArray.h +++ b/panda/src/express/pointerToArray.h @@ -112,6 +112,8 @@ PUBLISHED: 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); @@ -266,6 +270,8 @@ PUBLISHED: 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); @@ -341,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/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/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/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/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index fab18bcb62..af84e2bafd 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -7561,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 diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index e4da7cade1..2b357ece5b 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -381,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 diff --git a/panda/src/gobj/geom.I b/panda/src/gobj/geom.I index d553c12977..340cfd099c 100644 --- a/panda/src/gobj/geom.I +++ b/panda/src/gobj/geom.I @@ -512,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 8590342010..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)) { } /** @@ -631,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); } } @@ -1491,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); @@ -1536,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); } diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h index 8b220a5a4a..ec81442703 100644 --- a/panda/src/gobj/geom.h +++ b/panda/src/gobj/geom.h @@ -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; 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/geomVertexData.I b/panda/src/gobj/geomVertexData.I index 53c1c76367..5a24b166a5 100644 --- a/panda/src/gobj/geomVertexData.I +++ b/panda/src/gobj/geomVertexData.I @@ -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)); + } } /** diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx index 339f38114e..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 diff --git a/panda/src/gobj/geomVertexData.h b/panda/src/gobj/geomVertexData.h index f2858d5a16..d86dd36fc4 100644 --- a/panda/src/gobj/geomVertexData.h +++ b/panda/src/gobj/geomVertexData.h @@ -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; 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 a2c54a5d0b..add7acef2f 100644 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -3179,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"; @@ -3197,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); @@ -3208,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) { @@ -3223,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) { @@ -3235,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; @@ -3245,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"; @@ -3260,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); @@ -3271,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) { @@ -3284,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; @@ -3294,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"; @@ -3303,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); @@ -3314,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; diff --git a/panda/src/gobj/shader.h b/panda/src/gobj/shader.h index 3b23b2d698..996ee1b7aa 100644 --- a/panda/src/gobj/shader.h +++ b/panda/src/gobj/shader.h @@ -84,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 = "", @@ -92,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); @@ -464,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 f5fe50dbc1..8d48fed425 100644 --- a/panda/src/gobj/shaderBuffer.h +++ b/panda/src/gobj/shaderBuffer.h @@ -32,6 +32,8 @@ private: INLINE ShaderBuffer() DEFAULT_CTOR; PUBLISHED: + ~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); 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/grutil/shaderTerrainMesh.cxx b/panda/src/grutil/shaderTerrainMesh.cxx index a7f6b1d8af..8c9c7bd9b6 100644 --- a/panda/src/grutil/shaderTerrainMesh.cxx +++ b/panda/src/grutil/shaderTerrainMesh.cxx @@ -52,6 +52,18 @@ ConfigVariableInt stm_max_views "with. Each camera rendering the terrain corresponds to a view. Lowering this " "value will reduce the data that has to be transferred to the GPU.")); +ConfigVariableEnum stm_heightfield_minfilter +("stm-heightfield-minfilter", SamplerState::FT_linear, + PRC_DESC("This specifies the minfilter that is applied for a heightfield texture. This " + "can be used to create heightfield that is visual correct with collision " + "geometry (for example bullet terrain mesh) by changing it to nearest")); + +ConfigVariableEnum stm_heightfield_magfilter +("stm-heightfield-magfilter", SamplerState::FT_linear, + PRC_DESC("This specifies the magfilter that is applied for a heightfield texture. This " + "can be used to create heightfield that is visual correct with collision " + "geometry (for example bullet terrain mesh) by changing it to nearest")); + PStatCollector ShaderTerrainMesh::_basic_collector("Cull:ShaderTerrainMesh:Setup"); PStatCollector ShaderTerrainMesh::_lod_collector("Cull:ShaderTerrainMesh:CollectLOD"); @@ -148,8 +160,10 @@ void ShaderTerrainMesh::do_extract_heightfield() { } else { _heightfield_tex->set_format(Texture::F_r16); } - _heightfield_tex->set_minfilter(SamplerState::FT_linear); - _heightfield_tex->set_magfilter(SamplerState::FT_linear); + _heightfield_tex->set_minfilter(stm_heightfield_minfilter); + _heightfield_tex->set_magfilter(stm_heightfield_magfilter); + _heightfield_tex->set_wrap_u(SamplerState::WM_clamp); + _heightfield_tex->set_wrap_v(SamplerState::WM_clamp); } /** 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 464cd6fa12..bdf7e917b6 100644 --- a/panda/src/linmath/lvecBase4_src.I +++ b/panda/src/linmath/lvecBase4_src.I @@ -971,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 fdd81dad4b..d04201d988 100644 --- a/panda/src/linmath/lvecBase4_src.h +++ b/panda/src/linmath/lvecBase4_src.h @@ -243,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/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/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/nativenet/socket_address.cxx b/panda/src/nativenet/socket_address.cxx old mode 100755 new mode 100644 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/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..6c817f56bd 100644 --- a/panda/src/pandabase/pandasymbols.h +++ b/panda/src/pandabase/pandasymbols.h @@ -326,12 +326,6 @@ #define INLINE_GUI INLINE #define INLINE_AUDIO INLINE -#endif - - -#if defined(DIRECTORY_DLLS) - -#else #define EXPCL_PANDA_PGRAPH EXPCL_PANDA #define EXPTP_PANDA_PGRAPH EXPTP_PANDA @@ -429,7 +423,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/pgraph/lightAttrib.cxx b/panda/src/pgraph/lightAttrib.cxx index 15b010336e..40014c53ee 100644 --- a/panda/src/pgraph/lightAttrib.cxx +++ b/panda/src/pgraph/lightAttrib.cxx @@ -841,6 +841,9 @@ compose_impl(const RenderAttrib *other) const { 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); } diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index c9ccfd7c90..71bbb3a561 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -3262,7 +3262,7 @@ get_shader() const { * */ void NodePath:: -set_shader_input(ShaderInput inp) { +set_shader_input(const ShaderInput &inp) { nassertv_always(!is_empty()); PandaNode *pnode = node(); @@ -3278,6 +3278,26 @@ set_shader_input(ShaderInput inp) { } } +/** + * + */ +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))); + } +} + /** * */ diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 617aac46a5..c5a3902c32 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -629,7 +629,8 @@ PUBLISHED: void set_shader_auto(BitMask32 shader_switch, int priority=0); void clear_shader(); - void set_shader_input(ShaderInput input); + 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); diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index 0d64cb0828..96b94dd0c2 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -3798,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 @@ -3817,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 diff --git a/panda/src/pgraph/shaderAttrib.cxx b/panda/src/pgraph/shaderAttrib.cxx index 67bba2d196..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,7 +195,22 @@ clear_flag(int flag) const { * */ CPT(RenderAttrib) ShaderAttrib:: -set_shader_input(ShaderInput input) const { +set_shader_input(const 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(), 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()) { diff --git a/panda/src/pgraph/shaderAttrib.h b/panda/src/pgraph/shaderAttrib.h index cb1eec37d9..02a3189233 100644 --- a/panda/src/pgraph/shaderAttrib.h +++ b/panda/src/pgraph/shaderAttrib.h @@ -71,7 +71,8 @@ PUBLISHED: CPT(RenderAttrib) clear_shader() const; // Shader Inputs - CPT(RenderAttrib) set_shader_input(ShaderInput input) 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; diff --git a/panda/src/pgraph/textureAttrib.cxx b/panda/src/pgraph/textureAttrib.cxx index 9b30906e1f..ee3c5a8165 100644 --- a/panda/src/pgraph/textureAttrib.cxx +++ b/panda/src/pgraph/textureAttrib.cxx @@ -825,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/pgraphnodes/lightLensNode.I b/panda/src/pgraphnodes/lightLensNode.I index eb2b192f97..1df14eb971 100644 --- a/panda/src/pgraphnodes/lightLensNode.I +++ b/panda/src/pgraphnodes/lightLensNode.I @@ -41,6 +41,9 @@ set_shadow_caster(bool caster) { } _shadow_caster = caster; set_active(caster); + if (caster) { + setup_shadow_map(); + } } /** @@ -65,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; } /** @@ -82,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 f68b7084ec..b79b5e8dc0 100644 --- a/panda/src/pgraphnodes/lightLensNode.cxx +++ b/panda/src/pgraphnodes/lightLensNode.cxx @@ -67,6 +67,9 @@ LightLensNode(const LightLensNode ©) : _has_specular_color(copy._has_specular_color), _attrib_count(0) { + if (_shadow_caster) { + setup_shadow_map(); + } } /** @@ -75,20 +78,43 @@ 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. diff --git a/panda/src/pgraphnodes/lightLensNode.h b/panda/src/pgraphnodes/lightLensNode.h index 2102acc541..03c1342927 100644 --- a/panda/src/pgraphnodes/lightLensNode.h +++ b/panda/src/pgraphnodes/lightLensNode.h @@ -41,6 +41,8 @@ PUBLISHED: 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); @@ -53,12 +55,15 @@ 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; @@ -106,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/pointLight.cxx b/panda/src/pgraphnodes/pointLight.cxx index caa8d86261..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; @@ -184,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 cbfcf9b50d..ba3a334d47 100644 --- a/panda/src/pgraphnodes/pointLight.h +++ b/panda/src/pgraphnodes/pointLight.h @@ -63,6 +63,8 @@ public: int light_id); private: + virtual void setup_shadow_map(); + // 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/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index 6b83c3ef14..f741b02db8 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -409,11 +409,12 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) { } // 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)) { - continue; + skip = true; } else { info._flags = ShaderKey::TF_map_normal; } @@ -422,24 +423,34 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) { if (shader_attrib->auto_glow_on()) { info._flags = ShaderKey::TF_map_glow; } else { - continue; + skip = true; } break; case TextureStage::M_gloss: if (key._lighting && shader_attrib->auto_gloss_on()) { info._flags = ShaderKey::TF_map_gloss; } else { - continue; + skip = true; } break; case TextureStage::M_height: if (parallax_mapping_samples > 0) { info._flags = ShaderKey::TF_map_height; } else { - continue; + skip = true; } break; } + // 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; + } // Check if this state has a texture matrix to transform the texture // coordinates. @@ -947,6 +958,11 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } 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) { @@ -1023,6 +1039,10 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { // has a TexMatrixAttrib, also transform them. 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; + } switch (tex._gen_mode) { case TexGenAttrib::M_off: // Cg seems to be able to optimize this temporary away when appropriate. @@ -1099,6 +1119,10 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } 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. @@ -1444,7 +1468,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << "\t result.rgb = "; text << combine_mode_as_string(tex, combine_rgb, false, i); text << ";\n\t result.a = "; - text << combine_mode_as_string(tex, combine_alpha, false, i); + text << combine_mode_as_string(tex, combine_alpha, true, i); text << ";\n"; } if (tex._flags & ShaderKey::TF_rgb_scale_2) { @@ -1667,6 +1691,7 @@ combine_source_as_string(const ShaderKey::TextureInfo &info, short num, bool alp csource << "saturate(1.0f - "; } switch (c_src) { + case TextureStage::CS_undefined: case TextureStage::CS_texture: csource << "tex" << texindex; break; @@ -1685,8 +1710,6 @@ combine_source_as_string(const ShaderKey::TextureInfo &info, short num, bool alp 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) { diff --git a/panda/src/physics/physicalNode.cxx b/panda/src/physics/physicalNode.cxx index 0fdf995720..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; 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/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 9b078a6a30..fa6a97499b 100644 --- a/panda/src/pipeline/pipelineCyclerTrueImpl.I +++ b/panda/src/pipeline/pipelineCyclerTrueImpl.I @@ -393,7 +393,7 @@ cycle_2() { _data[1]._cdata = _data[0]._cdata; // No longer dirty. - _dirty = false; + _dirty = 0; return last_val; } @@ -423,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/thread.I b/panda/src/pipeline/thread.I index 12a8607e63..10d7b6e44f 100644 --- a/panda/src/pipeline/thread.I +++ b/panda/src/pipeline/thread.I @@ -277,9 +277,9 @@ preempt() { * AsyncTaskManager), if any, or NULL if the thread is not currently servicing * a task. */ -INLINE AsyncTask *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 4dbebacc8b..730deb5872 100644 --- a/panda/src/pipeline/thread.h +++ b/panda/src/pipeline/thread.h @@ -23,6 +23,10 @@ #include "pnotify.h" #include "config_pipeline.h" +#ifdef ANDROID +typedef struct _JNIEnv JNIEnv; +#endif + class Mutex; class ReMutex; class MutexDebug; @@ -89,7 +93,7 @@ PUBLISHED: BLOCKING INLINE void join(); INLINE void preempt(); - INLINE AsyncTask *get_current_task() const; + INLINE TypedReferenceCount *get_current_task() const; INLINE void set_python_index(int index); @@ -128,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(); @@ -142,7 +150,7 @@ private: int _pipeline_stage; PStatsCallback *_pstats_callback; bool _joinable; - AsyncTask *_current_task; + AtomicAdjust::Pointer _current_task; int _python_index; 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/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/pnmimagetypes/pnmFileTypeStbImage.cxx b/panda/src/pnmimagetypes/pnmFileTypeStbImage.cxx index a87aae11f6..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 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/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/datagramBuffer.h b/panda/src/putil/datagramBuffer.h index acb67836f9..8962ddbb2b 100644 --- a/panda/src/putil/datagramBuffer.h +++ b/panda/src/putil/datagramBuffer.h @@ -15,6 +15,7 @@ #define DATAGRAMBUFFER_H #include "pandabase.h" +#include "datagramGenerator.h" #include "datagramSink.h" #include "vector_uchar.h" 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/simpleHashMap.I b/panda/src/putil/simpleHashMap.I index 78d4bdf2bd..26728f1e01 100644 --- a/panda/src/putil/simpleHashMap.I +++ b/panda/src/putil/simpleHashMap.I @@ -41,7 +41,13 @@ SimpleHashMap(const SimpleHashMap ©) : _deleted_chain = memory_hook->get_deleted_chain(alloc_size); _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none()); - memcpy(_table, copy._table, alloc_size); + + 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); } /** @@ -88,7 +94,12 @@ operator = (const SimpleHashMap ©) { _deleted_chain = memory_hook->get_deleted_chain(alloc_size); _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none()); - memcpy(_table, copy._table, alloc_size); + 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; } diff --git a/panda/src/text/textAssembler.cxx b/panda/src/text/textAssembler.cxx index a10179db04..49c507dafd 100644 --- a/panda/src/text/textAssembler.cxx +++ b/panda/src/text/textAssembler.cxx @@ -1626,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 diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx index 9da9aa0fea..a9c6b113a2 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx +++ b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx @@ -2397,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()); @@ -2438,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; @@ -2559,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/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/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index 8efbf01b5d..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()) { 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/scripts/MayaPandaTool.mel b/pandatool/src/scripts/MayaPandaTool.mel old mode 100755 new mode 100644 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/console.rcss b/samples/rocket-console/assets/console.rcss old mode 100755 new mode 100644 diff --git a/samples/rocket-console/assets/console.rml b/samples/rocket-console/assets/console.rml old mode 100755 new mode 100644 diff --git a/samples/rocket-console/assets/loading.rml b/samples/rocket-console/assets/loading.rml old mode 100755 new mode 100644 diff --git a/samples/rocket-console/assets/rkt.rcss b/samples/rocket-console/assets/rkt.rcss old mode 100755 new mode 100644 diff --git a/samples/rocket-console/assets/window.rcss b/samples/rocket-console/assets/window.rcss old mode 100755 new mode 100644 diff --git a/samples/rocket-console/assets/window.rml b/samples/rocket-console/assets/window.rml old mode 100755 new mode 100644 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/display/conftest.py b/tests/display/conftest.py index 821b06af80..c66ef2f54a 100644 --- a/tests/display/conftest.py +++ b/tests/display/conftest.py @@ -1,23 +1,29 @@ import pytest -@pytest.fixture -def graphics_pipe(scope='session'): + +@pytest.fixture(scope='session') +def graphics_pipe(): from panda3d.core import GraphicsPipeSelection pipe = GraphicsPipeSelection.get_global_ptr().make_default_pipe() - if not pipe.is_valid(): - pytest.xfail("GraphicsPipe is invalid") + if pipe is None or not pipe.is_valid(): + pytest.skip("GraphicsPipe is invalid") yield pipe -@pytest.fixture -def graphics_engine(scope='session'): + +@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 @@ -40,3 +46,28 @@ def window(graphics_pipe, graphics_engine): 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_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(' 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()